Merge branch 'acquires-seq'

This uses the sequence number from acquires when installing the SA. This
allows handling narrowing properly by changing the reqid and still
removing the temporary state in the kernel.  It also changes that
traffic selectors are reused during rekeying/recreation/reauthentication,
so narrowed selectors won't return to the wider configured TS because
there won't be any TS from triggering packets to narrow again.
This commit is contained in:
Tobias Brunner 2025-05-28 16:30:56 +02:00
commit b6a4cfc705
49 changed files with 1699 additions and 498 deletions

View File

@ -50,7 +50,8 @@ encoding/payloads/fragment_payload.c encoding/payloads/fragment_payload.h \
kernel/kernel_interface.c kernel/kernel_interface.h \
kernel/kernel_ipsec.c kernel/kernel_ipsec.h \
kernel/kernel_net.c kernel/kernel_net.h \
kernel/kernel_listener.h kernel/kernel_handler.c kernel/kernel_handler.h \
kernel/kernel_listener.c kernel/kernel_listener.h \
kernel/kernel_handler.c kernel/kernel_handler.h \
network/receiver.c network/receiver.h network/sender.c network/sender.h \
network/socket.c network/socket.h \
network/socket_manager.c network/socket_manager.h \

View File

@ -48,7 +48,8 @@ encoding/payloads/fragment_payload.c encoding/payloads/fragment_payload.h \
kernel/kernel_interface.c kernel/kernel_interface.h \
kernel/kernel_ipsec.c kernel/kernel_ipsec.h \
kernel/kernel_net.c kernel/kernel_net.h \
kernel/kernel_listener.h kernel/kernel_handler.c kernel/kernel_handler.h \
kernel/kernel_listener.c kernel/kernel_listener.h \
kernel/kernel_handler.c kernel/kernel_handler.h \
network/receiver.c network/receiver.h network/sender.c network/sender.h \
network/socket.c network/socket.h \
network/socket_manager.c network/socket_manager.h \

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2019 Tobias Brunner
* Copyright (C) 2008-2025 Tobias Brunner
* Copyright (C) 2016 Andreas Steffen
* Copyright (C) 2005-2007 Martin Willi
* Copyright (C) 2005 Jan Hutter
@ -67,12 +67,12 @@ struct private_child_cfg_t {
/**
* list for traffic selectors for my site
*/
linked_list_t *my_ts;
traffic_selector_list_t *my_ts;
/**
* list for traffic selectors for others site
*/
linked_list_t *other_ts;
traffic_selector_list_t *other_ts;
/**
* updown script
@ -258,165 +258,66 @@ METHOD(child_cfg_t, add_traffic_selector, void,
{
if (local)
{
this->my_ts->insert_last(this->my_ts, ts);
this->my_ts->add(this->my_ts, ts);
}
else
{
this->other_ts->insert_last(this->other_ts, ts);
this->other_ts->add(this->other_ts, ts);
}
}
METHOD(child_cfg_t, get_traffic_selectors, linked_list_t*,
private_child_cfg_t *this, bool local, linked_list_t *supplied,
linked_list_t *hosts, bool log)
/**
* Check whether the config is for regular transport mode.
*/
static bool is_transport_mode(private_child_cfg_t *this)
{
enumerator_t *e1, *e2;
traffic_selector_t *ts1, *ts2, *selected;
linked_list_t *result, *derived;
host_t *host;
return this->mode == MODE_TRANSPORT && !has_option(this, OPT_PROXY_MODE);
}
result = linked_list_create();
derived = linked_list_create();
if (local)
{
e1 = this->my_ts->create_enumerator(this->my_ts);
}
else
{
e1 = this->other_ts->create_enumerator(this->other_ts);
}
/* in a first step, replace "dynamic" TS with the host list */
while (e1->enumerate(e1, &ts1))
{
if (hosts && hosts->get_count(hosts))
{ /* set hosts if TS is dynamic or as initiator in transport mode */
bool dynamic = ts1->is_dynamic(ts1),
proxy_mode = has_option(this, OPT_PROXY_MODE);
if (dynamic || (this->mode == MODE_TRANSPORT && !proxy_mode &&
!supplied))
{
e2 = hosts->create_enumerator(hosts);
while (e2->enumerate(e2, &host))
{
if (!dynamic && !host->is_anyaddr(host) &&
!ts1->includes(ts1, host))
{ /* for transport mode, we skip TS that don't match
* specific IPs */
continue;
}
ts2 = ts1->clone(ts1);
if (dynamic || !host->is_anyaddr(host))
{ /* don't make regular TS larger than they were */
ts2->set_address(ts2, host);
}
derived->insert_last(derived, ts2);
}
e2->destroy(e2);
continue;
}
}
derived->insert_last(derived, ts1->clone(ts1));
}
e1->destroy(e1);
METHOD(child_cfg_t, get_traffic_selectors, linked_list_t*,
private_child_cfg_t *this, bool local, linked_list_t *hosts)
{
traffic_selector_list_t *ts = local ? this->my_ts : this->other_ts;
if (log)
{
DBG2(DBG_CFG, "%s traffic selectors for %s:",
supplied ? "selecting" : "proposing", local ? "us" : "other");
}
if (!supplied)
{
while (derived->remove_first(derived, (void**)&ts1) == SUCCESS)
{
if (log)
{
DBG2(DBG_CFG, " %R", ts1);
}
result->insert_last(result, ts1);
}
derived->destroy(derived);
}
else
{
e1 = derived->create_enumerator(derived);
e2 = supplied->create_enumerator(supplied);
/* enumerate all configured/derived selectors */
while (e1->enumerate(e1, &ts1))
{
/* enumerate all supplied traffic selectors */
while (e2->enumerate(e2, &ts2))
{
selected = ts1->get_subset(ts1, ts2);
if (selected)
{
if (log)
{
DBG2(DBG_CFG, " config: %R, received: %R => match: %R",
ts1, ts2, selected);
}
result->insert_last(result, selected);
}
else if (log)
{
DBG2(DBG_CFG, " config: %R, received: %R => no match",
ts1, ts2);
}
}
supplied->reset_enumerator(supplied, e2);
}
e1->destroy(e1);
e2->destroy(e2);
/* force replacing non-dynamic TS to the IPs in transport mode */
return ts->get(ts, hosts, is_transport_mode(this));
}
/* check if we/peer did any narrowing, raise alert */
e1 = derived->create_enumerator(derived);
e2 = result->create_enumerator(result);
while (e1->enumerate(e1, &ts1))
{
if (!e2->enumerate(e2, &ts2) || !ts1->equals(ts1, ts2))
{
charon->bus->alert(charon->bus, ALERT_TS_NARROWED,
local, result, this);
break;
}
}
e1->destroy(e1);
e2->destroy(e2);
/*
* Described in header
*/
linked_list_t *child_cfg_select_ts(child_cfg_t *cfg, bool local,
traffic_selector_list_t *list,
linked_list_t *supplied, linked_list_t *hosts)
{
private_child_cfg_t *this = (private_child_cfg_t*)cfg;
traffic_selector_list_t *ts = list ?: (local ? this->my_ts : this->other_ts);
linked_list_t *result;
bool force, narrowed = FALSE;
derived->destroy_offset(derived, offsetof(traffic_selector_t, destroy));
}
DBG2(DBG_CFG, "%s traffic selectors for %s:",
supplied ? "selecting" : "proposing", local ? "us" : "other");
/* remove any redundant traffic selectors in the list */
e1 = result->create_enumerator(result);
e2 = result->create_enumerator(result);
while (e1->enumerate(e1, &ts1))
/* force replacing non-dynamic TS to the IPs in transport mode, but only
* when proposing as initiator */
force = supplied && is_transport_mode(this);
result = ts->select(ts, supplied, hosts, force, &narrowed);
if (narrowed)
{
while (e2->enumerate(e2, &ts2))
{
if (ts1 != ts2)
{
if (ts2->is_contained_in(ts2, ts1))
{
result->remove_at(result, e2);
ts2->destroy(ts2);
result->reset_enumerator(result, e1);
break;
}
if (ts1->is_contained_in(ts1, ts2))
{
result->remove_at(result, e1);
ts1->destroy(ts1);
break;
}
}
}
result->reset_enumerator(result, e2);
charon->bus->alert(charon->bus, ALERT_TS_NARROWED,
local, result, this);
}
e1->destroy(e1);
e2->destroy(e2);
return result;
}
METHOD(child_cfg_t, select_traffic_selectors, linked_list_t*,
private_child_cfg_t *this, bool local, linked_list_t *supplied,
linked_list_t *hosts)
{
return child_cfg_select_ts(&this->public, local, NULL, supplied, hosts);
}
METHOD(child_cfg_t, get_updown, char*,
private_child_cfg_t *this)
{
@ -681,13 +582,8 @@ METHOD(child_cfg_t, equals, bool,
{
return FALSE;
}
if (!this->my_ts->equals_offset(this->my_ts, other->my_ts,
offsetof(traffic_selector_t, equals)))
{
return FALSE;
}
if (!this->other_ts->equals_offset(this->other_ts, other->other_ts,
offsetof(traffic_selector_t, equals)))
if (!this->my_ts->equals(this->my_ts, other->my_ts) ||
!this->other_ts->equals(this->other_ts, other->other_ts))
{
return FALSE;
}
@ -733,8 +629,8 @@ METHOD(child_cfg_t, destroy, void,
if (ref_put(&this->refcount))
{
this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
this->my_ts->destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
this->my_ts->destroy(this->my_ts);
this->other_ts->destroy(this->other_ts);
DESTROY_IF(this->label);
free(this->updown);
free(this->interface);
@ -755,6 +651,7 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
.get_name = _get_name,
.add_traffic_selector = _add_traffic_selector,
.get_traffic_selectors = _get_traffic_selectors,
.select_traffic_selectors = _select_traffic_selectors,
.add_proposal = _add_proposal,
.get_proposals = _get_proposals,
.select_proposal = _select_proposal,
@ -809,8 +706,8 @@ child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data)
.interface = strdupnull(data->interface),
.refcount = 1,
.proposals = linked_list_create(),
.my_ts = linked_list_create(),
.other_ts = linked_list_create(),
.my_ts = traffic_selector_list_create(),
.other_ts = traffic_selector_list_create(),
.replay_window = lib->settings->get_int(lib->settings,
"%s.replay_window", DEFAULT_REPLAY_WINDOW, lib->ns),
.hw_offload = data->hw_offload,

View File

@ -32,6 +32,7 @@ typedef struct child_cfg_create_t child_cfg_create_t;
#include <library.h>
#include <selectors/traffic_selector.h>
#include <selectors/traffic_selector_list.h>
#include <crypto/proposal/proposal.h>
#include <kernel/kernel_ipsec.h>
@ -119,26 +120,53 @@ struct child_cfg_t {
traffic_selector_t *ts);
/**
* Get a list of traffic selectors to use for the CHILD_SA.
* Get a list of configured traffic selectors to use for the CHILD_SA.
*
* The config contains two set of traffic selectors, one for the local
* The config contains two sets of traffic selectors, one for the local
* side, one for the remote side.
*
* Some traffic selectors may be "dynamic", meaning they are narrowed down
* to a specific address (host-to-host or virtual-IP setups). Use the
* \p hosts parameter to narrow such traffic selectors to an address.
*
* Returned list and its traffic selectors must be destroyed after use.
*
* Note that this method does not log anything. If logging is required, use
* select_traffic_selectors() without passing supplied traffic selectors.
*
* @param local TRUE for TS on local side, FALSE for remote
* @param hosts addresses to use for narrowing "dynamic" TS', host_t
* @return list containing the traffic selectors
*/
linked_list_t *(*get_traffic_selectors)(child_cfg_t *this, bool local,
linked_list_t *hosts);
/**
* Select a list of traffic selectors to use for the CHILD_SA.
*
* The config contains two sets of traffic selectors, one for the local
* side, one for the remote side.
*
* If a list with traffic selectors is supplied, these are used to narrow
* down the traffic selector list to the greatest common divisor.
* Some traffic selector may be "dynamic", meaning they are narrowed down
* to a specific address (host-to-host or virtual-IP setups). Use
* the "host" parameter to narrow such traffic selectors to that address.
* Resulted list and its traffic selectors must be destroyed after use.
* down the traffic selector list to the greatest common subset.
*
* Some traffic selectors may be "dynamic", meaning they are narrowed down
* to a specific address (host-to-host or virtual-IP setups). Use the
* \p hosts parameter to narrow such traffic selectors to an address.
*
* Returned list and its traffic selectors must be destroyed after use.
*
* Details about the selection process are logged and an alert is triggered
* if narrowing occurred.
*
* @param local TRUE for TS on local side, FALSE for remote
* @param supplied list with TS to select from, or NULL
* @param hosts addresses to use for narrowing "dynamic" TS', host_t
* @param log FALSE to avoid logging details about the selection
* @return list containing the traffic selectors
*/
linked_list_t *(*get_traffic_selectors)(child_cfg_t *this, bool local,
linked_list_t *supplied,
linked_list_t *hosts, bool log);
linked_list_t *(*select_traffic_selectors)(child_cfg_t *this, bool local,
linked_list_t *supplied,
linked_list_t *hosts);
/**
* Get the updown script to run for the CHILD_SA.
@ -449,4 +477,21 @@ struct child_cfg_create_t {
*/
child_cfg_t *child_cfg_create(char *name, child_cfg_create_t *data);
/**
* Select and narrow traffic selectors in the given traffic selector list.
* Refer to child_cfg_t::select_traffic_selectors() for details, the difference
* is that this can work with external traffic selector lists.
*
* @param this config to use
* @param local TRUE for TS on local side, FALSE for remote (also
* used for logging)
* @param list traffic selectors to use instead of those in config
* @param supplied list with TS to select from, or NULL
* @param hosts addresses to use for narrowing "dynamic" TS', host_t
* @return list containing the traffic selectors
*/
linked_list_t *child_cfg_select_ts(child_cfg_t *this, bool local,
traffic_selector_list_t *list,
linked_list_t *supplied, linked_list_t *hosts);
#endif /** CHILD_CFG_H_ @}*/

View File

@ -414,7 +414,7 @@ static u_int get_ts_match(child_cfg_t *cfg, bool local,
}
/* fetch configured TS list, narrowing dynamic TS */
cfg_list = cfg->get_traffic_selectors(cfg, local, NULL, hosts, TRUE);
cfg_list = cfg->select_traffic_selectors(cfg, local, NULL, hosts);
/* use a round counter to rate leading TS with higher priority */
round = sup_list->get_count(sup_list);

View File

@ -56,22 +56,26 @@ static inline protocol_id_t proto_ip2ike(uint8_t protocol)
METHOD(kernel_listener_t, acquire, bool,
private_kernel_handler_t *this, uint32_t reqid, kernel_acquire_data_t *data)
{
char buf[BUF_LEN] = "";
char buf[BUF_LEN] = "", sbuf[BUF_LEN] = "";
if (data->label)
{
snprintf(buf, sizeof(buf), ", label {%s}",
data->label->get_string(data->label));
}
if (data->seq)
{
snprintf(sbuf, sizeof(sbuf), ", seq {%u}", data->seq);
}
if (data->src && data->dst)
{
DBG1(DBG_KNL, "creating acquire job for policy %R === %R with "
"reqid {%u}%s", data->src, data->dst, reqid, buf);
"reqid {%u}%s%s", data->src, data->dst, reqid, buf, sbuf);
}
else
{
DBG1(DBG_KNL, "creating acquire job for policy with reqid {%u}%s",
reqid, buf);
DBG1(DBG_KNL, "creating acquire job for policy with reqid {%u}%s%s",
reqid, buf, sbuf);
}
lib->processor->queue_job(lib->processor,
(job_t*)acquire_job_create(reqid, data));

View File

@ -392,12 +392,19 @@ METHOD(kernel_interface_t, alloc_reqid, status_t,
/* search by reqid if given */
entry = this->reqids->get(this->reqids, tmpl);
}
if (entry && entry_equals_selectors(entry, tmpl))
/* if the IPsec stack supports sequence numbers for acquires, we can
* allocate a new reqid if narrowing occurred (otherwise, we get the same
* reqid back anyway). if not, we want to reuse the existing reqid of
* the trap polices and explicitly don't want to match the traffic
* selectors. another case where we want to reuse an existing reqid is
* when labels are used. because we currently only install policies once
* with the generic label, the reqid has to stay the same even if narrowing
* occurs. however, in either case we don't want to reuse the reqid if the
* additional selectors (e.g. marks) are different */
if (entry && (label || !(get_features(this) & KERNEL_ACQUIRE_SEQ)) &&
entry_equals_selectors(entry, tmpl))
{
/* we don't require a traffic selector match for existing reqids,
* as we want to reuse a reqid for trap-triggered policies that
* got narrowed during negotiation, but we don't want to reuse the
* reqid if the additional selectors (e.g. marks) are different */
reqid_entry_destroy(tmpl);
}
else

View File

@ -81,6 +81,8 @@ enum kernel_feature_t {
KERNEL_POLICY_SPI = (1<<4),
/** IPsec backend reports use time per SA via query_sa() */
KERNEL_SA_USE_TIME = (1<<5),
/** IPsec backend associates acquires and SAs with a sequence number */
KERNEL_ACQUIRE_SEQ = (1<<6),
};
/**

View File

@ -67,6 +67,8 @@ struct kernel_ipsec_sa_id_t {
struct kernel_ipsec_add_sa_t {
/** Reqid */
uint32_t reqid;
/** Optional sequence number associated with the acquire triggering the SA */
uint32_t seq;
/** Mode (tunnel, transport...) */
ipsec_mode_t mode;
/** List of source traffic selectors */

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2025 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
* 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
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "kernel_listener.h"
/*
* Described in header
*/
kernel_acquire_data_t *kernel_acquire_data_clone(kernel_acquire_data_t *data)
{
kernel_acquire_data_t *clone;
INIT(clone);
*clone = *data;
if (clone->src)
{
clone->src = clone->src->clone(clone->src);
}
if (clone->dst)
{
clone->dst = clone->dst->clone(clone->dst);
}
if (clone->label)
{
clone->label = clone->label->clone(clone->label);
}
return clone;
}
/*
* Described in header
*/
void kernel_acquire_data_destroy(kernel_acquire_data_t *data)
{
if (data)
{
DESTROY_IF(data->src);
DESTROY_IF(data->dst);
DESTROY_IF(data->label);
free(data);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2013 Tobias Brunner
* Copyright (C) 2010-2025 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
@ -40,6 +40,8 @@ struct kernel_acquire_data_t {
traffic_selector_t *dst;
/** Optional security label of the triggering packet */
sec_label_t *label;
/** Optional sequence number associated with the acquire */
uint32_t seq;
};
/**
@ -117,4 +119,21 @@ struct kernel_listener_t {
bool (*tun)(kernel_listener_t *this, tun_device_t *tun, bool created);
};
/**
* Clone the given acquire data received from the kernel.
*
* @param data data to clone
* @return clone of the original object and all associated data
*/
kernel_acquire_data_t *kernel_acquire_data_clone(kernel_acquire_data_t *data);
/**
* Destroy the given previously cloned acquire data.
*
* @note This must not be used on the original data from the kernel interface.
*
* @param data cloned data to destroy
*/
void kernel_acquire_data_destroy(kernel_acquire_data_t *data);
#endif /** KERNEL_LISTENER_H_ @}*/

View File

@ -1010,12 +1010,12 @@ static void process_acquire(private_kernel_netlink_ipsec_t *this,
break;
default:
/* acquire for AH/ESP only, not for IPCOMP */
return;
}
data.src = selector2ts(&acquire->sel, TRUE);
data.dst = selector2ts(&acquire->sel, FALSE);
data.label = label.len ? sec_label_from_encoding(label) : NULL;
data.seq = acquire->seq;
charon->kernel->acquire(charon->kernel, reqid, &data);
@ -1188,7 +1188,7 @@ CALLBACK(receive_events, void,
METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
private_kernel_netlink_ipsec_t *this)
{
return KERNEL_ESP_V3_TFC | KERNEL_POLICY_SPI |
return KERNEL_ESP_V3_TFC | KERNEL_POLICY_SPI | KERNEL_ACQUIRE_SEQ |
(this->sa_lastused ? KERNEL_SA_USE_TIME : 0);
}
@ -1751,6 +1751,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
sa->id.proto = id->proto;
sa->family = id->src->get_family(id->src);
sa->mode = mode2kernel(mode);
sa->seq = data->seq;
if (!data->copy_ecn)
{

View File

@ -1326,6 +1326,7 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this,
pfkey_msg_t response;
kernel_acquire_data_t data = {};
uint32_t index, reqid = 0;
uint8_t mode = 0;
policy_entry_t *policy;
policy_sa_t *sa;
@ -1350,6 +1351,7 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this,
if (response.x_sa2)
{
reqid = response.x_sa2->sadb_x_sa2_reqid;
mode = response.x_sa2->sadb_x_sa2_mode;
}
else
{
@ -1360,6 +1362,7 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this,
policy->used_by->get_first(policy->used_by, (void**)&sa) == SUCCESS)
{
reqid = sa->sa->cfg.reqid;
mode = sa->sa->cfg.mode;
}
else
{
@ -1371,13 +1374,24 @@ static void process_acquire(private_kernel_pfkey_ipsec_t *this,
if (reqid)
{
data.src = sadb_address2ts(response.src);
data.dst = sadb_address2ts(response.dst);
/* while we could pass the sequence number from the acquire in order
* to use it in the SA install, we currently don't do that. the reason
* is that the addresses we get here are the endpoints of the SA, not
* information about the matched packet (except for the ports, according
* to the RFC, although the Linux kernel doesn't do that). so these are
* only useful in transport mode with wildcard policies. in tunnel mode,
* where narrowing could occur and the sequence number would be
* relevant, these TS are useless and might not even match the policy */
if (mode == IPSEC_MODE_TRANSPORT)
{
data.src = sadb_address2ts(response.src);
data.dst = sadb_address2ts(response.dst);
}
charon->kernel->acquire(charon->kernel, reqid, &data);
data.src->destroy(data.src);
data.dst->destroy(data.dst);
DESTROY_IF(data.src);
DESTROY_IF(data.dst);
}
}

View File

@ -330,12 +330,10 @@ static void request_query_config(xmlTextReaderPtr reader, xmlTextWriterPtr write
xmlTextWriterStartElement(writer, "childconfig");
xmlTextWriterWriteElement(writer, "name",
child_cfg->get_name(child_cfg));
list = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL,
NULL, FALSE);
list = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL);
write_networks(writer, "local", list);
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
list = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL,
NULL, FALSE);
list = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL);
write_networks(writer, "remote", list);
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
xmlTextWriterEndElement(writer);

View File

@ -585,10 +585,9 @@ METHOD(stroke_list_t, status, void,
children = peer_cfg->create_child_cfg_enumerator(peer_cfg);
while (children->enumerate(children, &child_cfg))
{
my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE,
NULL, NULL, FALSE);
my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL);
other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE,
NULL, NULL, FALSE);
NULL);
fprintf(out, "%12s: child: %#R === %#R %N",
child_cfg->get_name(child_cfg), my_ts, other_ts,
ipsec_mode_names, child_cfg->get_mode(child_cfg));
@ -621,10 +620,8 @@ METHOD(stroke_list_t, status, void,
fprintf(out, "Shunted Connections:\n");
first = FALSE;
}
my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL,
NULL, FALSE);
other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL,
NULL, FALSE);
my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL);
other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL);
fprintf(out, "%12s: %#R === %#R %N\n",
child_cfg->get_name(child_cfg), my_ts, other_ts,
ipsec_mode_names, child_cfg->get_mode(child_cfg));

