aboutsummaryrefslogtreecommitdiffstats
path: root/src/libcharon/sa/child_sa.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa/child_sa.c')
-rw-r--r--src/libcharon/sa/child_sa.c128
1 files changed, 96 insertions, 32 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index 66be5fe61..fdeb605ee 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -101,6 +101,16 @@ struct private_child_sa_t {
u_int32_t reqid;
/**
+ * Did we allocate/confirm and must release the reqid?
+ */
+ bool reqid_allocated;
+
+ /*
+ * Unique CHILD_SA identifier
+ */
+ u_int32_t unique_id;
+
+ /**
* inbound mark used for this child_sa
*/
mark_t mark_in;
@@ -229,6 +239,12 @@ METHOD(child_sa_t, get_reqid, u_int32_t,
return this->reqid;
}
+METHOD(child_sa_t, get_unique_id, u_int32_t,
+ private_child_sa_t *this)
+{
+ return this->unique_id;
+}
+
METHOD(child_sa_t, get_config, child_cfg_t*,
private_child_sa_t *this)
{
@@ -603,7 +619,7 @@ METHOD(child_sa_t, alloc_spi, u_int32_t,
{
if (hydra->kernel_interface->get_spi(hydra->kernel_interface,
this->other_addr, this->my_addr,
- proto_ike2ip(protocol), this->reqid,
+ proto_ike2ip(protocol),
&this->my_spi) == SUCCESS)
{
/* if we allocate a SPI, but then are unable to establish the SA, we
@@ -619,7 +635,7 @@ METHOD(child_sa_t, alloc_cpi, u_int16_t,
{
if (hydra->kernel_interface->get_cpi(hydra->kernel_interface,
this->other_addr, this->my_addr,
- this->reqid, &this->my_cpi) == SUCCESS)
+ &this->my_cpi) == SUCCESS)
{
return this->my_cpi;
}
@@ -633,13 +649,12 @@ METHOD(child_sa_t, install, status_t,
{
u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size;
u_int16_t esn = NO_EXT_SEQ_NUMBERS;
- traffic_selector_t *src_ts = NULL, *dst_ts = NULL;
+ linked_list_t *src_ts = NULL, *dst_ts = NULL;
time_t now;
lifetime_cfg_t *lifetime;
u_int32_t tfc = 0;
host_t *src, *dst;
status_t status;
- bool update = FALSE;
/* 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,
@@ -648,10 +663,6 @@ METHOD(child_sa_t, install, status_t,
{
dst = this->my_addr;
src = this->other_addr;
- if (this->my_spi == spi)
- { /* alloc_spi has been called, do an SA update */
- update = TRUE;
- }
this->my_spi = spi;
this->my_cpi = cpi;
}
@@ -681,6 +692,18 @@ METHOD(child_sa_t, install, status_t,
this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS,
&esn, NULL);
+ if (!this->reqid_allocated)
+ {
+ status = hydra->kernel_interface->alloc_reqid(hydra->kernel_interface,
+ my_ts, other_ts, this->mark_in, this->mark_out,
+ &this->reqid);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ this->reqid_allocated = TRUE;
+ }
+
lifetime = this->config->get_lifetime(this->config);
now = time_monotonic(NULL);
@@ -705,18 +728,16 @@ METHOD(child_sa_t, install, status_t,
lifetime->time.rekey = 0;
}
- /* BEET requires the bound address from the traffic selectors.
- * TODO: We add just the first traffic selector for now, as the
- * kernel accepts a single TS per SA only */
+ /* BEET requires the bound address from the traffic selectors */
if (inbound)
{
- my_ts->get_first(my_ts, (void**)&dst_ts);
- other_ts->get_first(other_ts, (void**)&src_ts);
+ dst_ts = my_ts;
+ src_ts = other_ts;
}
else
{
- my_ts->get_first(my_ts, (void**)&src_ts);
- other_ts->get_first(other_ts, (void**)&dst_ts);
+ src_ts = my_ts;
+ dst_ts = other_ts;
}
status = hydra->kernel_interface->add_sa(hydra->kernel_interface,
@@ -724,7 +745,7 @@ METHOD(child_sa_t, install, status_t,
inbound ? this->mark_in : this->mark_out, tfc,
lifetime, enc_alg, encr, int_alg, integ, this->mode,
this->ipcomp, cpi, this->config->get_replay_window(this->config),
- initiator, this->encap, esn, update, src_ts, dst_ts);
+ initiator, this->encap, esn, inbound, src_ts, dst_ts);
free(lifetime);
@@ -799,6 +820,19 @@ METHOD(child_sa_t, add_policies, status_t,
traffic_selector_t *my_ts, *other_ts;
status_t status = SUCCESS;
+ if (!this->reqid_allocated)
+ {
+ /* trap policy, get or confirm reqid */
+ status = hydra->kernel_interface->alloc_reqid(
+ hydra->kernel_interface, my_ts_list, other_ts_list,
+ this->mark_in, this->mark_out, &this->reqid);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ this->reqid_allocated = TRUE;
+ }
+
/* apply traffic selectors */
enumerator = my_ts_list->create_enumerator(my_ts_list);
while (enumerator->enumerate(enumerator, &my_ts))
@@ -806,12 +840,15 @@ METHOD(child_sa_t, add_policies, status_t,
array_insert(this->my_ts, ARRAY_TAIL, my_ts->clone(my_ts));
}
enumerator->destroy(enumerator);
+ array_sort(this->my_ts, (void*)traffic_selector_cmp, NULL);
+
enumerator = other_ts_list->create_enumerator(other_ts_list);
while (enumerator->enumerate(enumerator, &other_ts))
{
array_insert(this->other_ts, ARRAY_TAIL, other_ts->clone(other_ts));
}
enumerator->destroy(enumerator);
+ array_sort(this->other_ts, (void*)traffic_selector_cmp, NULL);
if (this->config->install_policy(this->config))
{
@@ -1104,6 +1141,15 @@ METHOD(child_sa_t, destroy, void,
enumerator->destroy(enumerator);
}
+ if (this->reqid_allocated)
+ {
+ if (hydra->kernel_interface->release_reqid(hydra->kernel_interface,
+ this->reqid, this->mark_in, this->mark_out) != SUCCESS)
+ {
+ DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid);
+ }
+ }
+
array_destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
array_destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
this->my_addr->destroy(this->my_addr);
@@ -1152,15 +1198,17 @@ static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local)
* Described in header.
*/
child_sa_t * child_sa_create(host_t *me, host_t* other,
- child_cfg_t *config, u_int32_t rekey, bool encap)
+ child_cfg_t *config, u_int32_t rekey, bool encap,
+ u_int mark_in, u_int mark_out)
{
- static refcount_t reqid = 0;
private_child_sa_t *this;
+ static refcount_t unique_id = 0, unique_mark = 0, mark;
INIT(this,
.public = {
.get_name = _get_name,
.get_reqid = _get_reqid,
+ .get_unique_id = _get_unique_id,
.get_config = _get_config,
.get_state = _get_state,
.set_state = _set_state,
@@ -1202,6 +1250,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
.close_action = config->get_close_action(config),
.dpd_action = config->get_dpd_action(config),
.reqid = config->get_reqid(config),
+ .unique_id = ref_get(&unique_id),
.mark_in = config->get_mark(config, TRUE),
.mark_out = config->get_mark(config, FALSE),
.install_time = time_monotonic(NULL),
@@ -1210,9 +1259,37 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
this->config = config;
config->get_ref(config);
+ if (mark_in)
+ {
+ this->mark_in.value = mark_in;
+ }
+ if (mark_out)
+ {
+ this->mark_out.value = mark_out;
+ }
+ if (this->mark_in.value == MARK_UNIQUE ||
+ this->mark_out.value == MARK_UNIQUE)
+ {
+ mark = ref_get(&unique_mark);
+ if (this->mark_in.value == MARK_UNIQUE)
+ {
+ this->mark_in.value = mark;
+ }
+ if (this->mark_out.value == MARK_UNIQUE)
+ {
+ this->mark_out.value = mark;
+ }
+ }
+
if (!this->reqid)
{
- /* reuse old reqid if we are rekeying an existing CHILD_SA */
+ /* reuse old reqid if we are rekeying an existing CHILD_SA. While the
+ * reqid cache would find the same reqid for our selectors, this does
+ * not work in a special case: If an SA is triggered by a trap policy,
+ * but the negotiated SA gets narrowed, we still must reuse the same
+ * reqid to succesfully "trigger" the SA on the kernel level. Rekeying
+ * such an SA requires an explicit reqid, as the cache currently knows
+ * the original selectors only for that reqid. */
if (rekey)
{
this->reqid = rekey;
@@ -1220,22 +1297,9 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
else
{
this->reqid = charon->traps->find_reqid(charon->traps, config);
- if (!this->reqid)
- {
- this->reqid = ref_get(&reqid);
- }
}
}
- if (this->mark_in.value == MARK_REQID)
- {
- this->mark_in.value = this->reqid;
- }
- if (this->mark_out.value == MARK_REQID)
- {
- this->mark_out.value = this->reqid;
- }
-
/* MIPv6 proxy transport mode sets SA endpoints to TS hosts */
if (config->get_mode(config) == MODE_TRANSPORT &&
config->use_proxy_mode(config))