]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Re-engineer the RTR PDU handlers
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Sat, 23 May 2026 04:30:24 +0000 (22:30 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Sat, 23 May 2026 04:30:24 +0000 (22:30 -0600)
The old algorithm was ill-advised.

It used to be caching incremental deltas between RTR serials.
Then, because the only thing that matters in the end is the net delta
between the router's serial and the latest, it was building up the net
delta from the incremental ones.

The complexity was O(n^2), where n is the total VRPs + RKs + ASPAs
across all involved incremental deltas. Under normal load, n seems to
tend to be small. But that's asking for trouble. Also, the algorithm
also had to allocate n objects, which is rather excessive for a Serial
Query handler.

There was also the issue that all the RTR data was cached in RAM,
which means the RTR session had to change every time Fort was
restarted. And this memory usage wasn't terribly insignificant.

And also, computing a net delta from incremental ASPAs is awkward.
Because ASPAs that share customerAS are supposed to override each other,
they cancel out differently from VRPs.

So I ended up rewriting the entire RTR session caching code.
The Serial Query handler now computes net deltas from RTR snapshots.
The snapshots are stored in the cache, not RAM.

Complexity is O(m), where m is the total VRPs + RKs + ASPAs of the two
relevant snapshots. So m tends to be large, but it degrades much
healthier. The handler also allocates very little memory.

Fixes #70. Fixes #176.

38 files changed:
src/Makefile.am
src/config.c
src/crypto/hash.c
src/file.c
src/main.c
src/object/bgpsec.c
src/object/tal.c
src/rtr/db/db_table.c
src/rtr/db/db_table.h
src/rtr/db/delta.c [deleted file]
src/rtr/db/delta.h [deleted file]
src/rtr/db/deltas_array.c [deleted file]
src/rtr/db/deltas_array.h [deleted file]
src/rtr/db/vrps.c
src/rtr/db/vrps.h
src/rtr/meta.c [new file with mode: 0644]
src/rtr/meta.h [new file with mode: 0644]
src/rtr/pdu_handler.c
src/rtr/pdu_handler.h
src/rtr/pdu_sender.c
src/rtr/pdu_sender.h
src/rtr/pdu_stream.c
src/rtr/pdu_stream.h
src/rtr/rtr.c
src/rtr/rtr.h
src/slurm/slurm_loader.c
src/slurm/slurm_loader.h
src/types/delta.c [deleted file]
src/types/delta.h [deleted file]
src/types/serial.h
test/Makefile.am
test/rtr/db/db_table_test.c
test/rtr/db/deltas_array_test.c [deleted file]
test/rtr/db/rtr_db_mock.c [deleted file]
test/rtr/db/vrps_test.c [deleted file]
test/rtr/pdu_handler_test.c
test/rtr/pdu_stream_test.c
test/tal_test.c

index 54c08952f3a73ea53d27b3f43407624718ffbbf9..f177f75dbf10bbb2039c097fefdb8b5f59aa4028 100644 (file)
@@ -44,7 +44,6 @@ fort_SOURCES += asn1/signed_data.h asn1/signed_data.c
 fort_SOURCES += types/address.h types/address.c
 fort_SOURCES += types/aspa.h types/aspa.c
 fort_SOURCES += types/bio_seq.c types/bio_seq.h
-fort_SOURCES += types/delta.c types/delta.h
 fort_SOURCES += types/router_key.c types/router_key.h
 fort_SOURCES += types/serial.h types/serial.c
 fort_SOURCES += types/uri.h types/uri.c
@@ -98,6 +97,7 @@ fort_SOURCES += rsync/rsync.h rsync/rsync.c
 
 fort_SOURCES += rtr/pdu_stream.c rtr/pdu_stream.h
 fort_SOURCES += rtr/err_pdu.c rtr/err_pdu.h
+fort_SOURCES += rtr/meta.c rtr/meta.h
 fort_SOURCES += rtr/pdu_handler.c rtr/pdu_handler.h
 fort_SOURCES += rtr/pdu_sender.c rtr/pdu_sender.h
 fort_SOURCES += rtr/pdu.c rtr/pdu.h
@@ -105,8 +105,6 @@ fort_SOURCES += rtr/primitive_writer.c rtr/primitive_writer.h
 fort_SOURCES += rtr/rtr.c rtr/rtr.h
 
 fort_SOURCES += rtr/db/db_table.c rtr/db/db_table.h
-fort_SOURCES += rtr/db/delta.c rtr/db/delta.h
-fort_SOURCES += rtr/db/deltas_array.c rtr/db/deltas_array.h
 fort_SOURCES += rtr/db/vrps.c rtr/db/vrps.h
 
 fort_SOURCES += slurm/db_slurm.c slurm/db_slurm.h
index be850efd78e4ee462e988b1b5da051f237cccc8b..83be8f995b7bb6b37e2c91e3d4430e6107ed56f5 100644 (file)
@@ -428,8 +428,12 @@ static const struct option_field options[] = {
                .type = &gt_uint,
                .offset = offsetof(struct rpki_config, server.deltas_lifetime),
                .doc = "Number of iterations the deltas will be stored.",
-               .min = 0,
-               .max = UINT_MAX,
+               .min = 1,
+               /*
+                * It's a serial, which means the technical maximum is about
+                * 2^31 - 1. But that's too much.
+                */
+               .max = 1000,
        },
 
        /* Prometheus fields */
index ff5020b7acf8f4c205d1586ebd301373ea5a6025..dda693daa255c0728ff886f08cc5721441efae75 100644 (file)
@@ -68,10 +68,11 @@ hash_local_file(char const *uri, unsigned char *result,
 
        do {
                consumed = fread(buffer, 1, stat.st_blksize, file);
-               error = ferror(file);
-               if (error) {
-                       pr_val_err("File reading error. Error message (apparently): %s",
-                          strerror(error));
+               if (ferror(file)) {
+                       error = errno;
+                       if (!error)
+                               error = EINVAL;
+                       pr_val_err("File read failure: %s", strerror(error));
                        goto end;
                }
 
index 29b1dce9168dfcea1994f0cff9bcd98f14112bcf..58f59edb2c86652e336c3ca0e6cf1abc09efc60b 100644 (file)
@@ -81,15 +81,12 @@ file_load(char const *file_name, struct file_contents *fc)
 
        fread_result = fread(fc->buffer, 1, fc->buffer_size, file);
        if (fread_result < fc->buffer_size) {
-               error = ferror(file);
-               if (error) {
-                       /*
-                        * The manpage doesn't say that the result is an error
-                        * code. It literally doesn't say how to get an error
-                        * code.
-                        */
-                       pr_val_err("File reading error. The error message is (possibly) '%s'",
-                           strerror(error));
+               if (ferror(file)) {
+                       error = errno;
+                       /* errno on fread() is POSIX, not ISO C. */
+                       if (!error)
+                               error = EINVAL;
+                       pr_val_err("File read failure: %s", strerror(error));
                        free(fc->buffer);
                        goto end;
                }
index e4f458c3426129f70ed162b36a79ff826b971171..d05a2eef263c2e7b1998ec39a54cacca58f08772 100644 (file)
@@ -32,8 +32,8 @@ fort_standalone(void)
 static int
 fort_server(void)
 {
+       struct rtr_metadata rtr;
        int error;
-       bool changed;
 
        pr_op_info("Main loop: Starting...");
 
@@ -41,7 +41,7 @@ fort_server(void)
        if (error)
                return error;
 
-       error = vrps_update(NULL);
+       error = vrps_update(&rtr);
        if (fort_end)
                goto end;
        if (error) {
@@ -49,7 +49,7 @@ fort_server(void)
                goto end;
        }
 
-       rtr_notify();
+       rtr_notify(&rtr);
 
        /* TODO (#133) Stats ready; remove this message in a couple versions. */
        pr_op_warn("First validation cycle successfully ended, now you can connect your router(s)");
@@ -62,16 +62,15 @@ fort_server(void)
                        goto end;
                pr_op_info("Main loop: Time to work!");
 
-               error = vrps_update(&changed);
-               if (fort_end || error == -EINTR)
+               error = vrps_update(&rtr);
+               if (fort_end)
                        break;
                if (error) {
                        pr_op_debug("Main loop: Error %d (%s)", error,
                            strerror(abs(error)));
                        continue;
                }
-               if (changed)
-                       rtr_notify();
+               rtr_notify(&rtr);
        } while (true);
 
 end:   rtr_stop();
@@ -150,9 +149,6 @@ main(int argc, char **argv)
        error = relax_ng_init();
        if (error)
                goto revert_http;
-       error = vrps_init();
-       if (error)
-               goto revert_relax_ng;
 
        /* Meat */
 
@@ -170,8 +166,6 @@ main(int argc, char **argv)
 
        /* End */
 
-       vrps_destroy();
-revert_relax_ng:
        relax_ng_cleanup();
 revert_http:
        http_cleanup();
index 2af60bd38e66f7c08dd3532d2916a1297b84f568..f783606c1c4da54b693c6c2026996829b9264f5a 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "log.h"
 #include "object/certificate.h"
+#include "types/router_key.h"
 #include "validation_handler.h"
 
 struct resource_params {
index 635d813f318f5fed1680fadb63823cbeda56f3a2..5a492f64f5f3b8b882f58040ff96daf42484eea4 100644 (file)
@@ -175,16 +175,15 @@ base64_sanitize(struct line_file *lfile, char **out)
        offset = 0;
        while ((fread_result = fread(buf, 1,
            (original_size > BUF_SIZE) ? BUF_SIZE : original_size, fd)) > 0) {
-               error = ferror(lfile_fd(lfile));
-               if (error) {
-                       /*
-                        * The manpage doesn't say that the result is an error
-                        * code. It literally doesn't say how to get an error
-                        * code.
-                        */
-                       pr_op_err("File reading error. Presumably, the error message is '%s.'",
-                           strerror(error));
-                       goto free_result;
+               if (ferror(lfile_fd(lfile))) {
+                       error = errno;
+                       /* errno on fread() is POSIX, not ISO C. */
+                       if (!error)
+                               error = EINVAL;
+                       pr_op_err("File read failure: %s", strerror(error));
+                       free(buf);
+                       free(result);
+                       return error;
                }
 
                original_size -= fread_result;
@@ -218,10 +217,6 @@ base64_sanitize(struct line_file *lfile, char **out)
 
        *out = result;
        return 0;
-free_result:
-       free(buf);
-       free(result);
-       return error;
 #undef BUF_SIZE
 }
 
index d31ca6f8172034c2d5b1ea3a8fe0f607810d19cb..782f05f044b5021f3e818afbf078928e1021ae37 100644 (file)
@@ -1,11 +1,16 @@
 #include "rtr/db/db_table.h"
 
 #include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
 #include "alloc.h"
 #include "config.h"
+#include "data_structure/common.h"
 #include "data_structure/uthash.h"
+#include "file.h"
 #include "log.h"
+#include "rtr/meta.h"
 #include "types/aspa.h"
 
 struct hashable_roa {
@@ -30,6 +35,8 @@ struct db_table {
 
        unsigned int total_roas_v4;
        unsigned int total_roas_v6;
+
+       struct rtr_metadata rtr;
 };
 
 struct db_table *
@@ -228,6 +235,355 @@ db_table_join(struct db_table *dst, struct db_table *src)
        return 0;
 }
 
+static int
+cmp_u8(uint8_t a, uint8_t b)
+{
+       /* JIC the platform typedefs uintXX_ts into unsigned ints */
+       a &= 0xFFu;
+       b &= 0xFFu;
+
+       if (a > b) return 1;
+       if (a < b) return -1;
+       return 0;
+}
+
+static int
+cmp_u32(uint32_t a, uint32_t b)
+{
+       a = htonl(a);
+       b = htonl(b);
+       return memcmp(&a, &b, 4);
+}
+
+static int
+cmp_vrp(struct hashable_roa *_a, struct hashable_roa *_b)
+{
+       struct vrp *a = &_a->data;
+       struct vrp *b = &_b->data;
+       int cmp;
+
+       cmp = cmp_u8(a->addr_fam, b->addr_fam);
+       if (cmp) return cmp;
+       cmp = cmp_u32(a->asn, b->asn);
+       if (cmp) return cmp;
+
+       switch (a->addr_fam) {
+       case AF_INET:  cmp = memcmp(&a->prefix.v4, &b->prefix.v4, 4);  break;
+       case AF_INET6: cmp = memcmp(&a->prefix.v6, &b->prefix.v6, 16); break;
+       }
+       if (cmp) return cmp;
+
+       cmp = cmp_u8(a->prefix_length, b->prefix_length);
+       if (cmp) return cmp;
+       return cmp_u8(a->max_prefix_length, b->max_prefix_length);
+}
+
+static int
+cmp_rk(struct hashable_key *_a, struct hashable_key *_b)
+{
+       struct router_key *a = &_a->data;
+       struct router_key *b = &_b->data;
+       int cmp;
+
+       cmp = cmp_u32(a->as, b->as);
+       if (cmp) return cmp;
+       cmp = memcmp(a->ski, b->ski, RK_SKI_LEN);
+       if (cmp) return cmp;
+       return memcmp(a->spk, b->spk, RK_SPKI_LEN);
+}
+
+static int
+cmp_aspa(struct hashable_aspa *a, struct hashable_aspa *b)
+{
+       return cmp_u32(a->v->customer, b->v->customer);
+}
+
+void
+db_table_sort(struct db_table *table)
+{
+       HASH_SORT(table->roas, cmp_vrp);
+       HASH_SORT(table->router_keys, cmp_rk);
+       HASH_SORT(table->aspas, cmp_aspa);
+}
+
+static int
+mkdir_f(char const *path)
+{
+       int error;
+
+       pr_op_debug("mkdir -f %s", path);
+
+       if (mkdir(path, 0777) < 0) {
+               error = errno;
+               if (error != EEXIST) {
+                       pr_op_err("Cannot create directory '%s': %s",
+                           path, strerror(error));
+                       return error;
+               }
+       }
+
+       return 0;
+}
+
+static int
+write_u8(FILE *file, uint8_t v)
+{
+       return (fwrite(&v, 1, 1, file) != 1)
+           ? pr_op_err("fwrite() could not write 1 byte.")
+           : 0;
+}
+
+static int
+write_u32(FILE *file, uint32_t v)
+{
+       v = htonl(v);
+       return (fwrite(&v, 4, 1, file) != 1)
+           ? pr_op_err("fwrite() could not write 4 bytes.")
+           : 0;
+}
+
+static int
+write_buf(FILE *file, unsigned char *buf, size_t len)
+{
+       return (fwrite(buf, len, 1, file) != 1)
+           ? pr_op_err("fwrite() could not write %zu bytes.", len)
+           : 0;
+}
+
+static int
+write_addr(FILE *file, struct vrp *vrp)
+{
+       switch (vrp->addr_fam) {
+       case AF_INET:  return write_buf(file, (unsigned char *)&vrp->prefix, 4);
+       case AF_INET6: return write_buf(file, (unsigned char *)&vrp->prefix, 16);
+       }
+       return EINVAL;
+}
+
+static int
+cache_vrps(struct db_table *table)
+{
+       FILE *f4 = NULL;
+       FILE *f6 = NULL;
+       FILE *file;
+       struct hashable_roa *hvrp, *tmpv;
+       struct vrp *vrp;
+       int err;
+
+       err = rtr_open_file(table->rtr.serial, "vrp4", "w", &f4);
+       if (err)
+               return err;
+       err = rtr_open_file(table->rtr.serial, "vrp6", "w", &f6);
+       if (err)
+               goto end;
+
+       HASH_ITER(hh, table->roas, hvrp, tmpv) {
+               vrp = &hvrp->data;
+
+               switch (vrp->addr_fam) {
+               case AF_INET:  file = f4; break;
+               case AF_INET6: file = f6; break;
+               default:       continue;
+               }
+
+               if ((err = write_u32(file, vrp->asn)) != 0)
+                       goto end;
+               if ((err = write_addr(file, vrp)) != 0)
+                       goto end;
+               if ((err = write_u8(file, vrp->prefix_length)) != 0)
+                       goto end;
+               if ((err = write_u8(file, vrp->max_prefix_length)) != 0)
+                       goto end;
+       }
+
+end:   if (f4) fclose(f4);
+       if (f6) fclose(f6);
+       return err;
+}
+
+static int
+cache_rks(struct db_table *table)
+{
+       FILE *file = NULL;
+       struct hashable_key *rk, *tmpr;
+       int err;
+
+       err = rtr_open_file(table->rtr.serial, "rk", "w", &file);
+       if (err)
+               return err;
+
+       HASH_ITER(hh, table->router_keys, rk, tmpr) {
+               if ((err = write_u32(file, rk->data.as)) != 0)
+                       break;
+               if ((err = write_buf(file, rk->data.ski, RK_SKI_LEN)) != 0)
+                       break;
+               if ((err = write_buf(file, rk->data.spk, RK_SPKI_LEN)) != 0)
+                       break;
+       }
+
+       fclose(file);
+       return err;
+}
+
+static int
+cache_aspas(struct db_table *table)
+{
+       FILE *file = NULL;
+       struct hashable_aspa *aspa, *tmpa;
+       array_index i;
+       int err;
+
+       err = rtr_open_file(table->rtr.serial, "aspa", "w", &file);
+       if (err)
+               return err;
+
+       HASH_ITER(hh, table->aspas, aspa, tmpa) {
+               if ((err = write_u32(file, aspa->v->customer)) != 0)
+                       break;
+               if ((err = write_u32(file, aspa->v->providers.count)) != 0)
+                       break;
+               for (i = 0; i < aspa->v->providers.count; i++)
+                       if ((err = write_u32(file, aspa->v->providers.asids[i])) != 0)
+                               break;
+       }
+
+       fclose(file);
+       return err;
+}
+
+static int
+cache_metadata(struct db_table *table)
+{
+       char *dir;
+       int error;
+
+       error = rtr_save_metadata(&table->rtr);
+       if (error) {
+               pr_op_err("Could not save RTR metadata; RTR can no longer be served.");
+               dir = rtr_filename(NULL, NULL);
+               file_rm_rf(dir);
+               free(dir);
+       }
+
+       return error;
+}
+
+static bool
+is_number(char const *str)
+{
+       if (*str == 0)
+               return false;
+
+       for (; *str != 0; str++)
+               if (*str < '0' || '9' < *str)
+                       return false;
+
+       return true;
+}
+
+static void
+delete_old_serials(serial_t current)
+{
+       char *path;
+       serial_t lowest;
+       DIR *dir;
+       struct dirent *file;
+       unsigned long serial;
+       int ret;
+
+       lowest = current - ((serial_t)config_get_deltas_lifetime());
+
+       path = rtr_filename(NULL, NULL);
+       dir = opendir(path);
+       free(path);
+       if (!dir) {
+               if (errno != ENOENT)
+                       pr_op_warn("Cannot clean rtr directory: %s",
+                           strerror(errno));
+               return;
+       }
+
+       FOREACH_DIR_FILE(dir, file) {
+               if (!is_number(file->d_name))
+                       continue;
+
+               errno = 0;
+               serial = strtoul(file->d_name, NULL, 10);
+               if (errno) {
+                       pr_op_warn("Cannot delete rtr/%s: %s",
+                           file->d_name, strerror(errno));
+                       continue;
+               }
+               if (serial > UINT32_MAX) {
+                       pr_op_warn("Cannot delete rtr/%s: Serial too big",
+                           file->d_name);
+                       continue;
+               }
+
+               if (!serial_lt(serial, lowest))
+                       continue;
+
+               path = rtr_filename2(serial, NULL);
+               ret = file_rm_rf(path);
+               free(path);
+               if (ret < 0)
+                       pr_op_warn("Cannot delete rtr/%s: nftw returned %d",
+                           file->d_name, ret);
+               else if (ret)
+                       pr_op_warn("Cannot delete rtr/%s: %s",
+                           file->d_name, strerror(ret));
+       }
+       if (errno)
+               pr_op_warn("Cleanup rtr directory traversal interrupted: %s",
+                   strerror(errno));
+
+       closedir(dir);
+}
+
+int
+db_table_cache(struct db_table *table)
+{
+       char *path;
+       int ret;
+
+       if (rtr_load_metadata(&table->rtr) != 0)
+               rtr_new_metadata(&table->rtr);
+       table->rtr.serial++;
+
+       path = rtr_filename(NULL, NULL);
+       ret = mkdir_f(path);
+       free(path);
+       if (ret)
+               return ret;
+
+       path = rtr_filename2(table->rtr.serial, NULL);
+       ret = mkdir_f(path);
+       free(path);
+       if (ret)
+               return ret;
+
+       ret = cache_vrps(table);
+       if (ret)
+               goto fail;
+       ret = cache_rks(table);
+       if (ret)
+               goto fail;
+       ret = cache_aspas(table);
+       if (ret)
+               goto fail;
+       ret = cache_metadata(table);
+       if (ret)
+               goto fail;
+       delete_old_serials(table->rtr.serial);
+
+       return 0;
+
+fail:  path = rtr_filename2(table->rtr.serial, NULL);
+       file_rm_rf(path);
+       free(path);
+       return ret;
+}
+
 int
 db_table_foreach_roa(struct db_table const *table, vrp_foreach_cb cb, void *arg)
 {
@@ -278,31 +634,43 @@ db_table_foreach_aspa(struct db_table const *table, aspa_foreach_cb cb,
 unsigned int
 db_table_roa_count(struct db_table *table)
 {
-       return HASH_COUNT(table->roas);
+       return table ? HASH_COUNT(table->roas) : 0;
 }
 
 unsigned int
 db_table_roa_count_v4(struct db_table *table)
 {
-       return table->total_roas_v4;
+       return table ? table->total_roas_v4 : 0;
 }
 
 unsigned int
 db_table_roa_count_v6(struct db_table *table)
 {
-       return table->total_roas_v6;
+       return table ? table->total_roas_v6 : 0;
 }
 
 unsigned int
 db_table_router_key_count(struct db_table *table)
 {
-       return HASH_COUNT(table->router_keys);
+       return table ? HASH_COUNT(table->router_keys) : 0;
 }
 
 unsigned int
 db_table_aspa_count(struct db_table *table)
 {
-       return HASH_COUNT(table->aspas);
+       return table ? HASH_COUNT(table->aspas) : 0;
+}
+
+uint16_t
+db_table_session(struct db_table *table)
+{
+       return table ? table->rtr.session : 0;
+}
+
+serial_t
+db_table_serial(struct db_table *table)
+{
+       return table ? table->rtr.serial : 0;
 }
 
 void
@@ -395,99 +763,3 @@ rtrhandler_handle_aspa(struct db_table *table, struct aspa *v)
 
        return error;
 }
-
-/*
- * Copies `@roas1 - roas2` into @deltas.
- *
- * (Places the ROAs that exist in @roas1 but not in @roas2 in @deltas.)
- */
-static int
-add_roa_deltas(struct hashable_roa *roas1, struct hashable_roa *roas2,
-    struct deltas *deltas, int op, char r1type)
-{
-       struct hashable_roa *n1; /* A node from @roas1 */
-       struct hashable_roa *n2; /* A node from @roas2 */
-       struct hashable_roa *tmp;
-       unsigned int r;
-       unsigned int roa1_count;
-
-       r = 0;
-       roa1_count = HASH_COUNT(roas1);
-
-       HASH_ITER(hh, roas1, n1, tmp) {
-               HASH_FIND(hh, roas2, &n1->data, sizeof(n1->data), n2);
-               if (n2 == NULL)
-                       deltas_add_roa(deltas, &n1->data, op, r1type, r,
-                           roa1_count);
-               r++;
-       }
-
-       return 0;
-}
-
-/*
- * Copies `@keys1 - keys2` into @deltas.
- *
- * (Places the Router Keys that exist in @keys1 but not in @key2 in @deltas.)
- */
-static void
-add_router_key_deltas(struct hashable_key *keys1, struct hashable_key *keys2,
-    struct deltas *deltas, int op)
-{
-       struct hashable_key *n1; /* A node from @keys1 */
-       struct hashable_key *n2; /* A node from @keys2 */
-
-       for (n1 = keys1; n1 != NULL; n1 = n1->hh.next) {
-               HASH_FIND(hh, keys2, &n1->data, sizeof(n1->data), n2);
-               if (n2 == NULL)
-                       deltas_add_router_key(deltas, &n1->data, op);
-       }
-}
-
-static void
-add_aspa_announcements(struct hashable_aspa *old, struct hashable_aspa *new,
-    struct deltas *deltas)
-{
-       struct hashable_aspa *o, *n, *tmp;
-
-       HASH_ITER(hh, new, n, tmp) {
-               HASH_FIND(hh, old, &n->v->customer, sizeof(n->v->customer), o);
-               if (o == NULL || !providers_equal(&o->v->providers, &n->v->providers))
-                       deltas_add_aspa(deltas, n->v, FLAG_ANNOUNCEMENT);
-       }
-}
-
-static void
-add_aspa_withdraws(struct hashable_aspa *old, struct hashable_aspa *new,
-    struct deltas *deltas)
-{
-       struct hashable_aspa *o, *n, *tmp;
-
-       HASH_ITER(hh, old, o, tmp) {
-               HASH_FIND(hh, new, &o->v->customer, sizeof(o->v->customer), n);
-               if (n == NULL)
-                       deltas_add_aspa(deltas, o->v, FLAG_WITHDRAWAL);
-       }
-}
-
-struct deltas *
-compute_deltas(struct db_table *old, struct db_table *new)
-{
-       struct deltas *deltas = deltas_create();
-
-       add_roa_deltas(new->roas, old->roas, deltas, FLAG_ANNOUNCEMENT, 'n');
-       add_roa_deltas(old->roas, new->roas, deltas, FLAG_WITHDRAWAL, 'o');
-       add_router_key_deltas(new->router_keys, old->router_keys, deltas,
-           FLAG_ANNOUNCEMENT);
-       add_router_key_deltas(old->router_keys, new->router_keys, deltas,
-           FLAG_WITHDRAWAL);
-       add_aspa_announcements(old->aspas, new->aspas, deltas);
-       add_aspa_withdraws(old->aspas, new->aspas, deltas);
-
-       if (deltas_is_empty(deltas)) {
-               deltas_refput(deltas);
-               return NULL;
-       }
-
-       return deltas;
-}
index ea47ec8a31ea18f6c34530fc7ea2c3b1f4538e40..4d13f7c4ff9a6e47cfc96c6f29da460d44b3001d 100644 (file)
@@ -1,9 +1,11 @@
 #ifndef SRC_RTR_DB_DB_TABLE_H_
 #define SRC_RTR_DB_DB_TABLE_H_
 
-#include "rtr/db/delta.h"
 #include "types/address.h"
 #include "types/aspa.h"
+#include "types/router_key.h"
+#include "types/serial.h"
+#include "types/vrp.h"
 
 struct db_table;
 
@@ -17,6 +19,11 @@ unsigned int db_table_roa_count_v4(struct db_table *);
 unsigned int db_table_roa_count_v6(struct db_table *);
 unsigned int db_table_router_key_count(struct db_table *);
 unsigned int db_table_aspa_count(struct db_table *);
+uint16_t db_table_session(struct db_table *);
+serial_t db_table_serial(struct db_table *);
+
+void db_table_sort(struct db_table *);
+int db_table_cache(struct db_table *);
 
 int db_table_foreach_roa(struct db_table const *, vrp_foreach_cb, void *);
 void db_table_remove_roa(struct db_table *, struct vrp const *);
@@ -34,6 +41,5 @@ int rtrhandler_handle_roa_v6(struct db_table *, uint32_t,
 int rtrhandler_handle_router_key(struct db_table *, unsigned char const *,
     uint32_t, unsigned char const *);
 int rtrhandler_handle_aspa(struct db_table *, struct aspa *);
-struct deltas *compute_deltas(struct db_table *, struct db_table *);
 
 #endif /* SRC_RTR_DB_DB_TABLE_H_ */
diff --git a/src/rtr/db/delta.c b/src/rtr/db/delta.c
deleted file mode 100644 (file)
index d248b92..0000000
+++ /dev/null
@@ -1,357 +0,0 @@
-#include "rtr/db/delta.h"
-
-#include <stdatomic.h>
-#include <string.h>
-
-#include "data_structure/array_list.h"
-#include "log.h"
-#include "types/address.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;
-};
-
-struct delta_rk {
-       unsigned char   ski[RK_SKI_LEN];
-       uint32_t        as;
-       unsigned char   spk[RK_SPKI_LEN];
-};
-
-struct _delta_aspa {
-       struct aspa *aspa;
-};
-
-STATIC_ARRAY_LIST(deltas_v6, struct delta_v6)
-STATIC_ARRAY_LIST(deltas_v4, struct delta_v4)
-STATIC_ARRAY_LIST(deltas_rk, struct delta_rk)
-STATIC_ARRAY_LIST(deltas_aspa, struct _delta_aspa)
-
-struct deltas {
-       struct {
-               struct deltas_v4 adds;
-               struct deltas_v4 removes;
-       } v4;
-       struct {
-               struct deltas_v6 adds;
-               struct deltas_v6 removes;
-       } v6;
-       struct {
-               struct deltas_rk adds;
-               struct deltas_rk removes;
-       } rk;
-       struct {
-               struct deltas_aspa adds;
-               struct deltas_aspa removes;
-       } aspa;
-
-       atomic_uint references;
-};
-
-struct deltas *
-deltas_create(void)
-{
-       struct deltas *result;
-
-       result = pmalloc(sizeof(struct deltas));
-
-       deltas_v4_init(&result->v4.adds);
-       deltas_v4_init(&result->v4.removes);
-       deltas_v6_init(&result->v6.adds);
-       deltas_v6_init(&result->v6.removes);
-       deltas_rk_init(&result->rk.adds);
-       deltas_rk_init(&result->rk.removes);
-       deltas_aspa_init(&result->aspa.adds);
-       deltas_aspa_init(&result->aspa.removes);
-       atomic_init(&result->references, 1);
-
-       return result;
-}
-
-void
-deltas_refget(struct deltas *deltas)
-{
-       atomic_fetch_add(&deltas->references, 1);
-}
-
-static void
-clean_aspa(struct _delta_aspa *da)
-{
-       aspa_refput(da->aspa);
-}
-
-void
-deltas_refput(struct deltas *deltas)
-{
-       /*
-        * Reminder: atomic_fetch_sub() returns the previous value, not the
-        * resulting one.
-        */
-       if (atomic_fetch_sub(&deltas->references, 1) == 1) {
-               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);
-               deltas_rk_cleanup(&deltas->rk.adds, NULL);
-               deltas_rk_cleanup(&deltas->rk.removes, NULL);
-               deltas_aspa_cleanup(&deltas->aspa.adds, clean_aspa);
-               deltas_aspa_cleanup(&deltas->aspa.removes, clean_aspa);
-               free(deltas);
-       }
-}
-
-static struct deltas_v4 *
-get_deltas_array4(struct deltas *deltas, int op)
-{
-       switch (op) {
-       case FLAG_ANNOUNCEMENT:
-               return &deltas->v4.adds;
-       case FLAG_WITHDRAWAL:
-               return &deltas->v4.removes;
-       }
-
-       pr_crit("Unknown delta operation: %d", op);
-       return NULL; /* Warning shutupper */
-}
-
-static struct deltas_v6 *
-get_deltas_array6(struct deltas *deltas, int op)
-{
-       switch (op) {
-       case FLAG_ANNOUNCEMENT:
-               return &deltas->v6.adds;
-       case FLAG_WITHDRAWAL:
-               return &deltas->v6.removes;
-       }
-
-       pr_crit("Unknown delta operation: %d", op);
-       return NULL; /* Warning shutupper */
-}
-
-void
-deltas_add_roa(struct deltas *deltas, struct vrp const *vrp, int op,
-    char r1type, unsigned int roa_counter, unsigned int roa_count)
-{
-       union {
-               struct delta_v4 v4;
-               struct delta_v6 v6;
-       } delta;
-       char buffer[INET6_ADDRSTRLEN];
-
-       switch (vrp->addr_fam) {
-       case AF_INET:
-               delta.v4.as = vrp->asn;
-               delta.v4.prefix.addr = vrp->prefix.v4;
-               delta.v4.prefix.len = vrp->prefix_length;
-               delta.v4.max_length = vrp->max_prefix_length;
-               deltas_v4_add(get_deltas_array4(deltas, op), &delta.v4);
-               return;
-       case AF_INET6:
-               delta.v6.as = vrp->asn;
-               delta.v6.prefix.addr = vrp->prefix.v6;
-               delta.v6.prefix.len = vrp->prefix_length;
-               delta.v6.max_length = vrp->max_prefix_length;
-               deltas_v6_add(get_deltas_array6(deltas, op), &delta.v6);
-               return;
-       }
-
-       pr_crit("Unknown protocol: [%u %s/%u-%u %u] %c %u/%u "
-           "(Please report this output to https://github.com/NICMx/FORT-validator/issues/89)",
-           vrp->asn,
-           addr2str6(&vrp->prefix.v6, buffer),
-           vrp->prefix_length,
-           vrp->max_prefix_length,
-           vrp->addr_fam,
-           r1type,
-           roa_counter,
-           roa_count);
-}
-
-void
-deltas_add_router_key(struct deltas *deltas, struct router_key const *key,
-    int op)
-{
-       struct delta_rk delta = {
-               .as = key->as,
-       };
-       memcpy(delta.ski, key->ski, RK_SKI_LEN);
-       memcpy(delta.spk, key->spk, RK_SPKI_LEN);
-
-       switch (op) {
-       case FLAG_ANNOUNCEMENT:
-               deltas_rk_add(&deltas->rk.adds, &delta);
-               return;
-       case FLAG_WITHDRAWAL:
-               deltas_rk_add(&deltas->rk.removes, &delta);
-               return;
-       }
-
-       pr_crit("Unknown delta operation: %d", op);
-}
-
-void
-deltas_add_aspa(struct deltas *deltas, struct aspa *aspa, int op)
-{
-       struct _delta_aspa delta;
-
-       delta.aspa = aspa;
-       aspa_refget(aspa);
-
-       switch (op) {
-       case FLAG_ANNOUNCEMENT:
-               deltas_aspa_add(&deltas->aspa.adds, &delta);
-               return;
-       case FLAG_WITHDRAWAL:
-               deltas_aspa_add(&deltas->aspa.removes, &delta);
-               return;
-       }
-
-       pr_crit("Unknown delta operation: %d", 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)
-           && (deltas->rk.adds.len == 0)
-           && (deltas->rk.removes.len == 0)
-           && (deltas->aspa.adds.len == 0)
-           && (deltas->aspa.removes.len == 0);
-}
-
-static int
-__foreach_v4(struct deltas_v4 *array, delta_vrp_foreach_cb cb, void *arg,
-    uint8_t flags)
-{
-       struct delta_vrp delta;
-       struct delta_v4 *d;
-       int error;
-
-       delta.vrp.addr_fam = AF_INET;
-       delta.flags = flags;
-
-       ARRAYLIST_FOREACH(array, d) {
-               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;
-       }
-
-       return 0;
-}
-
-static int
-__foreach_v6(struct deltas_v6 *array, delta_vrp_foreach_cb cb, void *arg,
-    uint8_t flags)
-{
-       struct delta_vrp delta;
-       struct delta_v6 *d;
-       int error;
-
-       delta.vrp.addr_fam = AF_INET6;
-       delta.flags = flags;
-
-       ARRAYLIST_FOREACH(array, d) {
-               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;
-       }
-
-       return 0;
-}
-
-static int
-__foreach_rk(struct deltas_rk *array,  delta_router_key_foreach_cb cb,
-    void *arg, uint8_t flags)
-{
-       struct delta_router_key delta;
-       struct delta_rk *d;
-       int error;
-
-       delta.flags = flags;
-
-       ARRAYLIST_FOREACH(array, d) {
-               delta.router_key.as = d->as;
-               memcpy(delta.router_key.ski, d->ski, RK_SKI_LEN);
-               memcpy(delta.router_key.spk, d->spk, RK_SPKI_LEN);
-               error = cb(&delta, arg);
-               if (error)
-                       return error;
-       }
-
-       return 0;
-}
-
-static int
-__foreach_aspa(struct deltas_aspa *array, delta_aspa_foreach_cb cb, void *arg,
-    uint8_t flags)
-{
-       struct delta_aspa delta;
-       struct _delta_aspa *d;
-       int error;
-
-       delta.flags = flags;
-
-       ARRAYLIST_FOREACH(array, d) {
-               delta.aspa = d->aspa;
-               error = cb(&delta, arg);
-               if (error)
-                       return error;
-       }
-
-       return 0;
-}
-
-int
-deltas_foreach(struct deltas *deltas, delta_vrp_foreach_cb cb_vrp,
-    delta_router_key_foreach_cb cb_rk, delta_aspa_foreach_cb cb_aspa,
-    void *arg)
-{
-       int error;
-
-       error = __foreach_v4(&deltas->v4.adds, cb_vrp, arg, FLAG_ANNOUNCEMENT);
-       if (error)
-               return error;
-       error = __foreach_v4(&deltas->v4.removes, cb_vrp, arg, FLAG_WITHDRAWAL);
-       if (error)
-               return error;
-       error = __foreach_v6(&deltas->v6.adds, cb_vrp, arg, FLAG_ANNOUNCEMENT);
-       if (error)
-               return error;
-       error = __foreach_v6(&deltas->v6.removes, cb_vrp, arg, FLAG_WITHDRAWAL);
-       if (error)
-               return error;
-       error = __foreach_rk(&deltas->rk.adds, cb_rk, arg, FLAG_ANNOUNCEMENT);
-       if (error)
-               return error;
-       error = __foreach_rk(&deltas->rk.removes, cb_rk, arg, FLAG_WITHDRAWAL);
-       if (error)
-               return error;
-       error = __foreach_aspa(&deltas->aspa.adds, cb_aspa, arg, FLAG_ANNOUNCEMENT);
-       if (error)
-               return error;
-       return __foreach_aspa(&deltas->aspa.removes, cb_aspa, arg, FLAG_WITHDRAWAL);
-}
-
-void
-deltas_print(struct deltas *deltas)
-{
-       deltas_foreach(deltas, delta_vrp_print, delta_rk_print, delta_aspa_print, NULL);
-}
diff --git a/src/rtr/db/delta.h b/src/rtr/db/delta.h
deleted file mode 100644 (file)
index b7d943c..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef SRC_DELTA_H_
-#define SRC_DELTA_H_
-
-#include "types/aspa.h"
-#include "types/delta.h"
-
-struct deltas;
-
-struct deltas *deltas_create(void);
-void deltas_refget(struct deltas *);
-void deltas_refput(struct deltas *);
-
-void deltas_add_roa(struct deltas *, struct vrp const *, int,
-    char, unsigned int, unsigned int);
-void deltas_add_router_key(struct deltas *, struct router_key const *, int);
-void deltas_add_aspa(struct deltas *, struct aspa *, int);
-
-bool deltas_is_empty(struct deltas *);
-int deltas_foreach(struct deltas *, delta_vrp_foreach_cb,
-    delta_router_key_foreach_cb, delta_aspa_foreach_cb, void *);
-void deltas_print(struct deltas *);
-
-#endif /* SRC_DELTA_H_ */
diff --git a/src/rtr/db/deltas_array.c b/src/rtr/db/deltas_array.c
deleted file mode 100644 (file)
index 85025fe..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-#include "rtr/db/deltas_array.h"
-
-#include <errno.h>
-#include <limits.h>
-
-#include "alloc.h"
-#include "config.h"
-
-struct deltas_array {
-       struct deltas **array; /* It's a circular array. */
-       unsigned int len; /* Occupied slots. */
-       unsigned int last; /* Index of last added element. */
-};
-
-struct deltas_array *
-darray_create(void)
-{
-       struct deltas_array *result;
-
-       result = pmalloc(sizeof(struct deltas_array));
-
-       result->array = pcalloc(config_get_deltas_lifetime(),
-           sizeof(struct deltas *));
-       result->len = 0;
-       result->last = UINT_MAX;
-
-       return result;
-}
-
-void
-darray_destroy(struct deltas_array *darray)
-{
-       darray_clear(darray);
-       free(darray->array);
-       free(darray);
-}
-
-unsigned int
-darray_len(struct deltas_array *darray)
-{
-       return darray->len;
-}
-
-void
-darray_add(struct deltas_array *darray, struct deltas *addend)
-{
-       unsigned int first;
-
-       if (darray->len < config_get_deltas_lifetime()) {
-               darray->array[darray->len] = addend;
-               darray->last = darray->len;
-               darray->len++;
-       } else {
-               first = (darray->last == darray->len - 1)
-                   ? 0 : (darray->last + 1);
-               deltas_refput(darray->array[first]);
-               darray->array[first] = addend;
-               darray->last = first;
-       }
-}
-
-void
-darray_clear(struct deltas_array *darray)
-{
-       unsigned int i;
-       for (i = 0; i < darray->len; i++)
-               deltas_refput(darray->array[i]);
-       darray->len = 0;
-}
-
-int
-darray_foreach_since(struct deltas_array *darray, unsigned int from,
-    darray_foreach_cb cb, void *arg)
-{
-       unsigned int i;
-       unsigned int j;
-       int error;
-
-       if (from == 0)
-               return 0;
-       if (from > darray->len)
-               return -EINVAL;
-
-       i = darray->last - from + 1;
-       if (i > darray->len)
-               i += darray->len;
-
-       from += i;
-       for (; i < from; i++) {
-               j = (i >= darray->len) ? (i - darray->len) : i;
-               error = cb(darray->array[j], arg);
-               if (error)
-                       return error;
-       }
-
-       return 0;
-
-}
diff --git a/src/rtr/db/deltas_array.h b/src/rtr/db/deltas_array.h
deleted file mode 100644 (file)
index 65e687c..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef SRC_RTR_DB_DELTAS_ARRAY_H_
-#define SRC_RTR_DB_DELTAS_ARRAY_H_
-
-#include "rtr/db/delta.h"
-#include "types/serial.h"
-
-struct deltas_array;
-
-struct deltas_array *darray_create(void);
-void darray_destroy(struct deltas_array *);
-
-unsigned int darray_len(struct deltas_array *);
-void darray_add(struct deltas_array *, struct deltas *);
-void darray_clear(struct deltas_array *);
-
-typedef int (*darray_foreach_cb)(struct deltas *, void *);
-int darray_foreach_since(struct deltas_array *, serial_t from,
-    darray_foreach_cb, void *);
-
-#endif /* SRC_RTR_DB_DELTAS_ARRAY_H_ */
index 54755b41639f62d2f8376a0dd9cb69d54ec97ba3..724fc8ce92955e6cfa40ae84798d0ed3e6cb34b9 100644 (file)
 #include <errno.h>
 #include <time.h>
 
-#include "common.h"
 #include "config.h"
 #include "log.h"
 #include "object/tal.h"
 #include "output_printer.h"
-#include "rtr/db/deltas_array.h"
-#include "rtr/pdu.h"
+#include "rtr/db/db_table.h"
 #include "slurm/slurm_loader.h"
 
-struct vrp_node {
-       struct delta_vrp delta;
-       SLIST_ENTRY(vrp_node) next;
-};
-
-struct rk_node {
-       struct delta_router_key delta;
-       SLIST_ENTRY(rk_node) next;
-};
-
-struct aspa_node {
-       struct delta_aspa delta;
-       SLIST_ENTRY(aspa_node) next;
-};
-
-/** Sorted list to filter deltas */
-SLIST_HEAD(vrp_slist, vrp_node);
-SLIST_HEAD(rk_slist, rk_node);
-SLIST_HEAD(aspa_slist, aspa_node);
-
-struct sorted_lists {
-       struct vrp_slist prefixes;
-       struct rk_slist router_keys;
-       struct aspa_slist aspas;
-};
-
-struct state {
-       /**
-        * All the current valid ROAs.
-        *
-        * Can be NULL, so handle gracefully.
-        * (We use this to know we're supposed to generate a @deltas entry
-        * during the current iteration.)
-        */
-       struct db_table *base;
-       /** DB changes to @base over time. */
-       struct deltas_array *deltas;
-
-       /*
-        * Last valid SLURM applied to base.
-        *
-        * Doesn't need locking, because the only writer is also the only
-        * reader.
-        */
-       struct db_slurm *slurm;
-
-       /*
-        * This is the serial number of base.
-        *
-        * At least one RTR client implementation (Cloudflare's rpki-rtr-client)
-        * malfunctions if the validator uses zero as the first serial, so this
-        * value behaves as follows:
-        *
-        * serial = 0. After every successful validation cycle, serial++.
-        *
-        * Do not use this value to check whether we already finished our first
-        * validation. (Use base != NULL for that.) Zero is totally a valid
-        * serial, particularly when the integer wraps.
-        *
-        * TODO (fine) this should probably be moved to struct db_table.
-        */
-       serial_t serial;
-       uint16_t v0_session_id;
-       uint16_t v1_session_id;
-};
-
-static struct state state;
-
-/** Protects @state.base, @state.deltas and @state.serial. */
-static pthread_rwlock_t state_lock;
-
-int
-vrps_init(void)
-{
-       time_t now;
-       int error;
-
-       state.base = NULL;
-       state.deltas = darray_create();
-
-       /*
-        * Use the same start serial, the session ID will avoid
-        * "desynchronization" (more at RFC 6810 'Glossary' and
-        * 'Fields of a PDU')
-        */
-       state.serial = 0;
-
-       /* Get the bits that'll fit in session_id */
-       now = 0;
-       error = get_current_time(&now);
-       if (error)
-               goto revert_deltas;
-       state.v0_session_id = now & 0xFFFF;
-
-       /* Minus 1 to prevent same ID */
-       state.v1_session_id = (state.v0_session_id != 0)
-           ? (state.v0_session_id - 1)
-           : (0xFFFFu);
-
-       state.slurm = NULL;
-
-       error = pthread_rwlock_init(&state_lock, NULL);
-       if (error) {
-               pr_op_err("state pthread_rwlock_init() errored: %s",
-                   strerror(error));
-               goto revert_deltas;
-       }
-
-       return 0;
-
-revert_deltas:
-       darray_destroy(state.deltas);
-       return error;
-}
-
-void
-vrps_destroy(void)
-{
-       pthread_rwlock_destroy(&state_lock);
-
-       if (state.slurm != NULL)
-               db_slurm_destroy(state.slurm);
-
-       darray_destroy(state.deltas);
-       if (state.base != NULL)
-               db_table_destroy(state.base);
-}
-
 int
 handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix,
     uint8_t max_length, void *arg)
@@ -187,67 +57,30 @@ handle_aspa(struct aspa *aspa, void *arg)
  * - Downloads tree
  * - Validates tree
  * - Updates RTR state
- *
- * If the database changed, @changed will be true. Meant for RTR notificates.
  */
-static int
-__vrps_update(bool *changed)
+static struct db_table *
+__vrps_update(void)
 {
-       /*
-        * This function is the only writer, and it runs once at a time.
-        * Therefore, it's going to worry about write locking, but not read
-        * locking.
-        */
+       struct db_table *db;
 
-       struct db_table *old_base;
-       struct db_table *new_base;
-       struct deltas *new_deltas;
-       int error;
-
-       if (changed != NULL)
-               *changed = false;
-       old_base = state.base;
-       new_base = NULL;
-       new_deltas = NULL;
+       db = perform_standalone_validation();
+       if (!db)
+               return NULL;
 
-       new_base = perform_standalone_validation();
-       if (new_base == NULL)
-               return EINVAL;
-
-       error = slurm_apply(new_base, &state.slurm);
-       if (error) {
-               db_table_destroy(new_base);
-               return error;
-       }
+       if (slurm_apply(db) != 0)
+               goto fail;
 
-       /*
-        * This is done after the validation, not during it, to prevent
-        * duplicate ROAs.
-        */
-       output_print_data(new_base);
+       db_table_sort(db);
 
-       if (old_base != NULL) {
-               new_deltas = compute_deltas(old_base, new_base);
-               if (new_deltas == NULL) {
-                       db_table_destroy(new_base);
-                       return 0;
-               }
-       }
+       if (db_table_cache(db) != 0)
+               goto fail;
 
-       rwlock_write_lock(&state_lock);
-       state.base = new_base;
-       state.serial++;
-       if (new_deltas != NULL)
-               /* Ownership transferred */
-               darray_add(state.deltas, new_deltas);
-       rwlock_unlock(&state_lock);
+       output_print_data(db);
 
-       if (old_base != NULL)
-               db_table_destroy(old_base);
-       if (changed != NULL)
-               *changed = true;
+       return db;
 
-       return 0;
+fail:  db_table_destroy(db);
+       return NULL;
 }
 
 /*
@@ -261,307 +94,29 @@ __vrps_update(bool *changed)
  * TODO (#50) remove this wrapper once Prometheus is implemented
  */
 int
-vrps_update(bool *changed)
+vrps_update(struct rtr_metadata *rtr)
 {
+       struct db_table *db;
        time_t start, finish;
-       unsigned int roas, rks, aspas;
-       serial_t serial;
-       int error;
 
        start = time(NULL);
-       error = __vrps_update(changed);
+       db = __vrps_update();
        finish = time(NULL);
 
-       rwlock_read_lock(&state_lock);
-       if (state.base == NULL) {
-               roas = 0;
-               rks = 0;
-               aspas = 0;
-               serial = 0;
-       } else {
-               roas = db_table_roa_count(state.base);
-               rks = db_table_router_key_count(state.base);
-               aspas = db_table_aspa_count(state.base);
-               serial = state.serial;
-       }
-       rwlock_unlock(&state_lock);
-
        pr_op_info("Validation finished:");
-       pr_op_info("- Valid ROAs: %u", roas);
-       pr_op_info("- Valid Router Keys: %u", rks);
-       pr_op_info("- Valid ASPAs: %u", aspas);
+       pr_op_info("- Valid ROAs: %u", db_table_roa_count(db));
+       pr_op_info("- Valid Router Keys: %u", db_table_router_key_count(db));
+       pr_op_info("- Valid ASPAs: %u", db_table_aspa_count(db));
        if (config_get_mode() == SERVER)
-               pr_op_info("- Serial: %u", serial);
+               pr_op_info("- Serial: %u", db_table_serial(db));
        if (start != ((time_t) -1) && finish != ((time_t) -1))
                pr_op_info("- Real execution time: %.0lfs", difftime(finish, start));
 
-       return error;
-}
-
-/**
- * Please keep in mind that there is at least one errcode-aware caller. The most
- * important ones are
- * 1. 0: No errors.
- * 2. -EAGAIN: No data available; database still under construction.
- */
-int
-vrps_foreach_base(vrp_foreach_cb cb_roa, router_key_foreach_cb cb_rk,
-    aspa_foreach_cb cb_aspa, void *arg)
-{
-       int error;
-
-       error = rwlock_read_lock(&state_lock);
-       if (error)
-               return error;
-
-       if (state.base != NULL) {
-               error = db_table_foreach_roa(state.base, cb_roa, arg);
-               if (error)
-                       goto end;
-               error = db_table_foreach_router_key(state.base, cb_rk, arg);
-               if (error)
-                       goto end;
-               error = db_table_foreach_aspa(state.base, cb_aspa, arg);
-       } else
-               error = -EAGAIN;
-
-end:
-       rwlock_unlock(&state_lock);
-       return error;
-}
-
-/*
- * 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 delta_vrp const *delta, void *arg)
-{
-       struct sorted_lists *lists = arg;
-       struct vrp_node *ptr;
-       struct vrp_slist *filtered_vrps;
-
-       filtered_vrps = &lists->prefixes;
-       SLIST_FOREACH(ptr, filtered_vrps, next)
-               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;
-               }
-
-       ptr = pmalloc(sizeof(struct vrp_node));
-       ptr->delta = *delta;
-       SLIST_INSERT_HEAD(filtered_vrps, ptr, next);
-
-       return 0;
-}
-
-static int
-router_key_ovrd_remove(struct delta_router_key const *delta, void *arg)
-{
-       struct sorted_lists *lists = arg;
-       struct rk_node *ptr;
-       struct rk_slist *filtered_keys;
-       struct router_key const *key;
-
-       filtered_keys = &lists->router_keys;
-       SLIST_FOREACH(ptr, filtered_keys, next) {
-               key = &delta->router_key;
-               if (key->as == ptr->delta.router_key.as &&
-                   memcmp(key->ski, ptr->delta.router_key.ski, RK_SKI_LEN) == 0
-                   && memcmp(key->spk, ptr->delta.router_key.spk,
-                   RK_SPKI_LEN) == 0 &&
-                   delta->flags != ptr->delta.flags) {
-                       /* TODO (rk) Shouldn't it be replaced? */
-                       SLIST_REMOVE(filtered_keys, ptr, rk_node, next);
-                       free(ptr);
-                       return 0;
-               }
-       }
-
-       ptr = pmalloc(sizeof(struct rk_node));
-       ptr->delta = *delta;
-       SLIST_INSERT_HEAD(filtered_keys, ptr, next);
-
-       return 0;
-}
-
-static int
-aspa_ovrd_remove(struct delta_aspa const *delta, void *arg)
-{
-       struct sorted_lists *lists = arg;
-       struct aspa_node *ptr;
-       struct aspa_slist *filtered_aspas;
-
-       filtered_aspas = &lists->aspas;
-       SLIST_FOREACH(ptr, filtered_aspas, next) {
-               if (delta->aspa->customer == ptr->delta.aspa->customer) {
-                       SLIST_REMOVE(filtered_aspas, ptr, aspa_node, next);
-                       ptr->delta = *delta;
-                       SLIST_INSERT_HEAD(filtered_aspas, ptr, next);
-                       return 0;
-               }
+       if (rtr) {
+               rtr->session = db_table_session(db);
+               rtr->serial = db_table_serial(db);
        }
 
-       ptr = pmalloc(sizeof(struct aspa_node));
-       ptr->delta = *delta;
-       SLIST_INSERT_HEAD(filtered_aspas, ptr, next);
-       return 0;
-}
-
-static int
-__deltas_foreach(struct deltas *deltas, void *arg)
-{
-       return deltas_foreach(deltas, vrp_ovrd_remove, router_key_ovrd_remove,
-           aspa_ovrd_remove, arg);
-}
-
-/**
- * Runs @vrp_cb and @rk_cb on all the deltas from the database whose
- * serial > @from, excluding those that cancel each other.
- *
- * Please keep in mind that there is at least one errcode-aware caller. The most
- * important ones are
- * 1. 0: No errors.
- * 2. -EAGAIN: No data available; database still under construction.
- * 3. -ESRCH: @from was not found.
- */
-int
-vrps_foreach_delta_since(serial_t from, serial_t *to,
-    delta_vrp_foreach_cb vrp_cb, delta_router_key_foreach_cb rk_cb,
-    delta_aspa_foreach_cb aspa_cb, void *arg)
-{
-       struct sorted_lists filtered_lists;
-       struct vrp_node *vnode;
-       struct rk_node *rnode;
-       struct aspa_node *anode;
-       int error;
-
-       error = rwlock_read_lock(&state_lock);
-       if (error)
-               return error;
-
-       if (state.base == NULL) {
-               /* Database still under construction. */
-               rwlock_unlock(&state_lock);
-               return -EAGAIN;
-       }
-
-       if (from == state.serial) {
-               /* Client already has the latest serial. */
-               rwlock_unlock(&state_lock);
-               *to = from;
-               return 0;
-       }
-
-       /* if from < first serial */
-       if (serial_lt(from, state.serial - darray_len(state.deltas)))
-               goto cache_reset; /* Delta was already deleted. */
-       /* if from > last serial */
-       if (serial_lt(state.serial, from))
-               goto cache_reset; /* Serial is invalid. */
-
-       /*
-        * TODO (performance) this implementation is naive.
-        * Either use a hash set, or sort the resources.
-        * Also, deltas that share a serial do not need to be compared to each
-        * other. (Corollary: If there's one serial, no comparisons whatsoever
-        * need to be made.)
-        */
-
-       /*
-        * Filter: Remove entries that cancel each other.
-        * (We'll have to build a separate list because the database nodes
-        * are immutable.)
-        */
-       SLIST_INIT(&filtered_lists.prefixes);
-       SLIST_INIT(&filtered_lists.router_keys);
-       SLIST_INIT(&filtered_lists.aspas);
-
-       error = darray_foreach_since(state.deltas, state.serial - from,
-           __deltas_foreach, &filtered_lists);
-       if (error)
-               goto release_list;
-
-       /* Now do the corresponding callback on the filtered deltas */
-       SLIST_FOREACH(vnode, &filtered_lists.prefixes, next) {
-               error = vrp_cb(&vnode->delta, arg);
-               if (error)
-                       break;
-       }
-       SLIST_FOREACH(rnode, &filtered_lists.router_keys, next) {
-               error = rk_cb(&rnode->delta, arg);
-               if (error)
-                       break;
-       }
-       SLIST_FOREACH(anode, &filtered_lists.aspas, next) {
-               error = aspa_cb(&anode->delta, arg);
-               if (error)
-                       break;
-       }
-
-release_list:
-       while (!SLIST_EMPTY(&filtered_lists.prefixes)) {
-               vnode = filtered_lists.prefixes.slh_first;
-               SLIST_REMOVE_HEAD(&filtered_lists.prefixes, next);
-               free(vnode);
-       }
-       while (!SLIST_EMPTY(&filtered_lists.router_keys)) {
-               rnode = filtered_lists.router_keys.slh_first;
-               SLIST_REMOVE_HEAD(&filtered_lists.router_keys, next);
-               free(rnode);
-       }
-       while (!SLIST_EMPTY(&filtered_lists.aspas)) {
-               anode = filtered_lists.aspas.slh_first;
-               SLIST_REMOVE_HEAD(&filtered_lists.aspas, next);
-               free(anode);
-       }
-
-       *to = state.serial;
-       rwlock_unlock(&state_lock);
-       return 0;
-
-cache_reset:
-       rwlock_unlock(&state_lock);
-       return -ESRCH;
-}
-
-int
-get_last_serial_number(serial_t *result)
-{
-       int error;
-
-       error = rwlock_read_lock(&state_lock);
-       if (error)
-               return error;
-
-       if (state.base != NULL)
-               *result = state.serial;
-       else
-               error = -EAGAIN;
-
-       rwlock_unlock(&state_lock);
-
-       return error;
-}
-
-uint16_t
-get_current_session_id(uint8_t rtr_version)
-{
-       /*
-        * These values are constant after initialization,
-        * so locking isn't needed.
-        */
-       if (rtr_version == 1)
-               return state.v1_session_id;
-       return state.v0_session_id;
-}
-
-void
-vrps_print_base(void)
-{
-       vrps_foreach_base(vrp_print, router_key_print, aspa_print, NULL);
+       db_table_destroy(db);
+       return db ? 0 : EINVAL;
 }
index 4a9bd2cde373b68a21d8d0a6b03b76eb4d028aa4..351ac9afa214ba051012763681fc36891ae4a192 100644 (file)
@@ -8,27 +8,11 @@
  */
 
 #include "as_number.h"
+#include "rtr/meta.h"
 #include "types/address.h"
 #include "types/aspa.h"
-#include "types/delta.h"
-#include "types/serial.h"
 
-int vrps_init(void);
-void vrps_destroy(void);
-
-int vrps_update(bool *);
-
-/*
- * 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(vrp_foreach_cb, router_key_foreach_cb, aspa_foreach_cb,
-    void *);
-int vrps_foreach_delta_since(serial_t, serial_t *, delta_vrp_foreach_cb,
-    delta_router_key_foreach_cb, delta_aspa_foreach_cb, void *);
-int get_last_serial_number(serial_t *);
+int vrps_update(struct rtr_metadata *);
 
 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 *);
@@ -36,8 +20,4 @@ int handle_router_key(unsigned char const *, struct asn_range const *,
     unsigned char const *, void *);
 int handle_aspa(struct aspa *, void *);
 
-uint16_t get_current_session_id(uint8_t);
-
-void vrps_print_base(void);
-
 #endif /* SRC_VRPS_H_ */
diff --git a/src/rtr/meta.c b/src/rtr/meta.c
new file mode 100644 (file)
index 0000000..6e7d811
--- /dev/null
@@ -0,0 +1,182 @@
+#include "rtr/meta.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "alloc.h"
+#include "common.h"
+#include "config.h"
+#include "log.h"
+#include "file.h"
+
+/* TODO delete in Fort2 */
+char *
+rtr_filename(char const *a, char const *b)
+{
+       char const *root;
+       char *result;
+       size_t len;
+       int ret, error;
+
+       root = config_get_local_repository();
+
+       len = strlen(root) + strlen("/rtr") + 1;
+       if (a) {
+               len += 1 + strlen(a);
+               if (b)
+                       len += 1 + strlen(b);
+       }
+
+       result = pmalloc(len);
+
+       if (b)
+               ret = snprintf(result, len, "%s/rtr/%s/%s", root, a, b);
+       else if (a)
+               ret = snprintf(result, len, "%s/rtr/%s", root, a);
+       else
+               ret = snprintf(result, len, "%s/rtr", root);
+
+       if (ret < 0 || len <= ret) {
+               error = errno;
+               if (!error)
+                       error = EINVAL;
+               pr_crit("snprintf(): %s", strerror(error));
+       }
+
+       return result;
+}
+
+/* TODO delete in Fort2 */
+char *
+rtr_filename2(serial_t serial, char const *b)
+{
+       char const *root;
+       char *result;
+       size_t len;
+       int ret, error;
+
+       root = config_get_local_repository();
+
+       len = strlen(root) + strlen("/rtr/") + 10 + 1;
+       if (b)
+               len += 1 + strlen(b);
+       result = pmalloc(len);
+
+       ret = b
+           ? snprintf(result, len, "%s/rtr/%u/%s", root, serial, b)
+           : snprintf(result, len, "%s/rtr/%u", root, serial);
+       if (ret < 0 || len <= ret) {
+               error = errno;
+               if (!error)
+                       error = EINVAL;
+               pr_crit("snprintf(): %s", strerror(error));
+       }
+
+       return result;
+}
+
+void
+rtr_new_metadata(struct rtr_metadata *rtr)
+{
+       time_t now = 0x1234u;
+       get_current_time(&now);
+       rtr->session = now & 0xFFFFu;
+       rtr->serial = 0;
+}
+
+int
+rtr_save_metadata(struct rtr_metadata *rtr)
+{
+       char *filepath;
+       FILE *file;
+       int error = 0;
+
+       filepath = rtr_filename("metadata", NULL);
+
+       file = fopen(filepath, "w");
+       if (!file) {
+               error = errno;
+               pr_op_err("Cannot open '%s' for writing: %s",
+                   filepath, strerror(error));
+               free(filepath);
+               return error;
+       }
+
+       free(filepath);
+
+       if (fprintf(file, "session:%u serial:%u", rtr->session, rtr->serial) < 0)
+               error = pr_op_err("fprintf(serial) failed.");
+
+       fclose(file);
+       return error;
+}
+
+int
+rtr_load_metadata(struct rtr_metadata *rtr)
+{
+       char *filepath;
+       FILE *file;
+       unsigned int session;
+       unsigned int serial;
+       int error = 0;
+
+       filepath = rtr_filename("metadata", NULL);
+
+       file = fopen(filepath, "r");
+       if (!file) {
+               free(filepath);
+               return errno;
+       }
+
+       free(filepath);
+
+       if (fscanf(file, "session:%u serial:%u", &session, &serial) >= 2) {
+               rtr->session = session;
+               rtr->serial = serial;
+       } else {
+               error = EINVAL;
+       }
+
+       fclose(file);
+       return error;
+}
+
+int
+rtr_serial_stat(serial_t serial)
+{
+       char *path;
+       int ret;
+
+       path = rtr_filename2(serial, NULL);
+       ret = file_exists(path);
+       free(path);
+
+       return ret;
+}
+
+int
+rtr_open_file(serial_t serial, char const *basename, char const *mode,
+    FILE **result)
+{
+       char *path;
+       FILE *file;
+       int ret;
+
+       path = rtr_filename2(serial, basename);
+
+       file = fopen(path, mode);
+       if (!file) {
+               ret = errno;
+               pr_op_err("Cannot open '%s' in '%s' mode: %s",
+                   path, mode, strerror(ret));
+               free(path);
+               return ret;
+       }
+
+       free(path);
+
+       *result = file;
+       return 0;
+}
diff --git a/src/rtr/meta.h b/src/rtr/meta.h
new file mode 100644 (file)
index 0000000..67af12a
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef SRC_RTR_META_H_
+#define SRC_RTR_META_H_
+
+#include <stdio.h>
+#include "types/serial.h"
+
+struct rtr_metadata {
+       uint16_t session;
+
+       /*
+        * At least one RTR client implementation (Cloudflare's rpki-rtr-client)
+        * malfunctions if the validator uses zero as the first serial, so this
+        * value behaves as follows:
+        *
+        * serial = 0. After every successful validation cycle, serial++.
+        *
+        * Do not use this value to check whether we already finished our first
+        * validation. (Use base != NULL for that.) Zero is totally a valid
+        * serial, particularly when the integer wraps.
+        */
+       serial_t serial;
+};
+
+char *rtr_filename(char const *, char const *);
+char *rtr_filename2(serial_t, char const *);
+
+void rtr_new_metadata(struct rtr_metadata *);
+int rtr_save_metadata(struct rtr_metadata *);
+int rtr_load_metadata(struct rtr_metadata *);
+
+int rtr_serial_stat(serial_t serial);
+
+int rtr_open_file(serial_t, char const *, char const *, FILE **);
+
+#endif /* SRC_RTR_META_H_ */
index 39ea6b24994edba10103f89432a80ea46701d7f4..98f63eceafdb289abfae36a8fb9bf059537b7dbb 100644 (file)
 #include "rtr/pdu_handler.h"
 
+#include <stdio.h>
 #include <errno.h>
+#include <string.h>
 
+#include "alloc.h"
 #include "log.h"
-#include "rtr/db/vrps.h"
+#include "data_structure/common.h"
 #include "rtr/err_pdu.h"
 #include "rtr/pdu_sender.h"
 
-struct send_delta_args {
+struct rtr_stream {
        int fd;
-       uint8_t rtr_version;
-       bool cache_response_sent;
+       uint8_t ver; /* RTR version */
+
+       char const *type;
+       size_t rawlen;
+       int (*send)(struct rtr_stream *, unsigned char const *, int);
 };
 
+static uint32_t
+read_u32(unsigned char const *raw)
+{
+       return (((unsigned int)(raw[0])) << 24)
+            | (((unsigned int)(raw[1])) << 16)
+            | (((unsigned int)(raw[2])) <<  8)
+            | (((unsigned int)(raw[3])) <<  0);
+}
+
+static int
+send_vrp4(struct rtr_stream *rs, unsigned char const *raw, int flag)
+{
+       struct vrp vrp;
+
+       vrp.addr_fam = AF_INET;
+       vrp.asn = read_u32(raw);
+       memcpy(&vrp.prefix.v4, raw + 4, 4);
+       vrp.prefix_length = raw[8];
+       vrp.max_prefix_length = raw[9];
+
+       return send_prefix_pdu(rs->fd, rs->ver, &vrp, flag);
+}
+
+static int
+send_vrp6(struct rtr_stream *rs, unsigned char const *raw, int flag)
+{
+       struct vrp vrp;
+
+       vrp.addr_fam = AF_INET6;
+       vrp.asn = read_u32(raw);
+       memcpy(&vrp.prefix.v6, raw + 4, 16);
+       vrp.prefix_length = raw[20];
+       vrp.max_prefix_length = raw[21];
+
+       return send_prefix_pdu(rs->fd, rs->ver, &vrp, flag);
+}
+
+static int
+send_rk(struct rtr_stream *rs, unsigned char const *raw, int flag)
+{
+       struct router_key rk;
+
+       rk.as = read_u32(raw);
+       memcpy(rk.ski, raw + 4, RK_SKI_LEN);
+       memcpy(rk.spk, raw + 4 + RK_SKI_LEN, RK_SPKI_LEN);
+
+       return send_router_key_pdu(rs->fd, rs->ver, &rk, flag);
+}
+
 static int
-send_cache_response_maybe(struct send_delta_args *args)
+parse_providers(unsigned char const *hdr, FILE *file,
+    struct aspa_providers *providers)
 {
+       array_index i;
+       unsigned char buf[4];
        int error;
 
-       if (!args->cache_response_sent) {
-               error = send_cache_response_pdu(args->fd, args->rtr_version);
-               if (error)
-                       return error;
-               args->cache_response_sent = true;
+       providers->count = read_u32(hdr + 4);
+       providers->asids = pcalloc(sizeof(uint32_t), providers->count);
+
+       for (i = 0; i < providers->count; i++) {
+               if (fread(buf, 4, 1, file) == 1) {
+                       providers->asids[i] = read_u32(buf);
+               } else if (feof(file)) {
+                       error = pr_op_err("File ended prematurely");
+                       goto end;
+               } else if (ferror(file)) {
+                       error = errno;
+                       if (!error)
+                               error = EINVAL;
+                       pr_op_err("File read failure: %s", strerror(error));
+                       goto end;
+               }
        }
 
        return 0;
+
+end:   free(providers->asids);
+       return error;
 }
 
 static int
-send_delta_vrp(struct delta_vrp const *delta, void *arg)
+send_aspa_announce(int fd, uint8_t ver, unsigned char const *hdr, FILE *file)
 {
-       struct send_delta_args *args = arg;
+       struct aspa aspa = { 0 };
        int error;
 
-       error = send_cache_response_maybe(args);
+       aspa.customer = read_u32(hdr);
+       error = parse_providers(hdr, file, &aspa.providers);
        if (error)
                return error;
 
-       return send_prefix_pdu(args->fd, args->rtr_version, &delta->vrp,
-           delta->flags);
+       error = send_aspa_announce_pdu(fd, ver, &aspa);
+
+       free(aspa.providers.asids);
+       return error;
+}
+
+/* Throw away the providers list */
+static int
+skip_providers(unsigned char const *hdr, FILE *file)
+{
+       unsigned char buf[256];
+       unsigned int total; /* Total providers */
+       unsigned int want; /* Providers we want to read */
+       unsigned int red; /* Actual read providers */
+       int error;
+
+       for (total = read_u32(hdr + 4); total > 0; total -= red) {
+               want = (total < 64) ? total : 64;
+               red = fread(buf, 4, want, file);
+               if (want != red) {
+                       if (feof(file))
+                               return pr_op_err("File ended prematurely");
+                       if (ferror(file)) {
+                               error = errno;
+                               if (!error)
+                                       error = EINVAL;
+                               pr_op_err("File read failure: %s", strerror(error));
+                               return error;
+                       }
+               }
+       }
+
+       return 0;
 }
 
 static int
-send_delta_rk(struct delta_router_key const *delta, void *arg)
+send_aspa_withdraw(int fd, uint8_t ver, unsigned char const *hdr, FILE *file)
 {
-       struct send_delta_args *args = arg;
        int error;
 
-       error = send_cache_response_maybe(args);
+       error = skip_providers(hdr, file);
        if (error)
                return error;
 
-       return send_router_key_pdu(args->fd, args->rtr_version,
-           &delta->router_key, delta->flags);
+       return send_aspa_withdraw_pdu(fd, ver, read_u32(hdr));
+}
+
+static unsigned char const *
+next_chunk(FILE *file, unsigned char *buf, size_t size, int *error)
+{
+       int n;
+
+again: n = fread(buf, size, 1, file);
+       if (n < 1) {
+               if (ferror(file)) {
+                       *error = errno;
+                       if (!*error)
+                               *error = EINVAL;
+                       pr_op_err("File read failure: %s", strerror(*error));
+                       return NULL;
+               }
+               if (feof(file))
+                       return NULL;
+               goto again; /* Dead code, unless fread() is borked */
+       }
+
+       return buf;
+}
+
+static int
+send_serial(struct rtr_stream *stream, serial_t serial)
+{
+       FILE *file = 0;
+       unsigned char *buf;
+       unsigned char const *chunk;
+       int error;
+
+       buf = pmalloc(stream->rawlen);
+
+       error = rtr_open_file(serial, stream->type, "r", &file);
+       if (error)
+               goto end;
+
+       do {
+               chunk = next_chunk(file, buf, stream->rawlen, &error);
+               if (!chunk || error)
+                       break;
+               error = stream->send(stream, chunk, FLAG_ANNOUNCEMENT);
+       } while (!error);
+
+       fclose(file);
+end:   free(buf);
+       return error;
 }
 
 static int
-send_delta_aspa(struct delta_aspa const *delta, void *arg)
+send_aspas(int fd, uint8_t ver, serial_t serial)
 {
-       struct send_delta_args *args = arg;
+       FILE *file = 0;
+       unsigned char buf[8];
+       unsigned char const *chunk;
        int error;
 
-       error = send_cache_response_maybe(args);
+       error = rtr_open_file(serial, "aspa", "r", &file);
        if (error)
                return error;
 
-       return send_aspa_pdu(args->fd, args->rtr_version,
-           delta->aspa, delta->flags);
+       do {
+               chunk = next_chunk(file, buf, 8, &error);
+               if (!chunk || error)
+                       break;
+               error = send_aspa_announce(fd, ver, chunk, file);
+       } while (!error);
+
+       fclose(file);
+       return error;
 }
 
 int
-handle_serial_query_pdu(struct rtr_request *request)
+handle_reset_query_pdu(struct rtr_request *request)
 {
-       struct send_delta_args args;
-       serial_t final_serial;
+       struct rtr_metadata rtr;
+       struct rtr_stream stream;
        int error;
 
-       pr_op_debug("Serial Query. Request version/session/serial: %u/%u/%u",
-           request->pdu.rtr_version,
-           request->pdu.obj.sq.session_id,
-           request->pdu.obj.sq.serial_number);
+       pr_op_debug("Reset Query. Request version: %u",
+           request->pdu.rtr_version);
+
+       stream.fd = request->fd;
+       stream.ver = request->pdu.rtr_version;
 
-       args.fd = request->fd;
-       args.rtr_version = request->pdu.rtr_version;
-       args.cache_response_sent = false;
-
-       /*
-        * 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 (request->pdu.obj.sq.session_id != get_current_session_id(args.rtr_version))
-               return err_pdu_send_corrupt_data(args.fd, args.rtr_version,
-                       &request->pdu.raw, "Session ID doesn't match.");
-
-       /*
-        * 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.
-        */
-
-       error = vrps_foreach_delta_since(request->pdu.obj.sq.serial_number,
-           &final_serial, send_delta_vrp, send_delta_rk, send_delta_aspa,
-           &args);
+       error = rtr_load_metadata(&rtr);
        switch (error) {
        case 0:
-               /*
-                * https://tools.ietf.org/html/rfc6810#section-6.2
-                *
-                * These functions presently only fail on writes, allocations
-                * and programming errors. Best avoid error PDUs.
-                */
-               if (!args.cache_response_sent) {
-                       error = send_cache_response_pdu(args.fd,
-                           args.rtr_version);
-                       if (error)
-                               return error;
-               }
-               return send_end_of_data_pdu(args.fd, args.rtr_version,
-                   final_serial);
-       case -EAGAIN: /* Database still under construction */
-               return err_pdu_send_no_data_available(args.fd, args.rtr_version);
-       case -ESRCH: /* Invalid serial */
-               /* https://tools.ietf.org/html/rfc6810#section-6.3 */
-               return send_cache_reset_pdu(args.fd, args.rtr_version);
-       case -ENOMEM: /* Memory allocation failure */
-               enomem_panic();
-       case EAGAIN: /* Too many threads */
-               /*
-                * I think this should be more of a "try again" thing, but
-                * RTR does not provide a code for that. Just fall through.
-                */
                break;
+       case ENOENT:
+               return err_pdu_send_no_data_available(stream.fd, stream.ver);
+       default:
+               goto internal_error;
        }
 
-       return err_pdu_send_internal_error(args.fd, args.rtr_version);
-}
+       pdustream_set_session(request->stream, rtr.session);
 
-struct base_roa_args {
-       bool started;
-       int fd;
-       uint8_t version;
-};
+       error = send_cache_response_pdu(stream.fd, stream.ver, rtr.session);
+       if (error)
+               return error;
 
-static int
-send_base_roa(struct vrp const *vrp, void *arg)
-{
-       struct base_roa_args *args = arg;
-       int error;
+       stream.type = "vrp4";
+       stream.rawlen = 10;
+       stream.send = send_vrp4;
+       error = send_serial(&stream, rtr.serial);
+       if (error)
+               goto internal_error;
+
+       stream.type = "vrp6";
+       stream.rawlen = 22;
+       stream.send = send_vrp6;
+       error = send_serial(&stream, rtr.serial);
+       if (error)
+               goto internal_error;
+
+       if (stream.ver >= RTR_V1) {
+               stream.type = "rk";
+               stream.rawlen = 4 + RK_SKI_LEN + RK_SPKI_LEN;
+               stream.send = send_rk;
+               error = send_serial(&stream, rtr.serial);
+               if (error)
+                       goto internal_error;
+       }
 
-       if (!args->started) {
-               error = send_cache_response_pdu(args->fd, args->version);
+       if (stream.ver >= RTR_V2) {
+               error = send_aspas(stream.fd, stream.ver, rtr.serial);
                if (error)
-                       return error;
-               args->started = true;
+                       goto internal_error;
        }
 
-       return send_prefix_pdu(args->fd, args->version, vrp, FLAG_ANNOUNCEMENT);
+       return send_end_of_data_pdu(stream.fd, stream.ver, rtr.session, rtr.serial);
+
+internal_error:
+       return err_pdu_send_internal_error(stream.fd, stream.ver);
 }
 
 static int
-send_base_router_key(struct router_key const *key, void *arg)
+send_delta(struct rtr_stream *rs, serial_t oserial, serial_t nserial)
 {
-       struct base_roa_args *args = arg;
+       FILE *ofile = NULL;
+       FILE *nfile = NULL;
+       unsigned char *buf1;
+       unsigned char *buf2;
+       unsigned char const *ochunk;
+       unsigned char const *nchunk;
+       int cmp;
        int error;
 
-       if (!args->started) {
-               error = send_cache_response_pdu(args->fd, args->version);
+       buf1 = pmalloc(rs->rawlen);
+       buf2 = pmalloc(rs->rawlen);
+
+       error = rtr_open_file(oserial, rs->type, "r", &ofile);
+       if (error)
+               goto end;
+       error = rtr_open_file(nserial, rs->type, "r", &nfile);
+       if (error)
+               goto end;
+
+       ochunk = next_chunk(ofile, buf1, rs->rawlen, &error);
+       if (error)
+               goto end;
+       nchunk = next_chunk(nfile, buf2, rs->rawlen, &error);
+       if (error)
+               goto end;
+
+       while (ochunk && nchunk) {
+               cmp = memcmp(ochunk, nchunk, rs->rawlen);
+               if (cmp < 0) {
+                       error = rs->send(rs, ochunk, FLAG_WITHDRAWAL);
+                       if (error)
+                               goto end;
+                       ochunk = next_chunk(ofile, buf1, rs->rawlen, &error);
+                       if (error)
+                               goto end;
+
+               } else if (cmp > 0) {
+                       error = rs->send(rs, nchunk, FLAG_ANNOUNCEMENT);
+                       if (error)
+                               goto end;
+                       nchunk = next_chunk(nfile, buf2, rs->rawlen, &error);
+                       if (error)
+                               goto end;
+
+               } else {
+                       ochunk = next_chunk(ofile, buf1, rs->rawlen, &error);
+                       if (error)
+                               goto end;
+                       nchunk = next_chunk(nfile, buf2, rs->rawlen, &error);
+                       if (error)
+                               goto end;
+               }
+       }
+
+       while (ochunk) {
+               error = rs->send(rs, ochunk, FLAG_WITHDRAWAL);
+               if (error)
+                       goto end;
+               ochunk = next_chunk(ofile, buf1, rs->rawlen, &error);
+               if (error)
+                       goto end;
+       }
+
+       while (nchunk) {
+               error = rs->send(rs, nchunk, FLAG_ANNOUNCEMENT);
                if (error)
-                       return error;
-               args->started = true;
+                       goto end;
+               nchunk = next_chunk(nfile, buf2, rs->rawlen, &error);
+               if (error)
+                       goto end;
        }
 
-       return send_router_key_pdu(args->fd, args->version, key,
-           FLAG_ANNOUNCEMENT);
+end:   if (nfile) fclose(nfile);
+       if (ofile) fclose(ofile);
+       free(buf2);
+       free(buf1);
+       return error;
 }
 
 static int
-send_base_aspa(struct aspa const *aspa, void *arg)
+send_aspa_delta(int fd, uint8_t ver, serial_t oserial, serial_t nserial)
 {
-       struct base_roa_args *args = arg;
+       FILE *ofile = NULL;
+       FILE *nfile = NULL;
+       unsigned char buf1[8];
+       unsigned char buf2[8];
+       unsigned char const *ochunk;
+       unsigned char const *nchunk;
+       struct aspa_providers oprovs;
+       struct aspa_providers nprovs;
+       int cmp;
+       struct aspa aspa;
        int error;
 
-       if (!args->started) {
-               error = send_cache_response_pdu(args->fd, args->version);
+       error = rtr_open_file(oserial, "aspa", "r", &ofile);
+       if (error)
+               return error;
+       error = rtr_open_file(nserial, "aspa", "r", &nfile);
+       if (error)
+               goto end;
+
+       ochunk = next_chunk(ofile, buf1, 8, &error);
+       if (error)
+               goto end;
+       nchunk = next_chunk(nfile, buf2, 8, &error);
+       if (error)
+               goto end;
+
+       while (ochunk && nchunk) {
+               cmp = memcmp(ochunk, nchunk, 4); /* AS only */
+               if (cmp < 0) {
+                       error = send_aspa_withdraw(fd, ver, ochunk, ofile);
+                       if (error)
+                               goto end;
+                       ochunk = next_chunk(ofile, buf1, 8, &error);
+                       if (error)
+                               goto end;
+               } else if (cmp > 0) {
+                       error = send_aspa_announce(fd, ver, nchunk, nfile);
+                       if (error)
+                               goto end;
+                       nchunk = next_chunk(nfile, buf2, 8, &error);
+                       if (error)
+                               goto end;
+               } else {
+                       error = parse_providers(ochunk, ofile, &oprovs);
+                       if (error)
+                               goto end;
+                       error = parse_providers(nchunk, nfile, &nprovs);
+                       if (error) {
+                               free(oprovs.asids);
+                               goto end;
+                       }
+
+                       if (!providers_equal(&oprovs, &nprovs)) {
+                               aspa.customer = read_u32(nchunk);
+                               aspa.providers = nprovs;
+                               error = send_aspa_announce_pdu(fd, ver, &aspa);
+                               if (error) {
+                                       free(oprovs.asids);
+                                       free(nprovs.asids);
+                                       goto end;
+                               }
+                       }
+
+                       free(oprovs.asids);
+                       free(nprovs.asids);
+
+                       ochunk = next_chunk(ofile, buf1, 8, &error);
+                       if (error)
+                               goto end;
+                       nchunk = next_chunk(nfile, buf2, 8, &error);
+                       if (error)
+                               goto end;
+               }
+       }
+
+       while (ochunk) {
+               error = send_aspa_withdraw(fd, ver, ochunk, ofile);
+               if (error)
+                       goto end;
+               ochunk = next_chunk(ofile, buf1, 8, &error);
+               if (error)
+                       goto end;
+       }
+
+       while (nchunk) {
+               error = send_aspa_announce(fd, ver, nchunk, nfile);
+               if (error)
+                       goto end;
+               nchunk = next_chunk(nfile, buf2, 8, &error);
                if (error)
-                       return error;
-               args->started = true;
+                       goto end;
        }
 
-       return send_aspa_pdu(args->fd, args->version, aspa, FLAG_ANNOUNCEMENT);
+end:   if (ofile) fclose(ofile);
+       if (nfile) fclose(nfile);
+       return error;
 }
 
 int
-handle_reset_query_pdu(struct rtr_request *request)
+handle_serial_query_pdu(struct rtr_request *request)
 {
-       struct base_roa_args args;
-       serial_t current_serial;
+       struct rtr_metadata rtr;
+       uint16_t stream_session;
+       serial_t oserial, nserial;
+       struct rtr_stream stream;
        int error;
 
-       args.started = false;
-       args.fd = request->fd;
-       args.version = request->pdu.rtr_version;
+       pr_op_debug("Serial Query. Request version/session/serial: %u/%u/%u",
+           request->pdu.rtr_version,
+           request->pdu.obj.sq.session_id,
+           request->pdu.obj.sq.serial_number);
+
+       stream.fd = request->fd;
+       stream.ver = request->pdu.rtr_version;
 
-       error = get_last_serial_number(&current_serial);
+       error = rtr_load_metadata(&rtr);
        switch (error) {
-       case 0:
-               break;
-       case -EAGAIN:
-               return err_pdu_send_no_data_available(args.fd, args.version);
-       default:
-               err_pdu_send_internal_error(args.fd, args.version);
-               return error;
+       case 0:      break;
+       case ENOENT: return err_pdu_send_no_data_available(stream.fd, stream.ver);
+       default:     goto internal_error;
        }
 
-       /*
-        * 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 thankfully, most clients are supposed to request far more serial
-        * queries than reset queries.
-        */
+       /* Request session vs negotiated session */
+       if (pdustream_get_session(request->stream, &stream_session, rtr.session))
+               if (request->pdu.obj.sq.session_id != stream_session)
+                       return err_pdu_send_corrupt_data(stream.fd, stream.ver,
+                           &request->pdu.raw, "Session ID doesn't match.");
+
+       /* Request session vs existing cache session */
+       if (request->pdu.obj.sq.session_id != rtr.session)
+               return send_cache_reset_pdu(stream.fd, stream.ver);
 
-       error = vrps_foreach_base(send_base_roa, send_base_router_key,
-           send_base_aspa, &args);
+       oserial = request->pdu.obj.sq.serial_number;
+       nserial = rtr.serial;
 
-       /* See handle_serial_query_pdu() for some comments. */
+       error = rtr_serial_stat(oserial);
        switch (error) {
-       case 0:
-               /* Assure that cache response is (or was) sent */
-               if (args.started)
-                       break;
-               error = send_cache_response_pdu(args.fd, args.version);
-               if (error)
-                       return error;
-               break;
-       case -EAGAIN:
-               return err_pdu_send_no_data_available(args.fd, args.version);
-       case EAGAIN:
-               err_pdu_send_internal_error(args.fd, args.version);
-               return error;
-       default:
-               /* Any other error must stop sending more PDUs */
+       case 0:      break;
+       case ENOENT: return send_cache_reset_pdu(stream.fd, stream.ver);
+       default:     goto internal_error;
+       }
+
+       error = send_cache_response_pdu(stream.fd, stream.ver, rtr.session);
+       if (error)
                return error;
+
+       stream.type = "vrp4";
+       stream.rawlen = 10;
+       stream.send = send_vrp4;
+       error = send_delta(&stream, oserial, nserial);
+       if (error)
+               goto internal_error;
+
+       stream.type = "vrp6";
+       stream.rawlen = 22;
+       stream.send = send_vrp6;
+       error = send_delta(&stream, oserial, nserial);
+       if (error)
+               goto internal_error;
+
+       if (stream.ver >= RTR_V1) {
+               stream.type = "rk";
+               stream.rawlen = 4 + RK_SKI_LEN + RK_SPKI_LEN;
+               stream.send = send_rk;
+               error = send_delta(&stream, oserial, nserial);
+               if (error)
+                       goto internal_error;
        }
 
-       return send_end_of_data_pdu(args.fd, args.version, current_serial);
+       if (stream.ver >= RTR_V2) {
+               error = send_aspa_delta(stream.fd, stream.ver, oserial, nserial);
+               if (error)
+                       goto internal_error;
+       }
+
+       return send_end_of_data_pdu(stream.fd, stream.ver, rtr.session, nserial);
+
+internal_error:
+       err_pdu_send_internal_error(stream.fd, stream.ver);
+       return error;
 }
index 406827a50ad707e4af46e0c73124b1ea11719de8..9d2c580ce5939e671a13fd789e436181c1e70787 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "rtr/pdu_stream.h"
 
-int handle_serial_query_pdu(struct rtr_request *);
 int handle_reset_query_pdu(struct rtr_request *);
+int handle_serial_query_pdu(struct rtr_request *);
 
 #endif /* SRC_RTR_PDU_HANDLER_H_ */
index 6e396e8b3bb3147dcb764ecfde73261d9ece4a0b..1d3ac7716bf201f5e948d8a3d7d805d734469249 100644 (file)
@@ -3,12 +3,12 @@
 #include <errno.h>
 #include <poll.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "alloc.h"
 #include "config.h"
 #include "data_structure/common.h"
 #include "log.h"
-#include "rtr/db/vrps.h"
 #include "rtr/primitive_writer.h"
 
 static unsigned char *
@@ -85,16 +85,15 @@ send_response(int fd, uint8_t pdu_type, unsigned char *data, size_t data_len)
 }
 
 int
-send_serial_notify_pdu(int fd, uint8_t version, serial_t start_serial)
+send_serial_notify_pdu(int fd, uint8_t version, struct rtr_metadata *meta)
 {
        static const uint8_t type = PDU_TYPE_SERIAL_NOTIFY;
        static const uint32_t len = RTRPDU_SERIAL_NOTIFY_LEN;
        unsigned char data[RTRPDU_SERIAL_NOTIFY_LEN];
        unsigned char *buf;
 
-       buf = serialize_hdr(data, version, type,
-           get_current_session_id(version), len);
-       buf = write_uint32(buf, start_serial);
+       buf = serialize_hdr(data, version, type, meta->session, len);
+       buf = write_uint32(buf, meta->serial);
 
        return send_response(fd, type, data, len);
 }
@@ -112,13 +111,13 @@ send_cache_reset_pdu(int fd, uint8_t version)
 }
 
 int
-send_cache_response_pdu(int fd, uint8_t version)
+send_cache_response_pdu(int fd, uint8_t version, uint16_t session)
 {
        static const uint8_t type = PDU_TYPE_CACHE_RESPONSE;
        static const uint32_t len = RTRPDU_CACHE_RESPONSE_LEN;
        unsigned char data[RTRPDU_CACHE_RESPONSE_LEN];
 
-       serialize_hdr(data, version, type, get_current_session_id(version), len);
+       serialize_hdr(data, version, type, session, len);
 
        return send_response(fd, type, data, len);
 }
@@ -199,55 +198,64 @@ send_router_key_pdu(int fd, uint8_t version,
 }
 
 int
-send_aspa_pdu(int fd, uint8_t version, struct aspa const *aspa, uint8_t flags)
+send_aspa_announce_pdu(int fd, uint8_t version, struct aspa const *aspa)
 {
        static const uint8_t type = PDU_TYPE_ASPA;
-       unsigned char data[1024];
+       unsigned char *buf, *loc;
+       size_t bufsize;
        array_index i;
-       unsigned char *buf;
-       int error;
+       int error = 0;
 
        if (version < RTR_V2)
                return 0;
 
-       if (flags & FLAG_ANNOUNCEMENT) {
-               buf = serialize_hdr(data, version, type, FLAG_ANNOUNCEMENT << 8,
-                   12 + 4 * aspa->providers.count);
-               buf = write_uint32(buf, aspa->customer);
+       bufsize = 12 + 4 * aspa->providers.count;
+       if (bufsize > 1024)
+               bufsize = 1024;
+       buf = pmalloc(bufsize);
 
-               for (i = 0; i < aspa->providers.count; i++) {
-                       buf = write_uint32(buf, aspa->providers.asids[i]);
+       loc = serialize_hdr(buf, version, type, FLAG_ANNOUNCEMENT << 8,
+           12 + 4 * aspa->providers.count);
+       loc = write_uint32(loc, aspa->customer);
 
-                       if (buf >= data + 1024) {
-                               error = send_response(fd, type, data, buf - data);
-                               if (error)
-                                       return error;
-                               buf = data;
-                       }
-               }
+       for (i = 0; i < aspa->providers.count; i++) {
+               loc = write_uint32(loc, aspa->providers.asids[i]);
 
-               if (buf > data) {
-                       error = send_response(fd, type, data, buf - data);
+               if (loc >= buf + bufsize) {
+                       error = send_response(fd, type, buf, loc - buf);
                        if (error)
-                               return error;
+                               goto end;
+                       loc = buf;
                }
+       }
 
-       } else {
-               buf = serialize_hdr(data, version, type, FLAG_WITHDRAWAL << 8, 12);
-               write_uint32(buf, aspa->customer);
-               error = send_response(fd, type, data, 12);
+       if (loc > buf) {
+               error = send_response(fd, type, buf, loc - buf);
                if (error)
-                       return error;
+                       goto end;
        }
 
+end:   free(buf);
+       return error;
+}
 
-       return 0;
+int
+send_aspa_withdraw_pdu(int fd, uint8_t version, uint32_t customer)
+{
+       static const uint8_t type = PDU_TYPE_ASPA;
+       unsigned char data[12];
+       unsigned char *buf;
+
+       buf = serialize_hdr(data, version, type, FLAG_WITHDRAWAL << 8, 12);
+       write_uint32(buf, customer);
+
+       return send_response(fd, type, data, 12);
 }
 
 #define MAX(a, b) ((a > b) ? a : b)
 
 int
-send_end_of_data_pdu(int fd, uint8_t version, serial_t end_serial)
+send_end_of_data_pdu(int fd, uint8_t version, uint16_t session, serial_t serial)
 {
        static const uint8_t type = PDU_TYPE_END_OF_DATA;
        unsigned char data[
@@ -259,16 +267,14 @@ send_end_of_data_pdu(int fd, uint8_t version, serial_t end_serial)
        switch (version) {
        case RTR_V0:
                len = RTRPDU_END_OF_DATA_V0_LEN;
-               buf = serialize_hdr(data, version, type,
-                   get_current_session_id(version), len);
-               buf = write_uint32(buf, end_serial);
+               buf = serialize_hdr(data, version, type, session, len);
+               buf = write_uint32(buf, serial);
                break;
        case RTR_V1:
        case RTR_V2:
                len = RTRPDU_END_OF_DATA_V1_LEN;
-               buf = serialize_hdr(data, version, type,
-                   get_current_session_id(version), len);
-               buf = write_uint32(buf, end_serial);
+               buf = serialize_hdr(data, version, type, session, len);
+               buf = write_uint32(buf, serial);
                buf = write_uint32(buf, config_get_interval_refresh());
                buf = write_uint32(buf, config_get_interval_retry());
                buf = write_uint32(buf, config_get_interval_expire());
index ea05dc1a175b9ff5d47eafd69beca4d1f6aecf27..eee6245746d1dbbb66e30f0343b4cd83869ec35b 100644 (file)
@@ -2,18 +2,23 @@
 #define SRC_RTR_PDU_SENDER_H_
 
 #include "rtr/pdu.h"
+#include "rtr/meta.h"
 #include "types/aspa.h"
 #include "types/router_key.h"
 #include "types/serial.h"
 #include "types/vrp.h"
 
-int send_serial_notify_pdu(int, uint8_t, serial_t);
+#define FLAG_WITHDRAWAL                0
+#define FLAG_ANNOUNCEMENT      1
+
+int send_serial_notify_pdu(int, uint8_t, struct rtr_metadata *);
 int send_cache_reset_pdu(int, uint8_t);
-int send_cache_response_pdu(int, uint8_t);
+int send_cache_response_pdu(int, uint8_t, uint16_t);
 int send_prefix_pdu(int, uint8_t, struct vrp const *, uint8_t);
 int send_router_key_pdu(int, uint8_t, struct router_key const *, uint8_t);
-int send_aspa_pdu(int, uint8_t, struct aspa const *, uint8_t);
-int send_end_of_data_pdu(int, uint8_t, serial_t);
+int send_aspa_announce_pdu(int, uint8_t, struct aspa const *);
+int send_aspa_withdraw_pdu(int, uint8_t, uint32_t);
+int send_end_of_data_pdu(int, uint8_t, uint16_t, serial_t);
 int send_error_report_pdu(int, uint8_t, uint16_t, struct rtr_buffer const *,
     char *);
 
index 498eec4c09e136e85ffef49d5641fbdd25fa1b5a..e1b050241ed03749e4bd66f2aacc4df454d1850e 100644 (file)
@@ -7,6 +7,7 @@
 #include <unistd.h>
 
 #include "alloc.h"
+#include "common.h"
 #include "log.h"
 #include "rtr/err_pdu.h"
 
@@ -31,6 +32,20 @@ struct pdu_stream {
        /* buffer's active bytes */
        unsigned char *start;
        unsigned char *end;
+
+       /*
+        * Negotiated session.
+        * We need this mess because the RTR RFCs specify that, if the client
+        * changes the session out of nowhere, we have to respond a Corrupt Data
+        * Error PDU instead of the usual Cache Reset.
+        * The routine that decides the session is the Reset Query handler, but
+        * the PDU stream is the only structure that can remember it, and it
+        * lives in a separate thread.
+        * FML.
+        */
+       pthread_mutex_t session_lock;
+       bool session_set;
+       uint16_t session;
 };
 
 struct pdu_header {
@@ -47,11 +62,19 @@ struct pdu_header {
 struct pdu_stream *pdustream_create(int fd, char const *addr)
 {
        struct pdu_stream *result;
+       int error;
 
        result = pmalloc(sizeof(struct pdu_stream));
        result->fd = fd;
        strcpy(result->addr, addr);
        result->rtr_version = -1;
+
+       error = pthread_mutex_init(&result->session_lock, NULL);
+       if (error)
+               pr_crit("pthread_mutex_init() failed: %s", strerror(error));
+       result->session_set = false;
+       result->session = 0;
+
        result->start = result->buffer;
        result->end = result->buffer;
 
@@ -62,6 +85,7 @@ void
 pdustream_destroy(struct pdu_stream **_stream)
 {
        struct pdu_stream *stream = *_stream;
+       pthread_mutex_destroy(&stream->session_lock);
        close(stream->fd);
        free(stream);
 }
@@ -440,6 +464,7 @@ create_request(struct pdu_stream *stream, struct pdu_header *hdr,
        result = pmalloc(sizeof(struct rtr_request));
        result->fd = stream->fd;
        strcpy(result->client_addr, stream->addr);
+       result->stream = stream;
        result->pdu.rtr_version = hdr->version;
        result->pdu.type = hdr->type;
        result->pdu.raw = *raw;
@@ -607,6 +632,34 @@ pdustream_version(struct pdu_stream *stream)
        return stream->rtr_version;
 }
 
+bool
+pdustream_get_session(struct pdu_stream *stream, uint16_t *session, uint16_t proposal)
+{
+       bool set;
+
+       mutex_lock(&stream->session_lock);
+       if (stream->session_set) {
+               set = true;
+               *session = stream->session;
+       } else {
+               set = false;
+               stream->session_set = true;
+               stream->session = proposal;
+       }
+       mutex_unlock(&stream->session_lock);
+
+       return set;
+}
+
+void
+pdustream_set_session(struct pdu_stream *stream, uint16_t session)
+{
+       mutex_lock(&stream->session_lock);
+       stream->session_set = true;
+       stream->session = session;
+       mutex_unlock(&stream->session_lock);
+}
+
 void
 rtreq_destroy(struct rtr_request *request)
 {
index e827aa9e443506bda64098a389211ac7816ca3c3..1f6b5005b02d7610976b1c4b40757dca6db4a83c 100644 (file)
@@ -11,6 +11,8 @@ struct rtr_request {
        int fd;
        char client_addr[INET6_ADDRSTRLEN];
 
+       struct pdu_stream *stream;
+
        struct {
                enum rtr_version rtr_version;
                enum pdu_type type;
@@ -41,6 +43,9 @@ int pdustream_fd(struct pdu_stream *);
 char const *pdustream_addr(struct pdu_stream *);
 int pdustream_version(struct pdu_stream *);
 
+bool pdustream_get_session(struct pdu_stream *, uint16_t *, uint16_t);
+void pdustream_set_session(struct pdu_stream *, uint16_t);
+
 void rtreq_destroy(struct rtr_request *);
 
 #endif /* SRC_RTR_PDU_STREAM_H_ */
index 64942205116e08b9ae3681c215f1b60ad72c4d97..e3fedaedb639b3cae746b669a35659e408868347 100644 (file)
@@ -4,16 +4,17 @@
 #include <fcntl.h>
 #include <poll.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "common.h"
 #include "config.h"
 #include "data_structure/array_list.h"
 #include "log.h"
-#include "rtr/db/vrps.h"
 #include "rtr/pdu_handler.h"
 #include "rtr/pdu_sender.h"
 #include "stats.h"
 #include "thread/thread_pool.h"
+#include "types/address.h"
 
 struct rtr_server {
        int fd;
@@ -724,19 +725,10 @@ void rtr_stop(void)
 }
 
 void
-rtr_notify(void)
+rtr_notify(struct rtr_metadata *rtr)
 {
-       serial_t serial;
        struct pdu_stream **client;
        int fd;
-       int error;
-
-       error = get_last_serial_number(&serial);
-       if (error) {
-               pr_op_info("Can't notify RTR clients: %d (%s)", error,
-                   strerror(abs(error)));
-               return;
-       }
 
        mutex_lock(&lock);
 
@@ -744,7 +736,7 @@ rtr_notify(void)
                fd = pdustream_fd(*client);
                if (fd != -1)
                        send_serial_notify_pdu(fd, pdustream_version(*client),
-                           serial);
+                           rtr);
        }
 
        mutex_unlock(&lock);
index f85aa16fa8b8f85025fcfb0fac641d955f15e62d..59af85964592dc6b26e0e97ebc996ae9b42d63ed 100644 (file)
@@ -1,9 +1,11 @@
 #ifndef RTR_RTR_H_
 #define RTR_RTR_H_
 
+#include "rtr/meta.h"
+
 int rtr_start(void);
 void rtr_stop(void);
 
-void rtr_notify(void);
+void rtr_notify(struct rtr_metadata *);
 
 #endif /* RTR_RTR_H_ */
index 883a57dd764ea78057462effcda305fdf2c2c87a..e1f897e65959818a45e125c37ffbfcf2f9312527 100644 (file)
@@ -266,39 +266,43 @@ success:
 }
 
 int
-slurm_apply(struct db_table *base, struct db_slurm **slurm)
+slurm_apply(struct db_table *base)
 {
+       struct db_slurm *slurm = NULL;
        struct slurm_parser_params params;
        int error;
 
        if (config_get_slurm() == NULL)
                return 0;
 
-       error = update_slurm(slurm);
+       error = update_slurm(&slurm);
        if (error)
                return error;
 
-       if (*slurm == NULL)
+       if (slurm == NULL)
                return 0;
 
        /* Ok, apply SLURM */
 
        params.db_table = base;
-       params.db_slurm = *slurm;
+       params.db_slurm = slurm;
 
        /* TODO invert this. SLURM rules are few, and base is massive. */
        error = db_table_foreach_roa(base, slurm_pfx_filters_apply, &params);
        if (error)
-               return error;
+               goto end;
 
        error = db_table_foreach_router_key(base, slurm_bgpsec_filters_apply,
            &params);
        if (error)
-               return error;
+               goto end;
 
        error = slurm_pfx_assertions_apply(&params);
        if (error)
-               return error;
+               goto end;
 
-       return slurm_bgpsec_assertions_apply(&params);
+       error = slurm_bgpsec_assertions_apply(&params);
+
+end:   db_slurm_destroy(slurm);
+       return error;
 }
index e23dfd33d95a001eacba62891dafd310ffb7efea..96face9458330c0c88eb4ad9f2474cc8591da054 100644 (file)
@@ -5,8 +5,7 @@
 #include "slurm/db_slurm.h"
 
 /*
- * Load the SLURM file/dir and try to apply it on @db_table, point to the SLURM
- * applied at @db_slurm.
+ * Load the SLURM file/dir and try to apply it on @db_table.
  *
  * Return error only when there's a major issue on the process (no memory,
  * SLURM loaded but something happened applying it).
@@ -14,9 +13,8 @@
  * Return 0 when there's no problem applying the SLURM:
  * - There's no SLURM configured
  * - The SLURM was successfully applied
- * - The @last_slurm was applied due to a syntax problem with a newer SLURM
- * - SLURM configured but couldn't be read (file doesn't exists, no permission)
+ * - SLURM configured but couldn't be read (file doesn't exist, no permission)
  */
-int slurm_apply(struct db_table *, struct db_slurm **);
+int slurm_apply(struct db_table *);
 
 #endif /* SRC_SLURM_SLURM_LOADER_H_ */
diff --git a/src/types/delta.c b/src/types/delta.c
deleted file mode 100644 (file)
index 1b9d895..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "types/delta.h"
-
-#include <stdio.h>
-
-static void
-print_flag(uint8_t flag)
-{
-       switch (flag) {
-       case FLAG_WITHDRAWAL:
-               printf("- DEL ");
-               break;
-       case FLAG_ANNOUNCEMENT:
-               printf("- ADD ");
-               break;
-       default:
-               printf("- (unknown)");
-               break;
-       }
-}
-
-int
-delta_vrp_print(struct delta_vrp const *delta, void *arg)
-{
-       print_flag(delta->flags);
-       return vrp_print(&delta->vrp, arg);
-}
-
-int
-delta_rk_print(struct delta_router_key const *delta, void *arg)
-{
-       print_flag(delta->flags);
-       return router_key_print(&delta->router_key, arg);
-}
-
-int
-delta_aspa_print(struct delta_aspa const *delta, void *arg)
-{
-       print_flag(delta->flags);
-       return aspa_print(delta->aspa, arg);
-}
diff --git a/src/types/delta.h b/src/types/delta.h
deleted file mode 100644 (file)
index c29f3c4..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef SRC_TYPES_DELTA_H_
-#define SRC_TYPES_DELTA_H_
-
-#include "types/aspa.h"
-#include "types/router_key.h"
-#include "types/vrp.h"
-
-#define FLAG_WITHDRAWAL                0
-#define FLAG_ANNOUNCEMENT      1
-
-struct delta_vrp {
-       struct vrp vrp;
-       uint8_t flags;
-};
-
-struct delta_router_key {
-       struct router_key router_key;
-       uint8_t flags;
-};
-
-struct delta_aspa {
-       struct aspa *aspa;
-       uint8_t flags;
-};
-
-typedef int (*delta_vrp_foreach_cb)(struct delta_vrp const *, void *);
-typedef int (*delta_router_key_foreach_cb)(struct delta_router_key const *,
-    void *);
-typedef int (*delta_aspa_foreach_cb)(struct delta_aspa const *, void *);
-
-int delta_vrp_print(struct delta_vrp const *, void *);
-int delta_rk_print(struct delta_router_key const *, void *);
-int delta_aspa_print(struct delta_aspa const *, void *);
-
-#endif /* SRC_TYPES_DELTA_H_ */
index 117789e7e56b2676283e3b55323eac0e46e529b2..a9b1683d455979aeae360ddfede4fffe3198ed32 100644 (file)
@@ -1,9 +1,6 @@
 #ifndef SRC_TYPES_SERIAL_H_
 #define SRC_TYPES_SERIAL_H_
 
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <netinet/in.h>
 #include <stdbool.h>
 #include <stdint.h>
 
index 1a182544eeed4f2e85091b210cbd28a183c0a476..740e36781024fa91a8aafbf2ae50b4f805351a62 100644 (file)
@@ -25,7 +25,6 @@ MY_LDADD = ${CHECK_LIBS} ${JANSSON_LIBS}
 check_PROGRAMS  = address.test
 check_PROGRAMS += cache.test
 check_PROGRAMS += db_table.test
-check_PROGRAMS += deltas_array.test
 check_PROGRAMS += json.test
 check_PROGRAMS += line_file.test
 check_PROGRAMS += pb.test
@@ -39,7 +38,6 @@ check_PROGRAMS += thread_pool.test
 check_PROGRAMS += uri.test
 check_PROGRAMS += uthash.test
 check_PROGRAMS += vcard.test
-check_PROGRAMS += vrps.test
 check_PROGRAMS += xml.test
 TESTS = ${check_PROGRAMS}
 
@@ -52,9 +50,6 @@ cache_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS}
 db_table_test_SOURCES = rtr/db/db_table_test.c
 db_table_test_LDADD = ${MY_LDADD}
 
-deltas_array_test_SOURCES = rtr/db/deltas_array_test.c
-deltas_array_test_LDADD = ${MY_LDADD}
-
 json_test_SOURCES = json_util_test.c
 json_test_LDADD = ${MY_LDADD}
 
@@ -94,9 +89,6 @@ uthash_test_LDADD = ${MY_LDADD}
 vcard_test_SOURCES = vcard_test.c
 vcard_test_LDADD = ${MY_LDADD}
 
-vrps_test_SOURCES = rtr/db/vrps_test.c
-vrps_test_LDADD = ${MY_LDADD} ${JANSSON_LIBS}
-
 xml_test_SOURCES = xml_test.c
 xml_test_LDADD = ${MY_LDADD} ${XML2_LIBS}
 
@@ -104,7 +96,6 @@ EXTRA_DIST  = mock.c mock.h
 EXTRA_DIST += line_file/core.txt
 EXTRA_DIST += line_file/empty.txt
 EXTRA_DIST += line_file/error.txt
-EXTRA_DIST += rtr/db/rtr_db_mock.c
 EXTRA_DIST += tal/lacnic.tal
 EXTRA_DIST += xml/notification.xml
 
index a16833596080b21c12832bf97e47c50a203ea9c2..3bcd93886d63d7b36e80b7719babecfc112b342f 100644 (file)
@@ -3,13 +3,13 @@
 
 #include "alloc.c"
 #include "common.c"
+#include "file.c"
 #include "mock.c"
 #include "types/address.c"
 #include "types/aspa.c"
-#include "types/delta.c"
 #include "types/router_key.c"
+#include "types/serial.c"
 #include "types/vrp.c"
-#include "rtr/db/delta.c"
 #include "rtr/db/db_table.c"
 
 #define ADDR1 htonl(0xC0000201) /* 192.0.2.1 */
 static bool roas_found[TOTAL_ROAS];
 static unsigned int total_found;
 
+__MOCK_ABORT(config_get_deltas_lifetime, unsigned int, 0, void)
 __MOCK_ABORT(config_get_local_repository, char const *, "tmp/dbt", void)
 MOCK_UINT(config_get_max_aspa_providers, 10, void)
+MOCK_ABORT_VOID(rtr_new_metadata, struct rtr_metadata *m)
+MOCK_ABORT_INT(rtr_save_metadata, struct rtr_metadata *m)
+MOCK_ABORT_INT(rtr_load_metadata, struct rtr_metadata *m)
+MOCK_ABORT_INT(rtr_open_file, serial_t serial, char const *basename,
+    char const *mode, FILE **result)
+__MOCK_ABORT(rtr_filename, char *, NULL, char const *a, char const *b)
+__MOCK_ABORT(rtr_filename2, char *, NULL, serial_t serial, char const *b)
 
 static bool
 vrp_equals_v4(struct vrp const *vrp, uint8_t as, uint32_t addr,
diff --git a/test/rtr/db/deltas_array_test.c b/test/rtr/db/deltas_array_test.c
deleted file mode 100644 (file)
index d65ebe0..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-#include <check.h>
-#include <stdlib.h>
-
-#include "alloc.c"
-#include "mock.c"
-#include "types/address.c"
-#include "types/aspa.c"
-#include "types/delta.c"
-#include "types/router_key.c"
-#include "types/vrp.c"
-#include "rtr/db/delta.c"
-#include "rtr/db/deltas_array.c"
-
-#define TOTAL_CREATED 15
-static struct deltas *created[TOTAL_CREATED];
-
-unsigned int
-config_get_deltas_lifetime(void)
-{
-       return 5;
-}
-
-static int
-foreach_cb(struct deltas *deltas, void *arg)
-{
-       unsigned int *next_index = arg;
-
-       ck_assert_ptr_eq(created[*next_index], deltas);
-       (*next_index)++;
-
-       return 0;
-}
-
-static void
-test_foreach(struct deltas_array *darray, unsigned int total,
-    unsigned int offset)
-{
-       unsigned int next_index;
-       unsigned int i;
-
-       for (i = 0; i <= total; i++) {
-               next_index = total - i + offset;
-               ck_assert_int_eq(0, darray_foreach_since(darray, i,
-                   foreach_cb, &next_index));
-               ck_assert_uint_eq(total + offset, next_index);
-       }
-
-       ck_assert_int_eq(-EINVAL, darray_foreach_since(darray, total + 1,
-           foreach_cb, &next_index));
-}
-
-START_TEST(add_only)
-{
-       struct deltas_array *darray;
-       unsigned int i;
-
-       darray = darray_create();
-       ck_assert_ptr_ne(NULL, darray);
-
-       for (i = 0; i < TOTAL_CREATED; i++) {
-               created[i] = deltas_create();
-               ck_assert_ptr_ne(NULL, created[i]);
-       }
-
-       test_foreach(darray, 0, 0);
-
-       darray_add(darray, created[0]);
-       test_foreach(darray, 1, 0);
-
-       darray_add(darray, created[1]);
-       test_foreach(darray, 2, 0);
-
-       darray_add(darray, created[2]);
-       test_foreach(darray, 3, 0);
-
-       darray_add(darray, created[3]);
-       test_foreach(darray, 4, 0);
-
-       for (i = 4; i < TOTAL_CREATED; i++) {
-               darray_add(darray, created[i]);
-               test_foreach(darray, 5, i - 4);
-       }
-}
-END_TEST
-
-static Suite *address_load_suite(void)
-{
-       Suite *suite;
-       TCase *core;
-
-       core = tcase_create("Core");
-       tcase_add_test(core, add_only);
-
-       suite = suite_create("Deltas Array");
-       suite_add_tcase(suite, core);
-       return suite;
-}
-
-int main(void)
-{
-       Suite *suite;
-       SRunner *runner;
-       int tests_failed;
-
-       suite = address_load_suite();
-
-       runner = srunner_create(suite);
-       srunner_run_all(runner, CK_NORMAL);
-       tests_failed = srunner_ntests_failed(runner);
-       srunner_free(runner);
-
-       return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-}
diff --git a/test/rtr/db/rtr_db_mock.c b/test/rtr/db/rtr_db_mock.c
deleted file mode 100644 (file)
index 9c7809a..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-#include <check.h>
-#include "object/tal.h"
-
-#include "types/address.c"
-
-static unsigned char db_imp_ski[] = {
-    0x0e, 0xe9, 0x6a, 0x8e, 0x2f, 0xac, 0x50, 0xce, 0x6c, 0x5f,
-    0x93, 0x3e, 0xde, 0x6a, 0xa7, 0x80, 0xa6, 0x85, 0x0e, 0x31 };
-
-static unsigned char db_imp_spk[] = {
-    0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce,
-    0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
-    0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xfa, 0xb9, 0x12,
-    0x2d, 0x79, 0x4f, 0xa4, 0xbf, 0xe6, 0xf8, 0xbe, 0xc2, 0x7c,
-    0x27, 0xca, 0xae, 0xfd, 0x45, 0x1e, 0xb3, 0x39, 0xe4, 0x5b,
-    0x08, 0x73, 0xc7, 0xcc, 0x96, 0x78, 0xc7, 0x13, 0xa6, 0x39,
-    0x9d, 0x3b, 0x82, 0x9f, 0x75, 0x20, 0x59, 0xf0, 0x95, 0xea,
-    0xc6, 0x2e, 0x19, 0x46, 0x73, 0x3d, 0x9d, 0x04, 0xcb, 0xa0,
-    0x2f, 0x7b, 0x39, 0x9f, 0x70, 0x42, 0xd4, 0x07, 0xce, 0xde,
-    0x04 };
-
-static int serial = 1;
-
-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;
-       in6_addr_init(&prefix.addr, 0x20010DB8u, 0, 0, 0);
-       prefix.len = 96;
-       ck_assert_int_eq(0, handler->handle_roa_v6(as, &prefix, 120,
-           handler->arg));
-}
-
-static void
-add_rk(struct validation_handler *handler, uint32_t as)
-{
-       struct asn_range range = { .min = as, .max = as };
-       ck_assert_int_eq(0, handler->handle_router_key(db_imp_ski, &range,
-           db_imp_spk, handler->arg));
-}
-
-static void
-__add_aspa(struct validation_handler *handler, uint32_t customer)
-{
-       struct aspa *aspa;
-
-       aspa = pmalloc(sizeof(struct aspa));
-       aspa->customer = customer;
-       aspa->providers.asids = pcalloc(3, sizeof(uint32_t));
-       aspa->providers.asids[0] = 100;
-       aspa->providers.asids[1] = 200;
-       aspa->providers.asids[2] = 300;
-       aspa->providers.count = 3;
-       aspa->refs = 0;
-
-       ck_assert_int_eq(0, handler->handle_aspa(aspa, handler->arg));
-}
-
-static int
-__handle_roa_v4(uint32_t as, struct ipv4_prefix const *prefix,
-    uint8_t max_length, void *arg)
-{
-       return rtrhandler_handle_roa_v4(arg, as, prefix, max_length);
-}
-
-static int
-__handle_roa_v6(uint32_t as, struct ipv6_prefix const *prefix,
-    uint8_t max_length, void *arg)
-{
-       return rtrhandler_handle_roa_v6(arg, as, prefix, max_length);
-}
-
-static int
-__handle_router_key(unsigned char const *ski, struct asn_range const *range,
-    unsigned char const *spk, void *arg)
-{
-       uint64_t as;
-       int error;
-
-       for (as = range->min; as <= range->max; as++) {
-               error = rtrhandler_handle_router_key(arg, ski, as, spk);
-               if (error)
-                       return error;
-       }
-
-       return 0;
-}
-
-static int
-__handle_aspa(struct aspa *aspa, void *arg)
-{
-       return rtrhandler_handle_aspa(arg, aspa);
-}
-
-struct db_table *
-perform_standalone_validation(void)
-{
-       struct validation_handler handler;
-
-       handler.handle_roa_v4 = __handle_roa_v4;
-       handler.handle_roa_v6 = __handle_roa_v6;
-       handler.handle_router_key = __handle_router_key;
-       handler.handle_aspa = __handle_aspa;
-       handler.arg = db_table_create();
-
-       switch (serial) {
-       case 1:
-               add_v4(&handler, 0);
-               add_v6(&handler, 0);
-               add_rk(&handler, 0);
-               __add_aspa(&handler, 0);
-               break;
-       case 2:
-               add_v4(&handler, 0);
-               add_v6(&handler, 0);
-               add_rk(&handler, 0);
-               __add_aspa(&handler, 0);
-               add_v4(&handler, 1);
-               add_v6(&handler, 1);
-               add_rk(&handler, 1);
-               __add_aspa(&handler, 1);
-               break;
-       case 3:
-               add_v4(&handler, 1);
-               add_v6(&handler, 1);
-               add_rk(&handler, 1);
-               __add_aspa(&handler, 1);
-               break;
-       case 4:
-               add_v4(&handler, 0);
-               add_v6(&handler, 0);
-               add_rk(&handler, 0);
-               __add_aspa(&handler, 0);
-               break;
-       default:
-               ck_abort_msg("perform_standalone_validation() was called too many times (%d).",
-                   serial);
-       }
-
-       serial++;
-       return handler.arg;
-}
diff --git a/test/rtr/db/vrps_test.c b/test/rtr/db/vrps_test.c
deleted file mode 100644 (file)
index b3d0f4d..0000000
+++ /dev/null
@@ -1,532 +0,0 @@
-#include <check.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-#include "crypto/base64.c"
-#include "algorithm.c"
-#include "alloc.c"
-#include "common.c"
-#include "file.c"
-#include "json_util.c"
-#include "mock.c"
-#include "output_printer.c"
-#include "types/aspa.c"
-#include "types/delta.c"
-#include "types/router_key.c"
-#include "types/serial.c"
-#include "types/vrp.c"
-#include "rtr/db/delta.c"
-#include "rtr/db/deltas_array.c"
-#include "rtr/db/db_table.c"
-#include "rtr/db/rtr_db_mock.c"
-#include "rtr/db/vrps.c"
-#include "slurm/db_slurm.c"
-#include "slurm/slurm_loader.c"
-#include "slurm/slurm_parser.c"
-#include "thread/thread_pool.c"
-
-/* -- Expected database descriptors -- */
-
-/*
- * BASE
- * 0: IPv4, ASN 0
- * 1: IPv4, ASN 1
- * 2: IPv6, ASN 0
- * 3: IPv6, ASN 1
- * 4: Router key, ASN 0
- * 5: Router key, ASN 1
- * 6: ASPA, customer 0
- * 7: ASPA, customer 1
- */
-static const bool iteration1_base[] = { 1, 0, 1, 0, 1, 0, 1, 0 };
-static const bool iteration2_base[] = { 1, 1, 1, 1, 1, 1, 1, 1 };
-static const bool iteration3_base[] = { 0, 1, 0, 1, 0, 1, 0, 1 };
-static const bool iteration4_base[] = { 1, 0, 1, 0, 1, 0, 1, 0 };
-
-/*
- * DELTA
- * 0: Withdrawal, IPv4, ASN 0    8: Announcement, IPv4, ASN 0
- * 1: Withdrawal, IPv4, ASN 1    9: Announcement, IPv4, ASN 1
- * 2: Withdrawal, IPv6, ASN 0   10: Announcement, IPv6, ASN 0
- * 3: Withdrawal, IPv6, ASN 1   11: Announcement, IPv6, ASN 1
- * 4: Withdrawal, RK,   ASN 0   12: Announcement, RK,   ASN 0
- * 5: Withdrawal, RK,   ASN 1   13: Announcement, RK,   ASN 1
- * 6: Withdrawal, ASPA, ASN 0   14: Announcement, ASPA, ASN 0
- * 7: Withdrawal, ASPA, ASN 1   15: Announcement, ASPA, ASN 1
- */
-
-static const bool deltas_1to1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
-static const bool deltas_1to2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1 };
-static const bool deltas_2to2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
-static const bool deltas_1to3[] = { 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1 };
-static const bool deltas_2to3[] = { 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-static const bool deltas_3to3[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
-static const bool deltas_1to4[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-static const bool deltas_2to4[] = { 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
-static const bool deltas_3to4[] = { 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0 };
-static const bool deltas_4to4[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
-/* Mocks */
-
-static unsigned int deltas_lifetime = 5;
-
-MOCK_UINT(config_get_deltas_lifetime, deltas_lifetime, void)
-MOCK_ABORT_ENUM(config_get_output_format, output_format, void)
-MOCK_ABORT_INT(hash_local_file, char const *uri, unsigned char *result,
-    unsigned int *result_len)
-__MOCK_ABORT(config_get_local_repository, char const *, "tmp/vrps", void)
-MOCK(config_get_output_aspa, char const *, NULL, void)
-MOCK_UINT(config_get_max_aspa_providers, 10, void)
-
-/* Test functions */
-
-static char const *
-vrpaddr2str(struct vrp const *vrp)
-{
-       switch (vrp->addr_fam) {
-       case AF_INET:
-               return v4addr2str(&vrp->prefix.v4);
-       case AF_INET6:
-               return v6addr2str(&vrp->prefix.v6);
-       }
-
-       return "unknown";
-}
-
-static int
-vrp_fail(struct vrp const *vrp, void *arg)
-{
-       ck_abort_msg("Expected no callbacks, got VRP %u/%s/%u/%u.",
-           vrp->asn, vrpaddr2str(vrp), vrp->prefix_length,
-           vrp->max_prefix_length);
-       return -EINVAL;
-}
-
-static int
-rk_fail(struct router_key const *key, void *arg)
-{
-       ck_abort_msg("Expected no callbacks, got RK %u.", key->as);
-       return -EINVAL;
-}
-
-static int
-aspa_fail(struct aspa const *aspa, void *arg)
-{
-       ck_abort_msg("Expected no callbacks, got ASPA %u.", aspa->customer);
-       return -EINVAL;
-}
-
-static int
-dvrp_fail(struct delta_vrp const *delta, void *arg)
-{
-       ck_abort_msg("Expected no callbacks, got Delta VRP %u/%s/%u/%u/%u.",
-           delta->vrp.asn, vrpaddr2str(&delta->vrp), delta->vrp.prefix_length,
-           delta->vrp.max_prefix_length, delta->flags);
-       return -EINVAL;
-}
-
-static int
-drk_fail(struct delta_router_key const *delta, void *arg)
-{
-       ck_abort_msg("Expected no callbacks, got Delta RK %u/%u.",
-           delta->router_key.as, delta->flags);
-       return -EINVAL;
-}
-
-static int
-daspa_fail(struct delta_aspa const *aspa, void *arg)
-{
-       ck_abort_msg("Expected no callbacks, got Delta ASPA %u/%u.",
-           aspa->aspa->customer, aspa->flags);
-       return -EINVAL;
-}
-
-static array_index
-get_vrp_index(struct vrp const *vrp)
-{
-       struct in6_addr tmp;
-       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:
-               in6_addr_init(&tmp, 0x20010DB8u, 0, 0, 0);
-               ck_assert(addr6_equals(&tmp, &vrp->prefix.v6));
-               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_rk_index(struct router_key const *rk)
-{
-       array_index i;
-
-       for (i = 0; i < RK_SKI_LEN; i++)
-               ck_assert_uint_eq(rk->ski[i], db_imp_ski[i]);
-
-       ck_assert_msg(rk->as <= 1, "Unexpected AS number: %u", rk->as);
-
-       for (i = 0; i < RK_SPKI_LEN; i++)
-               ck_assert_uint_eq(rk->spk[i], db_imp_spk[i]);
-
-       return rk->as + 4;
-}
-
-static array_index
-get_aspa_index(struct aspa const *aspa)
-{
-       ck_assert_msg(aspa->customer <= 1, "Unexpected AS number: %u",
-           aspa->customer);
-       return aspa->customer + 6;
-}
-
-static array_index
-get_delta_vrp_index(struct delta_vrp const *delta)
-{
-       array_index result;
-
-       result = get_vrp_index(&delta->vrp);
-       ck_assert_msg(delta->flags <= 1, "VRP Unexpected flags: %u",
-           delta->flags);
-
-       return result + (delta->flags ? 8 : 0);
-}
-
-static array_index
-get_delta_rk_index(struct delta_router_key const *delta)
-{
-       array_index result;
-
-       result = get_rk_index(&delta->router_key);
-       ck_assert_msg(delta->flags <= 1, "RK Unexpected flags: %u",
-           delta->flags);
-
-       return result + (delta->flags ? 8 : 0);
-}
-
-static array_index
-get_delta_aspa_index(struct delta_aspa const *delta)
-{
-       ck_assert_msg(delta->flags <= 1, "ASPA Unexpected flags: %u",
-           delta->flags);
-
-       return (delta->aspa->customer + 6) + (delta->flags ? 8 : 0);
-}
-
-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
-rk_check(struct router_key const *rk, void *arg)
-{
-       bool *array = arg;
-       array_index index;
-
-       index = get_rk_index(rk);
-       ck_assert_uint_eq(false, array[index]);
-       array[index] = true;
-
-       return 0;
-}
-
-static int
-aspa_check(struct aspa const *aspa, void *arg)
-{
-       bool *array = arg;
-       array_index index;
-
-       index = get_aspa_index(aspa);
-       ck_assert_uint_eq(false, array[index]);
-       array[index] = true;
-
-       return 0;
-}
-
-static int
-delta_vrp_check(struct delta_vrp const *delta, void *arg)
-{
-       bool *array = arg;
-       array_index index;
-
-       index = get_delta_vrp_index(delta);
-       ck_assert_uint_eq(false, array[index]);
-       array[index] = true;
-
-       return 0;
-}
-
-static int
-delta_rk_check(struct delta_router_key const *delta, void *arg)
-{
-       bool *array = arg;
-       array_index index;
-
-       index = get_delta_rk_index(delta);
-       ck_assert_uint_eq(false, array[index]);
-       array[index] = true;
-
-       return 0;
-}
-
-static int
-delta_aspa_check(struct delta_aspa const *aspa, void *arg)
-{
-       bool *array = arg;
-       array_index index;
-
-       index = get_delta_aspa_index(aspa);
-       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[8];
-       array_index i;
-
-       memset(actual_base, 0, sizeof(actual_base));
-       ck_assert_int_eq(0, get_last_serial_number(&actual_serial));
-       ck_assert_int_eq(0, vrps_foreach_base(vrp_check, rk_check, aspa_check,
-           actual_base));
-       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 int
-vrp_add(struct delta_vrp const *delta, void *arg)
-{
-       printf("%s VRP %u\n", delta->flags ? "+" : "-", delta->vrp.asn);
-       deltas_add_roa(arg, &delta->vrp, delta->flags, 'a', 0, 0);
-       return 0;
-}
-
-static int
-rk_add(struct delta_router_key const *delta, void *arg)
-{
-       printf("%s RK %u\n", delta->flags ? "+" : "-", delta->router_key.as);
-       deltas_add_router_key(arg, &delta->router_key, delta->flags);
-       return 0;
-}
-
-static int
-aspa_add(struct delta_aspa const *delta, void *arg)
-{
-       array_index i;
-       printf("%s ASPA %u [ ", delta->flags ? "+" : "-", delta->aspa->customer);
-       for (i = 0; i < delta->aspa->providers.count; i++)
-               printf("%u ", delta->aspa->providers.asids[i]);
-       printf("]\n");
-       deltas_add_aspa(arg, delta->aspa, delta->flags);
-       return 0;
-}
-
-static void
-check_deltas(serial_t from, serial_t to, bool const *expected_deltas)
-{
-       struct deltas *deltas;
-       serial_t actual_serial;
-       bool actual_deltas[16];
-       array_index i;
-
-       deltas = deltas_create();
-       ck_assert_ptr_ne(NULL, deltas);
-
-       ck_assert_int_eq(0, vrps_foreach_delta_since(from, &actual_serial,
-           vrp_add, rk_add, aspa_add, deltas));
-       ck_assert_uint_eq(to, actual_serial);
-
-       memset(actual_deltas, 0, sizeof(actual_deltas));
-       ck_assert_int_eq(0, deltas_foreach(deltas, delta_vrp_check,
-           delta_rk_check, delta_aspa_check, actual_deltas));
-       for (i = 0; i < ARRAY_LEN(actual_deltas); i++)
-               ck_assert_uint_eq(expected_deltas[i], actual_deltas[i]);
-}
-
-static void
-check_no_deltas(serial_t from)
-{
-       serial_t actual_to;
-       ck_assert_int_eq(-ESRCH, vrps_foreach_delta_since(from, &actual_to,
-           dvrp_fail, drk_fail, daspa_fail, NULL));
-}
-
-static void
-create_deltas_1to2(void)
-{
-       serial_t serial;
-       bool changed;
-       bool iterated_entries[16];
-
-       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(vrp_fail, rk_fail,
-           aspa_fail, iterated_entries));
-       ck_assert_int_eq(-EAGAIN, vrps_foreach_delta_since(0, &serial,
-           dvrp_fail, drk_fail, daspa_fail, NULL));
-
-       /* First validation: One tree, no deltas */
-       ck_assert_int_eq(0, vrps_update(&changed));
-       ck_assert(changed);
-       check_serial(1);
-       check_base(1, iteration1_base);
-       check_deltas(1, 1, deltas_1to1);
-
-       /* Second validation: One tree, added deltas */
-       ck_assert_int_eq(0, vrps_update(&changed));
-
-       ck_assert(changed);
-       check_serial(2);
-       check_base(2, iteration2_base);
-       check_deltas(1, 2, deltas_1to2);
-       check_deltas(2, 2, deltas_2to2);
-}
-
-START_TEST(test_basic)
-{
-       bool changed;
-
-       deltas_lifetime = 5;
-
-       create_deltas_1to2();
-
-       /* Third validation: One tree, removed deltas */
-       ck_assert_int_eq(0, vrps_update(&changed));
-       ck_assert(changed);
-       check_serial(3);
-       check_base(3, iteration3_base);
-       check_deltas(1, 3, deltas_1to3);
-       check_deltas(2, 3, deltas_2to3);
-       check_deltas(3, 3, deltas_3to3);
-
-       vrps_destroy();
-}
-END_TEST
-
-START_TEST(test_delta_forget)
-{
-       bool changed;
-
-       deltas_lifetime = 1;
-
-       create_deltas_1to2();
-
-       /* Third validation: One tree, removed deltas and delta 1 removed */
-       ck_assert_int_eq(0, vrps_update(&changed));
-       ck_assert(changed);
-       check_serial(3);
-       check_base(3, iteration3_base);
-       check_no_deltas(1);
-       check_deltas(2, 3, deltas_2to3);
-       check_deltas(3, 3, deltas_3to3);
-
-       vrps_destroy();
-}
-END_TEST
-
-START_TEST(test_delta_ovrd)
-{
-       bool changed;
-
-       deltas_lifetime = 3;
-
-       create_deltas_1to2();
-
-       /* Third validation: One tree, removed deltas */
-       ck_assert_int_eq(0, vrps_update(&changed));
-       ck_assert(changed);
-       check_serial(3);
-       check_base(3, iteration3_base);
-       check_deltas(1, 3, deltas_1to3);
-       check_deltas(2, 3, deltas_2to3);
-       check_deltas(3, 3, deltas_3to3);
-
-       /* Fourth validation with deltas that override each other */
-       ck_assert_int_eq(0, vrps_update(&changed));
-       ck_assert(changed);
-       check_serial(4);
-       check_base(4, iteration4_base);
-       check_deltas(1, 4, deltas_1to4);
-       check_deltas(2, 4, deltas_2to4);
-       check_deltas(3, 4, deltas_3to4);
-       check_deltas(4, 4, deltas_4to4);
-
-       vrps_destroy();
-}
-END_TEST
-
-static Suite *
-pdu_suite(void)
-{
-       Suite *suite;
-       TCase *core;
-
-       core = tcase_create("Core");
-//     tcase_add_test(core, test_basic);
-//     tcase_add_test(core, test_delta_forget);
-       tcase_add_test(core, test_delta_ovrd);
-
-       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;
-}
index fb4b7a90a8fb9461a90fb8d6044771b2df2c9998..5d8bedc96ccc0ee112037a30969d482f0571ad90 100644 (file)
 
 #include "alloc.c"
 #include "common.c"
+#include "file.c"
 #include "mock.c"
+#include "rtr/db/db_table.c"
+#include "rtr/err_pdu.c"
+#include "rtr/meta.c"
+#include "rtr/pdu.c"
+#include "rtr/pdu_handler.c"
+#include "rtr/pdu_stream.c"
 #include "types/aspa.c"
-#include "types/delta.c"
 #include "types/router_key.c"
 #include "types/serial.c"
-#include "types/vrp.c"
-#include "rtr/pdu_handler.c"
-#include "rtr/err_pdu.c"
-#include "rtr/db/delta.c"
-#include "rtr/db/deltas_array.c"
-#include "rtr/db/db_table.c"
-#include "rtr/db/rtr_db_mock.c"
-#include "rtr/db/vrps.c"
-#include "thread/thread_pool.c"
 
-/* Mocks */
+unsigned int deltas_lifetime = 5;
 
-MOCK_INT(slurm_apply, 0, struct db_table *base, struct db_slurm **slurm)
-MOCK_ABORT_VOID(db_slurm_destroy, struct db_slurm *db)
-MOCK_VOID(output_print_data, struct db_table const *db)
-__MOCK_ABORT(config_get_local_repository, char const *, "tmp/pdu", void)
+MOCK(config_get_local_repository, char const *, "tmp", void)
+MOCK_UINT(config_get_deltas_lifetime, deltas_lifetime, void)
 MOCK_UINT(config_get_max_aspa_providers, 10, void)
 
-/* Mocks end */
+struct sent_pdu {
+       enum pdu_type type;
+       uint32_t as;
+       uint8_t flags;
+};
+
+static struct sent_pdu expected[24];
+static struct sent_pdu actual[24];
+static array_index e, a;
 
-struct expected_pdu {
-       uint8_t pdu_type;
-       STAILQ_ENTRY(expected_pdu) list_hook;
+static const unsigned char db_imp_ski[] = {
+    0x0e, 0xe9, 0x6a, 0x8e, 0x2f, 0xac, 0x50, 0xce, 0x6c, 0x5f,
+    0x93, 0x3e, 0xde, 0x6a, 0xa7, 0x80, 0xa6, 0x85, 0x0e, 0x31
 };
 
-static STAILQ_HEAD(, expected_pdu) expected_pdus = STAILQ_HEAD_INITIALIZER(expected_pdus);
+static const unsigned char db_imp_spk[] = {
+    0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce,
+    0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+    0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xfa, 0xb9, 0x12,
+    0x2d, 0x79, 0x4f, 0xa4, 0xbf, 0xe6, 0xf8, 0xbe, 0xc2, 0x7c,
+    0x27, 0xca, 0xae, 0xfd, 0x45, 0x1e, 0xb3, 0x39, 0xe4, 0x5b,
+    0x08, 0x73, 0xc7, 0xcc, 0x96, 0x78, 0xc7, 0x13, 0xa6, 0x39,
+    0x9d, 0x3b, 0x82, 0x9f, 0x75, 0x20, 0x59, 0xf0, 0x95, 0xea,
+    0xc6, 0x2e, 0x19, 0x46, 0x73, 0x3d, 0x9d, 0x04, 0xcb, 0xa0,
+    0x2f, 0x7b, 0x39, 0x9f, 0x70, 0x42, 0xd4, 0x07, 0xce, 0xde,
+    0x04
+};
 
 static void
-expected_pdu_add(uint8_t pdu_type)
+add_v4(struct db_table *tbl, char const *ip, uint8_t plen, uint8_t mlen, uint32_t as)
 {
-       struct expected_pdu *pdu;
+       struct ipv4_prefix pfx = { 0 };
+       ck_assert_int_eq(1, inet_pton(AF_INET, ip, &pfx.addr));
+       pfx.len = plen;
+       ck_assert_int_eq(0, rtrhandler_handle_roa_v4(tbl, as, &pfx, mlen));
+}
 
-       pdu = malloc(sizeof(struct expected_pdu));
-       ck_assert_ptr_ne(NULL, pdu);
+static void
+add_v6(struct db_table *tbl, char const *ip, uint8_t plen, uint8_t mlen, uint32_t as)
+{
+       struct ipv6_prefix pfx = { 0 };
+       ck_assert_int_eq(1, inet_pton(AF_INET6, ip, &pfx.addr));
+       pfx.len = plen;
+       ck_assert_int_eq(0, rtrhandler_handle_roa_v6(tbl, as, &pfx, mlen));
+}
+
+static void
+add_rk(struct db_table *tbl, uint32_t as)
+{
+       ck_assert_int_eq(0, rtrhandler_handle_router_key(tbl, db_imp_ski, as, db_imp_spk));
+}
 
-       pdu->pdu_type = pdu_type;
-       STAILQ_INSERT_TAIL(&expected_pdus, pdu, list_hook);
+static void
+_add_aspa(struct db_table *tbl, uint32_t customer)
+{
+       struct aspa *aspa;
+
+       aspa = pmalloc(sizeof(struct aspa));
+       aspa->customer = customer;
+       aspa->providers.asids = pcalloc(3, sizeof(uint32_t));
+       aspa->providers.asids[0] = 100;
+       aspa->providers.asids[1] = 200;
+       aspa->providers.asids[2] = 300;
+       aspa->providers.count = 3;
+       aspa->refs = 0;
+
+       ck_assert_int_eq(0, rtrhandler_handle_aspa(tbl, aspa));
 }
 
-static uint8_t
-pop_expected_pdu(void)
+static struct db_table *
+mock_table(serial_t serial)
 {
-       struct expected_pdu *pdu;
-       uint8_t result;
+       struct db_table *tbl;
 
-       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);
+       tbl = db_table_create();
+       tbl->rtr.session = 0x1234;
+       tbl->rtr.serial = serial;
 
-       return result;
+       return tbl;
 }
 
-static bool
-has_expected_pdus(void)
+static void
+mock_resources(struct db_table *tbl, uint32_t as)
 {
-       return !STAILQ_EMPTY(&expected_pdus);
+       add_v4(tbl, "192.0.2.0", 24, 32, as);
+       add_v6(tbl, "200:db8::", 96, 120, as);
+       add_rk(tbl, as);
+       _add_aspa(tbl, as);
 }
 
-/*
- * This initializes the database using the test values from
- * db/rtr_db_mock.c.
- */
-static void
-init_db_full(void)
+static uint16_t
+mock_commit(struct db_table *tbl)
 {
-       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);
+       uint16_t session;
+
+       db_table_sort(tbl);
+       ck_assert_int_eq(0, db_table_cache(tbl));
+       session = tbl->rtr.session;
+       db_table_destroy(tbl);
+
+       return session;
 }
 
-static void
-init_reset_query(struct rtr_request *request)
+uint16_t
+mock_serial1(void)
 {
-       static unsigned char raw[] = { 1, 2, 0, 0, 0, 0, 0, 8 };
-
-       request->fd = 0;
-       strcpy(request->client_addr, "192.0.2.1");
-       request->pdu.rtr_version = RTR_V1;
-       request->pdu.type = PDU_TYPE_RESET_QUERY;
-       request->pdu.raw.bytes = raw;
-       request->pdu.raw.bytes_len = sizeof(raw);
-       request->eos = true;
+       struct db_table *tbl = mock_table(1);
+       mock_resources(tbl, 1);
+       return mock_commit(tbl);
 }
 
-static void
-init_serial_query(struct rtr_request *request, uint32_t serial)
+void
+mock_serial2(void)
 {
-       static unsigned char raw[] = { 1, 1, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0 };
-
-       request->fd = 0;
-       strcpy(request->client_addr, "192.0.2.1");
-       request->pdu.rtr_version = RTR_V1;
-       request->pdu.type = PDU_TYPE_SERIAL_QUERY;
-       request->pdu.obj.sq.session_id = get_current_session_id(RTR_V1);
-       request->pdu.obj.sq.serial_number = serial;
-       request->pdu.raw.bytes = raw;
-       request->pdu.raw.bytes_len = sizeof(raw);
-       request->eos = true;
+       struct db_table *tbl = mock_table(2);
+       mock_resources(tbl, 1);
+       mock_resources(tbl, 2);
+       mock_commit(tbl);
 }
 
-/* Mocks */
+void
+mock_serial3(void)
+{
+       struct db_table *tbl = mock_table(3);
+       mock_resources(tbl, 2);
+       mock_commit(tbl);
+}
 
-MOCK_UINT(config_get_deltas_lifetime, 5, void)
+void
+mock_serial4(void)
+{
+       struct db_table *tbl = mock_table(4);
+       mock_resources(tbl, 1);
+       mock_commit(tbl);
+}
 
-int
-send_cache_reset_pdu(int fd, uint8_t version)
+static int
+send_pdu(enum pdu_type type, uint32_t as, uint8_t flags)
 {
-       pr_op_info("    Server sent Cache Reset.");
-       ck_assert_int_eq(pop_expected_pdu(), PDU_TYPE_CACHE_RESET);
+       ck_assert_uint_lt(a, ARRAY_LEN(actual));
+
+       actual[a].type = type;
+       actual[a].as = as;
+       actual[a].flags = flags;
+
+       a++;
        return 0;
 }
 
 int
-send_cache_response_pdu(int fd, uint8_t version)
+send_serial_notify_pdu(int fd, uint8_t ver, struct rtr_metadata *meta)
 {
-       pr_op_info("    Server sent Cache Response.");
-       ck_assert_int_eq(pop_expected_pdu(), PDU_TYPE_CACHE_RESPONSE);
-       return 0;
+       return send_pdu(PDU_TYPE_SERIAL_NOTIFY, 0, 0);
 }
 
-static char const *
-flags2str(uint8_t flags)
+int
+send_cache_reset_pdu(int fd, uint8_t ver)
 {
-       switch (flags) {
-       case FLAG_ANNOUNCEMENT:
-               return "add";
-       case FLAG_WITHDRAWAL:
-               return "rm";
-       }
-       return "unk";
+       return send_pdu(PDU_TYPE_CACHE_RESET, 0, 0);
 }
 
 int
-send_prefix_pdu(int fd, uint8_t version, struct vrp const *vrp, uint8_t flags)
+send_cache_response_pdu(int fd, uint8_t ver, uint16_t session)
 {
-       /*
-        * 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_op_info("    Server sent Prefix PDU.");
+       return send_pdu(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+}
 
+int
+send_prefix_pdu(int fd, uint8_t ver, struct vrp const *vrp, uint8_t flags)
+{
        switch (vrp->addr_fam) {
        case AF_INET:
-               printf("%s asn%u IPv4\n", flags2str(flags), vrp->asn);
-               break;
+               return send_pdu(PDU_TYPE_IPV4_PREFIX, vrp->asn, flags);
        case AF_INET6:
-               printf("%s asn%u IPv6\n", flags2str(flags), vrp->asn);
-               break;
-       default:
-               printf("%s asn%u Unknown\n", flags2str(flags), vrp->asn);
-               break;
+               return send_pdu(PDU_TYPE_IPV6_PREFIX, vrp->asn, flags);
        }
+       ck_abort();
+}
 
-       ck_assert_msg(pdu_type == PDU_TYPE_IPV4_PREFIX
-           || pdu_type == PDU_TYPE_IPV6_PREFIX,
-           "Server sent a prefix. Expected PDU type was %d.", pdu_type);
-       return 0;
+int
+send_router_key_pdu(int fd, uint8_t ver, struct router_key const *rk,
+    uint8_t flags)
+{
+       return send_pdu(PDU_TYPE_ROUTER_KEY, rk->as, flags);
 }
 
 int
-send_router_key_pdu(int fd, uint8_t version,
-    struct router_key const *router_key, uint8_t flags)
+send_aspa_announce_pdu(int fd, uint8_t ver, struct aspa const *aspa)
 {
-       uint8_t pdu_type = pop_expected_pdu();
-       pr_op_info("    Server sent Router Key PDU.");
-       printf("%s asn%u RK\n", flags2str(flags), router_key->as);
-       ck_assert_msg(pdu_type == PDU_TYPE_ROUTER_KEY,
-           "Server sent a Router Key. Expected PDU type was %d.", pdu_type);
-       return 0;
+       return send_pdu(PDU_TYPE_ASPA, aspa->customer, FLAG_ANNOUNCEMENT);
 }
 
 int
-send_aspa_pdu(int fd, uint8_t version, struct aspa const *aspa, uint8_t flags)
+send_aspa_withdraw_pdu(int fd, uint8_t ver, uint32_t customer)
 {
-       uint8_t pdu_type = pop_expected_pdu();
-       pr_op_info("    Server sent ASPA PDU.");
-       printf("%s asn%u ASPA\n", flags2str(flags), aspa->customer);
-       ck_assert_msg(pdu_type == PDU_TYPE_ASPA,
-           "Server sent an ASPA PDU. Expected PDU type was %d.", pdu_type);
-       return 0;
+       return send_pdu(PDU_TYPE_ASPA, customer, FLAG_WITHDRAWAL);
 }
 
 int
-send_end_of_data_pdu(int fd, uint8_t version, serial_t end_serial)
+send_end_of_data_pdu(int fd, uint8_t ver, uint16_t session, serial_t serial)
 {
-       pr_op_info("    Server sent End of Data.");
-       ck_assert_int_eq(pop_expected_pdu(), PDU_TYPE_END_OF_DATA);
-       return 0;
+       return send_pdu(PDU_TYPE_END_OF_DATA, 0, 0);
 }
 
 int
 send_error_report_pdu(int fd, uint8_t version, uint16_t code,
     struct rtr_buffer const *request, char *message)
 {
-       pr_op_info("    Server sent Error Report %u: '%s'", code, message);
-       ck_assert_int_eq(pop_expected_pdu(), PDU_TYPE_ERROR_REPORT);
-       return 0;
+       return send_pdu(PDU_TYPE_ERROR_REPORT, 0, 0);
 }
 
-/* Tests */
-
-/* https://tools.ietf.org/html/rfc8210#section-8.1 */
-START_TEST(test_start_or_restart)
+static void
+check_response(void)
 {
-       struct rtr_request request;
-
-       pr_op_info("-- Start or Restart --");
+       array_index i;
+
+       pr_op_debug("Expected:");
+       for (i = 0; i < e; i++)
+               pr_op_debug("- %s %u %u", pdutype2str(expected[i].type),
+                   expected[i].as, expected[i].flags);
+       pr_op_debug("Actual:");
+       for (i = 0; i < a; i++)
+               pr_op_debug("- %s %u %u", pdutype2str(actual[i].type),
+                   actual[i].as, actual[i].flags);
+
+       ck_assert_uint_eq(e, a);
+       for (i = 0; i < e; i++) {
+               ck_assert_int_eq(expected[i].type, actual[i].type);
+               ck_assert_int_eq(expected[i].as, actual[i].as);
+               ck_assert_int_eq(expected[i].flags, actual[i].flags);
+       }
+}
 
-       /* Init */
-       init_db_full();
-       init_reset_query(&request);
+static void
+rcv_reset_query(void)
+{
+       struct pdu_stream stream = { 0 };
+       struct rtr_request req = { 0 };
+       unsigned char raw[8] = { 0 };
+
+       ck_assert_int_eq(0, pthread_mutex_init(&stream.session_lock, NULL));
+       stream.session_set = false;
+
+       req.fd = -1;
+       req.stream = &stream;
+       req.pdu.rtr_version = RTR_V2;
+       req.pdu.type = PDU_TYPE_RESET_QUERY;
+
+       req.pdu.raw.bytes = raw;
+       req.pdu.raw.bytes_len = 8;
+       raw[0] = RTR_V2;
+       raw[1] = PDU_TYPE_RESET_QUERY;
+       raw[7] = 8;
+
+       a = 0;
+       ck_assert_int_eq(0, handle_reset_query_pdu(&req));
+       check_response();
+}
 
-       /* 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_ROUTER_KEY);
-       expected_pdu_add(PDU_TYPE_ASPA);
-       expected_pdu_add(PDU_TYPE_END_OF_DATA);
+static void
+rcv_serial_query(uint16_t session, serial_t serial)
+{
+       struct pdu_stream stream = { 0 };
+       struct rtr_request req = { 0 };
+       unsigned char raw[12] = { 0 };
+
+       ck_assert_int_eq(0, pthread_mutex_init(&stream.session_lock, NULL));
+       stream.session_set = true;
+       stream.session = session;
+
+       req.fd = -1;
+       req.pdu.rtr_version = RTR_V2;
+       req.stream = &stream;
+       req.pdu.type = PDU_TYPE_SERIAL_QUERY;
+       req.pdu.obj.sq.session_id = session;
+       req.pdu.obj.sq.serial_number = serial;
+
+       req.pdu.raw.bytes = raw;
+       req.pdu.raw.bytes_len = 12;
+       raw[0] = RTR_V2;
+       raw[1] = PDU_TYPE_SERIAL_QUERY;
+       raw[2] = session >> 8;
+       raw[3] = session;
+       raw[7] = 12;
+       raw[8] = serial >> 24;
+       raw[9] = serial >> 16;
+       raw[10] = serial >> 8;
+       raw[11] = serial;
+
+       a = 0;
+       ck_assert_int_eq(0, handle_serial_query_pdu(&req));
+       check_response();
+
+       pthread_mutex_destroy(&stream.session_lock);
+}
 
-       /* Run and validate */
-       ck_assert_int_eq(0, handle_reset_query_pdu(&request));
-       ck_assert_uint_eq(false, has_expected_pdus());
+static void
+expected_pdu_add(enum pdu_type type, uint32_t as, uint8_t flags)
+{
+       expected[e].type = type;
+       expected[e].as = as;
+       expected[e].flags = flags;
+       e++;
+}
 
-       /* Clean up */
-       vrps_destroy();
+/* https://datatracker.ietf.org/doc/html/rfc8210#section-8.1 */
+/* https://datatracker.ietf.org/doc/html/rfc8210#section-8.2 */
+START_TEST(test_natural_flows)
+{
+       uint16_t session;
+
+       pr_op_info("-- Natural Flows --");
+
+       deltas_lifetime = 5;
+       if (file_exists("tmp/rtr") == 0)
+               ck_assert_int_eq(0, file_rm_rf("tmp/rtr"));
+
+       /* First cycle not yet performed: Tell routers to wait */
+       e = 0;
+       expected_pdu_add(PDU_TYPE_ERROR_REPORT, 0, 0);
+       rcv_reset_query();
+       rcv_serial_query(0x1234, 0);
+
+       /* First cycle: One tree, no deltas */
+       session = mock_serial1();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_reset_query();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 1);
+
+       /* Second cycle: One tree, added deltas */
+       mock_serial2();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_reset_query();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 1);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 2);
+
+       /* Third cycle: One tree, removed deltas */
+       mock_serial3();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_reset_query();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ASPA, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 1);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 2);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 3);
+
+       /* Fourth cycle: Back to serial 1 data */
+       mock_serial4();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_reset_query();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 1);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ASPA, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 2);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 3);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 4);
 }
 END_TEST
 
-/* https://tools.ietf.org/html/rfc8210#section-8.2 */
-START_TEST(test_typical_exchange)
+START_TEST(test_delta_forget)
 {
-       struct rtr_request request;
-
-       pr_op_info("-- Typical Exchange --");
-
-       /* Init */
-       init_db_full();
-       init_serial_query(&request, 0);
-
-       /* From serial 0: Define expected server response */
-       /* Server doesn't have serial 0. */
-       expected_pdu_add(PDU_TYPE_CACHE_RESET);
-
-       /* From serial 0: Run and validate */
-       ck_assert_int_eq(0, handle_serial_query_pdu(&request));
-       ck_assert_uint_eq(false, has_expected_pdus());
-
-       /* From serial 1: Init client request */
-       init_serial_query(&request, 1);
-
-       /* From serial 1 to 3: Define expected server response */
-       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE);
-       /* Remove all zeroes, add all ones */
-       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_ROUTER_KEY);
-       expected_pdu_add(PDU_TYPE_ROUTER_KEY);
-       expected_pdu_add(PDU_TYPE_ASPA);
-       expected_pdu_add(PDU_TYPE_ASPA);
-       expected_pdu_add(PDU_TYPE_END_OF_DATA);
-
-       /* From serial 1: Run and validate */
-       ck_assert_int_eq(0, handle_serial_query_pdu(&request));
-       ck_assert_uint_eq(false, has_expected_pdus());
-
-       /* From serial 2: Init client request */
-       init_serial_query(&request, 2);
-
-       /* From serial 2 to 3: Define expected server response */
-       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE);
-       /* Remove all zeroes */
-       expected_pdu_add(PDU_TYPE_IPV4_PREFIX);
-       expected_pdu_add(PDU_TYPE_IPV6_PREFIX);
-       expected_pdu_add(PDU_TYPE_ROUTER_KEY);
-       expected_pdu_add(PDU_TYPE_ASPA);
-       expected_pdu_add(PDU_TYPE_END_OF_DATA);
-
-       /* From serial 2: Run and validate */
-       ck_assert_int_eq(0, handle_serial_query_pdu(&request));
-       ck_assert_uint_eq(false, has_expected_pdus());
-
-       /* From serial 3: Init client request */
-       init_serial_query(&request, 3);
-
-       /* From serial 3 to 3: Define expected server response */
-       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE);
-       expected_pdu_add(PDU_TYPE_END_OF_DATA);
-
-       /* From serial 3: Run and validate */
-       ck_assert_int_eq(0, handle_serial_query_pdu(&request));
-       ck_assert_uint_eq(false, has_expected_pdus());
-
-       /* Clean up */
-       vrps_destroy();
+       uint16_t session;
+
+       pr_op_info("-- Delta Forgetting -- ");
+
+       deltas_lifetime = 1;
+       if (file_exists("tmp/rtr") == 0)
+               ck_assert_int_eq(0, file_rm_rf("tmp/rtr"));
+
+       /* First cycle not yet performed: Tell routers to wait */
+       e = 0;
+       expected_pdu_add(PDU_TYPE_ERROR_REPORT, 0, 0);
+       rcv_reset_query();
+       rcv_serial_query(0x1234, 0);
+
+       /* First cycle: One tree, no deltas */
+       session = mock_serial1();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_reset_query();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 1);
+
+       /* Second cycle: One tree, added deltas */
+       mock_serial2();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_reset_query();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 1);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 2);
+
+       /* Third cycle: One tree, removed deltas */
+       mock_serial3();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 2, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_reset_query();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESET, 0, 0);
+       rcv_serial_query(session, 1);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 2);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 3);
+
+       /* Fourth cycle: Back to serial 1 data */
+       mock_serial4();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_reset_query();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESET, 0, 0);
+       rcv_serial_query(session, 1);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESET, 0, 0);
+       rcv_serial_query(session, 2);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 2, FLAG_WITHDRAWAL);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 3);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_serial_query(session, 4);
 }
 END_TEST
 
 /* https://tools.ietf.org/html/rfc8210#section-8.3 */
 START_TEST(test_no_incremental_update_available)
 {
-       struct rtr_request request;
+       uint16_t session;
 
        pr_op_info("-- No Incremental Update Available --");
 
-       /* Init */
-       init_db_full();
-       init_serial_query(&request, 10000);
-
-       /* Define expected server response */
-       expected_pdu_add(PDU_TYPE_CACHE_RESET);
-
-       /* Run and validate */
-       ck_assert_int_eq(0, handle_serial_query_pdu(&request));
-       ck_assert_uint_eq(false, has_expected_pdus());
-
-       /* The Reset Query is already tested in start_or_restart. */
-
-       /* Clean up */
-       vrps_destroy();
+       deltas_lifetime = 5;
+       if (file_exists("tmp/rtr") == 0)
+               ck_assert_int_eq(0, file_rm_rf("tmp/rtr"));
+       session = mock_serial1();
+       mock_serial2();
+       mock_serial3();
+       mock_serial4();
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESET, 0, 0);
+       rcv_serial_query(session, 10000);
+
+       e = 0;
+       expected_pdu_add(PDU_TYPE_CACHE_RESPONSE, 0, 0);
+       expected_pdu_add(PDU_TYPE_IPV4_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_IPV6_PREFIX, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ROUTER_KEY, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_ASPA, 1, FLAG_ANNOUNCEMENT);
+       expected_pdu_add(PDU_TYPE_END_OF_DATA, 0, 0);
+       rcv_reset_query();
 }
 END_TEST
 
 /* https://tools.ietf.org/html/rfc8210#section-8.4 */
 START_TEST(test_cache_has_no_data_available)
 {
-       struct rtr_request request;
-
        pr_op_info("-- Cache Has No Data Available --");
 
-       /* Init */
-       ck_assert_int_eq(0, vrps_init());
-
-       /* Serial Query: Init client request */
-       init_serial_query(&request, 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(&request));
-       ck_assert_uint_eq(false, has_expected_pdus());
+       deltas_lifetime = 5;
+       if (file_exists("tmp/rtr") == 0)
+               ck_assert_int_eq(0, file_rm_rf("tmp/rtr"));
 
-       /* Reset Query: Init client request */
-       init_reset_query(&request);
-
-       /* 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(&request));
-       ck_assert_uint_eq(false, has_expected_pdus());
-
-       /* Clean up */
-       vrps_destroy();
+       e = 0;
+       expected_pdu_add(PDU_TYPE_ERROR_REPORT, 0, 0);
+       rcv_serial_query(0x1234, 0);
+       rcv_reset_query();
 }
 END_TEST
 