View File

@ -55,7 +55,7 @@ static void narrow_ts(child_cfg_t *cfg, traffic_selector_t *ts,
received = linked_list_create();
received->insert_last(received, ts);
selected = cfg->get_traffic_selectors(cfg, FALSE, received, NULL, FALSE);
selected = cfg->select_traffic_selectors(cfg, FALSE, received, NULL);
while (selected->remove_first(selected, (void**)&ts) == SUCCESS)
{
list->insert_last(list, ts);
@ -139,8 +139,7 @@ static void narrow_responder_post(child_cfg_t *child_cfg, linked_list_t *local)
{
ts->destroy(ts);
}
configured = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL,
FALSE);
configured = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL);
while (configured->remove_first(configured, (void**)&ts) == SUCCESS)
{

View File

@ -159,8 +159,7 @@ METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
while (enumerator->enumerate(enumerator, &child_cfg))
{
current = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL,
FALSE);
current = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL);
while (current->remove_first(current, (void**)&ts) == SUCCESS)
{
if (use_ts(ts))

View File

@ -629,6 +629,7 @@ static void raise_policy(private_vici_query_t *this, u_int id, char *ike,
snprintf(buf, sizeof(buf), "%s/%s", ike, child->get_name(child));
b->begin_section(b, buf);
b->add_kv(b, "child", "%s", child->get_name(child));
b->add_kv(b, "reqid", "%u", child->get_reqid(child));
b->add_kv(b, "ike", "%s", ike);
list_mode(b, child, NULL);
@ -684,7 +685,7 @@ static void raise_policy_cfg(private_vici_query_t *this, u_int id, char *ike,
list_label(b, NULL, cfg);
b->begin_list(b, "local-ts");
list = cfg->get_traffic_selectors(cfg, TRUE, NULL, NULL, FALSE);
list = cfg->get_traffic_selectors(cfg, TRUE, NULL);
enumerator = list->create_enumerator(list);
while (enumerator->enumerate(enumerator, &ts))
{
@ -695,7 +696,7 @@ static void raise_policy_cfg(private_vici_query_t *this, u_int id, char *ike,
b->end_list(b /* local-ts */);
b->begin_list(b, "remote-ts");
list = cfg->get_traffic_selectors(cfg, FALSE, NULL, NULL, FALSE);
list = cfg->get_traffic_selectors(cfg, FALSE, NULL);
enumerator = list->create_enumerator(list);
while (enumerator->enumerate(enumerator, &ts))
{
@ -1002,8 +1003,7 @@ CALLBACK(list_conns, vici_message_t*,
child_cfg->get_close_action(child_cfg));
b->begin_list(b, "local-ts");
list = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL,
NULL, FALSE);
list = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL);
selectors = list->create_enumerator(list);
while (selectors->enumerate(selectors, &ts))
{
@ -1014,8 +1014,7 @@ CALLBACK(list_conns, vici_message_t*,
b->end_list(b /* local-ts */);
b->begin_list(b, "remote-ts");
list = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL,
NULL, FALSE);
list = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL);
selectors = list->create_enumerator(list);
while (selectors->enumerate(selectors, &ts))
{

View File

@ -1,4 +1,5 @@
/*
* Copyright (C) 2022-2025 Tobias Brunner
* Copyright (C) 2006-2009 Martin Willi
*
* Copyright (C) secunet Security Networks AG
@ -38,22 +39,20 @@ struct private_acquire_job_t {
/**
* Data from the acquire
*/
kernel_acquire_data_t data;
kernel_acquire_data_t *data;
};
METHOD(job_t, destroy, void,
private_acquire_job_t *this)
{
DESTROY_IF(this->data.src);
DESTROY_IF(this->data.dst);
DESTROY_IF(this->data.label);
kernel_acquire_data_destroy(this->data);
free(this);
}
METHOD(job_t, execute, job_requeue_t,
private_acquire_job_t *this)
{
charon->traps->acquire(charon->traps, this->reqid, &this->data);
charon->traps->acquire(charon->traps, this->reqid, this->data);
return JOB_REQUEUE_NONE;
}
@ -79,21 +78,8 @@ acquire_job_t *acquire_job_create(uint32_t reqid, kernel_acquire_data_t *data)
},
},
.reqid = reqid,
.data = *data,
.data = kernel_acquire_data_clone(data),
);
if (this->data.src)
{
this->data.src = this->data.src->clone(this->data.src);
}
if (this->data.dst)
{
this->data.dst = this->data.dst->clone(this->data.dst);
}
if (this->data.label)
{
this->data.label = this->data.label->clone(this->data.label);
}
return &this->public;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2023 Tobias Brunner
* Copyright (C) 2006-2025 Tobias Brunner
* Copyright (C) 2016 Andreas Steffen
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2006 Daniel Roethlisberger
@ -161,6 +161,11 @@ struct private_child_sa_t {
*/
uint32_t unique_id;
/**
* Optional sequence number associated with triggering acquire
*/
uint32_t seq;
/**
* Whether FWD policies in the outbound direction should be installed
*/
@ -817,6 +822,18 @@ METHOD(child_sa_t, get_label, sec_label_t*,
return this->label ?: this->config->get_label(this->config);
}
METHOD(child_sa_t, get_acquire_seq, uint32_t,
private_child_sa_t *this)
{
return this->seq;
}
METHOD(child_sa_t, set_acquire_seq, void,
private_child_sa_t *this, uint32_t seq)
{
this->seq = seq;
}
METHOD(child_sa_t, get_lifetime, time_t,
private_child_sa_t *this, bool hard)
{
@ -1022,6 +1039,7 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr,
};
sa = (kernel_ipsec_add_sa_t){
.reqid = this->reqid,
.seq = this->seq,
.mode = this->mode,
.src_ts = src_ts,
.dst_ts = dst_ts,
@ -2018,7 +2036,7 @@ static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local)
traffic_selector_t *ts;
list = linked_list_create_with_items(ike, NULL);
ts_list = config->get_traffic_selectors(config, local, NULL, list, FALSE);
ts_list = config->get_traffic_selectors(config, local, list);
list->destroy(list);
enumerator = ts_list->create_enumerator(ts_list);
@ -2074,6 +2092,8 @@ child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config,
.get_mark = _get_mark,
.get_if_id = _get_if_id,
.get_label = _get_label,
.get_acquire_seq = _get_acquire_seq,
.set_acquire_seq = _set_acquire_seq,
.has_encap = _has_encap,
.get_ipcomp = _get_ipcomp,
.set_ipcomp = _set_ipcomp,
@ -2112,6 +2132,7 @@ child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config,
.if_id_in = config->get_if_id(config, TRUE) ?: data->if_id_in_def,
.if_id_out = config->get_if_id(config, FALSE) ?: data->if_id_out_def,
.label = data->label ? data->label->clone(data->label) : NULL,
.seq = data->seq,
.install_time = time_monotonic(NULL),
.policies_fwd_out = config->has_option(config, OPT_FWD_OUT_POLICIES),
);
@ -2142,13 +2163,14 @@ child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config,
if (!this->reqid)
{
/* reuse old reqid if we are rekeying an existing CHILD_SA and when
* initiating a trap policy. While the reqid cache would find the same
* reqid for our selectors, this does not work in a special case: If an
* SA is triggered by a trap policy, but the negotiated TS get
* narrowed, we still must reuse the same reqid to successfully
* replace the temporary SA on the kernel level. Rekeying such an SA
* requires an explicit reqid, as the cache currently knows the original
* selectors only for that reqid. */
* initiating a trap policy. the reqid cache will generally find the
* same reqid for our selectors. but this does not work in a special
* case: if the IPsec stack does not use sequence numbers for acquires,
* an SA is triggered by a trap policy and the negotiated TS get
* narrowed, we still must reuse the same reqid to successfully replace
* the temporary SA on the kernel level. however, if sequence numbers
* are supported, the reqid will later get updated in case of narrowing
* when alloc_reqid() is called */
if (data->reqid &&
charon->kernel->ref_reqid(charon->kernel, data->reqid) == SUCCESS)
{
@ -2176,3 +2198,72 @@ child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config,
}
return &this->public;
}
/**
* Check if the given traffic selector is contained in any of the traffic
* selectors in the given list.
*/
static bool is_ts_match(traffic_selector_t *to_check, array_t *list)
{
traffic_selector_t *ts;
int i;
for (i = 0; i < array_count(list); i++)
{
array_get(list, i, &ts);
if (to_check->is_contained_in(to_check, ts))
{
return TRUE;
}
}
return FALSE;
}
/**
* Check if all given traffic selectors are contained in any of the traffic
* selectors in the given list.
*/
static bool is_ts_list_match(traffic_selector_list_t *to_check, array_t *list)
{
enumerator_t *enumerator;
traffic_selector_t *ts;
bool matched = TRUE;
enumerator = to_check->create_enumerator(to_check);
while (enumerator->enumerate(enumerator, &ts))
{
if (!is_ts_match(ts, list))
{
matched = FALSE;
break;
}
}
enumerator->destroy(enumerator);
return matched;
}
/*
* Described in header
*/
bool child_sa_ts_match(child_sa_t *child, traffic_selector_t *src,
traffic_selector_t *dst)
{
private_child_sa_t *this = (private_child_sa_t*)child;
return src && dst &&
is_ts_match(src, this->my_ts) &&
is_ts_match(dst, this->other_ts);
}
/*
* Described in header
*/
bool child_sa_ts_lists_match(child_sa_t *child, traffic_selector_list_t *src,
traffic_selector_list_t *dst)
{
private_child_sa_t *this = (private_child_sa_t*)child;
return src && dst &&
is_ts_list_match(src, this->my_ts) &&
is_ts_list_match(dst, this->other_ts);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2006-2023 Tobias Brunner
* Copyright (C) 2006-2025 Tobias Brunner
* Copyright (C) 2006-2008 Martin Willi
* Copyright (C) 2006 Daniel Roethlisberger
*
@ -390,6 +390,22 @@ struct child_sa_t {
*/
sec_label_t *(*get_label)(child_sa_t *this);
/**
* Get the optional sequence number associated with the acquire that
* triggered this CHILD_SA.
*
* @return sequence number associated with the acquire or 0
*/
uint32_t (*get_acquire_seq)(child_sa_t *this);
/**
* Set the optional sequence number associated with the acquire that
* triggered this CHILD_SA.
*
* @param seq sequence number associated with the acquire
*/
void (*set_acquire_seq)(child_sa_t *this, uint32_t seq);
/**
* Create an enumerator over traffic selectors of one side.
*
@ -559,6 +575,9 @@ struct child_sa_create_t {
uint32_t if_id_out_def;
/** Optional security label to apply on SAs (cloned) */
sec_label_t *label;
/** Optional sequence number associated with the acquire that triggered
* this SA */
uint32_t seq;
/** TRUE to enable UDP encapsulation (NAT traversal) */
bool encap;
};
@ -575,4 +594,30 @@ struct child_sa_create_t {
child_sa_t *child_sa_create(host_t *me, host_t *other, child_cfg_t *config,
child_sa_create_t *data);
/**
* Check if the given source and destination traffic selectors (e.g. from a
* packet triggering an acquire) match the negotiated local and remote traffic
* selectors of this child SA.
*
* @param this CHILD_SA to check traffic selectors against
* @param src source traffic selector
* @param dst destination traffic selector
* @return TRUE if both traffic selectors match
*/
bool child_sa_ts_match(child_sa_t *this, traffic_selector_t *src,
traffic_selector_t *dst);
/**
* Check if the given lists of source and destination traffic selectors (e.g.
* from a previous SA) match the negotiated local and remote traffic
* selectors of this child SA.
*
* @param this CHILD_SA to check traffic selectors against
* @param src source traffic selector list
* @param dst destination traffic selector list
* @return TRUE if all traffic selectors match
*/
bool child_sa_ts_lists_match(child_sa_t *this, traffic_selector_list_t *src,
traffic_selector_list_t *dst);
#endif /** CHILD_SA_H_ @}*/

View File

@ -1628,7 +1628,7 @@ METHOD(ike_sa_t, initiate, status_t,
if (child_cfg)
{
/* normal IKE_SA with CHILD_SA */
this->task_manager->queue_child(this->task_manager, child_cfg, args);
this->task_manager->queue_child(this->task_manager, child_cfg, args, NULL);
#ifdef ME
if (this->peer_cfg->get_mediated_by(this->peer_cfg))
{
@ -2119,20 +2119,12 @@ static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new,
}
if (action & ACTION_START)
{
child_init_args_t args = {
.reqid = child_sa->get_reqid_ref(child_sa),
.label = child_sa->get_label(child_sa),
};
child_cfg = child_sa->get_config(child_sa);
DBG1(DBG_IKE, "restarting CHILD_SA %s",
child_cfg->get_name(child_cfg));
other->task_manager->queue_child(other->task_manager,
child_cfg->get_ref(child_cfg),
&args);
if (args.reqid)
{
charon->kernel->release_reqid(charon->kernel, args.reqid);
}
NULL, child_sa);
}
}
enumerator->destroy(enumerator);

View File

@ -412,6 +412,8 @@ struct child_init_args_t {
traffic_selector_t *dst;
/** Optional security label of triggering packet */
sec_label_t *label;
/** Optional sequence number associated with the acquire triggering the SA */
uint32_t seq;
};
/**

View File

@ -1009,7 +1009,7 @@ static status_t process_request(private_task_manager_t *this,
break;
}
task = (task_t *)quick_mode_create(this->ike_sa, NULL,
NULL, NULL);
NULL, NULL, 0);
this->passive_tasks->insert_last(this->passive_tasks, task);
break;
case INFORMATIONAL_V1:
@ -1691,18 +1691,31 @@ METHOD(task_manager_t, queue_mobike, void,
}
METHOD(task_manager_t, queue_child, void,
private_task_manager_t *this, child_cfg_t *cfg, child_init_args_t *args)
private_task_manager_t *this, child_cfg_t *cfg, child_init_args_t *args,
child_sa_t *child_sa)
{
quick_mode_t *task;
uint32_t reqid;
if (args)
if (child_sa)
{
task = quick_mode_create(this->ike_sa, cfg, args->src, args->dst);
task = quick_mode_create(this->ike_sa, cfg, NULL, NULL, 0);
reqid = child_sa->get_reqid_ref(child_sa);
if (reqid)
{
task->use_reqid(task, reqid);
charon->kernel->release_reqid(charon->kernel, reqid);
}
}
else if (args)
{
task = quick_mode_create(this->ike_sa, cfg, args->src, args->dst,
args->seq);
task->use_reqid(task, args->reqid);
}
else
{
task = quick_mode_create(this->ike_sa, cfg, NULL, NULL);
task = quick_mode_create(this->ike_sa, cfg, NULL, NULL, 0);
}
queue_task(this, &task->task);
}
@ -1816,7 +1829,7 @@ METHOD(task_manager_t, queue_child_rekey, void,
child_sa->set_state(child_sa, CHILD_REKEYING);
cfg = child_sa->get_config(child_sa);
task = quick_mode_create(this->ike_sa, cfg->get_ref(cfg),
get_first_ts(child_sa, TRUE), get_first_ts(child_sa, FALSE));
get_first_ts(child_sa, TRUE), get_first_ts(child_sa, FALSE), 0);
reqid = child_sa->get_reqid_ref(child_sa);
if (reqid)
{

View File

@ -540,8 +540,8 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local,
linked_list_t *list, *hosts;
hosts = get_dynamic_hosts(this->ike_sa, local);
list = this->config->get_traffic_selectors(this->config,
local, supplied, hosts, TRUE);
list = this->config->select_traffic_selectors(this->config, local,
supplied, hosts);
hosts->destroy(hosts);
if (list->get_first(list, (void**)&ts) == SUCCESS)
{
@ -1546,7 +1546,8 @@ METHOD(task_t, destroy, void,
* Described in header.
*/
quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
traffic_selector_t *tsi, traffic_selector_t *tsr)
traffic_selector_t *tsi, traffic_selector_t *tsr,
uint32_t seq)
{
private_quick_mode_t *this;
@ -1565,6 +1566,9 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
.rekey = _rekey,
.abort = _abort_,
},
.child = {
.seq = seq,
},
.ike_sa = ike_sa,
.initiator = config != NULL,
.config = config,

View File

@ -102,9 +102,11 @@ struct quick_mode_t {
* @param config child_cfg if task initiator, NULL if responder
* @param tsi source of triggering packet, or NULL
* @param tsr destination of triggering packet, or NULL
* @param seq optional sequence number of triggering acquire, or 0
* @return task to handle by the task_manager
*/
quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
traffic_selector_t *tsi, traffic_selector_t *tsr);
traffic_selector_t *tsi, traffic_selector_t *tsr,
uint32_t seq);
#endif /** QUICK_MODE_H_ @}*/

