summaryrefslogtreecommitdiff
path: root/src/libcharon/sa
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@corsac.net>2017-05-30 20:59:31 +0200
committerYves-Alexis Perez <corsac@corsac.net>2017-05-30 20:59:31 +0200
commitbba25e2ff6c4a193acb54560ea4417537bd2954e (patch)
tree9e074fe343f9ab6f5ce1e9c5142d9a6cf180fcda /src/libcharon/sa
parent05ddd767992d68bb38c7f16ece142e8c2e9ae016 (diff)
downloadvyos-strongswan-bba25e2ff6c4a193acb54560ea4417537bd2954e.tar.gz
vyos-strongswan-bba25e2ff6c4a193acb54560ea4417537bd2954e.zip
New upstream version 5.5.3
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r--src/libcharon/sa/child_sa.c560
-rw-r--r--src/libcharon/sa/child_sa.h120
-rw-r--r--src/libcharon/sa/eap/eap_manager.c53
-rw-r--r--src/libcharon/sa/ike_sa.c57
-rw-r--r--src/libcharon/sa/ike_sa_manager.c121
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c31
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c37
-rw-r--r--src/libcharon/sa/ikev2/connect_manager.c164
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.c47
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c96
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_delete.c229
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_rekey.c16
-rw-r--r--src/libcharon/sa/shunt_manager.c22
-rw-r--r--src/libcharon/sa/task_manager.c32
-rw-r--r--src/libcharon/sa/task_manager.h16
-rw-r--r--src/libcharon/sa/trap_manager.c72
16 files changed, 1204 insertions, 469 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index b9dd59b07..3d9f6133b 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -1,6 +1,6 @@
/*
+ * Copyright (C) 2006-2017 Tobias Brunner
* Copyright (C) 2016 Andreas Steffen
- * Copyright (C) 2006-2016 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005 Jan Hutter
@@ -40,6 +40,12 @@ ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING,
"DESTROYING",
);
+ENUM(child_sa_outbound_state_names, CHILD_OUTBOUND_NONE, CHILD_OUTBOUND_INSTALLED,
+ "NONE",
+ "REGISTERED",
+ "INSTALLED",
+);
+
typedef struct private_child_sa_t private_child_sa_t;
/**
@@ -92,6 +98,31 @@ struct private_child_sa_t {
array_t *other_ts;
/**
+ * Outbound encryption key cached during a rekeying
+ */
+ chunk_t encr_r;
+
+ /**
+ * Outbound integrity key cached during a rekeying
+ */
+ chunk_t integ_r;
+
+ /**
+ * Whether the outbound SA has only been registered yet during a rekeying
+ */
+ child_sa_outbound_state_t outbound_state;
+
+ /**
+ * Whether the peer supports TFCv3
+ */
+ bool tfcv3;
+
+ /**
+ * The outbound SPI of the CHILD_SA that replaced this one during a rekeying
+ */
+ uint32_t rekey_spi;
+
+ /**
* Protocol used to protect this SA, ESP|AH
*/
protocol_id_t protocol;
@@ -265,6 +296,10 @@ METHOD(child_sa_t, get_config, child_cfg_t*,
METHOD(child_sa_t, set_state, void,
private_child_sa_t *this, child_sa_state_t state)
{
+ DBG2(DBG_CHD, "CHILD_SA %s{%d} state change: %N => %N",
+ get_name(this), this->unique_id,
+ child_sa_state_names, this->state,
+ child_sa_state_names, state);
charon->bus->child_state_change(charon->bus, &this->public, state);
this->state = state;
}
@@ -275,6 +310,12 @@ METHOD(child_sa_t, get_state, child_sa_state_t,
return this->state;
}
+METHOD(child_sa_t, get_outbound_state, child_sa_outbound_state_t,
+ private_child_sa_t *this)
+{
+ return this->outbound_state;
+}
+
METHOD(child_sa_t, get_spi, uint32_t,
private_child_sa_t *this, bool inbound)
{
@@ -394,10 +435,11 @@ struct policy_enumerator_t {
};
METHOD(enumerator_t, policy_enumerate, bool,
- policy_enumerator_t *this, traffic_selector_t **my_out,
- traffic_selector_t **other_out)
+ policy_enumerator_t *this, va_list args)
{
- traffic_selector_t *other_ts;
+ traffic_selector_t *other_ts, **my_out, **other_out;
+
+ VA_ARGS_VGET(args, my_out, other_out);
while (this->ts || this->mine->enumerate(this->mine, &this->ts))
{
@@ -446,7 +488,8 @@ METHOD(child_sa_t, create_policy_enumerator, enumerator_t*,
INIT(e,
.public = {
- .enumerate = (void*)_policy_enumerate,
+ .enumerate = enumerator_enumerate_default,
+ .venumerate = _policy_enumerate,
.destroy = _policy_destroy,
},
.mine = array_create_enumerator(this->my_ts),
@@ -504,7 +547,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
}
else
{
- if (this->other_spi)
+ if (this->other_spi && this->outbound_state == CHILD_OUTBOUND_INSTALLED)
{
kernel_ipsec_sa_id_t id = {
.src = this->my_addr,
@@ -691,14 +734,16 @@ METHOD(child_sa_t, alloc_cpi, uint16_t,
return 0;
}
-METHOD(child_sa_t, install, status_t,
- private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi,
- uint16_t cpi, bool initiator, bool inbound, bool tfcv3,
- linked_list_t *my_ts, linked_list_t *other_ts)
+/**
+ * Install the given SA in the kernel
+ */
+static status_t install_internal(private_child_sa_t *this, chunk_t encr,
+ chunk_t integ, uint32_t spi, uint16_t cpi, bool initiator, bool inbound,
+ bool tfcv3)
{
uint16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size;
uint16_t esn = NO_EXT_SEQ_NUMBERS;
- linked_list_t *src_ts = NULL, *dst_ts = NULL;
+ linked_list_t *my_ts, *other_ts, *src_ts, *dst_ts;
time_t now;
kernel_ipsec_sa_id_t id;
kernel_ipsec_add_sa_t sa;
@@ -708,6 +753,12 @@ METHOD(child_sa_t, install, status_t,
status_t status;
bool update = FALSE;
+ /* BEET requires the bound address from the traffic selectors */
+ my_ts = linked_list_create_from_enumerator(
+ array_create_enumerator(this->my_ts));
+ other_ts = linked_list_create_from_enumerator(
+ array_create_enumerator(this->other_ts));
+
/* now we have to decide which spi to use. Use self allocated, if "in",
* or the one in the proposal, if not "in" (others). Additionally,
* source and dest host switch depending on the role */
@@ -721,6 +772,8 @@ METHOD(child_sa_t, install, status_t,
}
this->my_spi = spi;
this->my_cpi = cpi;
+ dst_ts = my_ts;
+ src_ts = other_ts;
}
else
{
@@ -728,11 +781,14 @@ METHOD(child_sa_t, install, status_t,
dst = this->other_addr;
this->other_spi = spi;
this->other_cpi = cpi;
+ src_ts = my_ts;
+ dst_ts = other_ts;
if (tfcv3)
{
tfc = this->config->get_tfc(this->config);
}
+ this->outbound_state = CHILD_OUTBOUND_INSTALLED;
}
DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound",
@@ -748,12 +804,22 @@ METHOD(child_sa_t, install, status_t,
this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS,
&esn, NULL);
+ if (int_alg == AUTH_HMAC_SHA2_256_128 &&
+ this->config->has_option(this->config, OPT_SHA256_96))
+ {
+ DBG2(DBG_CHD, " using %N with 96-bit truncation",
+ integrity_algorithm_names, int_alg);
+ int_alg = AUTH_HMAC_SHA2_256_96;
+ }
+
if (!this->reqid_allocated && !this->static_reqid)
{
status = charon->kernel->alloc_reqid(charon->kernel, my_ts, other_ts,
this->mark_in, this->mark_out, &this->reqid);
if (status != SUCCESS)
{
+ my_ts->destroy(my_ts);
+ other_ts->destroy(other_ts);
return status;
}
this->reqid_allocated = TRUE;
@@ -783,18 +849,6 @@ METHOD(child_sa_t, install, status_t,
lifetime->time.rekey = 0;
}
- /* BEET requires the bound address from the traffic selectors */
- if (inbound)
- {
- dst_ts = my_ts;
- src_ts = other_ts;
- }
- else
- {
- src_ts = my_ts;
- dst_ts = other_ts;
- }
-
id = (kernel_ipsec_sa_id_t){
.src = src,
.dst = dst,
@@ -818,6 +872,7 @@ METHOD(child_sa_t, install, status_t,
.ipcomp = this->ipcomp,
.cpi = cpi,
.encap = this->encap,
+ .hw_offload = this->config->has_option(this->config, OPT_HW_OFFLOAD),
.esn = esn,
.initiator = initiator,
.inbound = inbound,
@@ -826,11 +881,21 @@ METHOD(child_sa_t, install, status_t,
status = charon->kernel->add_sa(charon->kernel, &id, &sa);
+ my_ts->destroy(my_ts);
+ other_ts->destroy(other_ts);
free(lifetime);
return status;
}
+METHOD(child_sa_t, install, status_t,
+ private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi,
+ uint16_t cpi, bool initiator, bool inbound, bool tfcv3)
+{
+ return install_internal(this, encr, integ, spi, cpi, initiator, inbound,
+ tfcv3);
+}
+
/**
* Check kernel interface if policy updates are required
*/
@@ -887,34 +952,21 @@ static void prepare_sa_cfg(private_child_sa_t *this, ipsec_sa_cfg_t *my_sa,
}
/**
- * Install 3 policies: out, in and forward
+ * Install inbound policie(s): in, fwd
*/
-static status_t install_policies_internal(private_child_sa_t *this,
+static status_t install_policies_inbound(private_child_sa_t *this,
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, uint32_t manual_prio)
{
- kernel_ipsec_policy_id_t out_id = {
- .dir = POLICY_OUT,
- .src_ts = my_ts,
- .dst_ts = other_ts,
- .mark = this->mark_out,
- .interface = this->config->get_interface(this->config),
- }, in_id = {
+ kernel_ipsec_policy_id_t in_id = {
.dir = POLICY_IN,
.src_ts = other_ts,
.dst_ts = my_ts,
.mark = this->mark_in,
};
- kernel_ipsec_manage_policy_t out_policy = {
- .type = type,
- .prio = priority,
- .manual_prio = manual_prio,
- .src = my_addr,
- .dst = other_addr,
- .sa = other_sa,
- }, in_policy = {
+ kernel_ipsec_manage_policy_t in_policy = {
.type = type,
.prio = priority,
.manual_prio = manual_prio,
@@ -924,13 +976,45 @@ static status_t install_policies_internal(private_child_sa_t *this,
};
status_t status = SUCCESS;
- status |= charon->kernel->add_policy(charon->kernel, &out_id, &out_policy);
status |= charon->kernel->add_policy(charon->kernel, &in_id, &in_policy);
if (this->mode != MODE_TRANSPORT)
{
in_id.dir = POLICY_FWD;
status |= charon->kernel->add_policy(charon->kernel, &in_id, &in_policy);
+ }
+ return status;
+}
+/**
+ * Install outbound policie(s): out, [fwd]
+ */
+static status_t install_policies_outbound(private_child_sa_t *this,
+ 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, uint32_t manual_prio)
+{
+ kernel_ipsec_policy_id_t out_id = {
+ .dir = POLICY_OUT,
+ .src_ts = my_ts,
+ .dst_ts = other_ts,
+ .mark = this->mark_out,
+ .interface = this->config->get_interface(this->config),
+ };
+ kernel_ipsec_manage_policy_t out_policy = {
+ .type = type,
+ .prio = priority,
+ .manual_prio = manual_prio,
+ .src = my_addr,
+ .dst = other_addr,
+ .sa = other_sa,
+ };
+ status_t status = SUCCESS;
+
+ status |= charon->kernel->add_policy(charon->kernel, &out_id, &out_policy);
+
+ if (this->mode != MODE_TRANSPORT && this->policies_fwd_out)
+ {
/* install an "outbound" FWD policy in case there is a drop policy
* matching outbound forwarded traffic, to allow another tunnel to use
* the reversed subnets and do the same we don't set a reqid (this also
@@ -939,52 +1023,56 @@ static status_t install_policies_internal(private_child_sa_t *this,
* policies of two SAs we install them with reduced priority. As they
* basically act as bypass policies for drop policies we use a higher
* priority than is used for them. */
- if (this->policies_fwd_out)
+ out_id.dir = POLICY_FWD;
+ other_sa->reqid = 0;
+ if (priority == POLICY_PRIORITY_DEFAULT)
{
- out_id.dir = POLICY_FWD;
- other_sa->reqid = 0;
- if (priority == POLICY_PRIORITY_DEFAULT)
- {
- out_policy.prio = POLICY_PRIORITY_ROUTED;
- }
- status |= charon->kernel->add_policy(charon->kernel, &out_id,
- &out_policy);
- /* reset the reqid for any other further policies */
- other_sa->reqid = this->reqid;
+ out_policy.prio = POLICY_PRIORITY_ROUTED;
}
+ status |= charon->kernel->add_policy(charon->kernel, &out_id,
+ &out_policy);
+ /* reset the reqid for any other further policies */
+ other_sa->reqid = this->reqid;
}
return status;
}
/**
- * Delete 3 policies: out, in and forward
+ * Install all policies
*/
-static void del_policies_internal(private_child_sa_t *this,
+static status_t install_policies_internal(private_child_sa_t *this,
+ 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, uint32_t manual_prio)
+{
+ status_t status = SUCCESS;
+
+ status |= install_policies_inbound(this, my_addr, other_addr, my_ts,
+ other_ts, my_sa, other_sa, type,
+ priority, manual_prio);
+ status |= install_policies_outbound(this, my_addr, other_addr, my_ts,
+ other_ts, my_sa, other_sa, type,
+ priority, manual_prio);
+ return status;
+}
+
+/**
+ * Delete inbound policies: in, fwd
+ */
+static void del_policies_inbound(private_child_sa_t *this,
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, uint32_t manual_prio)
{
- kernel_ipsec_policy_id_t out_id = {
- .dir = POLICY_OUT,
- .src_ts = my_ts,
- .dst_ts = other_ts,
- .mark = this->mark_out,
- .interface = this->config->get_interface(this->config),
- }, in_id = {
+ kernel_ipsec_policy_id_t in_id = {
.dir = POLICY_IN,
.src_ts = other_ts,
.dst_ts = my_ts,
.mark = this->mark_in,
};
- kernel_ipsec_manage_policy_t out_policy = {
- .type = type,
- .prio = priority,
- .manual_prio = manual_prio,
- .src = my_addr,
- .dst = other_addr,
- .sa = other_sa,
- }, in_policy = {
+ kernel_ipsec_manage_policy_t in_policy = {
.type = type,
.prio = priority,
.manual_prio = manual_prio,
@@ -993,49 +1081,83 @@ static void del_policies_internal(private_child_sa_t *this,
.sa = my_sa,
};
- charon->kernel->del_policy(charon->kernel, &out_id, &out_policy);
charon->kernel->del_policy(charon->kernel, &in_id, &in_policy);
+
if (this->mode != MODE_TRANSPORT)
{
in_id.dir = POLICY_FWD;
charon->kernel->del_policy(charon->kernel, &in_id, &in_policy);
+ }
+}
- if (this->policies_fwd_out)
+/**
+ * Delete outbound policies: out, [fwd]
+ */
+static void del_policies_outbound(private_child_sa_t *this,
+ 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, uint32_t manual_prio)
+{
+ kernel_ipsec_policy_id_t out_id = {
+ .dir = POLICY_OUT,
+ .src_ts = my_ts,
+ .dst_ts = other_ts,
+ .mark = this->mark_out,
+ .interface = this->config->get_interface(this->config),
+ };
+ kernel_ipsec_manage_policy_t out_policy = {
+ .type = type,
+ .prio = priority,
+ .manual_prio = manual_prio,
+ .src = my_addr,
+ .dst = other_addr,
+ .sa = other_sa,
+ };
+
+ charon->kernel->del_policy(charon->kernel, &out_id, &out_policy);
+
+ if (this->mode != MODE_TRANSPORT && this->policies_fwd_out)
+ {
+ out_id.dir = POLICY_FWD;
+ other_sa->reqid = 0;
+ if (priority == POLICY_PRIORITY_DEFAULT)
{
- out_id.dir = POLICY_FWD;
- other_sa->reqid = 0;
- if (priority == POLICY_PRIORITY_DEFAULT)
- {
- out_policy.prio = POLICY_PRIORITY_ROUTED;
- }
- charon->kernel->del_policy(charon->kernel, &out_id, &out_policy);
- other_sa->reqid = this->reqid;
+ out_policy.prio = POLICY_PRIORITY_ROUTED;
}
+ charon->kernel->del_policy(charon->kernel, &out_id, &out_policy);
+ other_sa->reqid = this->reqid;
}
}
-METHOD(child_sa_t, add_policies, status_t,
+/**
+ * Delete in- and outbound policies
+ */
+static void del_policies_internal(private_child_sa_t *this,
+ 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, uint32_t manual_prio)
+{
+ del_policies_outbound(this, my_addr, other_addr, my_ts, other_ts, my_sa,
+ other_sa, type, priority, manual_prio);
+ del_policies_inbound(this, my_addr, other_addr, my_ts, other_ts, my_sa,
+ other_sa, type, priority, manual_prio);
+}
+
+METHOD(child_sa_t, set_policies, void,
private_child_sa_t *this, linked_list_t *my_ts_list,
linked_list_t *other_ts_list)
{
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
- status_t status = SUCCESS;
- if (!this->reqid_allocated && !this->static_reqid)
+ if (array_count(this->my_ts))
{
- /* trap policy, get or confirm reqid */
- status = charon->kernel->alloc_reqid(
- charon->kernel, my_ts_list, other_ts_list,
- this->mark_in, this->mark_out, &this->reqid);
- if (status != SUCCESS)
- {
- return status;
- }
- this->reqid_allocated = TRUE;
+ array_destroy_offset(this->my_ts,
+ offsetof(traffic_selector_t, destroy));
+ this->my_ts = array_create(0, 0);
}
-
- /* apply traffic selectors */
enumerator = my_ts_list->create_enumerator(my_ts_list);
while (enumerator->enumerate(enumerator, &my_ts))
{
@@ -1044,6 +1166,12 @@ METHOD(child_sa_t, add_policies, status_t,
enumerator->destroy(enumerator);
array_sort(this->my_ts, (void*)traffic_selector_cmp, NULL);
+ if (array_count(this->other_ts))
+ {
+ array_destroy_offset(this->other_ts,
+ offsetof(traffic_selector_t, destroy));
+ this->other_ts = array_create(0, 0);
+ }
enumerator = other_ts_list->create_enumerator(other_ts_list);
while (enumerator->enumerate(enumerator, &other_ts))
{
@@ -1051,12 +1179,40 @@ METHOD(child_sa_t, add_policies, status_t,
}
enumerator->destroy(enumerator);
array_sort(this->other_ts, (void*)traffic_selector_cmp, NULL);
+}
+
+METHOD(child_sa_t, install_policies, status_t,
+ private_child_sa_t *this)
+{
+ enumerator_t *enumerator;
+ linked_list_t *my_ts_list, *other_ts_list;
+ traffic_selector_t *my_ts, *other_ts;
+ status_t status = SUCCESS;
- if (this->config->install_policy(this->config))
+ if (!this->reqid_allocated && !this->static_reqid)
+ {
+ my_ts_list = linked_list_create_from_enumerator(
+ array_create_enumerator(this->my_ts));
+ other_ts_list = linked_list_create_from_enumerator(
+ array_create_enumerator(this->other_ts));
+ status = charon->kernel->alloc_reqid(
+ charon->kernel, my_ts_list, other_ts_list,
+ this->mark_in, this->mark_out, &this->reqid);
+ my_ts_list->destroy(my_ts_list);
+ other_ts_list->destroy(other_ts_list);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ this->reqid_allocated = TRUE;
+ }
+
+ if (!this->config->has_option(this->config, OPT_NO_POLICIES))
{
policy_priority_t priority;
ipsec_sa_cfg_t my_sa, other_sa;
uint32_t manual_prio;
+ bool install_outbound;
prepare_sa_cfg(this, &my_sa, &other_sa);
manual_prio = this->config->get_manual_prio(this->config);
@@ -1066,6 +1222,7 @@ METHOD(child_sa_t, add_policies, status_t,
this->trap = this->state == CHILD_CREATED;
priority = this->trap ? POLICY_PRIORITY_ROUTED
: POLICY_PRIORITY_DEFAULT;
+ install_outbound = this->outbound_state != CHILD_OUTBOUND_REGISTERED;
/* enumerate pairs of traffic selectors */
enumerator = create_policy_enumerator(this);
@@ -1074,20 +1231,27 @@ METHOD(child_sa_t, add_policies, status_t,
/* install outbound drop policy to avoid packets leaving unencrypted
* when updating policies */
if (priority == POLICY_PRIORITY_DEFAULT && manual_prio == 0 &&
- require_policy_update())
+ require_policy_update() && install_outbound)
{
- status |= install_policies_internal(this, this->my_addr,
+ status |= install_policies_outbound(this, this->my_addr,
this->other_addr, my_ts, other_ts,
&my_sa, &other_sa, POLICY_DROP,
POLICY_PRIORITY_FALLBACK, 0);
}
- /* install policies */
- status |= install_policies_internal(this, this->my_addr,
+ status |= install_policies_inbound(this, this->my_addr,
this->other_addr, my_ts, other_ts,
&my_sa, &other_sa, POLICY_IPSEC,
priority, manual_prio);
+ if (install_outbound)
+ {
+ status |= install_policies_outbound(this, this->my_addr,
+ this->other_addr, my_ts, other_ts,
+ &my_sa, &other_sa, POLICY_IPSEC,
+ priority, manual_prio);
+
+ }
if (status != SUCCESS)
{
break;
@@ -1103,13 +1267,150 @@ METHOD(child_sa_t, add_policies, status_t,
return status;
}
-/**
- * Callback to reinstall a virtual IP
- */
-static void reinstall_vip(host_t *vip, host_t *me)
+METHOD(child_sa_t, register_outbound, void,
+ private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi,
+ uint16_t cpi, bool tfcv3)
+{
+ DBG2(DBG_CHD, "registering outbound %N SA", protocol_id_names,
+ this->protocol);
+ DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), this->my_addr,
+ this->other_addr);
+
+ this->other_spi = spi;
+ this->other_cpi = cpi;
+ this->encr_r = chunk_clone(encr);
+ this->integ_r = chunk_clone(integ);
+ this->tfcv3 = tfcv3;
+ this->outbound_state = CHILD_OUTBOUND_REGISTERED;
+}
+
+METHOD(child_sa_t, install_outbound, status_t,
+ private_child_sa_t *this)
+{
+ enumerator_t *enumerator;
+ traffic_selector_t *my_ts, *other_ts;
+ status_t status;
+
+ status = install_internal(this, this->encr_r, this->integ_r,
+ this->other_spi, this->other_cpi, FALSE, FALSE,
+ this->tfcv3);
+ chunk_clear(&this->encr_r);
+ chunk_clear(&this->integ_r);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ if (!this->config->has_option(this->config, OPT_NO_POLICIES))
+ {
+ ipsec_sa_cfg_t my_sa, other_sa;
+ uint32_t manual_prio;
+
+ prepare_sa_cfg(this, &my_sa, &other_sa);
+ manual_prio = this->config->get_manual_prio(this->config);
+
+ enumerator = create_policy_enumerator(this);
+ while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
+ {
+ /* install outbound drop policy to avoid packets leaving unencrypted
+ * when updating policies */
+ if (manual_prio == 0 && require_policy_update())
+ {
+ status |= install_policies_outbound(this, this->my_addr,
+ this->other_addr, my_ts, other_ts,
+ &my_sa, &other_sa, POLICY_DROP,
+ POLICY_PRIORITY_FALLBACK, 0);
+ }
+ status |= install_policies_outbound(this, this->my_addr,
+ this->other_addr, my_ts, other_ts,
+ &my_sa, &other_sa, POLICY_IPSEC,
+ POLICY_PRIORITY_DEFAULT, manual_prio);
+ if (status != SUCCESS)
+ {
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ return status;
+}
+
+METHOD(child_sa_t, remove_outbound, void,
+ private_child_sa_t *this)
+{
+ enumerator_t *enumerator;
+ traffic_selector_t *my_ts, *other_ts;
+
+ switch (this->outbound_state)
+ {
+ case CHILD_OUTBOUND_INSTALLED:
+ break;
+ case CHILD_OUTBOUND_REGISTERED:
+ chunk_clear(&this->encr_r);
+ chunk_clear(&this->integ_r);
+ this->outbound_state = CHILD_OUTBOUND_NONE;
+ /* fall-through */
+ case CHILD_OUTBOUND_NONE:
+ return;
+ }
+
+ if (!this->config->has_option(this->config, OPT_NO_POLICIES))
+ {
+ ipsec_sa_cfg_t my_sa, other_sa;
+ uint32_t manual_prio;
+
+ prepare_sa_cfg(this, &my_sa, &other_sa);
+ manual_prio = this->config->get_manual_prio(this->config);
+
+ enumerator = create_policy_enumerator(this);
+ while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
+ {
+ del_policies_outbound(this, this->my_addr, this->other_addr,
+ my_ts, other_ts, &my_sa, &other_sa,
+ POLICY_IPSEC, POLICY_PRIORITY_DEFAULT,
+ manual_prio);
+ if (manual_prio == 0 && require_policy_update())
+ {
+ del_policies_outbound(this, this->my_addr, this->other_addr,
+ my_ts, other_ts, &my_sa, &other_sa,
+ POLICY_DROP, POLICY_PRIORITY_FALLBACK, 0);
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ kernel_ipsec_sa_id_t id = {
+ .src = this->my_addr,
+ .dst = this->other_addr,
+ .spi = this->other_spi,
+ .proto = proto_ike2ip(this->protocol),
+ .mark = this->mark_out,
+ };
+ kernel_ipsec_del_sa_t sa = {
+ .cpi = this->other_cpi,
+ };
+ charon->kernel->del_sa(charon->kernel, &id, &sa);
+ this->outbound_state = CHILD_OUTBOUND_NONE;
+}
+
+METHOD(child_sa_t, set_rekey_spi, void,
+ private_child_sa_t *this, uint32_t spi)
+{
+ this->rekey_spi = spi;
+}
+
+METHOD(child_sa_t, get_rekey_spi, uint32_t,
+ private_child_sa_t *this)
{
+ return this->rekey_spi;
+}
+
+CALLBACK(reinstall_vip, void,
+ host_t *vip, va_list args)
+{
+ host_t *me;
char *iface;
+ VA_ARGS_VGET(args, me);
if (charon->kernel->get_interface(charon->kernel, me, &iface))
{
charon->kernel->del_ip(charon->kernel, vip, -1, TRUE);
@@ -1134,8 +1435,9 @@ METHOD(child_sa_t, update, status_t,
old = this->state;
set_state(this, CHILD_UPDATING);
- transport_proxy_mode = this->config->use_proxy_mode(this->config) &&
- this->mode == MODE_TRANSPORT;
+ transport_proxy_mode = this->mode == MODE_TRANSPORT &&
+ this->config->has_option(this->config,
+ OPT_PROXY_MODE);
if (!transport_proxy_mode)
{
@@ -1189,7 +1491,8 @@ METHOD(child_sa_t, update, status_t,
}
}
- if (this->config->install_policy(this->config) && require_policy_update())
+ if (!this->config->has_option(this->config, OPT_NO_POLICIES) &&
+ require_policy_update())
{
if (!me->ip_equals(me, this->my_addr) ||
!other->ip_equals(other, this->other_addr))
@@ -1229,7 +1532,7 @@ METHOD(child_sa_t, update, status_t,
/* we reinstall the virtual IP to handle interface roaming
* correctly */
- vips->invoke_function(vips, (void*)reinstall_vip, me);
+ vips->invoke_function(vips, reinstall_vip, me);
/* reinstall updated policies */
install_policies_internal(this, me, other, my_ts, other_ts,
@@ -1239,12 +1542,12 @@ METHOD(child_sa_t, update, status_t,
/* update fallback policies after the new policy is in place */
if (manual_prio == 0)
{
- del_policies_internal(this, this->my_addr, this->other_addr,
+ del_policies_outbound(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, 0);
- install_policies_internal(this, me, other, my_ts, other_ts,
+ install_policies_outbound(this, me, other, my_ts, other_ts,
&my_sa, &other_sa, POLICY_DROP,
POLICY_PRIORITY_FALLBACK, 0);
}
@@ -1287,25 +1590,35 @@ METHOD(child_sa_t, destroy, void,
set_state(this, CHILD_DESTROYING);
- if (this->config->install_policy(this->config))
+ if (!this->config->has_option(this->config, OPT_NO_POLICIES))
{
ipsec_sa_cfg_t my_sa, other_sa;
uint32_t manual_prio;
+ bool del_outbound;
prepare_sa_cfg(this, &my_sa, &other_sa);
manual_prio = this->config->get_manual_prio(this->config);
+ del_outbound = this->trap ||
+ this->outbound_state == CHILD_OUTBOUND_INSTALLED;
/* delete all policies in the kernel */
enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
- del_policies_internal(this, this->my_addr, this->other_addr,
- my_ts, other_ts, &my_sa, &other_sa,
- POLICY_IPSEC, priority, manual_prio);
- if (priority == POLICY_PRIORITY_DEFAULT && manual_prio == 0 &&
- require_policy_update())
+ if (del_outbound)
{
- del_policies_internal(this, this->my_addr, this->other_addr,
+ del_policies_outbound(this, this->my_addr,
+ this->other_addr, my_ts, other_ts,
+ &my_sa, &other_sa, POLICY_IPSEC,
+ priority, manual_prio);
+ }
+ del_policies_inbound(this, this->my_addr, this->other_addr,
+ my_ts, other_ts, &my_sa, &other_sa,
+ POLICY_IPSEC, priority, manual_prio);
+ if (!this->trap && manual_prio == 0 && require_policy_update() &&
+ del_outbound)
+ {
+ del_policies_outbound(this, this->my_addr, this->other_addr,
my_ts, other_ts, &my_sa, &other_sa,
POLICY_DROP, POLICY_PRIORITY_FALLBACK, 0);
}
@@ -1327,7 +1640,7 @@ METHOD(child_sa_t, destroy, void,
};
charon->kernel->del_sa(charon->kernel, &id, &sa);
}
- if (this->other_spi)
+ if (this->other_spi && this->outbound_state == CHILD_OUTBOUND_INSTALLED)
{
kernel_ipsec_sa_id_t id = {
.src = this->my_addr,
@@ -1357,6 +1670,8 @@ METHOD(child_sa_t, destroy, void,
this->other_addr->destroy(this->other_addr);
DESTROY_IF(this->proposal);
this->config->destroy(this->config);
+ chunk_clear(&this->encr_r);
+ chunk_clear(&this->integ_r);
free(this);
}
@@ -1414,6 +1729,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
.get_config = _get_config,
.get_state = _get_state,
.set_state = _set_state,
+ .get_outbound_state = _get_outbound_state,
.get_spi = _get_spi,
.get_cpi = _get_cpi,
.get_protocol = _get_protocol,
@@ -1436,8 +1752,14 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
.alloc_spi = _alloc_spi,
.alloc_cpi = _alloc_cpi,
.install = _install,
+ .register_outbound = _register_outbound,
+ .install_outbound = _install_outbound,
+ .remove_outbound = _remove_outbound,
+ .set_rekey_spi = _set_rekey_spi,
+ .get_rekey_spi = _get_rekey_spi,
.update = _update,
- .add_policies = _add_policies,
+ .set_policies = _set_policies,
+ .install_policies = _install_policies,
.create_ts_enumerator = _create_ts_enumerator,
.create_policy_enumerator = _create_policy_enumerator,
.destroy = _destroy,
@@ -1456,7 +1778,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
.mark_in = config->get_mark(config, TRUE),
.mark_out = config->get_mark(config, FALSE),
.install_time = time_monotonic(NULL),
- .policies_fwd_out = config->install_fwd_out_policy(config),
+ .policies_fwd_out = config->has_option(config, OPT_FWD_OUT_POLICIES),
);
this->config = config;
@@ -1509,7 +1831,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
/* MIPv6 proxy transport mode sets SA endpoints to TS hosts */
if (config->get_mode(config) == MODE_TRANSPORT &&
- config->use_proxy_mode(config))
+ config->has_option(config, OPT_PROXY_MODE))
{
this->mode = MODE_TRANSPORT;
diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h
index bc7df996a..b9a913da1 100644
--- a/src/libcharon/sa/child_sa.h
+++ b/src/libcharon/sa/child_sa.h
@@ -1,8 +1,8 @@
/*
- * Copyright (C) 2006-2008 Tobias Brunner
+ * Copyright (C) 2006-2017 Tobias Brunner
* Copyright (C) 2006-2008 Martin Willi
* Copyright (C) 2006 Daniel Roethlisberger
- * 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
@@ -24,6 +24,7 @@
#define CHILD_SA_H_
typedef enum child_sa_state_t child_sa_state_t;
+typedef enum child_sa_outbound_state_t child_sa_outbound_state_t;
typedef struct child_sa_t child_sa_t;
#include <library.h>
@@ -53,7 +54,7 @@ enum child_sa_state_t {
CHILD_INSTALLING,
/**
- * Installed an in-use CHILD_SA
+ * Installed both SAs of a CHILD_SA
*/
CHILD_INSTALLED,
@@ -94,6 +95,32 @@ enum child_sa_state_t {
extern enum_name_t *child_sa_state_names;
/**
+ * States of the outbound SA of a CHILD_SA
+ */
+enum child_sa_outbound_state_t {
+
+ /**
+ * Outbound SA is not installed
+ */
+ CHILD_OUTBOUND_NONE,
+
+ /**
+ * Data for the outbound SA has been registered, but not installed yet
+ */
+ CHILD_OUTBOUND_REGISTERED,
+
+ /**
+ * The outbound SA is currently installed
+ */
+ CHILD_OUTBOUND_INSTALLED,
+};
+
+/**
+ * enum strings for child_sa_outbound_state_t.
+ */
+extern enum_name_t *child_sa_outbound_state_names;
+
+/**
* Represents an IPsec SAs between two hosts.
*
* A child_sa_t contains two SAs. SAs for both
@@ -152,7 +179,14 @@ struct child_sa_t {
*
* @return CHILD_SA state
*/
- child_sa_state_t (*get_state) (child_sa_t *this);
+ child_sa_state_t (*get_state)(child_sa_t *this);
+
+ /**
+ * Get the state of the outbound SA.
+ *
+ * @return outbound SA state
+ */
+ child_sa_outbound_state_t (*get_outbound_state)(child_sa_t *this);
/**
* Set the state of the CHILD_SA.
@@ -347,6 +381,8 @@ struct child_sa_t {
/**
* Install an IPsec SA for one direction.
*
+ * set_policies() should be called before calling this.
+ *
* @param encr encryption key, if any
* @param integ integrity key
* @param spi SPI to use, allocated for inbound
@@ -354,26 +390,84 @@ struct child_sa_t {
* @param initiator TRUE if initiator of exchange resulting in this SA
* @param inbound TRUE to install an inbound SA, FALSE for outbound
* @param tfcv3 TRUE if peer supports ESPv3 TFC
- * @param my_ts negotiated local traffic selector list
- * @param other_ts negotiated remote traffic selector list
* @return SUCCESS or FAILED
*/
status_t (*install)(child_sa_t *this, chunk_t encr, chunk_t integ,
uint32_t spi, uint16_t cpi,
- bool initiator, bool inbound, bool tfcv3,
- linked_list_t *my_ts, linked_list_t *other_ts);
+ bool initiator, bool inbound, bool tfcv3);
+
+ /**
+ * Register data for the installation of an outbound SA as responder during
+ * a rekeying.
+ *
+ * The SA is not installed until install_outbound() is called.
+ *
+ * @param encr encryption key, if any (cloned)
+ * @param integ integrity key (cloned)
+ * @param spi SPI to use, allocated for inbound
+ * @param cpi CPI to use, allocated for outbound
+ * @param tfcv3 TRUE if peer supports ESPv3 TFC
+ */
+ void (*register_outbound)(child_sa_t *this, chunk_t encr, chunk_t integ,
+ uint32_t spi, uint16_t cpi, bool tfcv3);
+
+ /**
+ * Install the outbound SA and the outbound policies as responder during a
+ * rekeying.
+ *
+ * @return SUCCESS or FAILED
+ */
+ status_t (*install_outbound)(child_sa_t *this);
+
+ /**
+ * Remove the outbound SA and the outbound policies after a rekeying.
+ */
+ void (*remove_outbound)(child_sa_t *this);
+
/**
- * Install the policies using some traffic selectors.
+ * Configure the policies using some traffic selectors.
*
* Supplied lists of traffic_selector_t's specify the policies
* to use for this child sa.
*
- * @param my_ts traffic selectors for local site
- * @param other_ts traffic selectors for remote site
+ * Install the policies by calling install_policies().
+ *
+ * This should be called before calling install() so the traffic selectors
+ * may be passed to the kernel interface when installing the SAs.
+ *
+ * @param my_ts traffic selectors for local site (cloned)
+ * @param other_ts traffic selectors for remote site (cloned)
+ */
+ void (*set_policies)(child_sa_t *this, linked_list_t *my_ts_list,
+ linked_list_t *other_ts_list);
+
+ /**
+ * Install the configured policies.
+ *
+ * If register_outbound() was called previously this only installs the
+ * inbound and forward policies, the outbound policies are installed when
+ * install_outbound() is called.
+ *
* @return SUCCESS or FAILED
*/
- status_t (*add_policies)(child_sa_t *this, linked_list_t *my_ts_list,
- linked_list_t *other_ts_list);
+ status_t (*install_policies)(child_sa_t *this);
+
+ /**
+ * Set the outbound SPI of the CHILD_SA that replaced this CHILD_SA during
+ * a rekeying.
+ *
+ * @param spi outbound SPI of the CHILD_SA that replaced this CHILD_SA
+ */
+ void (*set_rekey_spi)(child_sa_t *this, uint32_t spi);
+
+ /**
+ * Get the outbound SPI of the CHILD_SA that replaced this CHILD_SA during
+ * a rekeying.
+ *
+ * @return outbound SPI of the CHILD_SA that replaced this CHILD_SA
+ */
+ uint32_t (*get_rekey_spi)(child_sa_t *this);
+
/**
* Update hosts and ecapulation mode in the kernel SAs and policies.
*
diff --git a/src/libcharon/sa/eap/eap_manager.c b/src/libcharon/sa/eap/eap_manager.c
index e4fcbc8f0..b2a57ccfb 100644
--- a/src/libcharon/sa/eap/eap_manager.c
+++ b/src/libcharon/sa/eap/eap_manager.c
@@ -105,31 +105,38 @@ METHOD(eap_manager_t, remove_method, void,
this->lock->unlock(this->lock);
}
-/**
- * filter the registered methods
- */
-static bool filter_methods(uintptr_t role, eap_entry_t **entry,
- eap_type_t *type, void *in, uint32_t *vendor)
+CALLBACK(filter_methods, bool,
+ uintptr_t role, enumerator_t *orig, va_list args)
{
- if ((*entry)->role != (eap_role_t)role)
- {
- return FALSE;
- }
- if ((*entry)->vendor == 0 &&
- ((*entry)->type < 4 || (*entry)->type == EAP_EXPANDED ||
- (*entry)->type > EAP_EXPERIMENTAL))
- { /* filter invalid types */
- return FALSE;
- }
- if (type)
- {
- *type = (*entry)->type;
- }
- if (vendor)
+ eap_entry_t *entry;
+ eap_type_t *type;
+ uint32_t *vendor;
+
+ VA_ARGS_VGET(args, type, vendor);
+
+ while (orig->enumerate(orig, &entry))
{
- *vendor = (*entry)->vendor;
+ if (entry->role != (eap_role_t)role)
+ {
+ continue;
+ }
+ if (entry->vendor == 0 &&
+ (entry->type < 4 || entry->type == EAP_EXPANDED ||
+ entry->type > EAP_EXPERIMENTAL))
+ { /* filter invalid types */
+ continue;
+ }
+ if (type)
+ {
+ *type = entry->type;
+ }
+ if (vendor)
+ {
+ *vendor = entry->vendor;
+ }
+ return TRUE;
}
- return TRUE;
+ return FALSE;
}
METHOD(eap_manager_t, create_enumerator, enumerator_t*,
@@ -139,7 +146,7 @@ METHOD(eap_manager_t, create_enumerator, enumerator_t*,
return enumerator_create_cleaner(
enumerator_create_filter(
this->methods->create_enumerator(this->methods),
- (void*)filter_methods, (void*)(uintptr_t)role, NULL),
+ filter_methods, (void*)(uintptr_t)role, NULL),
(void*)this->lock->unlock, this->lock);
}
diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c
index 76e10691f..045858792 100644
--- a/src/libcharon/sa/ike_sa.c
+++ b/src/libcharon/sa/ike_sa.c
@@ -1200,12 +1200,20 @@ METHOD(ike_sa_t, generate_message, status_t,
return status;
}
-static bool filter_fragments(private_ike_sa_t *this, packet_t **fragment,
- packet_t **packet)
+CALLBACK(filter_fragments, bool,
+ private_ike_sa_t *this, enumerator_t *orig, va_list args)
{
- *packet = (*fragment)->clone(*fragment);
- set_dscp(this, *packet);
- return TRUE;
+ packet_t *fragment, **packet;
+
+ VA_ARGS_VGET(args, packet);
+
+ if (orig->enumerate(orig, &fragment))
+ {
+ *packet = fragment->clone(fragment);
+ set_dscp(this, *packet);
+ return TRUE;
+ }
+ return FALSE;
}
METHOD(ike_sa_t, generate_message_fragmented, status_t,
@@ -1265,7 +1273,7 @@ METHOD(ike_sa_t, generate_message_fragmented, status_t,
{
charon->bus->message(charon->bus, message, FALSE, FALSE);
}
- *packets = enumerator_create_filter(fragments, (void*)filter_fragments,
+ *packets = enumerator_create_filter(fragments, filter_fragments,
this, NULL);
}
return status;
@@ -1699,8 +1707,11 @@ typedef struct {
} child_enumerator_t;
METHOD(enumerator_t, child_enumerate, bool,
- child_enumerator_t *this, child_sa_t **child_sa)
+ child_enumerator_t *this, va_list args)
{
+ child_sa_t **child_sa;
+
+ VA_ARGS_VGET(args, child_sa);
if (this->inner->enumerate(this->inner, &this->current))
{
*child_sa = this->current;
@@ -1723,7 +1734,8 @@ METHOD(ike_sa_t, create_child_sa_enumerator, enumerator_t*,
INIT(enumerator,
.public = {
- .enumerate = (void*)_child_enumerate,
+ .enumerate = enumerator_enumerate_default,
+ .venumerate = _child_enumerate,
.destroy = _child_enumerator_destroy,
},
.inner = array_create_enumerator(this->child_sas),
@@ -2619,24 +2631,31 @@ METHOD(ike_sa_t, add_configuration_attribute, void,
array_insert(this->attributes, ARRAY_TAIL, &entry);
}
-/**
- * Enumerator filter for attributes
- */
-static bool filter_attribute(void *null, attribute_entry_t **in,
- configuration_attribute_type_t *type, void *in2,
- chunk_t *data, void *in3, bool *handled)
+CALLBACK(filter_attribute, bool,
+ void *null, enumerator_t *orig, va_list args)
{
- *type = (*in)->type;
- *data = (*in)->data;
- *handled = (*in)->handler != NULL;
- return TRUE;
+ attribute_entry_t *entry;
+ configuration_attribute_type_t *type;
+ chunk_t *data;
+ bool *handled;
+
+ VA_ARGS_VGET(args, type, data, handled);
+
+ if (orig->enumerate(orig, &entry))
+ {
+ *type = entry->type;
+ *data = entry->data;
+ *handled = entry->handler != NULL;
+ return TRUE;
+ }
+ return FALSE;
}
METHOD(ike_sa_t, create_attribute_enumerator, enumerator_t*,
private_ike_sa_t *this)
{
return enumerator_create_filter(array_create_enumerator(this->attributes),
- (void*)filter_attribute, NULL, NULL);
+ filter_attribute, NULL, NULL);
}
METHOD(ike_sa_t, create_task_enumerator, enumerator_t*,
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 6bd49a086..c0bfebb83 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -151,8 +151,10 @@ static entry_t *entry_create()
/**
* Function that matches entry_t objects by ike_sa_id_t.
*/
-static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id)
+static bool entry_match_by_id(entry_t *entry, void *arg)
{
+ ike_sa_id_t *id = arg;
+
if (id->equals(id, entry->ike_sa_id))
{
return TRUE;
@@ -172,7 +174,7 @@ static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id)
/**
* Function that matches entry_t objects by ike_sa_t pointers.
*/
-static bool entry_match_by_sa(entry_t *entry, ike_sa_t *ike_sa)
+static bool entry_match_by_sa(entry_t *entry, void *ike_sa)
{
return entry->ike_sa == ike_sa;
}
@@ -276,9 +278,6 @@ typedef struct segment_t segment_t;
struct segment_t {
/** mutex to access a segment exclusively */
mutex_t *mutex;
-
- /** the number of entries in this segment */
- u_int count;
};
typedef struct shareable_segment_t shareable_segment_t;
@@ -371,6 +370,11 @@ struct private_ike_sa_manager_t {
refcount_t half_open_count_responder;
/**
+ * Total number of IKE_SAs registered with IKE_SA manager.
+ */
+ refcount_t total_sa_count;
+
+ /**
* Hash table with connected_peers_t objects.
*/
table_item_t **connected_peers_table;
@@ -511,8 +515,13 @@ struct private_enumerator_t {
};
METHOD(enumerator_t, enumerate, bool,
- private_enumerator_t *this, entry_t **entry, u_int *segment)
+ private_enumerator_t *this, va_list args)
{
+ entry_t **entry;
+ u_int *segment;
+
+ VA_ARGS_VGET(args, entry, segment);
+
if (this->entry)
{
this->entry->condvar->signal(this->entry->condvar);
@@ -570,7 +579,8 @@ static enumerator_t* create_table_enumerator(private_ike_sa_manager_t *this)
INIT(enumerator,
.enumerator = {
- .enumerate = (void*)_enumerate,
+ .enumerate = enumerator_enumerate_default,
+ .venumerate = _enumerate,
.destroy = _enumerator_destroy,
},
.manager = this,
@@ -601,7 +611,7 @@ static u_int put_entry(private_ike_sa_manager_t *this, entry_t *entry)
item->next = current;
}
this->ike_sa_table[row] = item;
- this->segments[segment].count++;
+ ref_get(&this->total_sa_count);
return segment;
}
@@ -612,10 +622,9 @@ static u_int put_entry(private_ike_sa_manager_t *this, entry_t *entry)
static void remove_entry(private_ike_sa_manager_t *this, entry_t *entry)
{
table_item_t *item, *prev = NULL;
- u_int row, segment;
+ u_int row;
row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask;
- segment = row & this->segment_mask;
item = this->ike_sa_table[row];
while (item)
{
@@ -629,7 +638,7 @@ static void remove_entry(private_ike_sa_manager_t *this, entry_t *entry)
{
this->ike_sa_table[row] = item->next;
}
- this->segments[segment].count--;
+ ignore_result(ref_put(&this->total_sa_count));
free(item);
break;
}
@@ -648,7 +657,7 @@ static void remove_entry_at(private_enumerator_t *this)
{
table_item_t *current = this->current;
- this->manager->segments[this->segment].count--;
+ ignore_result(ref_put(&this->manager->total_sa_count));
this->current = this->prev;
if (this->prev)
@@ -670,7 +679,7 @@ static void remove_entry_at(private_enumerator_t *this)
*/
static status_t get_entry_by_match_function(private_ike_sa_manager_t *this,
ike_sa_id_t *ike_sa_id, entry_t **entry, u_int *segment,
- linked_list_match_t match, void *param)
+ bool (*match)(entry_t*,void*), void *param)
{
table_item_t *item;
u_int row, seg;
@@ -703,7 +712,7 @@ static status_t get_entry_by_id(private_ike_sa_manager_t *this,
ike_sa_id_t *ike_sa_id, entry_t **entry, u_int *segment)
{
return get_entry_by_match_function(this, ike_sa_id, entry, segment,
- (linked_list_match_t)entry_match_by_id, ike_sa_id);
+ entry_match_by_id, ike_sa_id);
}
/**
@@ -714,7 +723,7 @@ static status_t get_entry_by_sa(private_ike_sa_manager_t *this,
ike_sa_id_t *ike_sa_id, ike_sa_t *ike_sa, entry_t **entry, u_int *segment)
{
return get_entry_by_match_function(this, ike_sa_id, entry, segment,
- (linked_list_match_t)entry_match_by_sa, ike_sa);
+ entry_match_by_sa, ike_sa);
}
/**
@@ -851,6 +860,15 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry)
lock->unlock(lock);
}
+CALLBACK(id_matches, bool,
+ ike_sa_id_t *a, va_list args)
+{
+ ike_sa_id_t *b;
+
+ VA_ARGS_VGET(args, b);
+ return a->equals(a, b);
+}
+
/**
* Put an SA between two peers into the hash table.
*/
@@ -879,8 +897,7 @@ static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry)
entry->other_id, family))
{
if (connected_peers->sas->find_first(connected_peers->sas,
- (linked_list_match_t)entry->ike_sa_id->equals,
- NULL, entry->ike_sa_id) == SUCCESS)
+ id_matches, NULL, entry->ike_sa_id))
{
lock->unlock(lock);
return;
@@ -1555,42 +1572,52 @@ METHOD(ike_sa_manager_t, checkout_by_name, ike_sa_t*,
return ike_sa;
}
-/**
- * enumerator filter function, waiting variant
- */
-static bool enumerator_filter_wait(private_ike_sa_manager_t *this,
- entry_t **in, ike_sa_t **out, u_int *segment)
+CALLBACK(enumerator_filter_wait, bool,
+ private_ike_sa_manager_t *this, enumerator_t *orig, va_list args)
{
- if (wait_for_entry(this, *in, *segment))
+ entry_t *entry;
+ u_int segment;
+ ike_sa_t **out;
+
+ VA_ARGS_VGET(args, out);
+
+ while (orig->enumerate(orig, &entry, &segment))
{
- *out = (*in)->ike_sa;
- charon->bus->set_sa(charon->bus, *out);
- return TRUE;
+ if (wait_for_entry(this, entry, segment))
+ {
+ *out = entry->ike_sa;
+ charon->bus->set_sa(charon->bus, *out);
+ return TRUE;
+ }
}
return FALSE;
}
-/**
- * enumerator filter function, skipping variant
- */
-static bool enumerator_filter_skip(private_ike_sa_manager_t *this,
- entry_t **in, ike_sa_t **out, u_int *segment)
+CALLBACK(enumerator_filter_skip, bool,
+ private_ike_sa_manager_t *this, enumerator_t *orig, va_list args)
{
- if (!(*in)->driveout_new_threads &&
- !(*in)->driveout_waiting_threads &&
- !(*in)->checked_out)
+ entry_t *entry;
+ u_int segment;
+ ike_sa_t **out;
+
+ VA_ARGS_VGET(args, out);
+
+ while (orig->enumerate(orig, &entry, &segment))
{
- *out = (*in)->ike_sa;
- charon->bus->set_sa(charon->bus, *out);
- return TRUE;
+ if (!entry->driveout_new_threads &&
+ !entry->driveout_waiting_threads &&
+ !entry->checked_out)
+ {
+ *out = entry->ike_sa;
+ charon->bus->set_sa(charon->bus, *out);
+ return TRUE;
+ }
}
return FALSE;
}
-/**
- * Reset threads SA after enumeration
- */
-static void reset_sa(void *data)
+CALLBACK(reset_sa, void,
+ void *data)
{
charon->bus->set_sa(charon->bus, NULL);
}
@@ -2034,17 +2061,7 @@ METHOD(ike_sa_manager_t, has_contact, bool,
METHOD(ike_sa_manager_t, get_count, u_int,
private_ike_sa_manager_t *this)
{
- u_int segment, count = 0;
- mutex_t *mutex;
-
- for (segment = 0; segment < this->segment_count; segment++)
- {
- mutex = this->segments[segment & this->segment_mask].mutex;
- mutex->lock(mutex);
- count += this->segments[segment].count;
- mutex->unlock(mutex);
- }
- return count;
+ return (u_int)ref_cur(&this->total_sa_count);
}
METHOD(ike_sa_manager_t, get_half_open_count, u_int,
diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c
index 1da17ee50..48ec3e7f5 100644
--- a/src/libcharon/sa/ikev1/task_manager_v1.c
+++ b/src/libcharon/sa/ikev1/task_manager_v1.c
@@ -210,6 +210,16 @@ struct private_task_manager_t {
double retransmit_base;
/**
+ * Jitter to apply to calculated retransmit timeout (in percent)
+ */
+ u_int retransmit_jitter;
+
+ /**
+ * Limit retransmit timeout to this value
+ */
+ uint32_t retransmit_limit;
+
+ /**
* Sequence number for sending DPD requests
*/
uint32_t dpd_send;
@@ -345,7 +355,7 @@ static status_t retransmit_packet(private_task_manager_t *this, uint32_t seqnr,
u_int mid, u_int retransmitted, array_t *packets)
{
packet_t *packet;
- uint32_t t;
+ uint32_t t, max_jitter;
array_get(packets, 0, &packet);
if (retransmitted > this->retransmit_tries)
@@ -356,6 +366,15 @@ static status_t retransmit_packet(private_task_manager_t *this, uint32_t seqnr,
}
t = (uint32_t)(this->retransmit_timeout * 1000.0 *
pow(this->retransmit_base, retransmitted));
+ if (this->retransmit_limit)
+ {
+ t = min(t, this->retransmit_limit);
+ }
+ if (this->retransmit_jitter)
+ {
+ max_jitter = (t / 100.0) * this->retransmit_jitter;
+ t -= max_jitter * (random() / (RAND_MAX + 1.0));
+ }
if (retransmitted)
{
DBG1(DBG_IKE, "sending retransmit %u of %s message ID %u, seq %u",
@@ -2034,11 +2053,15 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
.active_tasks = linked_list_create(),
.passive_tasks = linked_list_create(),
.retransmit_tries = lib->settings->get_int(lib->settings,
- "%s.retransmit_tries", RETRANSMIT_TRIES, lib->ns),
+ "%s.retransmit_tries", RETRANSMIT_TRIES, lib->ns),
.retransmit_timeout = lib->settings->get_double(lib->settings,
- "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns),
+ "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns),
.retransmit_base = lib->settings->get_double(lib->settings,
- "%s.retransmit_base", RETRANSMIT_BASE, lib->ns),
+ "%s.retransmit_base", RETRANSMIT_BASE, lib->ns),
+ .retransmit_jitter = min(lib->settings->get_int(lib->settings,
+ "%s.retransmit_jitter", 0, lib->ns), RETRANSMIT_JITTER_MAX),
+ .retransmit_limit = lib->settings->get_int(lib->settings,
+ "%s.retransmit_limit", 0, lib->ns) * 1000,
);
if (!this->rng)
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
index bbb885850..8be82ebe2 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
@@ -325,6 +325,17 @@ static bool install(private_quick_mode_t *this)
return FALSE;
}
+ if (this->initiator)
+ {
+ this->child_sa->set_policies(this->child_sa, tsi, tsr);
+ }
+ else
+ {
+ this->child_sa->set_policies(this->child_sa, tsr, tsi);
+ }
+ tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
+ tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+
if (this->keymat->derive_child_keys(this->keymat, this->proposal, this->dh,
this->spi_i, this->spi_r, this->nonce_i, this->nonce_r,
&encr_i, &integ_i, &encr_r, &integ_r))
@@ -333,19 +344,19 @@ static bool install(private_quick_mode_t *this)
{
status_i = this->child_sa->install(this->child_sa,
encr_r, integ_r, this->spi_i, this->cpi_i,
- this->initiator, TRUE, FALSE, tsi, tsr);
+ this->initiator, TRUE, FALSE);
status_o = this->child_sa->install(this->child_sa,
encr_i, integ_i, this->spi_r, this->cpi_r,
- this->initiator, FALSE, FALSE, tsi, tsr);
+ this->initiator, FALSE, FALSE);
}
else
{
status_i = this->child_sa->install(this->child_sa,
encr_i, integ_i, this->spi_r, this->cpi_r,
- this->initiator, TRUE, FALSE, tsr, tsi);
+ this->initiator, TRUE, FALSE);
status_o = this->child_sa->install(this->child_sa,
encr_r, integ_r, this->spi_i, this->cpi_i,
- this->initiator, FALSE, FALSE, tsr, tsi);
+ this->initiator, FALSE, FALSE);
}
}
@@ -355,22 +366,12 @@ static bool install(private_quick_mode_t *this)
(status_i != SUCCESS) ? "inbound " : "",
(status_i != SUCCESS && status_o != SUCCESS) ? "and ": "",
(status_o != SUCCESS) ? "outbound " : "");
- tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
- tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
status = FAILED;
}
else
{
- if (this->initiator)
- {
- status = this->child_sa->add_policies(this->child_sa, tsi, tsr);
- }
- else
- {
- status = this->child_sa->add_policies(this->child_sa, tsr, tsi);
- }
- tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
- tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
+ status = this->child_sa->install_policies(this->child_sa);
+
if (status != SUCCESS)
{
DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
@@ -853,7 +854,7 @@ METHOD(task_t, build_i, status_t,
add_nat_oa_payloads(this, message);
}
- if (this->config->use_ipcomp(this->config))
+ if (this->config->has_option(this->config, OPT_IPCOMP))
{
this->cpi_i = this->child_sa->alloc_cpi(this->child_sa);
if (!this->cpi_i)
@@ -1108,7 +1109,7 @@ METHOD(task_t, process_r, status_t,
return send_notify(this, INVALID_ID_INFORMATION);
}
- if (this->config->use_ipcomp(this->config))
+ if (this->config->has_option(this->config, OPT_IPCOMP))
{
list = sa_payload->get_ipcomp_proposals(sa_payload,
&this->cpi_i);
diff --git a/src/libcharon/sa/ikev2/connect_manager.c b/src/libcharon/sa/ikev2/connect_manager.c
index 280796d8c..35856788c 100644
--- a/src/libcharon/sa/ikev2/connect_manager.c
+++ b/src/libcharon/sa/ikev2/connect_manager.c
@@ -450,22 +450,21 @@ static initiate_data_t *initiate_data_create(check_list_t *checklist,
return this;
}
-/**
- * Find an initiated connection by the peers' ids
- */
-static bool match_initiated_by_ids(initiated_t *current, identification_t *id,
- identification_t *peer_id)
+CALLBACK(match_initiated_by_ids, bool,
+ initiated_t *current, va_list args)
{
+ identification_t *id, *peer_id;
+
+ VA_ARGS_VGET(args, id, peer_id);
return id->equals(id, current->id) && peer_id->equals(peer_id, current->peer_id);
}
-static status_t get_initiated_by_ids(private_connect_manager_t *this,
- identification_t *id,
- identification_t *peer_id,
- initiated_t **initiated)
+static bool get_initiated_by_ids(private_connect_manager_t *this,
+ identification_t *id,
+ identification_t *peer_id,
+ initiated_t **initiated)
{
- return this->initiated->find_first(this->initiated,
- (linked_list_match_t)match_initiated_by_ids,
+ return this->initiated->find_first(this->initiated, match_initiated_by_ids,
(void**)initiated, id, peer_id);
}
@@ -490,21 +489,20 @@ static void remove_initiated(private_connect_manager_t *this,
enumerator->destroy(enumerator);
}
-/**
- * Find the checklist with a specific connect ID
- */
-static bool match_checklist_by_id(check_list_t *current, chunk_t *connect_id)
+CALLBACK(match_checklist_by_id, bool,
+ check_list_t *current, va_list args)
{
- return chunk_equals(*connect_id, current->connect_id);
+ chunk_t connect_id;
+
+ VA_ARGS_VGET(args, connect_id);
+ return chunk_equals(connect_id, current->connect_id);
}
-static status_t get_checklist_by_id(private_connect_manager_t *this,
- chunk_t connect_id,
- check_list_t **check_list)
+static bool get_checklist_by_id(private_connect_manager_t *this,
+ chunk_t connect_id, check_list_t **check_list)
{
- return this->checklists->find_first(this->checklists,
- (linked_list_match_t)match_checklist_by_id,
- (void**)check_list, &connect_id);
+ return this->checklists->find_first(this->checklists, match_checklist_by_id,
+ (void**)check_list, connect_id);
}
/**
@@ -528,19 +526,19 @@ static void remove_checklist(private_connect_manager_t *this,
enumerator->destroy(enumerator);
}
-/**
- * Checks if a list of endpoint_notify_t contains a certain host_t
- */
-static bool match_endpoint_by_host(endpoint_notify_t *current, host_t *host)
+CALLBACK(match_endpoint_by_host, bool,
+ endpoint_notify_t *current, va_list args)
{
+ host_t *host;
+
+ VA_ARGS_VGET(args, host);
return host->equals(host, current->get_host(current));
}
-static status_t endpoints_contain(linked_list_t *endpoints, host_t *host,
+static bool endpoints_contain(linked_list_t *endpoints, host_t *host,
endpoint_notify_t **endpoint)
{
- return endpoints->find_first(endpoints,
- (linked_list_match_t)match_endpoint_by_host,
+ return endpoints->find_first(endpoints, match_endpoint_by_host,
(void**)endpoint, host);
}
@@ -560,39 +558,44 @@ static void insert_pair_by_priority(linked_list_t *pairs, endpoint_pair_t *pair)
enumerator->destroy(enumerator);
}
-/**
- * Searches a list of endpoint_pair_t for a pair with specific host_ts
- */
-static bool match_pair_by_hosts(endpoint_pair_t *current, host_t *local,
- host_t *remote)
+CALLBACK(match_pair_by_hosts, bool,
+ endpoint_pair_t *current, va_list args)
{
- return local->equals(local, current->local) && remote->equals(remote, current->remote);
+ host_t *local, *remote;
+
+ VA_ARGS_VGET(args, local, remote);
+ return local->equals(local, current->local) &&
+ remote->equals(remote, current->remote);
}
-static status_t get_pair_by_hosts(linked_list_t *pairs, host_t *local,
- host_t *remote, endpoint_pair_t **pair)
+static bool get_pair_by_hosts(linked_list_t *pairs, host_t *local,
+ host_t *remote, endpoint_pair_t **pair)
{
- return pairs->find_first(pairs, (linked_list_match_t)match_pair_by_hosts,
- (void**)pair, local, remote);
+ return pairs->find_first(pairs, match_pair_by_hosts, (void**)pair, local,
+ remote);
}
-static bool match_pair_by_id(endpoint_pair_t *current, uint32_t *id)
+CALLBACK(match_pair_by_id, bool,
+ endpoint_pair_t *current, va_list args)
{
- return current->id == *id;
+ uint32_t id;
+
+ VA_ARGS_VGET(args, id);
+ return current->id == id;
}
/**
* Searches for a pair with a specific id
*/
-static status_t get_pair_by_id(check_list_t *checklist, uint32_t id,
- endpoint_pair_t **pair)
+static bool get_pair_by_id(check_list_t *checklist, uint32_t id,
+ endpoint_pair_t **pair)
{
- return checklist->pairs->find_first(checklist->pairs,
- (linked_list_match_t)match_pair_by_id,
- (void**)pair, &id);
+ return checklist->pairs->find_first(checklist->pairs, match_pair_by_id,
+ (void**)pair, id);
}
-static bool match_succeeded_pair(endpoint_pair_t *current)
+CALLBACK(match_succeeded_pair, bool,
+ endpoint_pair_t *current, va_list args)
{
return current->state == CHECK_SUCCEEDED;
}
@@ -600,15 +603,14 @@ static bool match_succeeded_pair(endpoint_pair_t *current)
/**
* Returns the best pair of state CHECK_SUCCEEDED from a checklist.
*/
-static status_t get_best_valid_pair(check_list_t *checklist,
- endpoint_pair_t **pair)
+static bool get_best_valid_pair(check_list_t *checklist, endpoint_pair_t **pair)
{
- return checklist->pairs->find_first(checklist->pairs,
- (linked_list_match_t)match_succeeded_pair,
- (void**)pair);
+ return checklist->pairs->find_first(checklist->pairs, match_succeeded_pair,
+ (void**)pair);
}
-static bool match_waiting_pair(endpoint_pair_t *current)
+CALLBACK(match_waiting_pair, bool,
+ endpoint_pair_t *current, va_list args)
{
return current->state == CHECK_WAITING;
}
@@ -865,7 +867,7 @@ static job_requeue_t initiator_finish(callback_data_t *data)
this->mutex->lock(this->mutex);
check_list_t *checklist;
- if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS)
+ if (!get_checklist_by_id(this, data->connect_id, &checklist))
{
DBG1(DBG_IKE, "checklist with id '%#B' not found, can't finish "
"connectivity checks", &data->connect_id);
@@ -953,7 +955,7 @@ static job_requeue_t retransmit(callback_data_t *data)
this->mutex->lock(this->mutex);
check_list_t *checklist;
- if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS)
+ if (!get_checklist_by_id(this, data->connect_id, &checklist))
{
DBG1(DBG_IKE, "checklist with id '%#B' not found, can't retransmit "
"connectivity check", &data->connect_id);
@@ -962,7 +964,7 @@ static job_requeue_t retransmit(callback_data_t *data)
}
endpoint_pair_t *pair;
- if (get_pair_by_id(checklist, data->mid, &pair) != SUCCESS)
+ if (!get_pair_by_id(checklist, data->mid, &pair))
{
DBG1(DBG_IKE, "pair with id '%d' not found, can't retransmit "
"connectivity check", data->mid);
@@ -1108,7 +1110,7 @@ static job_requeue_t sender(callback_data_t *data)
this->mutex->lock(this->mutex);
check_list_t *checklist;
- if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS)
+ if (!get_checklist_by_id(this, data->connect_id, &checklist))
{
DBG1(DBG_IKE, "checklist with id '%#B' not found, can't send "
"connectivity check", &data->connect_id);
@@ -1124,9 +1126,8 @@ static job_requeue_t sender(callback_data_t *data)
{
DBG1(DBG_IKE, "no triggered check queued, sending an ordinary check");
- if (checklist->pairs->find_first(checklist->pairs,
- (linked_list_match_t)match_waiting_pair,
- (void**)&pair) != SUCCESS)
+ if (!checklist->pairs->find_first(checklist->pairs, match_waiting_pair,
+ (void**)&pair))
{
this->mutex->unlock(this->mutex);
DBG1(DBG_IKE, "no pairs in waiting state, aborting");
@@ -1182,7 +1183,7 @@ static job_requeue_t initiate_mediated(initiate_data_t *data)
initiated_t *initiated = data->initiated;
endpoint_pair_t *pair;
- if (get_best_valid_pair(checklist, &pair) == SUCCESS)
+ if (get_best_valid_pair(checklist, &pair))
{
ike_sa_id_t *waiting_sa;
enumerator_t *enumerator = initiated->mediated->create_enumerator(
@@ -1219,7 +1220,7 @@ static void finish_checks(private_connect_manager_t *this, check_list_t *checkli
{
initiated_t *initiated;
if (get_initiated_by_ids(this, checklist->initiator.id,
- checklist->responder.id, &initiated) == SUCCESS)
+ checklist->responder.id, &initiated))
{
callback_job_t *job;
@@ -1247,7 +1248,7 @@ static void process_response(private_connect_manager_t *this, check_t *check,
check_list_t *checklist)
{
endpoint_pair_t *pair;
- if (get_pair_by_id(checklist, check->mid, &pair) == SUCCESS)
+ if (get_pair_by_id(checklist, check->mid, &pair))
{
if (pair->local->equals(pair->local, check->dst) &&
pair->remote->equals(pair->remote, check->src))
@@ -1261,9 +1262,9 @@ static void process_response(private_connect_manager_t *this, check_t *check,
checklist->initiator.endpoints : checklist->responder.endpoints;
endpoint_notify_t *local_endpoint;
- if (endpoints_contain(local_endpoints,
- check->endpoint->get_host(check->endpoint),
- &local_endpoint) != SUCCESS)
+ if (!endpoints_contain(local_endpoints,
+ check->endpoint->get_host(check->endpoint),
+ &local_endpoint))
{
local_endpoint = endpoint_notify_create_from_host(PEER_REFLEXIVE,
check->endpoint->get_host(check->endpoint), pair->local);
@@ -1302,15 +1303,14 @@ static void process_request(private_connect_manager_t *this, check_t *check,
peer_reflexive->set_priority(peer_reflexive,
check->endpoint->get_priority(check->endpoint));
- if (endpoints_contain(remote_endpoints, check->src, &remote_endpoint) != SUCCESS)
+ if (!endpoints_contain(remote_endpoints, check->src, &remote_endpoint))
{
remote_endpoint = peer_reflexive->clone(peer_reflexive);
remote_endpoints->insert_last(remote_endpoints, remote_endpoint);
}
endpoint_pair_t *pair;
- if (get_pair_by_hosts(checklist->pairs, check->dst, check->src,
- &pair) == SUCCESS)
+ if (get_pair_by_hosts(checklist->pairs, check->dst, check->src, &pair))
{
switch(pair->state)
{
@@ -1389,7 +1389,7 @@ METHOD(connect_manager_t, process_check, void,
this->mutex->lock(this->mutex);
check_list_t *checklist;
- if (get_checklist_by_id(this, check->connect_id, &checklist) != SUCCESS)
+ if (!get_checklist_by_id(this, check->connect_id, &checklist))
{
DBG1(DBG_IKE, "checklist with id '%#B' not found",
&check->connect_id);
@@ -1423,6 +1423,15 @@ METHOD(connect_manager_t, process_check, void,
check_destroy(check);
}
+CALLBACK(id_matches, bool,
+ ike_sa_id_t *a, va_list args)
+{
+ ike_sa_id_t *b;
+
+ VA_ARGS_VGET(args, b);
+ return a->equals(a, b);
+}
+
METHOD(connect_manager_t, check_and_register, bool,
private_connect_manager_t *this, identification_t *id,
identification_t *peer_id, ike_sa_id_t *mediated_sa)
@@ -1432,7 +1441,7 @@ METHOD(connect_manager_t, check_and_register, bool,
this->mutex->lock(this->mutex);
- if (get_initiated_by_ids(this, id, peer_id, &initiated) != SUCCESS)
+ if (!get_initiated_by_ids(this, id, peer_id, &initiated))
{
DBG2(DBG_IKE, "registered waiting mediated connection with '%Y'",
peer_id);
@@ -1441,9 +1450,8 @@ METHOD(connect_manager_t, check_and_register, bool,
already_there = FALSE;
}
- if (initiated->mediated->find_first(initiated->mediated,
- (linked_list_match_t)mediated_sa->equals,
- NULL, mediated_sa) != SUCCESS)
+ if (!initiated->mediated->find_first(initiated->mediated, id_matches,
+ NULL, mediated_sa))
{
initiated->mediated->insert_last(initiated->mediated,
mediated_sa->clone(mediated_sa));
@@ -1462,7 +1470,7 @@ METHOD(connect_manager_t, check_and_initiate, void,
this->mutex->lock(this->mutex);
- if (get_initiated_by_ids(this, id, peer_id, &initiated) != SUCCESS)
+ if (!get_initiated_by_ids(this, id, peer_id, &initiated))
{
DBG2(DBG_IKE, "no waiting mediated connections with '%Y'", peer_id);
this->mutex->unlock(this->mutex);
@@ -1492,7 +1500,7 @@ METHOD(connect_manager_t, set_initiator_data, status_t,
this->mutex->lock(this->mutex);
- if (get_checklist_by_id(this, connect_id, NULL) == SUCCESS)
+ if (get_checklist_by_id(this, connect_id, NULL))
{
DBG1(DBG_IKE, "checklist with id '%#B' already exists, aborting",
&connect_id);
@@ -1517,7 +1525,7 @@ METHOD(connect_manager_t, set_responder_data, status_t,
this->mutex->lock(this->mutex);
- if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS)
+ if (!get_checklist_by_id(this, connect_id, &checklist))
{
DBG1(DBG_IKE, "checklist with id '%#B' not found",
&connect_id);
@@ -1547,7 +1555,7 @@ METHOD(connect_manager_t, stop_checks, status_t,
this->mutex->lock(this->mutex);
- if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS)
+ if (!get_checklist_by_id(this, connect_id, &checklist))
{
DBG1(DBG_IKE, "checklist with id '%#B' not found",
&connect_id);
diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c
index e4a16faf0..c2ddbc588 100644
--- a/src/libcharon/sa/ikev2/task_manager_v2.c
+++ b/src/libcharon/sa/ikev2/task_manager_v2.c
@@ -161,6 +161,16 @@ struct private_task_manager_t {
double retransmit_base;
/**
+ * Jitter to apply to calculated retransmit timeout (in percent)
+ */
+ u_int retransmit_jitter;
+
+ /**
+ * Limit retransmit timeout to this value
+ */
+ uint32_t retransmit_limit;
+
+ /**
* Use make-before-break instead of break-before-make reauth?
*/
bool make_before_break;
@@ -321,7 +331,7 @@ METHOD(task_manager_t, retransmit, status_t,
if (message_id == this->initiating.mid &&
array_count(this->initiating.packets))
{
- uint32_t timeout;
+ uint32_t timeout, max_jitter;
job_t *job;
enumerator_t *enumerator;
packet_t *packet;
@@ -351,6 +361,16 @@ METHOD(task_manager_t, retransmit, status_t,
{
timeout = (uint32_t)(this->retransmit_timeout * 1000.0 *
pow(this->retransmit_base, this->initiating.retransmitted));
+
+ if (this->retransmit_limit)
+ {
+ timeout = min(timeout, this->retransmit_limit);
+ }
+ if (this->retransmit_jitter)
+ {
+ max_jitter = (timeout / 100.0) * this->retransmit_jitter;
+ timeout -= max_jitter * (random() / (RAND_MAX + 1.0));
+ }
}
else
{
@@ -2059,13 +2079,20 @@ METHOD(task_manager_t, reset, void,
this->reset = TRUE;
}
-/**
- * Filter queued tasks
- */
-static bool filter_queued(void *unused, queued_task_t **queued, task_t **task)
+CALLBACK(filter_queued, bool,
+ void *unused, enumerator_t *orig, va_list args)
{
- *task = (*queued)->task;
- return TRUE;
+ queued_task_t *queued;
+ task_t **task;
+
+ VA_ARGS_VGET(args, task);
+
+ if (orig->enumerate(orig, &queued))
+ {
+ *task = queued->task;
+ return TRUE;
+ }
+ return FALSE;
}
METHOD(task_manager_t, create_task_enumerator, enumerator_t*,
@@ -2080,7 +2107,7 @@ METHOD(task_manager_t, create_task_enumerator, enumerator_t*,
case TASK_QUEUE_QUEUED:
return enumerator_create_filter(
array_create_enumerator(this->queued_tasks),
- (void*)filter_queued, NULL, NULL);
+ filter_queued, NULL, NULL);
default:
return enumerator_create_empty();
}
@@ -2151,6 +2178,10 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa)
"%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns),
.retransmit_base = lib->settings->get_double(lib->settings,
"%s.retransmit_base", RETRANSMIT_BASE, lib->ns),
+ .retransmit_jitter = min(lib->settings->get_int(lib->settings,
+ "%s.retransmit_jitter", 0, lib->ns), RETRANSMIT_JITTER_MAX),
+ .retransmit_limit = lib->settings->get_int(lib->settings,
+ "%s.retransmit_limit", 0, lib->ns) * 1000,
.make_before_break = lib->settings->get_bool(lib->settings,
"%s.make_before_break", FALSE, lib->ns),
);
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c
index 71cb6b8ea..896cabb2b 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.c
+++ b/src/libcharon/sa/ikev2/tasks/child_create.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2016 Tobias Brunner
+ * Copyright (C) 2008-2017 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
* HSR Hochschule fuer Technik Rapperswil
@@ -602,7 +602,7 @@ static status_t select_and_install(private_child_create_t *this,
switch (this->mode)
{
case MODE_TRANSPORT:
- if (!this->config->use_proxy_mode(this->config) &&
+ if (!this->config->has_option(this->config, OPT_PROXY_MODE) &&
(!ts_list_is_host(this->tsi, other) ||
!ts_list_is_host(this->tsr, me))
)
@@ -630,6 +630,32 @@ static status_t select_and_install(private_child_create_t *this,
default:
break;
}
+ /* use a copy of the traffic selectors, as the POST hook should not
+ * change payloads */
+ my_ts = this->tsr->clone_offset(this->tsr,
+ offsetof(traffic_selector_t, clone));
+ other_ts = this->tsi->clone_offset(this->tsi,
+ offsetof(traffic_selector_t, clone));
+ charon->bus->narrow(charon->bus, this->child_sa,
+ NARROW_RESPONDER_POST, my_ts, other_ts);
+
+ if (my_ts->get_count(my_ts) == 0 || other_ts->get_count(other_ts) == 0)
+ {
+ my_ts->destroy_offset(my_ts,
+ offsetof(traffic_selector_t, destroy));
+ other_ts->destroy_offset(other_ts,
+ offsetof(traffic_selector_t, destroy));
+ return NOT_FOUND;
+ }
+ }
+
+ this->child_sa->set_policies(this->child_sa, my_ts, other_ts);
+ if (!this->initiator)
+ {
+ my_ts->destroy_offset(my_ts,
+ offsetof(traffic_selector_t, destroy));
+ other_ts->destroy_offset(other_ts,
+ offsetof(traffic_selector_t, destroy));
}
this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
@@ -651,19 +677,30 @@ static status_t select_and_install(private_child_create_t *this,
{
status_i = this->child_sa->install(this->child_sa, encr_r, integ_r,
this->my_spi, this->my_cpi, this->initiator,
- TRUE, this->tfcv3, my_ts, other_ts);
+ TRUE, this->tfcv3);
status_o = this->child_sa->install(this->child_sa, encr_i, integ_i,
this->other_spi, this->other_cpi, this->initiator,
- FALSE, this->tfcv3, my_ts, other_ts);
+ FALSE, this->tfcv3);
}
- else
+ else if (!this->rekey)
{
status_i = this->child_sa->install(this->child_sa, encr_i, integ_i,
this->my_spi, this->my_cpi, this->initiator,
- TRUE, this->tfcv3, my_ts, other_ts);
+ TRUE, this->tfcv3);
status_o = this->child_sa->install(this->child_sa, encr_r, integ_r,
this->other_spi, this->other_cpi, this->initiator,
- FALSE, this->tfcv3, my_ts, other_ts);
+ FALSE, this->tfcv3);
+ }
+ else
+ { /* as responder during a rekeying we only install the inbound
+ * SA now, the outbound SA and policies are installed when we
+ * receive the delete for the old SA */
+ status_i = this->child_sa->install(this->child_sa, encr_i, integ_i,
+ this->my_spi, this->my_cpi, this->initiator,
+ TRUE, this->tfcv3);
+ this->child_sa->register_outbound(this->child_sa, encr_r, integ_r,
+ this->other_spi, this->other_cpi, this->tfcv3);
+ status_o = SUCCESS;
}
}
@@ -679,36 +716,8 @@ static status_t select_and_install(private_child_create_t *this,
}
else
{
- if (this->initiator)
- {
- status = this->child_sa->add_policies(this->child_sa,
- my_ts, other_ts);
- }
- else
- {
- /* use a copy of the traffic selectors, as the POST hook should not
- * change payloads */
- my_ts = this->tsr->clone_offset(this->tsr,
- offsetof(traffic_selector_t, clone));
- other_ts = this->tsi->clone_offset(this->tsi,
- offsetof(traffic_selector_t, clone));
- charon->bus->narrow(charon->bus, this->child_sa,
- NARROW_RESPONDER_POST, my_ts, other_ts);
- if (my_ts->get_count(my_ts) == 0 ||
- other_ts->get_count(other_ts) == 0)
- {
- status = FAILED;
- }
- else
- {
- status = this->child_sa->add_policies(this->child_sa,
- my_ts, other_ts);
- }
- my_ts->destroy_offset(my_ts,
- offsetof(traffic_selector_t, destroy));
- other_ts->destroy_offset(other_ts,
- offsetof(traffic_selector_t, destroy));
- }
+ status = this->child_sa->install_policies(this->child_sa);
+
if (status != SUCCESS)
{
DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
@@ -736,7 +745,6 @@ static status_t select_and_install(private_child_create_t *this,
charon->bus->child_keys(charon->bus, this->child_sa, this->initiator,
this->dh, nonce_i, nonce_r);
- /* add to IKE_SA, and remove from task */
this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
this->established = TRUE;
@@ -748,16 +756,17 @@ static status_t select_and_install(private_child_create_t *this,
other_ts = linked_list_create_from_enumerator(
this->child_sa->create_ts_enumerator(this->child_sa, FALSE));
- DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
+ DBG0(DBG_IKE, "%sCHILD_SA %s{%d} established "
"with SPIs %.8x_i %.8x_o and TS %#R === %#R",
+ this->rekey && !this->initiator ? "inbound " : "",
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)),
- ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), my_ts, other_ts);
+ ntohl(this->child_sa->get_spi(this->child_sa, FALSE)),
+ my_ts, other_ts);
my_ts->destroy(my_ts);
other_ts->destroy(other_ts);
-
return SUCCESS;
}
@@ -1073,7 +1082,7 @@ METHOD(task_t, build_i, status_t,
this->dh_group);
}
- if (this->config->use_ipcomp(this->config))
+ if (this->config->has_option(this->config, OPT_IPCOMP))
{
/* IPCOMP_DEFLATE is the only transform we support at the moment */
add_ipcomp_notify(this, message, IPCOMP_DEFLATE);
@@ -1327,7 +1336,7 @@ METHOD(task_t, build_r, status_t,
if (this->ipcomp_received != IPCOMP_NONE)
{
- if (this->config->use_ipcomp(this->config))
+ if (this->config->has_option(this->config, OPT_IPCOMP))
{
add_ipcomp_notify(this, message, this->ipcomp_received);
}
@@ -1690,7 +1699,6 @@ METHOD(task_t, destroy, void,
{
this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
}
-
DESTROY_IF(this->config);
DESTROY_IF(this->nonceg);
free(this);
diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c
index 6fa8836ac..626796383 100644
--- a/src/libcharon/sa/ikev2/tasks/child_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.c
@@ -18,9 +18,14 @@
#include <daemon.h>
#include <encoding/payloads/delete_payload.h>
+#include <processing/jobs/delete_child_sa_job.h>
#include <sa/ikev2/tasks/child_create.h>
#include <sa/ikev2/tasks/child_rekey.h>
+#ifndef DELETE_REKEYED_DELAY
+#define DELETE_REKEYED_DELAY 5
+#endif
+
typedef struct private_child_delete_t private_child_delete_t;
/**
@@ -39,67 +44,80 @@ struct private_child_delete_t {
ike_sa_t *ike_sa;
/**
- * Are we the initiator?
+ * Whether we are the initiator of the exchange
*/
bool initiator;
/**
- * Protocol of CHILD_SA to delete
+ * Protocol of CHILD_SA to delete (as initiator)
*/
protocol_id_t protocol;
/**
- * Inbound SPI of CHILD_SA to delete
+ * Inbound SPI of CHILD_SA to delete (as initiator)
*/
uint32_t spi;
/**
- * whether to enforce delete action policy
- */
- bool check_delete_action;
-
- /**
- * is this delete exchange following a rekey?
- */
- bool rekeyed;
-
- /**
- * CHILD_SA already expired?
+ * CHILD_SA already expired (as initiator)
*/
bool expired;
/**
- * CHILD_SAs which get deleted
+ * CHILD_SAs which get deleted, entry_t*
*/
linked_list_t *child_sas;
};
/**
+ * Information about a deleted CHILD_SA
+ */
+typedef struct {
+ /** Deleted CHILD_SA */
+ child_sa_t *child_sa;
+ /** Whether the CHILD_SA was rekeyed */
+ bool rekeyed;
+ /** Whether to enforce any delete action policy */
+ bool check_delete_action;
+} entry_t;
+
+CALLBACK(match_child, bool,
+ entry_t *entry, va_list args)
+{
+ child_sa_t *child_sa;
+
+ VA_ARGS_VGET(args, child_sa);
+ return entry->child_sa == child_sa;
+}
+
+/**
* build the delete payloads from the listed child_sas
*/
static void build_payloads(private_child_delete_t *this, message_t *message)
{
delete_payload_t *ah = NULL, *esp = NULL;
enumerator_t *enumerator;
- child_sa_t *child_sa;
+ entry_t *entry;
+ protocol_id_t protocol;
+ uint32_t spi;
enumerator = this->child_sas->create_enumerator(this->child_sas);
- while (enumerator->enumerate(enumerator, (void**)&child_sa))
+ while (enumerator->enumerate(enumerator, (void**)&entry))
{
- protocol_id_t protocol = child_sa->get_protocol(child_sa);
- uint32_t spi = child_sa->get_spi(child_sa, TRUE);
+ protocol = entry->child_sa->get_protocol(entry->child_sa);
+ spi = entry->child_sa->get_spi(entry->child_sa, TRUE);
switch (protocol)
{
case PROTO_ESP:
- if (esp == NULL)
+ if (!esp)
{
esp = delete_payload_create(PLV2_DELETE, PROTO_ESP);
message->add_payload(message, (payload_t*)esp);
}
esp->add_spi(esp, spi);
DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
- protocol_id_names, protocol, ntohl(spi));
+ protocol_id_names, protocol, ntohl(spi));
break;
case PROTO_AH:
if (ah == NULL)
@@ -109,12 +127,12 @@ static void build_payloads(private_child_delete_t *this, message_t *message)
}
ah->add_spi(ah, spi);
DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
- protocol_id_names, protocol, ntohl(spi));
+ protocol_id_names, protocol, ntohl(spi));
break;
default:
break;
}
- child_sa->set_state(child_sa, CHILD_DELETING);
+ entry->child_sa->set_state(entry->child_sa, CHILD_DELETING);
}
enumerator->destroy(enumerator);
}
@@ -147,6 +165,57 @@ static bool is_redundant(private_child_delete_t *this, child_sa_t *child)
}
/**
+ * Install the outbound CHILD_SA with the given SPI
+ */
+static void install_outbound(private_child_delete_t *this,
+ protocol_id_t protocol, uint32_t spi)
+{
+ child_sa_t *child_sa;
+ linked_list_t *my_ts, *other_ts;
+ status_t status;
+
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
+ spi, FALSE);
+ if (!child_sa)
+ {
+ DBG1(DBG_IKE, "CHILD_SA not found after rekeying");
+ return;
+ }
+ if (this->initiator && is_redundant(this, child_sa))
+ { /* if we won the rekey collision we don't want to install the
+ * redundant SA created by the peer */
+ return;
+ }
+
+ status = child_sa->install_outbound(child_sa);
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "unable to install outbound IPsec SA (SAD) in kernel");
+ charon->bus->alert(charon->bus, ALERT_INSTALL_CHILD_SA_FAILED,
+ child_sa);
+ /* FIXME: delete the new child_sa? */
+ return;
+ }
+ child_sa->set_state(child_sa, CHILD_INSTALLED);
+
+ my_ts = linked_list_create_from_enumerator(
+ child_sa->create_ts_enumerator(child_sa, TRUE));
+ other_ts = linked_list_create_from_enumerator(
+ child_sa->create_ts_enumerator(child_sa, FALSE));
+
+ DBG0(DBG_IKE, "outbound CHILD_SA %s{%d} established "
+ "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);
+
+ my_ts->destroy(my_ts);
+ other_ts->destroy(other_ts);
+}
+
+/**
* read in payloads and find the children to delete
*/
static void process_payloads(private_child_delete_t *this, message_t *message)
@@ -157,6 +226,7 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
uint32_t spi;
protocol_id_t protocol;
child_sa_t *child_sa;
+ entry_t *entry;
payloads = message->create_payload_enumerator(message);
while (payloads->enumerate(payloads, &payload))
@@ -174,27 +244,37 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
{
child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
spi, FALSE);
- if (child_sa == NULL)
+ if (!child_sa)
{
- DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x, "
- "but no such SA", protocol_id_names, protocol, ntohl(spi));
+ DBG1(DBG_IKE, "received DELETE for unknown %N CHILD_SA with"
+ " SPI %.8x", protocol_id_names, protocol, ntohl(spi));
continue;
}
DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x",
protocol_id_names, protocol, ntohl(spi));
+ if (this->child_sas->find_first(this->child_sas, match_child,
+ NULL, child_sa))
+ {
+ continue;
+ }
+ INIT(entry,
+ .child_sa = child_sa
+ );
switch (child_sa->get_state(child_sa))
{
case CHILD_REKEYED:
- this->rekeyed = TRUE;
+ entry->rekeyed = TRUE;
break;
case CHILD_DELETING:
- /* we don't send back a delete if we initiated ourself */
+ /* we don't send back a delete if we already initiated
+ * a delete ourself */
if (!this->initiator)
{
+ free(entry);
continue;
}
- /* fall through */
+ break;
case CHILD_REKEYING:
/* we reply as usual, rekeying will fail */
case CHILD_INSTALLED:
@@ -202,22 +282,18 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
{
if (is_redundant(this, child_sa))
{
- this->rekeyed = TRUE;
+ entry->rekeyed = TRUE;
}
else
{
- this->check_delete_action = TRUE;
+ entry->check_delete_action = TRUE;
}
}
break;
default:
break;
}
- if (this->child_sas->find_first(this->child_sas, NULL,
- (void**)&child_sa) != SUCCESS)
- {
- this->child_sas->insert_last(this->child_sas, child_sa);
- }
+ this->child_sas->insert_last(this->child_sas, entry);
}
spis->destroy(spis);
}
@@ -231,29 +307,64 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
static status_t destroy_and_reestablish(private_child_delete_t *this)
{
enumerator_t *enumerator;
+ entry_t *entry;
child_sa_t *child_sa;
child_cfg_t *child_cfg;
protocol_id_t protocol;
- uint32_t spi, reqid;
+ uint32_t spi, reqid, rekey_spi;
action_t action;
status_t status = SUCCESS;
+ time_t now, expire;
+ u_int delay;
+
+ now = time_monotonic(NULL);
+ delay = lib->settings->get_int(lib->settings, "%s.delete_rekeyed_delay",
+ DELETE_REKEYED_DELAY, lib->ns);
enumerator = this->child_sas->create_enumerator(this->child_sas);
- while (enumerator->enumerate(enumerator, (void**)&child_sa))
+ while (enumerator->enumerate(enumerator, (void**)&entry))
{
+ child_sa = entry->child_sa;
/* signal child down event if we weren't rekeying */
- if (!this->rekeyed)
+ protocol = child_sa->get_protocol(child_sa);
+ if (!entry->rekeyed)
{
charon->bus->child_updown(charon->bus, child_sa, FALSE);
}
+ else
+ {
+ rekey_spi = child_sa->get_rekey_spi(child_sa);
+ if (rekey_spi)
+ {
+ install_outbound(this, protocol, rekey_spi);
+ }
+ /* for rekeyed CHILD_SAs we uninstall the outbound SA but don't
+ * immediately destroy it, by default, so we can process delayed
+ * packets */
+ child_sa->remove_outbound(child_sa);
+ expire = child_sa->get_lifetime(child_sa, TRUE);
+ if (delay && (!expire || ((now + delay) < expire)))
+ {
+ lib->scheduler->schedule_job(lib->scheduler,
+ (job_t*)delete_child_sa_job_create_id(
+ child_sa->get_unique_id(child_sa)), delay);
+ continue;
+ }
+ else if (expire)
+ { /* let it expire naturally */
+ continue;
+ }
+ /* no delay and no lifetime, destroy it immediately */
+ }
spi = child_sa->get_spi(child_sa, TRUE);
reqid = child_sa->get_reqid(child_sa);
- protocol = child_sa->get_protocol(child_sa);
child_cfg = child_sa->get_config(child_sa);
child_cfg->get_ref(child_cfg);
action = child_sa->get_close_action(child_sa);
+
this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
- if (this->check_delete_action)
+
+ if (entry->check_delete_action)
{ /* enforce child_cfg policy if deleted passively */
switch (action)
{
@@ -288,12 +399,14 @@ static void log_children(private_child_delete_t *this)
{
linked_list_t *my_ts, *other_ts;
enumerator_t *enumerator;
+ entry_t *entry;
child_sa_t *child_sa;
uint64_t bytes_in, bytes_out;
enumerator = this->child_sas->create_enumerator(this->child_sas);
- while (enumerator->enumerate(enumerator, (void**)&child_sa))
+ while (enumerator->enumerate(enumerator, (void**)&entry))
{
+ child_sa = entry->child_sa;
my_ts = linked_list_create_from_enumerator(
child_sa->create_ts_enumerator(child_sa, TRUE));
other_ts = linked_list_create_from_enumerator(
@@ -328,6 +441,7 @@ METHOD(task_t, build_i, status_t,
private_child_delete_t *this, message_t *message)
{
child_sa_t *child_sa;
+ entry_t *entry;
child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol,
this->spi, TRUE);
@@ -342,15 +456,24 @@ METHOD(task_t, build_i, status_t,
/* we work only with the inbound SPI */
this->spi = child_sa->get_spi(child_sa, TRUE);
}
- this->child_sas->insert_last(this->child_sas, child_sa);
- if (child_sa->get_state(child_sa) == CHILD_REKEYED)
- {
- this->rekeyed = TRUE;
+
+ if (child_sa->get_state(child_sa) == CHILD_DELETING)
+ { /* DELETEs for this CHILD_SA were already exchanged, but it was not yet
+ * destroyed to allow delayed packets to get processed */
+ this->ike_sa->destroy_child_sa(this->ike_sa, this->protocol, this->spi);
+ message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
+ return SUCCESS;
}
+
+ INIT(entry,
+ .child_sa = child_sa,
+ .rekeyed = child_sa->get_state(child_sa) == CHILD_REKEYED,
+ );
+ this->child_sas->insert_last(this->child_sas, entry);
log_children(this);
build_payloads(this, message);
- if (!this->rekeyed && this->expired)
+ if (!entry->rekeyed && this->expired)
{
child_cfg_t *child_cfg;
@@ -397,24 +520,28 @@ METHOD(child_delete_t , get_child, child_sa_t*,
private_child_delete_t *this)
{
child_sa_t *child_sa = NULL;
- this->child_sas->get_first(this->child_sas, (void**)&child_sa);
+ entry_t *entry;
+
+ if (this->child_sas->get_first(this->child_sas, (void**)&entry) == SUCCESS)
+ {
+ child_sa = entry->child_sa;
+ }
return child_sa;
}
METHOD(task_t, migrate, void,
private_child_delete_t *this, ike_sa_t *ike_sa)
{
- this->check_delete_action = FALSE;
this->ike_sa = ike_sa;
- this->child_sas->destroy(this->child_sas);
+ this->child_sas->destroy_function(this->child_sas, free);
this->child_sas = linked_list_create();
}
METHOD(task_t, destroy, void,
private_child_delete_t *this)
{
- this->child_sas->destroy(this->child_sas);
+ this->child_sas->destroy_function(this->child_sas, free);
free(this);
}
diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c
index c04ec141f..761c860e7 100644
--- a/src/libcharon/sa/ikev2/tasks/child_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c
@@ -132,6 +132,7 @@ static void find_child(private_child_rekey_t *this, message_t *message)
notify_payload_t *notify;
protocol_id_t protocol;
uint32_t spi;
+ child_sa_t *child_sa;
notify = message->get_notify(message, REKEY_SA);
if (notify)
@@ -141,8 +142,15 @@ static void find_child(private_child_rekey_t *this, message_t *message)
if (protocol == PROTO_ESP || protocol == PROTO_AH)
{
- this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
- spi, FALSE);
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
+ spi, FALSE);
+ if (child_sa &&
+ child_sa->get_state(child_sa) == CHILD_DELETING &&
+ child_sa->get_outbound_state(child_sa) == CHILD_OUTBOUND_NONE)
+ { /* ignore rekeyed CHILD_SAs we keep around */
+ return;
+ }
+ this->child_sa = child_sa;
}
}
}
@@ -227,6 +235,7 @@ METHOD(task_t, build_r, status_t,
child_cfg_t *config;
uint32_t reqid;
child_sa_state_t state;
+ child_sa_t *child_sa;
if (!this->child_sa)
{
@@ -260,7 +269,10 @@ METHOD(task_t, build_r, status_t,
return SUCCESS;
}
+ child_sa = this->child_create->get_child(this->child_create);
this->child_sa->set_state(this->child_sa, CHILD_REKEYED);
+ this->child_sa->set_rekey_spi(this->child_sa,
+ child_sa->get_spi(child_sa, FALSE));
/* invoke rekey hook */
charon->bus->child_rekey(charon->bus, this->child_sa,
diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c
index b0162751d..ad12f0579 100644
--- a/src/libcharon/sa/shunt_manager.c
+++ b/src/libcharon/sa/shunt_manager.c
@@ -381,14 +381,24 @@ METHOD(shunt_manager_t, uninstall, bool,
}
CALLBACK(filter_entries, bool,
- void *unused, entry_t **entry, char **ns, void **in, child_cfg_t **cfg)
+ void *unused, enumerator_t *orig, va_list args)
{
- if (ns)
+ entry_t *entry;
+ child_cfg_t **cfg;
+ char **ns;
+
+ VA_ARGS_VGET(args, ns, cfg);
+
+ if (orig->enumerate(orig, &entry))
{
- *ns = (*entry)->ns;
+ if (ns)
+ {
+ *ns = entry->ns;
+ }
+ *cfg = entry->cfg;
+ return TRUE;
}
- *cfg = (*entry)->cfg;
- return TRUE;
+ return FALSE;
}
METHOD(shunt_manager_t, create_enumerator, enumerator_t*,
@@ -397,7 +407,7 @@ METHOD(shunt_manager_t, create_enumerator, enumerator_t*,
this->lock->read_lock(this->lock);
return enumerator_create_filter(
this->shunts->create_enumerator(this->shunts),
- (void*)filter_entries, this->lock,
+ filter_entries, this->lock,
(void*)this->lock->unlock);
}
diff --git a/src/libcharon/sa/task_manager.c b/src/libcharon/sa/task_manager.c
index c42008ba9..bd1191406 100644
--- a/src/libcharon/sa/task_manager.c
+++ b/src/libcharon/sa/task_manager.c
@@ -15,10 +15,40 @@
#include "task_manager.h"
+#include <math.h>
#include <sa/ikev1/task_manager_v1.h>
#include <sa/ikev2/task_manager_v2.h>
-/**
+/*
+ * See header
+ */
+u_int task_manager_total_retransmit_timeout()
+{
+ double timeout, base, limit = 0, total = 0;
+ int tries, i;
+
+ tries = lib->settings->get_int(lib->settings, "%s.retransmit_tries",
+ RETRANSMIT_TRIES, lib->ns);
+ base = lib->settings->get_double(lib->settings, "%s.retransmit_base",
+ RETRANSMIT_BASE, lib->ns);
+ timeout = lib->settings->get_double(lib->settings, "%s.retransmit_timeout",
+ RETRANSMIT_TIMEOUT, lib->ns);
+ limit = lib->settings->get_double(lib->settings, "%s.retransmit_limit",
+ 0, lib->ns);
+
+ for (i = 0; i <= tries; i++)
+ {
+ double interval = timeout * pow(base, i);
+ if (limit)
+ {
+ interval = min(interval, limit);
+ }
+ total += interval;
+ }
+ return (u_int)total;
+}
+
+/*
* See header
*/
task_manager_t *task_manager_create(ike_sa_t *ike_sa)
diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h
index 7e9262291..e3fddf39b 100644
--- a/src/libcharon/sa/task_manager.h
+++ b/src/libcharon/sa/task_manager.h
@@ -48,6 +48,11 @@ typedef enum task_queue_t task_queue_t;
#define RETRANSMIT_TRIES 5
/**
+ * Maximum jitter in percent.
+ */
+#define RETRANSMIT_JITTER_MAX 20
+
+/**
* Interval for mobike routability checks in ms.
*/
#define ROUTEABILITY_CHECK_INTERVAL 2500
@@ -298,6 +303,17 @@ struct task_manager_t {
};
/**
+ * Calculate total timeout of the retransmission mechanism.
+ *
+ * This is affected by modifications of retransmit_base, retransmit_timeout,
+ * retransmit_limit or retransmit_tries. The resulting value can then be used
+ * e.g. in kernel plugins to set the system's acquire timeout properly.
+ *
+ * @return calculated total retransmission timeout in seconds
+ */
+u_int task_manager_total_retransmit_timeout();
+
+/**
* Create a task manager instance for the correct IKE version.
*
* @param ike_sa IKE_SA to create a task manager for
diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c
index 40a0682f2..f9fee5e7e 100644
--- a/src/libcharon/sa/trap_manager.c
+++ b/src/libcharon/sa/trap_manager.c
@@ -140,19 +140,21 @@ static void destroy_acquire(acquire_t *this)
free(this);
}
-/**
- * match an acquire entry by reqid
- */
-static bool acquire_by_reqid(acquire_t *this, uint32_t *reqid)
+CALLBACK(acquire_by_reqid, bool,
+ acquire_t *this, va_list args)
{
- return this->reqid == *reqid;
+ uint32_t reqid;
+
+ VA_ARGS_VGET(args, reqid);
+ return this->reqid == reqid;
}
-/**
- * match an acquire entry by destination address
- */
-static bool acquire_by_dst(acquire_t *this, host_t *dst)
+CALLBACK(acquire_by_dst, bool,
+ acquire_t *this, va_list args)
{
+ host_t *dst;
+
+ VA_ARGS_VGET(args, dst);
return this->dst && this->dst->ip_equals(this->dst, dst);
}
@@ -272,7 +274,8 @@ METHOD(trap_manager_t, install, uint32_t,
proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
child_sa->set_protocol(child_sa, proto);
child_sa->set_mode(child_sa, child->get_mode(child));
- status = child_sa->add_policies(child_sa, my_ts, other_ts);
+ child_sa->set_policies(child_sa, my_ts, other_ts);
+ status = child_sa->install_policies(child_sa);
my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
if (status != SUCCESS)
@@ -334,25 +337,32 @@ METHOD(trap_manager_t, uninstall, bool,
return TRUE;
}
-/**
- * convert enumerated entries to peer_cfg, child_sa
- */
-static bool trap_filter(rwlock_t *lock, entry_t **entry, peer_cfg_t **peer_cfg,
- void *none, child_sa_t **child_sa)
+CALLBACK(trap_filter, bool,
+ rwlock_t *lock, enumerator_t *orig, va_list args)
{
- if (!(*entry)->child_sa)
- { /* skip entries that are currently being installed */
- return FALSE;
- }
- if (peer_cfg)
- {
- *peer_cfg = (*entry)->peer_cfg;
- }
- if (child_sa)
+ entry_t *entry;
+ peer_cfg_t **peer_cfg;
+ child_sa_t **child_sa;
+
+ VA_ARGS_VGET(args, peer_cfg, child_sa);
+
+ while (orig->enumerate(orig, &entry))
{
- *child_sa = (*entry)->child_sa;
+ if (!entry->child_sa)
+ { /* skip entries that are currently being installed */
+ continue;
+ }
+ if (peer_cfg)
+ {
+ *peer_cfg = entry->peer_cfg;
+ }
+ if (child_sa)
+ {
+ *child_sa = entry->child_sa;
+ }
+ return TRUE;
}
- return TRUE;
+ return FALSE;
}
METHOD(trap_manager_t, create_enumerator, enumerator_t*,
@@ -360,7 +370,7 @@ METHOD(trap_manager_t, create_enumerator, enumerator_t*,
{
this->lock->read_lock(this->lock);
return enumerator_create_filter(this->traps->create_enumerator(this->traps),
- (void*)trap_filter, this->lock,
+ trap_filter, this->lock,
(void*)this->lock->unlock);
}
@@ -431,8 +441,8 @@ METHOD(trap_manager_t, acquire, void,
uint8_t mask;
dst->to_subnet(dst, &host, &mask);
- if (this->acquires->find_first(this->acquires, (void*)acquire_by_dst,
- (void**)&acquire, host) == SUCCESS)
+ if (this->acquires->find_first(this->acquires, acquire_by_dst,
+ (void**)&acquire, host))
{
host->destroy(host);
ignore = TRUE;
@@ -448,8 +458,8 @@ METHOD(trap_manager_t, acquire, void,
}
else
{
- if (this->acquires->find_first(this->acquires, (void*)acquire_by_reqid,
- (void**)&acquire, &reqid) == SUCCESS)
+ if (this->acquires->find_first(this->acquires, acquire_by_reqid,
+ (void**)&acquire, reqid))
{
ignore = TRUE;
}