]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
wip: ike-init: Add support for optimized rekeying
authorTobias Brunner <tobias@strongswan.org>
Fri, 17 Mar 2023 15:40:48 +0000 (16:40 +0100)
committerTobias Brunner <tobias@strongswan.org>
Wed, 22 Mar 2023 10:37:52 +0000 (11:37 +0100)
wip: should we handle the case of a responder returning an SA payload
instead of an OPTIMIZED_REKEY notify as error (or just accept it if we
get a valid proposal from it)?

src/libcharon/sa/ikev2/tasks/ike_init.c
src/libcharon/tests/suites/test_ike_rekey.c

index d795b6cde9dee44efb95540cd27f45ad1f5b1f89..e1f88aabc208b6318661d16dc99554feb84c48aa 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008-2020 Tobias Brunner
+ * Copyright (C) 2008-2023 Tobias Brunner
  * Copyright (C) 2005-2008 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  *
@@ -143,6 +143,11 @@ struct private_ike_init_t {
         * Whether to follow IKEv2 redirects as per RFC 5685
         */
        bool follow_redirects;
+
+       /**
+        * Whether to use optimized rekeying
+        */
+       bool optimized_rekeying;
 };
 
 /**
@@ -336,24 +341,22 @@ static bool send_use_ppk(private_ike_init_t *this)
 }
 
 /**
- * build the payloads for the message
+ * Add an SA payload to the message, either generated from configured proposals
+ * or returning the selected proposal.
+ *
+ * The optional SPI of the new SA is encoded during a rekeying.
+ *
+ * Returns TRUE if additional KEs are proposed.
  */