View File

@ -1168,7 +1168,7 @@ static status_t process_request(private_task_manager_t *this,
task = (task_t*)ike_auth_lifetime_create(this->ike_sa, FALSE);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
task = (task_t*)child_create_create(this->ike_sa, NULL, FALSE,
NULL, NULL);
NULL, NULL, 0);
array_insert(this->passive_tasks, ARRAY_TAIL, task);
break;
}
@ -1222,7 +1222,7 @@ static status_t process_request(private_task_manager_t *this,
else
{
task = (task_t*)child_create_create(this->ike_sa, NULL,
FALSE, NULL, NULL);
FALSE, NULL, NULL, 0);
}
}
else
@ -2196,7 +2196,8 @@ static void trigger_mbb_reauth(private_task_manager_t *this)
}
cfg = child_sa->get_config(child_sa);
child_create = child_create_create(new, cfg->get_ref(cfg),
FALSE, NULL, NULL);
FALSE, NULL, NULL, 0);
child_create->recreate_sa(child_create, child_sa);
reqid = child_sa->get_reqid_ref(child_sa);
if (reqid)
{
@ -2369,19 +2370,34 @@ METHOD(task_manager_t, queue_dpd, void,
}
METHOD(task_manager_t, queue_child, void,
private_task_manager_t *this, child_cfg_t *cfg, child_init_args_t *args)
private_task_manager_t *this, child_cfg_t *cfg, child_init_args_t *args,
child_sa_t *child_sa)
{
child_create_t *task;
uint32_t reqid;
if (args)
if (child_sa)
{
task = child_create_create(this->ike_sa, cfg, FALSE, args->src, args->dst);
task = child_create_create(this->ike_sa, cfg, FALSE, NULL, NULL, 0);
task->recreate_sa(task, child_sa);
reqid = child_sa->get_reqid_ref(child_sa);
if (reqid)
{
task->use_reqid(task, reqid);
charon->kernel->release_reqid(charon->kernel, reqid);
}
task->use_label(task, child_sa->get_label(child_sa));
}
else if (args)
{
task = child_create_create(this->ike_sa, cfg, FALSE, args->src,
args->dst, args->seq);
task->use_reqid(task, args->reqid);
task->use_label(task, args->label);
}
else
{
task = child_create_create(this->ike_sa, cfg, FALSE, NULL, NULL);
task = child_create_create(this->ike_sa, cfg, FALSE, NULL, NULL, 0);
}
queue_task(this, &task->task);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2024 Tobias Brunner
* Copyright (C) 2008-2025 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
*
@ -117,6 +117,16 @@ struct private_child_create_t {
*/
traffic_selector_t *packet_tsr;
/**
* Local traffic selectors as configured or previously negotiated
*/
traffic_selector_list_t *my_ts;
/**
* Remote traffic selectors as configured or previously negotiated
*/
traffic_selector_list_t *other_ts;
/**
* Key exchanges to perform
*/
@ -249,12 +259,22 @@ static void schedule_delayed_retry(private_child_create_t *this)
task = child_create_create(this->ike_sa,
this->config->get_ref(this->config), FALSE,
this->packet_tsi, this->packet_tsr);
this->packet_tsi, this->packet_tsr,
this->child.seq);
task->use_reqid(task, this->child.reqid);
task->use_marks(task, this->child.mark_in, this->child.mark_out);
task->use_if_ids(task, this->child.if_id_in, this->child.if_id_out);
task->use_label(task, this->child.label);
/* clone these directly as we don't have a public method */
if (this->my_ts && this->other_ts)
{
private_child_create_t *priv = (private_child_create_t*)task;
priv->my_ts = this->my_ts->clone(this->my_ts);
priv->other_ts = this->other_ts->clone(this->other_ts);
}
DBG1(DBG_IKE, "creating CHILD_SA failed, trying again in %d seconds",
retry);
this->ike_sa->queue_task_delayed(this->ike_sa, (task_t*)task, retry);
@ -451,15 +471,37 @@ static linked_list_t* get_transport_nat_ts(private_child_create_t *this,
return out;
}
/**
* Ensure we have traffic selector lists when not recreating an SA.
*/
static void ensure_ts_lists(private_child_create_t *this)
{
linked_list_t *ts;
if (!this->my_ts)
{
ts = this->config->get_traffic_selectors(this->config, TRUE, NULL);
this->my_ts = traffic_selector_list_create_from_list(ts);
}
if (!this->other_ts)
{
ts = this->config->get_traffic_selectors(this->config, FALSE, NULL);
this->other_ts = traffic_selector_list_create_from_list(ts);
}
}
/**
* Narrow received traffic selectors with configuration
*/
static linked_list_t* narrow_ts(private_child_create_t *this, bool local,
linked_list_t *in)
{
linked_list_t *hosts, *nat, *ts;
traffic_selector_list_t *ts;
linked_list_t *hosts, *nat, *result;
ike_condition_t cond;
ensure_ts_lists(this);
ts = local ? this->my_ts : this->other_ts;
cond = local ? COND_NAT_HERE : COND_NAT_THERE;
hosts = ike_sa_get_dynamic_hosts(this->ike_sa, local);
@ -467,19 +509,16 @@ static linked_list_t* narrow_ts(private_child_create_t *this, bool local,
this->ike_sa->has_condition(this->ike_sa, cond))
{
nat = get_transport_nat_ts(this, local, in);
ts = this->config->get_traffic_selectors(this->config, local, nat,
hosts, TRUE);
result = child_cfg_select_ts(this->config, local, ts, nat, hosts);
nat->destroy_offset(nat, offsetof(traffic_selector_t, destroy));
}
else
{
ts = this->config->get_traffic_selectors(this->config, local, in,
hosts, TRUE);
result = child_cfg_select_ts(this->config, local, ts, in, hosts);
}
hosts->destroy(hosts);
return ts;
return result;
}
/**
@ -652,7 +691,6 @@ static status_t install_child_sa(private_child_create_t *this)
this->child_sa->set_mode(this->child_sa, this->mode);
this->child_sa->set_protocol(this->child_sa,
this->proposal->get_protocol(this->proposal));
this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
/* addresses might have changed since we originally sent the request, update
* them before we configure any policies and install the SAs */
@ -669,6 +707,7 @@ static status_t install_child_sa(private_child_create_t *this)
other_ts->destroy_offset(other_ts,
offsetof(traffic_selector_t, destroy));
}
this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
if (this->my_cpi == 0 || this->other_cpi == 0 || this->ipcomp == IPCOMP_NONE)
{
@ -1291,21 +1330,62 @@ static status_t defer_child_sa(private_child_create_t *this)
}
/**
* Compare two CHILD_SA objects for equality
* Compare the reqids and possibly traffic selectors of two CHILD_SAs for
* equality.
*
* The second CHILD_SA is assumed to be the one newly created.
*/
static bool child_sa_equals(child_sa_t *a, child_sa_t *b)
static bool reqid_and_ts_equals(private_child_create_t *this, child_sa_t *a,
child_sa_t *b)
{
/* reqids are allocated based on the traffic selectors. if all the other
* selectors are the same, they can only differ if narrowing occurred.
*
* if the new SA has no reqid assigned, it was initiated manually or due to
* a start action and we assume the peer will do the same narrowing it
* possibly did before, so we treat the SAs as equal.
*
* if the new SA has a reqid, it was either triggered by an acquire or
* during a reestablishment. if they are equal, we are done */
if (!b->get_reqid(b) || a->get_reqid(a) == b->get_reqid(b))
{
return TRUE;
}
/* if the reqids differ, the one of the established SA was changed due to
* narrowing. in this case, we check if we have either triggering TS or
* previous TS. if so, we check whether the available TS match the TS of
* the existing SA. if they do, there is no point to negotiate another SA.
* if not, the peer will potentially narrow the TS to a different set for
* the new SA. */
if (this->packet_tsi && this->packet_tsr)
{
return child_sa_ts_match(a, this->packet_tsi, this->packet_tsr);
}
if (this->my_ts && this->other_ts)
{
return child_sa_ts_lists_match(a, this->my_ts, this->other_ts);
}
/* if we don't have any TS to compare, we assume the peer will do the same
* narrowing and treat the SAs equal.*/
return TRUE;
}
/**
* Compare two CHILD_SA objects for equality.
*
* The second CHILD_SA is assumed to be the one newly created.
*/
static bool child_sa_equals(private_child_create_t *this, child_sa_t *a,
child_sa_t *b)
{
child_cfg_t *cfg = a->get_config(a);
return cfg->equals(cfg, b->get_config(b)) &&
/* reqids are allocated based on the final TS, so we can only compare
* them if they are static (i.e. both have them) */
(!a->get_reqid(a) || !b->get_reqid(b) ||
a->get_reqid(a) == b->get_reqid(b)) &&
a->get_mark(a, TRUE).value == b->get_mark(b, TRUE).value &&
a->get_mark(a, FALSE).value == b->get_mark(b, FALSE).value &&
a->get_if_id(a, TRUE) == b->get_if_id(b, TRUE) &&
a->get_if_id(a, FALSE) == b->get_if_id(b, FALSE) &&
sec_labels_equal(a->get_label(a), b->get_label(b));
sec_labels_equal(a->get_label(a), b->get_label(b)) &&
reqid_and_ts_equals(this, a, b);
}
/**
@ -1321,7 +1401,7 @@ static bool check_for_duplicate(private_child_create_t *this)
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
if (child_sa->get_state(child_sa) == CHILD_INSTALLED &&
child_sa_equals(child_sa, this->child_sa))
child_sa_equals(this, child_sa, this->child_sa))
{
found = child_sa;
break;
@ -1402,13 +1482,66 @@ METHOD(task_t, build_i_multi_ke, status_t,
return NEED_MORE;
}
/**
* 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;
ensure_ts_lists(this);
list = linked_list_create();
if (!this->rekey)
{
/* check if we want a virtual IP */
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
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 a 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 = child_cfg_select_ts(this->config, TRUE, this->my_ts, NULL,
list);
list->destroy_offset(list, offsetof(host_t, destroy));
}
else
{
list->destroy(list);
list = ike_sa_get_dynamic_hosts(this->ike_sa, TRUE);
this->tsi = child_cfg_select_ts(this->config, TRUE, this->my_ts, NULL,
list);
list->destroy(list);
}
list = ike_sa_get_dynamic_hosts(this->ike_sa, FALSE);
this->tsr = child_cfg_select_ts(this->config, FALSE, this->other_ts, NULL,
list);
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))
@ -1444,49 +1577,7 @@ METHOD(task_t, build_i, status_t,
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)
{
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
@ -1541,7 +1632,11 @@ METHOD(task_t, build_i, status_t,
return FAILED;
}
if (!no_ke && !this->retry)
if (no_ke)
{ /* we might have one set if we are recreating this SA */
this->ke_method = KE_NONE;
}
else if (!this->retry)
{ /* during a rekeying the method might already be set */
if (this->ke_method == KE_NONE)
{
@ -2047,7 +2142,7 @@ METHOD(task_t, build_r, status_t,
return SUCCESS;
}
/* check if ike_config_t included non-critical error notifies */
/* check if ike_config_t task included non-critical error notifies */
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
@ -2507,10 +2602,81 @@ METHOD(child_create_t, use_label, void,
this->child.label = label ? label->clone(label) : NULL;
}
METHOD(child_create_t, use_ke_method, void,
private_child_create_t *this, key_exchange_method_t ke_method)
/**
* Prepare traffic selectors for reuse when recreating a CHILD_SA.
*/
static void reuse_ts(private_child_create_t *this, bool local, child_sa_t *old,
traffic_selector_list_t **target)
{
this->ke_method = ke_method;
enumerator_t *old_ts, *hosts_enum;
linked_list_t *hosts, *list;
traffic_selector_t *ts, *new_ts;
host_t *host;
old_ts = old->create_ts_enumerator(old, local);
if (this->rekey)
{
/* when rekeying, we just reuse the previous TS. this is also the only
* way a responder reuses TS */
*target = traffic_selector_list_create_from_enumerator(old_ts);
return;
}
/* when recreating/reauthenticating, we check whether the dynamic IPs of
* the IKE_SA (as copied from the old SA) match the TS and replace
* them with dynamic TS (reusing protocol/ports in case of narrowing) so
* they get updated to possibly new IPs when the TS are prepared later */
list = linked_list_create();
hosts = ike_sa_get_dynamic_hosts(this->ike_sa, local);
hosts_enum = hosts->create_enumerator(hosts);
while (old_ts->enumerate(old_ts, &ts))
{
new_ts = NULL;
while (hosts_enum->enumerate(hosts_enum, &host))
{
if (ts->is_host(ts, host))
{
new_ts = traffic_selector_create_dynamic(ts->get_protocol(ts),
ts->get_from_port(ts),
ts->get_to_port(ts));
break;
}
}
hosts->reset_enumerator(hosts, hosts_enum);
if (!new_ts)
{
new_ts = ts->clone(ts);
}
list->insert_last(list, new_ts);
}
hosts_enum->destroy(hosts_enum);
hosts->destroy(hosts);
old_ts->destroy(old_ts);
*target = traffic_selector_list_create_from_list(list);
}
METHOD(child_create_t, recreate_sa, void,
private_child_create_t *this, child_sa_t *old)
{
if (this->initiator)
{
proposal_t *proposal;
uint16_t ke_method;
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;
}
}
/* use previously negotiated traffic selectors */
reuse_ts(this, TRUE, old, &this->my_ts);
reuse_ts(this, FALSE, old, &this->other_ts);
}
METHOD(child_create_t, get_child, child_sa_t*,
@ -2570,32 +2736,17 @@ METHOD(task_t, migrate, void,
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->tsr, offsetof(traffic_selector_t, destroy));
DESTROY_OFFSET_IF(this->tsi, 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));
DESTROY_IF(this->child_sa);
DESTROY_IF(this->proposal);
DESTROY_IF(this->nonceg);
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));
}
DESTROY_OFFSET_IF(this->proposals, offsetof(proposal_t, destroy));
if (!this->rekey && !this->retry)
{
this->ke_method = KE_NONE;
@ -2626,22 +2777,10 @@ METHOD(task_t, destroy, void,
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->tsr, offsetof(traffic_selector_t, destroy));
DESTROY_OFFSET_IF(this->tsi, 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);
@ -2652,13 +2791,12 @@ METHOD(task_t, destroy, void,
}
DESTROY_IF(this->packet_tsi);
DESTROY_IF(this->packet_tsr);
DESTROY_IF(this->my_ts);
DESTROY_IF(this->other_ts);
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);
@ -2669,8 +2807,9 @@ METHOD(task_t, destroy, void,
* Described in header.
*/
child_create_t *child_create_create(ike_sa_t *ike_sa,
child_cfg_t *config, bool rekey,
traffic_selector_t *tsi, traffic_selector_t *tsr)
child_cfg_t *config, bool rekey,
traffic_selector_t *tsi,
traffic_selector_t *tsr, uint32_t seq)
{
private_child_create_t *this;
@ -2685,7 +2824,7 @@ child_create_t *child_create_create(ike_sa_t *ike_sa,
.use_marks = _use_marks,
.use_if_ids = _use_if_ids,
.use_label = _use_label,
.use_ke_method = _use_ke_method,
.recreate_sa = _recreate_sa,
.abort = _abort_,
.task = {
.get_type = _get_type,
@ -2693,6 +2832,9 @@ child_create_t *child_create_create(ike_sa_t *ike_sa,
.destroy = _destroy,
},
},
.child = {
.seq = seq,
},
.ike_sa = ike_sa,
.config = config,
.packet_tsi = tsi ? tsi->clone(tsi) : NULL,

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2018-2024 Tobias Brunner
* Copyright (C) 2018-2025 Tobias Brunner
* Copyright (C) 2007 Martin Willi
*
* Copyright (C) secunet Security Networks AG
@ -81,13 +81,12 @@ struct child_create_t {
void (*use_label)(child_create_t *this, sec_label_t *label);
/**
* Initially propose a specific KE method to override configuration.
* Use data from the given old SA (e.g. KE method and traffic selectors)
* when rekeying/recreating it.
*
* This is used during rekeying to prefer the previously negotiated method.
*
* @param ke_method KE method to use
* @param old old CHILD_SA that is getting rekeyed/recreated
*/
void (*use_ke_method)(child_create_t *this, key_exchange_method_t ke_method);
void (*recreate_sa)(child_create_t *this, child_sa_t *old);
/**
* Get the lower of the two nonces, used for rekey collisions.
@ -140,10 +139,12 @@ struct child_create_t {
* @param rekey whether we do a rekey or not
* @param tsi source of triggering packet, or NULL
* @param tsr destination of triggering packet, or NULL
* @param seq optional sequence number of triggering acquire, or 0
* @return child_create task to handle by the task_manager
*/
child_create_t *child_create_create(ike_sa_t *ike_sa,
child_cfg_t *config, bool rekey,
traffic_selector_t *tsi, traffic_selector_t *tsr);
child_cfg_t *config, bool rekey,
traffic_selector_t *tsi,
traffic_selector_t *tsr, uint32_t seq);
#endif /** CHILD_CREATE_H_ @}*/

View File

@ -151,6 +151,29 @@ static void conclude_rekeying(private_child_delete_t *this, child_sa_t *old)
child_rekey_conclude_rekeying(old, child_sa);
}
/**
* Queue a task to recreate the given CHILD_SA.
*/
static void queue_child_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
{
child_create_t *child_create;
child_cfg_t *child_cfg;
uint32_t reqid;
child_cfg = child_sa->get_config(child_sa);
child_create = child_create_create(ike_sa, child_cfg->get_ref(child_cfg),
FALSE, NULL, NULL, 0);
child_create->recreate_sa(child_create, child_sa);
reqid = child_sa->get_reqid_ref(child_sa);
if (reqid)
{
child_create->use_reqid(child_create, reqid);
charon->kernel->release_reqid(charon->kernel, reqid);
}
child_create->use_label(child_create, child_sa->get_label(child_sa));
ike_sa->queue_task(ike_sa, (task_t*)child_create);
}
/**
* Destroy and optionally reestablish the given CHILD_SA according to config.
*/
@ -160,12 +183,9 @@ static status_t destroy_and_reestablish_internal(ike_sa_t *ike_sa,
bool delete_action,
action_t forced_action)
{
child_init_args_t args = {};
child_cfg_t *child_cfg;
protocol_id_t protocol;
uint32_t spi;
action_t action;
status_t status = SUCCESS;
bool initiate = FALSE;
child_sa->set_state(child_sa, CHILD_DELETED);
if (trigger_updown)
@ -173,44 +193,29 @@ static status_t destroy_and_reestablish_internal(ike_sa_t *ike_sa,
charon->bus->child_updown(charon->bus, child_sa, FALSE);
}
protocol = child_sa->get_protocol(child_sa);
spi = child_sa->get_spi(child_sa, TRUE);
child_cfg = child_sa->get_config(child_sa);
child_cfg->get_ref(child_cfg);
args.reqid = child_sa->get_reqid_ref(child_sa);
args.label = child_sa->get_label(child_sa);
if (args.label)
{
args.label = args.label->clone(args.label);
}
action = forced_action ?: child_sa->get_close_action(child_sa);
DBG1(DBG_IKE, "CHILD_SA %s{%u} closed", child_sa->get_name(child_sa),
child_sa->get_unique_id(child_sa));
ike_sa->destroy_child_sa(ike_sa, protocol, spi);
action = forced_action ?: child_sa->get_close_action(child_sa);
if (delete_action)
{
if (action & ACTION_TRAP)
{
child_cfg = child_sa->get_config(child_sa);
charon->traps->install(charon->traps,
ike_sa->get_peer_cfg(ike_sa),
child_cfg);
child_cfg->get_ref(child_cfg));
}
if (action & ACTION_START)
{
child_cfg->get_ref(child_cfg);
status = ike_sa->initiate(ike_sa, child_cfg, &args);
queue_child_create(ike_sa, child_sa);
initiate = TRUE;
}
}
child_cfg->destroy(child_cfg);
if (args.reqid)
{
charon->kernel->release_reqid(charon->kernel, args.reqid);
}
DESTROY_IF(args.label);
return status;
ike_sa->destroy_child_sa(ike_sa, child_sa->get_protocol(child_sa),
child_sa->get_spi(child_sa, TRUE));
return initiate ? ike_sa->initiate(ike_sa, NULL, NULL) : SUCCESS;
}
/*
@ -550,13 +555,8 @@ METHOD(task_t, build_i, status_t,
if (this->expired)
{
child_cfg_t *child_cfg;
DBG1(DBG_IKE, "scheduling CHILD_SA recreate after hard expire");
child_cfg = child_sa->get_config(child_sa);
this->ike_sa->queue_task(this->ike_sa, (task_t*)
child_create_create(this->ike_sa, child_cfg->get_ref(child_cfg),
FALSE, NULL, NULL));
DBG1(DBG_IKE, "queue CHILD_SA recreate after hard expire");
queue_child_create(this->ike_sa, child_sa);
}
return NEED_MORE;
}

View File

@ -269,20 +269,14 @@ METHOD(task_t, build_i, status_t,
if (!this->child_create)
{
child_cfg_t *config;
proposal_t *proposal;
uint16_t ke_method;
uint32_t reqid;
config = this->child_sa->get_config(this->child_sa);
this->child_create = child_create_create(this->ike_sa,
config->get_ref(config), TRUE, NULL, NULL);
config->get_ref(config), TRUE, NULL, NULL, 0);
this->child_create->recreate_sa(this->child_create, this->child_sa);
proposal = this->child_sa->get_proposal(this->child_sa);
if (proposal->get_algorithm(proposal, KEY_EXCHANGE_METHOD,
&ke_method, NULL))
{ /* reuse the KE method negotiated previously */
this->child_create->use_ke_method(this->child_create, ke_method);
}
reqid = this->child_sa->get_reqid_ref(this->child_sa);
if (reqid)
{
@ -436,6 +430,7 @@ METHOD(task_t, build_r, status_t,
if (message->get_exchange_type(message) == CREATE_CHILD_SA)
{
this->child_create->recreate_sa(this->child_create, this->child_sa);
reqid = this->child_sa->get_reqid_ref(this->child_sa);
if (reqid)
{
@ -1240,7 +1235,8 @@ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol,
this->public.task.build = _build_r;
this->public.task.process = _process_r;
this->initiator = FALSE;
this->child_create = child_create_create(ike_sa, NULL, TRUE, NULL, NULL);
this->child_create = child_create_create(ike_sa, NULL, TRUE,
NULL, NULL, 0);
}
return &this->public;

View File

@ -119,10 +119,8 @@ static bool install_shunt_policy(child_cfg_t *child)
host_any6 = host_create_any(AF_INET6);
hosts = linked_list_create_with_items(host_any, host_any6, NULL);
my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts,
FALSE);
other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts,
FALSE);
my_ts_list = child->get_traffic_selectors(child, TRUE, hosts);
other_ts_list = child->get_traffic_selectors(child, FALSE, hosts);
hosts->destroy(hosts);
manual_prio = child->get_manual_prio(child);
@ -293,10 +291,8 @@ static void uninstall_shunt_policy(child_cfg_t *child)
host_any6 = host_create_any(AF_INET6);
hosts = linked_list_create_with_items(host_any, host_any6, NULL);
my_ts_list = child->get_traffic_selectors(child, TRUE, NULL, hosts,
FALSE);
other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts,
FALSE);
my_ts_list = child->get_traffic_selectors(child, TRUE, hosts);
other_ts_list = child->get_traffic_selectors(child, FALSE, hosts);
hosts->destroy(hosts);
manual_prio = child->get_manual_prio(child);

