summaryrefslogtreecommitdiff
path: root/src/libcharon/sa/ikev2
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@debian.org>2016-07-16 15:19:53 +0200
committerYves-Alexis Perez <corsac@debian.org>2016-07-16 15:19:53 +0200
commitbf372706c469764d59e9f29c39e3ecbebd72b8d2 (patch)
tree0f0e296e2d50e4a7faf99ae6fa428d2681e81ea1 /src/libcharon/sa/ikev2
parent518dd33c94e041db0444c7d1f33da363bb8e3faf (diff)
downloadvyos-strongswan-bf372706c469764d59e9f29c39e3ecbebd72b8d2.tar.gz
vyos-strongswan-bf372706c469764d59e9f29c39e3ecbebd72b8d2.zip
Imported Upstream version 5.5.0
Diffstat (limited to 'src/libcharon/sa/ikev2')
-rw-r--r--src/libcharon/sa/ikev2/authenticators/eap_authenticator.c16
-rw-r--r--src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c4
-rw-r--r--src/libcharon/sa/ikev2/connect_manager.c34
-rw-r--r--src/libcharon/sa/ikev2/keymat_v2.c20
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.c284
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c74
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.h2
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_delete.c73
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_delete.h2
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_rekey.c146
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_rekey.h23
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_auth.c23
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c8
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_delete.c62
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_init.c12
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_me.c2
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_mobike.c2
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_natd.c6
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_rekey.c261
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_rekey.h10
20 files changed, 707 insertions, 357 deletions
diff --git a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
index 91f6187f9..3ab59fada 100644
--- a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
@@ -104,7 +104,7 @@ struct private_eap_authenticator_t {
* load an EAP method
*/
static eap_method_t *load_method(private_eap_authenticator_t *this,
- eap_type_t type, u_int32_t vendor, eap_role_t role)
+ eap_type_t type, uint32_t vendor, eap_role_t role)
{
identification_t *server, *peer, *aaa;
auth_cfg_t *auth;
@@ -143,7 +143,7 @@ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this,
auth_cfg_t *auth;
eap_type_t type;
identification_t *id;
- u_int32_t vendor;
+ uint32_t vendor;
eap_payload_t *out;
char *action;
@@ -237,7 +237,7 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this,
eap_payload_t *in)
{
eap_type_t type, received_type, conf_type;
- u_int32_t vendor, received_vendor, conf_vendor;
+ uint32_t vendor, received_vendor, conf_vendor;
eap_payload_t *out;
auth_cfg_t *auth;
@@ -341,7 +341,7 @@ static eap_payload_t* client_process_eap(private_eap_authenticator_t *this,
eap_payload_t *in)
{
eap_type_t type, conf_type;
- u_int32_t vendor, conf_vendor;
+ uint32_t vendor, conf_vendor;
auth_cfg_t *auth;
eap_payload_t *out;
identification_t *id;
@@ -449,7 +449,7 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message,
auth_cfg_t *auth;
keymat_v2_t *keymat;
eap_type_t type;
- u_int32_t vendor;
+ uint32_t vendor;
auth_payload = (auth_payload_t*)message->get_payload(message,
PLV2_AUTH);
@@ -595,7 +595,7 @@ METHOD(authenticator_t, process_client, status_t,
}
if (this->require_mutual && !this->method->is_mutual(this->method))
{ /* we require mutual authentication due to EAP-only */
- u_int32_t vendor;
+ uint32_t vendor;
DBG1(DBG_IKE, "EAP-only authentication requires a mutual and "
"MSK deriving EAP method, but %N is not",
@@ -623,7 +623,7 @@ METHOD(authenticator_t, process_client, status_t,
case EAP_SUCCESS:
{
eap_type_t type;
- u_int32_t vendor;
+ uint32_t vendor;
auth_cfg_t *cfg;
if (this->method->get_msk(this->method, &this->msk) == SUCCESS)
@@ -685,7 +685,7 @@ METHOD(authenticator_t, is_mutual, bool,
{
if (this->method)
{
- u_int32_t vendor;
+ uint32_t vendor;
if (this->method->get_type(this->method, &vendor) != EAP_IDENTITY ||
vendor != 0)
diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
index 04ccd4f4f..6fd34e0a6 100644
--- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
@@ -63,7 +63,7 @@ struct private_pubkey_authenticator_t {
static bool parse_signature_auth_data(chunk_t *auth_data, key_type_t *key_type,
signature_scheme_t *scheme)
{
- u_int8_t len;
+ uint8_t len;
int oid;
if (!auth_data->len)
@@ -91,7 +91,7 @@ static bool build_signature_auth_data(chunk_t *auth_data,
signature_scheme_t scheme)
{
chunk_t data;
- u_int8_t len;
+ uint8_t len;
int oid;
oid = signature_scheme_to_oid(scheme);
diff --git a/src/libcharon/sa/ikev2/connect_manager.c b/src/libcharon/sa/ikev2/connect_manager.c
index 161c4fdaf..280796d8c 100644
--- a/src/libcharon/sa/ikev2/connect_manager.c
+++ b/src/libcharon/sa/ikev2/connect_manager.c
@@ -92,10 +92,10 @@ typedef struct endpoint_pair_t endpoint_pair_t;
*/
struct endpoint_pair_t {
/** pair id */
- u_int32_t id;
+ uint32_t id;
/** priority */
- u_int64_t priority;
+ uint64_t priority;
/** local endpoint */
host_t *local;
@@ -107,7 +107,7 @@ struct endpoint_pair_t {
check_state_t state;
/** number of retransmissions */
- u_int32_t retransmitted;
+ uint32_t retransmitted;
/** the generated packet */
packet_t *packet;
@@ -132,8 +132,8 @@ static endpoint_pair_t *endpoint_pair_create(endpoint_notify_t *initiator,
{
endpoint_pair_t *this;
- u_int32_t pi = initiator->get_priority(initiator);
- u_int32_t pr = responder->get_priority(responder);
+ uint32_t pi = initiator->get_priority(initiator);
+ uint32_t pr = responder->get_priority(responder);
INIT(this,
.priority = pow(2, 32) * min(pi, pr) + 2 * max(pi, pr)
@@ -313,7 +313,7 @@ typedef struct check_t check_t;
*/
struct check_t {
/** message id */
- u_int32_t mid;
+ uint32_t mid;
/** source of the connectivity check */
host_t *src;
@@ -375,7 +375,7 @@ struct callback_data_t {
chunk_t connect_id;
/** message (pair) id */
- u_int32_t mid;
+ uint32_t mid;
};
/**
@@ -406,7 +406,7 @@ static callback_data_t *callback_data_create(private_connect_manager_t *connect_
* Creates a new retransmission data object
*/
static callback_data_t *retransmit_data_create(private_connect_manager_t *connect_manager,
- chunk_t connect_id, u_int32_t mid)
+ chunk_t connect_id, uint32_t mid)
{
callback_data_t *this = callback_data_create(connect_manager, connect_id);
this->mid = mid;
@@ -576,7 +576,7 @@ static status_t get_pair_by_hosts(linked_list_t *pairs, host_t *local,
(void**)pair, local, remote);
}
-static bool match_pair_by_id(endpoint_pair_t *current, u_int32_t *id)
+static bool match_pair_by_id(endpoint_pair_t *current, uint32_t *id)
{
return current->id == *id;
}
@@ -584,7 +584,7 @@ static bool match_pair_by_id(endpoint_pair_t *current, u_int32_t *id)
/**
* Searches for a pair with a specific id
*/
-static status_t get_pair_by_id(check_list_t *checklist, u_int32_t id,
+static status_t get_pair_by_id(check_list_t *checklist, uint32_t id,
endpoint_pair_t **pair)
{
return checklist->pairs->find_first(checklist->pairs,
@@ -669,7 +669,7 @@ static void prune_pairs(linked_list_t *pairs)
{
enumerator_t *enumerator, *search;
endpoint_pair_t *current, *other;
- u_int32_t id = 0;
+ uint32_t id = 0;
enumerator = pairs->create_enumerator(pairs);
search = pairs->create_enumerator(pairs);
@@ -826,7 +826,7 @@ static status_t process_payloads(message_t *message, check_t *check)
static chunk_t build_signature(private_connect_manager_t *this,
check_list_t *checklist, check_t *check, bool outbound)
{
- u_int32_t mid;
+ uint32_t mid;
chunk_t mid_chunk, key_chunk, sig_chunk;
chunk_t sig_hash;
@@ -851,7 +851,7 @@ static chunk_t build_signature(private_connect_manager_t *this,
}
static void queue_retransmission(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair);
-static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, u_int32_t time);
+static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, uint32_t time);
static void finish_checks(private_connect_manager_t *this, check_list_t *checklist);
/**
@@ -1019,11 +1019,11 @@ static void queue_retransmission(private_connect_manager_t *this, check_list_t *
job = (job_t*)callback_job_create((callback_job_cb_t)retransmit, data,
(callback_job_cleanup_t)callback_data_destroy, NULL);
- u_int32_t retransmission = pair->retransmitted + 1;
- u_int32_t rto = ME_INTERVAL;
+ uint32_t retransmission = pair->retransmitted + 1;
+ uint32_t rto = ME_INTERVAL;
if (retransmission > ME_BOOST)
{
- rto = (u_int32_t)(ME_INTERVAL * pow(ME_RETRANS_BASE, retransmission - ME_BOOST));
+ rto = (uint32_t)(ME_INTERVAL * pow(ME_RETRANS_BASE, retransmission - ME_BOOST));
}
DBG2(DBG_IKE, "scheduling retransmission %d of pair '%d' in %dms",
retransmission, pair->id, rto);
@@ -1165,7 +1165,7 @@ static job_requeue_t sender(callback_data_t *data)
* Schedules checks for a checklist (time in ms)
*/
static void schedule_checks(private_connect_manager_t *this,
- check_list_t *checklist, u_int32_t time)
+ check_list_t *checklist, uint32_t time)
{
callback_data_t *data = callback_data_create(this, checklist->connect_id);
checklist->sender = (job_t*)callback_job_create((callback_job_cb_t)sender,
diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c
index 55cb5dd9c..e37399841 100644
--- a/src/libcharon/sa/ikev2/keymat_v2.c
+++ b/src/libcharon/sa/ikev2/keymat_v2.c
@@ -99,8 +99,8 @@ METHOD(keymat_t, create_nonce_gen, nonce_gen_t*,
/**
* Derive IKE keys for a combined AEAD algorithm
*/
-static bool derive_ike_aead(private_keymat_v2_t *this, u_int16_t alg,
- u_int16_t key_size, prf_plus_t *prf_plus)
+static bool derive_ike_aead(private_keymat_v2_t *this, uint16_t alg,
+ uint16_t key_size, prf_plus_t *prf_plus)
{
aead_t *aead_i, *aead_r;
chunk_t key = chunk_empty;
@@ -189,8 +189,8 @@ failure:
/**
* Derive IKE keys for traditional encryption and MAC algorithms
*/
-static bool derive_ike_traditional(private_keymat_v2_t *this, u_int16_t enc_alg,
- u_int16_t enc_size, u_int16_t int_alg, prf_plus_t *prf_plus)
+static bool derive_ike_traditional(private_keymat_v2_t *this, uint16_t enc_alg,
+ uint16_t enc_size, uint16_t int_alg, prf_plus_t *prf_plus)
{
crypter_t *crypter_i = NULL, *crypter_r = NULL;
signer_t *signer_i, *signer_r;
@@ -302,11 +302,11 @@ METHOD(keymat_v2_t, derive_ike_keys, bool,
chunk_t skeyseed, key, secret, full_nonce, fixed_nonce, prf_plus_seed;
chunk_t spi_i, spi_r;
prf_plus_t *prf_plus = NULL;
- u_int16_t alg, key_size, int_alg;
+ uint16_t alg, key_size, int_alg;
prf_t *rekey_prf = NULL;
- spi_i = chunk_alloca(sizeof(u_int64_t));
- spi_r = chunk_alloca(sizeof(u_int64_t));
+ spi_i = chunk_alloca(sizeof(uint64_t));
+ spi_r = chunk_alloca(sizeof(uint64_t));
if (!dh->get_shared_secret(dh, &secret))
{
@@ -354,8 +354,8 @@ METHOD(keymat_v2_t, derive_ike_keys, bool,
break;
}
fixed_nonce = chunk_cat("cc", nonce_i, nonce_r);
- *((u_int64_t*)spi_i.ptr) = id->get_initiator_spi(id);
- *((u_int64_t*)spi_r.ptr) = id->get_responder_spi(id);
+ *((uint64_t*)spi_i.ptr) = id->get_initiator_spi(id);
+ *((uint64_t*)spi_r.ptr) = id->get_responder_spi(id);
prf_plus_seed = chunk_cat("ccc", full_nonce, spi_i, spi_r);
/* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr)
@@ -489,7 +489,7 @@ METHOD(keymat_v2_t, derive_child_keys, bool,
chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i,
chunk_t *encr_r, chunk_t *integ_r)
{
- u_int16_t enc_alg, int_alg, enc_size = 0, int_size = 0;
+ uint16_t enc_alg, int_alg, enc_size = 0, int_size = 0;
chunk_t seed, secret = chunk_empty;
prf_plus_t *prf_plus;
diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c
index c2f972ab1..41a4e1b75 100644
--- a/src/libcharon/sa/ikev2/task_manager_v2.c
+++ b/src/libcharon/sa/ikev2/task_manager_v2.c
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2007-2015 Tobias Brunner
+ * Copyright (C) 2007-2016 Tobias Brunner
* Copyright (C) 2007-2010 Martin Willi
- * Hochschule fuer Technik Rapperswil
+ * 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
@@ -43,30 +43,14 @@
#include <encoding/payloads/unknown_payload.h>
#include <processing/jobs/retransmit_job.h>
#include <processing/jobs/delete_ike_sa_job.h>
+#include <processing/jobs/initiate_tasks_job.h>
#ifdef ME
#include <sa/ikev2/tasks/ike_me.h>
#endif
-typedef struct exchange_t exchange_t;
-
-/**
- * An exchange in the air, used do detect and handle retransmission
- */
-struct exchange_t {
-
- /**
- * Message ID used for this transaction
- */
- u_int32_t mid;
-
- /**
- * generated packet for retransmission
- */
- packet_t *packet;
-};
-
typedef struct private_task_manager_t private_task_manager_t;
+typedef struct queued_task_t queued_task_t;
/**
* private data of the task manager
@@ -90,7 +74,7 @@ struct private_task_manager_t {
/**
* Message ID of the exchange
*/
- u_int32_t mid;
+ uint32_t mid;
/**
* packet(s) for retransmission
@@ -111,7 +95,7 @@ struct private_task_manager_t {
/**
* Message ID of the exchange
*/
- u_int32_t mid;
+ uint32_t mid;
/**
* how many times we have retransmitted so far
@@ -182,6 +166,22 @@ struct private_task_manager_t {
};
/**
+ * Queued tasks
+ */
+struct queued_task_t {
+
+ /**
+ * Queued task
+ */
+ task_t *task;
+
+ /**
+ * Time before which the task is not to be initiated
+ */
+ timeval_t time;
+};
+
+/**
* Reset retransmission packet list
*/
static void clear_packets(array_t *array)
@@ -216,6 +216,12 @@ METHOD(task_manager_t, flush_queue, void,
}
while (array_remove(array, ARRAY_TAIL, &task))
{
+ if (queue == TASK_QUEUE_QUEUED)
+ {
+ queued_task_t *queued = (queued_task_t*)task;
+ task = queued->task;
+ free(queued);
+ }
task->destroy(task);
}
}
@@ -229,22 +235,28 @@ METHOD(task_manager_t, flush, void,
}
/**
- * move a task of a specific type from the queue to the active list
+ * Move a task of a specific type from the queue to the active list, if it is
+ * not delayed.
*/
static bool activate_task(private_task_manager_t *this, task_type_t type)
{
enumerator_t *enumerator;
- task_t *task;
+ queued_task_t *queued;
+ timeval_t now;
bool found = FALSE;
+ time_monotonic(&now);
+
enumerator = array_create_enumerator(this->queued_tasks);
- while (enumerator->enumerate(enumerator, (void**)&task))
+ while (enumerator->enumerate(enumerator, (void**)&queued))
{
- if (task->get_type(task) == type)
+ if (queued->task->get_type(queued->task) == type &&
+ !timercmp(&now, &queued->time, <))
{
DBG2(DBG_IKE, " activating %N task", task_type_names, type);
array_remove_at(this->queued_tasks, enumerator);
- array_insert(this->active_tasks, ARRAY_TAIL, task);
+ array_insert(this->active_tasks, ARRAY_TAIL, queued->task);
+ free(queued);
found = TRUE;
break;
}
@@ -303,12 +315,12 @@ static bool generate_message(private_task_manager_t *this, message_t *message,
}
METHOD(task_manager_t, retransmit, status_t,
- private_task_manager_t *this, u_int32_t message_id)
+ private_task_manager_t *this, uint32_t message_id)
{
if (message_id == this->initiating.mid &&
array_count(this->initiating.packets))
{
- u_int32_t timeout;
+ uint32_t timeout;
job_t *job;
enumerator_t *enumerator;
packet_t *packet;
@@ -336,7 +348,7 @@ METHOD(task_manager_t, retransmit, status_t,
{
if (this->initiating.retransmitted <= this->retransmit_tries)
{
- timeout = (u_int32_t)(this->retransmit_timeout * 1000.0 *
+ timeout = (uint32_t)(this->retransmit_timeout * 1000.0 *
pow(this->retransmit_base, this->initiating.retransmitted));
}
else
@@ -352,7 +364,8 @@ METHOD(task_manager_t, retransmit, status_t,
{
DBG1(DBG_IKE, "retransmit %d of request with message ID %d",
this->initiating.retransmitted, message_id);
- charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet);
+ charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet,
+ this->initiating.retransmitted);
}
if (!mobike)
{
@@ -534,6 +547,7 @@ METHOD(task_manager_t, initiate, status_t,
break;
}
case IKE_REKEYING:
+ case IKE_REKEYED:
if (activate_task(this, TASK_IKE_DELETE))
{
exchange = INFORMATIONAL;
@@ -610,7 +624,8 @@ METHOD(task_manager_t, initiate, status_t,
case FAILED:
default:
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
- if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
+ if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING &&
+ this->ike_sa->get_state(this->ike_sa) != IKE_REKEYED)
{
charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
}
@@ -694,6 +709,13 @@ static status_t process_response(private_task_manager_t *this,
}
enumerator->destroy(enumerator);
+ if (this->initiating.retransmitted)
+ {
+ packet_t *packet = NULL;
+ array_get(this->initiating.packets, 0, &packet);
+ charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_CLEARED, packet);
+ }
+
/* catch if we get resetted while processing */
this->reset = FALSE;
enumerator = array_create_enumerator(this->active_tasks);
@@ -751,8 +773,7 @@ static bool handle_collisions(private_task_manager_t *this, task_t *task)
/* do we have to check */
if (type == TASK_IKE_REKEY || type == TASK_CHILD_REKEY ||
- type == TASK_CHILD_DELETE || type == TASK_IKE_DELETE ||
- type == TASK_IKE_REAUTH)
+ type == TASK_CHILD_DELETE || type == TASK_IKE_DELETE)
{
/* find an exchange collision, and notify these tasks */
enumerator = array_create_enumerator(this->active_tasks);
@@ -761,8 +782,7 @@ static bool handle_collisions(private_task_manager_t *this, task_t *task)
switch (active->get_type(active))
{
case TASK_IKE_REKEY:
- if (type == TASK_IKE_REKEY || type == TASK_IKE_DELETE ||
- type == TASK_IKE_REAUTH)
+ if (type == TASK_IKE_REKEY || type == TASK_IKE_DELETE)
{
ike_rekey_t *rekey = (ike_rekey_t*)active;
rekey->collide(rekey, task);
@@ -799,7 +819,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
host_t *me, *other;
bool delete = FALSE, hook = FALSE;
ike_sa_id_t *id = NULL;
- u_int64_t responder_spi = 0;
+ uint64_t responder_spi = 0;
bool result;
me = request->get_destination(request);
@@ -839,6 +859,10 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
/* FALL */
case DESTROY_ME:
/* destroy IKE_SA, but SEND response first */
+ if (handle_collisions(this, task))
+ {
+ array_remove_at(this->passive_tasks, enumerator);
+ }
delete = TRUE;
break;
}
@@ -901,9 +925,11 @@ static status_t process_request(private_task_manager_t *this,
payload_t *payload;
notify_payload_t *notify;
delete_payload_t *delete;
+ ike_sa_state_t state;
if (array_count(this->passive_tasks) == 0)
{ /* create tasks depending on request type, if not already some queued */
+ state = this->ike_sa->get_state(this->ike_sa);
switch (message->get_exchange_type(message))
{
case IKE_SA_INIT:
@@ -939,8 +965,8 @@ static status_t process_request(private_task_manager_t *this,
{ /* FIXME: we should prevent this on mediation connections */
bool notify_found = FALSE, ts_found = FALSE;
- if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
- this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING)
+ if (state == IKE_CREATED ||
+ state == IKE_CONNECTING)
{
DBG1(DBG_IKE, "received CREATE_CHILD_SA request for "
"unestablished IKE_SA, rejected");
@@ -1005,6 +1031,14 @@ static status_t process_request(private_task_manager_t *this,
case PLV2_NOTIFY:
{
notify = (notify_payload_t*)payload;
+ if (state == IKE_REKEYED)
+ {
+ DBG1(DBG_IKE, "received unexpected notify %N "
+ "for rekeyed IKE_SA, ignored",
+ notify_type_names,
+ notify->get_notify_type(notify));
+ break;
+ }
switch (notify->get_notify_type(notify))
{
case ADDITIONAL_IP4_ADDRESS:
@@ -1252,7 +1286,7 @@ static void send_notify_response(private_task_manager_t *this,
static status_t parse_message(private_task_manager_t *this, message_t *msg)
{
status_t status;
- u_int8_t type = 0;
+ uint8_t type = 0;
status = msg->parse_body(msg, this->ike_sa->get_keymat(this->ike_sa));
@@ -1345,8 +1379,10 @@ METHOD(task_manager_t, process_message, status_t,
{
host_t *me, *other;
status_t status;
- u_int32_t mid;
+ uint32_t mid;
bool schedule_delete_job = FALSE;
+ ike_sa_state_t state;
+ exchange_type_t type;
charon->bus->message(charon->bus, msg, TRUE, FALSE);
status = parse_message(this, msg);
@@ -1387,15 +1423,16 @@ METHOD(task_manager_t, process_message, status_t,
{
if (mid == this->responding.mid)
{
- /* reject initial messages if not received in specific states */
- if ((msg->get_exchange_type(msg) == IKE_SA_INIT &&
- this->ike_sa->get_state(this->ike_sa) != IKE_CREATED) ||
- (msg->get_exchange_type(msg) == IKE_AUTH &&
- this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING))
+ /* reject initial messages if not received in specific states,
+ * after rekeying we only expect a DELETE in an INFORMATIONAL */
+ type = msg->get_exchange_type(msg);
+ state = this->ike_sa->get_state(this->ike_sa);
+ if ((type == IKE_SA_INIT && state != IKE_CREATED) ||
+ (type == IKE_AUTH && state != IKE_CONNECTING) ||
+ (state == IKE_REKEYED && type != INFORMATIONAL))
{
DBG1(DBG_IKE, "ignoring %N in IKE_SA state %N",
- exchange_type_names, msg->get_exchange_type(msg),
- ike_sa_state_names, this->ike_sa->get_state(this->ike_sa));
+ exchange_type_names, type, ike_sa_state_names, state);
return FAILED;
}
if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
@@ -1499,18 +1536,19 @@ METHOD(task_manager_t, process_message, status_t,
return SUCCESS;
}
-METHOD(task_manager_t, queue_task, void,
- private_task_manager_t *this, task_t *task)
+METHOD(task_manager_t, queue_task_delayed, void,
+ private_task_manager_t *this, task_t *task, uint32_t delay)
{
+ enumerator_t *enumerator;
+ queued_task_t *queued;
+ timeval_t time;
+
if (task->get_type(task) == TASK_IKE_MOBIKE)
{ /* there is no need to queue more than one mobike task */
- enumerator_t *enumerator;
- task_t *current;
-
enumerator = array_create_enumerator(this->queued_tasks);
- while (enumerator->enumerate(enumerator, &current))
+ while (enumerator->enumerate(enumerator, &queued))
{
- if (current->get_type(current) == TASK_IKE_MOBIKE)
+ if (queued->task->get_type(queued->task) == TASK_IKE_MOBIKE)
{
enumerator->destroy(enumerator);
task->destroy(task);
@@ -1519,8 +1557,35 @@ METHOD(task_manager_t, queue_task, void,
}
enumerator->destroy(enumerator);
}
- DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
- array_insert(this->queued_tasks, ARRAY_TAIL, task);
+ time_monotonic(&time);
+ if (delay)
+ {
+ job_t *job;
+
+ DBG2(DBG_IKE, "queueing %N task (delayed by %us)", task_type_names,
+ task->get_type(task), delay);
+ time.tv_sec += delay;
+
+ job = (job_t*)initiate_tasks_job_create(
+ this->ike_sa->get_id(this->ike_sa));
+ lib->scheduler->schedule_job_tv(lib->scheduler, job, time);
+ }
+ else
+ {
+ DBG2(DBG_IKE, "queueing %N task", task_type_names,
+ task->get_type(task));
+ }
+ INIT(queued,
+ .task = task,
+ .time = time,
+ );
+ array_insert(this->queued_tasks, ARRAY_TAIL, queued);
+}
+
+METHOD(task_manager_t, queue_task, void,
+ private_task_manager_t *this, task_t *task)
+{
+ queue_task_delayed(this, task, 0);
}
/**
@@ -1530,12 +1595,12 @@ static bool has_queued(private_task_manager_t *this, task_type_t type)
{
enumerator_t *enumerator;
bool found = FALSE;
- task_t *task;
+ queued_task_t *queued;
enumerator = array_create_enumerator(this->queued_tasks);
- while (enumerator->enumerate(enumerator, &task))
+ while (enumerator->enumerate(enumerator, &queued))
{
- if (task->get_type(task) == type)
+ if (queued->task->get_type(queued->task) == type)
{
found = TRUE;
break;
@@ -1614,7 +1679,7 @@ static void trigger_mbb_reauth(private_task_manager_t *this)
child_cfg_t *cfg;
ike_sa_t *new;
host_t *host;
- task_t *task;
+ queued_task_t *queued;
new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
this->ike_sa->get_version(this->ike_sa), TRUE);
@@ -1645,13 +1710,14 @@ static void trigger_mbb_reauth(private_task_manager_t *this)
enumerator->destroy(enumerator);
enumerator = array_create_enumerator(this->queued_tasks);
- while (enumerator->enumerate(enumerator, &task))
+ while (enumerator->enumerate(enumerator, &queued))
{
- if (task->get_type(task) == TASK_CHILD_CREATE)
+ if (queued->task->get_type(queued->task) == TASK_CHILD_CREATE)
{
- task->migrate(task, new);
- new->queue_task(new, task);
+ queued->task->migrate(queued->task, new);
+ new->queue_task(new, queued->task);
array_remove_at(this->queued_tasks, enumerator);
+ free(queued);
}
}
enumerator->destroy(enumerator);
@@ -1726,7 +1792,7 @@ METHOD(task_manager_t, queue_mobike, void,
}
METHOD(task_manager_t, queue_child, void,
- private_task_manager_t *this, child_cfg_t *cfg, u_int32_t reqid,
+ private_task_manager_t *this, child_cfg_t *cfg, uint32_t reqid,
traffic_selector_t *tsi, traffic_selector_t *tsr)
{
child_create_t *task;
@@ -1740,13 +1806,13 @@ METHOD(task_manager_t, queue_child, void,
}
METHOD(task_manager_t, queue_child_rekey, void,
- private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi)
+ private_task_manager_t *this, protocol_id_t protocol, uint32_t spi)
{
queue_task(this, (task_t*)child_rekey_create(this->ike_sa, protocol, spi));
}
METHOD(task_manager_t, queue_child_delete, void,
- private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi,
+ private_task_manager_t *this, protocol_id_t protocol, uint32_t spi,
bool expired)
{
queue_task(this, (task_t*)child_delete_create(this->ike_sa,
@@ -1776,34 +1842,62 @@ METHOD(task_manager_t, adopt_tasks, void,
private_task_manager_t *this, task_manager_t *other_public)
{
private_task_manager_t *other = (private_task_manager_t*)other_public;
- task_t *task;
+ queued_task_t *queued;
+ timeval_t now;
+
+ time_monotonic(&now);
/* move queued tasks from other to this */
- while (array_remove(other->queued_tasks, ARRAY_TAIL, &task))
+ while (array_remove(other->queued_tasks, ARRAY_TAIL, &queued))
{
- DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task));
- task->migrate(task, this->ike_sa);
- array_insert(this->queued_tasks, ARRAY_HEAD, task);
+ DBG2(DBG_IKE, "migrating %N task", task_type_names,
+ queued->task->get_type(queued->task));
+ queued->task->migrate(queued->task, this->ike_sa);
+ /* don't delay tasks on the new IKE_SA */
+ queued->time = now;
+ array_insert(this->queued_tasks, ARRAY_HEAD, queued);
}
}
/**
- * Migrates child-creating tasks from src to dst
+ * Migrates child-creating tasks from other to this
*/
static void migrate_child_tasks(private_task_manager_t *this,
- array_t *src, array_t *dst)
+ private_task_manager_t *other,
+ task_queue_t queue)
{
enumerator_t *enumerator;
+ array_t *array;
task_t *task;
- enumerator = array_create_enumerator(src);
+ switch (queue)
+ {
+ case TASK_QUEUE_ACTIVE:
+ array = other->active_tasks;
+ break;
+ case TASK_QUEUE_QUEUED:
+ array = other->queued_tasks;
+ break;
+ default:
+ return;
+ }
+
+ enumerator = array_create_enumerator(array);
while (enumerator->enumerate(enumerator, &task))
{
+ queued_task_t *queued = NULL;
+
+ if (queue == TASK_QUEUE_QUEUED)
+ {
+ queued = (queued_task_t*)task;
+ task = queued->task;
+ }
if (task->get_type(task) == TASK_CHILD_CREATE)
{
- array_remove_at(src, enumerator);
+ array_remove_at(array, enumerator);
task->migrate(task, this->ike_sa);
- array_insert(dst, ARRAY_TAIL, task);
+ queue_task(this, task);
+ free(queued);
}
}
enumerator->destroy(enumerator);
@@ -1815,9 +1909,9 @@ METHOD(task_manager_t, adopt_child_tasks, void,
private_task_manager_t *other = (private_task_manager_t*)other_public;
/* move active child tasks from other to this */
- migrate_child_tasks(this, other->active_tasks, this->queued_tasks);
+ migrate_child_tasks(this, other, TASK_QUEUE_ACTIVE);
/* do the same for queued tasks */
- migrate_child_tasks(this, other->queued_tasks, this->queued_tasks);
+ migrate_child_tasks(this, other, TASK_QUEUE_QUEUED);
}
METHOD(task_manager_t, busy, bool,
@@ -1827,10 +1921,12 @@ METHOD(task_manager_t, busy, bool,
}
METHOD(task_manager_t, reset, void,
- private_task_manager_t *this, u_int32_t initiate, u_int32_t respond)
+ private_task_manager_t *this, uint32_t initiate, uint32_t respond)
{
enumerator_t *enumerator;
+ queued_task_t *queued;
task_t *task;
+ timeval_t now;
/* reset message counters and retransmit packets */
clear_packets(this->responding.packets);
@@ -1849,11 +1945,13 @@ METHOD(task_manager_t, reset, void,
}
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+ time_monotonic(&now);
/* reset queued tasks */
enumerator = array_create_enumerator(this->queued_tasks);
- while (enumerator->enumerate(enumerator, &task))
+ while (enumerator->enumerate(enumerator, &queued))
{
- task->migrate(task, this->ike_sa);
+ queued->time = now;
+ queued->task->migrate(queued->task, this->ike_sa);
}
enumerator->destroy(enumerator);
@@ -1861,12 +1959,25 @@ METHOD(task_manager_t, reset, void,
while (array_remove(this->active_tasks, ARRAY_TAIL, &task))
{
task->migrate(task, this->ike_sa);
- array_insert(this->queued_tasks, ARRAY_HEAD, task);
+ INIT(queued,
+ .task = task,
+ .time = now,
+ );
+ array_insert(this->queued_tasks, ARRAY_HEAD, queued);
}
this->reset = TRUE;
}
+/**
+ * Filter queued tasks
+ */
+static bool filter_queued(void *unused, queued_task_t **queued, task_t **task)
+{
+ *task = (*queued)->task;
+ return TRUE;
+}
+
METHOD(task_manager_t, create_task_enumerator, enumerator_t*,
private_task_manager_t *this, task_queue_t queue)
{
@@ -1877,7 +1988,9 @@ METHOD(task_manager_t, create_task_enumerator, enumerator_t*,
case TASK_QUEUE_PASSIVE:
return array_create_enumerator(this->passive_tasks);
case TASK_QUEUE_QUEUED:
- return array_create_enumerator(this->queued_tasks);
+ return enumerator_create_filter(
+ array_create_enumerator(this->queued_tasks),
+ (void*)filter_queued, NULL, NULL);
default:
return enumerator_create_empty();
}
@@ -1913,6 +2026,7 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa)
.task_manager = {
.process_message = _process_message,
.queue_task = _queue_task,
+ .queue_task_delayed = _queue_task_delayed,
.queue_ike = _queue_ike,
.queue_ike_rekey = _queue_ike_rekey,
.queue_ike_reauth = _queue_ike_reauth,
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c
index 3d4ded944..64a82850b 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.c
+++ b/src/libcharon/sa/ikev2/tasks/child_create.c
@@ -1,8 +1,8 @@
/*
- * Copyright (C) 2008 Tobias Brunner
+ * Copyright (C) 2008-2016 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
+ * 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
@@ -29,7 +29,7 @@
#include <encoding/payloads/delete_payload.h>
#include <processing/jobs/delete_ike_sa_job.h>
#include <processing/jobs/inactivity_job.h>
-
+#include <processing/jobs/initiate_tasks_job.h>
typedef struct private_child_create_t private_child_create_t;
@@ -151,27 +151,27 @@ struct private_child_create_t {
/**
* Own allocated SPI
*/
- u_int32_t my_spi;
+ uint32_t my_spi;
/**
* SPI received in proposal
*/
- u_int32_t other_spi;
+ uint32_t other_spi;
/**
* Own allocated Compression Parameter Index (CPI)
*/
- u_int16_t my_cpi;
+ uint16_t my_cpi;
/**
* Other Compression Parameter Index (CPI), received via IPCOMP_SUPPORTED
*/
- u_int16_t other_cpi;
+ uint16_t other_cpi;
/**
* reqid to use if we are rekeying
*/
- u_int32_t reqid;
+ uint32_t reqid;
/**
* Explicit inbound mark value
@@ -205,6 +205,25 @@ struct private_child_create_t {
};
/**
+ * Schedule a retry if creating the CHILD_SA temporary failed
+ */
+static void schedule_delayed_retry(private_child_create_t *this)
+{
+ child_create_t *task;
+ uint32_t retry;
+
+ retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
+
+ task = child_create_create(this->ike_sa,
+ this->config->get_ref(this->config), FALSE,
+ this->packet_tsi, this->packet_tsr);
+ task->use_reqid(task, this->reqid);
+ DBG1(DBG_IKE, "creating CHILD_SA failed, trying again in %d seconds",
+ retry);
+ this->ike_sa->queue_task_delayed(this->ike_sa, (task_t*)task, retry);
+}
+
+/**
* get the nonce from a message
*/
static status_t get_nonce(message_t *message, chunk_t *nonce)
@@ -306,7 +325,7 @@ static bool allocate_spi(private_child_create_t *this)
*/
static void schedule_inactivity_timeout(private_child_create_t *this)
{
- u_int32_t timeout, id;
+ uint32_t timeout, id;
bool close_ike;
timeout = this->config->get_inactivity(this->config);
@@ -386,7 +405,7 @@ static linked_list_t* get_transport_nat_ts(private_child_create_t *this,
linked_list_t *out;
traffic_selector_t *ts;
host_t *ike, *first = NULL;
- u_int8_t mask;
+ uint8_t mask;
if (local)
{
@@ -464,7 +483,7 @@ static status_t select_and_install(private_child_create_t *this,
chunk_t integ_i = chunk_empty, integ_r = chunk_empty;
linked_list_t *my_ts, *other_ts;
host_t *me, *other;
- bool private;
+ bool private, prefer_configured;
if (this->proposals == NULL)
{
@@ -481,8 +500,10 @@ static status_t select_and_install(private_child_create_t *this,
other = this->ike_sa->get_other_host(this->ike_sa);
private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN);
+ prefer_configured = lib->settings->get_bool(lib->settings,
+ "%s.prefer_configured_proposals", TRUE, lib->ns);
this->proposal = this->config->select_proposal(this->config,
- this->proposals, no_dh, private);
+ this->proposals, no_dh, private, prefer_configured);
if (this->proposal == NULL)
{
DBG1(DBG_IKE, "no acceptable proposal found");
@@ -501,7 +522,7 @@ static status_t select_and_install(private_child_create_t *this,
if (!this->proposal->has_dh_group(this->proposal, this->dh_group))
{
- u_int16_t group;
+ uint16_t group;
if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP,
&group, NULL))
@@ -798,7 +819,7 @@ static bool build_payloads(private_child_create_t *this, message_t *message)
* Adds an IPCOMP_SUPPORTED notify to the message, allocating a CPI
*/
static void add_ipcomp_notify(private_child_create_t *this,
- message_t *message, u_int8_t ipcomp)
+ message_t *message, uint8_t ipcomp)
{
this->my_cpi = this->child_sa->alloc_cpi(this->child_sa);
if (this->my_cpi)
@@ -838,11 +859,11 @@ static void handle_notify(private_child_create_t *this, notify_payload_t *notify
case IPCOMP_SUPPORTED:
{
ipcomp_transform_t ipcomp;
- u_int16_t cpi;
+ uint16_t cpi;
chunk_t data;
data = notify->get_notification_data(notify);
- cpi = *(u_int16_t*)data.ptr;
+ cpi = *(uint16_t*)data.ptr;
ipcomp = (ipcomp_transform_t)(*(data.ptr + 2));
switch (ipcomp)
{
@@ -1232,13 +1253,13 @@ METHOD(task_t, build_r, status_t,
if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING)
{
DBG1(DBG_IKE, "unable to create CHILD_SA while rekeying IKE_SA");
- message->add_notify(message, TRUE, NO_ADDITIONAL_SAS, chunk_empty);
+ message->add_notify(message, TRUE, TEMPORARY_FAILURE, chunk_empty);
return SUCCESS;
}
if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
{
DBG1(DBG_IKE, "unable to create CHILD_SA while deleting IKE_SA");
- message->add_notify(message, TRUE, NO_ADDITIONAL_SAS, chunk_empty);
+ message->add_notify(message, TRUE, TEMPORARY_FAILURE, chunk_empty);
return SUCCESS;
}
@@ -1310,7 +1331,7 @@ METHOD(task_t, build_r, status_t,
return SUCCESS;
case INVALID_ARG:
{
- u_int16_t group = htons(this->dh_group);
+ uint16_t group = htons(this->dh_group);
message->add_notify(message, FALSE, INVALID_KE_PAYLOAD,
chunk_from_thing(group));
handle_child_sa_failure(this, message);
@@ -1441,10 +1462,21 @@ METHOD(task_t, process_i, status_t,
/* an error in CHILD_SA creation is not critical */
return SUCCESS;
}
+ case TEMPORARY_FAILURE:
+ {
+ DBG1(DBG_IKE, "received %N notify, will retry later",
+ notify_type_names, type);
+ enumerator->destroy(enumerator);
+ if (!this->rekey)
+ { /* the rekey task will retry itself if necessary */
+ schedule_delayed_retry(this);
+ }
+ return SUCCESS;
+ }
case INVALID_KE_PAYLOAD:
{
chunk_t data;
- u_int16_t group = MODP_NONE;
+ uint16_t group = MODP_NONE;
data = notify->get_notification_data(notify);
if (data.len == sizeof(group))
@@ -1529,7 +1561,7 @@ METHOD(task_t, process_i, status_t,
}
METHOD(child_create_t, use_reqid, void,
- private_child_create_t *this, u_int32_t reqid)
+ private_child_create_t *this, uint32_t reqid)
{
this->reqid = reqid;
}
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.h b/src/libcharon/sa/ikev2/tasks/child_create.h
index 46d9403ee..f48d7b0a9 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.h
+++ b/src/libcharon/sa/ikev2/tasks/child_create.h
@@ -49,7 +49,7 @@ struct child_create_t {
*
* @param reqid reqid to use
*/
- void (*use_reqid) (child_create_t *this, u_int32_t reqid);
+ void (*use_reqid) (child_create_t *this, uint32_t reqid);
/**
* Use specific mark values to override configuration.
diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c
index 877ae0531..6fa8836ac 100644
--- a/src/libcharon/sa/ikev2/tasks/child_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.c
@@ -1,6 +1,7 @@
/*
+ * Copyright (C) 2009-2016 Tobias Brunner
* Copyright (C) 2006-2007 Martin Willi
- * Hochschule fuer Technik Rapperswil
+ * 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
@@ -18,7 +19,7 @@
#include <daemon.h>
#include <encoding/payloads/delete_payload.h>
#include <sa/ikev2/tasks/child_create.h>
-
+#include <sa/ikev2/tasks/child_rekey.h>
typedef struct private_child_delete_t private_child_delete_t;
@@ -50,7 +51,7 @@ struct private_child_delete_t {
/**
* Inbound SPI of CHILD_SA to delete
*/
- u_int32_t spi;
+ uint32_t spi;
/**
* whether to enforce delete action policy
@@ -86,7 +87,7 @@ static void build_payloads(private_child_delete_t *this, message_t *message)
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
protocol_id_t protocol = child_sa->get_protocol(child_sa);
- u_int32_t spi = child_sa->get_spi(child_sa, TRUE);
+ uint32_t spi = child_sa->get_spi(child_sa, TRUE);
switch (protocol)
{
@@ -119,6 +120,33 @@ static void build_payloads(private_child_delete_t *this, message_t *message)
}
/**
+ * Check if the given CHILD_SA is the redundant SA created in a rekey collision.
+ */
+static bool is_redundant(private_child_delete_t *this, child_sa_t *child)
+{
+ enumerator_t *tasks;
+ task_t *task;
+
+ tasks = this->ike_sa->create_task_enumerator(this->ike_sa,
+ TASK_QUEUE_ACTIVE);
+ while (tasks->enumerate(tasks, &task))
+ {
+ if (task->get_type(task) == TASK_CHILD_REKEY)
+ {
+ child_rekey_t *rekey = (child_rekey_t*)task;
+
+ if (rekey->is_redundant(rekey, child))
+ {
+ tasks->destroy(tasks);
+ return TRUE;
+ }
+ }
+ }
+ tasks->destroy(tasks);
+ return FALSE;
+}
+
+/**
* read in payloads and find the children to delete
*/
static void process_payloads(private_child_delete_t *this, message_t *message)
@@ -126,7 +154,7 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
enumerator_t *payloads, *spis;
payload_t *payload;
delete_payload_t *delete_payload;
- u_int32_t spi;
+ uint32_t spi;
protocol_id_t protocol;
child_sa_t *child_sa;
@@ -157,24 +185,31 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
switch (child_sa->get_state(child_sa))
{
- case CHILD_REKEYING:
+ case CHILD_REKEYED:
this->rekeyed = TRUE;
- /* we reply as usual, rekeying will fail */
break;
case CHILD_DELETING:
/* we don't send back a delete if we initiated ourself */
if (!this->initiator)
{
- this->ike_sa->destroy_child_sa(this->ike_sa,
- protocol, spi);
continue;
}
/* fall through */
+ case CHILD_REKEYING:
+ /* we reply as usual, rekeying will fail */
case CHILD_INSTALLED:
if (!this->initiator)
- { /* reestablish installed children if required */
- this->check_delete_action = TRUE;
+ {
+ if (is_redundant(this, child_sa))
+ {
+ this->rekeyed = TRUE;
+ }
+ else
+ {
+ this->check_delete_action = TRUE;
+ }
}
+ break;
default:
break;
}
@@ -199,14 +234,14 @@ static status_t destroy_and_reestablish(private_child_delete_t *this)
child_sa_t *child_sa;
child_cfg_t *child_cfg;
protocol_id_t protocol;
- u_int32_t spi, reqid;
+ uint32_t spi, reqid;
action_t action;
status_t status = SUCCESS;
enumerator = this->child_sas->create_enumerator(this->child_sas);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
- /* signal child down event if we are not rekeying */
+ /* signal child down event if we weren't rekeying */
if (!this->rekeyed)
{
charon->bus->child_updown(charon->bus, child_sa, FALSE);
@@ -254,7 +289,7 @@ static void log_children(private_child_delete_t *this)
linked_list_t *my_ts, *other_ts;
enumerator_t *enumerator;
child_sa_t *child_sa;
- u_int64_t bytes_in, bytes_out;
+ uint64_t bytes_in, bytes_out;
enumerator = this->child_sas->create_enumerator(this->child_sas);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
@@ -308,7 +343,7 @@ METHOD(task_t, build_i, status_t,
this->spi = child_sa->get_spi(child_sa, TRUE);
}
this->child_sas->insert_last(this->child_sas, child_sa);
- if (child_sa->get_state(child_sa) == CHILD_REKEYING)
+ if (child_sa->get_state(child_sa) == CHILD_REKEYED)
{
this->rekeyed = TRUE;
}
@@ -347,11 +382,7 @@ METHOD(task_t, process_r, status_t,
METHOD(task_t, build_r, status_t,
private_child_delete_t *this, message_t *message)
{
- /* if we are rekeying, we send an empty informational */
- if (this->ike_sa->get_state(this->ike_sa) != IKE_REKEYING)
- {
- build_payloads(this, message);
- }
+ build_payloads(this, message);
DBG1(DBG_IKE, "CHILD_SA closed");
return destroy_and_reestablish(this);
}
@@ -391,7 +422,7 @@ METHOD(task_t, destroy, void,
* Described in header.
*/
child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
- u_int32_t spi, bool expired)
+ uint32_t spi, bool expired)
{
private_child_delete_t *this;
diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.h b/src/libcharon/sa/ikev2/tasks/child_delete.h
index 1ada0699e..1e9b2d2f7 100644
--- a/src/libcharon/sa/ikev2/tasks/child_delete.h
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.h
@@ -56,6 +56,6 @@ struct child_delete_t {
* @return child_delete task to handle by the task_manager
*/
child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol,
- u_int32_t spi, bool expired);
+ uint32_t spi, bool expired);
#endif /** CHILD_DELETE_H_ @}*/
diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c
index 6f0c2b2c7..c04ec141f 100644
--- a/src/libcharon/sa/ikev2/tasks/child_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c
@@ -1,7 +1,8 @@
/*
+ * Copyright (C) 2009-2016 Tobias Brunner
* Copyright (C) 2005-2007 Martin Willi
* Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
+ * 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
@@ -54,7 +55,7 @@ struct private_child_rekey_t {
/**
* Inbound SPI of CHILD_SA to rekey
*/
- u_int32_t spi;
+ uint32_t spi;
/**
* the CHILD_CREATE task which is reused to simplify rekeying
@@ -91,7 +92,7 @@ struct private_child_rekey_t {
*/
static void schedule_delayed_rekey(private_child_rekey_t *this)
{
- u_int32_t retry;
+ uint32_t retry;
job_t *job;
retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
@@ -130,7 +131,7 @@ static void find_child(private_child_rekey_t *this, message_t *message)
{
notify_payload_t *notify;
protocol_id_t protocol;
- u_int32_t spi;
+ uint32_t spi;
notify = message->get_notify(message, REKEY_SA);
if (notify)
@@ -150,7 +151,7 @@ METHOD(task_t, build_i, status_t,
private_child_rekey_t *this, message_t *message)
{
notify_payload_t *notify;
- u_int32_t reqid;
+ uint32_t reqid;
child_cfg_t *config;
this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol,
@@ -159,14 +160,21 @@ METHOD(task_t, build_i, status_t,
{ /* check if it is an outbound CHILD_SA */
this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol,
this->spi, FALSE);
- if (!this->child_sa)
- { /* CHILD_SA is gone, unable to rekey. As an empty CREATE_CHILD_SA
- * exchange is invalid, we fall back to an INFORMATIONAL exchange.*/
- message->set_exchange_type(message, INFORMATIONAL);
- return SUCCESS;
+ if (this->child_sa)
+ {
+ /* we work only with the inbound SPI */
+ this->spi = this->child_sa->get_spi(this->child_sa, TRUE);
}
- /* we work only with the inbound SPI */
- this->spi = this->child_sa->get_spi(this->child_sa, TRUE);
+ }
+ if (!this->child_sa ||
+ (!this->child_create &&
+ this->child_sa->get_state(this->child_sa) != CHILD_INSTALLED) ||
+ (this->child_create &&
+ this->child_sa->get_state(this->child_sa) != CHILD_REKEYING))
+ {
+ /* CHILD_SA is gone or in the wrong state, unable to rekey */
+ message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
+ return SUCCESS;
}
config = this->child_sa->get_config(this->child_sa);
@@ -217,13 +225,19 @@ METHOD(task_t, build_r, status_t,
private_child_rekey_t *this, message_t *message)
{
child_cfg_t *config;
- u_int32_t reqid;
+ uint32_t reqid;
+ child_sa_state_t state;
- if (this->child_sa == NULL ||
- this->child_sa->get_state(this->child_sa) == CHILD_DELETING)
+ if (!this->child_sa)
{
DBG1(DBG_IKE, "unable to rekey, CHILD_SA not found");
- message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ message->add_notify(message, TRUE, CHILD_SA_NOT_FOUND, chunk_empty);
+ return SUCCESS;
+ }
+ if (this->child_sa->get_state(this->child_sa) == CHILD_DELETING)
+ {
+ DBG1(DBG_IKE, "unable to rekey, we are deleting the CHILD_SA");
+ message->add_notify(message, TRUE, TEMPORARY_FAILURE, chunk_empty);
return SUCCESS;
}
@@ -237,14 +251,16 @@ METHOD(task_t, build_r, status_t,
this->child_create->set_config(this->child_create, config->get_ref(config));
this->child_create->task.build(&this->child_create->task, message);
+ state = this->child_sa->get_state(this->child_sa);
+ this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
+
if (message->get_payload(message, PLV2_SECURITY_ASSOCIATION) == NULL)
- {
- /* rekeying failed, reuse old child */
- this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+ { /* rekeying failed, reuse old child */
+ this->child_sa->set_state(this->child_sa, state);
return SUCCESS;
}
- this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
+ this->child_sa->set_state(this->child_sa, CHILD_REKEYED);
/* invoke rekey hook */
charon->bus->child_rekey(charon->bus, this->child_sa,
@@ -284,9 +300,9 @@ static child_sa_t *handle_collision(private_child_rekey_t *this)
if (child_sa)
{
child_sa->set_close_action(child_sa, ACTION_NONE);
- if (child_sa->get_state(child_sa) != CHILD_REKEYING)
+ if (child_sa->get_state(child_sa) != CHILD_REKEYED)
{
- child_sa->set_state(child_sa, CHILD_REKEYING);
+ child_sa->set_state(child_sa, CHILD_REKEYED);
}
}
}
@@ -324,7 +340,7 @@ METHOD(task_t, process_i, status_t,
private_child_rekey_t *this, message_t *message)
{
protocol_id_t protocol;
- u_int32_t spi;
+ uint32_t spi;
child_sa_t *to_delete;
if (message->get_notify(message, NO_ADDITIONAL_SAS))
@@ -337,6 +353,34 @@ METHOD(task_t, process_i, status_t,
this->ike_sa->get_id(this->ike_sa), TRUE));
return SUCCESS;
}
+ if (message->get_notify(message, CHILD_SA_NOT_FOUND))
+ {
+ child_cfg_t *child_cfg;
+ uint32_t reqid;
+
+ if (this->collision &&
+ this->collision->get_type(this->collision) == TASK_CHILD_DELETE)
+ { /* ignore this error if we already deleted the CHILD_SA on the
+ * peer's behalf (could happen if the other peer does not detect
+ * the collision and did not respond with TEMPORARY_FAILURE) */
+ return SUCCESS;
+ }
+ DBG1(DBG_IKE, "peer didn't find the CHILD_SA we tried to rekey");
+ /* FIXME: according to RFC 7296 we should only create a new CHILD_SA if
+ * it does not exist yet, we currently have no good way of checking for
+ * that (we could go by name, but that might be tricky e.g. due to
+ * narrowing) */
+ spi = this->child_sa->get_spi(this->child_sa, TRUE);
+ reqid = this->child_sa->get_reqid(this->child_sa);
+ protocol = this->child_sa->get_protocol(this->child_sa);
+ child_cfg = this->child_sa->get_config(this->child_sa);
+ child_cfg->get_ref(child_cfg);
+ charon->bus->child_updown(charon->bus, this->child_sa, FALSE);
+ this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
+ return this->ike_sa->initiate(this->ike_sa,
+ child_cfg->get_ref(child_cfg), reqid,
+ NULL, NULL);
+ }
if (this->child_create->task.process(&this->child_create->task,
message) == NEED_MORE)
@@ -346,10 +390,10 @@ METHOD(task_t, process_i, status_t,
}
if (message->get_payload(message, PLV2_SECURITY_ASSOCIATION) == NULL)
{
- /* establishing new child failed, reuse old. but not when we
- * received a delete in the meantime */
- if (!(this->collision &&
- this->collision->get_type(this->collision) == TASK_CHILD_DELETE))
+ /* establishing new child failed, reuse old and try again. but not when
+ * we received a delete in the meantime */
+ if (!this->collision ||
+ this->collision->get_type(this->collision) != TASK_CHILD_DELETE)
{
schedule_delayed_rekey(this);
}
@@ -377,9 +421,9 @@ METHOD(task_t, process_i, status_t,
return SUCCESS;
}
/* disable updown event for redundant CHILD_SA */
- if (to_delete->get_state(to_delete) != CHILD_REKEYING)
+ if (to_delete->get_state(to_delete) != CHILD_REKEYED)
{
- to_delete->set_state(to_delete, CHILD_REKEYING);
+ to_delete->set_state(to_delete, CHILD_REKEYED);
}
spi = to_delete->get_spi(to_delete, TRUE);
protocol = to_delete->get_protocol(to_delete);
@@ -398,6 +442,18 @@ METHOD(task_t, get_type, task_type_t,
return TASK_CHILD_REKEY;
}
+METHOD(child_rekey_t, is_redundant, bool,
+ private_child_rekey_t *this, child_sa_t *child)
+{
+ if (this->collision &&
+ this->collision->get_type(this->collision) == TASK_CHILD_REKEY)
+ {
+ private_child_rekey_t *rekey = (private_child_rekey_t*)this->collision;
+ return child == rekey->child_create->get_child(rekey->child_create);
+ }
+ return FALSE;
+}
+
METHOD(child_rekey_t, collide, void,
private_child_rekey_t *this, task_t *other)
{
@@ -406,9 +462,18 @@ METHOD(child_rekey_t, collide, void,
if (other->get_type(other) == TASK_CHILD_REKEY)
{
private_child_rekey_t *rekey = (private_child_rekey_t*)other;
+ child_sa_t *other_child;
+
if (rekey->child_sa != this->child_sa)
+ { /* not the same child => no collision */
+ other->destroy(other);
+ return;
+ }
+ /* ignore passive tasks that did not successfully create a CHILD_SA */
+ other_child = rekey->child_create->get_child(rekey->child_create);
+ if (!other_child ||
+ other_child->get_state(other_child) != CHILD_INSTALLED)
{
- /* not the same child => no collision */
other->destroy(other);
return;
}
@@ -416,19 +481,11 @@ METHOD(child_rekey_t, collide, void,
else if (other->get_type(other) == TASK_CHILD_DELETE)
{
child_delete_t *del = (child_delete_t*)other;
- if (this->collision &&
- this->collision->get_type(this->collision) == TASK_CHILD_REKEY)
+ if (is_redundant(this, del->get_child(del)))
{
- private_child_rekey_t *rekey;
-
- rekey = (private_child_rekey_t*)this->collision;
- if (del->get_child(del) == rekey->child_create->get_child(rekey->child_create))
- {
- /* peer deletes redundant child created in collision */
- this->other_child_destroyed = TRUE;
- other->destroy(other);
- return;
- }
+ this->other_child_destroyed = TRUE;
+ other->destroy(other);
+ return;
}
if (del->get_child(del) != this->child_sa)
{
@@ -439,7 +496,7 @@ METHOD(child_rekey_t, collide, void,
}
else
{
- /* any other task is not critical for collisisions, ignore */
+ /* any other task is not critical for collisions, ignore */
other->destroy(other);
return;
}
@@ -485,7 +542,7 @@ METHOD(task_t, destroy, void,
* Described in header.
*/
child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol,
- u_int32_t spi)
+ uint32_t spi)
{
private_child_rekey_t *this;
@@ -496,6 +553,7 @@ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol,
.migrate = _migrate,
.destroy = _destroy,
},
+ .is_redundant = _is_redundant,
.collide = _collide,
},
.ike_sa = ike_sa,
diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.h b/src/libcharon/sa/ikev2/tasks/child_rekey.h
index 23384653d..0ad1a062d 100644
--- a/src/libcharon/sa/ikev2/tasks/child_rekey.h
+++ b/src/libcharon/sa/ikev2/tasks/child_rekey.h
@@ -1,6 +1,7 @@
/*
+ * Copyright (C) 2016 Tobias Brunner
* Copyright (C) 2007 Martin Willi
- * Hochschule fuer Technik Rapperswil
+ * 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
@@ -39,13 +40,25 @@ struct child_rekey_t {
task_t task;
/**
- * Register a rekeying task which collides with this one
+ * Check if the given SA is the redundant CHILD_SA created during a rekey
+ * collision.
+ *
+ * This is called if the other peer deletes the redundant SA before we were
+ * able to handle the CREATE_CHILD_SA response.
+ *
+ * @param child CHILD_SA to check
+ * @return TRUE if the SA is the redundant CHILD_SA
+ */
+ bool (*is_redundant)(child_rekey_t *this, child_sa_t *child);
+
+ /**
+ * Register a rekeying/delete task which collides with this one
*
* If two peers initiate rekeying at the same time, the collision must
* be handled gracefully. The task manager is aware of what exchanges
- * are going on and notifies the outgoing task by passing the incoming.
+ * are going on and notifies the active task by passing the passive.
*
- * @param other incoming task
+ * @param other passive task (adopted)
*/
void (*collide)(child_rekey_t* this, task_t *other);
};
@@ -59,6 +72,6 @@ struct child_rekey_t {
* @return child_rekey task to handle by the task_manager
*/
child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol,
- u_int32_t spi);
+ uint32_t spi);
#endif /** CHILD_REKEY_H_ @}*/
diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c
index 79a436fbf..036910d0e 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_auth.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c
@@ -186,7 +186,7 @@ static status_t collect_other_init_data(private_ike_auth_t *this,
*/
static void get_reserved_id_bytes(private_ike_auth_t *this, id_payload_t *id)
{
- u_int8_t *byte;
+ uint8_t *byte;
int i;
for (i = 0; i < countof(this->reserved); i++)
@@ -564,6 +564,10 @@ METHOD(task_t, process_r, status_t,
this->ike_sa->enable_extension(this->ike_sa,
EXT_EAP_ONLY_AUTHENTICATION);
}
+ if (message->get_notify(message, INITIAL_CONTACT))
+ {
+ this->initial_contact = TRUE;
+ }
}
if (this->other_auth == NULL)
@@ -652,14 +656,6 @@ METHOD(task_t, process_r, status_t,
return NEED_MORE;
}
- /* If authenticated (with non-EAP) and received INITIAL_CONTACT,
- * delete any existing IKE_SAs with that peer. */
- if (message->get_message_id(message) == 1 &&
- message->get_notify(message, INITIAL_CONTACT))
- {
- this->initial_contact = TRUE;
- }
-
/* another auth round done, invoke authorize hook */
if (!charon->bus->authorize(charon->bus, FALSE))
{
@@ -749,13 +745,6 @@ METHOD(task_t, build_r, status_t,
get_reserved_id_bytes(this, id_payload);
message->add_payload(message, (payload_t*)id_payload);
- if (this->initial_contact)
- {
- charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
- this->ike_sa, TRUE);
- this->initial_contact = FALSE;
- }
-
if ((uintptr_t)cfg->get(cfg, AUTH_RULE_AUTH_CLASS) == AUTH_CLASS_EAP)
{ /* EAP-only authentication */
if (!this->ike_sa->supports_extension(this->ike_sa,
@@ -830,7 +819,7 @@ METHOD(task_t, build_r, status_t,
}
if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
- this->ike_sa, FALSE))
+ this->ike_sa, this->initial_contact))
{
DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy");
charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP);
diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c b/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c
index a7d162e68..47b0a3ed1 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c
@@ -45,14 +45,14 @@ struct private_ike_auth_lifetime_t {
static void add_auth_lifetime(private_ike_auth_lifetime_t *this, message_t *message)
{
chunk_t chunk;
- u_int32_t lifetime;
+ uint32_t lifetime;
lifetime = this->ike_sa->get_statistic(this->ike_sa, STAT_REAUTH);
if (lifetime)
{
lifetime -= time_monotonic(NULL);
chunk = chunk_from_thing(lifetime);
- *(u_int32_t*)chunk.ptr = htonl(lifetime);
+ *(uint32_t*)chunk.ptr = htonl(lifetime);
message->add_notify(message, FALSE, AUTH_LIFETIME, chunk);
}
}
@@ -64,13 +64,13 @@ static void process_payloads(private_ike_auth_lifetime_t *this, message_t *messa
{
notify_payload_t *notify;
chunk_t data;
- u_int32_t lifetime;
+ uint32_t lifetime;
notify = message->get_notify(message, AUTH_LIFETIME);
if (notify)
{
data = notify->get_notification_data(notify);
- lifetime = ntohl(*(u_int32_t*)data.ptr);
+ lifetime = ntohl(*(uint32_t*)data.ptr);
this->ike_sa->set_auth_lifetime(this->ike_sa, lifetime);
}
}
diff --git a/src/libcharon/sa/ikev2/tasks/ike_delete.c b/src/libcharon/sa/ikev2/tasks/ike_delete.c
index e972dba07..fd36b144a 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_delete.c
@@ -1,6 +1,7 @@
/*
+ * Copyright (C) 2016 Tobias Brunner
* Copyright (C) 2006-2007 Martin Willi
- * Hochschule fuer Technik Rapperswil
+ * 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
@@ -17,7 +18,7 @@
#include <daemon.h>
#include <encoding/payloads/delete_payload.h>
-
+#include <sa/ikev2/tasks/ike_rekey.h>
typedef struct private_ike_delete_t private_ike_delete_t;
@@ -45,11 +46,6 @@ struct private_ike_delete_t {
* are we deleting a rekeyed SA?
*/
bool rekeyed;
-
- /**
- * are we responding to a delete, but have initated our own?
- */
- bool simultaneous;
};
METHOD(task_t, build_i, status_t,
@@ -68,7 +64,8 @@ METHOD(task_t, build_i, status_t,
delete_payload = delete_payload_create(PLV2_DELETE, PROTO_IKE);
message->add_payload(message, (payload_t*)delete_payload);
- if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING)
+ if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING ||
+ this->ike_sa->get_state(this->ike_sa) == IKE_REKEYED)
{
this->rekeyed = TRUE;
}
@@ -93,6 +90,33 @@ METHOD(task_t, process_i, status_t,
return DESTROY_ME;
}
+/**
+ * Check if this delete happened after a rekey collsion
+ */
+static bool after_rekey_collision(private_ike_delete_t *this)
+{
+ enumerator_t *tasks;
+ task_t *task;
+
+ tasks = this->ike_sa->create_task_enumerator(this->ike_sa,
+ TASK_QUEUE_ACTIVE);
+ while (tasks->enumerate(tasks, &task))
+ {
+ if (task->get_type(task) == TASK_IKE_REKEY)
+ {
+ ike_rekey_t *rekey = (ike_rekey_t*)task;
+
+ if (rekey->did_collide(rekey))
+ {
+ tasks->destroy(tasks);
+ return TRUE;
+ }
+ }
+ }
+ tasks->destroy(tasks);
+ return FALSE;
+}
+
METHOD(task_t, process_r, status_t,
private_ike_delete_t *this, message_t *message)
{
@@ -119,16 +143,24 @@ METHOD(task_t, process_r, status_t,
switch (this->ike_sa->get_state(this->ike_sa))
{
+ case IKE_REKEYING:
+ /* if the peer concurrently deleted the IKE_SA we treat this as
+ * regular delete. however, in case the peer did not detect a rekey
+ * collision it will delete the replaced IKE_SA if we are still in
+ * state IKE_REKEYING */
+ if (after_rekey_collision(this))
+ {
+ this->rekeyed = TRUE;
+ break;
+ }
+ /* fall-through */
case IKE_ESTABLISHED:
this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
this->ike_sa->reestablish(this->ike_sa);
return NEED_MORE;
- case IKE_REKEYING:
+ case IKE_REKEYED:
this->rekeyed = TRUE;
break;
- case IKE_DELETING:
- this->simultaneous = TRUE;
- break;
default:
break;
}
@@ -141,11 +173,6 @@ METHOD(task_t, build_r, status_t,
{
DBG0(DBG_IKE, "IKE_SA deleted");
- if (this->simultaneous)
- {
- /* wait for peer's response for our delete request */
- return SUCCESS;
- }
if (!this->rekeyed)
{ /* invoke ike_down() hook if SA has not been rekeyed */
charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
@@ -164,7 +191,6 @@ METHOD(task_t, migrate, void,
private_ike_delete_t *this, ike_sa_t *ike_sa)
{
this->ike_sa = ike_sa;
- this->simultaneous = FALSE;
}
METHOD(task_t, destroy, void,
diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c
index 78579be95..801b6d8f3 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_init.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_init.c
@@ -221,7 +221,7 @@ static void handle_supported_hash_algorithms(private_ike_init_t *this,
notify_payload_t *notify)
{
bio_reader_t *reader;
- u_int16_t algo;
+ uint16_t algo;
bool added = FALSE;
reader = bio_reader_create(notify->get_notification_data(notify));
@@ -373,13 +373,15 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
{
sa_payload_t *sa_payload = (sa_payload_t*)payload;
linked_list_t *proposal_list;
- bool private;
+ bool private, prefer_configured;
proposal_list = sa_payload->get_proposals(sa_payload);
private = this->ike_sa->supports_extension(this->ike_sa,
EXT_STRONGSWAN);
+ prefer_configured = lib->settings->get_bool(lib->settings,
+ "%s.prefer_configured_proposals", TRUE, lib->ns);
this->proposal = this->config->select_proposal(this->config,
- proposal_list, private);
+ proposal_list, private, prefer_configured);
if (!this->proposal)
{
charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE,
@@ -633,7 +635,7 @@ METHOD(task_t, build_r, status_t,
if (this->dh == NULL ||
!this->proposal->has_dh_group(this->proposal, this->dh_group))
{
- u_int16_t group;
+ uint16_t group;
if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP,
&group, NULL))
@@ -765,7 +767,7 @@ METHOD(task_t, process_i, status_t,
bad_group = this->dh_group;
data = notify->get_notification_data(notify);
- this->dh_group = ntohs(*((u_int16_t*)data.ptr));
+ this->dh_group = ntohs(*((uint16_t*)data.ptr));
DBG1(DBG_IKE, "peer didn't accept DH group %N, "
"it requested %N", diffie_hellman_group_names,
bad_group, diffie_hellman_group_names, this->dh_group);
diff --git a/src/libcharon/sa/ikev2/tasks/ike_me.c b/src/libcharon/sa/ikev2/tasks/ike_me.c
index 10d412ffd..f077ccfb5 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_me.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_me.c
@@ -128,7 +128,7 @@ static void gather_and_add_endpoints(private_ike_me_t *this, message_t *message)
{
enumerator_t *enumerator;
host_t *addr, *host;
- u_int16_t port;
+ uint16_t port;
/* get the port that is used to communicate with the ms */
host = this->ike_sa->get_my_host(this->ike_sa);
diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
index 3f7bb175f..dc0f24fb8 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
@@ -299,7 +299,7 @@ static void update_children(private_ike_mobike_t *this)
/**
* Apply the port of the old host, if its ip equals the new, use port otherwise.
*/
-static void apply_port(host_t *host, host_t *old, u_int16_t port, bool local)
+static void apply_port(host_t *host, host_t *old, uint16_t port, bool local)
{
if (host->ip_equals(host, old))
{
diff --git a/src/libcharon/sa/ikev2/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c
index 4bf5264dd..f3f32d7af 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_natd.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c
@@ -99,8 +99,8 @@ static chunk_t generate_natd_hash(private_ike_natd_t *this,
{
chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk;
chunk_t natd_hash;
- u_int64_t spi_i, spi_r;
- u_int16_t port;
+ uint64_t spi_i, spi_r;
+ uint16_t port;
/* prepare all required chunks */
spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
@@ -142,7 +142,7 @@ static notify_payload_t *build_natd_payload(private_ike_natd_t *this,
config = this->ike_sa->get_ike_cfg(this->ike_sa);
if (force_encap(config) && type == NAT_DETECTION_SOURCE_IP)
{
- u_int32_t addr;
+ uint32_t addr;
/* chunk_hash() is randomly keyed so this produces a random IPv4 address
* that changes with every restart but otherwise stays the same */
diff --git a/src/libcharon/sa/ikev2/tasks/ike_rekey.c b/src/libcharon/sa/ikev2/tasks/ike_rekey.c
index eaba04e3a..2f0552a33 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.c
@@ -1,7 +1,8 @@
/*
+ * Copyright (C) 2015-2016 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
+ * 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
@@ -66,9 +67,30 @@ struct private_ike_rekey_t {
* colliding task detected by the task manager
*/
task_t *collision;
+
+ /**
+ * TRUE if rekeying can't be handled temporarily
+ */
+ bool failed_temporarily;
};
/**
+ * Schedule a retry if rekeying temporary failed
+ */
+static void schedule_delayed_rekey(private_ike_rekey_t *this)
+{
+ uint32_t retry;
+ job_t *job;
+
+ retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
+ job = (job_t*)rekey_ike_sa_job_create(
+ this->ike_sa->get_id(this->ike_sa), FALSE);
+ DBG1(DBG_IKE, "IKE_SA rekeying failed, trying again in %d seconds", retry);
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ lib->scheduler->schedule_job(lib->scheduler, job, retry);
+}
+
+/**
* Check if an IKE_SA has any queued tasks, return initiation job
*/
static job_t* check_queued_tasks(ike_sa_t *ike_sa)
@@ -83,7 +105,6 @@ static job_t* check_queued_tasks(ike_sa_t *ike_sa)
job = (job_t*)initiate_tasks_job_create(ike_sa->get_id(ike_sa));
}
enumerator->destroy(enumerator);
-
return job;
}
@@ -117,20 +138,9 @@ static void establish_new(private_ike_rekey_t *this)
}
this->new_sa = NULL;
charon->bus->set_sa(charon->bus, this->ike_sa);
- }
-}
-METHOD(task_t, process_r_delete, status_t,
- private_ike_rekey_t *this, message_t *message)
-{
- establish_new(this);
- return this->ike_delete->task.process(&this->ike_delete->task, message);
-}
-
-METHOD(task_t, build_r_delete, status_t,
- private_ike_rekey_t *this, message_t *message)
-{
- return this->ike_delete->task.build(&this->ike_delete->task, message);
+ this->ike_sa->set_state(this->ike_sa, IKE_REKEYED);
+ }
}
METHOD(task_t, build_i_delete, status_t,
@@ -172,36 +182,59 @@ METHOD(task_t, build_i, status_t,
return NEED_MORE;
}
-METHOD(task_t, process_r, status_t,
- private_ike_rekey_t *this, message_t *message)
+/**
+ * Check if there are any half-open children
+ */
+static bool have_half_open_children(private_ike_rekey_t *this)
{
enumerator_t *enumerator;
child_sa_t *child_sa;
-
- if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
- {
- DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting");
- return NEED_MORE;
- }
+ task_t *task;
enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
switch (child_sa->get_state(child_sa))
{
- case CHILD_CREATED:
case CHILD_REKEYING:
case CHILD_RETRYING:
case CHILD_DELETING:
- /* we do not allow rekeying while we have children in-progress */
- DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open");
enumerator->destroy(enumerator);
- return NEED_MORE;
+ return TRUE;
default:
break;
}
}
enumerator->destroy(enumerator);
+ enumerator = this->ike_sa->create_task_enumerator(this->ike_sa,
+ TASK_QUEUE_ACTIVE);
+ while (enumerator->enumerate(enumerator, (void**)&task))
+ {
+ if (task->get_type(task) == TASK_CHILD_CREATE)
+ {
+ enumerator->destroy(enumerator);
+ return TRUE;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return FALSE;
+}
+
+METHOD(task_t, process_r, status_t,
+ private_ike_rekey_t *this, message_t *message)
+{
+ if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
+ {
+ DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting");
+ this->failed_temporarily = TRUE;
+ return NEED_MORE;
+ }
+ if (have_half_open_children(this))
+ {
+ DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open");
+ this->failed_temporarily = TRUE;
+ return NEED_MORE;
+ }
this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
this->ike_sa->get_version(this->ike_sa), FALSE);
@@ -219,33 +252,57 @@ METHOD(task_t, process_r, status_t,
METHOD(task_t, build_r, status_t,
private_ike_rekey_t *this, message_t *message)
{
+ if (this->failed_temporarily)
+ {
+ message->add_notify(message, TRUE, TEMPORARY_FAILURE, chunk_empty);
+ return SUCCESS;
+ }
if (this->new_sa == NULL)
{
/* IKE_SA/a CHILD_SA is in an inacceptable state, deny rekeying */
message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
return SUCCESS;
}
-
if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED)
{
+ this->ike_init->task.destroy(&this->ike_init->task);
+ this->ike_init = NULL;
charon->bus->set_sa(charon->bus, this->ike_sa);
return SUCCESS;
}
charon->bus->set_sa(charon->bus, this->ike_sa);
- this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
- /* rekeying successful, delete the IKE_SA using a subtask */
- this->ike_delete = ike_delete_create(this->ike_sa, FALSE);
- this->public.task.build = _build_r_delete;
- this->public.task.process = _process_r_delete;
-
- /* the peer does have to delete the IKE_SA. If it does not, we get a
- * unusable IKE_SA in REKEYING state without a replacement. We consider
- * this a timeout condition by the peer, and trigger a delete actively. */
- lib->scheduler->schedule_job(lib->scheduler, (job_t*)
- delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE), 90);
+ if (this->ike_sa->get_state(this->ike_sa) != IKE_REKEYING)
+ { /* in case of a collision we let the initiating task handle this */
+ establish_new(this);
+ /* make sure the IKE_SA is gone in case the peer fails to delete it */
+ lib->scheduler->schedule_job(lib->scheduler, (job_t*)
+ delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE),
+ 90);
+ }
+ return SUCCESS;
+}
- return NEED_MORE;
+/**
+ * Conclude any undetected rekey collision.
+ *
+ * If the peer does not detect the collision it will delete this IKE_SA.
+ * Depending on when our request reaches the peer and we receive the delete
+ * this may get called at different times.
+ *
+ * Returns TRUE if there was a collision, FALSE otherwise.
+ */
+static bool conclude_undetected_collision(private_ike_rekey_t *this)
+{
+ if (this->collision &&
+ this->collision->get_type(this->collision) == TASK_IKE_REKEY)
+ {
+ DBG1(DBG_IKE, "peer did not notice IKE_SA rekey collision, abort "
+ "active rekeying");
+ establish_new((private_ike_rekey_t*)this->collision);
+ return TRUE;
+ }
+ return FALSE;
}
METHOD(task_t, process_i, status_t,
@@ -266,18 +323,9 @@ METHOD(task_t, process_i, status_t,
{
case FAILED:
/* rekeying failed, fallback to old SA */
- if (!(this->collision && (
- this->collision->get_type(this->collision) == TASK_IKE_DELETE ||
- this->collision->get_type(this->collision) == TASK_IKE_REAUTH)))
+ if (!conclude_undetected_collision(this))
{
- job_t *job;
- u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
- job = (job_t*)rekey_ike_sa_job_create(
- this->ike_sa->get_id(this->ike_sa), FALSE);
- DBG1(DBG_IKE, "IKE_SA rekeying failed, "
- "trying again in %d seconds", retry);
- this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
- lib->scheduler->schedule_job(lib->scheduler, job, retry);
+ schedule_delayed_rekey(this);
}
return SUCCESS;
case NEED_MORE:
@@ -293,55 +341,53 @@ METHOD(task_t, process_i, status_t,
this->collision->get_type(this->collision) == TASK_IKE_REKEY)
{
private_ike_rekey_t *other = (private_ike_rekey_t*)this->collision;
+ host_t *host;
+ chunk_t this_nonce, other_nonce;
- /* ike_init can be NULL, if child_sa is half-open */
- if (other->ike_init)
- {
- host_t *host;
- chunk_t this_nonce, other_nonce;
-
- this_nonce = this->ike_init->get_lower_nonce(this->ike_init);
- other_nonce = other->ike_init->get_lower_nonce(other->ike_init);
+ this_nonce = this->ike_init->get_lower_nonce(this->ike_init);
+ other_nonce = other->ike_init->get_lower_nonce(other->ike_init);
- /* if we have the lower nonce, delete rekeyed SA. If not, delete
- * the redundant. */
- if (memcmp(this_nonce.ptr, other_nonce.ptr,
- min(this_nonce.len, other_nonce.len)) > 0)
+ /* if we have the lower nonce, delete rekeyed SA. If not, delete
+ * the redundant. */
+ if (memcmp(this_nonce.ptr, other_nonce.ptr,
+ min(this_nonce.len, other_nonce.len)) < 0)
+ {
+ DBG1(DBG_IKE, "IKE_SA rekey collision lost, deleting redundant "
+ "IKE_SA %s[%d]", this->new_sa->get_name(this->new_sa),
+ this->new_sa->get_unique_id(this->new_sa));
+ /* apply host for a proper delete */
+ host = this->ike_sa->get_my_host(this->ike_sa);
+ this->new_sa->set_my_host(this->new_sa, host->clone(host));
+ host = this->ike_sa->get_other_host(this->ike_sa);
+ this->new_sa->set_other_host(this->new_sa, host->clone(host));
+ /* IKE_SAs in state IKE_REKEYED are silently deleted, so we use
+ * IKE_REKEYING */
+ this->new_sa->set_state(this->new_sa, IKE_REKEYING);
+ if (this->new_sa->delete(this->new_sa) == DESTROY_ME)
{
- /* peer should delete this SA. Add a timeout just in case. */
- job_t *job = (job_t*)delete_ike_sa_job_create(
- other->new_sa->get_id(other->new_sa), TRUE);
- lib->scheduler->schedule_job(lib->scheduler, job, 10);
- DBG1(DBG_IKE, "IKE_SA rekey collision won, waiting for delete");
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, other->new_sa);
- other->new_sa = NULL;
+ this->new_sa->destroy(this->new_sa);
}
else
{
- DBG1(DBG_IKE, "IKE_SA rekey collision lost, "
- "deleting redundant IKE_SA");
- /* apply host for a proper delete */
- host = this->ike_sa->get_my_host(this->ike_sa);
- this->new_sa->set_my_host(this->new_sa, host->clone(host));
- host = this->ike_sa->get_other_host(this->ike_sa);
- this->new_sa->set_other_host(this->new_sa, host->clone(host));
- this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
- this->new_sa->set_state(this->new_sa, IKE_REKEYING);
- if (this->new_sa->delete(this->new_sa) == DESTROY_ME)
- {
- this->new_sa->destroy(this->new_sa);
- }
- else
- {
- charon->ike_sa_manager->checkin(
- charon->ike_sa_manager, this->new_sa);
- }
- charon->bus->set_sa(charon->bus, this->ike_sa);
- this->new_sa = NULL;
- establish_new(other);
- return SUCCESS;
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager,
+ this->new_sa);
}
+ charon->bus->set_sa(charon->bus, this->ike_sa);
+ this->new_sa = NULL;
+ establish_new(other);
+ return SUCCESS;
}
+ /* peer should delete this SA. Add a timeout just in case. */
+ job_t *job = (job_t*)delete_ike_sa_job_create(
+ other->new_sa->get_id(other->new_sa), TRUE);
+ lib->scheduler->schedule_job(lib->scheduler, job,
+ HALF_OPEN_IKE_SA_TIMEOUT);
+ DBG1(DBG_IKE, "IKE_SA rekey collision won, waiting for delete for "
+ "redundant IKE_SA %s[%d]", other->new_sa->get_name(other->new_sa),
+ other->new_sa->get_unique_id(other->new_sa));
+ other->new_sa->set_state(other->new_sa, IKE_REKEYED);
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, other->new_sa);
+ other->new_sa = NULL;
charon->bus->set_sa(charon->bus, this->ike_sa);
}
@@ -361,11 +407,41 @@ METHOD(task_t, get_type, task_type_t,
return TASK_IKE_REKEY;
}
+METHOD(ike_rekey_t, did_collide, bool,
+ private_ike_rekey_t *this)
+{
+ return this->collision &&
+ this->collision->get_type(this->collision) == TASK_IKE_REKEY;
+}
+
METHOD(ike_rekey_t, collide, void,
private_ike_rekey_t* this, task_t *other)
{
DBG1(DBG_IKE, "detected %N collision with %N", task_type_names,
TASK_IKE_REKEY, task_type_names, other->get_type(other));
+
+ switch (other->get_type(other))
+ {
+ case TASK_IKE_DELETE:
+ conclude_undetected_collision(this);
+ other->destroy(other);
+ return;
+ case TASK_IKE_REKEY:
+ {
+ private_ike_rekey_t *rekey = (private_ike_rekey_t*)other;
+
+ if (!rekey->ike_init)
+ {
+ DBG1(DBG_IKE, "colliding exchange did not result in an IKE_SA, "
+ "ignore");
+ other->destroy(other);
+ return;
+ }
+ break;
+ }
+ default:
+ break;
+ }
DESTROY_IF(this->collision);
this->collision = other;
}
@@ -425,6 +501,7 @@ ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator)
.migrate = _migrate,
.destroy = _destroy,
},
+ .did_collide = _did_collide,
.collide = _collide,
},
.ike_sa = ike_sa,
diff --git a/src/libcharon/sa/ikev2/tasks/ike_rekey.h b/src/libcharon/sa/ikev2/tasks/ike_rekey.h
index 6a12e9034..86b512c92 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_rekey.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.h
@@ -1,6 +1,7 @@
/*
+ * Copyright (C) 2016 Tobias Brunner
* Copyright (C) 2007 Martin Willi
- * Hochschule fuer Technik Rapperswil
+ * 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
@@ -38,6 +39,13 @@ struct ike_rekey_t {
task_t task;
/**
+ * Check if there was a rekey collision.
+ *
+ * @return TRUE if there was a rekey collision before
+ */
+ bool (*did_collide)(ike_rekey_t *this);
+
+ /**
* Register a rekeying task which collides with this one.
*
* If two peers initiate rekeying at the same time, the collision must