ikev2: Add possibility to delay initiation of a queued task

Such a task is not initiated unless a certain time has passed.  This
allows delaying certain tasks but avoids problems if we'd do this
via a scheduled job (e.g. if the IKE_SA is rekeyed in the meantime).

If the IKE_SA is rekeyed the delay of such tasks is reset when the
tasks are adopted i.e. they get executed immediately on the new IKE_SA.

This hasn't been implemented for IKEv1 yet.
This commit is contained in:
Tobias Brunner
2016-06-17 18:48:07 +02:00
parent 4e2e7d4fec
commit 208678e6e1
5 changed files with 189 additions and 66 deletions
+9 -2
View File
@@ -1,9 +1,9 @@
/*
* Copyright (C) 2006-2015 Tobias Brunner
* Copyright (C) 2006-2016 Tobias Brunner
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2009 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
@@ -2620,6 +2620,12 @@ METHOD(ike_sa_t, queue_task, void,
this->task_manager->queue_task(this->task_manager, task);
}
METHOD(ike_sa_t, queue_task_delayed, void,
private_ike_sa_t *this, task_t *task, uint32_t delay)
{
this->task_manager->queue_task_delayed(this->task_manager, task, delay);
}
METHOD(ike_sa_t, inherit_pre, void,
private_ike_sa_t *this, ike_sa_t *other_public)
{
@@ -2938,6 +2944,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
.create_task_enumerator = _create_task_enumerator,
.flush_queue = _flush_queue,
.queue_task = _queue_task,
.queue_task_delayed = _queue_task_delayed,
#ifdef ME
.act_as_mediation_server = _act_as_mediation_server,
.get_server_reflexive_host = _get_server_reflexive_host,
+11 -2
View File
@@ -1,9 +1,9 @@
/*
* Copyright (C) 2006-2015 Tobias Brunner
* Copyright (C) 2006-2016 Tobias Brunner
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2009 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
@@ -1123,6 +1123,15 @@ struct ike_sa_t {
*/
void (*queue_task)(ike_sa_t *this, task_t *task);
/**
* Queue a task in the manager, but delay its initiation for at least the
* given number of seconds.
*
* @param task task to queue
* @param delay minimum delay in s before initiating the task
*/
void (*queue_task_delayed)(ike_sa_t *this, task_t *task, uint32_t delay);
/**
* Inherit required attributes to new SA before rekeying.
*
+10 -3
View File
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2007-2016 Tobias Brunner
* Copyright (C) 2007-2011 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
@@ -1513,8 +1513,8 @@ static bool has_queued(private_task_manager_t *this, task_type_t type)
return found;
}
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)
{
task_type_t type = task->get_type(task);
@@ -1535,6 +1535,12 @@ METHOD(task_manager_t, queue_task, void,
this->queued_tasks->insert_last(this->queued_tasks, task);
}
METHOD(task_manager_t, queue_task, void,
private_task_manager_t *this, task_t *task)
{
queue_task_delayed(this, task, 0);
}
METHOD(task_manager_t, queue_ike, void,
private_task_manager_t *this)
{
@@ -1975,6 +1981,7 @@ task_manager_v1_t *task_manager_v1_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,
+146 -57
View File
@@ -1,7 +1,7 @@
/*
* 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
*/
uint32_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
@@ -181,6 +165,22 @@ struct private_task_manager_t {
bool make_before_break;
};
/**
* 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
*/
@@ -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;
}
@@ -1524,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);
@@ -1544,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);
}
/**
@@ -1555,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;
@@ -1639,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);
@@ -1670,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);
@@ -1801,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);
@@ -1840,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,
@@ -1855,7 +1924,9 @@ METHOD(task_manager_t, reset, void,
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);
@@ -1874,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);
@@ -1886,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)
{
@@ -1902,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();
}
@@ -1938,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,
+13 -2
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2013-2016 Tobias Brunner
* Copyright (C) 2006 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
@@ -122,7 +123,17 @@ struct task_manager_t {
*
* @param task task to queue
*/
void (*queue_task) (task_manager_t *this, task_t *task);
void (*queue_task)(task_manager_t *this, task_t *task);
/**
* Queue a task in the manager, but delay its initiation for at least the
* given number of seconds.
*
* @param task task to queue
* @param delay minimum delay in s before initiating the task
*/
void (*queue_task_delayed)(task_manager_t *this, task_t *task,
uint32_t delay);
/**
* Queue IKE_SA establishing tasks.