summaryrefslogtreecommitdiff
path: root/src/libcharon/sa
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@debian.org>2015-10-22 11:43:58 +0200
committerYves-Alexis Perez <corsac@debian.org>2015-10-22 11:43:58 +0200
commit5dca9ea0e2931f0e2a056c7964d311bcc30a01b8 (patch)
tree037f1ec5bb860846938ddcf29771c24e9c529be0 /src/libcharon/sa
parentb238cf34df3fe4476ae6b7012e7cb3e9769d4d51 (diff)
downloadvyos-strongswan-5dca9ea0e2931f0e2a056c7964d311bcc30a01b8.tar.gz
vyos-strongswan-5dca9ea0e2931f0e2a056c7964d311bcc30a01b8.zip
Imported Upstream version 5.3.3
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r--src/libcharon/sa/child_sa.c15
-rw-r--r--src/libcharon/sa/eap/eap_method.c3
-rw-r--r--src/libcharon/sa/ike_sa.c43
-rw-r--r--src/libcharon/sa/ike_sa_id.c4
-rw-r--r--src/libcharon/sa/ike_sa_manager.c67
-rw-r--r--src/libcharon/sa/ike_sa_manager.h9
-rw-r--r--src/libcharon/sa/ikev1/phase1.c2
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c35
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c26
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.h11
-rw-r--r--src/libcharon/sa/ikev2/authenticators/eap_authenticator.c9
-rw-r--r--src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c2
-rw-r--r--src/libcharon/sa/ikev2/keymat_v2.c2
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c36
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_rekey.c18
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_rekey.c34
-rw-r--r--src/libcharon/sa/shunt_manager.c71
-rw-r--r--src/libcharon/sa/shunt_manager.h6
-rw-r--r--src/libcharon/sa/trap_manager.c276
19 files changed, 541 insertions, 128 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index 94cf07c33..73f2ec9d3 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2011 Tobias Brunner
+ * Copyright (C) 2006-2015 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005 Jan Hutter
@@ -106,6 +106,11 @@ struct private_child_sa_t {
*/
bool reqid_allocated;
+ /**
+ * Is the reqid statically configured
+ */
+ bool static_reqid;
+
/*
* Unique CHILD_SA identifier
*/
@@ -698,7 +703,7 @@ METHOD(child_sa_t, install, status_t,
this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS,
&esn, NULL);
- if (!this->reqid_allocated && !this->reqid)
+ if (!this->reqid_allocated && !this->static_reqid)
{
status = hydra->kernel_interface->alloc_reqid(hydra->kernel_interface,
my_ts, other_ts, this->mark_in, this->mark_out,
@@ -826,7 +831,7 @@ METHOD(child_sa_t, add_policies, status_t,
traffic_selector_t *my_ts, *other_ts;
status_t status = SUCCESS;
- if (!this->reqid_allocated && !this->reqid)
+ if (!this->reqid_allocated && !this->static_reqid)
{
/* trap policy, get or confirm reqid */
status = hydra->kernel_interface->alloc_reqid(
@@ -1305,6 +1310,10 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
this->reqid = charon->traps->find_reqid(charon->traps, config);
}
}
+ else
+ {
+ this->static_reqid = TRUE;
+ }
/* MIPv6 proxy transport mode sets SA endpoints to TS hosts */
if (config->get_mode(config) == MODE_TRANSPORT &&
diff --git a/src/libcharon/sa/eap/eap_method.c b/src/libcharon/sa/eap/eap_method.c
index a05e8c59a..9ce6ecf00 100644
--- a/src/libcharon/sa/eap/eap_method.c
+++ b/src/libcharon/sa/eap/eap_method.c
@@ -30,7 +30,8 @@ bool eap_method_register(plugin_t *plugin, plugin_feature_t *feature,
{
if (reg)
{
- charon->eap->add_method(charon->eap, feature->arg.eap, 0,
+ charon->eap->add_method(charon->eap, feature->arg.eap.type,
+ feature->arg.eap.vendor,
feature->type == FEATURE_EAP_SERVER ? EAP_SERVER : EAP_PEER,
(eap_constructor_t)data);
}
diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c
index 3aafa4c13..dcf9d5f2c 100644
--- a/src/libcharon/sa/ike_sa.c
+++ b/src/libcharon/sa/ike_sa.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2014 Tobias Brunner
+ * Copyright (C) 2006-2015 Tobias Brunner
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2005 Jan Hutter
@@ -487,8 +487,9 @@ METHOD(ike_sa_t, send_keepalive, void,
send_keepalive_job_t *job;
time_t last_out, now, diff;
- if (!(this->conditions & COND_NAT_HERE) || this->keepalive_interval == 0)
- { /* disable keep alives if we are not NATed anymore */
+ if (!(this->conditions & COND_NAT_HERE) || this->keepalive_interval == 0 ||
+ this->state == IKE_PASSIVE)
+ { /* disable keep alives if we are not NATed anymore, or we are passive */
return;
}
@@ -651,7 +652,7 @@ METHOD(ike_sa_t, get_state, ike_sa_state_t,
METHOD(ike_sa_t, set_state, void,
private_ike_sa_t *this, ike_sa_state_t state)
{
- bool trigger_dpd = FALSE;
+ bool trigger_dpd = FALSE, keepalives = FALSE;
DBG2(DBG_IKE, "IKE_SA %s[%d] state change: %N => %N",
get_name(this), this->unique_id,
@@ -722,6 +723,10 @@ METHOD(ike_sa_t, set_state, void,
* so yet, so prevent that. */
this->stats[STAT_INBOUND] = this->stats[STAT_ESTABLISHED];
}
+ if (this->state == IKE_PASSIVE)
+ {
+ keepalives = TRUE;
+ }
}
break;
}
@@ -742,6 +747,10 @@ METHOD(ike_sa_t, set_state, void,
DBG1(DBG_IKE, "DPD not supported by peer, disabled");
}
}
+ if (keepalives)
+ {
+ send_keepalive(this);
+ }
}
METHOD(ike_sa_t, reset, void,
@@ -1200,6 +1209,19 @@ static void resolve_hosts(private_ike_sa_t *this)
break;
}
+ /* if an IP address is set locally, use the same family to resolve remote */
+ if (family == AF_UNSPEC && !this->remote_host)
+ {
+ if (this->local_host)
+ {
+ family = this->local_host->get_family(this->local_host);
+ }
+ else
+ {
+ family = ike_cfg_get_family(this->ike_cfg, TRUE);
+ }
+ }
+
if (this->remote_host)
{
host = this->remote_host->clone(this->remote_host);
@@ -1211,7 +1233,18 @@ static void resolve_hosts(private_ike_sa_t *this)
}
if (host)
{
- set_other_host(this, host);
+ if (!host->is_anyaddr(host) ||
+ this->other_host->is_anyaddr(this->other_host))
+ { /* don't set to %any if we currently have an address, but the
+ * address family might have changed */
+ set_other_host(this, host);
+ }
+ else
+ { /* reuse the original port as some implementations might not like
+ * initial IKE messages on other ports */
+ this->other_host->set_port(this->other_host, host->get_port(host));
+ host->destroy(host);
+ }
}
if (this->local_host)
diff --git a/src/libcharon/sa/ike_sa_id.c b/src/libcharon/sa/ike_sa_id.c
index 0f0f1ab63..e52086483 100644
--- a/src/libcharon/sa/ike_sa_id.c
+++ b/src/libcharon/sa/ike_sa_id.c
@@ -18,7 +18,7 @@
#include "ike_sa_id.h"
#include <stdio.h>
-
+#include <encoding/payloads/ike_header.h>
typedef struct private_ike_sa_id_t private_ike_sa_id_t;
@@ -90,6 +90,8 @@ METHOD(ike_sa_id_t, equals, bool,
return FALSE;
}
return this->ike_version == other->ike_version &&
+ (this->ike_version == IKEV1_MAJOR_VERSION ||
+ this->is_initiator_flag == other->is_initiator_flag) &&
this->initiator_spi == other->initiator_spi &&
this->responder_spi == other->responder_spi;
}
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 938f7848f..37d69874d 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2005-2011 Martin Willi
* Copyright (C) 2011 revosec AG
- * Copyright (C) 2008-2012 Tobias Brunner
+ * Copyright (C) 2008-2015 Tobias Brunner
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
@@ -157,6 +157,8 @@ static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id)
}
if ((id->get_responder_spi(id) == 0 ||
entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0) &&
+ (id->get_ike_version(id) == IKEV1_MAJOR_VERSION ||
+ id->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id)) &&
id->get_initiator_spi(id) == entry->ike_sa_id->get_initiator_spi(entry->ike_sa_id))
{
/* this is TRUE for IKE_SAs that we initiated but have not yet received a response */
@@ -204,6 +206,9 @@ struct half_open_t {
/** the number of half-open IKE_SAs with that host */
u_int count;
+
+ /** the number of half-open IKE_SAs we responded to with that host */
+ u_int count_responder;
};
/**
@@ -359,6 +364,11 @@ struct private_ike_sa_manager_t {
refcount_t half_open_count;
/**
+ * Total number of half-open IKE_SAs as responder.
+ */
+ refcount_t half_open_count_responder;
+
+ /**
* Hash table with connected_peers_t objects.
*/
table_item_t **connected_peers_table;
@@ -384,6 +394,11 @@ struct private_ike_sa_manager_t {
rng_t *rng;
/**
+ * Lock to access the RNG instance
+ */
+ rwlock_t *rng_lock;
+
+ /**
* reuse existing IKE_SAs in checkout_by_config
*/
bool reuse_ikesa;
@@ -730,9 +745,11 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
table_item_t *item;
u_int row, segment;
rwlock_t *lock;
+ ike_sa_id_t *ike_id;
half_open_t *half_open;
chunk_t addr;
+ ike_id = entry->ike_sa_id;
addr = entry->other->get_address(entry->other);
row = chunk_hash(addr) & this->table_mask;
segment = row & this->segment_mask;
@@ -745,7 +762,6 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
if (chunk_equals(addr, half_open->other))
{
- half_open->count++;
break;
}
item = item->next;
@@ -755,7 +771,6 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
{
INIT(half_open,
.other = chunk_clone(addr),
- .count = 1,
);
INIT(item,
.value = half_open,
@@ -763,8 +778,14 @@ static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry)
);
this->half_open_table[row] = item;
}
- this->half_open_segments[segment].count++;
+ half_open->count++;
ref_get(&this->half_open_count);
+ if (!ike_id->is_initiator(ike_id))
+ {
+ half_open->count_responder++;
+ ref_get(&this->half_open_count_responder);
+ }
+ this->half_open_segments[segment].count++;
lock->unlock(lock);
}
@@ -776,8 +797,10 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry)
table_item_t *item, *prev = NULL;
u_int row, segment;
rwlock_t *lock;
+ ike_sa_id_t *ike_id;
chunk_t addr;
+ ike_id = entry->ike_sa_id;
addr = entry->other->get_address(entry->other);
row = chunk_hash(addr) & this->table_mask;
segment = row & this->segment_mask;
@@ -790,6 +813,12 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry)
if (chunk_equals(addr, half_open->other))
{
+ if (!ike_id->is_initiator(ike_id))
+ {
+ half_open->count_responder--;
+ ignore_result(ref_put(&this->half_open_count_responder));
+ }
+ ignore_result(ref_put(&this->half_open_count));
if (--half_open->count == 0)
{
if (prev)
@@ -804,7 +833,6 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry)
free(item);
}
this->half_open_segments[segment].count--;
- ignore_result(ref_put(&this->half_open_count));
break;
}
prev = item;
@@ -943,12 +971,14 @@ static u_int64_t get_spi(private_ike_sa_manager_t *this)
{
u_int64_t spi;
- if (this->rng &&
- this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi))
+ this->rng_lock->read_lock(this->rng_lock);
+ if (!this->rng ||
+ !this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi))
{
- return spi;
+ spi = 0;
}
- return 0;
+ this->rng_lock->unlock(this->rng_lock);
+ return spi;
}
/**
@@ -1563,7 +1593,6 @@ METHOD(ike_sa_manager_t, checkin, void,
put_half_open(this, entry);
}
else if (!entry->half_open &&
- !entry->ike_sa_id->is_initiator(entry->ike_sa_id) &&
ike_sa->get_state(ike_sa) == IKE_CONNECTING)
{
/* this is a new half-open SA */
@@ -1579,6 +1608,12 @@ METHOD(ike_sa_manager_t, checkin, void,
entry = entry_create();
entry->ike_sa_id = ike_sa_id->clone(ike_sa_id);
entry->ike_sa = ike_sa;
+ if (ike_sa->get_state(ike_sa) == IKE_CONNECTING)
+ {
+ entry->half_open = TRUE;
+ entry->other = other->clone(other);
+ put_half_open(this, entry);
+ }
segment = put_entry(this, entry);
}
@@ -1937,7 +1972,7 @@ METHOD(ike_sa_manager_t, get_count, u_int,
}
METHOD(ike_sa_manager_t, get_half_open_count, u_int,
- private_ike_sa_manager_t *this, host_t *ip)
+ private_ike_sa_manager_t *this, host_t *ip, bool responder_only)
{
table_item_t *item;
u_int row, segment;
@@ -1959,7 +1994,8 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int,
if (chunk_equals(addr, half_open->other))
{
- count = half_open->count;
+ count = responder_only ? half_open->count_responder
+ : half_open->count;
break;
}
item = item->next;
@@ -1968,7 +2004,8 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int,
}
else
{
- count = (u_int)ref_cur(&this->half_open_count);
+ count = responder_only ? (u_int)ref_cur(&this->half_open_count_responder)
+ : (u_int)ref_cur(&this->half_open_count);
}
return count;
}
@@ -2055,8 +2092,10 @@ METHOD(ike_sa_manager_t, flush, void,
charon->bus->set_sa(charon->bus, NULL);
unlock_all_segments(this);
+ this->rng_lock->write_lock(this->rng_lock);
this->rng->destroy(this->rng);
this->rng = NULL;
+ this->rng_lock->unlock(this->rng_lock);
}
METHOD(ike_sa_manager_t, destroy, void,
@@ -2081,6 +2120,7 @@ METHOD(ike_sa_manager_t, destroy, void,
free(this->connected_peers_segments);
free(this->init_hashes_segments);
+ this->rng_lock->destroy(this->rng_lock);
free(this);
}
@@ -2138,6 +2178,7 @@ ike_sa_manager_t *ike_sa_manager_create()
free(this);
return NULL;
}
+ this->rng_lock = rwlock_create(RWLOCK_TYPE_DEFAULT);
this->ikesa_limit = lib->settings->get_int(lib->settings,
"%s.ikesa_limit", 0, lib->ns);
diff --git a/src/libcharon/sa/ike_sa_manager.h b/src/libcharon/sa/ike_sa_manager.h
index f259d8e56..3ea928ea5 100644
--- a/src/libcharon/sa/ike_sa_manager.h
+++ b/src/libcharon/sa/ike_sa_manager.h
@@ -216,14 +216,15 @@ struct ike_sa_manager_t {
* To prevent the server from resource exhaustion, cookies and other
* mechanisms are used. The number of half open IKE_SAs is a good
* indicator to see if a peer is flooding the server.
- * If a host is supplied, only the number of half open IKE_SAs initiated
- * from this IP are counted.
- * Only SAs for which we are the responder are counted.
+ * If a host is supplied, only the number of half open IKE_SAs with this IP
+ * are counted.
*
* @param ip NULL for all, IP for half open IKE_SAs with IP
+ * @param responder_only TRUE to return only the number of responding SAs
* @return number of half open IKE_SAs
*/
- u_int (*get_half_open_count) (ike_sa_manager_t *this, host_t *ip);
+ u_int (*get_half_open_count)(ike_sa_manager_t *this, host_t *ip,
+ bool responder_only);
/**
* Delete all existing IKE_SAs and destroy them immediately.
diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c
index c968b2a9c..b7047e8fc 100644
--- a/src/libcharon/sa/ikev1/phase1.c
+++ b/src/libcharon/sa/ikev1/phase1.c
@@ -404,7 +404,7 @@ static auth_method_t get_pubkey_method(private_phase1_t *this, auth_cfg_t *auth)
id = (identification_t*)auth->get(auth, AUTH_RULE_IDENTITY);
if (id)
{
- private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, NULL);
+ private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, auth);
if (private)
{
switch (private->get_type(private))
diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c
index ed547c4c2..678f99df1 100644
--- a/src/libcharon/sa/ikev1/task_manager_v1.c
+++ b/src/libcharon/sa/ikev1/task_manager_v1.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2014 Tobias Brunner
+ * Copyright (C) 2007-2015 Tobias Brunner
* Copyright (C) 2007-2011 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -901,6 +901,34 @@ static bool process_dpd(private_task_manager_t *this, message_t *message)
}
/**
+ * Check if we already have a quick mode task queued for the exchange with the
+ * given message ID
+ */
+static bool have_quick_mode_task(private_task_manager_t *this, u_int32_t mid)
+{
+ enumerator_t *enumerator;
+ quick_mode_t *qm;
+ task_t *task;
+ bool found = FALSE;
+
+ enumerator = this->passive_tasks->create_enumerator(this->passive_tasks);
+ while (enumerator->enumerate(enumerator, &task))
+ {
+ if (task->get_type(task) == TASK_QUICK_MODE)
+ {
+ qm = (quick_mode_t*)task;
+ if (qm->get_mid(qm) == mid)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ return found;
+}
+
+/**
* handle an incoming request message
*/
static status_t process_request(private_task_manager_t *this,
@@ -911,6 +939,7 @@ static status_t process_request(private_task_manager_t *this,
bool send_response = FALSE, dpd = FALSE;
if (message->get_exchange_type(message) == INFORMATIONAL_V1 ||
+ message->get_exchange_type(message) == QUICK_MODE ||
this->passive_tasks->get_count(this->passive_tasks) == 0)
{ /* create tasks depending on request type, if not already some queued */
switch (message->get_exchange_type(message))
@@ -946,6 +975,10 @@ static status_t process_request(private_task_manager_t *this,
"unestablished IKE_SA, ignored");
return FAILED;
}
+ if (have_quick_mode_task(this, message->get_message_id(message)))
+ {
+ break;
+ }
task = (task_t *)quick_mode_create(this->ike_sa, NULL,
NULL, NULL);
this->passive_tasks->insert_last(this->passive_tasks, task);
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
index 96edfd8d8..d6a3f2cd1 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* Copyright (C) 2011 Martin Willi
@@ -185,6 +185,11 @@ struct private_quick_mode_t {
*/
bool udp;
+ /**
+ * Message ID of handled quick mode exchange
+ */
+ u_int32_t mid;
+
/** states of quick mode */
enum {
QM_INIT,
@@ -1019,6 +1024,11 @@ static void check_for_rekeyed_child(private_quick_mode_t *this)
METHOD(task_t, process_r, status_t,
private_quick_mode_t *this, message_t *message)
{
+ if (this->mid && this->mid != message->get_message_id(message))
+ { /* not responsible for this quick mode exchange */
+ return NEED_MORE;
+ }
+
switch (this->state)
{
case QM_INIT:
@@ -1188,6 +1198,11 @@ METHOD(task_t, process_r, status_t,
METHOD(task_t, build_r, status_t,
private_quick_mode_t *this, message_t *message)
{
+ if (this->mid && this->mid != message->get_message_id(message))
+ { /* not responsible for this quick mode exchange */
+ return NEED_MORE;
+ }
+
switch (this->state)
{
case QM_INIT:
@@ -1242,6 +1257,7 @@ METHOD(task_t, build_r, status_t,
add_ts(this, message);
this->state = QM_NEGOTIATED;
+ this->mid = message->get_message_id(message);
return NEED_MORE;
}
case QM_NEGOTIATED:
@@ -1335,6 +1351,12 @@ METHOD(task_t, get_type, task_type_t,
return TASK_QUICK_MODE;
}
+METHOD(quick_mode_t, get_mid, u_int32_t,
+ private_quick_mode_t *this)
+{
+ return this->mid;
+}
+
METHOD(quick_mode_t, use_reqid, void,
private_quick_mode_t *this, u_int32_t reqid)
{
@@ -1368,6 +1390,7 @@ METHOD(task_t, migrate, void,
this->ike_sa = ike_sa;
this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
this->state = QM_INIT;
+ this->mid = 0;
this->tsi = NULL;
this->tsr = NULL;
this->proposal = NULL;
@@ -1414,6 +1437,7 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
.migrate = _migrate,
.destroy = _destroy,
},
+ .get_mid = _get_mid,
.use_reqid = _use_reqid,
.use_marks = _use_marks,
.rekey = _rekey,
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.h b/src/libcharon/sa/ikev1/tasks/quick_mode.h
index ee9b64d13..062d63465 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.h
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.h
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
* Copyright (C) 2011 Martin Willi
* Copyright (C) 2011 revosec AG
*
@@ -38,6 +41,14 @@ struct quick_mode_t {
task_t task;
/**
+ * Get the message ID of the quick mode exchange handled by this task as
+ * responder.
+ *
+ * @return message ID, or 0 (not defined yet or as initiator)
+ */
+ u_int32_t (*get_mid)(quick_mode_t *this);
+
+ /**
* Use a specific reqid to install this CHILD_SA.
*
* @param reqid reqid to use
diff --git a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
index f1442096c..91f6187f9 100644
--- a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
@@ -448,6 +448,8 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message,
identification_t *other_id;
auth_cfg_t *auth;
keymat_v2_t *keymat;
+ eap_type_t type;
+ u_int32_t vendor;
auth_payload = (auth_payload_t*)message->get_payload(message,
PLV2_AUTH);
@@ -478,6 +480,13 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message,
this->auth_complete = TRUE;
auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
+
+ type = this->method->get_type(this->method, &vendor);
+ auth->add(auth, AUTH_RULE_EAP_TYPE, type);
+ if (vendor)
+ {
+ auth->add(auth, AUTH_RULE_EAP_VENDOR, vendor);
+ }
return TRUE;
}
diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
index 151b49718..2284a484d 100644
--- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
@@ -321,7 +321,7 @@ METHOD(authenticator_t, build, status_t,
chunk_t auth_data;
status_t status;
auth_payload_t *auth_payload;
- auth_method_t auth_method;
+ auth_method_t auth_method = AUTH_NONE;
id = this->ike_sa->get_my_id(this->ike_sa);
auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c
index 6fedc8eb5..fce0840e3 100644
--- a/src/libcharon/sa/ikev2/keymat_v2.c
+++ b/src/libcharon/sa/ikev2/keymat_v2.c
@@ -112,6 +112,7 @@ static bool derive_ike_aead(private_keymat_v2_t *this, u_int16_t alg,
case ENCR_AES_GCM_ICV12:
case ENCR_AES_GCM_ICV16:
/* RFC 4106 */
+ case ENCR_CHACHA20_POLY1305:
salt_size = 4;
break;
case ENCR_AES_CCM_ICV8:
@@ -527,6 +528,7 @@ METHOD(keymat_v2_t, derive_child_keys, bool,
case ENCR_AES_GCM_ICV16:
case ENCR_AES_CTR:
case ENCR_NULL_AUTH_AES_GMAC:
+ case ENCR_CHACHA20_POLY1305:
enc_size += 4;
break;
default:
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c
index e0f930c3c..e08f3dab1 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.c
+++ b/src/libcharon/sa/ikev2/tasks/child_create.c
@@ -145,6 +145,11 @@ struct private_child_create_t {
ipcomp_transform_t ipcomp_received;
/**
+ * IPsec protocol
+ */
+ protocol_id_t proto;
+
+ /**
* Own allocated SPI
*/
u_int32_t my_spi;
@@ -260,23 +265,23 @@ static bool allocate_spi(private_child_create_t *this)
{
enumerator_t *enumerator;
proposal_t *proposal;
- protocol_id_t proto = PROTO_ESP;
if (this->initiator)
{
+ this->proto = PROTO_ESP;
/* we just get a SPI for the first protocol. TODO: If we ever support
* proposal lists with mixed protocols, we'd need multiple SPIs */
if (this->proposals->get_first(this->proposals,
(void**)&proposal) == SUCCESS)
{
- proto = proposal->get_protocol(proposal);
+ this->proto = proposal->get_protocol(proposal);
}
}
else
{
- proto = this->proposal->get_protocol(this->proposal);
+ this->proto = this->proposal->get_protocol(this->proposal);
}
- this->my_spi = this->child_sa->alloc_spi(this->child_sa, proto);
+ this->my_spi = this->child_sa->alloc_spi(this->child_sa, this->proto);
if (this->my_spi)
{
if (this->initiator)
@@ -1352,20 +1357,16 @@ METHOD(task_t, build_i_delete, status_t,
private_child_create_t *this, message_t *message)
{
message->set_exchange_type(message, INFORMATIONAL);
- if (this->child_sa && this->proposal)
+ if (this->my_spi && this->proto)
{
- protocol_id_t proto;
delete_payload_t *del;
- u_int32_t spi;
- proto = this->proposal->get_protocol(this->proposal);
- spi = this->child_sa->get_spi(this->child_sa, TRUE);
- del = delete_payload_create(PLV2_DELETE, proto);
- del->add_spi(del, spi);
+ del = delete_payload_create(PLV2_DELETE, this->proto);
+ del->add_spi(del, this->my_spi);
message->add_payload(message, (payload_t*)del);
DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
- protocol_id_names, proto, ntohl(spi));
+ protocol_id_names, this->proto, ntohl(this->my_spi));
}
return NEED_MORE;
}
@@ -1375,9 +1376,13 @@ METHOD(task_t, build_i_delete, status_t,
*/
static status_t delete_failed_sa(private_child_create_t *this)
{
- this->public.task.build = _build_i_delete;
- this->public.task.process = (void*)return_success;
- return NEED_MORE;
+ if (this->my_spi && this->proto)
+ {
+ this->public.task.build = _build_i_delete;
+ this->public.task.process = (void*)return_success;
+ return NEED_MORE;
+ }
+ return SUCCESS;
}
METHOD(task_t, process_i, status_t,
@@ -1596,6 +1601,7 @@ METHOD(task_t, migrate, void,
this->tsi = NULL;
this->tsr = NULL;
this->dh = NULL;
+ this->nonceg = NULL;
this->child_sa = NULL;
this->mode = MODE_TUNNEL;
this->ipcomp = IPCOMP_NONE;
diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c
index c806e19ca..c7a8a1342 100644
--- a/src/libcharon/sa/ikev2/tasks/child_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c
@@ -170,13 +170,8 @@ METHOD(task_t, build_i, status_t,
}
config = this->child_sa->get_config(this->child_sa);
- /* we just need the rekey notify ... */
- notify = notify_payload_create_from_protocol_and_type(PLV2_NOTIFY,
- this->protocol, REKEY_SA);
- notify->set_spi(notify, this->spi);
- message->add_payload(message, (payload_t*)notify);
- /* ... our CHILD_CREATE task does the hard work for us. */
+ /* our CHILD_CREATE task does the hard work for us */
if (!this->child_create)
{
this->child_create = child_create_create(this->ike_sa,
@@ -194,6 +189,14 @@ METHOD(task_t, build_i, status_t,
schedule_delayed_rekey(this);
return FAILED;
}
+ if (message->get_exchange_type(message) == CREATE_CHILD_SA)
+ {
+ /* don't add the notify if the CHILD_CREATE task changed the exchange */
+ notify = notify_payload_create_from_protocol_and_type(PLV2_NOTIFY,
+ this->protocol, REKEY_SA);
+ notify->set_spi(notify, this->spi);
+ message->add_payload(message, (payload_t*)notify);
+ }
this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
return NEED_MORE;
@@ -334,8 +337,7 @@ METHOD(task_t, process_i, status_t,
if (this->child_create->task.process(&this->child_create->task,
message) == NEED_MORE)
{
- /* bad DH group while rekeying, try again */
- this->child_create->task.migrate(&this->child_create->task, this->ike_sa);
+ /* bad DH group while rekeying, retry, or failure requiring deletion */
return NEED_MORE;
}
if (message->get_payload(message, PLV2_SECURITY_ASSOCIATION) == NULL)
diff --git a/src/libcharon/sa/ikev2/tasks/ike_rekey.c b/src/libcharon/sa/ikev2/tasks/ike_rekey.c
index 1855517ce..eaba04e3a 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.c
@@ -116,7 +116,6 @@ static void establish_new(private_ike_rekey_t *this)
lib->processor->queue_job(lib->processor, job);
}
this->new_sa = NULL;
- /* set threads active IKE_SA after checkin */
charon->bus->set_sa(charon->bus, this->ike_sa);
}
}
@@ -229,9 +228,10 @@ METHOD(task_t, build_r, status_t,
if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED)
{
+ 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 */
@@ -335,15 +335,13 @@ METHOD(task_t, process_i, status_t,
{
charon->ike_sa_manager->checkin(
charon->ike_sa_manager, this->new_sa);
- /* set threads active IKE_SA after checkin */
- charon->bus->set_sa(charon->bus, this->ike_sa);
}
+ charon->bus->set_sa(charon->bus, this->ike_sa);
this->new_sa = NULL;
establish_new(other);
return SUCCESS;
}
}
- /* set threads active IKE_SA after checkin */
charon->bus->set_sa(charon->bus, this->ike_sa);
}
@@ -372,9 +370,13 @@ METHOD(ike_rekey_t, collide, void,
this->collision = other;
}
-METHOD(task_t, migrate, void,
- private_ike_rekey_t *this, ike_sa_t *ike_sa)
+/**
+ * Cleanup the task
+ */
+static void cleanup(private_ike_rekey_t *this)
{
+ ike_sa_t *cur_sa;
+
if (this->ike_init)
{
this->ike_init->task.destroy(&this->ike_init->task);
@@ -383,9 +385,16 @@ METHOD(task_t, migrate, void,
{
this->ike_delete->task.destroy(&this->ike_delete->task);
}
+ cur_sa = charon->bus->get_sa(charon->bus);
DESTROY_IF(this->new_sa);
+ charon->bus->set_sa(charon->bus, cur_sa);
DESTROY_IF(this->collision);
+}
+METHOD(task_t, migrate, void,
+ private_ike_rekey_t *this, ike_sa_t *ike_sa)
+{
+ cleanup(this);
this->collision = NULL;
this->ike_sa = ike_sa;
this->new_sa = NULL;
@@ -396,16 +405,7 @@ METHOD(task_t, migrate, void,
METHOD(task_t, destroy, void,
private_ike_rekey_t *this)
{
- if (this->ike_init)
- {
- this->ike_init->task.destroy(&this->ike_init->task);
- }
- if (this->ike_delete)
- {
- this->ike_delete->task.destroy(&this->ike_delete->task);
- }
- DESTROY_IF(this->new_sa);
- DESTROY_IF(this->collision);
+ cleanup(this);
free(this);
}
diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c
index 73e1abbf3..1a984435c 100644
--- a/src/libcharon/sa/shunt_manager.c
+++ b/src/libcharon/sa/shunt_manager.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2015 Tobias Brunner
* Copyright (C) 2011 Andreas Steffen
* HSR Hochschule fuer Technik Rapperswil
*
@@ -18,8 +19,10 @@
#include <hydra.h>
#include <daemon.h>
#include <threading/rwlock.h>
+#include <threading/rwlock_condvar.h>
#include <collections/linked_list.h>
+#define INSTALL_DISABLED ((u_int)~0)
typedef struct private_shunt_manager_t private_shunt_manager_t;
@@ -37,6 +40,21 @@ struct private_shunt_manager_t {
* Installed shunts, as child_cfg_t
*/
linked_list_t *shunts;
+
+ /**
+ * Lock to safely access the list of shunts
+ */
+ rwlock_t *lock;
+
+ /**
+ * Number of threads currently installing shunts, or INSTALL_DISABLED
+ */
+ u_int installing;
+
+ /**
+ * Condvar to signal shunt installation
+ */
+ rwlock_condvar_t *condvar;
};
/**
@@ -117,9 +135,15 @@ METHOD(shunt_manager_t, install, bool,
{
enumerator_t *enumerator;
child_cfg_t *child_cfg;
- bool found = FALSE;
+ bool found = FALSE, success;
/* check if not already installed */
+ this->lock->write_lock(this->lock);
+ if (this->installing == INSTALL_DISABLED)
+ { /* flush() has been called */
+ this->lock->unlock(this->lock);
+ return FALSE;
+ }
enumerator = this->shunts->create_enumerator(this->shunts);
while (enumerator->enumerate(enumerator, &child_cfg))
{
@@ -130,16 +154,29 @@ METHOD(shunt_manager_t, install, bool,
}
}
enumerator->destroy(enumerator);
-
if (found)
{
DBG1(DBG_CFG, "shunt %N policy '%s' already installed",
ipsec_mode_names, child->get_mode(child), child->get_name(child));
+ this->lock->unlock(this->lock);
return TRUE;
}
this->shunts->insert_last(this->shunts, child->get_ref(child));
+ this->installing++;
+ this->lock->unlock(this->lock);
- return install_shunt_policy(child);
+ success = install_shunt_policy(child);
+
+ this->lock->write_lock(this->lock);
+ if (!success)
+ {
+ this->shunts->remove(this->shunts, child, NULL);
+ child->destroy(child);
+ }
+ this->installing--;
+ this->condvar->signal(this->condvar);
+ this->lock->unlock(this->lock);
+ return success;
}
/**
@@ -215,6 +252,7 @@ METHOD(shunt_manager_t, uninstall, bool,
enumerator_t *enumerator;
child_cfg_t *child, *found = NULL;
+ this->lock->write_lock(this->lock);
enumerator = this->shunts->create_enumerator(this->shunts);
while (enumerator->enumerate(enumerator, &child))
{
@@ -226,6 +264,7 @@ METHOD(shunt_manager_t, uninstall, bool,
}
}
enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
if (!found)
{
@@ -239,20 +278,37 @@ METHOD(shunt_manager_t, uninstall, bool,
METHOD(shunt_manager_t, create_enumerator, enumerator_t*,
private_shunt_manager_t *this)
{
- return this->shunts->create_enumerator(this->shunts);
+ this->lock->read_lock(this->lock);
+ return enumerator_create_cleaner(
+ this->shunts->create_enumerator(this->shunts),
+ (void*)this->lock->unlock, this->lock);
}
-METHOD(shunt_manager_t, destroy, void,
+METHOD(shunt_manager_t, flush, void,
private_shunt_manager_t *this)
{
child_cfg_t *child;
+ this->lock->write_lock(this->lock);
+ while (this->installing)
+ {
+ this->condvar->wait(this->condvar, this->lock);
+ }
while (this->shunts->remove_last(this->shunts, (void**)&child) == SUCCESS)
{
uninstall_shunt_policy(child);
child->destroy(child);
}
- this->shunts->destroy(this->shunts);
+ this->installing = INSTALL_DISABLED;
+ this->lock->unlock(this->lock);
+}
+
+METHOD(shunt_manager_t, destroy, void,
+ private_shunt_manager_t *this)
+{
+ this->shunts->destroy_offset(this->shunts, offsetof(child_cfg_t, destroy));
+ this->lock->destroy(this->lock);
+ this->condvar->destroy(this->condvar);
free(this);
}
@@ -268,9 +324,12 @@ shunt_manager_t *shunt_manager_create()
.install = _install,
.uninstall = _uninstall,
.create_enumerator = _create_enumerator,
+ .flush = _flush,
.destroy = _destroy,
},
.shunts = linked_list_create(),
+ .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ .condvar = rwlock_condvar_create(),
);
return &this->public;
diff --git a/src/libcharon/sa/shunt_manager.h b/src/libcharon/sa/shunt_manager.h
index 28a795dc9..c43f5db3d 100644
--- a/src/libcharon/sa/shunt_manager.h
+++ b/src/libcharon/sa/shunt_manager.h
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2015 Tobias Brunner
* Copyright (C) 2011 Andreas Steffen
* HSR Hochschule fuer Technik Rapperswil
*
@@ -56,6 +57,11 @@ struct shunt_manager_t {
enumerator_t* (*create_enumerator)(shunt_manager_t *this);
/**
+ * Clear any installed shunt.
+ */
+ void (*flush)(shunt_manager_t *this);
+
+ /**
* Destroy a shunt_manager_t.
*/
void (*destroy)(shunt_manager_t *this);
diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c
index d6ff3c8c5..63505c960 100644
--- a/src/libcharon/sa/trap_manager.c
+++ b/src/libcharon/sa/trap_manager.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2013 Tobias Brunner
+ * Copyright (C) 2011-2015 Tobias Brunner
* Copyright (C) 2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -18,9 +18,12 @@
#include <hydra.h>
#include <daemon.h>
+#include <threading/mutex.h>
#include <threading/rwlock.h>
+#include <threading/rwlock_condvar.h>
#include <collections/linked_list.h>
+#define INSTALL_DISABLED ((u_int)~0)
typedef struct private_trap_manager_t private_trap_manager_t;
typedef struct trap_listener_t trap_listener_t;
@@ -67,6 +70,26 @@ struct private_trap_manager_t {
trap_listener_t listener;
/**
+ * list of acquires we currently handle
+ */
+ linked_list_t *acquires;
+
+ /**
+ * mutex for list of acquires
+ */
+ mutex_t *mutex;
+
+ /**
+ * number of threads currently installing trap policies, or INSTALL_DISABLED
+ */
+ u_int installing;
+
+ /**
+ * condvar to signal trap policy installation
+ */
+ rwlock_condvar_t *condvar;
+
+ /**
* Whether to ignore traffic selectors from acquires
*/
bool ignore_acquire_ts;
@@ -80,23 +103,58 @@ typedef struct {
char *name;
/** ref to peer_cfg to initiate */
peer_cfg_t *peer_cfg;
- /** ref to instanciated CHILD_SA */
+ /** ref to instantiated CHILD_SA (i.e the trap policy) */
child_sa_t *child_sa;
- /** TRUE if an acquire is pending */
- bool pending;
+ /** TRUE in case of wildcard Transport Mode SA */
+ bool wildcard;
+} entry_t;
+
+/**
+ * A handled acquire
+ */
+typedef struct {
/** pending IKE_SA connecting upon acquire */
ike_sa_t *ike_sa;
-} entry_t;
+ /** reqid of pending trap policy */
+ u_int32_t reqid;
+ /** destination address (wildcard case) */
+ host_t *dst;
+} acquire_t;
/**
* actually uninstall and destroy an installed entry
*/
-static void destroy_entry(entry_t *entry)
+static void destroy_entry(entry_t *this)
+{
+ this->child_sa->destroy(this->child_sa);
+ this->peer_cfg->destroy(this->peer_cfg);
+ free(this->name);
+ free(this);
+}
+
+/**
+ * destroy a cached acquire entry
+ */
+static void destroy_acquire(acquire_t *this)
{
- entry->child_sa->destroy(entry->child_sa);
- entry->peer_cfg->destroy(entry->peer_cfg);
- free(entry->name);
- free(entry);
+ DESTROY_IF(this->dst);
+ free(this);
+}
+
+/**
+ * match an acquire entry by reqid
+ */
+static bool acquire_by_reqid(acquire_t *this, u_int32_t *reqid)
+{
+ return this->reqid == *reqid;
+}
+
+/**
+ * match an acquire entry by destination address
+ */
+static bool acquire_by_dst(acquire_t *this, host_t *dst)
+{
+ return this->dst && this->dst->ip_equals(this->dst, dst);
}
METHOD(trap_manager_t, install, u_int32_t,
@@ -113,32 +171,49 @@ METHOD(trap_manager_t, install, u_int32_t,
linked_list_t *proposals;
proposal_t *proposal;
protocol_id_t proto = PROTO_ESP;
+ bool wildcard = FALSE;
/* try to resolve addresses */
ike_cfg = peer->get_ike_cfg(peer);
other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC);
- if (!other || other->is_anyaddr(other))
+ if (other && other->is_anyaddr(other) &&
+ child->get_mode(child) == MODE_TRANSPORT)
+ {
+ /* allow wildcard for Transport Mode SAs */
+ me = host_create_any(other->get_family(other));
+ wildcard = TRUE;
+ }
+ else if (!other || other->is_anyaddr(other))
{
DESTROY_IF(other);
DBG1(DBG_CFG, "installing trap failed, remote address unknown");
return 0;
}
- me = ike_cfg->resolve_me(ike_cfg, other->get_family(other));
- if (!me || me->is_anyaddr(me))
+ else
{
- DESTROY_IF(me);
- me = hydra->kernel_interface->get_source_addr(
- hydra->kernel_interface, other, NULL);
- if (!me)
+ me = ike_cfg->resolve_me(ike_cfg, other->get_family(other));
+ if (!me || me->is_anyaddr(me))
{
- DBG1(DBG_CFG, "installing trap failed, local address unknown");
- other->destroy(other);
- return 0;
+ DESTROY_IF(me);
+ me = hydra->kernel_interface->get_source_addr(
+ hydra->kernel_interface, other, NULL);
+ if (!me)
+ {
+ DBG1(DBG_CFG, "installing trap failed, local address unknown");
+ other->destroy(other);
+ return 0;
+ }
+ me->set_port(me, ike_cfg->get_my_port(ike_cfg));
}
- me->set_port(me, ike_cfg->get_my_port(ike_cfg));
}
this->lock->write_lock(this->lock);
+ if (this->installing == INSTALL_DISABLED)
+ { /* flush() has been called */
+ this->lock->unlock(this->lock);
+ me->destroy(me);
+ return 0;
+ }
enumerator = this->traps->create_enumerator(this->traps);
while (enumerator->enumerate(enumerator, &entry))
{
@@ -160,6 +235,7 @@ METHOD(trap_manager_t, install, u_int32_t,
{
DBG1(DBG_CFG, "CHILD_SA '%s' is already being routed", found->name);
this->lock->unlock(this->lock);
+ me->destroy(me);
return 0;
}
/* config might have changed so update everything */
@@ -170,8 +246,10 @@ METHOD(trap_manager_t, install, u_int32_t,
INIT(entry,
.name = strdup(child->get_name(child)),
.peer_cfg = peer->get_ref(peer),
+ .wildcard = wildcard,
);
this->traps->insert_first(this->traps, entry);
+ this->installing++;
/* don't hold lock while creating CHILD_SA and installing policies */
this->lock->unlock(this->lock);
@@ -220,6 +298,11 @@ METHOD(trap_manager_t, install, u_int32_t,
{
destroy_entry(found);
}
+ this->lock->write_lock(this->lock);
+ /* do this at the end, so entries created temporarily are also destroyed */
+ this->installing--;
+ this->condvar->signal(this->condvar);
+ this->lock->unlock(this->lock);
return reqid;
}
@@ -314,9 +397,12 @@ METHOD(trap_manager_t, acquire, void,
{
enumerator_t *enumerator;
entry_t *entry, *found = NULL;
+ acquire_t *acquire;
peer_cfg_t *peer;
child_cfg_t *child;
ike_sa_t *ike_sa;
+ host_t *host;
+ bool wildcard, ignore = FALSE;
this->lock->read_lock(this->lock);
enumerator = this->traps->create_enumerator(this->traps);
@@ -333,11 +419,52 @@ METHOD(trap_manager_t, acquire, void,
if (!found)
{
- DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid);
+ DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d", reqid);
this->lock->unlock(this->lock);
return;
}
- if (!cas_bool(&found->pending, FALSE, TRUE))
+ reqid = found->child_sa->get_reqid(found->child_sa);
+ wildcard = found->wildcard;
+
+ this->mutex->lock(this->mutex);
+ if (wildcard)
+ { /* for wildcard acquires we check that we don't have a pending acquire
+ * with the same peer */
+ u_int8_t mask;
+
+ dst->to_subnet(dst, &host, &mask);
+ if (this->acquires->find_first(this->acquires, (void*)acquire_by_dst,
+ (void**)&acquire, host) == SUCCESS)
+ {
+ host->destroy(host);
+ ignore = TRUE;
+ }
+ else
+ {
+ INIT(acquire,
+ .dst = host,
+ .reqid = reqid,
+ );
+ this->acquires->insert_last(this->acquires, acquire);
+ }
+ }
+ else
+ {
+ if (this->acquires->find_first(this->acquires, (void*)acquire_by_reqid,
+ (void**)&acquire, &reqid) == SUCCESS)
+ {
+ ignore = TRUE;
+ }
+ else
+ {
+ INIT(acquire,
+ .reqid = reqid,
+ );
+ this->acquires->insert_last(this->acquires, acquire);
+ }
+ }
+ this->mutex->unlock(this->mutex);
+ if (ignore)
{
DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
this->lock->unlock(this->lock);
@@ -346,12 +473,40 @@ METHOD(trap_manager_t, acquire, void,
peer = found->peer_cfg->get_ref(found->peer_cfg);
child = found->child_sa->get_config(found->child_sa);
child = child->get_ref(child);
- reqid = found->child_sa->get_reqid(found->child_sa);
/* don't hold the lock while checking out the IKE_SA */
this->lock->unlock(this->lock);
- ike_sa = charon->ike_sa_manager->checkout_by_config(
+ if (wildcard)
+ { /* the peer config would match IKE_SAs with other peers */
+ ike_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+ peer->get_ike_version(peer), TRUE);
+ if (ike_sa)
+ {
+ ike_cfg_t *ike_cfg;
+ u_int16_t port;
+ u_int8_t mask;
+
+ ike_sa->set_peer_cfg(ike_sa, peer);
+ ike_cfg = ike_sa->get_ike_cfg(ike_sa);
+
+ port = ike_cfg->get_other_port(ike_cfg);
+ dst->to_subnet(dst, &host, &mask);
+ host->set_port(host, port);
+ ike_sa->set_other_host(ike_sa, host);
+
+ port = ike_cfg->get_my_port(ike_cfg);
+ src->to_subnet(src, &host, &mask);
+ host->set_port(host, port);
+ ike_sa->set_my_host(ike_sa, host);
+
+ charon->bus->set_sa(charon->bus, ike_sa);
+ }
+ }
+ else
+ {
+ ike_sa = charon->ike_sa_manager->checkout_by_config(
charon->ike_sa_manager, peer);
+ }
if (ike_sa)
{
if (ike_sa->get_peer_cfg(ike_sa) == NULL)
@@ -363,24 +518,29 @@ METHOD(trap_manager_t, acquire, void,
* have a single TS that we can establish in a Quick Mode. */
src = dst = NULL;
}
+
+ this->mutex->lock(this->mutex);
+ acquire->ike_sa = ike_sa;
+ this->mutex->unlock(this->mutex);
+
if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
{
- /* make sure the entry is still there */
- this->lock->read_lock(this->lock);
- if (this->traps->find_first(this->traps, NULL,
- (void**)&found) == SUCCESS)
- {
- found->ike_sa = ike_sa;
- }
- this->lock->unlock(this->lock);
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
}
else
{
- ike_sa->destroy(ike_sa);
- charon->bus->set_sa(charon->bus, NULL);
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+ ike_sa);
}
}
+ else
+ {
+ this->mutex->lock(this->mutex);
+ this->acquires->remove(this->acquires, acquire, NULL);
+ this->mutex->unlock(this->mutex);
+ destroy_acquire(acquire);
+ child->destroy(child);
+ }
peer->destroy(peer);
}
@@ -391,26 +551,33 @@ static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
child_sa_t *child_sa)
{
enumerator_t *enumerator;
- entry_t *entry;
+ acquire_t *acquire;
- this->lock->read_lock(this->lock);
- enumerator = this->traps->create_enumerator(this->traps);
- while (enumerator->enumerate(enumerator, &entry))
+ this->mutex->lock(this->mutex);
+ enumerator = this->acquires->create_enumerator(this->acquires);
+ while (enumerator->enumerate(enumerator, &acquire))
{
- if (entry->ike_sa != ike_sa)
+ if (!acquire->ike_sa || acquire->ike_sa != ike_sa)
{
continue;
}
- if (child_sa && child_sa->get_reqid(child_sa) !=
- entry->child_sa->get_reqid(entry->child_sa))
+ if (child_sa)
{
- continue;
+ if (acquire->dst)
+ {
+ /* since every wildcard acquire results in a separate IKE_SA
+ * there is no need to compare the destination address */
+ }
+ else if (child_sa->get_reqid(child_sa) != acquire->reqid)
+ {
+ continue;
+ }
}
- entry->ike_sa = NULL;
- entry->pending = FALSE;
+ this->acquires->remove_at(this->acquires, enumerator);
+ destroy_acquire(acquire);
}
enumerator->destroy(enumerator);
- this->lock->unlock(this->lock);
+ this->mutex->unlock(this->mutex);
}
METHOD(listener_t, ike_state_change, bool,
@@ -444,14 +611,15 @@ METHOD(listener_t, child_state_change, bool,
METHOD(trap_manager_t, flush, void,
private_trap_manager_t *this)
{
- linked_list_t *traps;
- /* since destroying the CHILD_SA results in events which require a read
- * lock we cannot destroy the list while holding the write lock */
this->lock->write_lock(this->lock);
- traps = this->traps;
+ while (this->installing)
+ {
+ this->condvar->wait(this->condvar, this->lock);
+ }
+ this->traps->destroy_function(this->traps, (void*)destroy_entry);
this->traps = linked_list_create();
+ this->installing = INSTALL_DISABLED;
this->lock->unlock(this->lock);
- traps->destroy_function(traps, (void*)destroy_entry);
}
METHOD(trap_manager_t, destroy, void,
@@ -459,6 +627,9 @@ METHOD(trap_manager_t, destroy, void,
{
charon->bus->remove_listener(charon->bus, &this->listener.listener);
this->traps->destroy_function(this->traps, (void*)destroy_entry);
+ this->acquires->destroy_function(this->acquires, (void*)destroy_acquire);
+ this->condvar->destroy(this->condvar);
+ this->mutex->destroy(this->mutex);
this->lock->destroy(this->lock);
free(this);
}
@@ -488,7 +659,10 @@ trap_manager_t *trap_manager_create(void)
},
},
.traps = linked_list_create(),
+ .acquires = linked_list_create(),
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ .condvar = rwlock_condvar_create(),
.ignore_acquire_ts = lib->settings->get_bool(lib->settings,
"%s.ignore_acquire_ts", FALSE, lib->ns),
);