View File

@ -202,9 +202,10 @@ struct task_manager_t {
*
* @param cfg CHILD_SA config to establish
* @param args optional arguments for the initiation
* @param child_sa optional CHILD_SA when recreating (instead of args)
*/
void (*queue_child)(task_manager_t *this, child_cfg_t *cfg,
child_init_args_t *args);
child_init_args_t *args, child_sa_t *child_sa);
/**
* Queue CHILD_SA rekeying tasks.

View File

@ -93,6 +93,11 @@ struct private_trap_manager_t {
* Whether to ignore traffic selectors from acquires
*/
bool ignore_acquire_ts;
/**
* Current acquire sequence number if not generated by the kernel
*/
refcount_t acquire_seq;
};
/**
@ -121,8 +126,10 @@ typedef struct {
uint32_t reqid;
/** destination address (wildcard case) */
host_t *dst;
/** security label, if any */
sec_label_t *label;
/** data from the kernel */
kernel_acquire_data_t *data;
/** optional sequence number from the kernel if an acquire is retriggered */
uint32_t new_seq;
} acquire_t;
/**
@ -145,7 +152,7 @@ static void destroy_entry(entry_t *this)
static void destroy_acquire(acquire_t *this)
{
DESTROY_IF(this->dst);
DESTROY_IF(this->label);
kernel_acquire_data_destroy(this->data);
free(this);
}
@ -156,7 +163,7 @@ CALLBACK(acquire_by_reqid, bool,
sec_label_t *label;
VA_ARGS_VGET(args, reqid, label);
return this->reqid == reqid && sec_labels_equal(this->label, label);
return this->reqid == reqid && sec_labels_equal(this->data->label, label);
}
CALLBACK(acquire_by_dst, bool,
@ -178,7 +185,7 @@ static bool dynamic_remote_ts(child_cfg_t *child)
traffic_selector_t *ts;
bool found = FALSE;
other_ts = child->get_traffic_selectors(child, FALSE, NULL, NULL, FALSE);
other_ts = child->get_traffic_selectors(child, FALSE, NULL);
enumerator = other_ts->create_enumerator(other_ts);
while (enumerator->enumerate(enumerator, &ts))
{
@ -206,8 +213,8 @@ static status_t install_trap(child_sa_t *child_sa, linked_list_t *local,
child = child_sa->get_config(child_sa);
my_ts = child->get_traffic_selectors(child, TRUE, NULL, local, FALSE);
other_ts = child->get_traffic_selectors(child, FALSE, NULL, remote, FALSE);
my_ts = child->get_traffic_selectors(child, TRUE, local);
other_ts = child->get_traffic_selectors(child, FALSE, remote);
/* we don't know the finally negotiated protocol (ESP|AH), we install
* the SA with the protocol of the first proposal */
@ -519,13 +526,13 @@ METHOD(trap_manager_t, acquire, void,
{
enumerator_t *enumerator;
entry_t *entry, *found = NULL;
acquire_t *acquire;
acquire_t *acquire = NULL;
peer_cfg_t *peer;
child_cfg_t *child;
ike_sa_t *ike_sa;
host_t *host;
uint32_t allocated_reqid;
bool wildcard, ignore = FALSE;
host_t *host = NULL;
uint32_t allocated_reqid, seq = 0;
bool wildcard;
this->lock->read_lock(this->lock);
enumerator = this->traps->create_enumerator(this->traps);
@ -550,44 +557,40 @@ METHOD(trap_manager_t, acquire, void,
this->mutex->lock(this->mutex);
if (wildcard)
{ /* for wildcard acquires we check that we don't have a pending acquire
{
/* for wildcard acquires we check that we don't have a pending acquire
* with the same peer */
uint8_t mask;
data->dst->to_subnet(data->dst, &host, &mask);
if (this->acquires->find_first(this->acquires, acquire_by_dst,
(void**)&acquire, host))
(void**)&acquire, host))
{
host->destroy(host);
ignore = TRUE;
}
else
{
INIT(acquire,
.dst = host,
.reqid = reqid,
);
this->acquires->insert_last(this->acquires, acquire);
}
}
else
{
if (this->acquires->find_first(this->acquires, acquire_by_reqid,
(void**)&acquire, reqid, data->label))
{
ignore = TRUE;
}
else
{
INIT(acquire,
.reqid = reqid,
.label = data->label ? data->label->clone(data->label) : NULL,
);
this->acquires->insert_last(this->acquires, acquire);
}
this->acquires->find_first(this->acquires, acquire_by_reqid,
(void**)&acquire, reqid, data->label);
}
if (!acquire)
{
INIT(acquire,
.dst = host,
.reqid = reqid,
.data = kernel_acquire_data_clone(data),
);
seq = data->seq = data->seq ?: ref_get_nonzero(&this->acquire_seq);
this->acquires->insert_last(this->acquires, acquire);
}
else if (data->seq && data->seq != acquire->data->seq)
{
/* acquire got retriggered, update to latest sequence number */
acquire->new_seq = data->seq;
}
this->mutex->unlock(this->mutex);
if (ignore)
if (!seq)
{
DBG1(DBG_CFG, "ignoring acquire for reqid %u, connection attempt "
"pending", reqid);
@ -642,6 +645,7 @@ METHOD(trap_manager_t, acquire, void,
.src = data->src,
.dst = data->dst,
.label = data->label,
.seq = seq,
};
if (this->ignore_acquire_ts || ike_sa->get_version(ike_sa) == IKEV1)
@ -679,39 +683,90 @@ METHOD(trap_manager_t, acquire, void,
}
/**
* Complete the acquire, if successful or failed
* Update the sequence number of the CHILD_SA before installing it in case
* the acquire got retriggered.
*/
static void update_acquire_seq(private_trap_manager_t *this, ike_sa_t *ike_sa,
child_sa_t *child_sa)
{
enumerator_t *enumerator;
acquire_t *acquire;
uint32_t seq;
/* ignore trap policies */
if (!ike_sa)
{
return;
}
/* if a CHILD_SA was not triggered by an acquire (e.g. manually or via
* start action) and the kernel supports sequence numbers, we check if the
* negotiated TS match the triggering packet and set the sequence number
* to remove the temporary state in the kernel */
seq = child_sa->get_acquire_seq(child_sa);
if (!seq &&
!(charon->kernel->get_features(charon->kernel) & KERNEL_ACQUIRE_SEQ))
{
return;
}
this->mutex->lock(this->mutex);
enumerator = this->acquires->create_enumerator(this->acquires);
while (enumerator->enumerate(enumerator, &acquire))
{
if (!seq)
{
/* if not triggered by an acquire, compare the TS from the packet
* that triggered the current acquire, set the seq if they match */
if (!child_sa_ts_match(child_sa, acquire->data->src,
acquire->data->dst))
{
continue;
}
child_sa->set_acquire_seq(child_sa, acquire->data->seq);
}
else if (!acquire->ike_sa || seq != acquire->data->seq)
{
continue;
}
if (acquire->new_seq)
{
child_sa->set_acquire_seq(child_sa, acquire->new_seq);
acquire->data->seq = acquire->new_seq;
acquire->new_seq = 0;
}
break;
}
enumerator->destroy(enumerator);
this->mutex->unlock(this->mutex);
}
/**
* Complete the acquire, if successful or failed.
*/
static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
child_sa_t *child_sa)
{
enumerator_t *enumerator;
acquire_t *acquire;
uint32_t seq = 0;
/* ignore trap policies and CHILD_SAs not triggered by an acquire */
if (!ike_sa || (child_sa && !(seq = child_sa->get_acquire_seq(child_sa))))
{
return;
}
this->mutex->lock(this->mutex);
enumerator = this->acquires->create_enumerator(this->acquires);
while (enumerator->enumerate(enumerator, &acquire))
{
if (!acquire->ike_sa || acquire->ike_sa != ike_sa)
/* just look at the sequence number when handling a CHILD_SA event,
* otherwise, compare the IKE_SA */
if (!acquire->ike_sa || (seq && seq != acquire->data->seq) ||
(!seq && acquire->ike_sa != ike_sa))
{
continue;
}
if (child_sa)
{
if (acquire->dst)
{
/* since every wildcard acquire results in a separate IKE_SA
* there is no need to compare the destination address */
}
else if (child_sa->get_reqid(child_sa) != acquire->reqid)
{
continue;
}
else if (!sec_labels_equal(acquire->label,
child_sa->get_label(child_sa)))
{
continue;
}
}
this->acquires->remove_at(this->acquires, enumerator);
destroy_acquire(acquire);
}
@ -738,6 +793,9 @@ METHOD(listener_t, child_state_change, bool,
{
switch (state)
{
case CHILD_INSTALLING:
update_acquire_seq(listener->traps, ike_sa, child_sa);
return TRUE;
case CHILD_INSTALLED:
case CHILD_DESTROYING:
complete(listener->traps, ike_sa, child_sa);

View File

@ -42,7 +42,7 @@ networking/streams/stream_tcp.c networking/streams/stream_service_tcp.c \
pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
selectors/sec_label.c selectors/traffic_selector.c \
selectors/sec_label.c selectors/traffic_selector.c selectors/traffic_selector_list.c \
settings/settings.c settings/settings_types.c \
settings/settings_parser.c settings/settings_lexer.c utils/cpu_feature.c \
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \

View File

@ -40,7 +40,7 @@ networking/streams/stream_tcp.c networking/streams/stream_service_tcp.c \
pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \
processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \
processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \
selectors/sec_label.c selectors/traffic_selector.c \
selectors/sec_label.c selectors/traffic_selector.c selectors/traffic_selector_list.c \
settings/settings.c settings/settings_types.c \
settings/settings_parser.y settings/settings_lexer.l utils/cpu_feature.c \
utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \
@ -125,7 +125,7 @@ resolver/rr.h resolver/resolver_manager.h \
plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \
processing/jobs/job.h processing/jobs/callback_job.h processing/processor.h \
processing/scheduler.h processing/watcher.h \
selectors/sec_label.h selectors/traffic_selector.h \
selectors/sec_label.h selectors/traffic_selector.h selectors/traffic_selector_list.h \
settings/settings.h settings/settings_parser.h threading/thread_value.h \
threading/thread.h threading/windows/thread.h \
threading/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h \

View File

@ -0,0 +1,292 @@
/*
* Copyright (C) 2008-2025 Tobias Brunner
* Copyright (C) 2005-2007 Martin Willi
*
* Copyright (C) secunet Security Networks AG
*
* 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
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "traffic_selector_list.h"
#include <utils/debug.h>
typedef struct private_traffic_selector_list_t private_traffic_selector_list_t;
/**
* Private data.
*/
struct private_traffic_selector_list_t {
/**
* Public interface.
*/
traffic_selector_list_t public;
/**
* List of managed traffic selectors.
*/
linked_list_t *ts;
};
METHOD(traffic_selector_list_t, add, void,
private_traffic_selector_list_t *this, traffic_selector_t *ts)
{
this->ts->insert_last(this->ts, ts);
}
METHOD(traffic_selector_list_t, create_enumerator, enumerator_t*,
private_traffic_selector_list_t *this)
{
return this->ts->create_enumerator(this->ts);
}
/**
* Create a copy of the traffic selectors in the given list, while resolving
* "dynamic" traffic selectors using the given hosts, if any. When not narrowing
* as initiator, we also replace TS in transport mode.
*/
static linked_list_t *resolve_dynamic_ts(private_traffic_selector_list_t *this,
linked_list_t *hosts, bool narrowing,
bool force_dynamic)
{
enumerator_t *e1, *e2;
traffic_selector_t *ts1, *ts2;
linked_list_t *result;
host_t *host;
if (!hosts || !hosts->get_count(hosts))
{
return this->ts->clone_offset(this->ts,
offsetof(traffic_selector_t, clone));
}
result = linked_list_create();
e1 = this->ts->create_enumerator(this->ts);
while (e1->enumerate(e1, &ts1))
{
/* set hosts if TS is dynamic or if forced as initiator in
* transport mode */
bool dynamic = ts1->is_dynamic(ts1);
if (!dynamic && !force_dynamic)
{
result->insert_last(result, ts1->clone(ts1));
continue;
}
e2 = hosts->create_enumerator(hosts);
while (e2->enumerate(e2, &host))
{
if (!dynamic && !host->is_anyaddr(host) &&
!ts1->includes(ts1, host))
{ /* for transport mode, we skip TS that don't match
* specific IPs */
continue;
}
ts2 = ts1->clone(ts1);
if (dynamic || !host->is_anyaddr(host))
{ /* don't make regular TS larger than they were */
ts2->set_address(ts2, host);
}
result->insert_last(result, ts2);
}
e2->destroy(e2);
}
e1->destroy(e1);
return result;
}
/**
* Remove duplicate traffic selectors in the given list.
*/
static void remove_duplicate_ts(linked_list_t *list)
{
enumerator_t *e1, *e2;
traffic_selector_t *ts1, *ts2;
e1 = list->create_enumerator(list);
e2 = list->create_enumerator(list);
while (e1->enumerate(e1, &ts1))
{
while (e2->enumerate(e2, &ts2))
{
if (ts1 != ts2)
{
if (ts2->is_contained_in(ts2, ts1))
{
list->remove_at(list, e2);
ts2->destroy(ts2);
list->reset_enumerator(list, e1);
break;
}
if (ts1->is_contained_in(ts1, ts2))
{
list->remove_at(list, e1);
ts1->destroy(ts1);
break;
}
}
}
list->reset_enumerator(list, e2);
}
e1->destroy(e1);
e2->destroy(e2);
}
METHOD(traffic_selector_list_t, get, linked_list_t*,
private_traffic_selector_list_t *this, linked_list_t *hosts,
bool force_dynamic)
{
linked_list_t *result;
result = resolve_dynamic_ts(this, hosts, FALSE, force_dynamic);
remove_duplicate_ts(result);
return result;
}
METHOD(traffic_selector_list_t, select_, linked_list_t*,
private_traffic_selector_list_t *this, linked_list_t *supplied,
linked_list_t *hosts, bool force_dynamic, bool *narrowed)
{
enumerator_t *e1, *e2;
traffic_selector_t *ts1, *ts2, *selected;
linked_list_t *resolved, *result;
result = linked_list_create();
resolved = resolve_dynamic_ts(this, hosts, supplied != NULL, force_dynamic);
if (!supplied)
{
while (resolved->remove_first(resolved, (void**)&ts1) == SUCCESS)
{
DBG2(DBG_CFG, " %R", ts1);
result->insert_last(result, ts1);
}
}
else
{
e1 = resolved->create_enumerator(resolved);
e2 = supplied->create_enumerator(supplied);
/* enumerate all configured/resolved selectors */
while (e1->enumerate(e1, &ts1))
{
/* enumerate all supplied traffic selectors */
while (e2->enumerate(e2, &ts2))
{
selected = ts1->get_subset(ts1, ts2);
if (selected)
{
DBG2(DBG_CFG, " config: %R, received: %R => match: %R",
ts1, ts2, selected);
result->insert_last(result, selected);
}
else
{
DBG2(DBG_CFG, " config: %R, received: %R => no match",
ts1, ts2);
}
}
supplied->reset_enumerator(supplied, e2);
}
e1->destroy(e1);
e2->destroy(e2);
if (narrowed)
{
*narrowed = FALSE;
e1 = resolved->create_enumerator(resolved);
e2 = result->create_enumerator(result);
while (e1->enumerate(e1, &ts1))
{
if (!e2->enumerate(e2, &ts2) || !ts1->equals(ts1, ts2))
{
*narrowed = TRUE;
break;
}
}
e1->destroy(e1);
e2->destroy(e2);
}
}
resolved->destroy_offset(resolved, offsetof(traffic_selector_t, destroy));
remove_duplicate_ts(result);
return result;
}
METHOD(traffic_selector_list_t, equals, bool,
private_traffic_selector_list_t *this, traffic_selector_list_t *other_pub)
{
private_traffic_selector_list_t *other = (private_traffic_selector_list_t*)other_pub;
return this->ts->equals_offset(this->ts, other->ts,
offsetof(traffic_selector_t, equals));
}
METHOD(traffic_selector_list_t, destroy, void,
private_traffic_selector_list_t *this)
{
this->ts->destroy_offset(this->ts, offsetof(traffic_selector_t, destroy));
free(this);
}
METHOD(traffic_selector_list_t, clone_, traffic_selector_list_t*,
private_traffic_selector_list_t *this)
{
return traffic_selector_list_create_from_list(
this->ts->clone_offset(this->ts, offsetof(traffic_selector_t, clone)));
}
/*
* Described in header
*/
traffic_selector_list_t *traffic_selector_list_create_from_list(linked_list_t *list)
{
private_traffic_selector_list_t *this;
INIT(this,
.public = {
.add = _add,
.create_enumerator = _create_enumerator,
.get = _get,
.select = _select_,
.equals = _equals,
.clone = _clone_,
.destroy = _destroy,
},
.ts = list,
);
return &this->public;
}
/*
* Described in header
*/
traffic_selector_list_t *traffic_selector_list_create()
{
return traffic_selector_list_create_from_list(linked_list_create());
}
/*
* Described in header
*/
traffic_selector_list_t *traffic_selector_list_create_from_enumerator(
enumerator_t *enumerator)
{
traffic_selector_list_t *this = traffic_selector_list_create();
traffic_selector_t *ts;
while (enumerator->enumerate(enumerator, &ts))
{
add((private_traffic_selector_list_t*)this, ts->clone(ts));
}
enumerator->destroy(enumerator);
return this;
}

View File

@ -0,0 +1,155 @@
/*
* Copyright (C) 2025 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
* 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
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup traffic_selector_list traffic_selector_list
* @{ @ingroup selectors
*/
#ifndef TRAFFIC_SELECTOR_LIST_H_
#define TRAFFIC_SELECTOR_LIST_H_
typedef struct traffic_selector_list_t traffic_selector_list_t;
#include "traffic_selector.h"
/**
* Collection of traffic selectors that can be narrowed to a new set of
* traffic selectors.
*/
struct traffic_selector_list_t {
/**
* Add a traffic selector to the collection.
*
* @param ts traffic_selector to add (adopted)
*/
void (*add)(traffic_selector_list_t *this, traffic_selector_t *ts);
/**
* Enumerate all traffic selectors in the collection.
*
* Similar to calling get() without \p hosts, but does not clone the traffic
* selectors and duplicates are not removed.
*
* @return enumerator over traffic_selector_t*
*/
enumerator_t *(*create_enumerator)(traffic_selector_list_t *this);
/**
* Get a list of traffic selectors contained in the collection.
*
* Some traffic selectors may be "dynamic", meaning they are narrowed down
* to a specific address (host-to-host or virtual-IP setups). Use the
* \p hosts parameter to narrow such traffic selectors to an address. If
* \p force_dynamic is also passed, even non-dynamic traffic selectors that
* match are replaced using the IPs in \p hosts (useful as initiator with
* transport mode).
*
* If \p hosts is not passed, the list of traffic selectors is returned as
* configured, except that exact duplicates are removed. However, note that
* "dynamic" traffic selectors are not considered duplicates.
*
* Returned list and its traffic selectors must be destroyed after use.
*
* Note that this method does not log anything. If logging is required, use
* select() without passing supplied traffic selectors.
*
* @param hosts addresses to use for narrowing "dynamic" TS, host_t
* @param force_dynamic TRUE to replace non-"dynamic" TS with \p hosts as
* initiator in transport mode
* @return list containing the traffic selectors
*/
linked_list_t *(*get)(traffic_selector_list_t *this, linked_list_t *hosts,
bool force_dynamic);
/**
* Select a list of traffic selectors contained in the collection.
*
* If a list with traffic selectors is supplied, these are used to narrow
* down the traffic selectors to the greatest common subset.
*
* Some traffic selectors may be "dynamic", meaning they are narrowed down
* to a specific address (host-to-host or virtual-IP setups). Use the
* \p hosts parameter to narrow such traffic selectors to an address. If
* \p force_dynamic is also passed, even non-dynamic traffic selectors that
* match are replaced using the IPs in \p hosts (useful as initiator with
* transport mode).
*
* Details about the selection of each individual traffic selector are
* logged.
*
* Returned list and its traffic selectors must be destroyed after use.
*
* @param supplied list with TS to select from, or NULL
* @param hosts addresses to use for narrowing "dynamic" TS', host_t
* @param force_dynamic TRUE to replace non-"dynamic" TS with \p hosts as
* initiator in transport mode
* @param narrowed[out] optional flag that indicates if any TS were narrowed
* @return list containing the traffic selectors
*/
linked_list_t *(*select)(traffic_selector_list_t *this,
linked_list_t *supplied, linked_list_t *hosts,
bool force_dynamic, bool *narrowed);
/**
* Compare two collections of traffic selectors.
*
* @param other collection to compare with this
* @return TRUE if equal, FALSE otherwise
*/
bool (*equals)(traffic_selector_list_t *this, traffic_selector_list_t *other);
/**
* Clone this collection of traffic selectors.
*
* @return cloned collection
*/
traffic_selector_list_t *(*clone)(traffic_selector_list_t *this);
/**
* Destroys this collection.
*/
void (*destroy)(traffic_selector_list_t *this);
};
/**
* Create an empty traffic selector collection.
*
* @return created object
*/
traffic_selector_list_t *traffic_selector_list_create();
/**
* Create a collection with traffic selectors from the given list (adopted).
*
* @param list list of traffic_selector_t (adopted)
* @return created object
*/
traffic_selector_list_t *traffic_selector_list_create_from_list(
linked_list_t *list);
/**
* Create a collection with traffic selectors from the given enumerator (objects
* are cloned, the enumerator is destroyed).
*
* @param enumerator enumerator over traffic_selector_t (cloned/destroyed)
* @return created object
*/
traffic_selector_list_t *traffic_selector_list_create_from_enumerator(
enumerator_t *enumerator);
#endif /** TRAFFIC_SELECTOR_LIST_H_ @}*/

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 Tobias Brunner
* Copyright (C) 2015-2025 Tobias Brunner
* Copyright (C) 2015 Martin Willi
*
* Copyright (C) secunet Security Networks AG
@ -18,6 +18,7 @@
#include "test_suite.h"
#include <selectors/traffic_selector.h>
#include <selectors/traffic_selector_list.h>
static void verify(const char *str, const char *alt, traffic_selector_t *ts)
@ -814,6 +815,199 @@ START_TEST(test_printf_hook_hash)
}
END_TEST
/**
* Create a linked list of either traffic selectors or hosts from a
* comma-separated list.
*/
static linked_list_t *create_list(char *str, bool hosts)
{
linked_list_t *list = linked_list_create();
enumerator_t *enumerator;
char *item;
void *obj;
enumerator = enumerator_create_token(str, " ", "");
while (enumerator->enumerate(enumerator, &item))
{
if (hosts)
{
obj = host_create_from_string(item, 0);
}
else if (streq(item, "dynamic"))
{
obj = traffic_selector_create_dynamic(0, 0, 65535);
}
else
{
obj = traffic_selector_create_from_cidr(item, 0, 0, 65535);
}
list->insert_last(list, obj);
}
enumerator->destroy(enumerator);
return list;
}
struct {
char *ts;
char *hosts;
char *get;
char *get_force;
} list_get_tests[] = {
{ "dynamic", NULL, "dynamic", "dynamic" },
{ "dynamic", "10.0.1.1", "10.0.1.1/32", "10.0.1.1/32" },
{ "dynamic", "10.0.1.1 192.168.0.1",
"10.0.1.1/32 192.168.0.1/32", "10.0.1.1/32 192.168.0.1/32" },
{ "0.0.0.0/0", "10.0.1.1", "0.0.0.0/0", "10.0.1.1/32" },
{ "0.0.0.0/0", "10.0.1.1 192.168.0.1",
"0.0.0.0/0", "10.0.1.1/32 192.168.0.1/32" },
{ "10.0.1.0/24", "10.0.1.1", "10.0.1.0/24", "10.0.1.1/32" },
{ "10.0.2.0/24", "10.0.1.1", "10.0.2.0/24", "" },
{ "10.0.2.0/24", "10.0.1.1 10.0.2.1", "10.0.2.0/24", "10.0.2.1/32" },
/* two dynamic TS are not treated as duplicates */
{ "dynamic dynamic", NULL, "dynamic dynamic", "dynamic dynamic" },
{ "dynamic dynamic", "10.0.1.1", "10.0.1.1/32", "10.0.1.1/32" },
{ "dynamic dynamic", "10.0.1.1 192.168.0.1",
"10.0.1.1/32 192.168.0.1/32", "10.0.1.1/32 192.168.0.1/32" },
{ "0.0.0.0/0 0.0.0.0/0", "10.0.1.1", "0.0.0.0/0", "10.0.1.1/32" },
{ "0.0.0.0/0 0.0.0.0/0", "10.0.1.1 192.168.0.1",
"0.0.0.0/0", "10.0.1.1/32 192.168.0.1/32" },
{ "10.0.1.0/24 10.0.1.0/24", NULL, "10.0.1.0/24", "10.0.1.0/24" },
{ "10.0.1.1/32 10.0.1.0/24", NULL, "10.0.1.0/24", "10.0.1.0/24" },
{ "10.0.1.0/24 10.0.1.1/32", NULL, "10.0.1.0/24", "10.0.1.0/24" },
{ "10.0.1.0/24 10.0.2.0/24", NULL,
"10.0.1.0/24 10.0.2.0/24", "10.0.1.0/24 10.0.2.0/24" },
{ "10.0.1.0/24 10.0.2.0/24", "10.0.1.1",
"10.0.1.0/24 10.0.2.0/24", "10.0.1.1/32" },
{ "10.0.1.0/24 10.0.2.0/24", "10.0.1.1 10.0.2.1",
"10.0.1.0/24 10.0.2.0/24", "10.0.1.1/32 10.0.2.1/32" },
};
START_TEST(test_list_get)
{
traffic_selector_list_t *ts;
linked_list_t *list, *hosts, *result;
list = create_list(list_get_tests[_i].ts, FALSE);
ts = traffic_selector_list_create_from_enumerator(list->create_enumerator(list));
hosts = list_get_tests[_i].hosts ? create_list(list_get_tests[_i].hosts, TRUE) : NULL;
result = ts->get(ts, hosts, FALSE);
verify_list(list_get_tests[_i].get, NULL, result);
result = ts->get(ts, hosts, TRUE);
verify_list(list_get_tests[_i].get_force, NULL, result);
DESTROY_OFFSET_IF(hosts, offsetof(host_t, destroy));
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
ts->destroy(ts);
}
END_TEST
struct {
char *ts;
char *hosts;
char *supplied;
char *select;
char *select_force;
bool narrowed;
} list_select_tests[] = {
{ "dynamic", NULL, NULL, "dynamic", "dynamic", FALSE },
{ "dynamic", NULL, "10.0.1.0/24", "", "", TRUE },
{ "dynamic", "10.0.1.1", "0.0.0.0/0", "10.0.1.1/32", "10.0.1.1/32", FALSE },
{ "dynamic", "10.0.1.1", "10.0.1.1/32", "10.0.1.1/32", "10.0.1.1/32", FALSE },
{ "dynamic", "10.0.1.1 192.168.0.1", "10.0.1.0/24",
"10.0.1.1/32", "10.0.1.1/32", TRUE },
{ "dynamic", "10.0.1.1 192.168.0.1", "10.0.1.0/24 192.168.0.0/24",
"10.0.1.1/32 192.168.0.1/32", "10.0.1.1/32 192.168.0.1/32", FALSE },
{ "0.0.0.0/0", NULL, "0.0.0.0/0", "0.0.0.0/0", "0.0.0.0/0", FALSE },
{ "0.0.0.0/0", "10.0.1.1", "0.0.0.0/0", "0.0.0.0/0", "10.0.1.1/32", FALSE },
{ "0.0.0.0/0", NULL, "10.0.1.0/24", "10.0.1.0/24", "10.0.1.0/24", TRUE },
{ "0.0.0.0/0", NULL, "10.0.1.0/24 10.0.2.0/24",
"10.0.1.0/24 10.0.2.0/24", "10.0.1.0/24 10.0.2.0/24", TRUE },
{ "0.0.0.0/0", NULL, "10.0.2.0/24 10.0.1.0/24",
"10.0.2.0/24 10.0.1.0/24", "10.0.2.0/24 10.0.1.0/24", TRUE },
{ "10.0.1.0/24", NULL, "0.0.0.0/0", "10.0.1.0/24", "10.0.1.0/24", FALSE },
{ "10.0.1.0/24", "10.0.1.1", "0.0.0.0/0", "10.0.1.0/24", "10.0.1.1/32", FALSE },
{ "10.0.1.0/24 10.0.2.0/24", NULL, "0.0.0.0/0",
"10.0.1.0/24 10.0.2.0/24", "10.0.1.0/24 10.0.2.0/24", FALSE },
{ "10.0.2.0/24 10.0.1.0/24", NULL, "0.0.0.0/0",
"10.0.2.0/24 10.0.1.0/24", "10.0.2.0/24 10.0.1.0/24", FALSE },
{ "10.0.1.0/24 10.0.2.0/24", NULL, "10.0.2.0/24 10.0.1.0/24",
"10.0.1.0/24 10.0.2.0/24", "10.0.1.0/24 10.0.2.0/24", FALSE },
{ "10.0.1.0/24 10.0.2.0/24", NULL, "10.0.1.1/32 10.0.1.0/24",
"10.0.1.0/24", "10.0.1.0/24", TRUE },
{ "10.0.1.0/24 10.0.2.0/24", NULL, "10.0.2.1/32 10.0.1.0/24",
"10.0.1.0/24 10.0.2.1/32", "10.0.1.0/24 10.0.2.1/32", TRUE },
};
START_TEST(test_list_select)
{
traffic_selector_list_t *ts;
linked_list_t *list, *hosts, *supplied, *result;
bool narrowed = FALSE;
list = create_list(list_select_tests[_i].ts, FALSE);
ts = traffic_selector_list_create_from_enumerator(list->create_enumerator(list));
hosts = list_select_tests[_i].hosts ? create_list(list_select_tests[_i].hosts, TRUE) : NULL;
supplied = list_select_tests[_i].supplied ? create_list(list_select_tests[_i].supplied, FALSE) : NULL;
result = ts->select(ts, supplied, hosts, FALSE, &narrowed);
verify_list(list_select_tests[_i].select, NULL, result);
ck_assert(narrowed == list_select_tests[_i].narrowed);
result = ts->select(ts, supplied, hosts, TRUE, &narrowed);
verify_list(list_select_tests[_i].select_force, NULL, result);
ck_assert(narrowed == list_select_tests[_i].narrowed);
DESTROY_OFFSET_IF(hosts, offsetof(host_t, destroy));
DESTROY_OFFSET_IF(supplied, offsetof(traffic_selector_t, destroy));
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
ts->destroy(ts);
}
END_TEST
START_TEST(test_list_equals)
{
traffic_selector_list_t *ts1, *ts2;
ts1 = traffic_selector_list_create();
ts2 = traffic_selector_list_create();
ck_assert(ts1->equals(ts1, ts2));
ts1->add(ts1, traffic_selector_create_from_cidr("10.0.1.0/24", 0, 0, 65535));
ck_assert(!ts1->equals(ts1, ts2));
ts2->add(ts2, traffic_selector_create_from_cidr("10.0.1.0/24", 0, 0, 65535));
ck_assert(ts1->equals(ts1, ts2));
ts1->destroy(ts1);
ts2->destroy(ts2);
}
END_TEST
START_TEST(test_list_clone)
{
traffic_selector_list_t *ts1, *ts2;
ts1 = traffic_selector_list_create();
ts2 = ts1->clone(ts1);
ck_assert(ts1->equals(ts1, ts2));
ts2->destroy(ts2);
ts1->add(ts1, traffic_selector_create_from_cidr("10.0.1.0/24", 0, 0, 65535));
ts2 = ts1->clone(ts1);
ck_assert(ts1->equals(ts1, ts2));
ts2->destroy(ts2);
ts1->add(ts1, traffic_selector_create_from_cidr("10.0.1.0/24", 0, 0, 65535));
ts2 = ts1->clone(ts1);
ck_assert(ts1->equals(ts1, ts2));
ts1->destroy(ts1);
ts2->destroy(ts2);
}
END_TEST
Suite *traffic_selector_suite_create()
{
Suite *s;
@ -890,5 +1084,12 @@ Suite *traffic_selector_suite_create()
tcase_add_test(tc, test_printf_hook_hash);
suite_add_tcase(s, tc);
tc = tcase_create("list");
tcase_add_loop_test(tc, test_list_get, 0, 0/*countof(list_get_tests)*/);
tcase_add_loop_test(tc, test_list_select, 0, countof(list_select_tests));
tcase_add_test(tc, test_list_equals);
tcase_add_test(tc, test_list_clone);
suite_add_tcase(s, tc);
return s;
}

View File

@ -86,7 +86,8 @@ CALLBACK(policies, int,
pol = hashtable_create(hashtable_hash_str, hashtable_equals_str, 1);
ret = vici_parse_cb(res, NULL, policy_values, policy_list, pol);
printf("%s, %s\n", name, pol->get(pol, "mode"));
printf("%s, reqid %s, %s\n", name, pol->get(pol, "reqid"),
pol->get(pol, "mode"));
print_label(" label: ", pol->get(pol, "label"));
printf(" local: %s\n", pol->get(pol, "local-ts"));
printf(" remote: %s\n", pol->get(pol, "remote-ts"));

View File

@ -0,0 +1,11 @@
A trap policy on gateway <b>sun</b> will trigger SAs to gateway <b>moon</b>
that connec the subnets behind the two gateways. Based on the received traffic
selector from the triggering packet, gateway <b>moon</b> narrows down the
traffic selectors to one of two options.
Subsequent pings issued by client <b>bob</b> behind gateway <b>sun</b> to
<b>alice</b> and <b>venus</b> located behind gateway <b>moon</b> trigger the
trap policy and lead to the automatic establishment of the subnet-to-subnet
tunnels.
<p/>
The updown script automatically inserts iptables-based firewall rules
that let pass the tunneled traffic.

View File

@ -0,0 +1,21 @@
sun::swanctl --list-pols --raw 2> /dev/null::net-net.*mode=TUNNEL local-ts=\[10.2.0.0/16] remote-ts=\[10.1.0.0/16]::YES
sun::cat /var/log/daemon.log::creating acquire job for policy 10.2.0.10/32\[icmp/8] === 10.1.0.10/32\[icmp/8]::YES
sun::cat /var/log/daemon.log::creating acquire job for policy 10.2.0.10/32\[icmp/8] === 10.1.0.20/32\[icmp/8]::YES
sun:: swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.2 local-port=500 local-id=sun.strongswan.org remote-host=192.168.0.1 remote-port=500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.2.0.0/16] remote-ts=\[10.1.0.0/28]::YES
sun:: swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.2 local-port=500 local-id=sun.strongswan.org remote-host=192.168.0.1 remote-port=500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128.*local-ts=\[10.2.0.0/16] remote-ts=\[10.1.0.16/28]::YES
moon::swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=500 local-id=moon.strongswan.org remote-host=192.168.0.2 remote-port=500 remote-id=sun.strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-1.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.1.0.0/28] remote-ts=\[10.2.0.0/16]::YES
moon::swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=500 local-id=moon.strongswan.org remote-host=192.168.0.2 remote-port=500 remote-id=sun.strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-2.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128.*local-ts=\[10.1.0.16/28] remote-ts=\[10.2.0.0/16]::YES
bob::ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_.eq=1::YES
bob::ping -c 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_.eq=1::YES
sun::swanctl --rekey --child net-net
sun::sleep 2
bob::ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_.eq=1::YES
bob::ping -c 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_.eq=1::YES
sun::swanctl --rekey --reauth --ike gw-gw
sun::sleep 1
sun::swanctl --list-sas --ike-id 2 --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.2 local-port=500 local-id=sun.strongswan.org remote-host=192.168.0.1 remote-port=500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[10.2.0.0/16] remote-ts=\[10.1.0.0/28]::YES
sun::swanctl --list-sas --ike-id 2 --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.2 local-port=500 local-id=sun.strongswan.org remote-host=192.168.0.1 remote-port=500 remote-id=moon.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*state=INSTALLED mode=TUNNEL.*ESP.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128.*local-ts=\[10.2.0.0/16] remote-ts=\[10.1.0.16/28]::YES
bob::ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_.eq=1::YES
bob::ping -c 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_.eq=1::YES
moon::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES
moon::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES

View File

@ -0,0 +1,14 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce openssl pem pkcs1 revocation curl kernel-netlink socket-default updown vici
syslog {
daemon {
cfg = 2
}
}
}

View File

@ -0,0 +1,34 @@
connections {
gw-gw {
local_addrs = 192.168.0.1
remote_addrs = 192.168.0.2
local {
auth = pubkey
certs = moonCert.pem
id = moon.strongswan.org
}
remote {
auth = pubkey
id = sun.strongswan.org
}
children {
net-1 {
local_ts = 10.1.0.0/28
remote_ts = 10.2.0.0/16
start_action = none
updown = /usr/local/libexec/ipsec/_updown iptables
esp_proposals = aes128gcm128-x25519
}
net-2 : connections.gw-gw.children.net-1 {
local_ts = 10.1.0.16/28
esp_proposals = aes128-sha256-x25519
}
}
version = 2
mobike = no
proposals = aes128-sha256-x25519
}
}

View File

@ -0,0 +1,16 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce openssl pem pkcs1 revocation kernel-netlink socket-default updown vici
# delete rekeyed CHILD_SAs quickly so we can reauthenticate the IKE_SA
delete_rekeyed_delay = 1
syslog {
daemon {
cfg = 2
}
}
}

View File

@ -0,0 +1,30 @@
connections {
gw-gw {
local_addrs = 192.168.0.2
remote_addrs = 192.168.0.1
local {
auth = pubkey
certs = sunCert.pem
id = sun.strongswan.org
}
remote {
auth = pubkey
id = moon.strongswan.org
}
children {
net-net {
local_ts = 10.2.0.0/16
remote_ts = 10.1.0.0/16
start_action = trap
updown = /usr/local/libexec/ipsec/_updown iptables
esp_proposals = aes128gcm128-x25519,aes128-sha256-x25519
}
}
version = 2
mobike = no
proposals = aes128-sha256-x25519
}
}

View File

@ -0,0 +1,5 @@
sun::swanctl --terminate --ike gw-gw 2> /dev/null
sun::systemctl stop strongswan
moon::systemctl stop strongswan
sun::iptables-restore < /etc/iptables.flush
moon::iptables-restore < /etc/iptables.flush

View File

@ -0,0 +1,9 @@
sun::iptables-restore < /etc/iptables.rules
moon::iptables-restore < /etc/iptables.rules
sun::systemctl start strongswan
moon::systemctl start strongswan
sun::expect-connection gw-gw
moon::expect-connection gw-gw
bob::ping -c 3 -W 1 -i 0.2 PH_IP_ALICE
bob::sleep 0.5
bob::ping -c 3 -W 1 -i 0.2 PH_IP_VENUS

View File

@ -0,0 +1,25 @@
#!/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 venus moon winnetou sun bob"
# Corresponding block diagram
#
DIAGRAM="a-v-m-w-s-b.png"
# Guest instances on which tcpdump is to be started
#
TCPDUMPHOSTS="moon"
# Guest instances on which IPsec is started
# Used for IPsec logging purposes
#
IPSECHOSTS="moon sun"
# charon controlled by swanctl
#
SWANCTL=1