-START_TEST(test_bad_session_id)
-{
-       struct rtr_request request;
-
-       pr_op_info("-- Bad Session ID --");
-
-       /* Init */
-       init_db_full();
-       init_serial_query(&request, 0);
-       request.pdu.obj.sq.session_id++;
-
-       /* From serial 0: Define expected server response */
-       expected_pdu_add(PDU_TYPE_ERROR_REPORT);
-
-       /* From serial 0: Run and validate */
-       ck_assert_int_eq(-EINVAL, handle_serial_query_pdu(&request));
-       ck_assert_uint_eq(false, has_expected_pdus());
-
-       /* Clean up */
-       vrps_destroy();
-}
-END_TEST
-
-static Suite *pdu_suite(void)
+static Suite *
+pdu_suite(void)
 {
        Suite *suite;
-       TCase *core, *error;
+       TCase *core;
 
-       core = tcase_create("RFC8210-Defined Protocol Sequences");
-       tcase_add_test(core, test_start_or_restart);
-       tcase_add_test(core, test_typical_exchange);
+       core = tcase_create("RTR flows");
+       tcase_add_test(core, test_natural_flows);
+       tcase_add_test(core, test_delta_forget);
        tcase_add_test(core, test_no_incremental_update_available);
        tcase_add_test(core, test_cache_has_no_data_available);
 
-       error = tcase_create("Unhappy path cases");
-       tcase_add_test(error, test_bad_session_id);
-
        suite = suite_create("PDU Handler");
        suite_add_tcase(suite, core);
-       suite_add_tcase(suite, error);
        return suite;
 }
 
