diff options
author | Tobias Brunner <tobias@strongswan.org> | 2017-02-08 15:11:20 +0100 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2017-02-08 15:11:20 +0100 |
commit | 0394bbf58c9ab57e59d1c44c06955f25b7f16f17 (patch) | |
tree | 5635aa7ad652cee89ddd66a1f83cffe5a6a8724b | |
parent | e9d13476cb8439bd18e4032e5cc6fd595c63326c (diff) | |
parent | 2f6ec15dff0495773c8dd616f6f7b594d09336ad (diff) | |
download | strongswan-0394bbf58c9ab57e59d1c44c06955f25b7f16f17.tar.bz2 strongswan-0394bbf58c9ab57e59d1c44c06955f25b7f16f17.tar.xz |
Merge branch 'mid-sync'
Adds support for handling IKEV2_MESSAGE_ID_SYNC notifies as responder
(usually the original initiator) as defined in RFC 6311. Some HA solutions
use these notifies to set the new IKEv2 message IDs after a failover event.
-rw-r--r-- | src/libcharon/Android.mk | 1 | ||||
-rw-r--r-- | src/libcharon/Makefile.am | 1 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa.c | 7 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa.h | 18 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/task_manager_v1.c | 7 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/task_manager_v2.c | 94 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/ike_auth.c | 7 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/ike_mid_sync.c | 264 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/ike_mid_sync.h | 74 | ||||
-rw-r--r-- | src/libcharon/sa/task.c | 1 | ||||
-rw-r--r-- | src/libcharon/sa/task.h | 2 | ||||
-rw-r--r-- | src/libcharon/sa/task_manager.h | 10 | ||||
-rw-r--r-- | src/libcharon/tests/Makefile.am | 1 | ||||
-rw-r--r-- | src/libcharon/tests/exchange_tests.h | 1 | ||||
-rw-r--r-- | src/libcharon/tests/suites/test_ike_mid_sync.c | 535 |
15 files changed, 1016 insertions, 7 deletions
diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index cb7e4b103..1a8e068d1 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -106,6 +106,7 @@ sa/ikev2/tasks/ike_delete.c sa/ikev2/tasks/ike_delete.h \ sa/ikev2/tasks/ike_dpd.c sa/ikev2/tasks/ike_dpd.h \ sa/ikev2/tasks/ike_init.c sa/ikev2/tasks/ike_init.h \ sa/ikev2/tasks/ike_natd.c sa/ikev2/tasks/ike_natd.h \ +sa/ikev2/tasks/ike_mid_sync.c sa/ikev2/tasks/ike_mid_sync.h \ sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \ sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \ sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 18f2dee10..8461d6230 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -105,6 +105,7 @@ sa/ikev2/tasks/ike_delete.c sa/ikev2/tasks/ike_delete.h \ sa/ikev2/tasks/ike_dpd.c sa/ikev2/tasks/ike_dpd.h \ sa/ikev2/tasks/ike_init.c sa/ikev2/tasks/ike_init.h \ sa/ikev2/tasks/ike_natd.c sa/ikev2/tasks/ike_natd.h \ +sa/ikev2/tasks/ike_mid_sync.c sa/ikev2/tasks/ike_mid_sync.h \ sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \ sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \ sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 7b87918d3..f180bfb01 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -617,6 +617,12 @@ METHOD(ike_sa_t, set_message_id, void, } } +METHOD(ike_sa_t, get_message_id, uint32_t, + private_ike_sa_t *this, bool initiate) +{ + return this->task_manager->get_mid(this->task_manager, initiate); +} + METHOD(ike_sa_t, send_keepalive, void, private_ike_sa_t *this, bool scheduled) { @@ -2885,6 +2891,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .get_other_host = _get_other_host, .set_other_host = _set_other_host, .set_message_id = _set_message_id, + .get_message_id = _get_message_id, .float_ports = _float_ports, .update_hosts = _update_hosts, .get_my_id = _get_my_id, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 6f5040d7c..c8ba2fd2a 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -151,6 +151,11 @@ enum ike_extension_t { * IKEv2 Redirect Mechanism, RFC 5685 */ EXT_IKE_REDIRECTION = (1<<13), + + /** + * IKEv2 Message ID sync, RFC 6311 + */ + EXT_IKE_MESSAGE_ID_SYNC = (1<<14), }; /** @@ -554,7 +559,7 @@ struct ike_sa_t { void (*set_proposal)(ike_sa_t *this, proposal_t *proposal); /** - * Set the message id of the IKE_SA. + * Set the message ID of the IKE_SA. * * The IKE_SA stores two message IDs, one for initiating exchanges (send) * and one to respond to exchanges (expect). @@ -565,6 +570,17 @@ struct ike_sa_t { void (*set_message_id)(ike_sa_t *this, bool initiate, uint32_t mid); /** + * Get the message ID of the IKE_SA. + * + * The IKE_SA stores two message IDs, one for initiating exchanges (send) + * and one to respond to exchanges (expect). + * + * @param initiate TRUE to get message ID for initiating + * @return current message + */ + uint32_t (*get_message_id)(ike_sa_t *this, bool initiate); + + /** * Add an additional address for the peer. * * In MOBIKE, a peer may transmit additional addresses where it is diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index ba393196d..7c8eebaba 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1896,6 +1896,12 @@ METHOD(task_manager_t, incr_mid, void, { } +METHOD(task_manager_t, get_mid, uint32_t, + private_task_manager_t *this, bool initiate) +{ + return initiate ? this->initiating.mid : this->responding.mid; +} + METHOD(task_manager_t, reset, void, private_task_manager_t *this, uint32_t initiate, uint32_t respond) { @@ -1999,6 +2005,7 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .initiate = _initiate, .retransmit = _retransmit, .incr_mid = _incr_mid, + .get_mid = _get_mid, .reset = _reset, .adopt_tasks = _adopt_tasks, .adopt_child_tasks = _adopt_child_tasks, diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 60a262ffc..e4a16faf0 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -34,6 +34,7 @@ #include <sa/ikev2/tasks/ike_delete.h> #include <sa/ikev2/tasks/ike_config.h> #include <sa/ikev2/tasks/ike_dpd.h> +#include <sa/ikev2/tasks/ike_mid_sync.h> #include <sa/ikev2/tasks/ike_vendor.h> #include <sa/ikev2/tasks/ike_verify_peer_cert.h> #include <sa/ikev2/tasks/child_create.h> @@ -817,7 +818,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request) task_t *task; message_t *message; host_t *me, *other; - bool delete = FALSE, hook = FALSE; + bool delete = FALSE, hook = FALSE, mid_sync = FALSE; ike_sa_id_t *id = NULL; uint64_t responder_spi = 0; bool result; @@ -836,6 +837,10 @@ static status_t build_response(private_task_manager_t *this, message_t *request) enumerator = array_create_enumerator(this->passive_tasks); while (enumerator->enumerate(enumerator, (void*)&task)) { + if (task->get_type(task) == TASK_IKE_MID_SYNC) + { + mid_sync = TRUE; + } switch (task->build(task, message)) { case SUCCESS: @@ -908,6 +913,15 @@ static status_t build_response(private_task_manager_t *this, message_t *request) } return DESTROY_ME; } + else if (mid_sync) + { + /* we don't want to resend messages to sync MIDs if requests with the + * previous MID arrive */ + clear_packets(this->responding.packets); + /* avoid increasing the expected message ID after handling a message + * to sync MIDs with MID 0 */ + return NEED_MORE; + } array_compress(this->passive_tasks); @@ -1069,6 +1083,10 @@ static status_t process_request(private_task_manager_t *this, task = (task_t*)ike_redirect_create( this->ike_sa, NULL); break; + case IKEV2_MESSAGE_ID_SYNC: + task = (task_t*)ike_mid_sync_create( + this->ike_sa); + break; default: break; } @@ -1200,6 +1218,12 @@ METHOD(task_manager_t, incr_mid, void, } } +METHOD(task_manager_t, get_mid, uint32_t, + private_task_manager_t *this, bool initiate) +{ + return initiate ? this->initiating.mid : this->responding.mid; +} + /** * Handle the given IKE fragment, if it is one. * @@ -1373,6 +1397,64 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg) return status; } +/** + * Check if a message with message ID 0 looks like it is used to synchronize + * the message IDs. + */ +static bool looks_like_mid_sync(private_task_manager_t *this, message_t *msg, + bool strict) +{ + enumerator_t *enumerator; + notify_payload_t *notify; + payload_t *payload; + bool found = FALSE, other = FALSE; + + if (msg->get_exchange_type(msg) == INFORMATIONAL) + { + enumerator = msg->create_payload_enumerator(msg); + while (enumerator->enumerate(enumerator, &payload)) + { + if (payload->get_type(payload) == PLV2_NOTIFY) + { + notify = (notify_payload_t*)payload; + switch (notify->get_notify_type(notify)) + { + case IKEV2_MESSAGE_ID_SYNC: + case IPSEC_REPLAY_COUNTER_SYNC: + found = TRUE; + continue; + default: + break; + } + } + if (strict) + { + other = TRUE; + break; + } + } + enumerator->destroy(enumerator); + } + return found && !other; +} + +/** + * Check if a message with message ID 0 looks like it is used to synchronize + * the message IDs and we are prepared to process it. + * + * Note: This is not called if the responder never sent a message before (i.e. + * we expect MID 0). + */ +static bool is_mid_sync(private_task_manager_t *this, message_t *msg) +{ + if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED && + this->ike_sa->supports_extension(this->ike_sa, + EXT_IKE_MESSAGE_ID_SYNC)) + { + return looks_like_mid_sync(this, msg, TRUE); + } + return FALSE; +} METHOD(task_manager_t, process_message, status_t, private_task_manager_t *this, message_t *msg) @@ -1421,7 +1503,7 @@ METHOD(task_manager_t, process_message, status_t, mid = msg->get_message_id(msg); if (msg->get_request(msg)) { - if (mid == this->responding.mid) + if (mid == this->responding.mid || (mid == 0 && is_mid_sync(this, msg))) { /* reject initial messages if not received in specific states, * after rekeying we only expect a DELETE in an INFORMATIONAL */ @@ -1462,7 +1544,8 @@ METHOD(task_manager_t, process_message, status_t, } } else if ((mid == this->responding.mid - 1) && - array_count(this->responding.packets)) + array_count(this->responding.packets) && + !(mid == 0 && looks_like_mid_sync(this, msg, FALSE))) { status = handle_fragment(this, &this->responding.defrag, msg); if (status != SUCCESS) @@ -1477,7 +1560,7 @@ METHOD(task_manager_t, process_message, status_t, } else { - DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored", + DBG1(DBG_IKE, "received message ID %d, expected %d, ignored", mid, this->responding.mid); } } @@ -1515,7 +1598,7 @@ METHOD(task_manager_t, process_message, status_t, } else { - DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored", + DBG1(DBG_IKE, "received message ID %d, expected %d, ignored", mid, this->initiating.mid); return SUCCESS; } @@ -2046,6 +2129,7 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa) .initiate = _initiate, .retransmit = _retransmit, .incr_mid = _incr_mid, + .get_mid = _get_mid, .reset = _reset, .adopt_tasks = _adopt_tasks, .adopt_child_tasks = _adopt_child_tasks, diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 1e47144dc..53daaf2ad 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -417,6 +417,9 @@ METHOD(task_t, build_i, status_t, /* indicate support for EAP-only authentication */ message->add_notify(message, FALSE, EAP_ONLY_AUTHENTICATION, chunk_empty); + /* indicate support for RFC 6311 Message ID synchronization */ + message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC_SUPPORTED, + chunk_empty); } if (!this->do_another_auth && !this->my_auth) @@ -992,6 +995,10 @@ METHOD(task_t, process_i, status_t, DBG1(DBG_IKE, "received invalid REDIRECT notify"); } break; + case IKEV2_MESSAGE_ID_SYNC_SUPPORTED: + this->ike_sa->enable_extension(this->ike_sa, + EXT_IKE_MESSAGE_ID_SYNC); + break; default: { if (type <= 16383) diff --git a/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c new file mode 100644 index 000000000..24cf276f4 --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.c @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +/* + * Copyright (C) 2016 Stephen J. Bevan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "ike_mid_sync.h" + +#include <daemon.h> +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> +#include <encoding/payloads/notify_payload.h> + +typedef struct private_ike_mid_sync_t private_ike_mid_sync_t; + +/** + * Private members + */ +struct private_ike_mid_sync_t { + + /** + * Public methods and task_t interface. + */ + ike_mid_sync_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Nonce sent by the peer and expected to be returned + */ + chunk_t nonce; + + /** + * Expected next sender message ID + */ + uint32_t send; + + /** + * Expected received message ID + */ + uint32_t recv; +}; + +/* + * Encoding of IKEV2_MESSAGE_SYNC_ID notify, RFC 6311 + * + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Next Payload |C| RESERVED | Payload Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |Protocol ID(=0)| SPI Size (=0) | Notify Message Type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Nonce Data | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | EXPECTED_SEND_REQ_MESSAGE_ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | EXPECTED_RECV_REQ_MESSAGE_ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +/* + * RFC 6311 section 5.1 + * + * o The peer MUST silently drop any received synchronization message + * if M1 is lower than or equal to the highest value it has seen from + * the cluster. This includes any previous received synchronization + * messages. + */ +METHOD(task_t, pre_process, status_t, + private_ike_mid_sync_t *this, message_t *message) +{ + notify_payload_t *notify; + bio_reader_t *reader; + chunk_t nonce; + uint32_t resp; + + if (message->get_message_id(message) != 0) + { /* ignore the notify if it was contained in an INFORMATIONAL with + * unexpected message ID */ + return SUCCESS; + } + if (!this->ike_sa->supports_extension(this->ike_sa, + EXT_IKE_MESSAGE_ID_SYNC)) + { + DBG1(DBG_ENC, "unexpected %N notify, ignored", notify_type_names, + IKEV2_MESSAGE_ID_SYNC); + return FAILED; + } + notify = message->get_notify(message, IKEV2_MESSAGE_ID_SYNC); + + reader = bio_reader_create(notify->get_notification_data(notify)); + if (!reader->read_data(reader, 4, &nonce) || + !reader->read_uint32(reader, &this->send) || + !reader->read_uint32(reader, &this->recv)) + { + reader->destroy(reader); + DBG1(DBG_ENC, "received invalid %N notify", + notify_type_names, IKEV2_MESSAGE_ID_SYNC); + return FAILED; + } + reader->destroy(reader); + resp = this->ike_sa->get_message_id(this->ike_sa, FALSE); + if (this->send < resp) + { + DBG1(DBG_ENC, "ignore %N notify with lower (%d) than expected (%d) " + "sender MID", notify_type_names, IKEV2_MESSAGE_ID_SYNC, this->send, + resp); + return FAILED; + } + this->nonce = chunk_clone(nonce); + return SUCCESS; +} + +/** + * Check if there are any active tasks, indicating that we already + * used the currents message ID and are waiting for a response. + */ +static bool has_active_tasks(private_ike_mid_sync_t *this) +{ + enumerator_t *enumerator; + task_t *task; + bool active; + + enumerator = this->ike_sa->create_task_enumerator(this->ike_sa, + TASK_QUEUE_ACTIVE); + active = enumerator->enumerate(enumerator, &task); + enumerator->destroy(enumerator); + return active; +} + +/* + * RFC 6311 section 5.1 + * + * o M2 MUST be at least the higher of the received M1, and one more + * than the highest sender value received from the cluster. This + * includes any previous received synchronization messages. + * + * o P2 MUST be the higher of the received P1 value, and one more than + * the highest sender value used by the peer. + * + * M1 is this->send, P1 is this->recv + */ +METHOD(task_t, process, status_t, + private_ike_mid_sync_t *this, message_t *message) +{ + uint32_t resp, init, m2, p2; + + if (message->get_message_id(message) != 0) + { /* ignore the notify if it was contained in an INFORMATIONAL with + * unexpected message id */ + return SUCCESS; + } + resp = this->ike_sa->get_message_id(this->ike_sa, FALSE); + m2 = max(this->send, resp); + if (resp != m2) + { + this->ike_sa->set_message_id(this->ike_sa, FALSE, m2); + } + init = this->ike_sa->get_message_id(this->ike_sa, TRUE); + p2 = max(this->recv, has_active_tasks(this) ? init + 1 : init); + if (init != p2) + { + this->ike_sa->set_message_id(this->ike_sa, TRUE, p2); + } + DBG1(DBG_IKE, "responder requested MID sync: initiating %d[%d], " + "responding %d[%d]", p2, init, m2, resp); + this->send = p2; + this->recv = m2; + return NEED_MORE; +} + +METHOD(task_t, build, status_t, + private_ike_mid_sync_t *this, message_t *message) +{ + bio_writer_t *writer; + + writer = bio_writer_create(12); + writer->write_data(writer, this->nonce); + writer->write_uint32(writer, this->send); + writer->write_uint32(writer, this->recv); + + message->set_message_id(message, 0); + message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC, + writer->get_buf(writer)); + + writer->destroy(writer); + return SUCCESS; +} + +METHOD(task_t, get_type, task_type_t, + private_ike_mid_sync_t *this) +{ + return TASK_IKE_MID_SYNC; +} + +METHOD(task_t, migrate, void, + private_ike_mid_sync_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + chunk_free(&this->nonce); +} + +METHOD(task_t, destroy, void, + private_ike_mid_sync_t *this) +{ + chunk_free(&this->nonce); + free(this); +} + +/* + * Described in header. + */ +ike_mid_sync_t *ike_mid_sync_create(ike_sa_t *ike_sa) +{ + private_ike_mid_sync_t *this; + + INIT(this, + .public = { + .task = { + .get_type = _get_type, + .build = _build, + .pre_process = _pre_process, + .process = _process, + .migrate = _migrate, + .destroy = _destroy, + }, + }, + .ike_sa = ike_sa, + ); + return &this->public; +} diff --git a/src/libcharon/sa/ikev2/tasks/ike_mid_sync.h b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.h new file mode 100644 index 000000000..9dd46f925 --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_mid_sync.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +/* + * Copyright (C) 2016 Stephen J. Bevan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup ike_mid_sync ike_mid_sync + * @{ @ingroup tasks_v2 + */ + +#ifndef IKE_MID_SYNC_H_ +#define IKE_MID_SYNC_H_ + +typedef struct ike_mid_sync_t ike_mid_sync_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/task.h> + +/** + * Task of type TASK_IKE_MID_SYNC, implements RFC 6311 responder. + * + * This task handles an IKEV2_MESSAGE_ID_SYNC notify sent by a peer + * and if acceptable updates the SA MIDs and replies with the updated + * MID values. + */ +struct ike_mid_sync_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * Create a new TASK_IKE_MID_SYNC task. + * + * @param ike_sa IKE_SA this task works for + * @return task to handle by the task_manager + */ +ike_mid_sync_t *ike_mid_sync_create(ike_sa_t *ike_sa); + +#endif /** IKE_MID_SYNC_H_ @}*/ diff --git a/src/libcharon/sa/task.c b/src/libcharon/sa/task.c index 405eda66b..30de08c9b 100644 --- a/src/libcharon/sa/task.c +++ b/src/libcharon/sa/task.c @@ -30,6 +30,7 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_ISAKMP_CERT_POST, "IKE_REAUTH_COMPLETE", "IKE_REDIRECT", "IKE_VERIFY_PEER_CERT", + "IKE_MID_SYNC", "IKE_DELETE", "IKE_DPD", "IKE_VENDOR", diff --git a/src/libcharon/sa/task.h b/src/libcharon/sa/task.h index 31d70fb3b..5f77149ba 100644 --- a/src/libcharon/sa/task.h +++ b/src/libcharon/sa/task.h @@ -61,6 +61,8 @@ enum task_type_t { TASK_IKE_REDIRECT, /** verify a peer's certificate */ TASK_IKE_VERIFY_PEER_CERT, + /** synchronize message IDs, RFC6311 */ + TASK_IKE_MID_SYNC, /** delete an IKE_SA */ TASK_IKE_DELETE, /** liveness check */ diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h index 86077d373..7e9262291 100644 --- a/src/libcharon/sa/task_manager.h +++ b/src/libcharon/sa/task_manager.h @@ -240,6 +240,14 @@ struct task_manager_t { void (*incr_mid)(task_manager_t *this, bool initiate); /** + * Get the current message ID counter, in- or outbound. + * + * @param initiate TRUE to get the initiating ID + * @return current message ID + */ + uint32_t (*get_mid)(task_manager_t *this, bool initiate); + + /** * Reset message ID counters of the task manager. * * The IKEv2 protocol requires to restart exchanges with message IDs @@ -253,7 +261,7 @@ struct task_manager_t { * @param initiate message ID / DPD seq to initiate exchanges (send) * @param respond message ID / DPD seq to respond to exchanges (expect) */ - void (*reset) (task_manager_t *this, uint32_t initiate, uint32_t respond); + void (*reset)(task_manager_t *this, uint32_t initiate, uint32_t respond); /** * Check if we are currently waiting for a reply. diff --git a/src/libcharon/tests/Makefile.am b/src/libcharon/tests/Makefile.am index b8670246b..8f762a2e6 100644 --- a/src/libcharon/tests/Makefile.am +++ b/src/libcharon/tests/Makefile.am @@ -29,6 +29,7 @@ exchange_tests_SOURCES = \ suites/test_child_delete.c \ suites/test_child_rekey.c \ suites/test_ike_delete.c \ + suites/test_ike_mid_sync.c \ suites/test_ike_rekey.c \ utils/exchange_test_asserts.h utils/exchange_test_asserts.c \ utils/exchange_test_helper.h utils/exchange_test_helper.c \ diff --git a/src/libcharon/tests/exchange_tests.h b/src/libcharon/tests/exchange_tests.h index 30086721f..6b35ea5e5 100644 --- a/src/libcharon/tests/exchange_tests.h +++ b/src/libcharon/tests/exchange_tests.h @@ -14,6 +14,7 @@ */ TEST_SUITE(ike_delete_suite_create) +TEST_SUITE(ike_mid_sync_suite_create) TEST_SUITE(ike_rekey_suite_create) TEST_SUITE(child_create_suite_create) TEST_SUITE(child_delete_suite_create) diff --git a/src/libcharon/tests/suites/test_ike_mid_sync.c b/src/libcharon/tests/suites/test_ike_mid_sync.c new file mode 100644 index 000000000..3776f39e9 --- /dev/null +++ b/src/libcharon/tests/suites/test_ike_mid_sync.c @@ -0,0 +1,535 @@ +/* + * Copyright (C) 2016 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "test_suite.h" + +#include <tests/utils/exchange_test_helper.h> +#include <tests/utils/exchange_test_asserts.h> +#include <tests/utils/sa_asserts.h> +#include <bio/bio_reader.h> +#include <bio/bio_writer.h> + +/** + * FIXME: Since we don't have the server side yet, this is kind of a hack!!! + */ + +/** + * Add the IKEV2_MESSAGE_ID_SYNC_SUPPORTED notify to the IKE_AUTH response + */ +static bool add_notify(listener_t *listener, ike_sa_t *ike_sa, + message_t *message, bool incoming, bool plain) +{ + if (plain && !incoming && message->get_exchange_type(message) == IKE_AUTH && + !message->get_request(message)) + { + message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC_SUPPORTED, + chunk_empty); + return FALSE; + } + return TRUE; +} +#define add_notify_to_ike_auth() ({ \ + listener_t _notify_listener = { \ + .message = add_notify, \ + }; \ + exchange_test_helper->add_listener(exchange_test_helper, &_notify_listener); \ +}) + +/** + * Handle IKEV2_MESSAGE_ID_SYNC notifies + */ +typedef struct { + listener_t listener; + struct { + chunk_t nonce; + uint32_t send; + uint32_t recv; + } init, resp; +} mid_sync_listener_t; + +static bool handle_mid(listener_t *listener, + ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain) +{ + mid_sync_listener_t *this = (mid_sync_listener_t*)listener; + + if (!plain || incoming) + { + return TRUE; + } + + if (message->get_exchange_type(message) == INFORMATIONAL) + { + if (streq("resp", ike_sa->get_name(ike_sa))) + { + bio_writer_t *writer; + rng_t *rng; + + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + ignore_result(rng->allocate_bytes(rng, 4, &this->init.nonce)); + rng->destroy(rng); + writer = bio_writer_create(12); + writer->write_data(writer, this->init.nonce); + writer->write_uint32(writer, this->init.send); + writer->write_uint32(writer, this->init.recv); + message->set_message_id(message, 0); + message->add_notify(message, FALSE, IKEV2_MESSAGE_ID_SYNC, + writer->get_buf(writer)); + writer->destroy(writer); + } + else + { + notify_payload_t *notify; + bio_reader_t *reader; + + notify = message->get_notify(message, IKEV2_MESSAGE_ID_SYNC); + reader = bio_reader_create(notify->get_notification_data(notify)); + chunk_clear(&this->resp.nonce); + reader->read_data(reader, 4, &this->resp.nonce); + this->resp.nonce = chunk_clone(this->resp.nonce); + reader->read_uint32(reader, &this->resp.send); + reader->read_uint32(reader, &this->resp.recv); + reader->destroy(reader); + } + } + return TRUE; +} + +/** + * Send a MESSAGE_ID_SYNC notify in an INFORMATIONAL. We reset the state + * afterwards so this seems as if nothing happened. + */ +static void send_mid_sync(ike_sa_t *sa, uint32_t send, uint32_t recv) +{ + call_ikesa(sa, send_dpd); + sa->set_message_id(sa, TRUE, send); + sa->set_message_id(sa, FALSE, recv); + sa->flush_queue(sa, TASK_QUEUE_QUEUED); +} + +/** + * Send a regular DPD from one IKE_SA to another + */ +static void send_dpd(ike_sa_t *from, ike_sa_t *to) +{ + uint32_t send, recv; + + send = from->get_message_id(from, TRUE); + recv = to->get_message_id(to, FALSE); + call_ikesa(from, send_dpd); + exchange_test_helper->process_message(exchange_test_helper, to, NULL); + exchange_test_helper->process_message(exchange_test_helper, from, NULL); + ck_assert_int_eq(send + 1, from->get_message_id(from, TRUE)); + ck_assert_int_eq(recv + 1, to->get_message_id(to, FALSE)); +} + +/** + * Send a number of DPDs from on IKE_SA to the other + */ +static void send_dpds(ike_sa_t *from, ike_sa_t *to, int count) +{ + while (count--) + { + send_dpd(from, to); + } +} + +static struct { + int dpds_a, dpds_b; + uint32_t send, recv; +} data[] = { + { 0, 0, 0, 2 }, + { 0, 0, 1, 3 }, + { 1, 0, 0, 3 }, + { 1, 0, 5, 8 }, + { 0, 1, 1, 2 }, + { 0, 1, 2, 2 }, + { 1, 1, 1, 3 }, + { 1, 1, 2, 4 }, + { 1, 2, 2, 4 }, +}; + +/** + * The responder syncs message IDs with the initiator + */ +START_TEST(test_responder) +{ + ike_sa_t *a, *b; + mid_sync_listener_t mid = { + .listener = { .message = (void*)handle_mid, }, + .init = { + .send = data[_i].send, + .recv = data[_i].recv, + }, + }; + + add_notify_to_ike_auth(); + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + send_dpds(a, b, data[_i].dpds_a); + send_dpds(b, a, data[_i].dpds_b); + + exchange_test_helper->add_listener(exchange_test_helper, &mid.listener); + send_mid_sync(b, data[_i].send, data[_i].recv); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_chunk_eq(mid.init.nonce, mid.resp.nonce); + ck_assert_int_eq(data[_i].recv, mid.resp.send); + ck_assert_int_eq(data[_i].send, mid.resp.recv); + ck_assert_int_eq(data[_i].recv, a->get_message_id(a, TRUE)); + ck_assert_int_eq(data[_i].send, a->get_message_id(a, FALSE)); + /* this currently won't be handled */ + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + charon->bus->remove_listener(charon->bus, &mid.listener); + + send_dpd(a, b); + send_dpd(b, a); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); + chunk_free(&mid.init.nonce); + chunk_free(&mid.resp.nonce); +} +END_TEST + +/** + * Make sure a retransmit is handled properly. + */ +START_TEST(test_retransmit) +{ + ike_sa_t *a, *b; + mid_sync_listener_t mid = { + .listener = { .message = (void*)handle_mid, }, + .init = { + .send = data[_i].send, + .recv = data[_i].recv, + }, + }; + message_t *msg, *retransmit; + + add_notify_to_ike_auth(); + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + send_dpds(a, b, data[_i].dpds_a); + send_dpds(b, a, data[_i].dpds_b); + + exchange_test_helper->add_listener(exchange_test_helper, &mid.listener); + send_mid_sync(b, data[_i].send, data[_i].recv); + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + retransmit = message_create_from_packet(msg->get_packet(msg)); + retransmit->parse_header(retransmit); + exchange_test_helper->process_message(exchange_test_helper, a, msg); + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + msg->destroy(msg); + exchange_test_helper->process_message(exchange_test_helper, a, retransmit); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + charon->bus->remove_listener(charon->bus, &mid.listener); + + send_dpd(a, b); + send_dpd(b, a); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); + chunk_free(&mid.init.nonce); + chunk_free(&mid.resp.nonce); +} +END_TEST + +/** + * Make sure a replayed or delayed notify is ignored. + */ +START_TEST(test_replay) +{ + ike_sa_t *a, *b; + mid_sync_listener_t mid = { + .listener = { .message = (void*)handle_mid, }, + .init = { + .send = data[_i].send, + .recv = data[_i].recv, + }, + }; + message_t *msg, *replay; + + add_notify_to_ike_auth(); + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + send_dpds(a, b, data[_i].dpds_a); + send_dpds(b, a, data[_i].dpds_b); + + exchange_test_helper->add_listener(exchange_test_helper, &mid.listener); + send_mid_sync(b, data[_i].send, data[_i].recv); + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + replay = message_create_from_packet(msg->get_packet(msg)); + replay->parse_header(replay); + exchange_test_helper->process_message(exchange_test_helper, a, msg); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + charon->bus->remove_listener(charon->bus, &mid.listener); + + send_dpd(a, b); + send_dpd(b, a); + + exchange_test_helper->process_message(exchange_test_helper, a, replay); + ck_assert(!exchange_test_helper->sender->dequeue(exchange_test_helper->sender)); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); + chunk_free(&mid.init.nonce); + chunk_free(&mid.resp.nonce); +} +END_TEST + +/** + * Make sure the notify is ignored if the extension is not enabled. + */ +START_TEST(test_disabled) +{ + ike_sa_t *a, *b; + mid_sync_listener_t mid = { + .listener = { .message = (void*)handle_mid, }, + .init = { + .send = data[_i].send, + .recv = data[_i].recv, + }, + }; + + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + send_dpds(a, b, data[_i].dpds_a); + send_dpds(b, a, data[_i].dpds_b); + + exchange_test_helper->add_listener(exchange_test_helper, &mid.listener); + send_mid_sync(b, data[_i].dpds_b, UINT_MAX); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + /* we don't expect a response and unchanged MIDs */ + ck_assert(!exchange_test_helper->sender->dequeue(exchange_test_helper->sender)); + ck_assert_int_eq(2 + data[_i].dpds_a, a->get_message_id(a, TRUE)); + ck_assert_int_eq(data[_i].dpds_b, a->get_message_id(a, FALSE)); + charon->bus->remove_listener(charon->bus, &mid.listener); + + send_dpd(a, b); + send_dpd(b, a); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); + chunk_free(&mid.init.nonce); + chunk_free(&mid.resp.nonce); +} +END_TEST + +static struct { + int dpds_a, dpds_b; + uint32_t send, recv; +} data_too_low[] = { + { 0, 1, 0, 2 }, + { 1, 2, 0, 0 }, + { 1, 2, 1, 3 }, +}; + +/** + * The responder syncs message IDs with the initiator but uses too low sender + * MIDs so the initiator ignores the notify. + */ +START_TEST(test_sender_too_low) +{ + ike_sa_t *a, *b; + mid_sync_listener_t mid = { + .listener = { .message = (void*)handle_mid, }, + .init = { + .send = data_too_low[_i].send, + .recv = data_too_low[_i].recv, + }, + }; + + add_notify_to_ike_auth(); + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + send_dpds(a, b, data_too_low[_i].dpds_a); + send_dpds(b, a, data_too_low[_i].dpds_b); + + exchange_test_helper->add_listener(exchange_test_helper, &mid.listener); + send_mid_sync(b, data_too_low[_i].dpds_b, UINT_MAX); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + /* we don't expect a response and unchanged MIDs */ + ck_assert(!exchange_test_helper->sender->dequeue(exchange_test_helper->sender)); + ck_assert_int_eq(2 + data_too_low[_i].dpds_a, a->get_message_id(a, TRUE)); + ck_assert_int_eq(data_too_low[_i].dpds_b, a->get_message_id(a, FALSE)); + charon->bus->remove_listener(charon->bus, &mid.listener); + + send_dpd(a, b); + send_dpd(b, a); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); + chunk_free(&mid.init.nonce); +} +END_TEST + +static struct { + int dpds_a, dpds_b; + uint32_t send, recv; + /* reversed so the table below is clearer */ + uint32_t recv_exp, send_exp; +} data_recv_update[] = { + { 0, 0, 0, 0, 0, 2 }, + { 0, 0, 0, 1, 0, 2 }, + { 0, 0, 1, 1, 1, 2 }, + { 1, 0, 0, 1, 0, 3 }, + { 1, 0, 5, 2, 5, 3 }, +}; + +/** + * The responder syncs message IDs with the initiator but uses too low receiver + * MID, which is updated by the initiator in the response. + */ +START_TEST(test_recv_update) +{ + ike_sa_t *a, *b; + mid_sync_listener_t mid = { + .listener = { .message = (void*)handle_mid, }, + .init = { + .send = data_recv_update[_i].send, + .recv = data_recv_update[_i].recv, + }, + }; + + add_notify_to_ike_auth(); + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + send_dpds(a, b, data_recv_update[_i].dpds_a); + send_dpds(b, a, data_recv_update[_i].dpds_b); + + exchange_test_helper->add_listener(exchange_test_helper, &mid.listener); + send_mid_sync(b, data_recv_update[_i].send, data_recv_update[_i].recv); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_chunk_eq(mid.init.nonce, mid.resp.nonce); + ck_assert_int_eq(data_recv_update[_i].send_exp, mid.resp.send); + ck_assert_int_eq(data_recv_update[_i].recv_exp, mid.resp.recv); + ck_assert_int_eq(data_recv_update[_i].send_exp, a->get_message_id(a, TRUE)); + ck_assert_int_eq(data_recv_update[_i].recv_exp, a->get_message_id(a, FALSE)); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + charon->bus->remove_listener(charon->bus, &mid.listener); + /* fake the receipt of the notify */ + b->set_message_id(b, TRUE, data_recv_update[_i].recv_exp); + b->set_message_id(b, FALSE, data_recv_update[_i].send_exp); + + send_dpd(a, b); + send_dpd(b, a); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); + chunk_free(&mid.init.nonce); + chunk_free(&mid.resp.nonce); +} +END_TEST + +static struct { + int dpds_a, dpds_b; + uint32_t send, recv; + /* reversed so the table below is clearer */ + uint32_t recv_exp, send_exp; +} data_active[] = { + { 0, 0, 0, 2, 0, 3 }, + { 0, 0, 1, 3, 1, 3 }, + { 1, 0, 0, 3, 0, 4 }, + { 1, 0, 5, 8, 5, 8 }, + { 0, 1, 1, 2, 1, 3 }, + { 0, 1, 2, 2, 2, 2 }, + { 1, 1, 1, 3, 1, 4 }, + { 1, 1, 2, 4, 2, 4 }, +}; + +/** + * The responder syncs message IDs with the initiator that waits for the + * response for an active task. + */ +START_TEST(test_active) +{ + ike_sa_t *a, *b; + mid_sync_listener_t mid = { + .listener = { .message = (void*)handle_mid, }, + .init = { + .send = data_active[_i].send, + .recv = data_active[_i].recv, + }, + }; + message_t *msg; + + add_notify_to_ike_auth(); + exchange_test_helper->establish_sa(exchange_test_helper, + &a, &b, NULL); + + send_dpds(a, b, data_active[_i].dpds_a); + send_dpds(b, a, data_active[_i].dpds_b); + + call_ikesa(a, send_dpd); + msg = exchange_test_helper->sender->dequeue(exchange_test_helper->sender); + msg->destroy(msg); + + exchange_test_helper->add_listener(exchange_test_helper, &mid.listener); + send_mid_sync(b, data_active[_i].recv_exp, data_active[_i].send_exp); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + ck_assert_chunk_eq(mid.init.nonce, mid.resp.nonce); + ck_assert_int_eq(data_active[_i].send_exp, mid.resp.send); + ck_assert_int_eq(data_active[_i].recv_exp, mid.resp.recv); + ck_assert_int_eq(data_active[_i].send_exp, a->get_message_id(a, TRUE)); + ck_assert_int_eq(data_active[_i].recv_exp, a->get_message_id(a, FALSE)); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + charon->bus->remove_listener(charon->bus, &mid.listener); + + /* the active task was queued again */ + call_ikesa(a, initiate, NULL, 0, NULL, NULL); + exchange_test_helper->process_message(exchange_test_helper, b, NULL); + exchange_test_helper->process_message(exchange_test_helper, a, NULL); + send_dpd(b, a); + + call_ikesa(a, destroy); + call_ikesa(b, destroy); + chunk_free(&mid.init.nonce); + chunk_free(&mid.resp.nonce); +} +END_TEST + +Suite *ike_mid_sync_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("ike MID sync"); + + tc = tcase_create("responder"); + tcase_add_loop_test(tc, test_responder, 0, countof(data)); + tcase_add_loop_test(tc, test_retransmit, 0, countof(data)); + tcase_add_loop_test(tc, test_replay, 0, countof(data)); + tcase_add_loop_test(tc, test_disabled, 0, countof(data)); + suite_add_tcase(s, tc); + + tc = tcase_create("sender MID too low"); + tcase_add_loop_test(tc, test_sender_too_low, 0, countof(data_too_low)); + suite_add_tcase(s, tc); + + tc = tcase_create("receiver MID updated"); + tcase_add_loop_test(tc, test_recv_update, 0, countof(data_recv_update)); + suite_add_tcase(s, tc); + + tc = tcase_create("active task"); + tcase_add_loop_test(tc, test_active, 0, countof(data_active)); + suite_add_tcase(s, tc); + + return s; +} |