summaryrefslogtreecommitdiff
path: root/src/libcharon/sa
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@debian.org>2015-11-18 14:49:27 +0100
committerYves-Alexis Perez <corsac@debian.org>2015-11-18 14:49:27 +0100
commit1e980d6be0ef0e243c6fe82b5e855454b97e24a4 (patch)
tree0d59eec2ce2ed332434ae80fc78a44db9ad293c5 /src/libcharon/sa
parent5dca9ea0e2931f0e2a056c7964d311bcc30a01b8 (diff)
downloadvyos-strongswan-1e980d6be0ef0e243c6fe82b5e855454b97e24a4.tar.gz
vyos-strongswan-1e980d6be0ef0e243c6fe82b5e855454b97e24a4.zip
Imported Upstream version 5.3.4
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r--src/libcharon/sa/child_sa.c178
-rw-r--r--src/libcharon/sa/ike_sa_manager.c84
-rw-r--r--src/libcharon/sa/ike_sa_manager.h21
-rw-r--r--src/libcharon/sa/ikev1/keymat_v1.c20
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c110
-rw-r--r--src/libcharon/sa/ikev1/tasks/mode_config.c4
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_delete.c4
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c6
-rw-r--r--src/libcharon/sa/ikev1/tasks/xauth.c10
-rw-r--r--src/libcharon/sa/ikev2/keymat_v2.c1
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c4
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_delete.c4
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_mobike.c6
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_natd.c28
-rw-r--r--src/libcharon/sa/shunt_manager.c66
-rw-r--r--src/libcharon/sa/trap_manager.c2
16 files changed, 375 insertions, 173 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index 73f2ec9d3..b0f163c83 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -413,8 +413,14 @@ METHOD(enumerator_t, policy_enumerate, bool,
{ /* protocol mismatch */
continue;
}
- *my_out = this->ts;
- *other_out = other_ts;
+ if (my_out)
+ {
+ *my_out = this->ts;
+ }
+ if (other_out)
+ {
+ *other_out = other_ts;
+ }
return TRUE;
}
return FALSE;
@@ -775,6 +781,50 @@ static bool require_policy_update()
}
/**
+ * Prepare SA config to install/delete policies
+ */
+static void prepare_sa_cfg(private_child_sa_t *this, ipsec_sa_cfg_t *my_sa,
+ ipsec_sa_cfg_t *other_sa)
+{
+ enumerator_t *enumerator;
+
+ *my_sa = (ipsec_sa_cfg_t){
+ .mode = this->mode,
+ .reqid = this->reqid,
+ .ipcomp = {
+ .transform = this->ipcomp,
+ },
+ };
+ *other_sa = *my_sa;
+
+ my_sa->ipcomp.cpi = this->my_cpi;
+ other_sa->ipcomp.cpi = this->other_cpi;
+
+ if (this->protocol == PROTO_ESP)
+ {
+ my_sa->esp.use = TRUE;
+ my_sa->esp.spi = this->my_spi;
+ other_sa->esp.use = TRUE;
+ other_sa->esp.spi = this->other_spi;
+ }
+ else
+ {
+ my_sa->ah.use = TRUE;
+ my_sa->ah.spi = this->my_spi;
+ other_sa->ah.use = TRUE;
+ other_sa->ah.spi = this->other_spi;
+ }
+
+ enumerator = create_policy_enumerator(this);
+ while (enumerator->enumerate(enumerator, NULL, NULL))
+ {
+ my_sa->policy_count++;
+ other_sa->policy_count++;
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
* Install 3 policies: out, in and forward
*/
static status_t install_policies_internal(private_child_sa_t *this,
@@ -806,20 +856,22 @@ static status_t install_policies_internal(private_child_sa_t *this,
* Delete 3 policies: out, in and forward
*/
static void del_policies_internal(private_child_sa_t *this,
- traffic_selector_t *my_ts, traffic_selector_t *other_ts,
- policy_priority_t priority)
+ host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts,
+ traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa,
+ ipsec_sa_cfg_t *other_sa, policy_type_t type, policy_priority_t priority)
{
+
hydra->kernel_interface->del_policy(hydra->kernel_interface,
- my_ts, other_ts, POLICY_OUT, this->reqid,
- this->mark_out, priority);
+ my_addr, other_addr, my_ts, other_ts, POLICY_OUT, type,
+ other_sa, this->mark_out, priority);
hydra->kernel_interface->del_policy(hydra->kernel_interface,
- other_ts, my_ts, POLICY_IN, this->reqid,
- this->mark_in, priority);
+ other_addr, my_addr, other_ts, my_ts, POLICY_IN,
+ type, my_sa, this->mark_in, priority);
if (this->mode != MODE_TRANSPORT)
{
hydra->kernel_interface->del_policy(hydra->kernel_interface,
- other_ts, my_ts, POLICY_FWD, this->reqid,
- this->mark_in, priority);
+ other_addr, my_addr, other_ts, my_ts, POLICY_FWD,
+ type, my_sa, this->mark_in, priority);
}
}
@@ -864,31 +916,9 @@ METHOD(child_sa_t, add_policies, status_t,
if (this->config->install_policy(this->config))
{
policy_priority_t priority;
- ipsec_sa_cfg_t my_sa = {
- .mode = this->mode,
- .reqid = this->reqid,
- .ipcomp = {
- .transform = this->ipcomp,
- },
- }, other_sa = my_sa;
-
- my_sa.ipcomp.cpi = this->my_cpi;
- other_sa.ipcomp.cpi = this->other_cpi;
-
- if (this->protocol == PROTO_ESP)
- {
- my_sa.esp.use = TRUE;
- my_sa.esp.spi = this->my_spi;
- other_sa.esp.use = TRUE;
- other_sa.esp.spi = this->other_spi;
- }
- else
- {
- my_sa.ah.use = TRUE;
- my_sa.ah.spi = this->my_spi;
- other_sa.ah.use = TRUE;
- other_sa.ah.spi = this->other_spi;
- }
+ ipsec_sa_cfg_t my_sa, other_sa;
+
+ prepare_sa_cfg(this, &my_sa, &other_sa);
/* if we're not in state CHILD_INSTALLING (i.e. if there is no SAD
* entry) we install a trap policy */
@@ -896,14 +926,6 @@ METHOD(child_sa_t, add_policies, status_t,
priority = this->trap ? POLICY_PRIORITY_ROUTED
: POLICY_PRIORITY_DEFAULT;
- enumerator = create_policy_enumerator(this);
- while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
- {
- my_sa.policy_count++;
- other_sa.policy_count++;
- }
- enumerator->destroy(enumerator);
-
/* enumerate pairs of traffic selectors */
enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
@@ -1006,47 +1028,24 @@ METHOD(child_sa_t, update, status_t,
if (this->config->install_policy(this->config) && require_policy_update())
{
- ipsec_sa_cfg_t my_sa = {
- .mode = this->mode,
- .reqid = this->reqid,
- .ipcomp = {
- .transform = this->ipcomp,
- },
- }, other_sa = my_sa;
-
- my_sa.ipcomp.cpi = this->my_cpi;
- other_sa.ipcomp.cpi = this->other_cpi;
-
- if (this->protocol == PROTO_ESP)
- {
- my_sa.esp.use = TRUE;
- my_sa.esp.spi = this->my_spi;
- other_sa.esp.use = TRUE;
- other_sa.esp.spi = this->other_spi;
- }
- else
- {
- my_sa.ah.use = TRUE;
- my_sa.ah.spi = this->my_spi;
- other_sa.ah.use = TRUE;
- other_sa.ah.spi = this->other_spi;
- }
-
- /* update policies */
if (!me->ip_equals(me, this->my_addr) ||
!other->ip_equals(other, this->other_addr))
{
+ ipsec_sa_cfg_t my_sa, other_sa;
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
+ prepare_sa_cfg(this, &my_sa, &other_sa);
+
/* always use high priorities, as hosts getting updated are INSTALLED */
enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
traffic_selector_t *old_my_ts = NULL, *old_other_ts = NULL;
/* remove old policies first */
- del_policies_internal(this, my_ts, other_ts,
- POLICY_PRIORITY_DEFAULT);
+ del_policies_internal(this, this->my_addr, this->other_addr,
+ my_ts, other_ts, &my_sa, &other_sa,
+ POLICY_IPSEC, POLICY_PRIORITY_DEFAULT);
/* check if we have to update a "dynamic" traffic selector */
if (!me->ip_equals(me, this->my_addr) &&
@@ -1068,21 +1067,20 @@ METHOD(child_sa_t, update, status_t,
/* reinstall updated policies */
install_policies_internal(this, me, other, my_ts, other_ts,
- &my_sa, &other_sa, POLICY_IPSEC,
- POLICY_PRIORITY_DEFAULT);
+ &my_sa, &other_sa, POLICY_IPSEC,
+ POLICY_PRIORITY_DEFAULT);
/* update fallback policies after the new policy is in place */
- if (old_my_ts || old_other_ts)
- {
- del_policies_internal(this, old_my_ts ?: my_ts,
- old_other_ts ?: other_ts,
+ del_policies_internal(this, this->my_addr, this->other_addr,
+ old_my_ts ?: my_ts,
+ old_other_ts ?: other_ts,
+ &my_sa, &other_sa, POLICY_DROP,
+ POLICY_PRIORITY_FALLBACK);
+ install_policies_internal(this, me, other, my_ts, other_ts,
+ &my_sa, &other_sa, POLICY_DROP,
POLICY_PRIORITY_FALLBACK);
- install_policies_internal(this, me, other, my_ts, other_ts,
- &my_sa, &other_sa, POLICY_DROP,
- POLICY_PRIORITY_FALLBACK);
- DESTROY_IF(old_my_ts);
- DESTROY_IF(old_other_ts);
- }
+ DESTROY_IF(old_my_ts);
+ DESTROY_IF(old_other_ts);
}
enumerator->destroy(enumerator);
}
@@ -1122,15 +1120,21 @@ METHOD(child_sa_t, destroy, void,
if (this->config->install_policy(this->config))
{
+ ipsec_sa_cfg_t my_sa, other_sa;
+
+ prepare_sa_cfg(this, &my_sa, &other_sa);
+
/* delete all policies in the kernel */
enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
- del_policies_internal(this, my_ts, other_ts, priority);
+ del_policies_internal(this, this->my_addr, this->other_addr,
+ my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, priority);
if (priority == POLICY_PRIORITY_DEFAULT && require_policy_update())
{
- del_policies_internal(this, my_ts, other_ts,
- POLICY_PRIORITY_FALLBACK);
+ del_policies_internal(this, this->my_addr, this->other_addr,
+ my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP,
+ POLICY_PRIORITY_FALLBACK);
}
}
enumerator->destroy(enumerator);
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 37d69874d..4625df5b8 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -394,9 +394,17 @@ struct private_ike_sa_manager_t {
rng_t *rng;
/**
- * Lock to access the RNG instance
+ * Registered callback for IKE SPIs
*/
- rwlock_t *rng_lock;
+ struct {
+ spi_cb_t cb;
+ void *data;
+ } spi_cb;
+
+ /**
+ * Lock to access the RNG instance and the callback
+ */
+ rwlock_t *spi_lock;
/**
* reuse existing IKE_SAs in checkout_by_config
@@ -971,13 +979,17 @@ static u_int64_t get_spi(private_ike_sa_manager_t *this)
{
u_int64_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))
+ this->spi_lock->read_lock(this->spi_lock);
+ if (this->spi_cb.cb)
+ {
+ spi = this->spi_cb.cb(this->spi_cb.data);
+ }
+ else if (!this->rng ||
+ !this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi))
{
spi = 0;
}
- this->rng_lock->unlock(this->rng_lock);
+ this->spi_lock->unlock(this->spi_lock);
return spi;
}
@@ -1188,11 +1200,15 @@ METHOD(ike_sa_manager_t, checkout_new, ike_sa_t*,
*/
static u_int32_t get_message_id_or_hash(message_t *message)
{
- /* Use the message ID, or the message hash in IKEv1 Main/Aggressive mode */
- if (message->get_major_version(message) == IKEV1_MAJOR_VERSION &&
- message->get_message_id(message) == 0)
+ if (message->get_major_version(message) == IKEV1_MAJOR_VERSION)
{
- return chunk_hash(message->get_packet_data(message));
+ /* Use a hash for IKEv1 Phase 1, where we don't have a MID, and Quick
+ * Mode, where all three messages use the same message ID */
+ if (message->get_message_id(message) == 0 ||
+ message->get_exchange_type(message) == QUICK_MODE)
+ {
+ return chunk_hash(message->get_packet_data(message));
+ }
}
return message->get_message_id(message);
}
@@ -1384,7 +1400,8 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
continue;
}
if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING)
- { /* skip IKE_SAs which are not usable */
+ { /* skip IKE_SAs which are not usable, wake other waiting threads */
+ entry->condvar->signal(entry->condvar);
continue;
}
@@ -1402,6 +1419,8 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
break;
}
}
+ /* other threads might be waiting for this entry */
+ entry->condvar->signal(entry->condvar);
}
enumerator->destroy(enumerator);
@@ -1434,6 +1453,8 @@ METHOD(ike_sa_manager_t, checkout_by_id, ike_sa_t*,
entry->checked_out = TRUE;
break;
}
+ /* other threads might be waiting for this entry */
+ entry->condvar->signal(entry->condvar);
}
}
enumerator->destroy(enumerator);
@@ -1490,6 +1511,8 @@ METHOD(ike_sa_manager_t, checkout_by_name, ike_sa_t*,
ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
break;
}
+ /* other threads might be waiting for this entry */
+ entry->condvar->signal(entry->condvar);
}
}
enumerator->destroy(enumerator);
@@ -1628,8 +1651,27 @@ METHOD(ike_sa_manager_t, checkin, void,
* delete any existing IKE_SAs with that peer. */
if (ike_sa->has_condition(ike_sa, COND_INIT_CONTACT_SEEN))
{
+ /* We can't hold the segment locked while checking the
+ * uniqueness as this could lead to deadlocks. We mark the
+ * entry as checked out while we release the lock so no other
+ * thread can acquire it. Since it is not yet in the list of
+ * connected peers that will not cause a deadlock as no other
+ * caller of check_unqiueness() will try to check out this SA */
+ entry->checked_out = TRUE;
+ unlock_single_segment(this, segment);
+
this->public.check_uniqueness(&this->public, ike_sa, TRUE);
ike_sa->set_condition(ike_sa, COND_INIT_CONTACT_SEEN, FALSE);
+
+ /* The entry could have been modified in the mean time, e.g.
+ * because another SA was added/removed next to it or another
+ * thread is waiting, but it should still exist, so there is no
+ * need for a lookup via get_entry_by... */
+ lock_single_segment(this, segment);
+ entry->checked_out = FALSE;
+ /* We already signaled waiting threads above, we have to do that
+ * again after checking the SA out and back in again. */
+ entry->condvar->signal(entry->condvar);
}
}
@@ -2010,6 +2052,15 @@ METHOD(ike_sa_manager_t, get_half_open_count, u_int,
return count;
}
+METHOD(ike_sa_manager_t, set_spi_cb, void,
+ private_ike_sa_manager_t *this, spi_cb_t callback, void *data)
+{
+ this->spi_lock->write_lock(this->spi_lock);
+ this->spi_cb.cb = callback;
+ this->spi_cb.data = data;
+ this->spi_lock->unlock(this->spi_lock);
+}
+
METHOD(ike_sa_manager_t, flush, void,
private_ike_sa_manager_t *this)
{
@@ -2092,10 +2143,12 @@ 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->spi_lock->write_lock(this->spi_lock);
this->rng->destroy(this->rng);
this->rng = NULL;
- this->rng_lock->unlock(this->rng_lock);
+ this->spi_cb.cb = NULL;
+ this->spi_cb.data = NULL;
+ this->spi_lock->unlock(this->spi_lock);
}
METHOD(ike_sa_manager_t, destroy, void,
@@ -2120,7 +2173,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);
+ this->spi_lock->destroy(this->spi_lock);
free(this);
}
@@ -2167,6 +2220,7 @@ ike_sa_manager_t *ike_sa_manager_create()
.get_count = _get_count,
.get_half_open_count = _get_half_open_count,
.flush = _flush,
+ .set_spi_cb = _set_spi_cb,
.destroy = _destroy,
},
);
@@ -2178,7 +2232,7 @@ ike_sa_manager_t *ike_sa_manager_create()
free(this);
return NULL;
}
- this->rng_lock = rwlock_create(RWLOCK_TYPE_DEFAULT);
+ this->spi_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 3ea928ea5..f1b7c2579 100644
--- a/src/libcharon/sa/ike_sa_manager.h
+++ b/src/libcharon/sa/ike_sa_manager.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Tobias Brunner
+ * Copyright (C) 2008-2015 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -31,6 +31,16 @@ typedef struct ike_sa_manager_t ike_sa_manager_t;
#include <config/peer_cfg.h>
/**
+ * Callback called to generate an IKE SPI.
+ *
+ * This may be called from multiple threads concurrently.
+ *
+ * @param data data supplied during registration of the callback
+ * @return allocated SPI, 0 on failure
+ */
+typedef u_int64_t (*spi_cb_t)(void *data);
+
+/**
* Manages and synchronizes access to all IKE_SAs.
*
* To synchronize access to thread-unsave IKE_SAs, they are checked out for
@@ -227,6 +237,15 @@ struct ike_sa_manager_t {
bool responder_only);
/**
+ * Set the callback to generate IKE SPIs
+ *
+ * @param callback callback to register
+ * @param data data provided to callback
+ */
+ void (*set_spi_cb)(ike_sa_manager_t *this, spi_cb_t callback,
+ void *data);
+
+ /**
* Delete all existing IKE_SAs and destroy them immediately.
*
* Threads will be driven out, so all SAs can be deleted cleanly.
diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c
index f5a91dbeb..e428966ad 100644
--- a/src/libcharon/sa/ikev1/keymat_v1.c
+++ b/src/libcharon/sa/ikev1/keymat_v1.c
@@ -23,14 +23,9 @@
typedef struct private_keymat_v1_t private_keymat_v1_t;
/**
- * Max. number of IVs to track.
+ * Max. number of IVs/QMs to track.
*/
-#define MAX_IV 3
-
-/**
- * Max. number of Quick Modes to track.
- */
-#define MAX_QM 2
+#define MAX_EXCHANGES_DEFAULT 3
/**
* Data stored for IVs
@@ -110,6 +105,11 @@ struct private_keymat_v1_t {
* of QMs are tracked at the same time. Stores qm_data_t objects.
*/
linked_list_t *qms;
+
+ /**
+ * Max. number of IVs/Quick Modes to track.
+ */
+ int max_exchanges;
};
@@ -874,7 +874,7 @@ static qm_data_t *lookup_quick_mode(private_keymat_v1_t *this, u_int32_t mid)
}
this->qms->insert_first(this->qms, found);
/* remove least recently used state if maximum reached */
- if (this->qms->get_count(this->qms) > MAX_QM &&
+ if (this->qms->get_count(this->qms) > this->max_exchanges &&
this->qms->remove_last(this->qms, (void**)&qm) == SUCCESS)
{
qm_data_destroy(qm);
@@ -1048,7 +1048,7 @@ static iv_data_t *lookup_iv(private_keymat_v1_t *this, u_int32_t mid)
}
this->ivs->insert_first(this->ivs, found);
/* remove least recently used IV if maximum reached */
- if (this->ivs->get_count(this->ivs) > MAX_IV &&
+ if (this->ivs->get_count(this->ivs) > this->max_exchanges &&
this->ivs->remove_last(this->ivs, (void**)&iv) == SUCCESS)
{
iv_data_destroy(iv);
@@ -1163,6 +1163,8 @@ keymat_v1_t *keymat_v1_create(bool initiator)
.ivs = linked_list_create(),
.qms = linked_list_create(),
.initiator = initiator,
+ .max_exchanges = lib->settings->get_int(lib->settings,
+ "%s.max_ikev1_exchanges", MAX_EXCHANGES_DEFAULT, lib->ns),
);
return &this->public;
diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c
index 678f99df1..3c601a4fa 100644
--- a/src/libcharon/sa/ikev1/task_manager_v1.c
+++ b/src/libcharon/sa/ikev1/task_manager_v1.c
@@ -752,6 +752,12 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
case ALREADY_DONE:
cancelled = TRUE;
break;
+ case INVALID_ARG:
+ if (task->get_type(task) == TASK_QUICK_MODE)
+ { /* not responsible for this exchange */
+ continue;
+ }
+ /* FALL */
case FAILED:
default:
charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
@@ -929,6 +935,28 @@ static bool have_quick_mode_task(private_task_manager_t *this, u_int32_t mid)
}
/**
+ * Check if we still have an aggressive mode task queued
+ */
+static bool have_aggressive_mode_task(private_task_manager_t *this)
+{
+ enumerator_t *enumerator;
+ 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_AGGRESSIVE_MODE)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return found;
+}
+
+/**
* handle an incoming request message
*/
static status_t process_request(private_task_manager_t *this,
@@ -1034,6 +1062,12 @@ static status_t process_request(private_task_manager_t *this,
case ALREADY_DONE:
send_response = FALSE;
break;
+ case INVALID_ARG:
+ if (task->get_type(task) == TASK_QUICK_MODE)
+ { /* not responsible for this exchange */
+ continue;
+ }
+ /* FALL */
case FAILED:
default:
charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
@@ -1061,6 +1095,22 @@ static status_t process_request(private_task_manager_t *this,
* the same message again. */
clear_packets(this->responding.packets);
}
+ if (this->queued &&
+ this->queued->get_exchange_type(this->queued) == INFORMATIONAL_V1)
+ {
+ message_t *queued;
+ status_t status;
+
+ queued = this->queued;
+ this->queued = NULL;
+ status = this->public.task_manager.process_message(
+ &this->public.task_manager, queued);
+ queued->destroy(queued);
+ if (status == DESTROY_ME)
+ {
+ return status;
+ }
+ }
if (this->passive_tasks->get_count(this->passive_tasks) == 0 &&
this->queued_tasks->get_count(this->queued_tasks) > 0)
{
@@ -1133,7 +1183,8 @@ static status_t process_response(private_task_manager_t *this,
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
clear_packets(this->initiating.packets);
- if (this->queued && this->active_tasks->get_count(this->active_tasks) == 0)
+ if (this->queued && !this->active_tasks->get_count(this->active_tasks) &&
+ this->queued->get_exchange_type(this->queued) == TRANSACTION)
{
queued = this->queued;
this->queued = NULL;
@@ -1228,6 +1279,29 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg)
return status;
}
+/**
+ * Queue the given message if possible
+ */
+static status_t queue_message(private_task_manager_t *this, message_t *msg)
+{
+ if (this->queued)
+ {
+ DBG1(DBG_IKE, "ignoring %N request, queue full",
+ exchange_type_names, msg->get_exchange_type(msg));
+ return FAILED;
+ }
+ this->queued = message_create_from_packet(msg->get_packet(msg));
+ if (this->queued->parse_header(this->queued) != SUCCESS)
+ {
+ this->queued->destroy(this->queued);
+ this->queued = NULL;
+ return FAILED;
+ }
+ DBG1(DBG_IKE, "queueing %N request as tasks still active",
+ exchange_type_names, msg->get_exchange_type(msg));
+ return SUCCESS;
+}
+
METHOD(task_manager_t, process_message, status_t,
private_task_manager_t *this, message_t *msg)
{
@@ -1328,25 +1402,29 @@ METHOD(task_manager_t, process_message, status_t,
}
}
- if (msg->get_exchange_type(msg) == TRANSACTION &&
- this->active_tasks->get_count(this->active_tasks))
- { /* main mode not yet complete, queue XAuth/Mode config tasks */
- if (this->queued)
+ /* drop XAuth/Mode Config/Quick Mode messages until we received the last
+ * Aggressive Mode message. since Informational messages are not
+ * retransmitted we queue them. */
+ if (have_aggressive_mode_task(this))
+ {
+ if (msg->get_exchange_type(msg) == INFORMATIONAL_V1)
{
- DBG1(DBG_IKE, "ignoring additional %N request, queue full",
- exchange_type_names, TRANSACTION);
- return SUCCESS;
+ return queue_message(this, msg);
}
- this->queued = message_create_from_packet(msg->get_packet(msg));
- if (this->queued->parse_header(this->queued) != SUCCESS)
+ else if (msg->get_exchange_type(msg) != AGGRESSIVE)
{
- this->queued->destroy(this->queued);
- this->queued = NULL;
+ DBG1(DBG_IKE, "ignoring %N request while phase 1 is incomplete",
+ exchange_type_names, msg->get_exchange_type(msg));
return FAILED;
}
- DBG1(DBG_IKE, "queueing %N request as tasks still active",
- exchange_type_names, TRANSACTION);
- return SUCCESS;
+ }
+
+ /* queue XAuth/Mode Config messages unless the Main Mode exchange we
+ * initiated is complete */
+ if (msg->get_exchange_type(msg) == TRANSACTION &&
+ this->active_tasks->get_count(this->active_tasks))
+ {
+ return queue_message(this, msg);
}
msg->set_request(msg, TRUE);
@@ -1724,6 +1802,8 @@ METHOD(task_manager_t, queue_dpd, void,
pow(this->retransmit_base, retransmit));
}
}
+ /* compensate for the already elapsed dpd delay */
+ t -= 1000 * peer_cfg->get_dpd(peer_cfg);
/* schedule DPD timeout job */
lib->scheduler->schedule_job_ms(lib->scheduler,
diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c
index d0994a961..a03477e18 100644
--- a/src/libcharon/sa/ikev1/tasks/mode_config.c
+++ b/src/libcharon/sa/ikev1/tasks/mode_config.c
@@ -482,7 +482,9 @@ static host_t *assign_migrated_vip(linked_list_t *migrated, host_t *requested)
enumerator = migrated->create_enumerator(migrated);
while (enumerator->enumerate(enumerator, &vip))
{
- if (vip->ip_equals(vip, requested))
+ if (vip->ip_equals(vip, requested) ||
+ (requested->is_anyaddr(requested) &&
+ requested->get_family(requested) == vip->get_family(vip)))
{
migrated->remove_at(migrated, enumerator);
found = vip;
diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.c b/src/libcharon/sa/ikev1/tasks/quick_delete.c
index 1b95a8b11..ade59a2dd 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_delete.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_delete.c
@@ -115,7 +115,7 @@ static bool delete_child(private_quick_delete_t *this, protocol_id_t protocol,
if (this->expired)
{
DBG0(DBG_IKE, "closing expired CHILD_SA %s{%d} "
- "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
+ "with SPIs %.8x_i %.8x_o and TS %#R === %#R",
child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa),
ntohl(child_sa->get_spi(child_sa, TRUE)),
ntohl(child_sa->get_spi(child_sa, FALSE)), my_ts, other_ts);
@@ -126,7 +126,7 @@ static bool delete_child(private_quick_delete_t *this, protocol_id_t protocol,
child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, NULL);
DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs "
- "%.8x_i (%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R",
+ "%.8x_i (%llu bytes) %.8x_o (%llu bytes) and TS %#R === %#R",
child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa),
ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in,
ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out,
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
index d6a3f2cd1..e7d26443b 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
@@ -388,7 +388,7 @@ static bool install(private_quick_mode_t *this)
this->child_sa->create_ts_enumerator(this->child_sa, FALSE));
DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
- "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
+ "with SPIs %.8x_i %.8x_o and TS %#R === %#R",
this->child_sa->get_name(this->child_sa),
this->child_sa->get_unique_id(this->child_sa),
ntohl(this->child_sa->get_spi(this->child_sa, TRUE)),
@@ -1026,7 +1026,7 @@ METHOD(task_t, process_r, status_t,
{
if (this->mid && this->mid != message->get_message_id(message))
{ /* not responsible for this quick mode exchange */
- return NEED_MORE;
+ return INVALID_ARG;
}
switch (this->state)
@@ -1200,7 +1200,7 @@ METHOD(task_t, build_r, status_t,
{
if (this->mid && this->mid != message->get_message_id(message))
{ /* not responsible for this quick mode exchange */
- return NEED_MORE;
+ return INVALID_ARG;
}
switch (this->state)
diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c
index a770e90ff..c0c91574c 100644
--- a/src/libcharon/sa/ikev1/tasks/xauth.c
+++ b/src/libcharon/sa/ikev1/tasks/xauth.c
@@ -271,7 +271,10 @@ static bool add_auth_cfg(private_xauth_t *this, identification_t *id, bool local
auth = auth_cfg_create();
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_XAUTH);
- auth->add(auth, AUTH_RULE_XAUTH_IDENTITY, id->clone(id));
+ if (id)
+ {
+ auth->add(auth, AUTH_RULE_XAUTH_IDENTITY, id->clone(id));
+ }
auth->merge(auth, this->ike_sa->get_auth_cfg(this->ike_sa, local), FALSE);
this->ike_sa->add_auth_cfg(this->ike_sa, local, auth);
@@ -342,7 +345,10 @@ METHOD(task_t, build_i, status_t,
break;
case SUCCESS:
DESTROY_IF(cp);
- this->status = XAUTH_OK;
+ if (add_auth_cfg(this, NULL, FALSE) && allowed(this))
+ {
+ this->status = XAUTH_OK;
+ }
this->public.task.process = _process_i_status;
return build_i_status(this, message);
default:
diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c
index fce0840e3..55cb5dd9c 100644
--- a/src/libcharon/sa/ikev2/keymat_v2.c
+++ b/src/libcharon/sa/ikev2/keymat_v2.c
@@ -527,6 +527,7 @@ METHOD(keymat_v2_t, derive_child_keys, bool,
case ENCR_AES_GCM_ICV12:
case ENCR_AES_GCM_ICV16:
case ENCR_AES_CTR:
+ case ENCR_CAMELLIA_CTR:
case ENCR_NULL_AUTH_AES_GMAC:
case ENCR_CHACHA20_POLY1305:
enc_size += 4;
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c
index e08f3dab1..97f73d851 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.c
+++ b/src/libcharon/sa/ikev2/tasks/child_create.c
@@ -712,7 +712,7 @@ static status_t select_and_install(private_child_create_t *this,
this->child_sa->create_ts_enumerator(this->child_sa, FALSE));
DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
- "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
+ "with SPIs %.8x_i %.8x_o and TS %#R === %#R",
this->child_sa->get_name(this->child_sa),
this->child_sa->get_unique_id(this->child_sa),
ntohl(this->child_sa->get_spi(this->child_sa, TRUE)),
@@ -1245,7 +1245,7 @@ METHOD(task_t, build_r, status_t,
}
if (this->config == NULL)
{
- DBG1(DBG_IKE, "traffic selectors %#R=== %#R inacceptable",
+ DBG1(DBG_IKE, "traffic selectors %#R === %#R inacceptable",
this->tsr, this->tsi);
charon->bus->alert(charon->bus, ALERT_TS_MISMATCH, this->tsi, this->tsr);
message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty);
diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c
index f0b11e291..877ae0531 100644
--- a/src/libcharon/sa/ikev2/tasks/child_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.c
@@ -266,7 +266,7 @@ static void log_children(private_child_delete_t *this)
if (this->expired)
{
DBG0(DBG_IKE, "closing expired CHILD_SA %s{%d} "
- "with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
+ "with SPIs %.8x_i %.8x_o and TS %#R === %#R",
child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa),
ntohl(child_sa->get_spi(child_sa, TRUE)),
ntohl(child_sa->get_spi(child_sa, FALSE)), my_ts, other_ts);
@@ -277,7 +277,7 @@ static void log_children(private_child_delete_t *this)
child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, NULL);
DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs %.8x_i "
- "(%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R",
+ "(%llu bytes) %.8x_o (%llu bytes) and TS %#R === %#R",
child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa),
ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in,
ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out,
diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
index 11b0bb281..cbdc5e797 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
@@ -339,7 +339,11 @@ METHOD(ike_mobike_t, transmit, bool,
{
if (me->ip_equals(me, me_old))
{
- charon->sender->send(charon->sender, packet->clone(packet));
+ copy = packet->clone(packet);
+ /* hosts might have been updated by a peer's MOBIKE exchange */
+ copy->set_source(copy, me_old->clone(me_old));
+ copy->set_destination(copy, other_old->clone(other_old));
+ charon->sender->send(charon->sender, copy);
me->destroy(me);
return TRUE;
}
diff --git a/src/libcharon/sa/ikev2/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c
index 9e0eb68ce..dd34c1234 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_natd.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c
@@ -129,25 +129,6 @@ static chunk_t generate_natd_hash(private_ike_natd_t *this,
}
/**
- * build a faked NATD payload to enforce UDP encap
- */
-static chunk_t generate_natd_hash_faked(private_ike_natd_t *this)
-{
- rng_t *rng;
- chunk_t chunk;
-
- rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
- if (!rng || !rng->allocate_bytes(rng, HASH_SIZE_SHA1, &chunk))
- {
- DBG1(DBG_IKE, "unable to get random bytes for NATD fake");
- DESTROY_IF(rng);
- return chunk_empty;
- }
- rng->destroy(rng);
- return chunk;
-}
-
-/**
* Build a NAT detection notify payload.
*/
static notify_payload_t *build_natd_payload(private_ike_natd_t *this,
@@ -162,7 +143,14 @@ 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)
{
- hash = generate_natd_hash_faked(this);
+ u_int32_t addr;
+
+ /* chunk_hash() is randomly keyed so this produces a random IPv4 address
+ * that changes with every restart but otherwise stays the same */
+ addr = chunk_hash(chunk_from_chars(0x00, 0x00, 0x00, 0x00));
+ host = host_create_from_chunk(AF_INET, chunk_from_thing(addr), 0);
+ hash = generate_natd_hash(this, ike_sa_id, host);
+ host->destroy(host);
}
else
{
diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c
index 1a984435c..5231994c8 100644
--- a/src/libcharon/sa/shunt_manager.c
+++ b/src/libcharon/sa/shunt_manager.c
@@ -63,9 +63,9 @@ struct private_shunt_manager_t {
static bool install_shunt_policy(child_cfg_t *child)
{
enumerator_t *e_my_ts, *e_other_ts;
- linked_list_t *my_ts_list, *other_ts_list;
+ linked_list_t *my_ts_list, *other_ts_list, *hosts;
traffic_selector_t *my_ts, *other_ts;
- host_t *host_any;
+ host_t *host_any, *host_any6;
policy_type_t policy_type;
policy_priority_t policy_prio;
status_t status = SUCCESS;
@@ -85,9 +85,13 @@ static bool install_shunt_policy(child_cfg_t *child)
return FALSE;
}
- my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, NULL);
- other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, NULL);
host_any = host_create_any(AF_INET);
+ host_any6 = host_create_any(AF_INET6);
+
+ hosts = linked_list_create_with_items(host_any, host_any6, NULL);
+ my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts);
+ other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts);
+ hosts->destroy(hosts);
/* enumerate pairs of traffic selectors */
e_my_ts = my_ts_list->create_enumerator(my_ts_list);
@@ -96,6 +100,16 @@ static bool install_shunt_policy(child_cfg_t *child)
e_other_ts = other_ts_list->create_enumerator(other_ts_list);
while (e_other_ts->enumerate(e_other_ts, &other_ts))
{
+ if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
+ {
+ continue;
+ }
+ if (my_ts->get_protocol(my_ts) &&
+ other_ts->get_protocol(other_ts) &&
+ my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
+ {
+ continue;
+ }
/* install out policy */
status |= hydra->kernel_interface->add_policy(
hydra->kernel_interface, host_any, host_any,
@@ -125,6 +139,7 @@ static bool install_shunt_policy(child_cfg_t *child)
offsetof(traffic_selector_t, destroy));
other_ts_list->destroy_offset(other_ts_list,
offsetof(traffic_selector_t, destroy));
+ host_any6->destroy(host_any6);
host_any->destroy(host_any);
return status == SUCCESS;
@@ -185,25 +200,35 @@ METHOD(shunt_manager_t, install, bool,
static void uninstall_shunt_policy(child_cfg_t *child)
{
enumerator_t *e_my_ts, *e_other_ts;
- linked_list_t *my_ts_list, *other_ts_list;
+ linked_list_t *my_ts_list, *other_ts_list, *hosts;
traffic_selector_t *my_ts, *other_ts;
+ host_t *host_any, *host_any6;
+ policy_type_t policy_type;
policy_priority_t policy_prio;
status_t status = SUCCESS;
+ ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT };
switch (child->get_mode(child))
{
case MODE_PASS:
+ policy_type = POLICY_PASS;
policy_prio = POLICY_PRIORITY_PASS;
break;
case MODE_DROP:
+ policy_type = POLICY_DROP;
policy_prio = POLICY_PRIORITY_FALLBACK;
break;
default:
return;
}
- my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, NULL);
- other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, NULL);
+ host_any = host_create_any(AF_INET);
+ host_any6 = host_create_any(AF_INET6);
+
+ hosts = linked_list_create_with_items(host_any, host_any6, NULL);
+ my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts);
+ other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts);
+ hosts->destroy(hosts);
/* enumerate pairs of traffic selectors */
e_my_ts = my_ts_list->create_enumerator(my_ts_list);
@@ -212,22 +237,35 @@ static void uninstall_shunt_policy(child_cfg_t *child)
e_other_ts = other_ts_list->create_enumerator(other_ts_list);
while (e_other_ts->enumerate(e_other_ts, &other_ts))
{
+ if (my_ts->get_type(my_ts) != other_ts->get_type(other_ts))
+ {
+ continue;
+ }
+ if (my_ts->get_protocol(my_ts) &&
+ other_ts->get_protocol(other_ts) &&
+ my_ts->get_protocol(my_ts) != other_ts->get_protocol(other_ts))
+ {
+ continue;
+ }
/* uninstall out policy */
status |= hydra->kernel_interface->del_policy(
- hydra->kernel_interface, my_ts, other_ts,
- POLICY_OUT, 0, child->get_mark(child, FALSE),
+ hydra->kernel_interface, host_any, host_any,
+ my_ts, other_ts, POLICY_OUT, policy_type,
+ &sa, child->get_mark(child, FALSE),
policy_prio);
/* uninstall in policy */
status |= hydra->kernel_interface->del_policy(
- hydra->kernel_interface, other_ts, my_ts,
- POLICY_IN, 0, child->get_mark(child, TRUE),
+ hydra->kernel_interface, host_any, host_any,
+ other_ts, my_ts, POLICY_IN, policy_type,
+ &sa, child->get_mark(child, TRUE),
policy_prio);
/* uninstall forward policy */
status |= hydra->kernel_interface->del_policy(
- hydra->kernel_interface, other_ts, my_ts,
- POLICY_FWD, 0, child->get_mark(child, TRUE),
+ hydra->kernel_interface, host_any, host_any,
+ other_ts, my_ts, POLICY_FWD, policy_type,
+ &sa, child->get_mark(child, TRUE),
policy_prio);
}
e_other_ts->destroy(e_other_ts);
@@ -238,6 +276,8 @@ static void uninstall_shunt_policy(child_cfg_t *child)
offsetof(traffic_selector_t, destroy));
other_ts_list->destroy_offset(other_ts_list,
offsetof(traffic_selector_t, destroy));
+ host_any6->destroy(host_any6);
+ host_any->destroy(host_any);
if (status != SUCCESS)
{
diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c
index 63505c960..90ad7e40e 100644
--- a/src/libcharon/sa/trap_manager.c
+++ b/src/libcharon/sa/trap_manager.c
@@ -211,6 +211,7 @@ METHOD(trap_manager_t, install, u_int32_t,
if (this->installing == INSTALL_DISABLED)
{ /* flush() has been called */
this->lock->unlock(this->lock);
+ other->destroy(other);
me->destroy(me);
return 0;
}
@@ -235,6 +236,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);
+ other->destroy(other);
me->destroy(me);
return 0;
}