From: Tobias Brunner Date: Tue, 31 May 2016 08:09:29 +0000 (+0200) Subject: unit-tests: Add tests for IKE/CHILD delete collisions X-Git-Tag: 5.5.0dr1~4^2~27 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=0a2cad40a6fd5a60fc97a3620d4292c9a193d950;p=thirdparty%2Fstrongswan.git unit-tests: Add tests for IKE/CHILD delete collisions --- diff --git a/src/libcharon/tests/suites/test_child_delete.c b/src/libcharon/tests/suites/test_child_delete.c index 51b3f059de..437e919c7a 100644 --- a/src/libcharon/tests/suites/test_child_delete.c +++ b/src/libcharon/tests/suites/test_child_delete.c @@ -18,6 +18,7 @@ #include #include #include +#include #include /** @@ -181,6 +182,162 @@ START_TEST(test_collision_drop) } END_TEST +/** + * One of the hosts initiates a rekey of the IKE_SA of the CHILD_SA the other + * peer is concurrently trying to delete. + * + * delete ----\ /---- rekey IKE + * \-----/----> detect collision + * detect collision <---------/ /---- delete + * TEMP_FAIL ----\ / + * \----/-----> + * <--------/ + */ +START_TEST(test_collision_ike_rekey) +{ + ike_sa_t *a, *b; + uint32_t spi_a = _i+1; + + if (_i) + { /* responder deletes the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator deletes the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + call_ikesa(a, delete_child_sa, PROTO_ESP, spi_a, FALSE); + assert_child_sa_state(a, spi_a, CHILD_DELETING); + call_ikesa(b, rekey); + assert_ike_sa_state(b, IKE_REKEYING); + + /* this should never get called as there is no successful rekeying */ + assert_hook_not_called(ike_rekey); + + /* RFC 7296, 2.25.2: If a peer receives a request to delete a Child SA when + * it is currently rekeying the IKE SA, it SHOULD reply as usual, with a + * Delete payload. + */ + + /* INFORMATIONAL { D } --> */ + assert_hook_updown(child_updown, FALSE); + assert_single_payload(OUT, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_REKEYING); + assert_child_sa_count(b, 0); + assert_hook(); + + /* RFC 7296, 2.25.1: If a peer receives a request to rekey the IKE SA, and + * it is currently, rekeying, or closing a Child SA of that IKE SA, it + * SHOULD reply with TEMPORARY_FAILURE. + */ + + /* <-- CREATE_CHILD_SA { SA, Ni, KEi } */ + assert_single_notify(OUT, TEMPORARY_FAILURE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_state(a, spi_a, CHILD_DELETING); + + /* <-- INFORMATIONAL { D } */ + assert_hook_updown(child_updown, FALSE); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + assert_child_sa_count(a, 0); + assert_hook(); + + /* CREATE_CHILD_SA { N(TEMP_FAIL) } --> */ + /* we expect a job to retry the rekeying is scheduled */ + assert_jobs_scheduled(1); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_ESTABLISHED); + assert_scheduler(); + + /* ike_rekey */ + assert_hook(); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); +} +END_TEST + +/** + * One of the hosts initiates a delete of the IKE_SA of the CHILD_SA the other + * peer is concurrently trying to delete. + * + * delete ----\ /---- delete IKE + * \-----/----> detect collision + * <---------/ /---- delete + * delete ----\ / + * \----/-----> + * sa already gone <--------/ + */ +START_TEST(test_collision_ike_delete) +{ + ike_sa_t *a, *b; + uint32_t spi_a = _i+1; + message_t *msg; + status_t s; + + if (_i) + { /* responder rekeys the CHILD_SA (SPI 2) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &b, &a, NULL); + } + else + { /* initiator rekeys the CHILD_SA (SPI 1) */ + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + } + call_ikesa(a, delete_child_sa, PROTO_ESP, spi_a, FALSE); + assert_child_sa_state(a, spi_a, CHILD_DELETING); + call_ikesa(b, delete); + assert_ike_sa_state(b, IKE_DELETING); + + /* RFC 7296, 2.25.2 does not explicitly state what the behavior SHOULD be if + * a peer receives a request to delete a CHILD_SA when it is currently + * closing the IKE SA. We expect a regular response. + */ + + /* INFORMATIONAL { D } --> */ + assert_hook_updown(child_updown, FALSE); + assert_single_payload(OUT, PLV2_DELETE); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + assert_ike_sa_state(b, IKE_DELETING); + assert_child_sa_count(b, 0); + assert_hook(); + + /* RFC 7296, 2.25.1 does not explicitly state what the behavior SHOULD be if + * a peer receives a request to close the IKE SA if it is currently deleting + * a Child SA of that IKE SA. Let's just close the IKE_SA and forget the + * delete. + */ + + /* <-- INFORMATIONAL { D } */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_updown(child_updown, FALSE); + assert_message_empty(OUT); + s = exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(a, destroy); + assert_hook(); + assert_hook(); + + /* <-- INFORMATIONAL { D } */ + /* the SA is already gone */ + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + msg->destroy(msg); + + /* INFORMATIONAL { } --> */ + assert_hook_updown(ike_updown, FALSE); + assert_hook_not_called(child_updown); + s = exchange_test_helper->process_message(exchange_test_helper, b, NULL); + ck_assert_int_eq(DESTROY_ME, s); + call_ikesa(b, destroy); + assert_hook(); + assert_hook(); +} +END_TEST + Suite *child_delete_suite_create() { Suite *s; @@ -197,5 +354,13 @@ Suite *child_delete_suite_create() tcase_add_test(tc, test_collision_drop); suite_add_tcase(s, tc); + tc = tcase_create("collisions ike rekey"); + tcase_add_loop_test(tc, test_collision_ike_rekey, 0, 2); + suite_add_tcase(s, tc); + + tc = tcase_create("collisions ike delete"); + tcase_add_loop_test(tc, test_collision_ike_delete, 0, 2); + suite_add_tcase(s, tc); + return s; }