summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:54:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:54:46 +0000
commitcd7b005519ade8ab6c97fcb21590b71b7d1be6e3 (patch)
treec611a8d0cd5e8f68f41b8c2d16ba580e0f40a38d /tests
parentInitial commit. (diff)
downloadlibrtr-cd7b005519ade8ab6c97fcb21590b71b7d1be6e3.tar.xz
librtr-cd7b005519ade8ab6c97fcb21590b71b7d1be6e3.zip
Adding upstream version 0.8.0.upstream/0.8.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--tests/CMakeLists.txt35
-rw-r--r--tests/test_dynamic_groups.c181
-rw-r--r--tests/test_getbits.c168
-rw-r--r--tests/test_ht_spkitable.c649
-rw-r--r--tests/test_ht_spkitable_locks.c174
-rw-r--r--tests/test_ipaddr.c256
-rw-r--r--tests/test_live_validation.c121
-rw-r--r--tests/test_pfx.c450
-rw-r--r--tests/test_pfx_locks.c171
-rw-r--r--tests/test_trie.c200
-rw-r--r--tests/unittests/CMakeLists.txt7
-rw-r--r--tests/unittests/rtrlib_unittests.h18
-rw-r--r--tests/unittests/test_packets.c72
-rw-r--r--tests/unittests/test_packets.h14
-rw-r--r--tests/unittests/test_packets_static.c444
-rw-r--r--tests/unittests/test_packets_static.h14
16 files changed, 2974 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..d4757da
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,35 @@
+set(CMAKE_BUILD_TYPE Debug)
+
+add_executable(test_pfx test_pfx.c)
+target_link_libraries(test_pfx rtrlib_static)
+add_coverage(test_pfx)
+add_executable(test_trie test_trie.c)
+target_link_libraries(test_trie rtrlib_static)
+add_coverage(test_trie)
+add_executable(test_pfx_locks test_pfx_locks.c)
+target_link_libraries(test_pfx_locks rtrlib_static)
+add_coverage(test_pfx_locks)
+add_executable(test_ht_spkitable test_ht_spkitable.c)
+target_link_libraries(test_ht_spkitable rtrlib_static)
+add_coverage(test_ht_spkitable)
+add_executable(test_ht_spkitable_locks test_ht_spkitable_locks.c)
+target_link_libraries(test_ht_spkitable_locks rtrlib_static)
+add_coverage(test_ht_spkitable_locks)
+add_executable(test_live_validation test_live_validation.c)
+target_link_libraries(test_live_validation rtrlib_static)
+add_coverage(test_live_validation)
+add_executable(test_ipaddr test_ipaddr.c)
+target_link_libraries(test_ipaddr rtrlib_static)
+add_coverage(test_ipaddr)
+add_executable(test_getbits test_getbits.c)
+target_link_libraries(test_getbits rtrlib_static)
+add_coverage(test_getbits)
+add_executable(test_dynamic_groups test_dynamic_groups.c)
+target_link_libraries(test_dynamic_groups rtrlib_static)
+add_coverage(test_dynamic_groups)
+
+
+if(UNIT_TESTING AND NOT APPLE)
+ find_package(CMocka REQUIRED)
+ add_subdirectory(unittests)
+endif(UNIT_TESTING AND NOT APPLE)
diff --git a/tests/test_dynamic_groups.c b/tests/test_dynamic_groups.c
new file mode 100644
index 0000000..51d042c
--- /dev/null
+++ b/tests/test_dynamic_groups.c
@@ -0,0 +1,181 @@
+#include "rtrlib/rtr_mgr_private.h"
+#include "rtrlib/rtrlib.h"
+
+#include "third-party/tommyds/tommylist.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+const int connection_timeout = 20;
+enum rtr_mgr_status connection_status = -1;
+
+static void connection_status_callback(const struct rtr_mgr_group *group __attribute__((unused)),
+ enum rtr_mgr_status status,
+ const struct rtr_socket *socket __attribute__((unused)),
+ void *data __attribute__((unused)))
+{
+ connection_status = status;
+}
+
+int main(void)
+{
+ //create a TCP transport socket
+ int retval = 0;
+ struct tr_socket tr_tcp;
+ char tcp_host[] = "rpki-validator.realmv6.org";
+ char tcp_port[] = "8283";
+
+ struct tr_tcp_config tcp_config = {
+ tcp_host, //IP
+ tcp_port, //Port
+ NULL, //Source address
+ NULL, //data
+ NULL, //new_socket()
+ 0, // connect timeout
+ };
+ tr_tcp_init(&tcp_config, &tr_tcp);
+
+ struct rtr_socket rtr_tcp;
+
+ rtr_tcp.tr_socket = &tr_tcp;
+
+ struct rtr_mgr_group groups[1];
+
+ groups[0].sockets = malloc(sizeof(struct rtr_socket *));
+ groups[0].sockets_len = 1;
+ groups[0].sockets[0] = &rtr_tcp;
+ groups[0].preference = 1;
+
+ struct tr_socket tr_tcp2;
+ struct rtr_socket rtr_tcp2;
+ struct rtr_mgr_group group2;
+
+ tr_tcp_init(&tcp_config, &tr_tcp2);
+ rtr_tcp2.tr_socket = &tr_tcp2;
+ group2.sockets = malloc(sizeof(struct rtr_socket *));
+ group2.sockets_len = 1;
+ group2.sockets[0] = &rtr_tcp2;
+ group2.preference = 2;
+
+ struct rtr_mgr_config *conf;
+
+ rtr_mgr_init(&conf, groups, 1, 30, 600, 600, NULL, NULL, &connection_status_callback, NULL);
+
+ //start the connection manager
+ rtr_mgr_start(conf);
+
+ int sleep_counter = 0;
+ // wait 20 sec till at least one group is fully synchronized with the server
+ // otherwise EXIT_FAILURE.
+ while (!rtr_mgr_conf_in_sync(conf)) {
+ sleep_counter++;
+ if (connection_status == RTR_MGR_ERROR || sleep_counter > connection_timeout)
+ return EXIT_FAILURE;
+
+ sleep(1);
+ }
+
+ assert(conf->len == 1);
+
+ retval = rtr_mgr_add_group(conf, &group2);
+ assert(retval == RTR_SUCCESS);
+
+ //checking behavior in case the group preference already exists
+ //by adding the same group twice.
+ retval = rtr_mgr_add_group(conf, &group2);
+ assert(retval == RTR_INVALID_PARAM);
+
+ tommy_node *node = tommy_list_head(&conf->groups->list);
+ struct rtr_mgr_group_node *group_node = node->data;
+ struct rtr_mgr_group_node *group_node2 = node->next->data;
+
+ assert(group_node->group->preference == 1);
+ assert(group_node2->group->preference == 2);
+ assert(conf->len == 2);
+
+ rtr_mgr_remove_group(conf, 1);
+
+ node = tommy_list_head(&conf->groups->list);
+ group_node = node->data;
+ assert(group_node->group->preference == 2);
+ assert(conf->len == 1);
+
+ struct tr_socket tr_tcp3;
+ struct rtr_socket rtr_tcp3;
+ struct rtr_mgr_group group3;
+
+ tr_tcp_init(&tcp_config, &tr_tcp3);
+ rtr_tcp3.tr_socket = &tr_tcp3;
+ group3.sockets = malloc(sizeof(struct rtr_socket *));
+ group3.sockets_len = 1;
+ group3.sockets[0] = &rtr_tcp3;
+ group3.preference = 3;
+
+ struct tr_socket tr_tcp4;
+ struct rtr_socket rtr_tcp4;
+ struct rtr_mgr_group group4;
+
+ tr_tcp_init(&tcp_config, &tr_tcp4);
+ rtr_tcp4.tr_socket = &tr_tcp4;
+ group4.sockets = malloc(sizeof(struct rtr_socket *));
+ group4.sockets_len = 1;
+ group4.sockets[0] = &rtr_tcp4;
+ group4.preference = 4;
+
+ rtr_mgr_add_group(conf, &group4);
+
+ // remove group 2 so group 4 becomes the active group.
+ rtr_mgr_remove_group(conf, 2);
+
+ // add group 3 which has a higher preference than group 4
+ // and check whether it will be set as the active group.
+ rtr_mgr_add_group(conf, &group3);
+
+ node = tommy_list_head(&conf->groups->list);
+ group_node = node->data;
+ assert(group_node->group->preference == 3);
+
+ //try to remove non-existent group
+ retval = rtr_mgr_remove_group(conf, 10);
+ assert(retval == RTR_ERROR);
+
+ struct tr_socket tr_tcp5;
+ struct rtr_socket rtr_tcp5;
+ struct rtr_mgr_group group5;
+
+ tr_tcp_init(&tcp_config, &tr_tcp5);
+ rtr_tcp5.tr_socket = &tr_tcp5;
+ group5.sockets = malloc(sizeof(struct rtr_socket *));
+ group5.sockets_len = 1;
+ group5.sockets[0] = &rtr_tcp5;
+ group5.preference = 5;
+
+ //add 100 groups
+ for (int i = 0; i < 100; i++) {
+ retval = rtr_mgr_add_group(conf, &group5);
+ group5.preference++;
+ assert(retval == RTR_SUCCESS);
+ }
+
+ //remove 100 groups
+ for (int i = 104; i >= 5; i--) {
+ retval = rtr_mgr_remove_group(conf, i);
+ assert(retval == RTR_SUCCESS);
+ }
+
+ rtr_mgr_remove_group(conf, 4);
+
+ //try to remove last remainig group.
+ retval = rtr_mgr_remove_group(conf, 3);
+ assert(retval == RTR_ERROR);
+
+ rtr_mgr_stop(conf);
+ rtr_mgr_free(conf);
+ free(groups[0].sockets);
+ free(group2.sockets);
+ free(group3.sockets);
+ free(group4.sockets);
+ free(group5.sockets);
+}
diff --git a/tests/test_getbits.c b/tests/test_getbits.c
new file mode 100644
index 0000000..125ce94
--- /dev/null
+++ b/tests/test_getbits.c
@@ -0,0 +1,168 @@
+#include "rtrlib/lib/ip_private.h"
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * @brief Test IPv4 address bit operations required by trie
+ */
+static void get_bits_testv4(void)
+{
+ struct lrtr_ip_addr addr;
+ struct lrtr_ip_addr result;
+
+ addr.ver = LRTR_IPV4;
+ addr.u.addr4.addr = 0xAABBCC22;
+
+ result = lrtr_ip_addr_get_bits(&addr, 0, 32);
+ assert(result.u.addr4.addr == 0xAABBCC22);
+
+ result = lrtr_ip_addr_get_bits(&addr, 0, 1);
+ assert(result.u.addr4.addr == 0x80000000);
+
+ result = lrtr_ip_addr_get_bits(&addr, 1, 1);
+ assert(result.u.addr4.addr == 0);
+
+ result = lrtr_ip_addr_get_bits(&addr, 2, 1);
+ assert(result.u.addr4.addr == 0x20000000);
+
+ result = lrtr_ip_addr_get_bits(&addr, 0, 8);
+ assert(result.u.addr4.addr == 0xAA000000);
+
+ result = lrtr_ip_addr_get_bits(&addr, 8, 8);
+ assert(result.u.addr4.addr == 0x00BB0000);
+
+ lrtr_ip_str_to_addr("10.10.10.0", &addr);
+
+ result = lrtr_ip_addr_get_bits(&addr, 0, 8);
+ assert(lrtr_ip_str_cmp(&result, "10.0.0.0"));
+
+ result = lrtr_ip_addr_get_bits(&addr, 0, 16);
+ assert(lrtr_ip_str_cmp(&result, "10.10.0.0"));
+
+ result = lrtr_ip_addr_get_bits(&addr, 8, 8);
+ assert(lrtr_ip_str_cmp(&result, "0.10.0.0"));
+
+ result = lrtr_ip_addr_get_bits(&addr, 8, 24);
+ assert(lrtr_ip_str_cmp(&result, "0.10.10.0"));
+
+ result = lrtr_ip_addr_get_bits(&addr, 31, 1);
+ assert(result.u.addr4.addr == 0);
+
+ result = lrtr_ip_addr_get_bits(&addr, 0, 1);
+ assert(result.u.addr4.addr == 0);
+
+ result = lrtr_ip_addr_get_bits(&addr, 3, 3);
+ assert(lrtr_ip_str_cmp(&result, "8.0.0.0"));
+
+ assert(lrtr_ip_str_to_addr("132.200.0.0", &addr) == 0);
+ result = lrtr_ip_addr_get_bits(&addr, 0, 1);
+ assert(result.u.addr4.addr == 0x80000000);
+
+ assert(lrtr_ip_str_to_addr("101.200.0.0", &addr) == 0);
+ result = lrtr_ip_addr_get_bits(&addr, 0, 1);
+ assert(result.u.addr4.addr == 0);
+
+ addr.u.addr4.addr = 0x6D698000;
+ result = lrtr_ip_addr_get_bits(&addr, 0, 19);
+ assert(result.u.addr4.addr == 0x6D698000);
+
+ /* ip_str_to_addr("109.105.128.0", &addr);
+ * result = ip_addr_get_bits(&addr, 0, 8);
+ * printf("%u\n", result.u.addr4.addr);
+ */
+ char buf[INET_ADDRSTRLEN];
+
+ assert(lrtr_ip_str_to_addr("10.10.10.5", &addr) == 0);
+ assert(lrtr_ip_addr_to_str(&addr, buf, sizeof(buf)) == 0);
+ assert(strcmp("10.10.10.5", buf) == 0);
+}
+
+/*
+ * @brief Test IPv6 address bit operations required by trie
+ */
+static void get_bits_testv6(void)
+{
+ struct lrtr_ip_addr addr;
+ struct lrtr_ip_addr result;
+
+ addr.ver = LRTR_IPV6;
+ addr.u.addr6.addr[0] = 0x22AABBCC;
+ addr.u.addr6.addr[1] = 0xDDEEFF99;
+ addr.u.addr6.addr[2] = 0x33001122;
+ addr.u.addr6.addr[3] = 0x33445566;
+
+ result = lrtr_ip_addr_get_bits(&addr, 0, 128);
+ assert(result.u.addr6.addr[0] == addr.u.addr6.addr[0] && result.u.addr6.addr[1] == addr.u.addr6.addr[1] &&
+ result.u.addr6.addr[2] == addr.u.addr6.addr[2] && result.u.addr6.addr[3] == addr.u.addr6.addr[3]);
+
+ result = lrtr_ip_addr_get_bits(&addr, 0, 64);
+ assert(result.u.addr6.addr[0] == addr.u.addr6.addr[0] && result.u.addr6.addr[1] == addr.u.addr6.addr[1] &&
+ result.u.addr6.addr[2] == 0 && result.u.addr6.addr[3] == 0);
+
+ bzero(&result, sizeof(result));
+ result = lrtr_ip_addr_get_bits(&addr, 64, 64);
+ assert(result.u.addr6.addr[0] == 0);
+ assert(result.u.addr6.addr[1] == 0);
+ assert(result.u.addr6.addr[2] == addr.u.addr6.addr[2]);
+ assert(result.u.addr6.addr[3] == addr.u.addr6.addr[3]);
+
+ result = lrtr_ip_addr_get_bits(&addr, 0, 8);
+ assert(result.u.addr6.addr[0] == 0x22000000 && result.u.addr6.addr[1] == 0);
+
+ result = lrtr_ip_addr_get_bits(&addr, 64, 8);
+ assert(result.u.addr6.addr[1] == 0 && result.u.addr6.addr[2] == 0x33000000);
+
+ result = lrtr_ip_addr_get_bits(&addr, 7, 8);
+ assert(result.u.addr6.addr[0] == 0xAA0000 && result.u.addr6.addr[1] == 0);
+
+ result = lrtr_ip_addr_get_bits(&addr, 68, 7);
+ assert(result.u.addr6.addr[0] == 0 && result.u.addr6.addr[2] == 0x03000000);
+
+ char buf[INET6_ADDRSTRLEN];
+
+ lrtr_ip_str_to_addr("fe80::862b:2bff:fe9a:f50f", &addr);
+ addr.ver = LRTR_IPV6;
+ assert(addr.u.addr6.addr[0] == 0xfe800000);
+ assert(addr.u.addr6.addr[1] == 0);
+ assert(addr.u.addr6.addr[2] == 0x862b2bff);
+ assert(addr.u.addr6.addr[3] == 0xfe9af50f);
+
+ assert(lrtr_ip_str_to_addr("2001::", &addr) == 0);
+ assert(addr.u.addr6.addr[0] == 0x20010000);
+ assert(addr.u.addr6.addr[1] == 0);
+ assert(addr.u.addr6.addr[2] == 0);
+ assert(addr.u.addr6.addr[3] == 0);
+
+ assert(lrtr_ip_addr_to_str(&addr, buf, sizeof(buf)) == 0);
+ assert(strcmp("2001::", buf) == 0);
+
+ lrtr_ip_str_to_addr("2001:0db8:85a3:08d3:1319:8a2e:0370:7344", &addr);
+ assert(lrtr_ip_addr_to_str(&addr, buf, sizeof(buf)) == 0);
+ assert(strcmp("2001:db8:85a3:8d3:1319:8a2e:370:7344", buf) == 0);
+
+ result = lrtr_ip_addr_get_bits(&addr, 0, 16);
+ assert(lrtr_ip_addr_to_str(&result, buf, sizeof(buf)) == 0);
+ assert(lrtr_ip_str_cmp(&result, "2001::"));
+
+ result = lrtr_ip_addr_get_bits(&addr, 16, 16);
+ assert(lrtr_ip_addr_to_str(&result, buf, sizeof(buf)) == 0);
+ assert(lrtr_ip_str_cmp(&result, "0:db8::"));
+ result = lrtr_ip_addr_get_bits(&addr, 0, 1);
+ assert(lrtr_ip_str_cmp(&result, "::"));
+
+ result = lrtr_ip_addr_get_bits(&addr, 126, 1);
+ assert(lrtr_ip_addr_to_str(&result, buf, sizeof(buf)) == 0);
+ assert(lrtr_ip_str_cmp(&result, "::"));
+}
+
+int main(void)
+{
+ get_bits_testv4();
+ get_bits_testv6();
+ printf("Test successful\n");
+ return EXIT_SUCCESS;
+}
diff --git a/tests/test_ht_spkitable.c b/tests/test_ht_spkitable.c
new file mode 100644
index 0000000..ad6178d
--- /dev/null
+++ b/tests/test_ht_spkitable.c
@@ -0,0 +1,649 @@
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website: http://rtrlib.realmv6.org/
+ */
+
+#include "rtrlib/rtr/rtr_private.h"
+#include "rtrlib/spki/hashtable/ht-spkitable_private.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+/* test_ht_4 */
+#define NUM_TABLE_X (50)
+#define NUM_TABLE_Y (50)
+/* test_ht_6 */
+#define NUM_SKIS (10)
+#define NUM_SKIS_RECORDS (10)
+/* test_ht_7 */
+#define NUM_OF_SKI (200)
+#define NUM_OF_RECORDS (2000)
+
+/**
+ * @brief Compare SPKI records for equality
+ *
+ * @return true if r1 == r2, false otherwise
+ */
+static bool spki_records_are_equal(struct spki_record *r1, struct spki_record *r2)
+{
+ if (r1->asn != r2->asn)
+ return false;
+ if (r1->socket != r2->socket)
+ return false;
+ if (memcmp(r1->ski, r2->ski, SKI_SIZE) != 0)
+ return false;
+ if (memcmp(r1->spki, r2->spki, SPKI_SIZE) != 0)
+ return false;
+
+ return true;
+}
+
+/**
+ * @brief Create a SPKI record
+ *
+ * @return new SPKI record
+ */
+static struct spki_record *create_record(int ASN, int ski_offset, int spki_offset, struct rtr_socket *socket)
+{
+ struct spki_record *record = malloc(sizeof(struct spki_record));
+ uint32_t i;
+
+ memset(record, 0, sizeof(*record));
+ record->asn = ASN;
+
+ for (i = 0; i < sizeof(record->ski) / sizeof(uint32_t); i++)
+ ((uint32_t *)record->ski)[i] = i + ski_offset;
+
+ for (i = 0; i < sizeof(record->spki) / sizeof(uint32_t); i++)
+ ((uint32_t *)record->spki)[i] = i + spki_offset;
+
+ record->socket = socket;
+ return record;
+}
+
+/**
+ * @brief Helper shortcut, to assert insert of table entry
+ * Assert fails if memory allocation for new entry fails OR
+ * if the entry already exists.
+ */
+static void _spki_table_add_assert(struct spki_table *table, struct spki_record *record)
+{
+ assert(spki_table_add_entry(table, record) == SPKI_SUCCESS);
+}
+
+/**
+ * @brief Helper shortcut, to assert remove of table entry
+ * Assert fails if entry does not exist in table, or any search error occurs.
+ */
+static void _spki_table_remove_assert(struct spki_table *table, struct spki_record *record)
+{
+ assert(spki_table_remove_entry(table, record) == SPKI_SUCCESS);
+}
+
+/**
+ * @brief Helper shortcut, to assert search of table entry
+ * Assert fails if a matching enrty is found, but memory allocation fails OR
+ * if number of results does not match reference value: NUM_SKIS_RECORDS.
+ */
+static void _spki_table_search_assert(struct spki_table *table, uint8_t *ski)
+{
+ struct spki_record *result;
+ unsigned int result_len;
+
+ assert(spki_table_search_by_ski(table, ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == NUM_SKIS_RECORDS);
+ free(result);
+}
+
+/* ----- TESTS FUNCTIONS ----- */
+
+/**
+ * @brief Test of spki_table_src_remove function
+ * Test if the spki_table_src_remove function is working correctly by adding
+ * spki_record associated with different rtr_socket.
+ * Then call spki_table_src_remove with one of the sockets as argument and
+ * validate that all records associated with different sockets are still there.
+ */
+static void test_ht_1(void)
+{
+ struct spki_table table;
+ struct rtr_socket *socket_one = malloc(sizeof(struct rtr_socket));
+ struct rtr_socket *socket_two = malloc(sizeof(struct rtr_socket));
+ uint8_t ski[SKI_SIZE];
+ uint32_t asn = 1;
+
+ /* test create record */
+ struct spki_record *record = create_record(1, 0, 0, NULL);
+
+ spki_table_init(&table, NULL);
+ memcpy(ski, record->ski, 20);
+ free(record);
+
+ /* create and add records with either socket_one or socket_two */
+ for (int i = 0; i < 255; i++) {
+ if (i % 2)
+ record = create_record(1, 0, i, socket_one);
+ else
+ record = create_record(1, 0, i, socket_two);
+
+ _spki_table_add_assert(&table, record);
+ free(record);
+ }
+
+ struct spki_record *result;
+ unsigned int result_len;
+ int count = 0;
+ /* verify all (255) records for asn and ski size */
+ spki_table_get_all(&table, asn, ski, &result, &result_len);
+ for (unsigned int i = 0; i < result_len; i++) {
+ assert(result->asn == asn);
+ assert(memcmp(&result[i].ski, ski, SKI_SIZE) == 0);
+ count++;
+ }
+ assert(count == 255);
+ free(result);
+
+ /* remove all records with socket_one */
+ spki_table_src_remove(&table, socket_one);
+ /* verify remaining records have socket_two */
+ spki_table_get_all(&table, asn, ski, &result, &result_len);
+ for (unsigned int i = 0; i < result_len; i++) {
+ assert(result[i].asn == asn);
+ assert(memcmp(&result[i].ski, ski, SKI_SIZE) == 0);
+ assert(result[i].socket == socket_two);
+ }
+
+ /* cleanup: free memory */
+ spki_table_free(&table);
+ free(result);
+ free(socket_one);
+ free(socket_two);
+ printf("%s() complete\n", __func__);
+}
+
+/**
+ * @brief Test of spki_table_get_all with equal records
+ * Check the behaviour of spki_table_get_all if we add spki_record with
+ * different SPKI values but same SKI values (Hash collision).
+ */
+static void test_ht_2(void)
+{
+ struct spki_table table;
+ struct spki_record *result;
+ unsigned int result_len;
+ /* create 2 distinct records */
+ struct spki_record *record1 = create_record(10, 20, 30, NULL);
+ struct spki_record *record2 = create_record(10, 20, 40, NULL);
+ /* TEST1: verify 2 diff SPKIs hash to the same SKI */
+ spki_table_init(&table, NULL);
+ _spki_table_add_assert(&table, record1);
+ _spki_table_add_assert(&table, record2);
+ spki_table_get_all(&table, 10, record1->ski, &result, &result_len);
+ assert(result_len == 2);
+
+ /* TEST2: check that returned records are the same we added. */
+ assert(spki_records_are_equal(&result[0], record1) != spki_records_are_equal(&result[0], record2));
+ assert(spki_records_are_equal(&result[1], record1) != spki_records_are_equal(&result[1], record2));
+ free(result);
+
+ /* TEST3: remove record1 and verify result is record2 */
+ _spki_table_remove_assert(&table, record1);
+ spki_table_get_all(&table, 10, record1->ski, &result, &result_len);
+ assert(result_len == 1);
+ assert(spki_records_are_equal(&result[0], record2));
+ free(result);
+
+ /*TEST4: remove record2 and verify search result is empty */
+ _spki_table_remove_assert(&table, record2);
+ spki_table_get_all(&table, 10, record1->ski, &result, &result_len);
+ assert(result_len == 0);
+ assert(!result);
+ spki_table_get_all(&table, 10, record2->ski, &result, &result_len);
+ assert(result_len == 0);
+ assert(!result);
+
+ /* cleanup: free memory */
+ free(record1);
+ free(record2);
+ spki_table_free(&table);
+ printf("%s() complete\n", __func__);
+}
+
+/**
+ * @brief Test spki_table_get_all with records, differing in one attribute
+ * Test if the compare function for spki_record work correctly:
+ * Add spki_records which only differ in one attribute and delete one
+ * of the records, then check if the other records are still there.
+ */
+static void test_ht_3(void)
+{
+ struct spki_table table;
+ struct spki_record *result;
+ unsigned int result_len;
+
+ /* create 4 distinct SPKI records, (at least) one parameter different */
+ struct spki_record *record1 = create_record(10, 10, 10, NULL);
+ struct spki_record *record2 = create_record(10, 10, 11, NULL);
+ struct spki_record *record3 = create_record(10, 11, 10, NULL);
+ struct spki_record *record4 = create_record(11, 10, 10, NULL);
+
+ /* TEST0:init table and add records -> checks compare function */
+ spki_table_init(&table, NULL);
+ _spki_table_add_assert(&table, record1);
+ _spki_table_add_assert(&table, record2);
+ _spki_table_add_assert(&table, record3);
+ _spki_table_add_assert(&table, record4);
+
+ /* TEST1: remove record1, check others -------------------------------*/
+ _spki_table_remove_assert(&table, record1);
+ /* Check if other records are still there */
+ assert(spki_table_get_all(&table, record2->asn, record2->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+
+ assert(spki_table_get_all(&table, record3->asn, record3->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+
+ assert(spki_table_get_all(&table, record4->asn, record4->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+ /* (re)add record1 */
+ _spki_table_add_assert(&table, record1);
+
+ /* TEST2: remove record2, check others -------------------------------*/
+ _spki_table_remove_assert(&table, record2);
+ /* Check if other records are still there */
+ assert(spki_table_get_all(&table, record1->asn, record1->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+
+ assert(spki_table_get_all(&table, record3->asn, record3->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+
+ assert(spki_table_get_all(&table, record4->asn, record4->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+ /* (re)add record2 */
+ _spki_table_add_assert(&table, record2);
+
+ /* TEST3: remove record3, check others -------------------------------*/
+ _spki_table_remove_assert(&table, record3);
+ /* Check if other records are still there */
+ assert(spki_table_get_all(&table, record1->asn, record1->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 2);
+ free(result);
+
+ assert(spki_table_get_all(&table, record2->asn, record2->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 2);
+ free(result);
+
+ assert(spki_table_get_all(&table, record4->asn, record4->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+ /* (re)add record3 */
+ _spki_table_add_assert(&table, record3);
+
+ /* TEST4: remove record4, check others -------------------------------*/
+ _spki_table_remove_assert(&table, record4);
+ /* Check if other records are still there */
+ assert(spki_table_get_all(&table, record1->asn, record1->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 2);
+ free(result);
+
+ assert(spki_table_get_all(&table, record2->asn, record2->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 2);
+ free(result);
+
+ assert(spki_table_get_all(&table, record3->asn, record3->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+
+ /* cleanup: free memory */
+ spki_table_free(&table);
+ free(record1);
+ free(record2);
+ free(record3);
+ free(record4);
+ printf("%s complete\n", __func__);
+}
+
+/**
+ * @brief Test remove records one by one until spki table is empty
+ * Test if all added records can be deleted and test if any of the added
+ * records retain in the table although they got deleted.
+ */
+static void test_ht_4(void)
+{
+ struct spki_table table;
+ struct spki_record *result;
+ unsigned int result_len;
+ struct spki_record *records[NUM_TABLE_X][NUM_TABLE_Y];
+
+ spki_table_init(&table, NULL);
+ /* Add 50 * 50 entries */
+ for (int i = 0; i < NUM_TABLE_X; i++) {
+ for (int j = 0; j < NUM_TABLE_Y; j++) {
+ records[i][j] = create_record(i, j, j, NULL);
+ _spki_table_add_assert(&table, records[i][j]);
+ }
+ }
+
+ /* Check if every record is there and then delete it */
+ for (int i = 0; i < NUM_TABLE_X; i++) {
+ for (int j = 0; j < NUM_TABLE_Y; j++) {
+ assert(spki_table_get_all(&table, records[i][j]->asn, records[i][j]->ski, &result,
+ &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ _spki_table_remove_assert(&table, records[i][j]);
+ free(result);
+ free(records[i][j]);
+ }
+ }
+
+ /* Add all record again and look for SPKI_DUPLICATE_RECORD */
+ for (int i = 0; i < NUM_TABLE_X; i++) {
+ for (int j = 0; j < NUM_TABLE_Y; j++) {
+ records[i][j] = create_record(i, j, j, NULL);
+ _spki_table_add_assert(&table, records[i][j]);
+ free(records[i][j]);
+ }
+ }
+
+ /* cleanup: free memory */
+ spki_table_free(&table);
+ printf("%s() complete\n", __func__);
+}
+
+/**
+ * @brief Test for SPKI_DUPLICATE_RECORD
+ * Test the behavior if equal spki_records get added to the table.
+ */
+static void test_ht_5(void)
+{
+ struct spki_table table;
+ /* create 2 equal records */
+ struct spki_record *record1 = create_record(10, 10, 10, NULL);
+ struct spki_record *record2 = create_record(10, 10, 10, NULL);
+
+ /* create table and (try) add records -> check for duplicates */
+ spki_table_init(&table, NULL);
+ assert(spki_table_add_entry(&table, record1) == SPKI_SUCCESS);
+ assert(spki_table_add_entry(&table, record2) == SPKI_DUPLICATE_RECORD);
+ assert(spki_table_add_entry(&table, record1) == SPKI_DUPLICATE_RECORD);
+
+ struct spki_record *result;
+ unsigned int result_len;
+
+ /* check that only record1 is in table and matches query */
+ spki_table_get_all(&table, 10, record1->ski, &result, &result_len);
+ assert(result_len == 1);
+ free(result);
+
+ /* cleanup: free memory */
+ spki_table_free(&table);
+ free(record1);
+ free(record2);
+ printf("%s() complete\n", __func__);
+}
+
+/**
+ * @brief Test spki_table_search_by_ski
+ * Test if all spki_records with the same SKI get returned.
+ */
+static void test_ht_6(void)
+{
+ struct spki_table table;
+ struct spki_record *records[NUM_SKIS][NUM_SKIS_RECORDS];
+ /* Add the records to the table */
+ spki_table_init(&table, NULL);
+ for (unsigned int i = 0; i < NUM_SKIS; i++) {
+ for (unsigned int j = 0; j < NUM_SKIS_RECORDS; j++) {
+ records[i][j] = create_record(j, i, j, NULL);
+ _spki_table_add_assert(&table, records[i][j]);
+ }
+ }
+
+ /* Search for records by SKI and check result */
+ for (unsigned int i = 0; i < NUM_SKIS; i++) {
+ for (unsigned int j = 0; j < NUM_SKIS_RECORDS; j++) {
+ _spki_table_search_assert(&table, records[i][j]->ski);
+ free(records[i][j]);
+ }
+ }
+
+ /* cleanup: free memory */
+ spki_table_free(&table);
+ printf("%s() complete\n", __func__);
+}
+
+/**
+ * @brief Test releation between ASN and SKI
+ * This tests has 3 parts, in each part spki records are created and validated:
+ * a) spki records with same ASN but different SKI: ASN(1) <---> SKI(n)
+ * b) spki records with different ASN but same SKI: ASN(n) <---> SKI(1)
+ * c) spki records with different ASN and diff SKI: ASN(n) <---> SKI(n)
+ */
+static void test_ht_7(void)
+{
+ struct spki_table table;
+ struct spki_record *records[NUM_OF_RECORDS];
+ /* ASN(1) <---> SKI(n) ---------------------------------------------- */
+ spki_table_init(&table, NULL);
+ /* Create NUM_OF_RECORDS spki_records with same ASN but different SKI */
+ for (unsigned int i = 0; i < NUM_OF_RECORDS; i++) {
+ records[i] = create_record(2555, i, i, NULL);
+ _spki_table_add_assert(&table, records[i]);
+ }
+
+ /* Validate */
+ for (unsigned int i = 0; i < NUM_OF_RECORDS; i++) {
+ struct spki_record *result;
+ unsigned int size = 0;
+
+ spki_table_get_all(&table, 2555, records[i]->ski, &result, &size);
+
+ assert(size == 1);
+ assert(spki_records_are_equal(records[i], &result[0]));
+ free(result);
+ free(records[i]);
+ }
+ spki_table_free(&table);
+
+ /* ASN(n) <---> SKI(1) ---------------------------------------------- */
+ spki_table_init(&table, NULL);
+ /* Create NUM_OF_RECORDS spki_records with same SKI but different ASN */
+ for (unsigned int i = 0; i < NUM_OF_RECORDS; i++) {
+ records[i] = create_record(i, 100, 100, NULL);
+ _spki_table_add_assert(&table, records[i]);
+ }
+
+ /* Validate */
+ for (unsigned int i = 0; i < NUM_OF_RECORDS; i++) {
+ struct spki_record *result;
+ unsigned int size = 0;
+
+ spki_table_get_all(&table, i, records[NUM_OF_RECORDS - 1]->ski, &result, &size);
+
+ assert(size == 1);
+ assert(spki_records_are_equal(records[i], &result[0]));
+ free(result);
+ free(records[i]);
+ }
+ spki_table_free(&table);
+
+ struct spki_record *records_n_n[NUM_OF_RECORDS][NUM_OF_SKI];
+ /* ASN(n) <---> SKI(n) ---------------------------------------------- */
+ spki_table_init(&table, NULL);
+ /* Create: {NUM_OF_RECORDS} x {NUM_OF_SKI} spki_records */
+ /* {ASN_0,ASN_1...} x {SKI_0, SKI_1...} */
+ for (unsigned int i = 0; i < NUM_OF_RECORDS; i++) {
+ for (unsigned int j = 0; j < NUM_OF_SKI; j++) {
+ records_n_n[i][j] = create_record(i, j, j, NULL);
+ _spki_table_add_assert(&table, records_n_n[i][j]);
+ }
+ }
+
+ /* Validate */
+ for (unsigned int i = 0; i < NUM_OF_RECORDS; i++) {
+ for (unsigned int j = 0; j < NUM_OF_SKI; j++) {
+ struct spki_record *result;
+ unsigned int size = 0;
+
+ spki_table_get_all(&table, i, records_n_n[i][j]->ski, &result, &size);
+
+ assert(size == 1);
+ assert(spki_records_are_equal(records_n_n[i][j], &result[0]));
+ free(result);
+ free(records_n_n[i][j]);
+ }
+ }
+
+ /* cleanup: free memory */
+ spki_table_free(&table);
+ printf("%s() complete\n", __func__);
+}
+
+static void test_table_swap(void)
+{
+ struct spki_table table1;
+ struct spki_table table2;
+
+ struct spki_record *test_record1 = create_record(1, 10, 100, NULL);
+ struct spki_record *test_record2 = create_record(2, 20, 200, NULL);
+
+ spki_table_init(&table1, NULL);
+ spki_table_init(&table2, NULL);
+
+ _spki_table_add_assert(&table1, test_record1);
+ _spki_table_add_assert(&table2, test_record2);
+
+ struct spki_record *result;
+ unsigned int result_len;
+
+ assert(spki_table_search_by_ski(&table1, test_record1->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+ result = NULL;
+
+ assert(spki_table_search_by_ski(&table2, test_record2->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+ result = NULL;
+
+ spki_table_swap(&table1, &table2);
+
+ assert(spki_table_search_by_ski(&table1, test_record2->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+ result = NULL;
+
+ assert(spki_table_search_by_ski(&table2, test_record1->ski, &result, &result_len) == SPKI_SUCCESS);
+ assert(result_len == 1);
+ free(result);
+ result = NULL;
+
+ spki_table_free(&table1);
+ spki_table_free(&table2);
+ free(test_record1);
+ free(test_record2);
+
+ printf("%s() complete\n", __func__);
+}
+
+static void update_spki(struct spki_table *s __attribute__((unused)), const struct spki_record record, const bool added)
+{
+ printf("%c ASN: %u\n", (added ? '+' : '-'), record.asn);
+
+ int i;
+ int size = sizeof(record.ski);
+
+ printf("SKI: ");
+ for (i = 0; i < size; i++) {
+ printf("%02x", record.ski[i]);
+ if (i < size - 1)
+ printf(":");
+ }
+ printf("\n ");
+
+ i = 0;
+ size = sizeof(record.spki);
+ printf("SPKI: ");
+ for (i = 0; i < size; i++) {
+ if ((i % 40 == 0) && (i != 0))
+ printf("\n ");
+
+ printf("%02x", record.spki[i]);
+ if (i < size - 1)
+ printf(":");
+ }
+ printf("\n");
+
+ if (record.asn == 1)
+ assert(!added);
+ if (record.asn == 2)
+ assert(false);
+ if (record.asn == 3)
+ assert(added);
+}
+
+static void test_table_diff(void)
+{
+ struct spki_table table1;
+ struct spki_table table2;
+ struct rtr_socket *socket = (struct rtr_socket *)1;
+
+ struct spki_record *test_record1 = create_record(1, 10, 100, socket);
+ struct spki_record *test_record2 = create_record(2, 20, 200, socket);
+ struct spki_record *test_record3 = create_record(3, 30, 300, socket);
+
+ spki_table_init(&table1, NULL);
+ spki_table_init(&table2, NULL);
+
+ printf("Adding to table 1\n");
+ _spki_table_add_assert(&table1, test_record1);
+ _spki_table_add_assert(&table1, test_record2);
+
+ printf("Adding to table 2\n");
+ _spki_table_add_assert(&table2, test_record2);
+ _spki_table_add_assert(&table2, test_record3);
+
+ printf("Calculating diff\n");
+ table1.update_fp = update_spki;
+ table2.update_fp = update_spki;
+ spki_table_notify_diff(&table2, &table1, socket);
+ table1.update_fp = NULL;
+ table2.update_fp = NULL;
+
+ printf("Freeing tables\n");
+ spki_table_free(&table1);
+ spki_table_free(&table2);
+ free(test_record1);
+ free(test_record2);
+ free(test_record3);
+
+ printf("%s() complete\n", __func__);
+}
+
+int main(void)
+{
+ test_ht_1();
+ test_ht_2();
+ test_ht_3();
+ test_ht_4();
+ test_ht_5();
+ test_ht_6();
+ test_ht_7();
+ test_table_swap();
+ test_table_diff();
+ return EXIT_SUCCESS;
+}
diff --git a/tests/test_ht_spkitable_locks.c b/tests/test_ht_spkitable_locks.c
new file mode 100644
index 0000000..9e19810
--- /dev/null
+++ b/tests/test_ht_spkitable_locks.c
@@ -0,0 +1,174 @@
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website: http://rtrlib.realmv6.org/
+ */
+
+#include "rtrlib/spki/hashtable/ht-spkitable_private.h"
+
+#include <assert.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+
+struct add_records_args {
+ struct spki_table *table;
+ int start_asn;
+ int count;
+};
+
+struct remove_records_args {
+ struct spki_table *table;
+ int start_asn;
+ int count;
+};
+
+static struct spki_record *create_record(int ASN, int ski_offset, int spki_offset, struct rtr_socket *socket);
+
+/**
+ * @brief Compare SPKI records for equality
+ *
+ * @return true if r1 == r2, false otherwise
+ */
+static bool compare_spki_records(struct spki_record *r1, struct spki_record *r2)
+{
+ if (r1->asn != r2->asn)
+ return false;
+ if (r1->socket != r2->socket)
+ return false;
+ if (memcmp(r1->ski, r2->ski, SKI_SIZE) != 0)
+ return false;
+ if (memcmp(r1->spki, r2->spki, SPKI_SIZE) != 0)
+ return false;
+
+ return true;
+}
+
+/**
+ * @brief Create a SPKI record
+ *
+ * @return new SPKI record
+ */
+static struct spki_record *create_record(int ASN, int ski_offset, int spki_offset, struct rtr_socket *socket)
+{
+ struct spki_record *record = malloc(sizeof(struct spki_record));
+ uint32_t i;
+
+ record->asn = ASN;
+
+ for (i = 0; i < sizeof(record->ski); i++)
+ record->ski[i] = i + ski_offset;
+
+ for (i = 0; i < sizeof(record->spki); i++)
+ record->spki[i] = i + spki_offset;
+
+ record->socket = socket;
+ return record;
+}
+
+/**
+ * @brief Add records to spki table
+ * Add 'args->count' records to the spki table 'args->table', start with
+ * ASN 'args->start_asn'.
+ */
+static void *add_records(struct add_records_args *args)
+{
+ printf("Add %i records: ASN [%i..%i]\n", args->count, args->start_asn, args->count + args->start_asn - 1);
+ for (int i = args->start_asn; i < args->count + args->start_asn; i++) {
+ struct spki_record *record = create_record(i, i, i, NULL);
+ int ret = spki_table_add_entry(args->table, record);
+
+ assert(ret == SPKI_SUCCESS);
+ free(record);
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief remove records from spki table
+ * Remove 'args->count' records from the spki table 'args->table'.
+ */
+static void *remove_records(struct remove_records_args *args)
+{
+ printf("Remove %i records: ASN [%i..%i]\n", args->count, args->start_asn, args->count + args->start_asn - 1);
+ for (int i = args->start_asn; i < args->count + args->start_asn; i++) {
+ struct spki_record *record = create_record(i, i, i, NULL);
+ int ret = spki_table_remove_entry(args->table, record);
+
+ assert(ret == SPKI_SUCCESS);
+ free(record);
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Test concurrent add and delete operations on spki table
+ */
+static void lock_test1(void)
+{
+ unsigned int max_threads = 20;
+ unsigned int records_per_thread = 10000;
+ struct spki_table spkit;
+ struct add_records_args args[max_threads];
+
+ spki_table_init(&spkit, NULL);
+ pthread_t threads[max_threads];
+
+ /* Concurrently add SPKI records to the table */
+ for (unsigned int i = 0; i < max_threads; i++) {
+ args[i].table = &spkit;
+ args[i].start_asn = i * records_per_thread;
+ args[i].count = records_per_thread;
+ pthread_create(&threads[i], NULL, (void *(*)(void *))add_records, &args[i]);
+ }
+
+ /* Wait for parallel add operations to finish */
+ for (unsigned int i = 0; i < max_threads; i++) {
+ pthread_join(threads[i], NULL);
+ printf("Thread %i returned\n", i);
+ }
+
+ /* Check all records for successful add */
+ struct spki_record *result;
+ unsigned int result_size = 0;
+
+ for (unsigned int i = 0; i < records_per_thread * max_threads; i++) {
+ struct spki_record *record = create_record(i, i, i, NULL);
+
+ spki_table_get_all(&spkit, record->asn, record->ski, &result, &result_size);
+ assert(result_size == 1);
+ assert(compare_spki_records(record, &result[0]));
+ free(result);
+ free(record);
+ }
+
+ struct remove_records_args remove_args[max_threads];
+ /* Concurrently delete SPKI records */
+ for (unsigned int i = 0; i < max_threads; i++) {
+ remove_args[i].table = &spkit;
+ remove_args[i].start_asn = i * records_per_thread;
+ remove_args[i].count = records_per_thread;
+ pthread_create(&threads[i], NULL, (void *(*)(void *))remove_records, &remove_args[i]);
+ }
+
+ /* Wait for parallel delete operation to finish */
+ for (unsigned int i = 0; i < max_threads; i++) {
+ pthread_join(threads[i], NULL);
+ printf("Thread %i returned\n", i);
+ }
+
+ /* cleanup: free spki_table */
+ spki_table_free(&spkit);
+ printf("%s() complete\n", __func__);
+}
+
+int main(void)
+{
+ lock_test1();
+ return EXIT_SUCCESS;
+}
diff --git a/tests/test_ipaddr.c b/tests/test_ipaddr.c
new file mode 100644
index 0000000..c0fea5b
--- /dev/null
+++ b/tests/test_ipaddr.c
@@ -0,0 +1,256 @@
+
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website: http://rtrlib.realmv6.org/
+ */
+
+#include "rtrlib/lib/ip.h"
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * @brief test ipv4 parsing
+ */
+static void test_v4(void)
+{
+ struct lrtr_ip_addr addr;
+ char buf[INET_ADDRSTRLEN];
+
+ lrtr_ip_str_to_addr("0.0.0.0", &addr);
+ assert(addr.ver == LRTR_IPV4);
+ assert(addr.u.addr4.addr == 0);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("0.0.0.0", buf) == 0);
+
+ lrtr_ip_str_to_addr("255.255.255.255", &addr);
+ assert(addr.ver == LRTR_IPV4);
+ assert(addr.u.addr4.addr == 0xffffffff);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("255.255.255.255", buf) == 0);
+
+ lrtr_ip_str_to_addr("0.2.6.7", &addr);
+ assert(addr.ver == LRTR_IPV4);
+ assert(addr.u.addr4.addr == 0x20607);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("0.2.6.7", buf) == 0);
+
+ lrtr_ip_str_to_addr("78.69.255.0", &addr);
+ assert(addr.ver == LRTR_IPV4);
+ assert(addr.u.addr4.addr == 0x4e45ff00);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("78.69.255.0", buf) == 0);
+
+ lrtr_ip_str_to_addr("1.1.1.1", &addr);
+ assert(addr.ver == LRTR_IPV4);
+ assert(addr.u.addr4.addr == 0x1010101);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("1.1.1.1", buf) == 0);
+
+ lrtr_ip_str_to_addr("5.0.255.255", &addr);
+ assert(addr.ver == LRTR_IPV4);
+ assert(addr.u.addr4.addr == 0x500ffff);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("5.0.255.255", buf) == 0);
+
+ lrtr_ip_str_to_addr("8.9.6.3", &addr);
+ assert(addr.ver == LRTR_IPV4);
+ assert(addr.u.addr4.addr == 0x8090603);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("8.9.6.3", buf) == 0);
+
+ /* check some malformed addresses */
+ assert(lrtr_ip_str_to_addr("8,3,4,5", &addr) == -1);
+ assert(lrtr_ip_str_to_addr("8.4.5", &addr) == -1);
+}
+
+/*
+ * @brief test ipv6 parsing
+ */
+static void test_v6(void)
+{
+ struct lrtr_ip_addr addr;
+ char buf[INET6_ADDRSTRLEN];
+
+ lrtr_ip_str_to_addr("fdf8:f53b:82e4::53", &addr);
+ assert(addr.u.addr6.addr[0] == 0xfdf8f53b);
+ assert(addr.u.addr6.addr[1] == 0x82e40000);
+ assert(addr.u.addr6.addr[2] == 0);
+ assert(addr.u.addr6.addr[3] == 0x53);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("fdf8:f53b:82e4::53", buf) == 0);
+
+ lrtr_ip_str_to_addr("fe80::200:5aee:feaa:20a2", &addr);
+ assert(addr.u.addr6.addr[0] == 0xfe800000);
+ assert(addr.u.addr6.addr[1] == 0);
+ assert(addr.u.addr6.addr[2] == 0x2005aee);
+ assert(addr.u.addr6.addr[3] == 0xfeaa20a2);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("fe80::200:5aee:feaa:20a2", buf) == 0);
+
+ lrtr_ip_str_to_addr("2001::1", &addr);
+ assert(addr.u.addr6.addr[0] == 0x20010000);
+ assert(addr.u.addr6.addr[1] == 0);
+ assert(addr.u.addr6.addr[2] == 0);
+ assert(addr.u.addr6.addr[3] == 0x1);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("2001::1", buf) == 0);
+
+ lrtr_ip_str_to_addr("2001:0:4136:e378:8000:63bf:3fff:fdd2", &addr);
+ assert(addr.u.addr6.addr[0] == 0x20010000);
+ assert(addr.u.addr6.addr[1] == 0x4136e378);
+ assert(addr.u.addr6.addr[2] == 0x800063bf);
+ assert(addr.u.addr6.addr[3] == 0x3ffffdd2);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("2001:0:4136:e378:8000:63bf:3fff:fdd2", buf) == 0);
+
+ lrtr_ip_str_to_addr("2001:2:6c::430", &addr);
+ assert(addr.u.addr6.addr[0] == 0x20010002);
+ assert(addr.u.addr6.addr[1] == 0x6C0000);
+ assert(addr.u.addr6.addr[2] == 0);
+ assert(addr.u.addr6.addr[3] == 0x430);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("2001:2:6c::430", buf) == 0);
+
+ lrtr_ip_str_to_addr("2001:10:240:ab::a", &addr);
+ assert(addr.u.addr6.addr[0] == 0x20010010);
+ assert(addr.u.addr6.addr[1] == 0x24000AB);
+ assert(addr.u.addr6.addr[2] == 0);
+ assert(addr.u.addr6.addr[3] == 0xa);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("2001:10:240:ab::a", buf) == 0);
+
+ lrtr_ip_str_to_addr("2002:cb0a:3cdd:1::1", &addr);
+ assert(addr.u.addr6.addr[0] == 0x2002cb0a);
+ assert(addr.u.addr6.addr[1] == 0x3cdd0001);
+ assert(addr.u.addr6.addr[2] == 0);
+ assert(addr.u.addr6.addr[3] == 0x1);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("2002:cb0a:3cdd:1::1", buf) == 0);
+
+ lrtr_ip_str_to_addr("2001:db8:8:4::2", &addr);
+ assert(addr.u.addr6.addr[0] == 0x20010db8);
+ assert(addr.u.addr6.addr[1] == 0x80004);
+ assert(addr.u.addr6.addr[2] == 0);
+ assert(addr.u.addr6.addr[3] == 0x2);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("2001:db8:8:4::2", buf) == 0);
+
+ lrtr_ip_str_to_addr("FF01:0:0:0:0:0:0:2", &addr);
+ assert(addr.u.addr6.addr[0] == 0xff010000);
+ assert(addr.u.addr6.addr[1] == 0);
+ assert(addr.u.addr6.addr[2] == 0);
+ assert(addr.u.addr6.addr[3] == 0x2);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("ff01::2", buf) == 0);
+
+ lrtr_ip_str_to_addr("fdf8:f53b:82e4::53", &addr);
+ assert(addr.u.addr6.addr[0] == 0xfdf8f53b);
+ assert(addr.u.addr6.addr[1] == 0x82e40000);
+ assert(addr.u.addr6.addr[2] == 0);
+ assert(addr.u.addr6.addr[3] == 0x53);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("fdf8:f53b:82e4::53", buf) == 0);
+
+ lrtr_ip_str_to_addr("fe80::200:5aee:feaa:20a2", &addr);
+ assert(addr.u.addr6.addr[0] == 0xfe800000);
+ assert(addr.u.addr6.addr[1] == 0);
+ assert(addr.u.addr6.addr[2] == 0x2005aee);
+ assert(addr.u.addr6.addr[3] == 0xfeaa20a2);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("fe80::200:5aee:feaa:20a2", buf) == 0);
+
+ lrtr_ip_str_to_addr("2001::1", &addr);
+ assert(addr.u.addr6.addr[0] == 0x20010000);
+ assert(addr.u.addr6.addr[1] == 0);
+ assert(addr.u.addr6.addr[2] == 0);
+ assert(addr.u.addr6.addr[3] == 1);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("2001::1", buf) == 0);
+
+ lrtr_ip_str_to_addr("2001:0:4136:e378:8000:63bf:3fff:fdd2", &addr);
+ assert(addr.u.addr6.addr[0] == 0x20010000);
+ assert(addr.u.addr6.addr[1] == 0x4136e378);
+ assert(addr.u.addr6.addr[2] == 0x800063bf);
+ assert(addr.u.addr6.addr[3] == 0x3ffffdd2);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("2001:0:4136:e378:8000:63bf:3fff:fdd2", buf) == 0);
+
+ /* test embedded ipv4 */
+ lrtr_ip_str_to_addr("::ffff:192.0.2.128", &addr);
+ assert(addr.u.addr6.addr[0] == 0);
+ assert(addr.u.addr6.addr[1] == 0);
+ assert(addr.u.addr6.addr[2] == 0xffff);
+ assert(addr.u.addr6.addr[3] == 0xc0000280);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("::ffff:192.0.2.128", buf) == 0);
+
+ lrtr_ip_str_to_addr("::10.58.64.34", &addr);
+ assert(addr.u.addr6.addr[0] == 0);
+ assert(addr.u.addr6.addr[1] == 0);
+ assert(addr.u.addr6.addr[2] == 0);
+ assert(addr.u.addr6.addr[3] == 0xa3a4022);
+ lrtr_ip_addr_to_str(&addr, buf, sizeof(buf));
+ assert(strcmp("::10.58.64.34", buf) == 0);
+
+ /* test check for malformed embedded ipv4 */
+ assert(lrtr_ip_str_to_addr("::ffff:192.0,2.128", &addr) == -1);
+
+ /* buffer size check*/
+ assert(lrtr_ip_addr_to_str(&addr, buf, 10) == -1);
+
+ /* test leading single colon check */
+ assert(lrtr_ip_str_to_addr(":ffff::ffff", &addr) == -1);
+
+ /* test multiple double colons check */
+ assert(lrtr_ip_str_to_addr("::ffff::ffff", &addr) == -1);
+
+ /* test check for to long addresses */
+ assert(lrtr_ip_str_to_addr("2001:0:6:8:0:f:3fff:fdd2:55", &addr) == -1);
+
+ /* test check for to big groups */
+ assert(lrtr_ip_str_to_addr("::fffff", &addr) == -1);
+
+ /* check for null byte in address string */
+ assert(lrtr_ip_str_to_addr("2001:\0::", &addr) == -1);
+}
+
+/*
+ * @brief test ip comparisons
+ */
+static void test_cmp(void)
+{
+ struct lrtr_ip_addr addr1, addr2;
+
+ lrtr_ip_str_to_addr("2001:0:4136:e378:8000:63bf:3fff:fdd2", &addr1);
+ lrtr_ip_str_to_addr("2001:0:4136:e378:8000:63bf:3fff:fdd2", &addr2);
+
+ assert(lrtr_ip_addr_equal(addr1, addr2) == true);
+
+ lrtr_ip_str_to_addr("2001:0:4136:e378:8000:63bf:3fff:fdd3", &addr2);
+ assert(lrtr_ip_addr_equal(addr1, addr2) == false);
+
+ lrtr_ip_str_to_addr("141.22.5.22", &addr2);
+ assert(lrtr_ip_addr_equal(addr1, addr2) == false);
+
+ lrtr_ip_str_to_addr("141.22.5.22", &addr1);
+ assert(lrtr_ip_addr_equal(addr1, addr2) == true);
+
+ lrtr_ip_str_to_addr("141.26.5.23", &addr1);
+}
+
+int main(void)
+{
+ test_v4();
+ test_v6();
+ test_cmp();
+ printf("Test successful\n");
+ return EXIT_SUCCESS;
+}
diff --git a/tests/test_live_validation.c b/tests/test_live_validation.c
new file mode 100644
index 0000000..73c8575
--- /dev/null
+++ b/tests/test_live_validation.c
@@ -0,0 +1,121 @@
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website: http://rtrlib.realmv6.org/
+ */
+
+#include "rtrlib/rtrlib.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct test_validity_query {
+ const char *pfx;
+ int len;
+ int asn;
+ unsigned int val;
+};
+
+/*
+ * Verification is based on ROAs for RIPE RIS Routing Beacons, see:
+ * (https://www.ripe.net/analyse/internet-measurements/
+ * routing-information-service-ris/current-ris-routing-beacons)
+ */
+const struct test_validity_query queries[] = {{"93.175.146.0", 24, 12654, BGP_PFXV_STATE_VALID},
+ {"2001:7fb:fd02::", 48, 12654, BGP_PFXV_STATE_VALID},
+ {"93.175.147.0", 24, 12654, BGP_PFXV_STATE_INVALID},
+ {"2001:7fb:fd03::", 48, 12654, BGP_PFXV_STATE_INVALID},
+ {"84.205.83.0", 24, 12654, BGP_PFXV_STATE_NOT_FOUND},
+ {"2001:7fb:ff03::", 48, 12654, BGP_PFXV_STATE_NOT_FOUND},
+ {NULL, 0, 0, 0} };
+
+const int connection_timeout = 20;
+enum rtr_mgr_status connection_status = -1;
+
+static void connection_status_callback(const struct rtr_mgr_group *group __attribute__((unused)),
+ enum rtr_mgr_status status,
+ const struct rtr_socket *socket __attribute__((unused)),
+ void *data __attribute__((unused)))
+{
+ if (status == RTR_MGR_ERROR)
+ connection_status = status;
+}
+
+/**
+ * @brief live prefix validation test
+ * This test requires an active network connection. It runs an on-line live
+ * validation of specific IP prefixes, i.e., RIPE BGP beacons, that have known
+ * RPKI validation states. This tests uses a TCP transport connection.
+ */
+int main(void)
+{
+ /* These variables are not in the global scope
+ * because it would cause warnings about discarding constness
+ */
+ char RPKI_CACHE_HOST[] = "rpki-validator.realmv6.org";
+ char RPKI_CACHE_POST[] = "8283";
+
+ /* create a TCP transport socket */
+ struct tr_socket tr_tcp;
+ struct tr_tcp_config tcp_config = {RPKI_CACHE_HOST, RPKI_CACHE_POST, NULL, NULL, NULL, 0};
+ struct rtr_socket rtr_tcp;
+ struct rtr_mgr_group groups[1];
+
+ /* init a TCP transport and create rtr socket */
+ tr_tcp_init(&tcp_config, &tr_tcp);
+ rtr_tcp.tr_socket = &tr_tcp;
+
+ /* create a rtr_mgr_group array with 1 element */
+ groups[0].sockets = malloc(1 * sizeof(struct rtr_socket *));
+ groups[0].sockets_len = 1;
+ groups[0].sockets[0] = &rtr_tcp;
+ groups[0].preference = 1;
+
+ struct rtr_mgr_config *conf;
+
+ if (rtr_mgr_init(&conf, groups, 1, 30, 600, 600, NULL, NULL, &connection_status_callback, NULL) < 0)
+ return EXIT_FAILURE;
+
+ rtr_mgr_start(conf);
+ int sleep_counter = 0;
+ /* wait for connection, or timeout and exit eventually */
+ while (!rtr_mgr_conf_in_sync(conf)) {
+ if (connection_status == RTR_MGR_ERROR)
+ return EXIT_FAILURE;
+
+ sleep(1);
+ sleep_counter++;
+ if (sleep_counter >= connection_timeout)
+ return EXIT_FAILURE;
+ }
+
+ int i = 0;
+ struct test_validity_query q = queries[i];
+ /* test validity of entries in queries[] */
+ while (q.pfx) {
+ struct lrtr_ip_addr pref;
+ enum pfxv_state result;
+ struct pfx_record *reason = NULL;
+ unsigned int reason_len = 0;
+
+ lrtr_ip_str_to_addr(q.pfx, &pref);
+ pfx_table_validate_r(groups[0].sockets[0]->pfx_table, &reason, &reason_len, q.asn, &pref, q.len,
+ &result);
+ if (result != q.val) {
+ printf("ERROR: prefix validation mismatch.\n");
+ return EXIT_FAILURE;
+ }
+ printf("%s/%d \tOK\n", q.pfx, q.len);
+ q = queries[++i];
+ }
+
+ rtr_mgr_stop(conf);
+ rtr_mgr_free(conf);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/test_pfx.c b/tests/test_pfx.c
new file mode 100644
index 0000000..e7f2651
--- /dev/null
+++ b/tests/test_pfx.c
@@ -0,0 +1,450 @@
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website: http://rtrlib.realmv6.org/
+ */
+
+#include "rtrlib/lib/ip_private.h"
+#include "rtrlib/lib/utils_private.h"
+#include "rtrlib/pfx/pfx.h"
+#include "rtrlib/pfx/pfx_private.h"
+#include "rtrlib/pfx/trie/trie_private.h"
+#include "rtrlib/rtr/rtr.h"
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+static void validate(struct pfx_table *pfxt, uint32_t asn, const char *prefix, uint8_t prefix_len,
+ enum pfxv_state expected_result)
+{
+ struct lrtr_ip_addr ip;
+ enum pfxv_state val_res;
+
+ assert(!lrtr_ip_str_to_addr(prefix, &ip));
+
+ assert(pfx_table_validate(pfxt, asn, &ip, prefix_len, &val_res) == PFX_SUCCESS);
+
+ assert(val_res == expected_result);
+}
+
+/**
+ * @brief remove_src_test
+ * This test verifies pfx_table_src_remove function. It first adds certain
+ * records with different sockets into a pfx_table. Afterwards entries with
+ * socket tr1 are removed, and the remaining records are verified.
+ */
+static void remove_src_test(void)
+{
+ struct pfx_table pfxt;
+ struct rtr_socket tr1;
+ struct pfx_record pfx;
+ /* TEST1: init prefix table ----------------------------------------- */
+ pfx_table_init(&pfxt, NULL);
+ pfx.min_len = 32;
+ pfx.max_len = 32;
+ /* TEST2: add and verify different prefixes -------------------------- */
+ pfx.asn = 80;
+ pfx.socket = &tr1;
+ lrtr_ip_str_to_addr("10.11.10.0", &pfx.prefix);
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+
+ pfx.asn = 90;
+ pfx.socket = NULL;
+ lrtr_ip_str_to_addr("10.11.10.0", &pfx.prefix);
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+
+ pfx.socket = NULL;
+ pfx.min_len = 24;
+ lrtr_ip_str_to_addr("192.168.0.0", &pfx.prefix);
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+
+ pfx.socket = &tr1;
+ pfx.min_len = 8;
+ lrtr_ip_str_to_addr("10.0.0.0", &pfx.prefix);
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+
+ unsigned int len = 0;
+ struct trie_node **array = NULL;
+ /* verify that table has 3 distinct prefix entries */
+ assert(trie_get_children(pfxt.ipv4, &array, &len) != -1);
+ free(array);
+ array = NULL;
+ assert((len + 1) == 3);
+
+ /* remove entries with socket tr1, verify remaining 2 records */
+ pfx_table_src_remove(&pfxt, &tr1);
+ len = 0;
+ assert(trie_get_children(pfxt.ipv4, &array, &len) != -1);
+ free(array);
+ assert((len + 1) == 2);
+
+ /* verify validation of prefixes */
+ validate(&pfxt, 90, "10.0.0.0", 8, BGP_PFXV_STATE_NOT_FOUND);
+
+ validate(&pfxt, 90, "10.11.10.0", 32, BGP_PFXV_STATE_VALID);
+
+ validate(&pfxt, 80, "10.11.10.0", 32, BGP_PFXV_STATE_INVALID);
+
+ /* cleanup: free table */
+ pfx_table_free(&pfxt);
+ printf("%s() successful\n", __func__);
+}
+
+/**
+ * @brief Test pfx_table operations with many records
+ * This test adds, validates, and removes a huge number of records in/to/from
+ * a pfx_table.
+ */
+static void mass_test(void)
+{
+ struct pfx_table pfxt;
+ struct pfx_record pfx;
+ enum pfxv_state res;
+ const uint32_t min_i = 0xFFFF0000;
+ const uint32_t max_i = 0xFFFFFFF0;
+ /* init table with huge number of records */
+ pfx_table_init(&pfxt, NULL);
+ printf("Inserting %u records\n", (max_i - min_i) * 3);
+ for (uint32_t i = max_i; i >= min_i; i--) {
+ pfx.min_len = 32;
+ pfx.max_len = 32;
+ pfx.socket = NULL;
+ pfx.asn = i;
+ pfx.prefix.u.addr4.addr = htonl(i);
+ pfx.prefix.ver = LRTR_IPV4;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ /* add same prefix, with diff asn */
+ pfx.asn = i + 1;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ /* add same prefix, with diff len */
+ pfx.min_len = 128;
+ pfx.max_len = 128;
+ pfx.prefix.ver = LRTR_IPV6;
+ ((uint64_t *)pfx.prefix.u.addr6.addr)[1] = max_i;
+ ((uint64_t *)pfx.prefix.u.addr6.addr)[0] = min_i + i;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ }
+
+ /* verify validation of huge number of records */
+ printf("validating..\n");
+ for (uint32_t i = max_i; i >= min_i; i--) {
+ pfx.min_len = 32;
+ pfx.max_len = 32;
+ pfx.prefix.ver = LRTR_IPV4;
+ pfx.prefix.u.addr4.addr = htonl(i);
+ assert(pfx_table_validate(&pfxt, i, &pfx.prefix, pfx.min_len, &res) == PFX_SUCCESS);
+ assert(res == BGP_PFXV_STATE_VALID);
+ assert(pfx_table_validate(&pfxt, i + 1, &pfx.prefix, pfx.min_len, &res) == PFX_SUCCESS);
+ assert(res == BGP_PFXV_STATE_VALID);
+
+ pfx.min_len = 128;
+ pfx.max_len = 128;
+ pfx.prefix.ver = LRTR_IPV6;
+ ((uint64_t *)pfx.prefix.u.addr6.addr)[1] = max_i;
+ ((uint64_t *)pfx.prefix.u.addr6.addr)[0] = min_i + i;
+
+ assert(pfx_table_validate(&pfxt, i + 1, &pfx.prefix, pfx.min_len, &res) == PFX_SUCCESS);
+ assert(res == BGP_PFXV_STATE_VALID);
+ }
+
+ /* verify removal of huge number of records */
+ printf("removing records\n");
+ for (uint32_t i = max_i; i >= min_i; i--) {
+ pfx.socket = NULL;
+ pfx.min_len = 32;
+ pfx.max_len = 32;
+ pfx.asn = i;
+ pfx.prefix.ver = LRTR_IPV4;
+ pfx.prefix.u.addr4.addr = htonl(i);
+ assert(pfx_table_remove(&pfxt, &pfx) == PFX_SUCCESS);
+
+ pfx.asn = i + 1;
+ assert(pfx_table_remove(&pfxt, &pfx) == PFX_SUCCESS);
+
+ pfx.prefix.ver = LRTR_IPV6;
+ pfx.min_len = 128;
+ pfx.max_len = 128;
+ ((uint64_t *)pfx.prefix.u.addr6.addr)[1] = max_i;
+ ((uint64_t *)pfx.prefix.u.addr6.addr)[0] = min_i + i;
+ assert(pfx_table_remove(&pfxt, &pfx) == PFX_SUCCESS);
+ }
+
+ /* cleanup: free table */
+ pfx_table_free(&pfxt);
+ printf("%s() successful\n", __func__);
+}
+
+/**
+ * @brief Test of pfx_table functions
+ * This test verifies pfx_table and its core functions, namely
+ * pfx_table_add and pfx_table_validate.
+ */
+static void pfx_table_test(void)
+{
+ struct pfx_table pfxt;
+ struct pfx_record pfx;
+ enum pfxv_state res;
+
+ pfx_table_init(&pfxt, NULL);
+
+ pfx.asn = 123;
+ pfx.prefix.ver = LRTR_IPV4;
+ lrtr_ip_str_to_addr("10.10.0.0", &pfx.prefix);
+ pfx.min_len = 16;
+ pfx.max_len = 24;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+
+ validate(&pfxt, 123, "10.10.0.0", 16, BGP_PFXV_STATE_VALID);
+ validate(&pfxt, 124, "10.10.0.0", 16, BGP_PFXV_STATE_INVALID);
+ validate(&pfxt, 123, "10.10.0.0", 24, BGP_PFXV_STATE_VALID);
+ validate(&pfxt, 123, "10.10.10.0", 20, BGP_PFXV_STATE_VALID);
+ validate(&pfxt, 123, "10.10.10.0", 25, BGP_PFXV_STATE_INVALID);
+ validate(&pfxt, 123, "10.11.10.0", 16, BGP_PFXV_STATE_NOT_FOUND);
+
+ lrtr_ip_str_to_addr("10.10.0.0", &pfx.prefix);
+ pfx.asn = 122;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ validate(&pfxt, 122, "10.10.0.0", 18, BGP_PFXV_STATE_VALID);
+
+ lrtr_ip_str_to_addr("11.10.0.0", &pfx.prefix);
+ pfx.asn = 22;
+ pfx.min_len = 17;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ validate(&pfxt, 22, "11.10.0.0", 17, BGP_PFXV_STATE_VALID);
+
+ lrtr_ip_str_to_addr("2a01:4f8:131::", &pfx.prefix);
+ pfx.prefix.ver = LRTR_IPV6;
+ pfx.min_len = 48;
+ pfx.max_len = 48;
+ pfx.asn = 124;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ assert(pfx_table_validate(&pfxt, 124, &pfx.prefix, 48, &res) == PFX_SUCCESS);
+ validate(&pfxt, 124, "2a01:4f8:131::", 48, BGP_PFXV_STATE_VALID);
+
+ validate(&pfxt, 124, "2a01:4f8:131:15::", 56, BGP_PFXV_STATE_INVALID);
+
+ assert(lrtr_ip_str_to_addr("1.0.4.0", &pfx.prefix) == 0);
+ pfx.min_len = 22;
+ pfx.max_len = 22;
+ pfx.asn = 56203;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ validate(&pfxt, pfx.asn, "1.0.4.0", pfx.min_len, BGP_PFXV_STATE_VALID);
+
+ assert(lrtr_ip_str_to_addr("1.8.1.0", &pfx.prefix) == 0);
+ pfx.min_len = 24;
+ pfx.max_len = 24;
+ pfx.asn = 38345;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ validate(&pfxt, pfx.asn, "1.8.1.0", pfx.min_len, BGP_PFXV_STATE_VALID);
+
+ assert(lrtr_ip_str_to_addr("1.8.8.0", &pfx.prefix) == 0);
+ pfx.min_len = 24;
+ pfx.max_len = 24;
+ pfx.asn = 38345;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ validate(&pfxt, pfx.asn, "1.8.8.0", pfx.min_len, BGP_PFXV_STATE_VALID);
+ pfx_table_free(&pfxt);
+
+ assert(lrtr_ip_str_to_addr("1.0.65.0", &pfx.prefix) == 0);
+ pfx.min_len = 18;
+ pfx.max_len = 18;
+ pfx.asn = 18144;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ validate(&pfxt, pfx.asn, "1.0.65.0", pfx.min_len, BGP_PFXV_STATE_VALID);
+ pfx_table_free(&pfxt);
+
+ assert(lrtr_ip_str_to_addr("10.0.0.0", &pfx.prefix) == 0);
+ pfx.min_len = 16;
+ pfx.max_len = 16;
+ pfx.asn = 123;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ validate(&pfxt, pfx.asn, "10.0.0.0", pfx.min_len, BGP_PFXV_STATE_VALID);
+ validate(&pfxt, 124, "10.0.5.0", 24, BGP_PFXV_STATE_INVALID);
+ pfx_table_free(&pfxt);
+
+ assert(lrtr_ip_str_to_addr("109.105.96.0", &pfx.prefix) == 0);
+ pfx.min_len = 19;
+ pfx.max_len = 19;
+ pfx.asn = 123;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ validate(&pfxt, 456, "109.105.128.0", 20, BGP_PFXV_STATE_NOT_FOUND);
+ pfx_table_free(&pfxt);
+
+ assert(lrtr_ip_str_to_addr("190.57.224.0", &pfx.prefix) == 0);
+ pfx.min_len = 19;
+ pfx.max_len = 24;
+ pfx.asn = 123;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ validate(&pfxt, 123, "190.57.72.0", 21, BGP_PFXV_STATE_NOT_FOUND);
+ pfx_table_free(&pfxt);
+
+ assert(lrtr_ip_str_to_addr("80.253.128.0", &pfx.prefix) == 0);
+ pfx.min_len = 19;
+ pfx.max_len = 19;
+ pfx.asn = 123;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ validate(&pfxt, 123, "80.253.144.0", 20, BGP_PFXV_STATE_INVALID);
+
+ assert(lrtr_ip_str_to_addr("10.10.0.0", &pfx.prefix) == 0);
+ pfx.min_len = 16;
+ pfx.max_len = 16;
+ pfx.asn = 0;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+ validate(&pfxt, 123, "10.10.0.0", 16, BGP_PFXV_STATE_INVALID);
+
+ assert(lrtr_ip_str_to_addr("10.0.0.0", &pfx.prefix) == 0);
+ pfx.min_len = 8;
+ pfx.max_len = 15;
+ pfx.asn = 6;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+
+ assert(lrtr_ip_str_to_addr("10.0.0.0", &pfx.prefix) == 0);
+ pfx.min_len = 8;
+ pfx.max_len = 15;
+ pfx.asn = 5;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+
+ assert(lrtr_ip_str_to_addr("10.1.0.0", &pfx.prefix) == 0);
+ pfx.min_len = 16;
+ pfx.max_len = 16;
+ pfx.asn = 5;
+ assert(pfx_table_add(&pfxt, &pfx) == PFX_SUCCESS);
+
+ validate(&pfxt, 123, "10.1.0.0", 16, BGP_PFXV_STATE_INVALID);
+
+ /* cleanup: free record and table */
+ pfx_table_free(&pfxt);
+ printf("%s() successful\n", __func__);
+}
+
+static void create_ip4_pfx_record(struct pfx_record *pfx, uint32_t asn, const char *ip, uint8_t min_mask_len,
+ uint8_t max_mask_len)
+{
+ pfx->asn = asn;
+ pfx->min_len = min_mask_len;
+ pfx->max_len = max_mask_len;
+ pfx->socket = (struct rtr_socket *)1;
+ assert(!lrtr_ip_str_to_addr(ip, &pfx->prefix));
+}
+
+static void add_ip4_pfx_record(struct pfx_table *pfxt, uint32_t asn, const char *ip, uint8_t min_mask_len,
+ uint8_t max_mask_len)
+{
+ struct pfx_record pfx;
+ enum pfxv_state val_res;
+
+ create_ip4_pfx_record(&pfx, asn, ip, min_mask_len, max_mask_len);
+
+ assert(pfx_table_add(pfxt, &pfx) == PFX_SUCCESS);
+
+ assert(pfx_table_validate(pfxt, pfx.asn, &pfx.prefix, pfx.min_len, &val_res) == PFX_SUCCESS);
+ assert(val_res == BGP_PFXV_STATE_VALID);
+}
+
+static void test_issue99(void)
+{
+ struct pfx_table pfxt;
+
+ pfx_table_init(&pfxt, NULL);
+
+ add_ip4_pfx_record(&pfxt, 200, "10.100.255.0", 24, 24);
+ add_ip4_pfx_record(&pfxt, 300, "255.0.0.0", 24, 24);
+ add_ip4_pfx_record(&pfxt, 400, "128.0.0.0", 1, 24);
+
+ validate(&pfxt, 400, "255.0.0.0", 24, BGP_PFXV_STATE_VALID);
+ pfx_table_free(&pfxt);
+}
+
+static void test_issue152(void)
+{
+ struct pfx_table pfxt;
+ struct pfx_record *records = calloc(6, sizeof(struct pfx_record));
+
+ pfx_table_init(&pfxt, NULL);
+ create_ip4_pfx_record(&records[0], 1, "89.18.183.0", 24, 24);
+ create_ip4_pfx_record(&records[1], 2, "109.164.0.0", 17, 25);
+ create_ip4_pfx_record(&records[2], 3, "185.131.60.0", 22, 24);
+ create_ip4_pfx_record(&records[3], 4, "185.146.28.0", 22, 22);
+ create_ip4_pfx_record(&records[4], 5, "212.5.51.0", 24, 24);
+ create_ip4_pfx_record(&records[5], 6, "213.175.86.0", 24, 24);
+
+ for (size_t i = 0; i < 6; i++)
+ assert(pfx_table_add(&pfxt, &records[i]) == PFX_SUCCESS);
+
+ for (size_t i = 0; i < 6; i++)
+ assert(pfx_table_remove(&pfxt, &records[i]) == PFX_SUCCESS);
+
+ free(records);
+}
+
+static void update_cb1(struct pfx_table *p __attribute__((unused)), const struct pfx_record rec, const bool added)
+{
+ char ip[INET6_ADDRSTRLEN];
+
+ if (added)
+ printf("+ ");
+ else
+ printf("- ");
+ lrtr_ip_addr_to_str(&rec.prefix, ip, sizeof(ip));
+ printf("%-40s %3u - %3u %10u\n", ip, rec.min_len, rec.max_len, rec.asn);
+
+ if (rec.asn == 1)
+ assert(!added);
+ if (rec.asn == 2)
+ assert(false);
+ if (rec.asn == 3)
+ assert(added);
+}
+
+static void test_pfx_merge(void)
+{
+ struct pfx_table pfxt1;
+ struct pfx_table pfxt2;
+ struct pfx_record records[3];
+
+ pfx_table_init(&pfxt1, NULL);
+ pfx_table_init(&pfxt2, NULL);
+
+ create_ip4_pfx_record(&records[0], 1, "1.1.1.1", 24, 24);
+ create_ip4_pfx_record(&records[1], 2, "2.2.2.2", 24, 24);
+ create_ip4_pfx_record(&records[2], 3, "3.3.3.3", 24, 24);
+
+ printf("Adding to table one\n");
+ assert(pfx_table_add(&pfxt1, &records[0]) == PFX_SUCCESS);
+ assert(pfx_table_add(&pfxt1, &records[1]) == PFX_SUCCESS);
+ printf("Adding to table two\n");
+ assert(pfx_table_add(&pfxt2, &records[1]) == PFX_SUCCESS);
+ assert(pfx_table_add(&pfxt2, &records[2]) == PFX_SUCCESS);
+
+ printf("Computing diff\n");
+ pfxt1.update_fp = update_cb1;
+ pfxt2.update_fp = update_cb1;
+ pfx_table_notify_diff(&pfxt2, &pfxt1, (struct rtr_socket *)1);
+ pfxt1.update_fp = NULL;
+ pfxt2.update_fp = NULL;
+
+ printf("Freeing table one\n");
+ pfx_table_free(&pfxt1);
+ printf("Freeing table two\n");
+ pfx_table_free(&pfxt2);
+}
+
+int main(void)
+{
+ pfx_table_test();
+ remove_src_test();
+ mass_test();
+ test_issue99();
+ test_issue152();
+ test_pfx_merge();
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/test_pfx_locks.c b/tests/test_pfx_locks.c
new file mode 100644
index 0000000..1decf91
--- /dev/null
+++ b/tests/test_pfx_locks.c
@@ -0,0 +1,171 @@
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website: http://rtrlib.realmv6.org/
+ */
+
+#include "rtrlib/lib/ip.h"
+#include "rtrlib/pfx/pfx.h"
+#include "rtrlib/pfx/trie/trie-pfx.h"
+
+#include <arpa/inet.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+uint32_t min_i = 0xFF000000;
+uint32_t max_i = 0xFFFFFFF0;
+
+/**
+ * @brief Add records to prefix table
+ */
+static void *rec_add(struct pfx_table *pfxt)
+{
+ const int tid = getpid();
+ struct pfx_record rec;
+
+ rec.min_len = 32;
+ rec.max_len = 32;
+ rec.prefix.ver = LRTR_IPV4;
+ rec.prefix.u.addr4.addr = 0;
+
+ printf("Inserting %u records\n", (max_i - min_i) * 3);
+ for (uint32_t i = max_i; i >= min_i; i--) {
+ rec.min_len = 32;
+ rec.max_len = 32;
+ rec.socket = NULL;
+ rec.asn = tid % 2;
+ rec.prefix.u.addr4.addr = htonl(i);
+ rec.prefix.ver = LRTR_IPV4;
+ pfx_table_add(pfxt, &rec);
+ rec.asn = (tid % 2) + 1;
+ pfx_table_add(pfxt, &rec);
+
+ rec.min_len = 128;
+ rec.max_len = 128;
+ rec.prefix.ver = LRTR_IPV6;
+ rec.prefix.u.addr6.addr[1] = min_i + 0xFFFFFFFF;
+ rec.prefix.u.addr6.addr[0] = htonl(i) + 0xFFFFFFFF;
+ pfx_table_add(pfxt, &rec);
+ usleep(rand() / (RAND_MAX / 20));
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Validate records in prefix table
+ */
+static void *rec_val(struct pfx_table *pfxt)
+{
+ const int tid = getpid();
+ struct pfx_record rec;
+ enum pfxv_state res;
+
+ rec.min_len = 32;
+ rec.max_len = 32;
+ rec.prefix.ver = LRTR_IPV4;
+ rec.prefix.u.addr4.addr = 0;
+ printf("validating..\n");
+ for (uint32_t i = max_i; i >= min_i; i--) {
+ rec.min_len = 32;
+ rec.max_len = 32;
+ rec.prefix.ver = LRTR_IPV4;
+ rec.prefix.u.addr4.addr = htonl(i);
+ pfx_table_validate(pfxt, (tid % 2), &rec.prefix, rec.min_len, &res);
+ pfx_table_validate(pfxt, (tid % 2) + 1, &rec.prefix, rec.min_len, &res);
+
+ rec.min_len = 128;
+ rec.max_len = 128;
+ rec.prefix.ver = LRTR_IPV6;
+ rec.prefix.u.addr6.addr[1] = min_i + 0xFFFFFFFF;
+ rec.prefix.u.addr6.addr[0] = htonl(i) + 0xFFFFFFFF;
+
+ pfx_table_validate(pfxt, (tid % 2) + 1, &rec.prefix, rec.min_len, &res);
+ usleep(rand() / (RAND_MAX / 20));
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Delete records from prefix table
+ */
+static void *rec_del(struct pfx_table *pfxt)
+{
+ const int tid = getpid();
+ struct pfx_record rec;
+
+ rec.min_len = 32;
+ rec.max_len = 32;
+ rec.prefix.ver = LRTR_IPV4;
+ rec.prefix.u.addr4.addr = 0;
+ printf("removing records\n");
+ for (uint32_t i = max_i; i >= min_i; i--) {
+ rec.socket = NULL;
+ rec.min_len = 32;
+ rec.max_len = 32;
+ rec.asn = tid % 2;
+ rec.prefix.ver = LRTR_IPV4;
+ rec.prefix.u.addr4.addr = htonl(i);
+ pfx_table_remove(pfxt, &rec);
+ rec.asn = (tid % 2) + 1;
+ pfx_table_remove(pfxt, &rec);
+
+ rec.prefix.ver = LRTR_IPV6;
+ rec.min_len = 128;
+ rec.max_len = 128;
+ rec.prefix.u.addr6.addr[1] = min_i + 0xFFFFFFFF;
+ rec.prefix.u.addr6.addr[0] = htonl(i) + 0xFFFFFFFF;
+
+ pfx_table_remove(pfxt, &rec);
+ usleep(rand() / (RAND_MAX / 20));
+ }
+ printf("Done\n");
+
+ return NULL;
+}
+
+/**
+ * @brief Test concurrent operations on pfx_table
+ * This test creates 15 (random) threads that add, delete, or validate huge
+ * numbers of entries in/to/from a pfx_table.
+ *
+ * NOTE: this test takes a (very) long time to complete, use with care!
+ */
+int main(void)
+{
+ unsigned int max_threads = 15;
+ struct pfx_table pfxt;
+ pthread_t threads[max_threads];
+
+ pfx_table_init(&pfxt, NULL);
+ srand(time(NULL));
+
+ for (unsigned int i = 0; i < max_threads; i++) {
+ int r = rand() / (RAND_MAX / 3);
+
+ if (r == 0)
+ pthread_create(&threads[i], NULL, (void *(*)(void *))rec_add, &pfxt);
+ else if (r == 1)
+ pthread_create(&threads[i], NULL, (void *(*)(void *))rec_del, &pfxt);
+ else if (r == 2)
+ pthread_create(&threads[i], NULL, (void *(*)(void *))rec_val, &pfxt);
+ printf("Started Thread %d\n", i);
+ usleep(200);
+ }
+ for (unsigned int i = 0; i < max_threads; i++) {
+ pthread_join(threads[i], NULL);
+ printf("Thread %i returned\n", i);
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/test_trie.c b/tests/test_trie.c
new file mode 100644
index 0000000..e7ebb3c
--- /dev/null
+++ b/tests/test_trie.c
@@ -0,0 +1,200 @@
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website: http://rtrlib.realmv6.org/
+ */
+
+#include "rtrlib/lib/log_private.h"
+#include "rtrlib/lib/utils_private.h"
+#include "rtrlib/pfx/trie/trie.c"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+/*
+ * @brief Test trie core operations such as add and remove
+ * This test validates core operations of the trie,
+ * namely insert, remove, lookup, and lookup_exact.
+ */
+static void trie_test(void)
+{
+ struct lrtr_ip_addr addr;
+ struct trie_node *result;
+ struct trie_node n1, n2, n3, n4;
+ unsigned int lvl = 0;
+ bool found;
+
+ addr.ver = LRTR_IPV4;
+
+ /* node 1
+ * Tree after insert should be:
+ * 100.200.0.0/16
+ */
+
+ n1.len = 16;
+ n1.lchild = NULL;
+ n1.rchild = NULL;
+ n1.parent = NULL;
+ n1.data = NULL;
+ lrtr_ip_str_to_addr("100.200.0.0", &n1.prefix);
+ addr = n1.prefix;
+ result = trie_lookup(&n1, &addr, 16, &lvl);
+ assert(result);
+ assert(lrtr_ip_str_cmp(&result->prefix, "100.200.0.0"));
+
+ lrtr_ip_str_to_addr("100.200.30.0", &addr);
+ result = trie_lookup(&n1, &addr, 16, &lvl);
+ assert(result);
+ assert(lrtr_ip_str_cmp(&result->prefix, "100.200.0.0"));
+
+ /* node 2
+ * Tree after insert should be:
+ * 100.200.0.0/16
+ * \
+ * 132.200.0.0/16
+ */
+ n2.len = 16;
+ n2.lchild = NULL;
+ n2.rchild = NULL;
+ n2.parent = NULL;
+ n2.data = NULL;
+ lrtr_ip_str_to_addr("132.200.0.0", &n2.prefix);
+ trie_insert(&n1, &n2, 0);
+ lrtr_ip_str_to_addr("132.200.0.0", &addr);
+ lvl = 0;
+ result = trie_lookup(&n1, &addr, 16, &lvl);
+ assert(result);
+ assert(lrtr_ip_str_cmp(&result->prefix, "132.200.0.0"));
+ assert(n1.rchild == &n2);
+
+ /* node 3
+ * Tree after insert should be:
+ * 100.200.0.0/16
+ * / \
+ * 101.200.0.0/16 132.200.0.0/16
+ */
+ n3.len = 16;
+ n3.lchild = NULL;
+ n3.rchild = NULL;
+ n3.parent = NULL;
+ n3.data = NULL;
+
+ lrtr_ip_str_to_addr("101.200.0.0", &n3.prefix);
+ trie_insert(&n1, &n3, 0);
+ lrtr_ip_str_to_addr("101.200.0.0", &addr);
+ lvl = 0;
+ result = trie_lookup(&n1, &addr, 16, &lvl);
+ assert(result);
+ assert(lrtr_ip_str_cmp(&result->prefix, "101.200.0.0"));
+ assert(n1.lchild == &n3);
+
+ /* node 4
+ * Tree after insert should be:
+ * 100.200.0.0/16
+ * / \
+ * 101.200.0.0/16 132.200.0.0/16
+ * /
+ * 132.201.3.0/24
+ */
+ n4.len = 24;
+ n4.lchild = NULL;
+ n4.rchild = NULL;
+ n4.parent = NULL;
+ n4.data = NULL;
+
+ lrtr_ip_str_to_addr("132.201.3.0", &n4.prefix);
+ trie_insert(&n1, &n4, 0);
+ lrtr_ip_str_to_addr("132.201.3.0", &addr);
+ lvl = 0;
+ result = trie_lookup(&n1, &addr, 24, &lvl);
+ assert(result);
+ assert(lrtr_ip_str_cmp(&result->prefix, "132.201.3.0"));
+
+ assert(lrtr_ip_str_cmp(&n1.prefix, "100.200.0.0"));
+ assert(n1.len == 16);
+
+ /* verify tree structure */
+ assert(lrtr_ip_str_cmp(&n1.lchild->prefix, "101.200.0.0"));
+ assert(n1.lchild->len == 16);
+
+ assert(lrtr_ip_str_cmp(&n1.rchild->prefix, "132.200.0.0"));
+ assert(n1.rchild->len == 16);
+
+ assert(lrtr_ip_str_cmp(&n1.rchild->lchild->prefix, "132.201.3.0"));
+ assert(n1.rchild->lchild->len == 24);
+
+ lrtr_ip_str_to_addr("132.200.0.0", &addr);
+ lvl = 0;
+ result = trie_lookup(&n1, &addr, 16, &lvl);
+ assert(result);
+ assert(lrtr_ip_str_cmp(&result->prefix, "132.200.0.0"));
+
+ /* verify that a search for 132.200.3.0 returns 132.200/16 */
+ lrtr_ip_str_to_addr("132.200.3.0", &addr);
+ lvl = 0;
+ result = trie_lookup(&n1, &addr, 16, &lvl);
+ assert(result);
+ assert(lrtr_ip_str_cmp(&result->prefix, "132.200.0.0"));
+
+ /* verify no result for prefix 132.0.0.0/16 is found */
+ lvl = 0;
+ lrtr_ip_str_to_addr("132.0.0.0", &addr);
+ result = trie_lookup_exact(&n1, &addr, 16, &lvl, &found);
+ assert(!found);
+
+ /* verify trie_lookup_exact for prefix 132.201.3.0/24 is found */
+ lvl = 0;
+ lrtr_ip_str_to_addr("132.201.3.0", &addr);
+ result = trie_lookup_exact(&n1, &addr, 24, &lvl, &found);
+ assert(found);
+ assert(lrtr_ip_str_cmp(&result->prefix, "132.201.3.0"));
+
+ /* remove root->rchild
+ * Tree after remove should be:
+ * 100.200.0.0/16
+ * / \
+ * 101.200.0.0/16 132.201.3.0/24
+ */
+ lrtr_ip_str_to_addr("132.200.0.0", &addr);
+ result = trie_remove(&n1, &addr, 16, 0);
+ assert(result);
+ assert(lrtr_ip_str_cmp(&n1.prefix, "100.200.0.0"));
+ assert(lrtr_ip_str_cmp(&n1.lchild->prefix, "101.200.0.0"));
+ assert(lrtr_ip_str_cmp(&n1.rchild->prefix, "132.201.3.0"));
+ assert(!n1.rchild->lchild);
+
+ /* remove root->lchild
+ * Tree after remove should be:
+ * 100.200.0.0/16
+ * \
+ * 132.201.3.0/24
+ */
+ lrtr_ip_str_to_addr("101.200.0.0", &addr);
+ result = trie_remove(&n1, &addr, 16, 0);
+ assert(result);
+ assert(lrtr_ip_str_cmp(&n1.rchild->prefix, "132.201.3.0"));
+ assert(!n1.lchild);
+
+ /* remove root node
+ * Tree after remove should be:
+ * 132.201.3.0/24
+ */
+ lrtr_ip_str_to_addr("100.200.0.0", &addr);
+ result = trie_remove(&n1, &addr, 16, 0);
+ assert(lrtr_ip_str_cmp(&n1.prefix, "132.201.3.0"));
+ assert(result);
+ assert(!n1.lchild);
+ assert(!n1.rchild);
+}
+
+int main(void)
+{
+ trie_test();
+ printf("Test successful\n");
+ return EXIT_SUCCESS;
+}
diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt
new file mode 100644
index 0000000..29151dd
--- /dev/null
+++ b/tests/unittests/CMakeLists.txt
@@ -0,0 +1,7 @@
+
+
+add_rtr_unit_test(test_packets_static test_packets_static.c rtrlib_static cmocka)
+wrap_functions(test_packets_static lrtr_get_monotonic_time tr_send_all)
+
+add_rtr_unit_test(test_packets test_packets.c rtrlib_static cmocka)
+wrap_functions(test_packets rtr_change_socket_state tr_send_all)
diff --git a/tests/unittests/rtrlib_unittests.h b/tests/unittests/rtrlib_unittests.h
new file mode 100644
index 0000000..4ea2866
--- /dev/null
+++ b/tests/unittests/rtrlib_unittests.h
@@ -0,0 +1,18 @@
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website; http://rtrlib.realmv6.org/
+ */
+
+// clang-format off
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <cmocka.h>
+// clang-format on
+
+#define UNUSED(x) (void)(x)
diff --git a/tests/unittests/test_packets.c b/tests/unittests/test_packets.c
new file mode 100644
index 0000000..be2e570
--- /dev/null
+++ b/tests/unittests/test_packets.c
@@ -0,0 +1,72 @@
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website: http://rtrlib.realmv6.org/
+ */
+
+#include "rtrlib_unittests.h"
+#include "test_packets.h"
+
+#include "rtrlib/rtr/packets_private.h"
+
+int __wrap_tr_send_all(const struct tr_socket *socket, const void *pdu, const size_t len, const time_t timeout)
+{
+ UNUSED(socket);
+ UNUSED(pdu);
+ UNUSED(len);
+ UNUSED(timeout);
+ return (int)mock();
+}
+
+void __wrap_rtr_change_socket_state(struct rtr_socket *rtr_socket, const enum rtr_socket_state new_state)
+{
+ UNUSED(rtr_socket);
+ UNUSED(new_state);
+}
+
+static void test_rtr_send_reset_query(void **state)
+{
+ struct rtr_socket socket;
+
+ UNUSED(state);
+
+ socket.connection_state_fp = NULL;
+
+ will_return(__wrap_tr_send_all, 0);
+ assert_int_equal(rtr_send_reset_query(&socket), RTR_ERROR);
+
+ will_return(__wrap_tr_send_all, 10);
+ assert_int_equal(rtr_send_reset_query(&socket), RTR_SUCCESS);
+}
+
+static void test_rtr_change_socket_state(void **state)
+{
+ struct rtr_socket socket;
+
+ UNUSED(state);
+
+ socket.connection_state_fp = NULL;
+ socket.state = RTR_RESET;
+
+ __real_rtr_change_socket_state(&socket, RTR_SYNC);
+ assert_int_equal(socket.state, RTR_SYNC);
+
+ __real_rtr_change_socket_state(&socket, RTR_SYNC);
+ assert_int_equal(socket.state, RTR_SYNC);
+
+ __real_rtr_change_socket_state(&socket, RTR_SHUTDOWN);
+ assert_int_equal(socket.state, RTR_SHUTDOWN);
+
+ __real_rtr_change_socket_state(&socket, RTR_ESTABLISHED);
+ assert_int_equal(socket.state, RTR_SHUTDOWN);
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {cmocka_unit_test(test_rtr_send_reset_query),
+ cmocka_unit_test(test_rtr_change_socket_state)};
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/unittests/test_packets.h b/tests/unittests/test_packets.h
new file mode 100644
index 0000000..2fe7f9c
--- /dev/null
+++ b/tests/unittests/test_packets.h
@@ -0,0 +1,14 @@
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website; http://rtrlib.realmv6.org/
+ */
+
+#include "rtrlib/rtr/rtr.h"
+
+void __real_rtr_change_socket_state(struct rtr_socket *rtr_socket, const enum rtr_socket_state new_state);
+int __wrap_tr_send_all(const struct tr_socket *socket, const void *pdu, const size_t len, const time_t timeout);
+void __wrap_rtr_change_socket_state(struct rtr_socket *rtr_socket, const enum rtr_socket_state new_state);
diff --git a/tests/unittests/test_packets_static.c b/tests/unittests/test_packets_static.c
new file mode 100644
index 0000000..b8589d7
--- /dev/null
+++ b/tests/unittests/test_packets_static.c
@@ -0,0 +1,444 @@
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website; http://rtrlib.realmv6.org/
+ */
+
+#include "rtrlib_unittests.h"
+#include "test_packets_static.h"
+
+#include "rtrlib/lib/alloc_utils_private.h"
+#include "rtrlib/rtr/packets.c"
+#include "rtrlib/rtr_mgr_private.h"
+
+int __wrap_lrtr_get_monotonic_time(time_t *seconds)
+{
+ UNUSED(seconds);
+ return mock_type(int);
+}
+
+int __wrap_tr_send_all(const struct tr_socket *socket, const void *pdu, const size_t len, const time_t timeout)
+{
+ check_expected(socket);
+ check_expected(pdu);
+ check_expected(len);
+ check_expected(timeout);
+ return (int)mock();
+}
+
+static int cmp_serial_in_error_pdu(const LargestIntegralType test, const LargestIntegralType reference)
+{
+ struct pdu_error *test_pdu = (struct pdu_error *)test;
+ struct pdu_serial_query *reference_pdu = (struct pdu_serial_query *)reference;
+ struct pdu_serial_query *encapsulated_pdu = (struct pdu_serial_query *)test_pdu->rest;
+
+ if (encapsulated_pdu->ver != reference_pdu->ver || encapsulated_pdu->type != reference_pdu->type ||
+ encapsulated_pdu->session_id != reference_pdu->session_id || encapsulated_pdu->len != reference_pdu->len ||
+ encapsulated_pdu->sn != reference_pdu->sn) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int cmp_header_in_error_pdu(const LargestIntegralType test, const LargestIntegralType reference)
+{
+ struct pdu_error *test_pdu = (struct pdu_error *)test;
+ struct pdu_serial_query *reference_pdu = (struct pdu_serial_query *)reference;
+ struct pdu_serial_query *encapsulated_pdu = (struct pdu_serial_query *)test_pdu->rest;
+
+ if (encapsulated_pdu->ver != reference_pdu->ver || encapsulated_pdu->type != reference_pdu->type ||
+ encapsulated_pdu->session_id != reference_pdu->session_id || encapsulated_pdu->len != reference_pdu->len) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void test_set_last_update(void **state)
+{
+ struct rtr_socket socket;
+
+ UNUSED(state);
+
+ socket.connection_state_fp = NULL;
+
+ will_return(__wrap_lrtr_get_monotonic_time, RTR_ERROR);
+ assert_int_equal(rtr_set_last_update(&socket), RTR_ERROR);
+
+ will_return(__wrap_lrtr_get_monotonic_time, RTR_SUCCESS);
+ assert_int_equal(rtr_set_last_update(&socket), RTR_SUCCESS);
+}
+
+static void test_rtr_get_pdu_type(void **state)
+{
+ struct pdu_header pdu;
+
+ UNUSED(state);
+
+ pdu.type = SERIAL_NOTIFY;
+ assert_int_equal(rtr_get_pdu_type(&pdu), SERIAL_NOTIFY);
+
+ pdu.type = SERIAL_QUERY;
+ assert_int_equal(rtr_get_pdu_type(&pdu), SERIAL_QUERY);
+
+ pdu.type = RESET_QUERY;
+ assert_int_equal(rtr_get_pdu_type(&pdu), RESET_QUERY);
+
+ pdu.type = CACHE_RESPONSE;
+ assert_int_equal(rtr_get_pdu_type(&pdu), CACHE_RESPONSE);
+
+ pdu.type = RESERVED;
+ assert_int_equal(rtr_get_pdu_type(&pdu), RESERVED);
+
+ pdu.type = IPV6_PREFIX;
+ assert_int_equal(rtr_get_pdu_type(&pdu), IPV6_PREFIX);
+
+ pdu.type = EOD;
+ assert_int_equal(rtr_get_pdu_type(&pdu), EOD);
+
+ pdu.type = CACHE_RESET;
+ assert_int_equal(rtr_get_pdu_type(&pdu), CACHE_RESET);
+
+ pdu.type = ROUTER_KEY;
+ assert_int_equal(rtr_get_pdu_type(&pdu), ROUTER_KEY);
+
+ pdu.type = ERROR;
+ assert_int_equal(rtr_get_pdu_type(&pdu), ERROR);
+}
+
+static void test_pdu_to_network_byte_order(void **state)
+{
+ struct pdu_serial_query pdu;
+
+ UNUSED(state);
+
+ pdu.ver = 0;
+ pdu.type = SERIAL_QUERY;
+ pdu.session_id = 0x32A5;
+ pdu.len = 0xC;
+ pdu.sn = 0xCC56E9BB;
+
+ rtr_pdu_to_network_byte_order(&pdu);
+
+ assert_int_equal(pdu.ver, 0);
+ assert_int_equal(pdu.type, SERIAL_QUERY);
+ assert_int_equal(pdu.session_id, 0xA532);
+ assert_int_equal(pdu.len, 0x0C000000);
+ assert_int_equal(pdu.sn, 0xBBE956CC);
+}
+
+static void test_pdu_to_host_byte_order(void **state)
+{
+ struct pdu_serial_notify pdu_serial;
+ struct pdu_end_of_data_v1 pdu_eod;
+
+ UNUSED(state);
+
+ pdu_serial.ver = 1;
+ pdu_serial.type = SERIAL_NOTIFY;
+ pdu_serial.session_id = 0xDDFF;
+ pdu_serial.len = 0xC;
+ pdu_serial.sn = 0xDF;
+
+ rtr_pdu_footer_to_host_byte_order(&pdu_serial);
+ rtr_pdu_header_to_host_byte_order(&pdu_serial);
+
+ assert_int_equal(pdu_serial.ver, 1);
+ assert_int_equal(pdu_serial.type, SERIAL_NOTIFY);
+ assert_int_equal(pdu_serial.len, 0xC000000);
+ assert_int_equal(pdu_serial.sn, 0xDF000000);
+
+ pdu_eod.ver = 1;
+ pdu_eod.type = EOD;
+ pdu_eod.session_id = 0xFDDF;
+ pdu_eod.len = 0x18;
+ pdu_eod.sn = 0xFEDCBA;
+ pdu_eod.refresh_interval = 0xAF;
+ pdu_eod.retry_interval = 0xDC;
+ pdu_eod.expire_interval = 0xCCF;
+
+ rtr_pdu_header_to_host_byte_order(&pdu_eod);
+ rtr_pdu_footer_to_host_byte_order(&pdu_eod);
+
+ assert_int_equal(pdu_eod.ver, 1);
+ assert_int_equal(pdu_eod.type, EOD);
+ assert_int_equal(pdu_eod.session_id, 0xDFFD);
+ assert_int_equal(pdu_eod.len, 0x18000000);
+ assert_int_equal(pdu_eod.sn, 0xBADCFE00);
+ assert_int_equal(pdu_eod.refresh_interval, 0xAF000000);
+ assert_int_equal(pdu_eod.retry_interval, 0xDC000000);
+ assert_int_equal(pdu_eod.expire_interval, 0xCF0C0000);
+}
+
+static void test_rtr_pdu_check_size(void **state)
+{
+ struct pdu_header pdu;
+ struct pdu_error *error = malloc(30);
+
+ UNUSED(state);
+
+ memset(error, 0, 30);
+
+ pdu.type = SERIAL_NOTIFY;
+ pdu.len = 20;
+ assert_false(rtr_pdu_check_size(&pdu));
+ pdu.len = 12;
+ assert_true(rtr_pdu_check_size(&pdu));
+
+ pdu.type = CACHE_RESPONSE;
+ pdu.len = 5;
+ assert_false(rtr_pdu_check_size(&pdu));
+ pdu.len = 8;
+ assert_true(rtr_pdu_check_size(&pdu));
+
+ pdu.type = IPV4_PREFIX;
+ pdu.len = 25;
+ assert_false(rtr_pdu_check_size(&pdu));
+ pdu.len = 20;
+ assert_true(rtr_pdu_check_size(&pdu));
+
+ pdu.type = IPV6_PREFIX;
+ pdu.len = 15;
+ assert_false(rtr_pdu_check_size(&pdu));
+ pdu.len = 32;
+ assert_true(rtr_pdu_check_size(&pdu));
+
+ pdu.type = CACHE_RESET;
+ pdu.len = 10;
+ assert_false(rtr_pdu_check_size(&pdu));
+ pdu.len = 8;
+ assert_true(rtr_pdu_check_size(&pdu));
+
+ pdu.type = SERIAL_QUERY;
+ pdu.len = 14;
+ assert_false(rtr_pdu_check_size(&pdu));
+ pdu.len = 12;
+ assert_true(rtr_pdu_check_size(&pdu));
+
+ pdu.type = RESET_QUERY;
+ pdu.len = 10;
+ assert_false(rtr_pdu_check_size(&pdu));
+ pdu.len = 8;
+ assert_true(rtr_pdu_check_size(&pdu));
+
+ pdu.type = RESERVED;
+ pdu.len = 0;
+ assert_false(rtr_pdu_check_size(&pdu));
+
+ pdu.type = EOD;
+ pdu.ver = RTR_PROTOCOL_VERSION_1;
+ pdu.len = 12;
+ assert_false(rtr_pdu_check_size(&pdu));
+ pdu.len = 24;
+ assert_true(rtr_pdu_check_size(&pdu));
+ pdu.ver = RTR_PROTOCOL_VERSION_0;
+ pdu.len = 18;
+ assert_false(rtr_pdu_check_size(&pdu));
+ pdu.len = 12;
+ assert_true(rtr_pdu_check_size(&pdu));
+
+ pdu.type = ROUTER_KEY;
+ pdu.len = 124;
+ assert_false(rtr_pdu_check_size(&pdu));
+ pdu.len = 123;
+ assert_true(rtr_pdu_check_size(&pdu));
+
+ /* Test error pdu size checks */
+ error->type = ERROR;
+ error->len = 14;
+ error->len_enc_pdu = 0;
+ assert_false(rtr_pdu_check_size((struct pdu_header *)error));
+ error->len = 20;
+ error->len_enc_pdu = 0x8000000;
+ assert_false(rtr_pdu_check_size((struct pdu_header *)error));
+ error->len = 24;
+ error->rest[11] = 0xA;
+ assert_false(rtr_pdu_check_size((struct pdu_header *)error));
+}
+
+static void test_rtr_send_error_pdu(void **state)
+{
+ struct pdu_serial_query pdu_serial, pdu_serial_network;
+ struct rtr_socket socket;
+ int ret;
+
+ UNUSED(state);
+
+ pdu_serial.ver = 1;
+ pdu_serial.type = SERIAL_NOTIFY;
+ pdu_serial.session_id = 0xDDFF;
+ pdu_serial.len = sizeof(struct pdu_serial_query);
+ pdu_serial.sn = 0xDF;
+
+ memcpy(&pdu_serial_network, &pdu_serial, sizeof(struct pdu_serial_query));
+ rtr_pdu_to_network_byte_order(&pdu_serial_network);
+
+ expect_any_count(__wrap_tr_send_all, socket, 1);
+ expect_any_count(__wrap_tr_send_all, len, 1);
+ expect_any_count(__wrap_tr_send_all, timeout, 1);
+ expect_check(__wrap_tr_send_all, pdu, cmp_serial_in_error_pdu, &pdu_serial_network);
+ will_return(__wrap_tr_send_all, sizeof(pdu_serial) + sizeof(struct pdu_error));
+
+ ret = rtr_send_error_pdu_from_host(&socket, &pdu_serial, pdu_serial.len, INTERNAL_ERROR, NULL, 0);
+ assert_int_equal(ret, RTR_SUCCESS);
+
+ expect_any_count(__wrap_tr_send_all, socket, 1);
+ expect_any_count(__wrap_tr_send_all, len, 1);
+ expect_any_count(__wrap_tr_send_all, timeout, 1);
+ expect_check(__wrap_tr_send_all, pdu, cmp_serial_in_error_pdu, &pdu_serial_network);
+ will_return(__wrap_tr_send_all, sizeof(pdu_serial_network) + sizeof(struct pdu_error));
+
+ ret = rtr_send_error_pdu_from_network(&socket, &pdu_serial_network, pdu_serial.len, INTERNAL_ERROR, NULL, 0);
+ assert_int_equal(ret, RTR_SUCCESS);
+
+ expect_any_count(__wrap_tr_send_all, socket, 1);
+ expect_any_count(__wrap_tr_send_all, len, 1);
+ expect_any_count(__wrap_tr_send_all, timeout, 1);
+ expect_check(__wrap_tr_send_all, pdu, cmp_header_in_error_pdu, &pdu_serial_network);
+ will_return(__wrap_tr_send_all, sizeof(pdu_serial) + sizeof(struct pdu_error));
+
+ ret = rtr_send_error_pdu_from_host(&socket, &pdu_serial, sizeof(struct pdu_header), INTERNAL_ERROR, NULL, 0);
+ assert_int_equal(ret, RTR_SUCCESS);
+
+ ret = rtr_send_error_pdu_from_host(&socket, &pdu_serial, 2, INTERNAL_ERROR, NULL, 0);
+ assert_int_equal(ret, RTR_ERROR);
+}
+
+static void test_rtr_pdu_check_interval(void **state)
+{
+ UNUSED(state);
+
+ struct rtr_socket rtr_socket;
+ struct pdu_end_of_data_v1 pdu_eod;
+
+ int retval;
+
+ rtr_socket.refresh_interval = 0;
+ rtr_socket.retry_interval = 0;
+ rtr_socket.expire_interval = 0;
+
+ pdu_eod.refresh_interval = 1;
+ pdu_eod.retry_interval = 2;
+ pdu_eod.expire_interval = 601;
+
+ /* test appliance of interval values to the rtr_socket */
+ apply_interval_value(&rtr_socket, pdu_eod.refresh_interval, RTR_INTERVAL_TYPE_REFRESH);
+ assert_int_equal(rtr_socket.refresh_interval, pdu_eod.refresh_interval);
+
+ apply_interval_value(&rtr_socket, pdu_eod.retry_interval, RTR_INTERVAL_TYPE_RETRY);
+ assert_int_equal(rtr_socket.retry_interval, pdu_eod.retry_interval);
+
+ apply_interval_value(&rtr_socket, pdu_eod.expire_interval, RTR_INTERVAL_TYPE_EXPIRATION);
+ assert_int_equal(rtr_socket.expire_interval, pdu_eod.expire_interval);
+
+ /* test checks that determine if value is inside range */
+ retval = rtr_check_interval_range(pdu_eod.refresh_interval, RTR_REFRESH_MIN, RTR_REFRESH_MAX);
+ assert_int_equal(retval, RTR_INSIDE_INTERVAL_RANGE);
+
+ retval = rtr_check_interval_range(pdu_eod.retry_interval, RTR_RETRY_MIN, RTR_RETRY_MAX);
+ assert_int_equal(retval, RTR_INSIDE_INTERVAL_RANGE);
+
+ retval = rtr_check_interval_range(pdu_eod.expire_interval, RTR_EXPIRATION_MIN, RTR_EXPIRATION_MAX);
+ assert_int_equal(retval, RTR_INSIDE_INTERVAL_RANGE);
+
+ /* test checks that determine if value is below range */
+ pdu_eod.refresh_interval = RTR_REFRESH_MIN - 1;
+ pdu_eod.retry_interval = RTR_RETRY_MIN - 1;
+ pdu_eod.expire_interval = RTR_EXPIRATION_MIN - 1;
+
+ retval = rtr_check_interval_range(pdu_eod.refresh_interval, RTR_REFRESH_MIN, RTR_REFRESH_MAX);
+ assert_int_equal(retval, RTR_BELOW_INTERVAL_RANGE);
+
+ retval = rtr_check_interval_range(pdu_eod.retry_interval, RTR_RETRY_MIN, RTR_RETRY_MAX);
+ assert_int_equal(retval, RTR_BELOW_INTERVAL_RANGE);
+
+ retval = rtr_check_interval_range(pdu_eod.expire_interval, RTR_EXPIRATION_MIN, RTR_EXPIRATION_MAX);
+ assert_int_equal(retval, RTR_BELOW_INTERVAL_RANGE);
+
+ /* test checks that determine if value is above range */
+ pdu_eod.refresh_interval = RTR_REFRESH_MAX + 1;
+ pdu_eod.retry_interval = RTR_RETRY_MAX + 1;
+ pdu_eod.expire_interval = RTR_EXPIRATION_MAX + 1;
+
+ retval = rtr_check_interval_range(pdu_eod.refresh_interval, RTR_REFRESH_MIN, RTR_REFRESH_MAX);
+ assert_int_equal(retval, RTR_ABOVE_INTERVAL_RANGE);
+
+ retval = rtr_check_interval_range(pdu_eod.retry_interval, RTR_RETRY_MIN, RTR_RETRY_MAX);
+ assert_int_equal(retval, RTR_ABOVE_INTERVAL_RANGE);
+
+ retval = rtr_check_interval_range(pdu_eod.expire_interval, RTR_EXPIRATION_MIN, RTR_EXPIRATION_MAX);
+ assert_int_equal(retval, RTR_ABOVE_INTERVAL_RANGE);
+
+ /* test the different interval options the user can choose */
+ rtr_socket.refresh_interval = 0;
+ pdu_eod.refresh_interval = 42;
+ retval = rtr_check_interval_option(&rtr_socket, RTR_INTERVAL_MODE_ACCEPT_ANY, pdu_eod.refresh_interval,
+ RTR_INTERVAL_TYPE_REFRESH);
+ assert_int_equal(retval, RTR_SUCCESS);
+ assert_int_equal(rtr_socket.refresh_interval, pdu_eod.refresh_interval);
+
+ rtr_socket.refresh_interval = 0;
+ pdu_eod.refresh_interval = RTR_REFRESH_MAX + 1;
+ retval = rtr_check_interval_option(&rtr_socket, RTR_INTERVAL_MODE_DEFAULT_MIN_MAX, pdu_eod.refresh_interval,
+ RTR_INTERVAL_TYPE_REFRESH);
+ assert_int_equal(retval, RTR_SUCCESS);
+ assert_int_equal(rtr_socket.refresh_interval, RTR_REFRESH_MAX);
+
+ rtr_socket.refresh_interval = 0;
+ pdu_eod.refresh_interval = RTR_REFRESH_MIN - 1;
+ retval = rtr_check_interval_option(&rtr_socket, RTR_INTERVAL_MODE_DEFAULT_MIN_MAX, pdu_eod.refresh_interval,
+ RTR_INTERVAL_TYPE_REFRESH);
+ assert_int_equal(retval, RTR_SUCCESS);
+ assert_int_equal(rtr_socket.refresh_interval, RTR_REFRESH_MIN);
+
+ rtr_socket.refresh_interval = 42;
+ pdu_eod.refresh_interval = RTR_REFRESH_MIN - 1;
+ retval = rtr_check_interval_option(&rtr_socket, RTR_INTERVAL_MODE_IGNORE_ON_FAILURE, pdu_eod.refresh_interval,
+ RTR_INTERVAL_TYPE_REFRESH);
+ assert_int_equal(retval, RTR_SUCCESS);
+ assert_int_equal(rtr_socket.refresh_interval, 42);
+
+ rtr_socket.refresh_interval = 0;
+ pdu_eod.refresh_interval = RTR_REFRESH_MAX + 1;
+ retval = rtr_check_interval_option(&rtr_socket, RTR_INTERVAL_MODE_ACCEPT_ANY, pdu_eod.refresh_interval,
+ RTR_INTERVAL_TYPE_REFRESH);
+ assert_int_equal(retval, RTR_SUCCESS);
+ assert_int_equal(rtr_socket.refresh_interval, RTR_REFRESH_MAX + 1);
+}
+
+static void test_set_interval_option(void **state)
+{
+ UNUSED(state);
+
+ struct rtr_socket rtr_socket;
+
+ rtr_set_interval_mode(&rtr_socket, RTR_INTERVAL_MODE_IGNORE_ANY);
+ assert_int_equal(rtr_get_interval_mode(&rtr_socket), RTR_INTERVAL_MODE_IGNORE_ANY);
+
+ rtr_set_interval_mode(&rtr_socket, RTR_INTERVAL_MODE_ACCEPT_ANY);
+ assert_int_equal(rtr_get_interval_mode(&rtr_socket), RTR_INTERVAL_MODE_ACCEPT_ANY);
+
+ rtr_set_interval_mode(&rtr_socket, RTR_INTERVAL_MODE_DEFAULT_MIN_MAX);
+ assert_int_equal(rtr_get_interval_mode(&rtr_socket), RTR_INTERVAL_MODE_DEFAULT_MIN_MAX);
+
+ rtr_set_interval_mode(&rtr_socket, RTR_INTERVAL_MODE_IGNORE_ON_FAILURE);
+ assert_int_equal(rtr_get_interval_mode(&rtr_socket), RTR_INTERVAL_MODE_IGNORE_ON_FAILURE);
+
+ rtr_set_interval_mode(&rtr_socket, 4);
+ assert(rtr_get_interval_mode(&rtr_socket) != 4);
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_set_last_update), cmocka_unit_test(test_rtr_get_pdu_type),
+ cmocka_unit_test(test_pdu_to_network_byte_order), cmocka_unit_test(test_pdu_to_host_byte_order),
+ cmocka_unit_test(test_rtr_pdu_check_size), cmocka_unit_test(test_rtr_send_error_pdu),
+ cmocka_unit_test(test_rtr_pdu_check_interval), cmocka_unit_test(test_set_interval_option),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/unittests/test_packets_static.h b/tests/unittests/test_packets_static.h
new file mode 100644
index 0000000..58704ba
--- /dev/null
+++ b/tests/unittests/test_packets_static.h
@@ -0,0 +1,14 @@
+/*
+ * This file is part of RTRlib.
+ *
+ * This file is subject to the terms and conditions of the MIT license.
+ * See the file LICENSE in the top level directory for more details.
+ *
+ * Website; http://rtrlib.realmv6.org/
+ */
+
+#include "rtrlib/transport/transport_private.h"
+
+int __wrap_lrtr_get_monotonic_time(time_t *seconds);
+
+int __wrap_tr_send_all(const struct tr_socket *socket, const void *pdu, const size_t len, const time_t timeout);