diff --git a/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c b/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c
index c9be8989a..5decde92b 100644
--- a/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c
+++ b/src/charon-tkm/src/tkm/tkm_kernel_ipsec.c
@@ -1,7 +1,8 @@
/*
+ * Copyright (C) 2017 Tobias Brunner
* Copyright (C) 2012-2014 Reto Buerki
* Copyright (C) 2012 Adrian-Ken Rueegsegger
- * 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
@@ -52,6 +53,12 @@ struct private_tkm_kernel_ipsec_t {
};
+METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
+ private_tkm_kernel_ipsec_t *this)
+{
+ return KERNEL_POLICY_SPI;
+}
+
METHOD(kernel_ipsec_t, get_spi, status_t,
private_tkm_kernel_ipsec_t *this, host_t *src, host_t *dst,
uint8_t protocol, uint32_t *spi)
@@ -176,15 +183,6 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
tkm->chunk_map->remove(tkm->chunk_map, nonce_loc);
tkm->idmgr->release_id(tkm->idmgr, TKM_CTX_NONCE, nonce_loc_id);
}
- if (ike_esa_select(esa_id) != TKM_OK)
- {
- DBG1(DBG_KNL, "error selecting new child SA (%llu)", esa_id);
- if (ike_esa_reset(esa_id) != TKM_OK)
- {
- DBG1(DBG_KNL, "child SA (%llu) deletion failed", esa_id);
- }
- goto failure;
- }
DBG1(DBG_KNL, "added child SA (esa: %llu, isa: %llu, esp_spi_loc: %x, "
"esp_spi_rem: %x, role: %s)", esa_id, esa.isa_id, ntohl(spi_loc),
@@ -215,23 +213,12 @@ METHOD(kernel_ipsec_t, del_sa, status_t,
private_tkm_kernel_ipsec_t *this, kernel_ipsec_sa_id_t *id,
kernel_ipsec_del_sa_t *data)
{
- esa_id_type esa_id, other_esa_id;
+ esa_id_type esa_id;
esa_id = tkm->sad->get_esa_id(tkm->sad, id->src, id->dst,
- id->spi, id->proto);
+ id->spi, id->proto, TRUE);
if (esa_id)
{
- other_esa_id = tkm->sad->get_other_esa_id(tkm->sad, esa_id);
- if (other_esa_id)
- {
- DBG1(DBG_KNL, "selecting child SA (esa: %llu)", other_esa_id);
- if (ike_esa_select(other_esa_id) != TKM_OK)
- {
- DBG1(DBG_KNL, "error selecting other child SA (esa: %llu)",
- other_esa_id);
- }
- }
-
DBG1(DBG_KNL, "deleting child SA (esa: %llu, spi: %x)", esa_id,
ntohl(id->spi));
if (ike_esa_reset(esa_id) != TKM_OK)
@@ -263,6 +250,43 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
private_tkm_kernel_ipsec_t *this, kernel_ipsec_policy_id_t *id,
kernel_ipsec_manage_policy_t *data)
{
+ esa_id_type esa_id;
+ uint32_t spi;
+ uint8_t proto;
+
+ if (id->dir == POLICY_OUT && data->type == POLICY_IPSEC &&
+ data->prio == POLICY_PRIORITY_DEFAULT)
+ {
+ if (data->sa->esp.use)
+ {
+ spi = data->sa->esp.spi;
+ proto = IPPROTO_ESP;
+ }
+ else if (data->sa->ah.use)
+ {
+ spi = data->sa->ah.spi;
+ proto = IPPROTO_AH;
+ }
+ else
+ {
+ return FAILED;
+ }
+ esa_id = tkm->sad->get_esa_id(tkm->sad, data->src, data->dst,
+ spi, proto, FALSE);
+ if (!esa_id)
+ {
+ DBG1(DBG_KNL, "unable to find esa ID for policy (spi: %x)",
+ ntohl(spi));
+ return FAILED;
+ }
+ DBG1(DBG_KNL, "selecting child SA (esa: %llu, spi: %x)", esa_id,
+ ntohl(spi));
+ if (ike_esa_select(esa_id) != TKM_OK)
+ {
+ DBG1(DBG_KNL, "error selecting new child SA (%llu)", esa_id);
+ return FAILED;
+ }
+ }
return SUCCESS;
}
@@ -358,6 +382,7 @@ tkm_kernel_ipsec_t *tkm_kernel_ipsec_create()
INIT(this,
.public = {
.interface = {
+ .get_features = _get_features,
.get_spi = _get_spi,
.get_cpi = _get_cpi,
.add_sa = _add_sa,
diff --git a/src/charon-tkm/src/tkm/tkm_kernel_sad.c b/src/charon-tkm/src/tkm/tkm_kernel_sad.c
index c888f2561..acc3ff10a 100644
--- a/src/charon-tkm/src/tkm/tkm_kernel_sad.c
+++ b/src/charon-tkm/src/tkm/tkm_kernel_sad.c
@@ -107,16 +107,23 @@ CALLBACK(sad_entry_match, bool,
const host_t *src, *dst;
const uint32_t *spi;
const uint8_t *proto;
+ const bool *local;
- VA_ARGS_VGET(args, src, dst, spi, proto);
+ VA_ARGS_VGET(args, src, dst, spi, proto, local);
- if (entry->src == NULL || entry->dst == NULL)
+ if (entry->src == NULL || entry->dst == NULL || entry->proto != *proto)
{
return FALSE;
}
- return src->ip_equals(entry->src, (host_t *)src) &&
- dst->ip_equals(entry->dst, (host_t *)dst) &&
- entry->spi_rem == *spi && entry->proto == *proto;
+ if (*local)
+ {
+ return entry->src->ip_equals(entry->src, (host_t *)dst) &&
+ entry->dst->ip_equals(entry->dst, (host_t *)src) &&
+ entry->spi_loc == *spi;
+ }
+ return entry->src->ip_equals(entry->src, (host_t *)src) &&
+ entry->dst->ip_equals(entry->dst, (host_t *)dst) &&
+ entry->spi_rem == *spi;
}
CALLBACK(sad_entry_match_dst, bool,
@@ -131,26 +138,6 @@ CALLBACK(sad_entry_match_dst, bool,
entry->proto == *proto;
}
-CALLBACK(sad_entry_match_esa_id, bool,
- sad_entry_t * const entry, va_list args)
-{
- const esa_id_type *esa_id;
-
- VA_ARGS_VGET(args, esa_id);
- return entry->esa_id == *esa_id;
-}
-
-CALLBACK(sad_entry_match_other_esa, bool,
- sad_entry_t * const entry, va_list args)
-{
- const esa_id_type *esa_id;
- const uint32_t *reqid;
-
- VA_ARGS_VGET(args, esa_id, reqid);
- return entry->reqid == *reqid &&
- entry->esa_id != *esa_id;
-}
-
CALLBACK(sad_entry_equal, bool,
sad_entry_t * const left, va_list args)
{
@@ -213,7 +200,8 @@ METHOD(tkm_kernel_sad_t, insert, bool,
METHOD(tkm_kernel_sad_t, get_esa_id, esa_id_type,
private_tkm_kernel_sad_t * const this, const host_t * const src,
- const host_t * const dst, const uint32_t spi, const uint8_t proto)
+ const host_t * const dst, const uint32_t spi, const uint8_t proto,
+ const bool local)
{
esa_id_type id = 0;
sad_entry_t *entry = NULL;
@@ -221,51 +209,18 @@ METHOD(tkm_kernel_sad_t, get_esa_id, esa_id_type,
this->mutex->lock(this->mutex);
const bool res = this->data->find_first(this->data, sad_entry_match,
(void**)&entry, src, dst, &spi,
- &proto);
+ &proto, &local);
if (res && entry)
{
id = entry->esa_id;
DBG3(DBG_KNL, "returning ESA id %llu of SAD entry (src: %H, dst: %H, "
- "spi: %x, proto: %u)", id, src, dst, ntohl(spi), proto);
+ "%sbound spi: %x, proto: %u)", id, src, dst, local ? "in" : "out",
+ ntohl(spi), proto);
}
else
{
- DBG3(DBG_KNL, "no SAD entry found for src %H, dst %H, spi %x, proto %u",
- src, dst, ntohl(spi), proto);
- }
- this->mutex->unlock(this->mutex);
- return id;
-}
-
-METHOD(tkm_kernel_sad_t, get_other_esa_id, esa_id_type,
- private_tkm_kernel_sad_t * const this, const esa_id_type esa_id)
-{
- esa_id_type id = 0;
- sad_entry_t *entry = NULL;
- uint32_t reqid;
- bool res;
-
- this->mutex->lock(this->mutex);
- res = this->data->find_first(this->data, sad_entry_match_esa_id,
- (void**)&entry, &esa_id);
- if (res && entry)
- {
- reqid = entry->reqid;
- }
- else
- {
- DBG3(DBG_KNL, "no SAD entry found for ESA id %llu", esa_id);
- this->mutex->unlock(this->mutex);
- return id;
- }
-
- res = this->data->find_first(this->data, sad_entry_match_other_esa,
- (void**)&entry, &esa_id, &reqid);
- if (res && entry)
- {
- id = entry->esa_id;
- DBG3(DBG_KNL, "returning ESA id %llu of other SAD entry with reqid %u",
- id, reqid);
+ DBG3(DBG_KNL, "no SAD entry found for src %H, dst %H, %sbound spi %x, "
+ "proto %u", src, dst, local ? "in" : "out", ntohl(spi), proto);
}
this->mutex->unlock(this->mutex);
return id;
@@ -350,7 +305,6 @@ tkm_kernel_sad_t *tkm_kernel_sad_create()
.public = {
.insert = _insert,
.get_esa_id = _get_esa_id,
- .get_other_esa_id = _get_other_esa_id,
.get_dst_host = _get_dst_host,
.remove = __remove,
.destroy = _destroy,
diff --git a/src/charon-tkm/src/tkm/tkm_kernel_sad.h b/src/charon-tkm/src/tkm/tkm_kernel_sad.h
index 63d02b7e4..3d9f5f3f8 100644
--- a/src/charon-tkm/src/tkm/tkm_kernel_sad.h
+++ b/src/charon-tkm/src/tkm/tkm_kernel_sad.h
@@ -55,23 +55,14 @@ struct tkm_kernel_sad_t {
*
* @param src source address of CHILD SA
* @param dst destination address of CHILD SA
- * @param spi Remote SPI of CHILD SA
+ * @param spi SPI of CHILD SA
* @param proto protocol of CHILD SA (ESP/AH)
+ * @param local whether the SPI is local or remote
* @return ESA id of entry if found, 0 otherwise
*/
esa_id_type (*get_esa_id)(tkm_kernel_sad_t * const this,
const host_t * const src, const host_t * const dst,
- const uint32_t spi, const uint8_t proto);
-
- /**
- * Get ESA id for entry associated with same security policy as the
- * specified ESA.
- *
- * @param esa_id id of ESA identifying the security policy
- * @return ESA id of entry if found, 0 otherwise
- */
- esa_id_type (*get_other_esa_id)(tkm_kernel_sad_t * const this,
- const esa_id_type esa_id);
+ const uint32_t spi, const uint8_t proto, const bool local);
/**
* Get destination host for entry with given parameters.
diff --git a/src/charon-tkm/tests/kernel_sad_tests.c b/src/charon-tkm/tests/kernel_sad_tests.c
index 2a033d237..39d8a790a 100644
--- a/src/charon-tkm/tests/kernel_sad_tests.c
+++ b/src/charon-tkm/tests/kernel_sad_tests.c
@@ -63,7 +63,20 @@ START_TEST(test_get_esa_id)
tkm_kernel_sad_t *sad = tkm_kernel_sad_create();
fail_unless(sad->insert(sad, 23, 54, addr, addr, 27, 42, 50),
"Error inserting SAD entry");
- fail_unless(sad->get_esa_id(sad, addr, addr, 42, 50) == 23,
+ fail_unless(sad->get_esa_id(sad, addr, addr, 42, 50, FALSE) == 23,
+ "Error getting esa id");
+ sad->destroy(sad);
+ addr->destroy(addr);
+}
+END_TEST
+
+START_TEST(test_get_esa_id_local)
+{
+ host_t *addr = host_create_from_string("127.0.0.1", 1024);
+ tkm_kernel_sad_t *sad = tkm_kernel_sad_create();
+ fail_unless(sad->insert(sad, 23, 54, addr, addr, 27, 42, 50),
+ "Error inserting SAD entry");
+ fail_unless(sad->get_esa_id(sad, addr, addr, 27, 50, TRUE) == 23,
"Error getting esa id");
sad->destroy(sad);
addr->destroy(addr);
@@ -74,44 +87,13 @@ START_TEST(test_get_esa_id_nonexistent)
{
host_t *addr = host_create_from_string("127.0.0.1", 1024);
tkm_kernel_sad_t *sad = tkm_kernel_sad_create();
- fail_unless(sad->get_esa_id(sad, addr, addr, 42, 50) == 0,
+ fail_unless(sad->get_esa_id(sad, addr, addr, 42, 50, FALSE) == 0,
"Got esa id for nonexistent SAD entry");
sad->destroy(sad);
addr->destroy(addr);
}
END_TEST
-START_TEST(test_get_other_esa_id)
-{
- host_t *addr = host_create_from_string("127.0.0.1", 1024);
- tkm_kernel_sad_t *sad = tkm_kernel_sad_create();
- fail_unless(sad->insert(sad, 23, 54, addr, addr, 27, 42, 50),
- "Error inserting SAD entry");
- fail_unless(sad->insert(sad, 24, 54, addr, addr, 27, 42, 50),
- "Error inserting SAD entry");
- fail_unless(sad->get_other_esa_id(sad, 23) == 24,
- "Error getting other esa id");
- sad->destroy(sad);
- addr->destroy(addr);
-}
-END_TEST
-
-START_TEST(test_get_other_esa_id_nonexistent)
-{
- host_t *addr = host_create_from_string("127.0.0.1", 1024);
- tkm_kernel_sad_t *sad = tkm_kernel_sad_create();
- fail_unless(sad->get_other_esa_id(sad, 1) == 0,
- "Got other esa id for nonexistent SAD entry");
- fail_unless(sad->insert(sad, 23, 54, addr, addr, 27, 42, 50),
- "Error inserting SAD entry");
- fail_unless(sad->get_other_esa_id(sad, 23) == 0,
- "Got own esa id");
-
- sad->destroy(sad);
- addr->destroy(addr);
-}
-END_TEST
-
START_TEST(test_get_dst_host)
{
host_t *addr = host_create_from_string("127.0.0.1", 1024);
@@ -179,14 +161,10 @@ Suite *make_kernel_sad_tests()
tc = tcase_create("get_esa_id");
tcase_add_test(tc, test_get_esa_id);
+ tcase_add_test(tc, test_get_esa_id_local);
tcase_add_test(tc, test_get_esa_id_nonexistent);
suite_add_tcase(s, tc);
- tc = tcase_create("get_other_esa_id");
- tcase_add_test(tc, test_get_other_esa_id);
- tcase_add_test(tc, test_get_other_esa_id_nonexistent);
- suite_add_tcase(s, tc);
-
tc = tcase_create("get_dst_host");
tcase_add_test(tc, test_get_dst_host);
tcase_add_test(tc, test_get_dst_host_nonexistent);
diff --git a/src/libcharon/bus/bus.c b/src/libcharon/bus/bus.c
index 77a910197..19943d060 100644
--- a/src/libcharon/bus/bus.c
+++ b/src/libcharon/bus/bus.c
@@ -827,7 +827,10 @@ METHOD(bus_t, ike_updown, void,
enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
- child_updown(this, child_sa, FALSE);
+ if (child_sa->get_state(child_sa) != CHILD_REKEYED)
+ {
+ child_updown(this, child_sa, FALSE);
+ }
}
enumerator->destroy(enumerator);
}
diff --git a/src/libcharon/kernel/kernel_interface.h b/src/libcharon/kernel/kernel_interface.h
index d601ebd4e..f4f55adfe 100644
--- a/src/libcharon/kernel/kernel_interface.h
+++ b/src/libcharon/kernel/kernel_interface.h
@@ -77,6 +77,8 @@ enum kernel_feature_t {
KERNEL_REQUIRE_UDP_ENCAPSULATION = (1<<2),
/** IPsec backend does not require a policy reinstall on SA updates */
KERNEL_NO_POLICY_UPDATES = (1<<3),
+ /** IPsec backend supports installing SPIs on policies */
+ KERNEL_POLICY_SPI = (1<<4),
};
/**
diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
index c411b829d..8ddaa71d3 100644
--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
+++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
@@ -1141,7 +1141,7 @@ static bool receive_events(private_kernel_netlink_ipsec_t *this, int fd,
METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
private_kernel_netlink_ipsec_t *this)
{
- return KERNEL_ESP_V3_TFC;
+ return KERNEL_ESP_V3_TFC | KERNEL_POLICY_SPI;
}
/**
@@ -2409,11 +2409,13 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
struct xfrm_user_tmpl *tmpl;
struct {
uint8_t proto;
+ uint32_t spi;
bool use;
} protos[] = {
- { IPPROTO_COMP, ipsec->cfg.ipcomp.transform != IPCOMP_NONE },
- { IPPROTO_ESP, ipsec->cfg.esp.use },
- { IPPROTO_AH, ipsec->cfg.ah.use },
+ { IPPROTO_COMP, htonl(ntohs(ipsec->cfg.ipcomp.cpi)),
+ ipsec->cfg.ipcomp.transform != IPCOMP_NONE },
+ { IPPROTO_ESP, ipsec->cfg.esp.spi, ipsec->cfg.esp.use },
+ { IPPROTO_AH, ipsec->cfg.ah.spi, ipsec->cfg.ah.use },
};
ipsec_mode_t proto_mode = ipsec->cfg.mode;
int count = 0;
@@ -2441,6 +2443,10 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
}
tmpl->reqid = ipsec->cfg.reqid;
tmpl->id.proto = protos[i].proto;
+ if (policy->direction == POLICY_OUT)
+ {
+ tmpl->id.spi = protos[i].spi;
+ }
tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0;
tmpl->mode = mode2kernel(proto_mode);
tmpl->optional = protos[i].proto == IPPROTO_COMP &&
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index 3d9f6133b..6c6804c64 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -40,10 +40,10 @@ ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING,
"DESTROYING",
);
-ENUM(child_sa_outbound_state_names, CHILD_OUTBOUND_NONE, CHILD_OUTBOUND_INSTALLED,
- "NONE",
+ENUM_FLAGS(child_sa_outbound_state_names, CHILD_OUTBOUND_REGISTERED, CHILD_OUTBOUND_POLICIES,
"REGISTERED",
- "INSTALLED",
+ "SA",
+ "POLICIES",
);
typedef struct private_child_sa_t private_child_sa_t;
@@ -547,7 +547,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
}
else
{
- if (this->other_spi && this->outbound_state == CHILD_OUTBOUND_INSTALLED)
+ if (this->other_spi && (this->outbound_state & CHILD_OUTBOUND_SA))
{
kernel_ipsec_sa_id_t id = {
.src = this->my_addr,
@@ -788,7 +788,7 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr,
{
tfc = this->config->get_tfc(this->config);
}
- this->outbound_state = CHILD_OUTBOUND_INSTALLED;
+ this->outbound_state |= CHILD_OUTBOUND_SA;
}
DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound",
@@ -1188,6 +1188,7 @@ METHOD(child_sa_t, install_policies, status_t,
linked_list_t *my_ts_list, *other_ts_list;
traffic_selector_t *my_ts, *other_ts;
status_t status = SUCCESS;
+ bool install_outbound = FALSE;
if (!this->reqid_allocated && !this->static_reqid)
{
@@ -1207,12 +1208,17 @@ METHOD(child_sa_t, install_policies, status_t,
this->reqid_allocated = TRUE;
}
+ if (!(this->outbound_state & CHILD_OUTBOUND_REGISTERED))
+ {
+ install_outbound = TRUE;
+ this->outbound_state |= CHILD_OUTBOUND_POLICIES;
+ }
+
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);
@@ -1222,7 +1228,6 @@ METHOD(child_sa_t, install_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);
@@ -1250,7 +1255,6 @@ METHOD(child_sa_t, install_policies, status_t,
this->other_addr, my_ts, other_ts,
&my_sa, &other_sa, POLICY_IPSEC,
priority, manual_prio);
-
}
if (status != SUCCESS)
{
@@ -1267,21 +1271,35 @@ METHOD(child_sa_t, install_policies, status_t,
return status;
}
-METHOD(child_sa_t, register_outbound, void,
+METHOD(child_sa_t, register_outbound, status_t,
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);
+ status_t status;
- 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;
+ /* if the kernel supports installing SPIs with policies we install the
+ * SA immediately as it will only be used once we update the policies */
+ if (charon->kernel->get_features(charon->kernel) & KERNEL_POLICY_SPI)
+ {
+ status = install_internal(this, encr, integ, spi, cpi, FALSE, FALSE,
+ tfcv3);
+ }
+ else
+ {
+ 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;
+ status = SUCCESS;
+ }
+ this->outbound_state |= CHILD_OUTBOUND_REGISTERED;
+ return status;
}
METHOD(child_sa_t, install_outbound, status_t,
@@ -1289,18 +1307,23 @@ METHOD(child_sa_t, install_outbound, status_t,
{
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
- status_t status;
+ status_t status = SUCCESS;
- 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 (!(this->outbound_state & CHILD_OUTBOUND_SA))
+ {
+ 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);
+ }
+ this->outbound_state &= ~CHILD_OUTBOUND_REGISTERED;
if (status != SUCCESS)
{
return status;
}
- if (!this->config->has_option(this->config, OPT_NO_POLICIES))
+ if (!this->config->has_option(this->config, OPT_NO_POLICIES) &&
+ !(this->outbound_state & CHILD_OUTBOUND_POLICIES))
{
ipsec_sa_cfg_t my_sa, other_sa;
uint32_t manual_prio;
@@ -1331,6 +1354,7 @@ METHOD(child_sa_t, install_outbound, status_t,
}
enumerator->destroy(enumerator);
}
+ this->outbound_state |= CHILD_OUTBOUND_POLICIES;
return status;
}
@@ -1340,20 +1364,19 @@ METHOD(child_sa_t, remove_outbound, void,
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
- switch (this->outbound_state)
+ if (!(this->outbound_state & CHILD_OUTBOUND_SA))
{
- case CHILD_OUTBOUND_INSTALLED:
- break;
- case CHILD_OUTBOUND_REGISTERED:
+ if (this->outbound_state & 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;
+ }
+ return;
}
- if (!this->config->has_option(this->config, OPT_NO_POLICIES))
+ if (!this->config->has_option(this->config, OPT_NO_POLICIES) &&
+ (this->outbound_state & CHILD_OUTBOUND_POLICIES))
{
ipsec_sa_cfg_t my_sa, other_sa;
uint32_t manual_prio;
@@ -1598,8 +1621,8 @@ METHOD(child_sa_t, destroy, void,
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;
+ del_outbound = (this->outbound_state & CHILD_OUTBOUND_POLICIES) ||
+ this->trap;
/* delete all policies in the kernel */
enumerator = create_policy_enumerator(this);
@@ -1640,7 +1663,7 @@ METHOD(child_sa_t, destroy, void,
};
charon->kernel->del_sa(charon->kernel, &id, &sa);
}
- if (this->other_spi && this->outbound_state == CHILD_OUTBOUND_INSTALLED)
+ if (this->other_spi && (this->outbound_state & CHILD_OUTBOUND_SA))
{
kernel_ipsec_sa_id_t id = {
.src = this->my_addr,
diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h
index b9a913da1..082404d93 100644
--- a/src/libcharon/sa/child_sa.h
+++ b/src/libcharon/sa/child_sa.h
@@ -102,17 +102,28 @@ enum child_sa_outbound_state_t {
/**
* Outbound SA is not installed
*/
- CHILD_OUTBOUND_NONE,
+ CHILD_OUTBOUND_NONE = 0,
/**
- * Data for the outbound SA has been registered, but not installed yet
+ * Data for the outbound SA has been registered during a rekeying (not set
+ * once the SA and policies are both installed)
*/
- CHILD_OUTBOUND_REGISTERED,
+ CHILD_OUTBOUND_REGISTERED = (1<<0),
/**
- * The outbound SA is currently installed
+ * The outbound SA has been installed
*/
- CHILD_OUTBOUND_INSTALLED,
+ CHILD_OUTBOUND_SA = (1<<1),
+
+ /**
+ * The outbound policies have been installed
+ */
+ CHILD_OUTBOUND_POLICIES = (1<<2),
+
+ /**
+ * The outbound SA and policies are both installed
+ */
+ CHILD_OUTBOUND_INSTALLED = (CHILD_OUTBOUND_SA|CHILD_OUTBOUND_POLICIES),
};
/**
@@ -400,20 +411,23 @@ struct child_sa_t {
* Register data for the installation of an outbound SA as responder during
* a rekeying.
*
- * The SA is not installed until install_outbound() is called.
+ * If the kernel is able to handle SPIs on policies the SA is installed
+ * immediately, if not it won't be 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
+ * @return SUCCESS or FAILED
*/
- void (*register_outbound)(child_sa_t *this, chunk_t encr, chunk_t integ,
- uint32_t spi, uint16_t cpi, bool tfcv3);
+ status_t (*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.
+ * Install the outbound policies and, if not already done, the outbound SA
+ * as responder during a rekeying.
*
* @return SUCCESS or FAILED
*/
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c
index 747b9fac4..9fe6929c1 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.c
+++ b/src/libcharon/sa/ikev2/tasks/child_create.c
@@ -478,6 +478,7 @@ static status_t select_and_install(private_child_create_t *this,
bool no_dh, bool ike_auth)
{
status_t status, status_i, status_o;
+ child_sa_outbound_state_t out_state;
chunk_t nonce_i, nonce_r;
chunk_t encr_i = chunk_empty, encr_r = chunk_empty;
chunk_t integ_i = chunk_empty, integ_r = chunk_empty;
@@ -678,29 +679,42 @@ 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);
- status_o = this->child_sa->install(this->child_sa, encr_i, integ_i,
- this->other_spi, this->other_cpi, this->initiator,
- FALSE, this->tfcv3);
}
- else if (!this->rekey)
+ else
{
status_i = this->child_sa->install(this->child_sa, encr_i, integ_i,
this->my_spi, this->my_cpi, this->initiator,
TRUE, this->tfcv3);
- status_o = this->child_sa->install(this->child_sa, encr_r, integ_r,
+ }
+ if (this->rekey)
+ { /* during rekeyings we install the outbound SA and/or policies
+ * separately: as responder when we receive the delete for the old
+ * SA, as initiator pretty much immediately in the ike-rekey task,
+ * unless there was a rekey collision that we lost */
+ if (this->initiator)
+ {
+ status_o = this->child_sa->register_outbound(this->child_sa,
+ encr_i, integ_i, this->other_spi, this->other_cpi,
+ this->tfcv3);
+ }
+ else
+ {
+ status_o = this->child_sa->register_outbound(this->child_sa,
+ encr_r, integ_r, this->other_spi, this->other_cpi,
+ this->tfcv3);
+ }
+ }
+ else if (this->initiator)
+ {
+ status_o = this->child_sa->install(this->child_sa, encr_i, integ_i,
this->other_spi, this->other_cpi, this->initiator,
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;
+ {
+ status_o = this->child_sa->install(this->child_sa, encr_r, integ_r,
+ this->other_spi, this->other_cpi, this->initiator,
+ FALSE, this->tfcv3);
}
}
@@ -749,10 +763,11 @@ static status_t select_and_install(private_child_create_t *this,
this->child_sa->create_ts_enumerator(this->child_sa, TRUE));
other_ts = linked_list_create_from_enumerator(
this->child_sa->create_ts_enumerator(this->child_sa, FALSE));
+ out_state = this->child_sa->get_outbound_state(this->child_sa);
DBG0(DBG_IKE, "%sCHILD_SA %s{%d} established "
"with SPIs %.8x_i %.8x_o and TS %#R === %#R",
- this->rekey && !this->initiator ? "inbound " : "",
+ (out_state == CHILD_OUTBOUND_INSTALLED) ? "" : "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)),
diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c
index 626796383..2217295b6 100644
--- a/src/libcharon/sa/ikev2/tasks/child_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.c
@@ -196,7 +196,6 @@ static void install_outbound(private_child_delete_t *this,
/* 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));
diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c
index 761c860e7..619ec4171 100644
--- a/src/libcharon/sa/ikev2/tasks/child_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c
@@ -283,7 +283,8 @@ METHOD(task_t, build_r, status_t,
/**
* Handle a rekey collision
*/
-static child_sa_t *handle_collision(private_child_rekey_t *this)
+static child_sa_t *handle_collision(private_child_rekey_t *this,
+ child_sa_t **to_install)
{
child_sa_t *to_delete;
@@ -303,6 +304,7 @@ static child_sa_t *handle_collision(private_child_rekey_t *this)
child_sa_t *child_sa;
DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting old child");
+ *to_install = this->child_create->get_child(this->child_create);
to_delete = this->child_sa;
/* don't touch child other created, it has already been deleted */
if (!this->other_child_destroyed)
@@ -353,7 +355,7 @@ METHOD(task_t, process_i, status_t,
{
protocol_id_t protocol;
uint32_t spi;
- child_sa_t *to_delete;
+ child_sa_t *to_delete, *to_install = NULL;
if (message->get_notify(message, NO_ADDITIONAL_SAS))
{
@@ -415,19 +417,48 @@ METHOD(task_t, process_i, status_t,
/* check for rekey collisions */
if (this->collision)
{
- to_delete = handle_collision(this);
+ to_delete = handle_collision(this, &to_install);
}
else
{
+ to_install = this->child_create->get_child(this->child_create);
to_delete = this->child_sa;
}
+ if (to_install)
+ {
+ if (to_install->install_outbound(to_install) != SUCCESS)
+ {
+ DBG1(DBG_IKE, "unable to install outbound IPsec SA (SAD) in kernel");
+ charon->bus->alert(charon->bus, ALERT_INSTALL_CHILD_SA_FAILED,
+ to_install);
+ /* FIXME: delete the child_sa? fail the task? */
+ }
+ else
+ {
+ linked_list_t *my_ts, *other_ts;
+ my_ts = linked_list_create_from_enumerator(
+ to_install->create_ts_enumerator(to_install, TRUE));
+ other_ts = linked_list_create_from_enumerator(
+ to_install->create_ts_enumerator(to_install, FALSE));
+
+ DBG0(DBG_IKE, "outbound CHILD_SA %s{%d} established "
+ "with SPIs %.8x_i %.8x_o and TS %#R === %#R",
+ to_install->get_name(to_install),
+ to_install->get_unique_id(to_install),
+ ntohl(to_install->get_spi(to_install, TRUE)),
+ ntohl(to_install->get_spi(to_install, FALSE)),
+ my_ts, other_ts);
+
+ my_ts->destroy(my_ts);
+ other_ts->destroy(other_ts);
+ }
+ }
if (to_delete != this->child_create->get_child(this->child_create))
{ /* invoke rekey hook if rekeying successful */
charon->bus->child_rekey(charon->bus, this->child_sa,
this->child_create->get_child(this->child_create));
}
-
if (to_delete == NULL)
{
return SUCCESS;
diff --git a/src/libcharon/tests/suites/test_child_rekey.c b/src/libcharon/tests/suites/test_child_rekey.c
index 76b23f589..ac169723f 100644
--- a/src/libcharon/tests/suites/test_child_rekey.c
+++ b/src/libcharon/tests/suites/test_child_rekey.c
@@ -483,6 +483,9 @@ START_TEST(test_collision)
CHILD_OUTBOUND_REGISTERED);
assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED,
CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING,
+ CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(a, 1, 2, 3, 5, 6);
}
else
{
@@ -493,10 +496,10 @@ START_TEST(test_collision)
CHILD_OUTBOUND_INSTALLED);
assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED,
CHILD_OUTBOUND_REGISTERED);
+ assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING,
+ CHILD_OUTBOUND_REGISTERED);
+ assert_ipsec_sas_installed(a, 1, 2, 3, 6);
}
- assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING,
- CHILD_OUTBOUND_INSTALLED);
- assert_ipsec_sas_installed(a, 1, 2, 3, 5, 6);
/* CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } --> */
if (data[_i].spi_del_b == 2)
{
@@ -507,6 +510,9 @@ START_TEST(test_collision)
CHILD_OUTBOUND_REGISTERED);
assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED,
CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING,
+ CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(b, 1, 2, 4, 5, 6);
}
else
{
@@ -517,10 +523,10 @@ START_TEST(test_collision)
CHILD_OUTBOUND_INSTALLED);
assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED,
CHILD_OUTBOUND_REGISTERED);
+ assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING,
+ CHILD_OUTBOUND_REGISTERED);
+ assert_ipsec_sas_installed(b, 1, 2, 4, 5);
}
- assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING,
- CHILD_OUTBOUND_INSTALLED);
- assert_ipsec_sas_installed(b, 1, 2, 4, 5, 6);
/* we don't expect this hook to get called anymore */
assert_hook_not_called(child_rekey);
@@ -528,27 +534,41 @@ START_TEST(test_collision)
assert_jobs_scheduled(1);
exchange_test_helper->process_message(exchange_test_helper, b, NULL);
assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING,
- CHILD_OUTBOUND_INSTALLED);
+ data[_i].spi_del_b == 2 ? CHILD_OUTBOUND_INSTALLED
+ : CHILD_OUTBOUND_REGISTERED);
assert_child_sa_state(b, data[_i].spi_del_a, CHILD_DELETING,
CHILD_OUTBOUND_NONE);
assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED,
CHILD_OUTBOUND_INSTALLED);
assert_child_sa_count(b, 3);
- assert_ipsec_sas_installed(b, 2, 4, 5, 6,
- data[_i].spi_del_b == 2 ? 1 : 3);
+ if (data[_i].spi_del_b == 2)
+ {
+ assert_ipsec_sas_installed(b, 1, 2, 4, 5, 6);
+ }
+ else
+ {
+ assert_ipsec_sas_installed(b, 2, 3, 4, 5);
+ }
assert_scheduler();
/* <-- INFORMATIONAL { D } */
assert_jobs_scheduled(1);
exchange_test_helper->process_message(exchange_test_helper, a, NULL);
assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING,
- CHILD_OUTBOUND_INSTALLED);
+ data[_i].spi_del_a == 1 ? CHILD_OUTBOUND_INSTALLED
+ : CHILD_OUTBOUND_REGISTERED);
assert_child_sa_state(a, data[_i].spi_del_b, CHILD_DELETING,
CHILD_OUTBOUND_NONE);
assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED,
CHILD_OUTBOUND_INSTALLED);
assert_child_sa_count(a, 3);
- assert_ipsec_sas_installed(a, 1, 3, 5, 6,
- data[_i].spi_del_a == 1 ? 2 : 4);
+ if (data[_i].spi_del_a == 1)
+ {
+ assert_ipsec_sas_installed(a, 1, 2, 3, 5, 6);
+ }
+ else
+ {
+ assert_ipsec_sas_installed(a, 1, 3, 4, 6);
+ }
assert_scheduler();
/* <-- INFORMATIONAL { D } */
assert_jobs_scheduled(1);
@@ -682,6 +702,9 @@ START_TEST(test_collision_delayed_response)
CHILD_OUTBOUND_REGISTERED);
assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED,
CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING,
+ CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(b, 1, 2, 4, 5, 6);
}
else
{
@@ -692,10 +715,10 @@ START_TEST(test_collision_delayed_response)
CHILD_OUTBOUND_INSTALLED);
assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED,
CHILD_OUTBOUND_REGISTERED);
+ assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING,
+ CHILD_OUTBOUND_REGISTERED);
+ assert_ipsec_sas_installed(b, 1, 2, 4, 5);
}
- assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING,
- CHILD_OUTBOUND_INSTALLED);
- assert_ipsec_sas_installed(b, 1, 2, 4, 5, 6);
/* <-- INFORMATIONAL { D } */
assert_hook_not_called(child_rekey);
@@ -748,21 +771,23 @@ START_TEST(test_collision_delayed_response)
assert_hook_rekey(child_rekey, 1, data[_i].spi_a);
exchange_test_helper->process_message(exchange_test_helper, a, msg);
assert_hook();
+ assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING,
+ CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(a, 1, 2, 3, 5, 6);
}
else
{
assert_hook_not_called(child_rekey);
exchange_test_helper->process_message(exchange_test_helper, a, msg);
assert_hook();
+ assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING,
+ CHILD_OUTBOUND_REGISTERED);
+ assert_ipsec_sas_installed(a, 1, 3, 4, 6);
}
- assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING,
- CHILD_OUTBOUND_INSTALLED);
assert_child_sa_state(a, data[_i].spi_del_b, CHILD_DELETING,
CHILD_OUTBOUND_NONE);
assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED,
CHILD_OUTBOUND_INSTALLED);
- assert_ipsec_sas_installed(a, 1, 3, 5, 6,
- data[_i].spi_del_a == 1 ? 2 : 4);
assert_child_sa_count(a, 3);
/* we don't expect this hook to get called anymore */
@@ -1173,6 +1198,8 @@ START_TEST(test_collision_ke_invalid)
CHILD_OUTBOUND_REGISTERED);
assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED,
CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING,
+ CHILD_OUTBOUND_INSTALLED);
}
else
{
@@ -1181,9 +1208,9 @@ START_TEST(test_collision_ke_invalid)
CHILD_OUTBOUND_INSTALLED);
assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED,
CHILD_OUTBOUND_REGISTERED);
+ assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING,
+ CHILD_OUTBOUND_REGISTERED);
}
- assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING,
- CHILD_OUTBOUND_INSTALLED);
/* CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } --> */
if (data[_i].spi_del_b == 2)
{
@@ -1194,6 +1221,8 @@ START_TEST(test_collision_ke_invalid)
CHILD_OUTBOUND_REGISTERED);
assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED,
CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING,
+ CHILD_OUTBOUND_INSTALLED);
}
else
{
@@ -1202,9 +1231,10 @@ START_TEST(test_collision_ke_invalid)
CHILD_OUTBOUND_INSTALLED);
assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED,
CHILD_OUTBOUND_REGISTERED);
+ assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING,
+ CHILD_OUTBOUND_REGISTERED);
}
- assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING,
- CHILD_OUTBOUND_INSTALLED);
+
/* we don't expect this hook to get called anymore */
assert_hook_not_called(child_rekey);
@@ -1212,7 +1242,8 @@ START_TEST(test_collision_ke_invalid)
assert_jobs_scheduled(1);
exchange_test_helper->process_message(exchange_test_helper, b, NULL);
assert_child_sa_state(b, data[_i].spi_del_b, CHILD_DELETING,
- CHILD_OUTBOUND_INSTALLED);
+ data[_i].spi_del_b == 2 ? CHILD_OUTBOUND_INSTALLED
+ : CHILD_OUTBOUND_REGISTERED);
assert_child_sa_state(b, data[_i].spi_del_a, CHILD_DELETING,
CHILD_OUTBOUND_NONE);
assert_child_sa_state(b, data[_i].spi_b, CHILD_INSTALLED,
@@ -1223,7 +1254,8 @@ START_TEST(test_collision_ke_invalid)
assert_jobs_scheduled(1);
exchange_test_helper->process_message(exchange_test_helper, a, NULL);
assert_child_sa_state(a, data[_i].spi_del_a, CHILD_DELETING,
- CHILD_OUTBOUND_INSTALLED);
+ data[_i].spi_del_a == 1 ? CHILD_OUTBOUND_INSTALLED
+ : CHILD_OUTBOUND_REGISTERED);
assert_child_sa_state(a, data[_i].spi_del_b, CHILD_DELETING,
CHILD_OUTBOUND_NONE);
assert_child_sa_state(a, data[_i].spi_a, CHILD_INSTALLED,
diff --git a/src/libcharon/tests/utils/sa_asserts.h b/src/libcharon/tests/utils/sa_asserts.h
index d23f724f1..216c15065 100644
--- a/src/libcharon/tests/utils/sa_asserts.h
+++ b/src/libcharon/tests/utils/sa_asserts.h
@@ -121,7 +121,8 @@
test_assert_msg(_state == _child->get_state(_child), "%N != %N", \
child_sa_state_names, _state, \
child_sa_state_names, _child->get_state(_child)); \
- test_assert_msg(_outbound == _child->get_outbound_state(_child), "%N != %N", \
+ typeof(outbound) _cur_out = _child->get_outbound_state(_child); \
+ test_assert_msg(_outbound == _cur_out || _outbound & _cur_out, "%N != %N", \
child_sa_outbound_state_names, _outbound, \
child_sa_outbound_state_names, _child->get_outbound_state(_child)); \
})
diff --git a/testing/do-tests b/testing/do-tests
index 230403b9f..ba2abb655 100755
--- a/testing/do-tests
+++ b/testing/do-tests
@@ -416,29 +416,50 @@ do
STATUS="passed"
eval `awk -F "::" '{
- host=$1
- command=$2
- pattern=$3
- hit=$4
- if (host !~ /^#.*/ && command != "")
- {
+ host=$1
+ command=$2
+ pattern=$3
+ hit=$4
+ if (host ~ /^#.*/ || command == "")
+ {
+ next
+ }
+ printf("cmd_err=\044(tempfile -p test -s err); ")
if (command == "tcpdump")
{
- printf("if [ \044TDUP_%s == \"true\" ]; then stop_tcpdump %s; fi; \n", host, host)
- printf("echo \"%s# cat /tmp/tcpdump.log | grep \047%s\047 [%s]\"; ", host, pattern, hit)
- printf("ssh \044SSHCONF root@\044ipv4_%s cat /tmp/tcpdump.log | grep \"%s\"; ", host, pattern)
+ printf("if [ \044TDUP_%s == \"true\" ]; then stop_tcpdump %s; fi; \n", host, host)
+ printf("cmd_out=\044(ssh \044SSHCONF root@\044ipv4_%s cat /tmp/tcpdump.log | grep \"%s\"); ", host, pattern)
}
else
{
- printf("echo \"%s# %s | grep \047%s\047 [%s]\"; ", host, command, pattern, hit)
- printf("ssh \044SSHCONF root@\044ipv4_%s %s | grep \"%s\"; ", host, command, pattern)
+ printf("cmd_out=\044(ssh \044SSHCONF root@\044ipv4_%s %s 2>\044cmd_err | grep \"%s\"); ", host, command, pattern)
}
printf("cmd_exit=\044?; ")
+ printf("cmd_fail=0; ")
+ if (hit ~ /^[0-9]+$/)
+ {
+ printf("if [ \044(echo \"\044cmd_out\" | wc -l) -ne %d ] ", hit)
+ }
+ else
+ {
+ printf("if [ \044cmd_exit -eq 0 -a \"%s\" = \"NO\" ] ", hit)
+ printf("|| [ \044cmd_exit -ne 0 -a \"%s\" = \"YES\" ] ", hit)
+ }
+ printf("; then STATUS=\"failed\"; cmd_fail=1; fi; \n")
+
+ printf("if [ \044cmd_fail -ne 0 ]; then echo \"~~~~~~~ FAIL ~~~~~~~\"; fi; \n")
+ if (command == "tcpdump")
+ {
+ printf("echo \"%s# cat /tmp/tcpdump.log | grep \047%s\047 [%s]\"; ", host, pattern, hit)
+ }
+ else
+ {
+ printf("echo \"%s# %s | grep \047%s\047 [%s]\"; ", host, command, pattern, hit)
+ }
+ printf("if [ -n \"\044cmd_out\" ]; then echo \"\044cmd_out\"; fi; \n")
+ printf("cat \044cmd_err; rm -f -- \044cmd_err; \n")
+ printf("if [ \044cmd_fail -ne 0 ]; then echo \"~~~~~~~~~~~~~~~~~~~~\"; fi; \n")
printf("echo; ")
- printf("if [ \044cmd_exit -eq 0 -a \"%s\" = \"NO\" ] ", hit)
- printf("|| [ \044cmd_exit -ne 0 -a \"%s\" = \"YES\" ] ", hit)
- printf("; then STATUS=\"failed\"; fi; \n")
- }
}' $TESTDIR/evaltest.dat` >> $CONSOLE_LOG 2>&1
diff --git a/testing/tests/ikev2/net2net-rekey/description.txt b/testing/tests/ikev2/net2net-rekey/description.txt
new file mode 100644
index 000000000..c3122a76f
--- /dev/null
+++ b/testing/tests/ikev2/net2net-rekey/description.txt
@@ -0,0 +1,10 @@
+A connection between the subnets behind the gateways moon and sun is set up.
+The authentication is based on X.509 certificates. Upon the successful
+establishment of the IPsec tunnel, leftfirewall=yes automatically
+inserts iptables-based firewall rules that let pass the tunneled traffic.
+After a while the CHILD_SA is rekeyed by moon (after a deliberately short
+time in this test scenario).
+In order to test both tunnel and firewall after the rekeying, client alice
+behind gateway moon pings client bob located behind gateway sun
+twice, once right after the rekeying and once after the old inbound SA has been
+deleted.
diff --git a/testing/tests/ikev2/net2net-rekey/evaltest.dat b/testing/tests/ikev2/net2net-rekey/evaltest.dat
new file mode 100644
index 000000000..0a34efe9d
--- /dev/null
+++ b/testing/tests/ikev2/net2net-rekey/evaltest.dat
@@ -0,0 +1,14 @@
+moon::ipsec status 2> /dev/null::net-net.*ESTABLISHED.*moon.strongswan.org.*sun.strongswan.org::YES
+sun:: ipsec status 2> /dev/null::net-net.*ESTABLISHED.*sun.strongswan.org.*moon.strongswan.org::YES
+moon::ipsec status 2> /dev/null::net-net.*INSTALLED, TUNNEL::YES
+sun:: ipsec status 2> /dev/null::net-net.*INSTALLED, TUNNEL::YES
+moon::sleep 6::wait for rekeying::NO
+moon::cat /var/log/daemon.log::creating rekey job for CHILD_SA::YES
+moon::cat /var/log/daemon.log::generating CREATE_CHILD_SA request.*REKEY_SA::YES
+moon::cat /var/log/daemon.log::deleted SAD entry with SPI::1
+alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES
+moon::sleep 2::wait until inbound SA is deleted::NO
+moon::cat /var/log/daemon.log::deleted SAD entry with SPI::2
+alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES
+sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES
+sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES
diff --git a/testing/tests/ikev2/net2net-rekey/hosts/moon/etc/ipsec.conf b/testing/tests/ikev2/net2net-rekey/hosts/moon/etc/ipsec.conf
new file mode 100644
index 000000000..dcd98b4de
--- /dev/null
+++ b/testing/tests/ikev2/net2net-rekey/hosts/moon/etc/ipsec.conf
@@ -0,0 +1,24 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+ charondebug="knl 2"
+
+conn %default
+ ikelifetime=60m
+ lifetime=10s
+ margintime=5s
+ rekeyfuzz=0%
+ keyingtries=1
+ keyexchange=ikev2
+ mobike=no
+
+conn net-net
+ left=PH_IP_MOON
+ leftcert=moonCert.pem
+ leftid=@moon.strongswan.org
+ leftsubnet=10.1.0.0/16
+ leftfirewall=yes
+ right=PH_IP_SUN
+ rightid=@sun.strongswan.org
+ rightsubnet=10.2.0.0/16
+ auto=add
diff --git a/testing/tests/ikev2/net2net-rekey/hosts/moon/etc/strongswan.conf b/testing/tests/ikev2/net2net-rekey/hosts/moon/etc/strongswan.conf
new file mode 100644
index 000000000..3dcbf76ea
--- /dev/null
+++ b/testing/tests/ikev2/net2net-rekey/hosts/moon/etc/strongswan.conf
@@ -0,0 +1,7 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+ load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac stroke kernel-netlink socket-default updown
+ # remove rekeyed inbound SA a bit quicker for the test scenario
+ delete_rekeyed_delay = 2
+}
diff --git a/testing/tests/ikev2/net2net-rekey/hosts/sun/etc/ipsec.conf b/testing/tests/ikev2/net2net-rekey/hosts/sun/etc/ipsec.conf
new file mode 100644
index 000000000..5b391db4b
--- /dev/null
+++ b/testing/tests/ikev2/net2net-rekey/hosts/sun/etc/ipsec.conf
@@ -0,0 +1,22 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+
+conn %default
+ ikelifetime=60m
+ keylife=20m
+ rekeymargin=3m
+ keyingtries=1
+ keyexchange=ikev2
+ mobike=no
+
+conn net-net
+ left=PH_IP_SUN
+ leftcert=sunCert.pem
+ leftid=@sun.strongswan.org
+ leftsubnet=10.2.0.0/16
+ leftfirewall=yes
+ right=PH_IP_MOON
+ rightid=@moon.strongswan.org
+ rightsubnet=10.1.0.0/16
+ auto=add
diff --git a/testing/tests/ikev2/net2net-rekey/hosts/sun/etc/strongswan.conf b/testing/tests/ikev2/net2net-rekey/hosts/sun/etc/strongswan.conf
new file mode 100644
index 000000000..93f434598
--- /dev/null
+++ b/testing/tests/ikev2/net2net-rekey/hosts/sun/etc/strongswan.conf
@@ -0,0 +1,5 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+ load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac stroke kernel-netlink socket-default updown
+}
diff --git a/testing/tests/ikev2/net2net-rekey/posttest.dat b/testing/tests/ikev2/net2net-rekey/posttest.dat
new file mode 100644
index 000000000..837738fc6
--- /dev/null
+++ b/testing/tests/ikev2/net2net-rekey/posttest.dat
@@ -0,0 +1,5 @@
+moon::ipsec stop
+sun::ipsec stop
+moon::iptables-restore < /etc/iptables.flush
+sun::iptables-restore < /etc/iptables.flush
+
diff --git a/testing/tests/ikev2/net2net-rekey/pretest.dat b/testing/tests/ikev2/net2net-rekey/pretest.dat
new file mode 100644
index 000000000..bcc2cb04d
--- /dev/null
+++ b/testing/tests/ikev2/net2net-rekey/pretest.dat
@@ -0,0 +1,7 @@
+moon::iptables-restore < /etc/iptables.rules
+sun::iptables-restore < /etc/iptables.rules
+sun::ipsec start
+moon::ipsec start
+sun::expect-connection net-net
+moon::expect-connection net-net
+moon::ipsec up net-net
diff --git a/testing/tests/ikev2/net2net-rekey/test.conf b/testing/tests/ikev2/net2net-rekey/test.conf
new file mode 100644
index 000000000..afa2accbe
--- /dev/null
+++ b/testing/tests/ikev2/net2net-rekey/test.conf
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# This configuration file provides information on the
+# guest instances used for this test
+
+# All guest instances that are required for this test
+#
+VIRTHOSTS="alice moon winnetou sun bob"
+
+# Corresponding block diagram
+#
+DIAGRAM="a-m-w-s-b.png"
+
+# Guest instances on which tcpdump is to be started
+#
+TCPDUMPHOSTS="sun"
+
+# Guest instances on which IPsec is started
+# Used for IPsec logging purposes
+#
+IPSECHOSTS="moon sun"
diff --git a/testing/tests/pfkey/net2net-rekey/description.txt b/testing/tests/pfkey/net2net-rekey/description.txt
new file mode 100644
index 000000000..c3122a76f
--- /dev/null
+++ b/testing/tests/pfkey/net2net-rekey/description.txt
@@ -0,0 +1,10 @@
+A connection between the subnets behind the gateways moon and sun is set up.
+The authentication is based on X.509 certificates. Upon the successful
+establishment of the IPsec tunnel, leftfirewall=yes automatically
+inserts iptables-based firewall rules that let pass the tunneled traffic.
+After a while the CHILD_SA is rekeyed by moon (after a deliberately short
+time in this test scenario).
+In order to test both tunnel and firewall after the rekeying, client alice
+behind gateway moon pings client bob located behind gateway sun
+twice, once right after the rekeying and once after the old inbound SA has been
+deleted.
diff --git a/testing/tests/pfkey/net2net-rekey/evaltest.dat b/testing/tests/pfkey/net2net-rekey/evaltest.dat
new file mode 100644
index 000000000..3bf3b274b
--- /dev/null
+++ b/testing/tests/pfkey/net2net-rekey/evaltest.dat
@@ -0,0 +1,16 @@
+moon::ipsec status 2> /dev/null::net-net.*ESTABLISHED.*moon.strongswan.org.*sun.strongswan.org::YES
+sun:: ipsec status 2> /dev/null::net-net.*ESTABLISHED.*sun.strongswan.org.*moon.strongswan.org::YES
+moon::ipsec status 2> /dev/null::net-net.*INSTALLED, TUNNEL::YES
+sun:: ipsec status 2> /dev/null::net-net.*INSTALLED, TUNNEL::YES
+# deleting the inbound SPI
+moon::cat /var/log/daemon.log::deleted SAD entry with SPI::1
+moon::sleep 6::wait for rekeying::NO
+moon::cat /var/log/daemon.log::creating rekey job for CHILD_SA::YES
+moon::cat /var/log/daemon.log::generating CREATE_CHILD_SA request.*REKEY_SA::YES
+moon::cat /var/log/daemon.log::deleted SAD entry with SPI::3
+alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES
+moon::sleep 2::wait until inbound SA is deleted::NO
+moon::cat /var/log/daemon.log::deleted SAD entry with SPI::4
+alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_.eq=1::YES
+sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES
+sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES
diff --git a/testing/tests/pfkey/net2net-rekey/hosts/moon/etc/ipsec.conf b/testing/tests/pfkey/net2net-rekey/hosts/moon/etc/ipsec.conf
new file mode 100644
index 000000000..dcd98b4de
--- /dev/null
+++ b/testing/tests/pfkey/net2net-rekey/hosts/moon/etc/ipsec.conf
@@ -0,0 +1,24 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+ charondebug="knl 2"
+
+conn %default
+ ikelifetime=60m
+ lifetime=10s
+ margintime=5s
+ rekeyfuzz=0%
+ keyingtries=1
+ keyexchange=ikev2
+ mobike=no
+
+conn net-net
+ left=PH_IP_MOON
+ leftcert=moonCert.pem
+ leftid=@moon.strongswan.org
+ leftsubnet=10.1.0.0/16
+ leftfirewall=yes
+ right=PH_IP_SUN
+ rightid=@sun.strongswan.org
+ rightsubnet=10.2.0.0/16
+ auto=add
diff --git a/testing/tests/pfkey/net2net-rekey/hosts/moon/etc/strongswan.conf b/testing/tests/pfkey/net2net-rekey/hosts/moon/etc/strongswan.conf
new file mode 100644
index 000000000..4234eb134
--- /dev/null
+++ b/testing/tests/pfkey/net2net-rekey/hosts/moon/etc/strongswan.conf
@@ -0,0 +1,7 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+ load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac stroke kernel-pfkey kernel-netlink socket-default updown
+ # remove rekeyed inbound SA a bit quicker for the test scenario
+ delete_rekeyed_delay = 2
+}
diff --git a/testing/tests/pfkey/net2net-rekey/hosts/sun/etc/ipsec.conf b/testing/tests/pfkey/net2net-rekey/hosts/sun/etc/ipsec.conf
new file mode 100644
index 000000000..5b391db4b
--- /dev/null
+++ b/testing/tests/pfkey/net2net-rekey/hosts/sun/etc/ipsec.conf
@@ -0,0 +1,22 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+
+conn %default
+ ikelifetime=60m
+ keylife=20m
+ rekeymargin=3m
+ keyingtries=1
+ keyexchange=ikev2
+ mobike=no
+
+conn net-net
+ left=PH_IP_SUN
+ leftcert=sunCert.pem
+ leftid=@sun.strongswan.org
+ leftsubnet=10.2.0.0/16
+ leftfirewall=yes
+ right=PH_IP_MOON
+ rightid=@moon.strongswan.org
+ rightsubnet=10.1.0.0/16
+ auto=add
diff --git a/testing/tests/pfkey/net2net-rekey/hosts/sun/etc/strongswan.conf b/testing/tests/pfkey/net2net-rekey/hosts/sun/etc/strongswan.conf
new file mode 100644
index 000000000..0ecc2f847
--- /dev/null
+++ b/testing/tests/pfkey/net2net-rekey/hosts/sun/etc/strongswan.conf
@@ -0,0 +1,5 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+ load = random nonce aes sha1 sha2 pem pkcs1 curve25519 gmp x509 curl revocation hmac stroke kernel-pfkey kernel-netlink socket-default updown
+}
diff --git a/testing/tests/pfkey/net2net-rekey/posttest.dat b/testing/tests/pfkey/net2net-rekey/posttest.dat
new file mode 100644
index 000000000..837738fc6
--- /dev/null
+++ b/testing/tests/pfkey/net2net-rekey/posttest.dat
@@ -0,0 +1,5 @@
+moon::ipsec stop
+sun::ipsec stop
+moon::iptables-restore < /etc/iptables.flush
+sun::iptables-restore < /etc/iptables.flush
+
diff --git a/testing/tests/pfkey/net2net-rekey/pretest.dat b/testing/tests/pfkey/net2net-rekey/pretest.dat
new file mode 100644
index 000000000..bcc2cb04d
--- /dev/null
+++ b/testing/tests/pfkey/net2net-rekey/pretest.dat
@@ -0,0 +1,7 @@
+moon::iptables-restore < /etc/iptables.rules
+sun::iptables-restore < /etc/iptables.rules
+sun::ipsec start
+moon::ipsec start
+sun::expect-connection net-net
+moon::expect-connection net-net
+moon::ipsec up net-net
diff --git a/testing/tests/pfkey/net2net-rekey/test.conf b/testing/tests/pfkey/net2net-rekey/test.conf
new file mode 100644
index 000000000..afa2accbe
--- /dev/null
+++ b/testing/tests/pfkey/net2net-rekey/test.conf
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# This configuration file provides information on the
+# guest instances used for this test
+
+# All guest instances that are required for this test
+#
+VIRTHOSTS="alice moon winnetou sun bob"
+
+# Corresponding block diagram
+#
+DIAGRAM="a-m-w-s-b.png"
+
+# Guest instances on which tcpdump is to be started
+#
+TCPDUMPHOSTS="sun"
+
+# Guest instances on which IPsec is started
+# Used for IPsec logging purposes
+#
+IPSECHOSTS="moon sun"
diff --git a/testing/tests/tkm/xfrmproxy-expire/evaltest.dat b/testing/tests/tkm/xfrmproxy-expire/evaltest.dat
index 05bf42057..a3f45871c 100644
--- a/testing/tests/tkm/xfrmproxy-expire/evaltest.dat
+++ b/testing/tests/tkm/xfrmproxy-expire/evaltest.dat
@@ -2,20 +2,24 @@ moon::ipsec stroke status 2> /dev/null::conn1.*ESTABLISHED.*moon.strongswan.org.
sun::ipsec status 2> /dev/null::host-host.*ESTABLISHED.*sun.strongswan.org.*moon.strongswan.org::YES
moon::ipsec stroke status 2> /dev/null::conn1.*INSTALLED, TRANSPORT::YES
sun::ipsec status 2> /dev/null::host-host.*INSTALLED, TRANSPORT::YES
-moon::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
-sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES
-sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES
+moon::sleep 2::wait for rekeying::NO
moon::cat /var/log/daemon.log::ees: acquire received for reqid 1::YES
moon::cat /var/log/daemon.log::ees: expire received for reqid 1, spi.*, dst 192.168.0.2::YES
moon::cat /var/log/daemon.log::creating rekey job for CHILD_SA ESP/0x.*/192.168.0.2::YES
+moon::cat /var/log/daemon.log::deleting child SA (esa: 1, spi:.*)::NO
+moon::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
+sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES
+sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES
+moon::sleep 2::wait until inbound SA is deleted::NO
moon::cat /var/log/daemon.log::deleting child SA (esa: 1, spi:.*)::YES
+moon::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::cat /tmp/tkm.log::RSA private key '/etc/tkm/moonKey.der' loaded::YES
moon::cat /tmp/tkm.log::Adding policy \[ 1, 192.168.0.1 <-> 192.168.0.2 \]::YES
moon::cat /tmp/tkm.log::Checked CA certificate of CC context 1::YES
moon::cat /tmp/tkm.log::Authentication of ISA context 1 successful::YES
moon::cat /tmp/tkm.log::Creating first new ESA context with ID 1 (Isa 1, Sp 1, Ea 1, Initiator TRUE, spi_loc.*, spi_rem.*)::YES
moon::cat /tmp/tkm.log::Creating ESA context with ID 2 (Isa 1, Sp 1, Ea 1, Dh_Id 1, Nc_Loc_Id 1, Initiator TRUE, spi_loc.*, spi_rem.*)::YES
-moon::cat /tmp/tkm.log | grep 'Adding ESA \[ 1, 192.168.0.1 <-> 192.168.0.2, SPI_in.*, SPI_out.*, soft 2, hard 60 \]' | wc -l::2::YES
+moon::cat /tmp/tkm.log | grep 'Adding ESA \[ 1, 192.168.0.1 <-> 192.168.0.2, SPI_in.*, SPI_out.*, soft 4, hard 60 \]' | wc -l::2::YES
moon::cat /tmp/tkm.log::Resetting ESA context 1::YES
moon::cat /tmp/tkm.log::Deleting ESA \[ 1, 192.168.0.1 <=> 192.168.0.2, SPI_in.*, SPI_out.* \]::YES
moon::cat /tmp/xfrm_proxy.log::Initiating ESA acquire for reqid 1::YES
diff --git a/testing/tests/tkm/xfrmproxy-expire/hosts/moon/etc/strongswan.conf b/testing/tests/tkm/xfrmproxy-expire/hosts/moon/etc/strongswan.conf
index cc9d6e072..5b79af9f4 100644
--- a/testing/tests/tkm/xfrmproxy-expire/hosts/moon/etc/strongswan.conf
+++ b/testing/tests/tkm/xfrmproxy-expire/hosts/moon/etc/strongswan.conf
@@ -1,6 +1,8 @@
# /etc/strongswan.conf - strongSwan configuration file
charon-tkm {
+ # remove rekeyed inbound SA a bit quicker for the test scenario
+ delete_rekeyed_delay = 2
dh_mapping {
15 = 1
16 = 2
diff --git a/testing/tests/tkm/xfrmproxy-expire/hosts/moon/etc/tkm/tkm.conf b/testing/tests/tkm/xfrmproxy-expire/hosts/moon/etc/tkm/tkm.conf
index 23e958ab0..62b103a80 100644
--- a/testing/tests/tkm/xfrmproxy-expire/hosts/moon/etc/tkm/tkm.conf
+++ b/testing/tests/tkm/xfrmproxy-expire/hosts/moon/etc/tkm/tkm.conf
@@ -14,7 +14,7 @@
192.168.0.2
- 2
+ 4
60
diff --git a/testing/tests/tkm/xfrmproxy-rekey/description.txt b/testing/tests/tkm/xfrmproxy-rekey/description.txt
new file mode 100644
index 000000000..648011b11
--- /dev/null
+++ b/testing/tests/tkm/xfrmproxy-rekey/description.txt
@@ -0,0 +1,6 @@
+A transport connection between the hosts moon and sun is set up.
+The host moon starts the Trusted Key Manager (TKM) and the Ada XFRM
+proxy, which relays XFRM kernel messages to charon. The authentication is based
+on X.509 certificates. The connection is initiated by a ping from moon
+to sun. The test asserts that a rekeying initiated by sun works
+as expected.
diff --git a/testing/tests/tkm/xfrmproxy-rekey/evaltest.dat b/testing/tests/tkm/xfrmproxy-rekey/evaltest.dat
new file mode 100644
index 000000000..15bdf3b39
--- /dev/null
+++ b/testing/tests/tkm/xfrmproxy-rekey/evaltest.dat
@@ -0,0 +1,23 @@
+moon::ipsec stroke status 2> /dev/null::conn1.*ESTABLISHED.*moon.strongswan.org.*sun.strongswan.org::YES
+sun::ipsec status 2> /dev/null::host-host.*ESTABLISHED.*sun.strongswan.org.*moon.strongswan.org::YES
+moon::ipsec stroke status 2> /dev/null::conn1.*INSTALLED, TRANSPORT::YES
+sun::ipsec status 2> /dev/null::host-host.*INSTALLED, TRANSPORT::YES
+moon::sleep 2::wait for rekeying::NO
+sun::cat /var/log/daemon.log::creating rekey job for CHILD_SA ESP/0x.*/192.168.0.2::YES
+moon::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
+sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES
+sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES
+moon::cat /var/log/daemon.log::deleting child SA (esa: 1, spi:.*)::NO
+moon::sleep 2::wait until inbound SA is deleted::NO
+moon::cat /var/log/daemon.log::deleting child SA (esa: 1, spi:.*)::YES
+moon::ping -c 1 PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
+moon::cat /tmp/tkm.log::RSA private key '/etc/tkm/moonKey.der' loaded::YES
+moon::cat /tmp/tkm.log::Adding policy \[ 1, 192.168.0.1 <-> 192.168.0.2 \]::YES
+moon::cat /tmp/tkm.log::Checked CA certificate of CC context 1::YES
+moon::cat /tmp/tkm.log::Authentication of ISA context 1 successful::YES
+moon::cat /tmp/tkm.log::Creating first new ESA context with ID 1 (Isa 1, Sp 1, Ea 1, Initiator TRUE, spi_loc.*, spi_rem.*)::YES
+moon::cat /tmp/tkm.log::Creating ESA context with ID 2 (Isa 1, Sp 1, Ea 1, Dh_Id 1, Nc_Loc_Id 1, Initiator FALSE, spi_loc.*, spi_rem.*)::YES
+moon::cat /tmp/tkm.log | grep 'Adding ESA \[ 1, 192.168.0.1 <-> 192.168.0.2, SPI_in.*, SPI_out.*, soft 30, hard 60 \]' | wc -l::2::YES
+moon::cat /tmp/tkm.log::Resetting ESA context 1::YES
+moon::cat /tmp/tkm.log::Deleting ESA \[ 1, 192.168.0.1 <=> 192.168.0.2, SPI_in.*, SPI_out.* \]::YES
+moon::cat /tmp/xfrm_proxy.log::Initiating ESA acquire for reqid 1::YES
diff --git a/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/strongswan.conf b/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/strongswan.conf
new file mode 100644
index 000000000..5b79af9f4
--- /dev/null
+++ b/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/strongswan.conf
@@ -0,0 +1,10 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon-tkm {
+ # remove rekeyed inbound SA a bit quicker for the test scenario
+ delete_rekeyed_delay = 2
+ dh_mapping {
+ 15 = 1
+ 16 = 2
+ }
+}
diff --git a/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/tkm/moonKey.der b/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/tkm/moonKey.der
new file mode 100644
index 000000000..d3748930a
Binary files /dev/null and b/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/tkm/moonKey.der differ
diff --git a/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/tkm/strongswanCert.der b/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/tkm/strongswanCert.der
new file mode 100644
index 000000000..a5a631f4b
Binary files /dev/null and b/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/tkm/strongswanCert.der differ
diff --git a/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/tkm/tkm.conf b/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/tkm/tkm.conf
new file mode 100644
index 000000000..2619c0089
--- /dev/null
+++ b/testing/tests/tkm/xfrmproxy-rekey/hosts/moon/etc/tkm/tkm.conf
@@ -0,0 +1,21 @@
+
+
+ moon.strongswan.org
+ moonCert.pem
+
+
+ transport
+
+ 1
+ 192.168.0.1
+
+
+ sun.strongswan.org
+ 192.168.0.2
+
+
+ 30
+ 60
+
+
+
diff --git a/testing/tests/tkm/xfrmproxy-rekey/hosts/sun/etc/ipsec.conf b/testing/tests/tkm/xfrmproxy-rekey/hosts/sun/etc/ipsec.conf
new file mode 100644
index 000000000..9dc641240
--- /dev/null
+++ b/testing/tests/tkm/xfrmproxy-rekey/hosts/sun/etc/ipsec.conf
@@ -0,0 +1,22 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+
+conn %default
+ ikelifetime=60m
+ keylife=10s
+ rekeymargin=6s
+ rekeyfuzz=0%
+ keyingtries=1
+ keyexchange=ikev2
+
+conn host-host
+ left=PH_IP_SUN
+ leftcert=sunCert.pem
+ leftid=sun.strongswan.org
+ right=PH_IP_MOON
+ rightid=moon.strongswan.org
+ ike=aes256-sha512-modp4096!
+ esp=aes256-sha512-modp4096!
+ type=transport
+ auto=add
diff --git a/testing/tests/tkm/xfrmproxy-rekey/hosts/sun/etc/strongswan.conf b/testing/tests/tkm/xfrmproxy-rekey/hosts/sun/etc/strongswan.conf
new file mode 100644
index 000000000..f585edfca
--- /dev/null
+++ b/testing/tests/tkm/xfrmproxy-rekey/hosts/sun/etc/strongswan.conf
@@ -0,0 +1,5 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+ load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 curl revocation hmac xcbc stroke kernel-netlink socket-default updown
+}
diff --git a/testing/tests/tkm/xfrmproxy-rekey/posttest.dat b/testing/tests/tkm/xfrmproxy-rekey/posttest.dat
new file mode 100644
index 000000000..99efe7b00
--- /dev/null
+++ b/testing/tests/tkm/xfrmproxy-rekey/posttest.dat
@@ -0,0 +1,5 @@
+moon::DAEMON_NAME=charon-tkm ipsec stop
+moon::killall xfrm_proxy
+moon::killall tkm_keymanager
+moon::rm -f /tmp/tkm.rpc.ike /tmp/tkm.rpc.ees /tmp/tkm.log /tmp/xfrm_proxy.log
+sun::ipsec stop
diff --git a/testing/tests/tkm/xfrmproxy-rekey/pretest.dat b/testing/tests/tkm/xfrmproxy-rekey/pretest.dat
new file mode 100644
index 000000000..d645ddbfe
--- /dev/null
+++ b/testing/tests/tkm/xfrmproxy-rekey/pretest.dat
@@ -0,0 +1,12 @@
+sun::ipsec start
+moon::rm /etc/ipsec.secrets
+moon::tkm_cfgtool -c /etc/tkm/tkm.conf -i /etc/ipsec.conf -t /etc/tkm/tkm.bin -s /usr/local/share/tkm/tkmconfig.xsd
+moon::cat /etc/ipsec.conf
+moon::tkm_keymanager -c /etc/tkm/tkm.bin -k /etc/tkm/moonKey.der -r /etc/tkm/strongswanCert.der >/tmp/tkm.log 2>&1 &
+moon::expect-file /tmp/tkm.rpc.ike
+moon::DAEMON_NAME=charon-tkm ipsec start
+moon::expect-file /tmp/tkm.rpc.ees
+moon::xfrm_proxy >/tmp/xfrm_proxy.log 2>&1 &
+moon::DAEMON_NAME=charon-tkm expect-connection conn1
+sun::expect-connection host-host
+moon::ping -c 3 192.168.0.2
diff --git a/testing/tests/tkm/xfrmproxy-rekey/test.conf b/testing/tests/tkm/xfrmproxy-rekey/test.conf
new file mode 100644
index 000000000..9647dc6a2
--- /dev/null
+++ b/testing/tests/tkm/xfrmproxy-rekey/test.conf
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# This configuration file provides information on the
+# guest instances used for this test
+
+# All guest instances that are required for this test
+#
+VIRTHOSTS="moon winnetou sun"
+
+# Corresponding block diagram
+#
+DIAGRAM="m-w-s.png"
+
+# Guest instances on which tcpdump is to be started
+#
+TCPDUMPHOSTS="sun"
+
+# Guest instances on which IPsec is started
+# Used for IPsec logging purposes
+#
+IPSECHOSTS="moon sun"