/*
- * Copyright (C) 2008-2020 Tobias Brunner
+ * Copyright (C) 2008-2023 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
*
child_sa_t *child_sa;
/**
- * successfully established the CHILD?
+ * Successfully established the CHILD?
*/
bool established;
/**
- * whether the CHILD_SA rekeys an existing one
+ * Whether the CHILD_SA rekeys an existing one
*/
bool rekey;
/**
- * whether we are retrying with another DH group
+ * Whether to use optimized rekeying
+ */
+ bool optimized_rekeying;
+
+ /**
+ * Whether we are retrying with another DH group
*/
bool retry;
};
{
proposal_t *proposal;
- if (this->initiator)
+ if (!this->proto)
{
- this->proto = PROTO_ESP;
- /* we just get a SPI for the first protocol. TODO: If we ever support
- * proposal lists with mixed protocols, we'd need multiple SPIs */
- if (this->proposals->get_first(this->proposals,
- (void**)&proposal) == SUCCESS)
+ if (this->initiator)
{
- this->proto = proposal->get_protocol(proposal);
+ this->proto = PROTO_ESP;
+ /* we just get a SPI for the first protocol */
+ if (this->proposals->get_first(this->proposals,
+ (void**)&proposal) == SUCCESS)
+ {
+ this->proto = proposal->get_protocol(proposal);
+ }
+ }
+ else
+ {
+ this->proto = this->proposal->get_protocol(this->proposal);
}
- }
- else
- {
- this->proto = this->proposal->get_protocol(this->proposal);
}
this->my_spi = this->child_sa->alloc_spi(this->child_sa, this->proto);
if (!this->my_spi)
*/
static bool build_payloads(private_child_create_t *this, message_t *message)
{
- sa_payload_t *sa_payload;
+ notify_payload_t *notify;
nonce_payload_t *nonce_payload;
+ sa_payload_t *sa_payload;
ts_payload_t *ts_payload;
kernel_feature_t features;
return build_payloads_multi_ke(this, message);
}
- if (this->initiator)
- {
- sa_payload = sa_payload_create_from_proposals_v2(this->proposals);
- }
- else
- {
- sa_payload = sa_payload_create_from_proposal_v2(this->proposal);
- }
- message->add_payload(message, (payload_t*)sa_payload);
-
/* add nonce payload if not in IKE_AUTH */
if (message->get_exchange_type(message) == CREATE_CHILD_SA)
{
return FALSE;
}
- /* add TSi/TSr payloads */
- ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi,
- this->child.label);
- message->add_payload(message, (payload_t*)ts_payload);
- ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr,
- this->child.label);
- message->add_payload(message, (payload_t*)ts_payload);
-
- /* add a notify if we are not in tunnel mode */
+ /* add a notify if we are not using tunnel mode */
switch (this->mode)
{
case MODE_TRANSPORT:
message->add_notify(message, FALSE, ESP_TFC_PADDING_NOT_SUPPORTED,
chunk_empty);
}
+
+ if (this->optimized_rekeying)
+ {
+ /* omit SA and TS payloads when using optimized rekeying, but send
+ * an appropriate notify with our SPI */
+ notify = notify_payload_create_from_protocol_and_type(PLV2_NOTIFY,
+ this->proto, OPTIMIZED_REKEY);
+ notify->set_spi(notify, this->my_spi);
+ message->add_payload(message, (payload_t*)notify);
+ return TRUE;
+ }
+
+ if (this->initiator)
+ {
+ sa_payload = sa_payload_create_from_proposals_v2(this->proposals);
+ }
+ else
+ {
+ sa_payload = sa_payload_create_from_proposal_v2(this->proposal);
+ }
+ message->add_payload(message, (payload_t*)sa_payload);
+
+ /* add TSi/TSr payloads */
+ ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi,
+ this->child.label);
+ message->add_payload(message, (payload_t*)ts_payload);
+ ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr,
+ this->child.label);
+ message->add_payload(message, (payload_t*)ts_payload);
+
return TRUE;
}
}
/**
- * handle a received notify payload
+ * Handle a received notify payload.
*/
static void handle_notify(private_child_create_t *this, notify_payload_t *notify)
{
switch (notify->get_notify_type(notify))
{
+ case OPTIMIZED_REKEY:
+ if (this->optimized_rekeying)
+ {
+ this->other_spi = notify->get_spi(notify);
+ if (!this->other_spi)
+ {
+ DBG1(DBG_IKE, "received invalid %N notify, ignored",
+ notify_type_names, OPTIMIZED_REKEY);
+ }
+ }
+ break;
case USE_TRANSPORT_MODE:
this->mode = MODE_TRANSPORT;
break;
payload_t *payload;
sa_payload_t *sa_payload;
ts_payload_t *ts_payload;
+ linked_list_t *proposals = NULL, *tsi = NULL, *tsr = NULL;
+ linked_list_t *labels_i = NULL, *labels_r = NULL;
+ proposal_t *proposal = NULL;
- /* defaults to TUNNEL mode */
- this->mode = MODE_TUNNEL;
+ if (!this->mode)
+ {
+ /* defaults to TUNNEL mode */
+ this->mode = MODE_TUNNEL;
+ }
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
case PLV2_SECURITY_ASSOCIATION:
sa_payload = (sa_payload_t*)payload;
- this->proposals = sa_payload->get_proposals(sa_payload);
+ proposals = sa_payload->get_proposals(sa_payload);
break;
case PLV2_KEY_EXCHANGE:
process_ke_payload(this, (ke_payload_t*)payload);
break;
case PLV2_TS_INITIATOR:
ts_payload = (ts_payload_t*)payload;
- this->tsi = ts_payload->get_traffic_selectors(ts_payload);
- this->labels_i = ts_payload->get_sec_labels(ts_payload);
+ tsi = ts_payload->get_traffic_selectors(ts_payload);
+ labels_i = ts_payload->get_sec_labels(ts_payload);
break;
case PLV2_TS_RESPONDER:
ts_payload = (ts_payload_t*)payload;
- this->tsr = ts_payload->get_traffic_selectors(ts_payload);
- this->labels_r = ts_payload->get_sec_labels(ts_payload);
+ tsr = ts_payload->get_traffic_selectors(ts_payload);
+ labels_r = ts_payload->get_sec_labels(ts_payload);
break;
case PLV2_NOTIFY:
handle_notify(this, (notify_payload_t*)payload);
}
}
enumerator->destroy(enumerator);
+
+ if (this->optimized_rekeying)
+ {
+ if (this->other_spi)
+ {
+ /* when using optimized rekeying, we use the original proposal but
+ * with the new SPI the peer supplied via notify */
+ this->proposals->get_first(this->proposals, (void**)&proposal);
+ proposal->set_spi(proposal, this->other_spi);
+
+ if (proposals)
+ {
+ DBG1(DBG_IKE, "peer sent unexpected SA payload during "
+ "optimized rekeying, ignored");
+ proposals->destroy_offset(proposals,
+ offsetof(proposal_t, destroy));
+ }
+ if (tsi || tsr || labels_i || labels_r)
+ {
+ DBG1(DBG_IKE, "peer sent unexpected TS payload(s) during "
+ "optimized rekeying, ignored");
+ }
+ DESTROY_OFFSET_IF(tsi, offsetof(traffic_selector_t, destroy));
+ DESTROY_OFFSET_IF(tsr, offsetof(traffic_selector_t, destroy));
+ DESTROY_OFFSET_IF(labels_i, offsetof(sec_label_t, destroy));
+ DESTROY_OFFSET_IF(labels_r, offsetof(sec_label_t, destroy));
+ }
+ else
+ {
+ if (this->initiator)
+ {
+ DBG1(DBG_IKE, "peer didn't reply with expected %N notify,"
+ "rekeying may fail", notify_type_names, OPTIMIZED_REKEY);
+ }
+ else
+ {
+ DBG2(DBG_IKE, "peer requested a regular rekeying, even though "
+ "optimized rekeying is supported");
+ }
+ /* use regular rekeying */
+ DESTROY_OFFSET_IF(this->proposals, offsetof(proposal_t, destroy));
+ DESTROY_OFFSET_IF(this->tsi, offsetof(traffic_selector_t, destroy));
+ DESTROY_OFFSET_IF(this->tsr, offsetof(traffic_selector_t, destroy));
+ DESTROY_OFFSET_IF(this->labels_i, offsetof(sec_label_t, destroy));
+ DESTROY_OFFSET_IF(this->labels_r, offsetof(sec_label_t, destroy));
+ this->optimized_rekeying = FALSE;
+ }
+ }
+
+ if (!this->optimized_rekeying)
+ {
+ this->proposals = proposals;
+ this->tsi = tsi;
+ this->tsr = tsr;
+ this->labels_i = labels_i;
+ this->labels_r = labels_r;
+ }
+}
+
+/**
+ * Prepare proposed traffic selectors as initiator.
+ */
+static void prepare_proposed_ts(private_child_create_t *this)
+{
+ enumerator_t *enumerator;
+ peer_cfg_t *peer_cfg;
+ linked_list_t *list;
+ host_t *vip;
+
+ /* check if we want a virtual IP, but don't have one */
+ list = linked_list_create();
+ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
+ if (!this->rekey)
+ {
+ enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg);
+ while (enumerator->enumerate(enumerator, &vip))
+ {
+ /* propose a 0.0.0.0/0 or ::/0 subnet when we use virtual ip */
+ vip = host_create_any(vip->get_family(vip));
+ list->insert_last(list, vip);
+ }
+ enumerator->destroy(enumerator);
+ }
+ if (list->get_count(list))
+ {
+ this->tsi = this->config->get_traffic_selectors(this->config,
+ TRUE, NULL, list, TRUE);
+ list->destroy_offset(list, offsetof(host_t, destroy));
+ }
+ else
+ { /* no virtual IPs configured */
+ list->destroy(list);
+ list = ike_sa_get_dynamic_hosts(this->ike_sa, TRUE);
+ this->tsi = this->config->get_traffic_selectors(this->config,
+ TRUE, NULL, list, TRUE);
+ list->destroy(list);
+ }
+ list = ike_sa_get_dynamic_hosts(this->ike_sa, FALSE);
+ this->tsr = this->config->get_traffic_selectors(this->config,
+ FALSE, NULL, list, TRUE);
+ list->destroy(list);
+
+ if (this->packet_tsi)
+ {
+ this->tsi->insert_first(this->tsi,
+ this->packet_tsi->clone(this->packet_tsi));
+ }
+ if (this->packet_tsr)
+ {
+ this->tsr->insert_first(this->tsr,
+ this->packet_tsr->clone(this->packet_tsr));
+ }
}
/**
METHOD(task_t, build_i, status_t,
private_child_create_t *this, message_t *message)
{
- enumerator_t *enumerator;
- host_t *vip;
- peer_cfg_t *peer_cfg;
- linked_list_t *list;
bool no_ke = TRUE;
switch (message->get_exchange_type(message))
return NEED_MORE;
}
- /* check if we want a virtual IP, but don't have one */
- list = linked_list_create();
- peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
- if (!this->rekey)
- {
- enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg);
- while (enumerator->enumerate(enumerator, &vip))
- {
- /* propose a 0.0.0.0/0 or ::/0 subnet when we use virtual ip */
- vip = host_create_any(vip->get_family(vip));
- list->insert_last(list, vip);
- }
- enumerator->destroy(enumerator);
- }
- if (list->get_count(list))
- {
- this->tsi = this->config->get_traffic_selectors(this->config,
- TRUE, NULL, list, TRUE);
- list->destroy_offset(list, offsetof(host_t, destroy));
- }
- else
- { /* no virtual IPs configured */
- list->destroy(list);
- list = ike_sa_get_dynamic_hosts(this->ike_sa, TRUE);
- this->tsi = this->config->get_traffic_selectors(this->config,
- TRUE, NULL, list, TRUE);
- list->destroy(list);
- }
- list = ike_sa_get_dynamic_hosts(this->ike_sa, FALSE);
- this->tsr = this->config->get_traffic_selectors(this->config,
- FALSE, NULL, list, TRUE);
- list->destroy(list);
-
- if (this->packet_tsi)
+ if (!this->optimized_rekeying)
{
- this->tsi->insert_first(this->tsi,
- this->packet_tsi->clone(this->packet_tsi));
- }
- if (this->packet_tsr)
- {
- this->tsr->insert_first(this->tsr,
- this->packet_tsr->clone(this->packet_tsr));
- }
+ prepare_proposed_ts(this);
- if (!generic_label_only(this) && !this->child.label)
- { /* in the simple label mode we propose the configured label as we
- * won't have labels from acquires */
- this->child.label = this->config->get_label(this->config);
+ if (!generic_label_only(this) && !this->child.label)
+ { /* in the simple label mode we propose the configured label as we
+ * won't have labels from acquires */
+ this->child.label = this->config->get_label(this->config);
+ if (this->child.label)
+ {
+ this->child.label = this->child.label->clone(this->child.label);
+ }
+ }
if (this->child.label)
{
- this->child.label = this->child.label->clone(this->child.label);
+ DBG2(DBG_CFG, "proposing security label '%s'",
+ this->child.label->get_string(this->child.label));
}
- }
- if (this->child.label)
- {
- DBG2(DBG_CFG, "proposing security label '%s'",
- this->child.label->get_string(this->child.label));
- }
- this->proposals = this->config->get_proposals(this->config, no_ke);
- this->mode = this->config->get_mode(this->config);
+ this->proposals = this->config->get_proposals(this->config, no_ke);
+ this->mode = this->config->get_mode(this->config);
+ }
this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa, TRUE);
this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa, FALSE);
}
if (!no_ke && !this->retry)
- { /* during a rekeying the method might already be set */
- if (this->ke_method == KE_NONE)
+ { /* during a rekeying the method is already set if one was negotiated
+ * before, if not we try to propose one even during rekeying, unless we
+ * use optimized rekeying of an SA without PFS */
+ if (this->ke_method == KE_NONE &&
+ !this->optimized_rekeying)
{
this->ke_method = this->config->get_algorithm(this->config,
KEY_EXCHANGE_METHOD);
return FAILED;
}
- this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
- this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
- this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
- this->tsi = NULL;
- this->tsr = NULL;
- this->proposals = NULL;
-
+ /* keep the proposals and TS during an optimized rekeying */
+ if (!this->optimized_rekeying)
+ {
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
+ this->tsi = NULL;
+ this->tsr = NULL;
+ this->proposals = NULL;
+ }
return NEED_MORE;
}
return SUCCESS;
}
- /* check if ike_config_t included non-critical error notifies */
+ /* check if ike_config_t task added non-critical error notifies */
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
DBG1(DBG_IKE, "peer didn't accept DH group %N, "
"it requested %N", key_exchange_method_names,
this->ke_method, key_exchange_method_names, alg);
+ if (this->optimized_rekeying)
+ {
+ DBG1(DBG_IKE, "peer rejected our DH group during "
+ "optimized rekeying, switch to regular rekeying");
+ this->optimized_rekeying = FALSE;
+ }
this->retry = TRUE;
this->ke_method = alg;
this->child_sa->set_state(this->child_sa, CHILD_RETRYING);
this->child.label = label ? label->clone(label) : NULL;
}
-METHOD(child_create_t, use_dh_group, void,
- private_child_create_t *this, key_exchange_method_t dh_group)
+METHOD(child_create_t, rekey_sa, void,
+ private_child_create_t *this, child_sa_t *old)
{
- this->ke_method = dh_group;
+ proposal_t *proposal;
+ uint16_t ke_method;
+ linked_list_t *my_ts, *other_ts;
+
+ proposal = old->get_proposal(old);
+ if (proposal->get_algorithm(proposal, KEY_EXCHANGE_METHOD,
+ &ke_method, NULL))
+ { /* reuse the KE method negotiated previously */
+ this->ke_method = ke_method;
+ }
+ if (old->get_optimized_rekey(old) &&
+ this->ike_sa->supports_extension(this->ike_sa,
+ EXT_OPTIMIZED_REKEY))
+ { /* with optimized rekeying we reuse the proposal and TS */
+ this->optimized_rekeying = TRUE;
+ proposal = proposal->clone(proposal, 0);
+ this->proposals = linked_list_create_with_items(proposal, NULL);
+ this->proto = old->get_protocol(old);
+ this->mode = old->get_mode(old);
+ my_ts = linked_list_create_from_enumerator(
+ old->create_ts_enumerator(old, TRUE));
+ other_ts = linked_list_create_from_enumerator(
+ old->create_ts_enumerator(old, FALSE));
+ if (this->initiator)
+ {
+ this->tsi = my_ts->clone_offset(my_ts,
+ offsetof(traffic_selector_t, clone));
+ this->tsr = other_ts->clone_offset(other_ts,
+ offsetof(traffic_selector_t, clone));
+ }
+ else
+ {
+ this->tsi = other_ts->clone_offset(other_ts,
+ offsetof(traffic_selector_t, clone));
+ this->tsr = my_ts->clone_offset(my_ts,
+ offsetof(traffic_selector_t, clone));
+ }
+ my_ts->destroy(my_ts);
+ other_ts->destroy(other_ts);
+ }
}
METHOD(child_create_t, get_child, child_sa_t*,
chunk_free(&this->my_nonce);
chunk_free(&this->other_nonce);
chunk_free(&this->link);
- if (this->tsr)
- {
- this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
- }
- if (this->tsi)
- {
- this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
- }
- if (this->labels_i)
- {
- this->labels_i->destroy_offset(this->labels_i, offsetof(sec_label_t, destroy));
- }
- if (this->labels_r)
- {
- this->labels_r->destroy_offset(this->labels_r, offsetof(sec_label_t, destroy));
+ if (!this->optimized_rekeying)
+ {
+ DESTROY_OFFSET_IF(this->tsi, offsetof(traffic_selector_t, destroy));
+ DESTROY_OFFSET_IF(this->tsr, offsetof(traffic_selector_t, destroy));
+ DESTROY_OFFSET_IF(this->labels_i, offsetof(sec_label_t, destroy));
+ DESTROY_OFFSET_IF(this->labels_r, offsetof(sec_label_t, destroy));
+ this->tsi = NULL;
+ this->tsr = NULL;
+ DESTROY_OFFSET_IF(this->proposals, offsetof(proposal_t, destroy));
+ this->proposals = NULL;
+ this->proto = PROTO_NONE;
+ this->mode = MODE_TUNNEL;
}
DESTROY_IF(this->child_sa);
DESTROY_IF(this->proposal);
DESTROY_IF(this->ke);
this->ke_failed = FALSE;
clear_key_exchanges(this);
- if (this->proposals)
- {
- this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
- }
if (!this->rekey && !this->retry)
{
this->ke_method = KE_NONE;
this->ike_sa = ike_sa;
this->keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa);
this->proposal = NULL;
- this->proposals = NULL;
- this->tsi = NULL;
- this->tsr = NULL;
this->ke = NULL;
this->nonceg = NULL;
this->child_sa = NULL;
- this->mode = MODE_TUNNEL;
this->ipcomp = IPCOMP_NONE;
this->ipcomp_received = IPCOMP_NONE;
this->other_cpi = 0;
chunk_free(&this->my_nonce);
chunk_free(&this->other_nonce);
chunk_free(&this->link);
- if (this->tsr)
- {
- this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
- }
- if (this->tsi)
- {
- this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
- }
- if (this->labels_i)
- {
- this->labels_i->destroy_offset(this->labels_i, offsetof(sec_label_t, destroy));
- }
- if (this->labels_r)
- {
- this->labels_r->destroy_offset(this->labels_r, offsetof(sec_label_t, destroy));
- }
+ DESTROY_OFFSET_IF(this->tsi, offsetof(traffic_selector_t, destroy));
+ DESTROY_OFFSET_IF(this->tsr, offsetof(traffic_selector_t, destroy));
+ DESTROY_OFFSET_IF(this->labels_i, offsetof(sec_label_t, destroy));
+ DESTROY_OFFSET_IF(this->labels_r, offsetof(sec_label_t, destroy));
if (!this->established)
{
DESTROY_IF(this->child_sa);
DESTROY_IF(this->proposal);
DESTROY_IF(this->ke);
clear_key_exchanges(this);
- if (this->proposals)
- {
- this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
- }
+ DESTROY_OFFSET_IF(this->proposals, offsetof(proposal_t, destroy));
DESTROY_IF(this->config);
DESTROY_IF(this->nonceg);
DESTROY_IF(this->child.label);
.use_marks = _use_marks,
.use_if_ids = _use_if_ids,
.use_label = _use_label,
- .use_dh_group = _use_dh_group,
+ .rekey_sa = _rekey_sa,
.task = {
.get_type = _get_type,
.migrate = _migrate,
/* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */
assert_hook_not_called(child_rekey);
assert_notify(IN, REKEY_SA);
+ assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_TS_INITIATOR);
+ assert_payload(IN, PLV2_TS_RESPONDER);
exchange_test_helper->process_message(exchange_test_helper, b, NULL);
assert_child_sa_state(b, spi_b, CHILD_REKEYED, CHILD_OUTBOUND_INSTALLED);
assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_REGISTERED);
/* <-- CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } */
assert_hook_rekey(child_rekey, spi_a, 3);
assert_no_notify(IN, REKEY_SA);
+ assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_TS_INITIATOR);
+ assert_payload(IN, PLV2_TS_RESPONDER);
exchange_test_helper->process_message(exchange_test_helper, a, NULL);
assert_child_sa_state(a, spi_a, CHILD_DELETING, CHILD_OUTBOUND_NONE);
assert_child_sa_state(a, 3, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
/* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, KEi, TSi, TSr } --> */
assert_hook_not_called(child_rekey);
assert_notify(IN, REKEY_SA);
+ assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_payload(IN, PLV2_TS_INITIATOR);
+ assert_payload(IN, PLV2_TS_RESPONDER);
exchange_test_helper->process_message(exchange_test_helper, b, NULL);
assert_child_sa_state(b, spi_b, CHILD_REKEYING, CHILD_OUTBOUND_INSTALLED);
assert_ipsec_sas_installed(b, spi_a, spi_b);
/* <-- CREATE_CHILD_SA { SA, Nr, KEr, TSi, TSr, N(ADD_KE) } */
assert_hook_not_called(child_rekey);
assert_no_notify(IN, REKEY_SA);
+ assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+ assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_payload(IN, PLV2_TS_INITIATOR);
+ assert_payload(IN, PLV2_TS_RESPONDER);
exchange_test_helper->process_message(exchange_test_helper, a, NULL);
assert_child_sa_state(a, spi_a, CHILD_REKEYING, CHILD_OUTBOUND_INSTALLED);
assert_ipsec_sas_installed(a, spi_a, spi_b);
/* IKE_FOLLOWUP_KE { KEi, N(ADD_KE) } --> */
assert_hook_not_called(child_rekey);
- assert_payload(IN, PLV2_KEY_EXCHANGE);
assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_no_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
exchange_test_helper->process_message(exchange_test_helper, b, NULL);
assert_child_sa_state(b, spi_b, CHILD_REKEYED, CHILD_OUTBOUND_INSTALLED);
assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_REGISTERED);
/* <-- IKE_FOLLOWUP_KE { KEr } */
assert_hook_rekey(child_rekey, spi_a, 3);
- assert_payload(IN, PLV2_KEY_EXCHANGE);
assert_no_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_no_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
exchange_test_helper->process_message(exchange_test_helper, a, NULL);
assert_child_sa_state(a, spi_a, CHILD_DELETING, CHILD_OUTBOUND_NONE);
assert_child_sa_state(a, 3, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
}
END_TEST
+/**
+ * Regular and then optimized CHILD_SA rekey either initiated by the original
+ * initiator or responder of the IKE_SA.
+ */
+START_TEST(test_optimized)
+{
+ ike_sa_t *a, *b;
+ uint32_t spi_a = _i+1, spi_b = 2-_i;
+
+ assert_track_sas_start();
+
+ if (_i)
+ { /* responder rekeys the CHILD_SA (SPI 2) */
+ exchange_test_helper->establish_sa(exchange_test_helper,
+ &b, &a, NULL);
+ }
+ else
+ { /* initiator rekeys the CHILD_SA (SPI 1) */
+ exchange_test_helper->establish_sa(exchange_test_helper,
+ &a, &b, NULL);
+ }
+ initiate_rekey(a, spi_a);
+ assert_ipsec_sas_installed(a, spi_a, spi_b);
+
+ /* this should never get called as this results in a successful rekeying */
+ assert_hook_not_called(child_updown);
+
+ /* the CHILD_SA created with IKE_AUTH has to be rekeyed regularly as we
+ * don't know if we use PFS or not */
+
+ /* CREATE_CHILD_SA { N(REKEY_SA), SA, Ni, [KEi,] TSi, TSr } --> */
+ assert_hook_not_called(child_rekey);
+ assert_notify(IN, REKEY_SA);
+ assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_TS_INITIATOR);
+ assert_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+ assert_child_sa_state(b, spi_b, CHILD_REKEYED, CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_REGISTERED);
+ assert_ipsec_sas_installed(b, spi_a, spi_b, 4);
+ assert_hook();
+
+ /* <-- CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } */
+ assert_hook_rekey(child_rekey, spi_a, 3);
+ assert_no_notify(IN, REKEY_SA);
+ assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_TS_INITIATOR);
+ assert_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+ assert_child_sa_state(a, spi_a, CHILD_DELETING, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(a, 3, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(a, spi_a, 3, 4);
+ assert_hook();
+
+ /* INFORMATIONAL { D } --> */
+ assert_hook_rekey(child_rekey, spi_b, 4);
+ assert_jobs_scheduled(1);
+ assert_single_payload(IN, PLV2_DELETE);
+ exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+ assert_child_sa_state(b, spi_b, CHILD_DELETED, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_count(b, 2);
+ assert_ipsec_sas_installed(b, spi_b, 3, 4);
+ assert_scheduler();
+ assert_hook();
+ /* <-- INFORMATIONAL { D } */
+ assert_hook_not_called(child_rekey);
+ assert_jobs_scheduled(1);
+ assert_single_payload(IN, PLV2_DELETE);
+ exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+ assert_child_sa_state(a, spi_a, CHILD_DELETED, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(a, 3, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_count(a, 2);
+ assert_ipsec_sas_installed(a, spi_a, 3, 4);
+ assert_scheduler();
+ assert_hook();
+
+ /* simulate the execution of the scheduled jobs */
+ destroy_rekeyed(a, spi_a);
+ assert_child_sa_count(a, 1);
+ assert_ipsec_sas_installed(a, 3, 4);
+ destroy_rekeyed(b, spi_b);
+ assert_child_sa_count(b, 1);
+ assert_ipsec_sas_installed(a, 3, 4);
+
+ /* child_updown */
+ assert_hook();
+
+ /* after the first rekeying the CHILD_SA can be rekeyed optimized */
+ initiate_rekey(a, 3);
+ /* this should never get called as this results in a successful rekeying */
+ assert_hook_not_called(child_updown);
+
+ /* CREATE_CHILD_SA { N(REKEY_SA), N(OPT_REKEY), Ni, [KEi] } --> */
+ assert_hook_not_called(child_rekey);
+ assert_notify(IN, REKEY_SA);
+ assert_notify(IN, OPTIMIZED_REKEY);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+ assert_child_sa_state(b, 4, CHILD_REKEYED, CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_state(b, 6, CHILD_INSTALLED, CHILD_OUTBOUND_REGISTERED);
+ assert_ipsec_sas_installed(b, 3, 4, 6);
+ assert_hook();
+
+ /* <-- CREATE_CHILD_SA { N(OPT_REKEY), Nr, [KEr] } */
+ assert_hook_called(child_rekey);
+ assert_no_notify(IN, REKEY_SA);
+ assert_notify(IN, OPTIMIZED_REKEY);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+ assert_child_sa_state(a, 3, CHILD_DELETING, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(a, 5, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(a, 3, 5, 6);
+ assert_hook();
+
+ /* INFORMATIONAL { D } --> */
+ assert_hook_called(child_rekey);
+ assert_single_payload(IN, PLV2_DELETE);
+ exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+ assert_child_sa_state(b, 4, CHILD_DELETED, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(b, 6, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_count(b, 2);
+ assert_ipsec_sas_installed(b, 4, 5, 6);
+ assert_hook();
+
+ /* <-- INFORMATIONAL { D } */
+ assert_hook_not_called(child_rekey);
+ assert_single_payload(IN, PLV2_DELETE);
+ exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+ assert_child_sa_state(a, 3, CHILD_DELETED, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(a, 5, CHILD_INSTALLED);
+ assert_child_sa_count(a, 2);
+ assert_ipsec_sas_installed(a, 3, 5, 6);
+ assert_hook();
+
+ /* simulate the execution of the scheduled jobs */
+ destroy_rekeyed(a, 3);
+ assert_child_sa_count(a, 1);
+ assert_ipsec_sas_installed(a, 5, 6);
+ destroy_rekeyed(b, 4);
+ assert_child_sa_count(b, 1);
+ assert_ipsec_sas_installed(b, 5, 6);
+
+ /* child_updown */
+ assert_hook();
+ assert_track_sas(2, 2);
+
+ call_ikesa(a, destroy);
+ call_ikesa(b, destroy);
+}
+END_TEST
+
+/**
+ * Optimized rekey of a CHILD_SA created with a CREATE_CHILD_SA exchange, either
+ * initiated by the original initiator or responder of the IKE_SA.
+ */
+START_TEST(test_optimized_childless)
+{
+ exchange_test_sa_conf_t conf = {
+ .initiator = {
+ .childless = CHILDLESS_FORCE,
+ },
+ };
+ ike_sa_t *init, *resp, *a, *b;
+ uint32_t spi_a = _i+1, spi_b = 2-_i;
+
+ assert_track_sas_start();
+
+ exchange_test_helper->establish_sa(exchange_test_helper,
+ &init, &resp, &conf);
+ if (_i)
+ { /* responder rekeys the CHILD_SA (SPI 2) */
+ a = resp;
+ b = init;
+ }
+ else
+ { /* initiator rekeys the CHILD_SA (SPI 1) */
+ a = init;
+ b = resp;
+ }
+ assert_child_sa_count(init, 0);
+
+ /* CREATE_CHILD_SA { SA, Ni, [KEi,] TSi, TSr } --> */
+ assert_hook_called(child_updown);
+ assert_no_notify(IN, REKEY_SA);
+ assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_TS_INITIATOR);
+ assert_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, resp, NULL);
+ assert_hook();
+
+ /* <-- CREATE_CHILD_SA { SA, Nr, [KEr,] TSi, TSr } */
+ assert_hook_called(child_updown);
+ assert_no_notify(IN, REKEY_SA);
+ assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_TS_INITIATOR);
+ assert_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, init, NULL);
+ assert_hook();
+
+ assert_child_sa_state(a, spi_a, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(a, spi_a, spi_b);
+ assert_child_sa_state(b, spi_b, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(b, spi_a, spi_b);
+
+ initiate_rekey(a, spi_a);
+ assert_ipsec_sas_installed(a, spi_a, spi_b);
+
+ /* this should never get called as this results in a successful rekeying */
+ assert_hook_not_called(child_updown);
+
+ /* since the CHILD_SA was created with a CREATE_CHILD_SA exchange, we can
+ * use optimized rekeying*/
+
+ /* CREATE_CHILD_SA { N(REKEY_SA), N(OPT_REKEY), Ni, [KEi] } --> */
+ assert_hook_not_called(child_rekey);
+ assert_notify(IN, REKEY_SA);
+ assert_notify(IN, OPTIMIZED_REKEY);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+ assert_child_sa_state(b, spi_b, CHILD_REKEYED, CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_REGISTERED);
+ assert_ipsec_sas_installed(b, spi_a, spi_b, 4);
+ assert_hook();
+
+ /* <-- CREATE_CHILD_SA { N(OPT_REKEY), Nr, [KEr] } */
+ assert_hook_called(child_rekey);
+ assert_no_notify(IN, REKEY_SA);
+ assert_notify(IN, OPTIMIZED_REKEY);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+ assert_child_sa_state(a, spi_a, CHILD_DELETING, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(a, 3, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(a, spi_a, 3, 4);
+ assert_hook();
+
+ /* INFORMATIONAL { D } --> */
+ assert_hook_rekey(child_rekey, spi_b, 4);
+ assert_jobs_scheduled(1);
+ assert_single_payload(IN, PLV2_DELETE);
+ exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+ assert_child_sa_state(b, spi_b, CHILD_DELETED, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_count(b, 2);
+ assert_ipsec_sas_installed(b, spi_b, 3, 4);
+ assert_scheduler();
+ assert_hook();
+ /* <-- INFORMATIONAL { D } */
+ assert_hook_not_called(child_rekey);
+ assert_jobs_scheduled(1);
+ assert_single_payload(IN, PLV2_DELETE);
+ exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+ assert_child_sa_state(a, spi_a, CHILD_DELETED, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(a, 3, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_count(a, 2);
+ assert_ipsec_sas_installed(a, spi_a, 3, 4);
+ assert_scheduler();
+ assert_hook();
+
+ /* simulate the execution of the scheduled jobs */
+ destroy_rekeyed(a, spi_a);
+ assert_child_sa_count(a, 1);
+ assert_ipsec_sas_installed(a, 3, 4);
+ destroy_rekeyed(b, spi_b);
+ assert_child_sa_count(b, 1);
+ assert_ipsec_sas_installed(a, 3, 4);
+
+ /* child_updown */
+ assert_hook();
+ assert_track_sas(2, 2);
+
+ call_ikesa(a, destroy);
+ call_ikesa(b, destroy);
+}
+END_TEST
+
+/**
+ * Optimized CHILD_SA rekey either initiated by the original initiator or
+ * responder of the IKE_SA.
+ */
+START_TEST(test_optimized_multi_ke)
+{
+ exchange_test_sa_conf_t conf = multi_ke_conf;
+ ike_sa_t *init, *resp, *a, *b;
+ uint32_t spi_a = _i+1, spi_b = 2-_i;
+
+ assert_track_sas_start();
+
+ /* use childless initiation so we can use optimized rekeying */
+ conf.initiator.childless = CHILDLESS_FORCE;
+ exchange_test_helper->establish_sa(exchange_test_helper,
+ &init, &resp, &conf);
+ if (_i)
+ { /* responder rekeys the CHILD_SA (SPI 2) */
+ a = resp;
+ b = init;
+ }
+ else
+ { /* initiator rekeys the CHILD_SA (SPI 1) */
+ a = init;
+ b = resp;
+ }
+ assert_child_sa_count(init, 0);
+
+ /* CREATE_CHILD_SA { SA, Ni, KEi, TSi, TSr } --> */
+ assert_hook_not_called(child_updown);
+ assert_no_notify(IN, REKEY_SA);
+ assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_payload(IN, PLV2_TS_INITIATOR);
+ assert_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, resp, NULL);
+ assert_hook();
+
+ /* <-- CREATE_CHILD_SA { SA, Nr, KEr, TSi, TSr, N(ADD_KE) } */
+ assert_hook_not_called(child_updown);
+ assert_no_notify(IN, REKEY_SA);
+ assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+ assert_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_payload(IN, PLV2_TS_INITIATOR);
+ assert_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, init, NULL);
+ assert_hook();
+
+ /* IKE_FOLLOWUP_KE { KEi, N(ADD_KE) } --> */
+ assert_hook_called(child_updown);
+ assert_no_notify(IN, REKEY_SA);
+ assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_no_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, resp, NULL);
+ assert_hook();
+
+ /* <-- IKE_FOLLOWUP_KE { KEr } */
+ assert_hook_called(child_updown);
+ assert_no_notify(IN, REKEY_SA);
+ assert_no_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_no_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, init, NULL);
+ assert_hook();
+
+ assert_child_sa_state(a, spi_a, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(a, spi_a, spi_b);
+ assert_child_sa_state(b, spi_b, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(b, spi_a, spi_b);
+
+ initiate_rekey(a, spi_a);
+ assert_ipsec_sas_installed(a, spi_a, spi_b);
+
+ /* this should never get called as this results in a successful rekeying */
+ assert_hook_not_called(child_updown);
+
+ /* CREATE_CHILD_SA { N(REKEY_SA), N(OPT_REKEY), Ni, KEi } --> */
+ assert_hook_not_called(child_rekey);
+ assert_notify(IN, REKEY_SA);
+ assert_notify(IN, OPTIMIZED_REKEY);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+ assert_child_sa_state(b, spi_b, CHILD_REKEYING, CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(b, spi_a, spi_b);
+ assert_hook();
+
+ /* <-- CREATE_CHILD_SA { N(OPT_REKEY), Nr, KEr, N(ADD_KE) } */
+ assert_hook_not_called(child_rekey);
+ assert_no_notify(IN, REKEY_SA);
+ assert_notify(IN, OPTIMIZED_REKEY);
+ assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+ assert_child_sa_state(a, spi_a, CHILD_REKEYING, CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(a, spi_a, spi_b);
+ assert_hook();
+
+ /* IKE_FOLLOWUP_KE { KEi, N(ADD_KE) } --> */
+ assert_hook_not_called(child_rekey);
+ assert_no_notify(IN, REKEY_SA);
+ assert_no_notify(IN, OPTIMIZED_REKEY);
+ assert_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_no_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+ assert_child_sa_state(b, spi_b, CHILD_REKEYED, CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_REGISTERED);
+ assert_ipsec_sas_installed(b, spi_a, spi_b, 4);
+ assert_hook();
+
+ /* <-- IKE_FOLLOWUP_KE { KEr } */
+ assert_hook_rekey(child_rekey, spi_a, 3);
+ assert_no_notify(IN, REKEY_SA);
+ assert_no_notify(IN, OPTIMIZED_REKEY);
+ assert_no_notify(IN, ADDITIONAL_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_SECURITY_ASSOCIATION);
+ assert_no_payload(IN, PLV2_NONCE);
+ assert_payload(IN, PLV2_KEY_EXCHANGE);
+ assert_no_payload(IN, PLV2_TS_INITIATOR);
+ assert_no_payload(IN, PLV2_TS_RESPONDER);
+ exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+ assert_child_sa_state(a, spi_a, CHILD_DELETING, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(a, 3, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_ipsec_sas_installed(a, spi_a, 3, 4);
+ assert_hook();
+
+ /* INFORMATIONAL { D } --> */
+ assert_hook_rekey(child_rekey, spi_b, 4);
+ assert_jobs_scheduled(1);
+ assert_single_payload(IN, PLV2_DELETE);
+ exchange_test_helper->process_message(exchange_test_helper, b, NULL);
+ assert_child_sa_state(b, spi_b, CHILD_DELETED, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(b, 4, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_count(b, 2);
+ assert_ipsec_sas_installed(b, spi_b, 3, 4);
+ assert_scheduler();
+ assert_hook();
+ /* <-- INFORMATIONAL { D } */
+ assert_hook_not_called(child_rekey);
+ assert_jobs_scheduled(1);
+ assert_single_payload(IN, PLV2_DELETE);
+ exchange_test_helper->process_message(exchange_test_helper, a, NULL);
+ assert_child_sa_state(a, spi_a, CHILD_DELETED, CHILD_OUTBOUND_NONE);
+ assert_child_sa_state(a, 3, CHILD_INSTALLED, CHILD_OUTBOUND_INSTALLED);
+ assert_child_sa_count(a, 2);
+ assert_ipsec_sas_installed(a, spi_a, 3, 4);
+ assert_scheduler();
+ assert_hook();
+
+ /* simulate the execution of the scheduled jobs */
+ destroy_rekeyed(a, spi_a);
+ assert_child_sa_count(a, 1);
+ assert_ipsec_sas_installed(a, 3, 4);
+ destroy_rekeyed(b, spi_b);
+ assert_child_sa_count(b, 1);
+ assert_ipsec_sas_installed(a, 3, 4);
+
+ /* child_updown */
+ assert_hook();
+ assert_track_sas(2, 2);
+
+ call_ikesa(a, destroy);
+ call_ikesa(b, destroy);
+}
+END_TEST
+
/**
* Both peers initiate the CHILD_SA rekeying concurrently and should handle
* the collision properly depending on the nonces.
}
END_TEST
+START_SETUP(disable_optimized_rekey)
+{
+ lib->settings->set_bool(lib->settings, "%s.optimized_rekeying",
+ FALSE, lib->ns);
+}
+END_SETUP
+
+START_TEARDOWN(enable_optimized_rekey)
+{
+ lib->settings->set_bool(lib->settings, "%s.optimized_rekeying",
+ TRUE, lib->ns);
+}
+END_TEARDOWN
+
Suite *child_rekey_suite_create()
{
Suite *s;
s = suite_create("child rekey");
tc = tcase_create("regular");
+ tcase_add_checked_fixture(tc, disable_optimized_rekey, enable_optimized_rekey);
tcase_add_loop_test(tc, test_regular, 0, 2);
tcase_add_loop_test(tc, test_regular_multi_ke, 0, 2);
tcase_add_loop_test(tc, test_regular_ke_invalid, 0, 2);
tcase_add_test(tc, test_regular_responder_incorrect_delete);
suite_add_tcase(s, tc);
+ tc = tcase_create("optimized");
+ tcase_add_loop_test(tc, test_optimized, 0, 2);
+ tcase_add_loop_test(tc, test_optimized_childless, 0, 2);
+ tcase_add_loop_test(tc, test_optimized_multi_ke, 0, 2);
+ suite_add_tcase(s, tc);
+
tc = tcase_create("collisions rekey");
+ tcase_add_checked_fixture(tc, disable_optimized_rekey, enable_optimized_rekey);
tcase_add_loop_test(tc, test_collision, 0, 4);
tcase_add_loop_test(tc, test_collision_multi_ke, 0, 4);
tcase_add_loop_test(tc, test_collision_mixed, 0, 4);