Merge commit 'child-sa-rekey-tkm'

This fixes CHILD_SA rekeying with TKM and changes how we switch to the
outbound IPsec SA with Netlink/XFRM (using SPIs on the outbound policy
instead of installing the outbound SA delayed).

For charon-tkm it changes when esa_select() and esa_reset() are called,
now with the outbound policy and the inbound SA, respectively, instead
of the outbound SA in both cases.

Also fixed is a potential traffic loss when a rekey collision is lost.
This commit is contained in:
Tobias Brunner 2017-08-07 10:46:45 +02:00
commit 11ddda2ecd
47 changed files with 714 additions and 256 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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.

View File

@ -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);

View File

@ -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);
}

View File

@ -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),
};
/**

View File

@ -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 &&

View File

@ -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,

View File

@ -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
*/

View File

@ -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)),

View File

@ -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));

View File

@ -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;

View File

@ -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,

View File

@ -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)); \
})

View File

@ -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

View File

@ -0,0 +1,10 @@
A connection between the subnets behind the gateways <b>moon</b> and <b>sun</b> is set up.
The authentication is based on <b>X.509 certificates</b>. Upon the successful
establishment of the IPsec tunnel, <b>leftfirewall=yes</b> automatically
inserts iptables-based firewall rules that let pass the tunneled traffic.
After a while the CHILD_SA is rekeyed by <b>moon</b> (after a deliberately short
time in this test scenario).
In order to test both tunnel and firewall after the rekeying, client <b>alice</b>
behind gateway <b>moon</b> pings client <b>bob</b> located behind gateway <b>sun</b>
twice, once right after the rekeying and once after the old inbound SA has been
deleted.

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -0,0 +1,5 @@
moon::ipsec stop
sun::ipsec stop
moon::iptables-restore < /etc/iptables.flush
sun::iptables-restore < /etc/iptables.flush

View File

@ -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

View File

@ -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"

View File

@ -0,0 +1,10 @@
A connection between the subnets behind the gateways <b>moon</b> and <b>sun</b> is set up.
The authentication is based on <b>X.509 certificates</b>. Upon the successful
establishment of the IPsec tunnel, <b>leftfirewall=yes</b> automatically
inserts iptables-based firewall rules that let pass the tunneled traffic.
After a while the CHILD_SA is rekeyed by <b>moon</b> (after a deliberately short
time in this test scenario).
In order to test both tunnel and firewall after the rekeying, client <b>alice</b>
behind gateway <b>moon</b> pings client <b>bob</b> located behind gateway <b>sun</b>
twice, once right after the rekeying and once after the old inbound SA has been
deleted.

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -0,0 +1,5 @@
moon::ipsec stop
sun::ipsec stop
moon::iptables-restore < /etc/iptables.flush
sun::iptables-restore < /etc/iptables.flush

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -14,7 +14,7 @@
<ip>192.168.0.2</ip>
</remote>
<lifetime>
<soft>2</soft>
<soft>4</soft>
<hard>60</hard>
</lifetime>
</policy>

View File

@ -0,0 +1,6 @@
A transport connection between the hosts <b>moon</b> and <b>sun</b> is set up.
The host <b>moon</b> 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 <b>moon</b>
to <b>sun</b>. The test asserts that a rekeying initiated by <b>sun</b> works
as expected.

View File

@ -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

View File

@ -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
}
}

View File

@ -0,0 +1,21 @@
<tkmconfig>
<local_identity id="1">
<identity>moon.strongswan.org</identity>
<certificate>moonCert.pem</certificate>
</local_identity>
<policy id="1">
<mode>transport</mode>
<local>
<identity_id>1</identity_id>
<ip>192.168.0.1</ip>
</local>
<remote>
<identity>sun.strongswan.org</identity>
<ip>192.168.0.2</ip>
</remote>
<lifetime>
<soft>30</soft>
<hard>60</hard>
</lifetime>
</policy>
</tkmconfig>

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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"