summaryrefslogtreecommitdiff
path: root/src/libcharon/sa/ikev2/task_manager_v2.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa/ikev2/task_manager_v2.c')
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.c284
1 files changed, 199 insertions, 85 deletions
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,