diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:54:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:54:46 +0000 |
commit | cd7b005519ade8ab6c97fcb21590b71b7d1be6e3 (patch) | |
tree | c611a8d0cd5e8f68f41b8c2d16ba580e0f40a38d /tests | |
parent | Initial commit. (diff) | |
download | librtr-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 'tests')
-rw-r--r-- | tests/CMakeLists.txt | 35 | ||||
-rw-r--r-- | tests/test_dynamic_groups.c | 181 | ||||
-rw-r--r-- | tests/test_getbits.c | 168 | ||||
-rw-r--r-- | tests/test_ht_spkitable.c | 649 | ||||
-rw-r--r-- | tests/test_ht_spkitable_locks.c | 174 | ||||
-rw-r--r-- | tests/test_ipaddr.c | 256 | ||||
-rw-r--r-- | tests/test_live_validation.c | 121 | ||||
-rw-r--r-- | tests/test_pfx.c | 450 | ||||
-rw-r--r-- | tests/test_pfx_locks.c | 171 | ||||
-rw-r--r-- | tests/test_trie.c | 200 | ||||
-rw-r--r-- | tests/unittests/CMakeLists.txt | 7 | ||||
-rw-r--r-- | tests/unittests/rtrlib_unittests.h | 18 | ||||
-rw-r--r-- | tests/unittests/test_packets.c | 72 | ||||
-rw-r--r-- | tests/unittests/test_packets.h | 14 | ||||
-rw-r--r-- | tests/unittests/test_packets_static.c | 444 | ||||
-rw-r--r-- | tests/unittests/test_packets_static.h | 14 |
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); |