From: pcarana Date: Sat, 1 Jun 2019 01:01:09 +0000 (-0500) Subject: Relocate VRPs override function, and complete unit tests. X-Git-Tag: v0.0.2~13 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=46857286e93f587e5b99cc21ba6acf7af0778560;p=thirdparty%2FFORT-validator.git Relocate VRPs override function, and complete unit tests. - The function that checks and removes VRPs that override eachother (before being sent by RTR), has been placed where VRPs functions are located. - Fix the impersonator function that returns the minimum client serial. - Add unit test to: + Check that deltas that won't be sent or used anymore, will be forgotten. + Check that a range of deltas that'll be sent by RTR, don't contain VRP announcements/withdrawals that override eachother. + Chech that a PDU with a wrong session ID is correctly handled. --- diff --git a/src/rtr/db/vrps.c b/src/rtr/db/vrps.c index aebc7d8e..de7c10f5 100644 --- a/src/rtr/db/vrps.c +++ b/src/rtr/db/vrps.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "clients.h" #include "common.h" #include "validation_handler.h" @@ -20,6 +21,14 @@ DEFINE_ARRAY_LIST_FUNCTIONS(deltas_db, struct delta_group, ) +struct vrp_node { + struct delta delta; + SLIST_ENTRY(vrp_node) next; +}; + +/** Sorted list to filter deltas */ +SLIST_HEAD(vrp_slist, vrp_node); + struct state { /** * All the current valid ROAs. @@ -349,6 +358,79 @@ vrps_foreach_base_roa(vrp_foreach_cb cb, void *arg, serial_t *serial) return error; } +/* + * Remove the announcements/withdrawals that override each other. + * + * (Note: We're assuming the array is already duplicateless enough thanks to the + * hash table.) + */ +static int +vrp_ovrd_remove(struct delta const *delta, void *arg) +{ + struct vrp_node *ptr; + struct vrp_slist *filtered_vrps = arg; + + SLIST_FOREACH(ptr, filtered_vrps, next) + if (VRP_EQ(&delta->vrp, &ptr->delta.vrp) && + delta->flags != ptr->delta.flags) { + SLIST_REMOVE(filtered_vrps, ptr, vrp_node, next); + free(ptr); + return 0; + } + + ptr = malloc(sizeof(struct vrp_node)); + if (ptr == NULL) + return pr_enomem(); + + ptr->delta = *delta; + SLIST_INSERT_HEAD(filtered_vrps, ptr, next); + return 0; +} + +/* + * Remove all operations on @deltas that override each other, and do @cb (with + * @arg) on each element of the resultant delta. + */ +int +vrps_foreach_filtered_delta(struct deltas_db *deltas, delta_foreach_cb cb, + void *arg) +{ + struct vrp_slist filtered_vrps; + struct delta_group *group; + struct vrp_node *ptr; + array_index i; + int error = 0; + + /* + * Filter: Remove entries that cancel each other. + * (We'll have to build a separate list because the database nodes + * are immutable.) + */ + SLIST_INIT(&filtered_vrps); + ARRAYLIST_FOREACH(deltas, group, i) { + error = deltas_foreach(group->serial, group->deltas, + vrp_ovrd_remove, &filtered_vrps); + if (error) + goto release_list; + } + + /* Now do the callback on the filtered deltas */ + SLIST_FOREACH(ptr, &filtered_vrps, next) { + error = cb(&ptr->delta, arg); + if (error) + break; + } + +release_list: + while (!SLIST_EMPTY(&filtered_vrps)) { + ptr = filtered_vrps.slh_first; + SLIST_REMOVE_HEAD(&filtered_vrps, next); + free(ptr); + } + + return error; +} + /** * Adds to @result the deltas whose serial > @from. * diff --git a/src/rtr/db/vrps.h b/src/rtr/db/vrps.h index 631f0c52..86f2b2b6 100644 --- a/src/rtr/db/vrps.h +++ b/src/rtr/db/vrps.h @@ -32,6 +32,8 @@ int vrps_foreach_base_roa(vrp_foreach_cb, void *, serial_t *); int vrps_get_deltas_from(serial_t, serial_t *, struct deltas_db *); int get_last_serial_number(serial_t *); +int vrps_foreach_filtered_delta(struct deltas_db *, delta_foreach_cb, void *); + uint16_t get_current_session_id(uint8_t); #endif /* SRC_VRPS_H_ */ diff --git a/src/rtr/pdu_sender.c b/src/rtr/pdu_sender.c index a54872fe..47a8f8ff 100644 --- a/src/rtr/pdu_sender.c +++ b/src/rtr/pdu_sender.c @@ -5,7 +5,6 @@ #include #include #include -#include #include "clients.h" #include "config.h" @@ -13,14 +12,6 @@ #include "rtr/pdu_serializer.h" #include "rtr/db/vrps.h" -struct vrp_node { - struct delta delta; - SLIST_ENTRY(vrp_node) next; -}; - -/** Sorted list to filter deltas */ -SLIST_HEAD(vrp_slist, vrp_node); - /* * Set all the header values, EXCEPT length field. */ @@ -179,43 +170,10 @@ vrp_simply_send(struct delta const *delta, void *arg) return send_prefix_pdu(*fd, &delta->vrp, delta->flags); } -/** - * Remove the announcements/withdrawals that override each other. - * - * (Note: We're assuming the array is already duplicateless enough thanks to the - * hash table.) - */ -static int -vrp_ovrd_remove(struct delta const *delta, void *arg) -{ - struct vrp_node *ptr; - struct vrp_slist *filtered_vrps = arg; - - SLIST_FOREACH(ptr, filtered_vrps, next) - if (VRP_EQ(&delta->vrp, &ptr->delta.vrp) && - delta->flags != ptr->delta.flags) { - SLIST_REMOVE(filtered_vrps, ptr, vrp_node, next); - free(ptr); - return 0; - } - - ptr = malloc(sizeof(struct vrp_node)); - if (ptr == NULL) - return pr_enomem(); - - ptr->delta = *delta; - SLIST_INSERT_HEAD(filtered_vrps, ptr, next); - return 0; -} - int send_delta_pdus(int fd, struct deltas_db *deltas) { - struct vrp_slist filtered_vrps; struct delta_group *group; - array_index i; - struct vrp_node *ptr; - int error = 0; /* * Short circuit: Entries that share serial are already guaranteed to @@ -227,34 +185,7 @@ send_delta_pdus(int fd, struct deltas_db *deltas) vrp_simply_send, &fd); } - /* - * Filter: Remove entries that cancel each other. - * (We'll have to build a separate list because the database nodes - * are immutable.) - */ - SLIST_INIT(&filtered_vrps); - ARRAYLIST_FOREACH(deltas, group, i) { - error = deltas_foreach(group->serial, group->deltas, - vrp_ovrd_remove, &filtered_vrps); - if (error) - goto release_list; - } - - /* Now send the filtered deltas */ - SLIST_FOREACH(ptr, &filtered_vrps, next) { - error = send_prefix_pdu(fd, &ptr->delta.vrp, ptr->delta.flags); - if (error) - break; - } - -release_list: - while (!SLIST_EMPTY(&filtered_vrps)) { - ptr = filtered_vrps.slh_first; - SLIST_REMOVE_HEAD(&filtered_vrps, next); - free(ptr); - } - - return error; + return vrps_foreach_filtered_delta(deltas, vrp_simply_send, &fd); } int diff --git a/test/rtr/db/impersonator.c b/test/rtr/db/impersonator.c index 5ea4cbf4..d5233e83 100644 --- a/test/rtr/db/impersonator.c +++ b/test/rtr/db/impersonator.c @@ -41,10 +41,13 @@ perform_standalone_validation(struct validation_handler *handler) add_v6(handler, 1); break; case 2: - case 3: add_v4(handler, 1); add_v6(handler, 1); break; + case 3: + add_v4(handler, 0); + add_v6(handler, 0); + break; default: ck_abort_msg("perform_standalone_validation() was called too many times (%d).", iteration); diff --git a/test/rtr/db/vrps_test.c b/test/rtr/db/vrps_test.c index 8fb14de4..6ad42b39 100644 --- a/test/rtr/db/vrps_test.c +++ b/test/rtr/db/vrps_test.c @@ -19,6 +19,7 @@ static const bool iteration0_base[] = { 1, 0, 1, 0, }; static const bool iteration1_base[] = { 1, 1, 1, 1, }; static const bool iteration2_base[] = { 0, 1, 0, 1, }; +static const bool iteration3_base[] = { 1, 0, 1, 0, }; /* * DELTA @@ -37,11 +38,26 @@ static const bool deltas_0to2[] = { 1, 0, 1, 0, 0, 1, 0, 1, }; static const bool deltas_1to2[] = { 1, 0, 1, 0, 0, 0, 0, 0, }; static const bool deltas_2to2[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; +/* Deltas with rules that override each other */ +static const bool deltas_0to3_ovrd[] = { 1, 1, 1, 1, 1, 1, 1, 1, }; +static const bool deltas_1to3_ovrd[] = { 1, 1, 1, 1, 1, 0, 1, 0, }; +static const bool deltas_2to3_ovrd[] = { 0, 1, 0, 1, 1, 0, 1, 0, }; +static const bool deltas_3to3_ovrd[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; + +/* Deltas cleaned up */ +static const bool deltas_0to3_clean[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; +static const bool deltas_1to3_clean[] = { 0, 1, 0, 1, 0, 0, 0, 0, }; +static const bool deltas_2to3_clean[] = { 0, 1, 0, 1, 1, 0, 1, 0, }; +static const bool deltas_3to3_clean[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; + /* Impersonator functions */ +serial_t current_min_serial = 0; + serial_t -clients_get_min_serial(void) +clients_get_min_serial(serial_t *result) { + *result = current_min_serial; return 0; } @@ -65,6 +81,7 @@ vrp_fail(struct vrp const *vrp, void *arg) ck_abort_msg("Expected no callbacks, got VRP %u/%s/%u/%u.", vrp->asn, addr, vrp->prefix_length, vrp->max_prefix_length); + return -EINVAL; } static array_index @@ -133,7 +150,6 @@ delta_check(struct delta const *delta, void *arg) array[index] = true; return 0; - } static void @@ -159,8 +175,55 @@ check_base(serial_t expected_serial, bool const *expected_base) ck_assert_uint_eq(expected_base[i], actual_base[i]); } +static int +vrp_add(struct delta const *delta, void *arg) +{ + struct deltas *deltas = arg; + struct vrp const *vrp; + struct v4_address v4; + struct v6_address v6; + + vrp = &delta->vrp; + switch (vrp->addr_fam) { + case AF_INET: + v4.prefix.len = vrp->prefix_length; + v4.prefix.addr = vrp->prefix.v4; + v4.max_length = vrp->max_prefix_length; + deltas_add_roa_v4(deltas, vrp->asn, &v4, delta->flags); + break; + case AF_INET6: + v6.prefix.len = vrp->prefix_length; + v6.prefix.addr = vrp->prefix.v6; + v6.max_length = vrp->max_prefix_length; + deltas_add_roa_v6(deltas, vrp->asn, &v6, delta->flags); + break; + default: + ck_abort_msg("Unknown addr family"); + } + return 0; +} + +static void +filter_deltas(struct deltas_db *db) +{ + struct deltas_db tmp; + struct delta_group group; + struct deltas *deltas; + + group.serial = 0; + ck_assert_int_eq(0, deltas_create(&deltas)); + group.deltas = deltas; + ck_assert_int_eq(0, vrps_foreach_filtered_delta(db, vrp_add, + group.deltas)); + deltas_db_init(&tmp); + ck_assert_int_eq(0, deltas_db_add(&tmp, &group)); + + *db = tmp; +} + static void -check_deltas(serial_t from, serial_t to, bool const *expected_deltas) +check_deltas(serial_t from, serial_t to, bool const *expected_deltas, + bool filter) { serial_t actual_serial; bool actual_deltas[8]; @@ -173,6 +236,9 @@ check_deltas(serial_t from, serial_t to, bool const *expected_deltas) &deltas)); ck_assert_uint_eq(to, actual_serial); + if (filter) + filter_deltas(&deltas); + memset(actual_deltas, 0, sizeof(actual_deltas)); ARRAYLIST_FOREACH(&deltas, group, i) ck_assert_int_eq(0, deltas_foreach(group->serial, group->deltas, @@ -181,45 +247,134 @@ check_deltas(serial_t from, serial_t to, bool const *expected_deltas) ck_assert_uint_eq(expected_deltas[i], actual_deltas[i]); } -START_TEST(test_basic) +static void +check_no_deltas(serial_t from, serial_t to) { + serial_t actual_serial; struct deltas_db deltas; - serial_t serial; - bool changed; - bool iterated_entries[8]; deltas_db_init(&deltas); + ck_assert_int_eq(-ESRCH, vrps_get_deltas_from(from, &actual_serial, + &deltas)); +} + +static void +create_deltas_0to1(struct deltas_db *deltas, serial_t *serial, bool *changed, + bool *iterated_entries) +{ + current_min_serial = 0; + + deltas_db_init(deltas); ck_assert_int_eq(0, vrps_init()); /* First validation not yet performed: Tell routers to wait */ - ck_assert_int_eq(-EAGAIN, get_last_serial_number(&serial)); + ck_assert_int_eq(-EAGAIN, get_last_serial_number(serial)); ck_assert_int_eq(-EAGAIN, vrps_foreach_base_roa(vrp_fail, - iterated_entries, &serial)); - ck_assert_int_eq(-EAGAIN, vrps_get_deltas_from(0, &serial, &deltas)); + iterated_entries, serial)); + ck_assert_int_eq(-EAGAIN, vrps_get_deltas_from(0, serial, deltas)); /* First validation: One tree, no deltas */ - ck_assert_int_eq(0, vrps_update(&changed)); + ck_assert_int_eq(0, vrps_update(changed)); check_serial(0); check_base(0, iteration0_base); - check_deltas(0, 0, deltas_0to0); + check_deltas(0, 0, deltas_0to0, false); /* Second validation: One tree, added deltas */ - ck_assert_int_eq(0, vrps_update(&changed)); + ck_assert_int_eq(0, vrps_update(changed)); check_serial(1); check_base(1, iteration1_base); - check_deltas(0, 1, deltas_0to1); - check_deltas(1, 1, deltas_1to1); + check_deltas(0, 1, deltas_0to1, false); + check_deltas(1, 1, deltas_1to1, false); +} + +START_TEST(test_basic) +{ + struct deltas_db deltas; + serial_t serial; + bool changed; + bool iterated_entries[8]; + + create_deltas_0to1(&deltas, &serial, &changed, iterated_entries); + + /* Third validation: One tree, removed deltas */ + ck_assert_int_eq(0, vrps_update(&changed)); + check_serial(2); + check_base(2, iteration2_base); + check_deltas(0, 2, deltas_0to2, false); + check_deltas(1, 2, deltas_1to2, false); + check_deltas(2, 2, deltas_2to2, false); + + vrps_destroy(); +} +END_TEST + +START_TEST(test_delta_forget) +{ + struct deltas_db deltas; + serial_t serial; + bool changed; + bool iterated_entries[8]; + + create_deltas_0to1(&deltas, &serial, &changed, iterated_entries); + + /* + * Assume that the client(s) already have serial 1 (serial 2 will be + * created) so serial 0 isn't needed anymore. + */ + current_min_serial = 1; + + /* Third validation: One tree, removed deltas and delta 0 removed */ + ck_assert_int_eq(0, vrps_update(&changed)); + check_serial(2); + check_base(2, iteration2_base); + check_no_deltas(0, 2); + check_deltas(1, 2, deltas_1to2, false); + check_deltas(2, 2, deltas_2to2, false); + + vrps_destroy(); + + /* Return to its initial value */ + current_min_serial = 0; +} +END_TEST + +START_TEST(test_delta_ovrd) +{ + struct deltas_db deltas; + serial_t serial; + bool changed; + bool iterated_entries[8]; + + create_deltas_0to1(&deltas, &serial, &changed, iterated_entries); /* Third validation: One tree, removed deltas */ ck_assert_int_eq(0, vrps_update(&changed)); check_serial(2); check_base(2, iteration2_base); - check_deltas(0, 2, deltas_0to2); - check_deltas(1, 2, deltas_1to2); - check_deltas(2, 2, deltas_2to2); + check_deltas(0, 2, deltas_0to2, false); + check_deltas(1, 2, deltas_1to2, false); + check_deltas(2, 2, deltas_2to2, false); + + /* Fourth validation with deltas that override each other */ + ck_assert_int_eq(0, vrps_update(&changed)); + check_serial(3); + check_base(3, iteration3_base); + check_deltas(0, 3, deltas_0to3_ovrd, false); + check_deltas(1, 3, deltas_1to3_ovrd, false); + check_deltas(2, 3, deltas_2to3_ovrd, false); + check_deltas(3, 3, deltas_3to3_ovrd, false); + + /* Check "cleaned up" deltas */ + check_deltas(0, 3, deltas_0to3_clean, true); + check_deltas(1, 3, deltas_1to3_clean, true); + check_deltas(2, 3, deltas_2to3_clean, true); + check_deltas(3, 3, deltas_3to3_clean, true); vrps_destroy(); + + /* Return to its initial value */ + current_min_serial = 0; } END_TEST @@ -230,6 +385,8 @@ Suite *pdu_suite(void) core = tcase_create("Core"); tcase_add_test(core, test_basic); + tcase_add_test(core, test_delta_forget); + tcase_add_test(core, test_delta_ovrd); suite = suite_create("VRP Database"); suite_add_tcase(suite, core); diff --git a/test/rtr/pdu_handler_test.c b/test/rtr/pdu_handler_test.c index 0b40b1f6..0652e237 100644 --- a/test/rtr/pdu_handler_test.c +++ b/test/rtr/pdu_handler_test.c @@ -8,9 +8,6 @@ #include "rtr/pdu_sender.h" #include "rtr/db/vrps.h" -/* TODO (now) test the removal of deltas that cancel each other */ -/* TODO (now) test delta forgetting */ - /* Helper functions */ struct expected_pdu { @@ -93,8 +90,9 @@ init_serial_query(struct rtr_request *request, struct serial_query_pdu *query, /* Impersonator functions */ serial_t -clients_get_min_serial(void) +clients_get_min_serial(serial_t *result) { + *result = 0; return 0; } @@ -319,10 +317,36 @@ START_TEST(test_cache_has_no_data_available) } END_TEST +START_TEST(test_bad_session_id) +{ + struct rtr_request request; + struct serial_query_pdu client_pdu; + + pr_info("-- Bad Session ID--"); + + /* Prepare DB */ + init_db_full(); + + /* From serial 0: Init client request */ + init_serial_query(&request, &client_pdu, 0); + client_pdu.header.m.session_id++; + + /* From serial 0: Define expected server response */ + expected_pdu_add(PDU_TYPE_ERROR_REPORT); + + /* From serial 0: Run and validate */ + ck_assert_int_eq(-EINVAL, handle_serial_query_pdu(0, &request)); + ck_assert_uint_eq(false, has_expected_pdus()); + + /* Clean up */ + vrps_destroy(); +} +END_TEST + Suite *pdu_suite(void) { Suite *suite; - TCase *core; + TCase *core, *error; core = tcase_create("RFC6810-Defined Protocol Sequences"); tcase_add_test(core, test_start_or_restart); @@ -330,10 +354,12 @@ Suite *pdu_suite(void) tcase_add_test(core, test_no_incremental_update_available); tcase_add_test(core, test_cache_has_no_data_available); - /* TODO (now) add an unhappy path TCase. */ + error = tcase_create("Unhappy path cases"); + tcase_add_test(error, test_bad_session_id); suite = suite_create("PDU Handler"); suite_add_tcase(suite, core); + suite_add_tcase(suite, error); return suite; }