-static bool build_payloads(private_ike_init_t *this, message_t *message)
+static bool build_sa_payload(private_ike_init_t *this, ike_cfg_t *ike_cfg,
+                                                        uint64_t new_spi, message_t *message)
 {
        sa_payload_t *sa_payload;
-       ke_payload_t *ke_payload;
-       nonce_payload_t *nonce_payload;
        linked_list_t *proposal_list, *other_dh_groups;
-       ike_sa_id_t *id;
-       proposal_t *proposal;
        enumerator_t *enumerator;
-       ike_cfg_t *ike_cfg;
+       proposal_t *proposal;
        bool additional_ke = FALSE;
 
-       id = this->ike_sa->get_id(this->ike_sa);
-
-       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
-
        if (this->initiator)
        {
                proposal_list = ike_cfg->get_proposals(ike_cfg);
@@ -361,11 +364,7 @@ static bool build_payloads(private_ike_init_t *this, message_t *message)
                enumerator = proposal_list->create_enumerator(proposal_list);
                while (enumerator->enumerate(enumerator, (void**)&proposal))
                {
-                       /* include SPI of new IKE_SA when we are rekeying */
-                       if (this->old_sa)
-                       {
-                               proposal->set_spi(proposal, id->get_initiator_spi(id));
-                       }
+                       proposal->set_spi(proposal, new_spi);
                        /* move the selected DH group to the front of the proposal */
                        if (!proposal->promote_transform(proposal, KEY_EXCHANGE_METHOD,
                                                                                         this->ke_method))
@@ -391,15 +390,47 @@ static bool build_payloads(private_ike_init_t *this, message_t *message)
        }
        else
        {
-               if (this->old_sa)
-               {
-                       /* include SPI of new IKE_SA when we are rekeying */
-                       this->proposal->set_spi(this->proposal, id->get_responder_spi(id));
-               }
+               this->proposal->set_spi(this->proposal, new_spi);
                sa_payload = sa_payload_create_from_proposal_v2(this->proposal);
                additional_ke = proposal_has_additional_ke(this->proposal);
        }
        message->add_payload(message, (payload_t*)sa_payload);
+       return additional_ke;
+}
+
+/**
+ * build the payloads for the message
+ */
+static bool build_payloads(private_ike_init_t *this, message_t *message)
+{
+       ke_payload_t *ke_payload;
+       notify_payload_t *notify;
+       nonce_payload_t *nonce_payload;
+       ike_sa_id_t *id;
+       ike_cfg_t *ike_cfg;
+       uint64_t my_new_spi = 0;
+       bool additional_ke = FALSE;
+
+       id = this->ike_sa->get_id(this->ike_sa);
+       ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+
+       if (this->old_sa)
+       {
+               my_new_spi = this->initiator ? id->get_initiator_spi(id)
+                                                                        : id->get_responder_spi(id);
+       }
+
+       if (this->optimized_rekeying)
+       {
+               notify = notify_payload_create_from_protocol_and_type(PLV2_NOTIFY,
+                                                                                                       PROTO_IKE, OPTIMIZED_REKEY);
+               notify->set_ike_spi(notify, my_new_spi);
+               message->add_payload(message, (payload_t*)notify);
+       }
+       else
+       {
+               additional_ke = build_sa_payload(this, ike_cfg, my_new_spi, message);
+       }
 
        ke_payload = ke_payload_create_from_key_exchange(PLV2_KEY_EXCHANGE,
                                                                                                         this->ke);
@@ -661,6 +692,7 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
        payload_t *payload;
        ike_sa_id_t *id;
        ke_payload_t *ke_pld = NULL;
+       uint64_t new_spi = 0;
 
        enumerator = message->create_payload_enumerator(message);
        while (enumerator->enumerate(enumerator, &payload))
@@ -692,6 +724,18 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
 
                                switch (notify->get_notify_type(notify))
                                {
+                                       case OPTIMIZED_REKEY:
+                                               if (this->optimized_rekeying)
+                                               {
+                                                       new_spi = notify->get_ike_spi(notify);
+                                                       if (!new_spi)
+                                                       {
+                                                               DBG1(DBG_IKE, "received invalid %N notify, "
+                                                                        "ignored", notify_type_names,
+                                                                        OPTIMIZED_REKEY);
+                                                       }
+                                               }
+                                               break;
                                        case FRAGMENTATION_SUPPORTED:
                                                this->ike_sa->enable_extension(this->ike_sa,
                                                                                                           EXT_IKE_FRAGMENTATION);
@@ -759,6 +803,38 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
        }
        enumerator->destroy(enumerator);
 
+       if (this->optimized_rekeying)
+       {
+               if (new_spi)
+               {
+                       /* when using optimized rekeying, we use the original proposal but
+                        * with the new SPI the peer supplied via notify */
+                       if (this->proposal)
+                       {
+                               DBG1(DBG_IKE, "peer sent unexpected SA payload during "
+                                        "optimized rekeying, ignored");
+                               this->proposal->destroy(this->proposal);
+                       }
+                       this->proposal = this->old_sa->get_proposal(this->old_sa);
+                       this->proposal = this->proposal->clone(this->proposal, 0);
+                       this->proposal->set_spi(this->proposal, new_spi);
+               }
+               else
+               {
+                       if (this->initiator)
+                       {
+                               DBG1(DBG_IKE, "peer didn't reply with expected %N notify,"
+                                        "rekeying may fail", notify_type_names, OPTIMIZED_REKEY);
+                       }
+                       else
+                       {
+                               DBG2(DBG_IKE, "peer requested a regular rekeying, even though "
+                                        "optimized rekeying is supported");
+                       }
+                       this->optimized_rekeying = FALSE;
+               }
+       }
+
        if (this->proposal)
        {
                this->ike_sa->set_proposal(this->ike_sa, this->proposal);
@@ -851,8 +927,15 @@ METHOD(task_t, build_i, status_t,
        if (!this->ke)
        {
                if (this->old_sa &&
-                       lib->settings->get_bool(lib->settings,
-                                                               "%s.prefer_previous_dh_group", TRUE, lib->ns))
+                       this->old_sa->supports_extension(this->old_sa,
+                                                                                        EXT_OPTIMIZED_REKEY))
+               {
+                       this->optimized_rekeying = TRUE;
+               }
+               if (this->old_sa &&
+                       (this->optimized_rekeying ||
+                        lib->settings->get_bool(lib->settings, "%s.prefer_previous_dh_group",
+                                                                       TRUE, lib->ns)))
                {       /* reuse the DH group we used for the old IKE_SA when rekeying */
                        proposal_t *proposal;
                        uint16_t dh_group;
@@ -885,6 +968,12 @@ METHOD(task_t, build_i, status_t,
        }
        else if (this->ke->get_method(this->ke) != this->ke_method)
        {       /* reset DH instance if group changed (INVALID_KE_PAYLOAD) */
+               if (this->optimized_rekeying)
+               {
+                       DBG1(DBG_IKE, "peer rejected our DH group during optimized "
+                                "rekeying, switch to regular rekeying");
+                       this->optimized_rekeying = FALSE;
+               }
                this->ke->destroy(this->ke);
                this->ke = this->keymat->keymat.create_ke(&this->keymat->keymat,
                                                                                                  this->ke_method);
@@ -981,6 +1070,12 @@ METHOD(task_t, process_r,  status_t,
        }
 #endif /* ME */
 
+       if (this->old_sa &&
+               this->old_sa->supports_extension(this->old_sa, EXT_OPTIMIZED_REKEY))
+       {       /* we expect an optimized rekeying if both peers support it */
+               this->optimized_rekeying = TRUE;
+       }
+
        process_payloads(this, message);
 
        return NEED_MORE;
index c6691acf44aa040e68c61079eebcccce476a57c7..bb5375b203743ba4848bd7bd0abf4ed323eafeec 100644 (file)
@@ -61,6 +61,9 @@ START_TEST(test_regular)
        /* CREATE_CHILD_SA { SA, Ni, KEi } --> */
        assert_hook_rekey(ike_rekey, 1, 3);
        assert_no_notify(IN, REKEY_SA);
+       assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_payload(IN, PLV2_NONCE);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
        exchange_test_helper->process_message(exchange_test_helper, b, NULL);
        assert_ike_sa_state(b, IKE_REKEYED);
        assert_child_sa_count(b, 0);
@@ -73,6 +76,9 @@ START_TEST(test_regular)
        /* <-- CREATE_CHILD_SA { SA, Nr, KEr } */
        assert_hook_rekey(ike_rekey, 1, 3);
        assert_no_notify(IN, REKEY_SA);
+       assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_payload(IN, PLV2_NONCE);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
        exchange_test_helper->process_message(exchange_test_helper, a, NULL);
        assert_ike_sa_state(a, IKE_DELETING);
        assert_child_sa_count(a, 0);
@@ -148,6 +154,9 @@ START_TEST(test_regular_multi_ke)
        /* CREATE_CHILD_SA { SA, Ni, KEi } --> */
        assert_hook_not_called(ike_rekey);
        assert_no_notify(IN, REKEY_SA);
+       assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_payload(IN, PLV2_NONCE);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
        exchange_test_helper->process_message(exchange_test_helper, b, NULL);
        assert_ike_sa_state(b, IKE_REKEYING);
        assert_child_sa_count(b, 1);
@@ -157,6 +166,9 @@ START_TEST(test_regular_multi_ke)
        /* <-- CREATE_CHILD_SA { SA, Nr, KEr, N(ADD_KE) } */
        assert_hook_not_called(ike_rekey);
        assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+       assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_payload(IN, PLV2_NONCE);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
        exchange_test_helper->process_message(exchange_test_helper, a, NULL);
        assert_ike_sa_state(a, IKE_REKEYING);
        assert_child_sa_count(a, 1);
@@ -166,6 +178,8 @@ START_TEST(test_regular_multi_ke)
        /* IKE_FOLLOWUP_KE { KEi, N(ADD_KE) } --> */
        assert_hook_rekey(ike_rekey, 1, 3);
        assert_payload(IN, PLV2_KEY_EXCHANGE);
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_no_payload(IN, PLV2_NONCE);
        assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
        exchange_test_helper->process_message(exchange_test_helper, b, NULL);
        assert_ike_sa_state(b, IKE_REKEYED);
@@ -179,6 +193,8 @@ START_TEST(test_regular_multi_ke)
        /* <-- IKE_FOLLOWUP_KE { KEr } */
        assert_hook_rekey(ike_rekey, 1, 3);
        assert_payload(IN, PLV2_KEY_EXCHANGE);
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_no_payload(IN, PLV2_NONCE);
        assert_no_notify(IN, ADDITIONAL_KEY_EXCHANGE);
        exchange_test_helper->process_message(exchange_test_helper, a, NULL);
        assert_ike_sa_state(a, IKE_DELETING);
@@ -440,6 +456,198 @@ START_TEST(test_regular_ke_invalid_multi_ke)
 }
 END_TEST
 
+/**
+ * Optimized IKE_SA rekeying either initiated by the original initiator or
+ * responder of the IKE_SA.
+ */
+START_TEST(test_optimized)
+{
+       ike_sa_t *a, *b, *new_sa;
+       status_t s;
+
+       assert_track_sas_start();
+
+       if (_i)
+       {       /* responder rekeys the IKE_SA */
+               exchange_test_helper->establish_sa(exchange_test_helper,
+                                                                                  &b, &a, NULL);
+       }
+       else
+       {       /* initiator rekeys the IKE_SA */
+               exchange_test_helper->establish_sa(exchange_test_helper,
+                                                                                  &a, &b, NULL);
+       }
+       /* these should never get called as this results in a successful rekeying */
+       assert_hook_not_called(ike_updown);
+       assert_hook_not_called(child_updown);
+
+       initiate_rekey(a);
+
+       /* CREATE_CHILD_SA { N(OPT_REKEY), Ni, KEi } --> */
+       assert_hook_rekey(ike_rekey, 1, 3);
+       assert_notify(IN, OPTIMIZED_REKEY);
+       assert_no_notify(IN, REKEY_SA);
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_payload(IN, PLV2_NONCE);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
+       exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+       assert_ike_sa_state(b, IKE_REKEYED);
+       assert_child_sa_count(b, 0);
+       new_sa = assert_ike_sa_checkout(3, 4, FALSE);
+       assert_ike_sa_state(new_sa, IKE_ESTABLISHED);
+       assert_child_sa_count(new_sa, 1);
+       assert_ike_sa_count(1);
+       assert_hook();
+
+       /* <-- CREATE_CHILD_SA { N(OPT_REKEY), Nr, KEr } */
+       assert_hook_rekey(ike_rekey, 1, 3);
+       assert_notify(IN, OPTIMIZED_REKEY);
+       assert_no_notify(IN, REKEY_SA);
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_payload(IN, PLV2_NONCE);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
+       exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+       assert_ike_sa_state(a, IKE_DELETING);
+       assert_child_sa_count(a, 0);
+       new_sa = assert_ike_sa_checkout(3, 4, TRUE);
+       assert_ike_sa_state(new_sa, IKE_ESTABLISHED);
+       assert_child_sa_count(new_sa, 1);
+       assert_ike_sa_count(2);
+       assert_hook();
+
+       /* we don't expect this hook to get called anymore */
+       assert_hook_not_called(ike_rekey);
+
+       /* INFORMATIONAL { D } --> */
+       assert_single_payload(IN, PLV2_DELETE);
+       s = exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+       ck_assert_int_eq(DESTROY_ME, s);
+       call_ikesa(b, destroy);
+       /* <-- INFORMATIONAL { } */
+       assert_message_empty(IN);
+       s = exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+       ck_assert_int_eq(DESTROY_ME, s);
+       call_ikesa(a, destroy);
+
+       /* ike_rekey/ike_updown/child_updown */
+       assert_hook();
+       assert_hook();
+       assert_hook();
+       assert_track_sas(2, 2);
+
+       charon->ike_sa_manager->flush(charon->ike_sa_manager);
+}
+END_TEST
+
+/**
+ * Optimized IKE_SA rekeying with multiple key exchanges either initiated by the
+ * original initiator or responder of the IKE_SA.
+ */
+START_TEST(test_optimized_multi_ke)
+{
+       ike_sa_t *a, *b, *new_sa;
+       status_t s;
+
+       assert_track_sas_start();
+
+       if (_i)
+       {       /* responder rekeys the IKE_SA */
+               exchange_test_helper->establish_sa(exchange_test_helper,
+                                                                                  &b, &a, &multi_ke_conf);
+       }
+       else
+       {       /* initiator rekeys the IKE_SA */
+               exchange_test_helper->establish_sa(exchange_test_helper,
+                                                                                  &a, &b, &multi_ke_conf);
+       }
+       /* these should never get called as this results in a successful rekeying */
+       assert_hook_not_called(ike_updown);
+       assert_hook_not_called(child_updown);
+
+       initiate_rekey(a);
+
+       /* CREATE_CHILD_SA { N(OPT_REKEY), Ni, KEi } --> */
+       assert_hook_not_called(ike_rekey);
+       assert_notify(IN, OPTIMIZED_REKEY);
+       assert_no_notify(IN, REKEY_SA);
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_payload(IN, PLV2_NONCE);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
+       exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+       assert_ike_sa_state(b, IKE_REKEYING);
+       assert_child_sa_count(b, 1);
+       assert_ike_sa_count(0);
+       assert_hook();
+
+       /* <-- CREATE_CHILD_SA { N(OPT_REKEY), Nr, KEr, N(ADD_KE) } */
+       assert_hook_not_called(ike_rekey);
+       assert_notify(IN, OPTIMIZED_REKEY);
+       assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_payload(IN, PLV2_NONCE);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
+       exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+       assert_ike_sa_state(a, IKE_REKEYING);
+       assert_child_sa_count(a, 1);
+       assert_ike_sa_count(0);
+       assert_hook();
+
+       /* IKE_FOLLOWUP_KE { KEi, N(ADD_KE) } --> */
+       assert_hook_rekey(ike_rekey, 1, 3);
+       assert_no_notify(IN, OPTIMIZED_REKEY);
+       assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_no_payload(IN, PLV2_NONCE);
+       exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+       assert_ike_sa_state(b, IKE_REKEYED);
+       assert_child_sa_count(b, 0);
+       new_sa = assert_ike_sa_checkout(3, 4, FALSE);
+       assert_ike_sa_state(new_sa, IKE_ESTABLISHED);
+       assert_child_sa_count(new_sa, 1);
+       assert_ike_sa_count(1);
+       assert_hook();
+
+       /* <-- IKE_FOLLOWUP_KE { KEr } */
+       assert_hook_rekey(ike_rekey, 1, 3);
+       assert_no_notify(IN, OPTIMIZED_REKEY);
+       assert_no_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+       assert_payload(IN, PLV2_KEY_EXCHANGE);
+       assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+       assert_no_payload(IN, PLV2_NONCE);
+       exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+       assert_ike_sa_state(a, IKE_DELETING);
+       assert_child_sa_count(a, 0);
+       new_sa = assert_ike_sa_checkout(3, 4, TRUE);
+       assert_ike_sa_state(new_sa, IKE_ESTABLISHED);
+       assert_child_sa_count(new_sa, 1);
+       assert_ike_sa_count(2);
+       assert_hook();
+
+       /* we don't expect this hook to get called anymore */
+       assert_hook_not_called(ike_rekey);
+
+       /* INFORMATIONAL { D } --> */
+       assert_single_payload(IN, PLV2_DELETE);
+       s = exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+       ck_assert_int_eq(DESTROY_ME, s);
+       call_ikesa(b, destroy);
+       /* <-- INFORMATIONAL { } */
+       assert_message_empty(IN);
+       s = exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+       ck_assert_int_eq(DESTROY_ME, s);
+       call_ikesa(a, destroy);
+
+       /* ike_rekey/ike_updown/child_updown */
+       assert_hook();
+       assert_hook();
+       assert_hook();
+       assert_track_sas(2, 2);
+
+       charon->ike_sa_manager->flush(charon->ike_sa_manager);
+}
+END_TEST
+
 /**
  * Both peers initiate the IKE_SA rekeying concurrently and should handle the
  * collision properly depending on the nonces.
@@ -2568,6 +2776,20 @@ START_TEST(test_collision_delete_drop_delete)
 }
 END_TEST
 
+START_SETUP(disable_optimized_rekey)
+{
+       lib->settings->set_bool(lib->settings, "%s.optimized_rekeying",
+                                                       FALSE, lib->ns);
+}
+END_SETUP
+
+START_TEARDOWN(enable_optimized_rekey)
+{
+       lib->settings->set_bool(lib->settings, "%s.optimized_rekeying",
+                                                       TRUE, lib->ns);
+}
+END_TEARDOWN
+
 Suite *ike_rekey_suite_create()
 {
        Suite *s;
@@ -2576,13 +2798,20 @@ Suite *ike_rekey_suite_create()
        s = suite_create("ike rekey");
 
        tc = tcase_create("regular");
+       tcase_add_checked_fixture(tc, disable_optimized_rekey, enable_optimized_rekey);
        tcase_add_loop_test(tc, test_regular, 0, 2);
        tcase_add_loop_test(tc, test_regular_multi_ke, 0, 2);
        tcase_add_loop_test(tc, test_regular_ke_invalid, 0, 2);
        tcase_add_loop_test(tc, test_regular_ke_invalid_multi_ke, 0, 2);
        suite_add_tcase(s, tc);
 
+       tc = tcase_create("optimized");
+       tcase_add_loop_test(tc, test_optimized, 0, 2);
+       tcase_add_loop_test(tc, test_optimized_multi_ke, 0, 2);
+       suite_add_tcase(s, tc);
+
        tc = tcase_create("collisions rekey");
+       tcase_add_checked_fixture(tc, disable_optimized_rekey, enable_optimized_rekey);
        tcase_add_loop_test(tc, test_collision, 0, 4);
        tcase_add_loop_test(tc, test_collision_multi_ke, 0, 4);
        tcase_add_loop_test(tc, test_collision_mixed, 0, 4);
@@ -2597,6 +2826,7 @@ Suite *ike_rekey_suite_create()
        suite_add_tcase(s, tc);
 
        tc = tcase_create("collisions delete");
+       tcase_add_checked_fixture(tc, disable_optimized_rekey, enable_optimized_rekey);
        tcase_add_loop_test(tc, test_collision_delete, 0, 2);
        tcase_add_loop_test(tc, test_collision_delete_multi_ke, 0, 2);
        tcase_add_loop_test(tc, test_collision_delete_drop_delete, 0, 2);