-int main(void)
+int
+main(void)
 {
        Suite *suite;
        SRunner *runner;
        int tests_failed;
+       int error;
+
+       error = mkdir_f("tmp");
+       if (error)
+               return error;
 
        suite = pdu_suite();
 
index 0314cafc6a7a8a8a0fbea0c832fed0bb1596cd78..eb0e7b017e305dbfac01ce5b1f0886c7a67b68e7 100644 (file)
@@ -3,11 +3,13 @@
 #include <fcntl.h>
 
 #include "alloc.c"
+#include "common.c"
 #include "mock.c"
 #include "rtr/pdu_stream.c"
 
 /* Mocks */
 
+MOCK(config_get_local_repository, char const *, "tmp", void)
 MOCK_ABORT_INT(err_pdu_send_invalid_request, int fd, uint8_t version,
     struct rtr_buffer const *request, char const *msg)
 MOCK_ABORT_INT(err_pdu_send_unsupported_proto_version, int fd, uint8_t version,
index 4c7b04207f16ae7206fd44e643e5c7c440b9e475..f4a768627d3a970b6d52eddf2db12daa0e6468ac 100644 (file)
@@ -18,12 +18,8 @@ MOCK(config_get_local_repository, char const *, "tmp/tal", void)
 MOCK_ABORT_VOID(cache_setup, void)
 MOCK(cache_create, struct rpki_cache *, NULL, char const *tal)
 MOCK_VOID(cache_destroy, struct rpki_cache *cache)
-MOCK_ABORT_INT(cache_download, struct rpki_cache *cache, struct rpki_uri *uri,
-    bool *changed)
 MOCK_ABORT_INT(cache_download_alt, struct rpki_cache *cache,
     struct uri_list *uris, bool use_rrdp, uris_dl_cb cb, void *arg)
-MOCK_ABORT_PTR(cache_recover, rpki_uri, struct rpki_cache *cache,
-    struct uri_list *uris, bool use_rrdp)
 MOCK_ABORT_VOID(cache_teardown, void)
 MOCK_ABORT_INT(certificate_traverse, struct rpp *rpp_parent,
     struct rpki_uri *cert_uri)
@@ -41,15 +37,20 @@ MOCK_ABORT_INT(handle_roa_v6, uint32_t as, struct ipv6_prefix const *prefix,
     uint8_t max_length, void *arg)
 MOCK_ABORT_INT(handle_router_key, unsigned char const *ski,
     struct asn_range const *asns, unsigned char const *spk, void *arg)
+MOCK_ABORT_INT(handle_aspa, struct aspa *aspa, void *arg)
 MOCK_ABORT_VOID(rpp_refput, struct rpp *pp)
-MOCK_ABORT_INT(rrdp_update, struct rpki_uri *uri)
-MOCK(state_retrieve, struct validation *, NULL, void)
 MOCK_ABORT_PTR(validation_certstack, cert_stack, struct validation *state)
 MOCK_ABORT_VOID(validation_destroy, struct validation *state)
 MOCK_ABORT_INT(validation_prepare, struct validation **out, struct tal *tal,
     struct validation_handler *validation_handler)
 MOCK_ABORT_ENUM(validation_pubkey_state, pubkey_state, struct validation *state)
-MOCK(validation_tal, struct tal *, NULL, struct validation *state)
+__MOCK_ABORT(validation_tal, struct tal *, NULL, struct validation *state)
+__MOCK_ABORT(db_table_roa_count_v4, unsigned int, 0, struct db_table *table)
+__MOCK_ABORT(db_table_roa_count_v6, unsigned int, 0, struct db_table *table)
+MOCK_ABORT_VOID(stats_set_tal_vrps, char const *tal_path, char const *proto,
+    unsigned int value)
+
+
 
 /* Tests */