refactored and cleaned up child_sa interface

replaced add/update calls by a install() call
	allocating SPIs always externally
	support installation of non-allocated CHILD_SAs
	some other cleanups
This commit is contained in:
Martin Willi 2008-11-19 15:31:27 +00:00
parent 9dd1229407
commit 3aaf7908d1
6 changed files with 396 additions and 450 deletions

View File

@ -110,7 +110,7 @@ static void execute(private_migrate_job_t *this)
host->set_port(host, IKEV2_UDP_PORT);
ike_sa->set_other_host(ike_sa, host);
if (child_sa->update_hosts(child_sa, this->local, this->remote,
if (child_sa->update(child_sa, this->local, this->remote,
ike_sa->get_virtual_ip(ike_sa, TRUE),
ike_sa->has_condition(ike_sa, COND_NAT_ANY)) == NOT_SUPPORTED)
{

View File

@ -89,16 +89,6 @@ struct private_child_sa_t {
*/
linked_list_t *other_ts;
/**
* Allocated SPI for a ESP proposal candidates
*/
u_int32_t alloc_esp_spi;
/**
* Allocated SPI for a AH proposal candidates
*/
u_int32_t alloc_ah_spi;
/**
* Protocol used to protect this SA, ESP|AH
*/
@ -134,11 +124,6 @@ struct private_child_sa_t {
*/
ipcomp_transform_t ipcomp;
/**
* TRUE if we allocated (or tried to allocate) a CPI
*/
bool cpi_allocated;
/**
* mode this SA uses, tunnel/transport
*/
@ -170,7 +155,32 @@ static u_int32_t get_reqid(private_child_sa_t *this)
{
return this->reqid;
}
/**
* Implements child_sa_t.get_config
*/
static child_cfg_t* get_config(private_child_sa_t *this)
{
return this->config;
}
/**
* Implements child_sa_t.set_state
*/
static void set_state(private_child_sa_t *this, child_sa_state_t state)
{
charon->bus->child_state_change(charon->bus, &this->public, state);
this->state = state;
}
/**
* Implements child_sa_t.get_state
*/
static child_sa_state_t get_state(private_child_sa_t *this)
{
return this->state;
}
/**
* Implements child_sa_t.get_spi
*/
@ -195,6 +205,14 @@ protocol_id_t get_protocol(private_child_sa_t *this)
return this->protocol;
}
/**
* Implementation of child_sa_t.set_protocol
*/
static void set_protocol(private_child_sa_t *this, protocol_id_t protocol)
{
this->protocol = protocol;
}
/**
* Implementation of child_sa_t.get_mode
*/
@ -203,6 +221,14 @@ static ipsec_mode_t get_mode(private_child_sa_t *this)
return this->mode;
}
/**
* Implementation of child_sa_t.set_mode
*/
static void set_mode(private_child_sa_t *this, ipsec_mode_t mode)
{
this->mode = mode;
}
/**
* Implementation of child_sa_t.has_encap
*/
@ -220,19 +246,35 @@ static ipcomp_transform_t get_ipcomp(private_child_sa_t *this)
}
/**
* Implements child_sa_t.get_state
* Implementation of child_sa_t.set_ipcomp.
*/
static child_sa_state_t get_state(private_child_sa_t *this)
static void set_ipcomp(private_child_sa_t *this, ipcomp_transform_t ipcomp)
{
return this->state;
this->ipcomp = ipcomp;
}
/**
* Implements child_sa_t.get_config
* Implementation of child_sa_t.get_proposal
*/
static child_cfg_t* get_config(private_child_sa_t *this)
static proposal_t* get_proposal(private_child_sa_t *this)
{
return this->config;
return this->proposal;
}
/**
* Implementation of child_sa_t.set_proposal
*/
static void set_proposal(private_child_sa_t *this, proposal_t *proposal)
{
this->proposal = proposal->clone(proposal);
}
/**
* Implementation of child_sa_t.get_traffic_selectors.
*/
static linked_list_t *get_traffic_selectors(private_child_sa_t *this, bool local)
{
return local ? this->my_ts : this->other_ts;
}
typedef struct policy_enumerator_t policy_enumerator_t;
@ -366,143 +408,100 @@ static u_int32_t get_lifetime(private_child_sa_t *this, bool hard)
}
/**
* Implements child_sa_t.set_state
* Implementation of child_sa_t.alloc_spi
*/
static void set_state(private_child_sa_t *this, child_sa_state_t state)
static u_int32_t alloc_spi(private_child_sa_t *this, protocol_id_t protocol)
{
charon->bus->child_state_change(charon->bus, &this->public, state);
this->state = state;
}
/**
* Allocate SPI for a single proposal
*/
static status_t alloc_proposal(private_child_sa_t *this, proposal_t *proposal)
{
protocol_id_t protocol = proposal->get_protocol(proposal);
if (protocol == PROTO_AH)
switch (protocol)
{
/* get a new spi for AH, if not already done */
if (this->alloc_ah_spi == 0)
{
if (charon->kernel_interface->get_spi(
charon->kernel_interface,
this->other_addr, this->my_addr,
PROTO_AH, this->reqid,
&this->alloc_ah_spi) != SUCCESS)
case PROTO_AH:
if (charon->kernel_interface->get_spi(charon->kernel_interface,
this->other_addr, this->my_addr, PROTO_AH,
this->reqid, &this->my_spi) == SUCCESS)
{
return FAILED;
return this->my_spi;
}
}
proposal->set_spi(proposal, this->alloc_ah_spi);
}
if (protocol == PROTO_ESP)
{
/* get a new spi for ESP, if not already done */
if (this->alloc_esp_spi == 0)
{
if (charon->kernel_interface->get_spi(
charon->kernel_interface,
this->other_addr, this->my_addr,
PROTO_ESP, this->reqid,
&this->alloc_esp_spi) != SUCCESS)
break;
case PROTO_ESP:
if (charon->kernel_interface->get_spi(charon->kernel_interface,
this->other_addr, this->my_addr, PROTO_ESP,
this->reqid, &this->my_spi) == SUCCESS)
{
return FAILED;
return this->my_spi;
}
}
proposal->set_spi(proposal, this->alloc_esp_spi);
break;
default:
break;
}
return SUCCESS;
return 0;
}
/**
* Implements child_sa_t.alloc
* Implementation of child_sa_t.alloc_cpi
*/
static status_t alloc(private_child_sa_t *this, linked_list_t *proposals)
static u_int16_t alloc_cpi(private_child_sa_t *this)
{
iterator_t *iterator;
proposal_t *proposal;
/* iterator through proposals to update spis */
iterator = proposals->create_iterator(proposals, TRUE);
while(iterator->iterate(iterator, (void**)&proposal))
if (charon->kernel_interface->get_cpi(charon->kernel_interface,
this->other_addr, this->my_addr, this->reqid,
&this->my_cpi) == SUCCESS)
{
if (alloc_proposal(this, proposal) != SUCCESS)
{
iterator->destroy(iterator);
return FAILED;
}
return this->my_cpi;
}
iterator->destroy(iterator);
return SUCCESS;
return 0;
}
/**
* Install an SA for one direction
* Implementation of child_sa_t.install
*/
static status_t install(private_child_sa_t *this, proposal_t *proposal,
ipsec_mode_t mode, chunk_t integ, chunk_t encr, bool in)
static status_t install(private_child_sa_t *this, chunk_t encr, chunk_t integ,
u_int32_t spi, u_int16_t cpi, bool inbound)
{
u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size;
u_int32_t spi, soft, hard, now;
u_int32_t soft, hard, now;
host_t *src, *dst;
status_t status;
bool update = FALSE;
/* now we have to decide which spi to use. Use self allocated, if "in",
* or the one in the proposal, if not "in" (others). Additionally,
* source and dest host switch depending on the role */
if (in)
if (inbound)
{
/* if we have allocated SPIs for AH and ESP, we must delete the unused
* one. */
if (this->protocol == PROTO_ESP)
{
this->my_spi = this->alloc_esp_spi;
if (this->alloc_ah_spi)
{
charon->kernel_interface->del_sa(charon->kernel_interface,
this->my_addr, this->alloc_ah_spi, 0, PROTO_AH);
}
}
else
{
this->my_spi = this->alloc_ah_spi;
if (this->alloc_esp_spi)
{
charon->kernel_interface->del_sa(charon->kernel_interface,
this->my_addr, this->alloc_esp_spi, 0, PROTO_ESP);
}
}
spi = this->my_spi;
dst = this->my_addr;
src = this->other_addr;
if (this->my_spi == spi)
{ /* alloc_spi has been called, do an SA update */
update = TRUE;
}
this->my_spi = spi;
this->my_cpi = cpi;
}
else
{
this->other_spi = proposal->get_spi(proposal);
spi = this->other_spi;
src = this->my_addr;
dst = this->other_addr;
this->other_spi = spi;
this->other_cpi = cpi;
}
DBG2(DBG_CHD, "adding %s %N SA", in ? "inbound" : "outbound",
DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound",
protocol_id_names, this->protocol);
/* send SA down to the kernel */
DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst);
proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_alg, &size);
proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_alg, &size);
this->proposal->get_algorithm(this->proposal, ENCRYPTION_ALGORITHM,
&enc_alg, &size);
this->proposal->get_algorithm(this->proposal, INTEGRITY_ALGORITHM,
&int_alg, &size);
soft = this->config->get_lifetime(this->config, TRUE);
hard = this->config->get_lifetime(this->config, FALSE);
status = charon->kernel_interface->add_sa(charon->kernel_interface,
src, dst, spi, this->protocol, this->reqid,
in ? soft : 0, hard, enc_alg, encr, int_alg, integ,
mode, this->ipcomp, in ? this->my_cpi : this->other_cpi,
this->encap, in);
inbound ? soft : 0, hard, enc_alg, encr, int_alg, integ,
this->mode, this->ipcomp, cpi, this->encap, update);
now = time(NULL);
this->rekey_time = now + soft;
@ -510,84 +509,17 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal,
return status;
}
/**
* Implementation of child_sa_t.add
*/
static status_t add(private_child_sa_t *this,
proposal_t *proposal, ipsec_mode_t mode,
chunk_t integ_in, chunk_t integ_out,
chunk_t encr_in, chunk_t encr_out)
{
this->proposal = proposal->clone(proposal);
this->protocol = proposal->get_protocol(proposal);
/* get SPIs for inbound SAs, write to proposal */
if (alloc_proposal(this, proposal) != SUCCESS)
{
return FAILED;
}
/* install inbound SAs using allocated SPI */
if (install(this, proposal, mode, integ_in, encr_in, TRUE) != SUCCESS)
{
return FAILED;
}
/* install outbound SAs using received SPI*/
if (install(this, this->proposal, mode, integ_out, encr_out, FALSE) != SUCCESS)
{
return FAILED;
}
return SUCCESS;
}
/**
* Implementation of child_sa_t.update
*/
static status_t update(private_child_sa_t *this,
proposal_t *proposal, ipsec_mode_t mode,
chunk_t integ_in, chunk_t integ_out,
chunk_t encr_in, chunk_t encr_out)
{
this->proposal = proposal->clone(proposal);
this->protocol = proposal->get_protocol(proposal);
/* install outbound SAs */
if (install(this, proposal, mode, integ_out, encr_out, FALSE) != SUCCESS)
{
return FAILED;
}
/* install inbound SAs */
if (install(this, proposal, mode, integ_in, encr_in, TRUE) != SUCCESS)
{
return FAILED;
}
return SUCCESS;
}
/**
* Implementation of child_sa_t.get_proposal
*/
static proposal_t* get_proposal(private_child_sa_t *this)
{
return this->proposal;
}
/**
* Implementation of child_sa_t.add_policies
*/
static status_t add_policies(private_child_sa_t *this,
linked_list_t *my_ts_list, linked_list_t *other_ts_list,
ipsec_mode_t mode, protocol_id_t proto)
linked_list_t *my_ts_list, linked_list_t *other_ts_list)
{
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
status_t status = SUCCESS;
bool routed = (this->state == CHILD_CREATED);
if (this->protocol == PROTO_NONE)
{ /* update if not set yet */
this->protocol = proto;
}
/* apply traffic selectors */
enumerator = my_ts_list->create_enumerator(my_ts_list);
while (enumerator->enumerate(enumerator, &my_ts))
@ -611,19 +543,19 @@ static status_t add_policies(private_child_sa_t *this,
/* install 3 policies: out, in and forward */
status |= charon->kernel_interface->add_policy(charon->kernel_interface,
this->my_addr, this->other_addr, my_ts, other_ts, POLICY_OUT,
this->other_spi, this->protocol, this->reqid, mode, this->ipcomp,
this->other_cpi, routed);
this->other_spi, this->protocol, this->reqid, this->mode,
this->ipcomp, this->other_cpi, routed);
status |= charon->kernel_interface->add_policy(charon->kernel_interface,
this->other_addr, this->my_addr, other_ts, my_ts, POLICY_IN,
this->my_spi, this->protocol, this->reqid, mode, this->ipcomp,
this->my_cpi, routed);
if (mode != MODE_TRANSPORT)
this->my_spi, this->protocol, this->reqid, this->mode,
this->ipcomp, this->my_cpi, routed);
if (this->mode != MODE_TRANSPORT)
{
status |= charon->kernel_interface->add_policy(charon->kernel_interface,
this->other_addr, this->my_addr, other_ts, my_ts, POLICY_FWD,
this->my_spi, this->protocol, this->reqid, mode, this->ipcomp,
this->my_cpi, routed);
this->my_spi, this->protocol, this->reqid, this->mode,
this->ipcomp, this->my_cpi, routed);
}
if (status != SUCCESS)
@ -634,32 +566,18 @@ static status_t add_policies(private_child_sa_t *this,
enumerator->destroy(enumerator);
}
if (status == SUCCESS)
{
/* switch to routed state if no SAD entry set up */
if (this->state == CHILD_CREATED)
{
set_state(this, CHILD_ROUTED);
}
/* needed to update hosts */
this->mode = mode;
if (status == SUCCESS && this->state == CHILD_CREATED)
{ /* switch to routed state if no SAD entry set up */
set_state(this, CHILD_ROUTED);
}
return status;
}
/**
* Implementation of child_sa_t.get_traffic_selectors.
* Implementation of child_sa_t.update.
*/
static linked_list_t *get_traffic_selectors(private_child_sa_t *this, bool local)
{
return local ? this->my_ts : this->other_ts;
}
/**
* Implementation of child_sa_t.update_hosts.
*/
static status_t update_hosts(private_child_sa_t *this,
host_t *me, host_t *other, host_t *vip, bool encap)
static status_t update(private_child_sa_t *this, host_t *me, host_t *other,
host_t *vip, bool encap)
{
child_sa_state_t old;
bool transport_proxy_mode;
@ -791,30 +709,6 @@ static status_t update_hosts(private_child_sa_t *this,
return SUCCESS;
}
/**
* Implementation of child_sa_t.activate_ipcomp.
*/
static void activate_ipcomp(private_child_sa_t *this, ipcomp_transform_t ipcomp,
u_int16_t other_cpi)
{
this->ipcomp = ipcomp;
this->other_cpi = other_cpi;
}
/**
* Implementation of child_sa_t.allocate_cpi.
*/
static u_int16_t allocate_cpi(private_child_sa_t *this)
{
if (!this->cpi_allocated)
{
charon->kernel_interface->get_cpi(charon->kernel_interface,
this->other_addr, this->my_addr, this->reqid, &this->my_cpi);
this->cpi_allocated = TRUE;
}
return this->my_cpi;
}
/**
* Implementation of child_sa_t.destroy.
*/
@ -833,16 +727,6 @@ static void destroy(private_child_sa_t *this)
this->my_addr, this->my_spi, this->protocol,
this->my_cpi);
}
if (this->alloc_esp_spi && this->alloc_esp_spi != this->my_spi)
{
charon->kernel_interface->del_sa(charon->kernel_interface,
this->my_addr, this->alloc_esp_spi, PROTO_ESP, 0);
}
if (this->alloc_ah_spi && this->alloc_ah_spi != this->my_spi)
{
charon->kernel_interface->del_sa(charon->kernel_interface,
this->my_addr, this->alloc_ah_spi, PROTO_AH, 0);
}
if (this->other_spi)
{
charon->kernel_interface->del_sa(charon->kernel_interface,
@ -890,40 +774,39 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
/* public functions */
this->public.get_name = (char*(*)(child_sa_t*))get_name;
this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid;
this->public.get_config = (child_cfg_t*(*)(child_sa_t*))get_config;
this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state;
this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state;
this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi;
this->public.get_cpi = (u_int16_t(*)(child_sa_t*, bool))get_cpi;
this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol;
this->public.set_protocol = (void(*)(child_sa_t*, protocol_id_t protocol))set_protocol;
this->public.get_mode = (ipsec_mode_t(*)(child_sa_t*))get_mode;
this->public.get_ipcomp = (ipcomp_transform_t(*)(child_sa_t*))get_ipcomp;
this->public.has_encap = (bool(*)(child_sa_t*))has_encap;
this->public.set_mode = (void(*)(child_sa_t*, ipsec_mode_t mode))set_mode;
this->public.get_proposal = (proposal_t*(*)(child_sa_t*))get_proposal;
this->public.set_proposal = (void(*)(child_sa_t*, proposal_t *proposal))set_proposal;
this->public.get_lifetime = (u_int32_t(*)(child_sa_t*, bool))get_lifetime;
this->public.get_usetime = (u_int32_t(*)(child_sa_t*, bool))get_usetime;
this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc;
this->public.add = (status_t(*)(child_sa_t*,proposal_t*,ipsec_mode_t,chunk_t,chunk_t,chunk_t,chunk_t))add;
this->public.update = (status_t(*)(child_sa_t*,proposal_t*,ipsec_mode_t,chunk_t,chunk_t,chunk_t,chunk_t))update;
this->public.get_proposal = (proposal_t*(*)(child_sa_t*))get_proposal;
this->public.update_hosts = (status_t (*)(child_sa_t*,host_t*,host_t*,host_t*,bool))update_hosts;
this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*,ipsec_mode_t,protocol_id_t))add_policies;
this->public.has_encap = (bool(*)(child_sa_t*))has_encap;
this->public.get_ipcomp = (ipcomp_transform_t(*)(child_sa_t*))get_ipcomp;
this->public.set_ipcomp = (void(*)(child_sa_t*,ipcomp_transform_t))set_ipcomp;
this->public.alloc_spi = (u_int32_t(*)(child_sa_t*, protocol_id_t protocol))alloc_spi;
this->public.alloc_cpi = (u_int16_t(*)(child_sa_t*))alloc_cpi;
this->public.install = (status_t(*)(child_sa_t*, chunk_t encr, chunk_t integ, u_int32_t spi, u_int16_t cpi, bool inbound))install;
this->public.update = (status_t (*)(child_sa_t*,host_t*,host_t*,host_t*,bool))update;
this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies;
this->public.get_traffic_selectors = (linked_list_t*(*)(child_sa_t*,bool))get_traffic_selectors;
this->public.create_policy_enumerator = (enumerator_t*(*)(child_sa_t*))create_policy_enumerator;
this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state;
this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state;
this->public.get_config = (child_cfg_t*(*)(child_sa_t*))get_config;
this->public.activate_ipcomp = (void(*)(child_sa_t*,ipcomp_transform_t,u_int16_t))activate_ipcomp;
this->public.allocate_cpi = (u_int16_t(*)(child_sa_t*))allocate_cpi;
this->public.destroy = (void(*)(child_sa_t*))destroy;
/* private data */
this->my_addr = me->clone(me);
this->other_addr = other->clone(other);
this->my_spi = 0;
this->my_cpi = 0;
this->other_spi = 0;
this->my_cpi = 0;
this->other_cpi = 0;
this->alloc_ah_spi = 0;
this->alloc_esp_spi = 0;
this->encap = encap;
this->cpi_allocated = FALSE;
this->ipcomp = IPCOMP_NONE;
this->state = CHILD_CREATED;
/* reuse old reqid if we are rekeying an existing CHILD_SA */
@ -935,7 +818,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
this->proposal = NULL;
this->config = config;
config->get_ref(config);
/* MIPv6 proxy transport mode sets SA endpoints to TS hosts */
if (config->get_mode(config) == MODE_TRANSPORT &&
config->use_proxy_mode(config))
@ -947,9 +830,9 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
enumerator_t *enumerator;
linked_list_t *my_ts_list, *other_ts_list;
traffic_selector_t *my_ts, *other_ts;
this->mode = MODE_TRANSPORT;
my_ts_list = config->get_traffic_selectors(config, TRUE, NULL, me);
enumerator = my_ts_list->create_enumerator(my_ts_list);
if (enumerator->enumerate(enumerator, &my_ts))
@ -970,7 +853,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
}
enumerator->destroy(enumerator);
my_ts_list->destroy_offset(my_ts_list, offsetof(traffic_selector_t, destroy));
other_ts_list = config->get_traffic_selectors(config, FALSE, NULL, other);
enumerator = other_ts_list->create_enumerator(other_ts_list);
if (enumerator->enumerate(enumerator, &other_ts))

View File

@ -1,6 +1,6 @@
/*
* Copyright (C) 2006-2008 Tobias Brunner
* Copyright (C) 2006-2007 Martin Willi
* Copyright (C) 2006-2008 Martin Willi
* Copyright (C) 2006 Daniel Roethlisberger
* Hochschule fuer Technik Rapperswil
*
@ -93,12 +93,13 @@ extern enum_name_t *child_sa_state_names;
* SAs and the policies have the same reqid.
*
* The procedure for child sa setup is as follows:
* - A gets SPIs for a proposal via child_sa_t.alloc
* - A send the updated proposal to B
* - A gets SPIs for a all protocols in its proposals via child_sa_t.alloc
* - A send the proposals with the allocated SPIs to B
* - B selects a suitable proposal
* - B calls child_sa_t.add to add and update the selected proposal
* - B sends the updated proposal to A
* - A calls child_sa_t.update to update the already allocated SPIs with the chosen proposal
* - B allocates an SPI for the selected protocol
* - B calls child_sa_t.install for both, the allocated and received SPI
* - B sends the proposal with the allocated SPI to A
* - A calls child_sa_t.install for both, the allocated and recevied SPI
*
* Once SAs are set up, policies can be added using add_policies.
*/
@ -121,6 +122,27 @@ struct child_sa_t {
*/
u_int32_t (*get_reqid)(child_sa_t *this);
/**
* Get the config used to set up this child sa.
*
* @return child_cfg
*/
child_cfg_t* (*get_config) (child_sa_t *this);
/**
* Get the state of the CHILD_SA.
*
* @return CHILD_SA state
*/
child_sa_state_t (*get_state) (child_sa_t *this);
/**
* Set the state of the CHILD_SA.
*
* @param state state to set on CHILD_SA
*/
void (*set_state) (child_sa_t *this, child_sa_state_t state);
/**
* Get the SPI of this CHILD_SA.
*
@ -152,6 +174,13 @@ struct child_sa_t {
*/
protocol_id_t (*get_protocol) (child_sa_t *this);
/**
* Set the negotiated protocol to use for this CHILD_SA.
*
* @param protocol AH | ESP
*/
void (*set_protocol)(child_sa_t *this, protocol_id_t protocol);
/**
* Get the IPsec mode of this CHILD_SA.
*
@ -159,6 +188,13 @@ struct child_sa_t {
*/
ipsec_mode_t (*get_mode)(child_sa_t *this);
/**
* Set the negotiated IPsec mode to use.
*
* @param mode TUNNEL | TRANPORT | BEET
*/
void (*set_mode)(child_sa_t *this, ipsec_mode_t mode);
/**
* Get the used IPComp algorithm.
*
@ -166,6 +202,27 @@ struct child_sa_t {
*/
ipcomp_transform_t (*get_ipcomp)(child_sa_t *this);
/**
* Set the IPComp algorithm to use.
*
* @param ipcomp the IPComp transform to use
*/
void (*set_ipcomp)(child_sa_t *this, ipcomp_transform_t ipcomp);
/**
* Get the selected proposal.
*
* @return selected proposal
*/
proposal_t* (*get_proposal)(child_sa_t *this);
/**
* Set the negotiated proposal.
*
* @param proposal selected proposal
*/
void (*set_proposal)(child_sa_t *this, proposal_t *proposal);
/**
* Check if this CHILD_SA uses UDP encapsulation.
*
@ -190,87 +247,7 @@ struct child_sa_t {
u_int32_t (*get_usetime)(child_sa_t *this, bool inbound);
/**
* Allocate SPIs for given proposals.
*
* Since the kernel manages SPIs for us, we need
* to allocate them. If a proposal contains more
* than one protocol, for each protocol an SPI is
* allocated. SPIs are stored internally and written
* back to the proposal.
*
* @param proposals list of proposals for which SPIs are allocated
*/
status_t (*alloc)(child_sa_t *this, linked_list_t* proposals);
/**
* Install the kernel SAs for a proposal, without previous SPI allocation.
*
* @param proposal proposal for which SPIs are allocated
* @param mode mode for the CHILD_SA
* @param integ_in integrity key for inbound traffic
* @param integ_out integrity key for outbound traffic
* @param encr_in encryption key for inbound traffic
* @param enc_out encryption key for outbound traffic
* @return SUCCESS or FAILED
*/
status_t (*add)(child_sa_t *this, proposal_t *proposal, ipsec_mode_t mode,
chunk_t integ_in, chunk_t integ_out,
chunk_t encr_in, chunk_t encr_out);
/**
* Install the kernel SAs for a proposal, after SPIs have been allocated.
*
* Updates an SA, for which SPIs are already allocated via alloc().
*
* @param proposal proposal for which SPIs are allocated
* @param mode mode for the CHILD_SA
* @param integ_in integrity key for inbound traffic
* @param integ_out integrity key for outbound traffic
* @param encr_in encryption key for inbound traffic
* @param enc_out encryption key for outbound traffic
* @return SUCCESS or FAILED
*/
status_t (*update)(child_sa_t *this, proposal_t *proposal, ipsec_mode_t mode,
chunk_t integ_in, chunk_t integ_out,
chunk_t encr_in, chunk_t encr_out);
/**
* Get the selected proposal passed to add()/update().
*
* @return selected proposal
*/
proposal_t* (*get_proposal)(child_sa_t *this);
/**
* Update the hosts in the kernel SAs and policies.
*
* The CHILD must be INSTALLED to do this update.
*
* @param me the new local host
* @param other the new remote host
* @param vip virtual IP, if any
* @param TRUE to use UDP encapsulation for NAT traversal
* @return SUCCESS or FAILED
*/
status_t (*update_hosts)(child_sa_t *this, host_t *me, host_t *other,
host_t *vip, bool encap);
/**
* Install the policies using some traffic selectors.
*
* Supplied lists of traffic_selector_t's specify the policies
* to use for this child sa.
*
* @param my_ts traffic selectors for local site
* @param other_ts traffic selectors for remote site
* @param mode mode for the SA: tunnel/transport
* @param proto protocol for policy, ESP/AH
* @return SUCCESS or FAILED
*/
status_t (*add_policies)(child_sa_t *this, linked_list_t *my_ts_list,
linked_list_t *other_ts_list, ipsec_mode_t mode,
protocol_id_t proto);
/**
* Get the traffic selectors of added policies of local host.
* Get the traffic selectors list added for one side.
*
* @param local TRUE for own traffic selectors, FALSE for remote
* @return list of traffic selectors
@ -285,40 +262,56 @@ struct child_sa_t {
enumerator_t* (*create_policy_enumerator)(child_sa_t *this);
/**
* Get the state of the CHILD_SA.
*/
child_sa_state_t (*get_state) (child_sa_t *this);
/**
* Set the state of the CHILD_SA.
* Allocate an SPI to include in a proposal.
*
* @param state state to set on CHILD_SA
*/
void (*set_state) (child_sa_t *this, child_sa_state_t state);
* @param protocol protocol to allocate SPI for (ESP|AH)
* @param spi SPI output pointer
* @return SPI, 0 on failure
*/
u_int32_t (*alloc_spi)(child_sa_t *this, protocol_id_t protocol);
/**
* Get the config used to set up this child sa.
* Allocate a CPI to use for IPComp.
*
* @return child_cfg
* @return CPI, 0 on failure
*/
child_cfg_t* (*get_config) (child_sa_t *this);
u_int16_t (*alloc_cpi)(child_sa_t *this);
/**
* Activate IPComp by setting the transform ID and CPI values.
*
* @param ipcomp the IPComp transform to use
* @param other_cpi other Compression Parameter Index
* Install an IPsec SA for one direction.
*
* @param encr encryption key, if any
* @param integ integrity key
* @param spi SPI to use, allocated for inbound
* @param cpi CPI to use, allocated for outbound
* @param inbound TRUE to install an inbound SA, FALSE for outbound
* @return SUCCESS or FAILED
*/
void (*activate_ipcomp) (child_sa_t *this, ipcomp_transform_t ipcomp,
u_int16_t other_cpi);
status_t (*install)(child_sa_t *this, chunk_t encr, chunk_t integ,
u_int32_t spi, u_int16_t cpi, bool inbound);
/**
* Returns the Compression Parameter Index (CPI) allocated from the kernel.
*
* @return allocated CPI
* Install the policies using some traffic selectors.
*
* Supplied lists of traffic_selector_t's specify the policies
* to use for this child sa.
*
* @param my_ts traffic selectors for local site
* @param other_ts traffic selectors for remote site
* @return SUCCESS or FAILED
*/
status_t (*add_policies)(child_sa_t *this, linked_list_t *my_ts_list,
linked_list_t *other_ts_list);
/**
* Update hosts and ecapulation mode in the kernel SAs and policies.
*
* @param me the new local host
* @param other the new remote host
* @param vip virtual IP, if any
* @param TRUE to use UDP encapsulation for NAT traversal
* @return SUCCESS or FAILED
*/
u_int16_t (*allocate_cpi) (child_sa_t *this);
status_t (*update)(child_sa_t *this, host_t *me, host_t *other,
host_t *vip, bool encap);
/**
* Destroys a child_sa.
*/

View File

@ -874,7 +874,7 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other)
iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
while (iterator->iterate(iterator, (void**)&child_sa))
{
if (child_sa->update_hosts(child_sa, this->my_host,
if (child_sa->update(child_sa, this->my_host,
this->other_host, this->my_virtual_ip,
has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED)
{
@ -1288,8 +1288,8 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg)
my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, me);
other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, other);
status = child_sa->add_policies(child_sa, my_ts, other_ts,
child_cfg->get_mode(child_cfg), PROTO_NONE);
child_sa->set_mode(child_sa, child_cfg->get_mode(child_cfg));
status = child_sa->add_policies(child_sa, my_ts, other_ts);
my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));

View File

@ -117,7 +117,22 @@ struct private_child_create_t {
ipcomp_transform_t ipcomp_received;
/**
* Other Compression Parameter Index (CPI)
* Own allocated SPI
*/
u_int32_t my_spi;
/**
* SPI received in proposal
*/
u_int32_t other_spi;
/**
* Own allocated Compression Parameter Index (CPI)
*/
u_int16_t my_cpi;
/**
* Other Compression Parameter Index (CPI), received via IPCOMP_SUPPORTED
*/
u_int16_t other_cpi;
@ -188,6 +203,36 @@ static bool ts_list_is_host(linked_list_t *list, host_t *host)
return is_host;
}
/**
* Allocate SPIs and update proposals
*/
static bool allocate_spi(private_child_create_t *this)
{
enumerator_t *enumerator;
proposal_t *proposal;
/* TODO: allocate additional SPI for AH if we have such proposals */
this->my_spi = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
if (this->my_spi)
{
if (this->initiator)
{
enumerator = this->proposals->create_enumerator(this->proposals);
while (enumerator->enumerate(enumerator, &proposal))
{
proposal->set_spi(proposal, this->my_spi);
}
enumerator->destroy(enumerator);
}
else
{
this->proposal->set_spi(this->proposal, this->my_spi);
}
return TRUE;
}
return FALSE;
}
/**
* Install a CHILD_SA for usage, return value:
* - FAILED: no acceptable proposal
@ -216,7 +261,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
other = this->ike_sa->get_other_host(this->ike_sa);
my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
this->proposal = this->config->select_proposal(this->config, this->proposals,
no_dh);
if (this->proposal == NULL)
@ -224,6 +269,14 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
DBG1(DBG_IKE, "no acceptable proposal found");
return FAILED;
}
this->other_spi = this->proposal->get_spi(this->proposal);
if (!this->initiator && !allocate_spi(this))
{ /* responder has no SPI allocated yet */
DBG1(DBG_IKE, "allocating SPI failed");
return FAILED;
}
this->child_sa->set_proposal(this->child_sa, this->proposal);
if (!this->proposal->has_dh_group(this->proposal, this->dh_group))
{
@ -328,26 +381,33 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
}
this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
this->child_sa->set_ipcomp(this->child_sa, this->ipcomp);
this->child_sa->set_mode(this->child_sa, this->mode);
this->child_sa->set_protocol(this->child_sa,
this->proposal->get_protocol(this->proposal));
if (this->ipcomp != IPCOMP_NONE)
if (this->my_cpi == 0 || this->other_cpi == 0 || this->ipcomp == IPCOMP_NONE)
{
this->child_sa->activate_ipcomp(this->child_sa, this->ipcomp,
this->other_cpi);
this->my_cpi = this->other_cpi = 0;
this->ipcomp = IPCOMP_NONE;
}
status = FAILED;
if (this->keymat->derive_child_keys(this->keymat, this->proposal,
this->dh, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r))
{
if (this->initiator)
{
status = this->child_sa->update(this->child_sa, this->proposal,
this->mode, integ_r, integ_i, encr_r, encr_i);
status = this->child_sa->install(this->child_sa, encr_r, integ_r,
this->my_spi, this->my_cpi, TRUE);
status = this->child_sa->install(this->child_sa, encr_i, integ_i,
this->other_spi, this->other_cpi, FALSE);
}
else
{
status = this->child_sa->add(this->child_sa, this->proposal,
this->mode, integ_i, integ_r, encr_i, encr_r);
status = this->child_sa->install(this->child_sa, encr_i, integ_i,
this->my_spi, this->my_cpi, TRUE);
status = this->child_sa->install(this->child_sa, encr_r, integ_r,
this->other_spi, this->other_cpi, FALSE);
}
}
chunk_clear(&integ_i);
@ -361,8 +421,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
return FAILED;
}
status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts,
this->mode, this->proposal->get_protocol(this->proposal));
status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts);
if (status != SUCCESS)
{
DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
@ -436,33 +495,71 @@ static void build_payloads(private_child_create_t *this, message_t *message)
}
/**
* Adds an IPCOMP_SUPPORTED notify to the message, if possible
* Adds an IPCOMP_SUPPORTED notify to the message, allocating a CPI
*/
static void build_ipcomp_supported_notify(private_child_create_t *this,
message_t *message)
static void add_ipcomp_notify(private_child_create_t *this,
message_t *message, u_int8_t ipcomp)
{
u_int16_t cpi;
u_int8_t tid;
if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY))
{
DBG1(DBG_IKE, "IPComp is not supported if either peer is natted, "
"IPComp disabled");
this->ipcomp = IPCOMP_NONE;
return;
}
cpi = this->child_sa->allocate_cpi(this->child_sa);
tid = this->ipcomp;
if (cpi)
this->my_cpi = this->child_sa->alloc_cpi(this->child_sa);
if (this->my_cpi)
{
message->add_notify(message, FALSE, IPCOMP_SUPPORTED,
chunk_cata("cc", chunk_from_thing(cpi), chunk_from_thing(tid)));
this->ipcomp = ipcomp;
message->add_notify(message, FALSE, IPCOMP_SUPPORTED,
chunk_cata("cc", chunk_from_thing(this->my_cpi),
chunk_from_thing(ipcomp)));
}
else
{
DBG1(DBG_IKE, "unable to allocate a CPI from kernel, IPComp disabled");
this->ipcomp = IPCOMP_NONE;
}
}
/**
* handle a received notify payload
*/
static void handle_notify(private_child_create_t *this, notify_payload_t *notify)
{
switch (notify->get_notify_type(notify))
{
case USE_TRANSPORT_MODE:
this->mode = MODE_TRANSPORT;
break;
case USE_BEET_MODE:
this->mode = MODE_BEET;
break;
case IPCOMP_SUPPORTED:
{
ipcomp_transform_t ipcomp;
u_int16_t cpi;
chunk_t data;
data = notify->get_notification_data(notify);
cpi = *(u_int16_t*)data.ptr;
ipcomp = (ipcomp_transform_t)(*(data.ptr + 2));
switch (ipcomp)
{
case IPCOMP_DEFLATE:
this->other_cpi = cpi;
this->ipcomp_received = ipcomp;
break;
case IPCOMP_LZS:
case IPCOMP_LZJH:
default:
DBG1(DBG_IKE, "received IPCOMP_SUPPORTED notify with a "
"transform ID we don't support %N",
ipcomp_transform_names, ipcomp);
break;
}
}
default:
break;
}
}
@ -476,7 +573,6 @@ static void process_payloads(private_child_create_t *this, message_t *message)
sa_payload_t *sa_payload;
ke_payload_t *ke_payload;
ts_payload_t *ts_payload;
notify_payload_t *notify_payload;
/* defaults to TUNNEL mode */
this->mode = MODE_TUNNEL;
@ -512,37 +608,7 @@ static void process_payloads(private_child_create_t *this, message_t *message)
this->tsr = ts_payload->get_traffic_selectors(ts_payload);
break;
case NOTIFY:
notify_payload = (notify_payload_t*)payload;
switch (notify_payload ->get_notify_type(notify_payload ))
{
case USE_TRANSPORT_MODE:
this->mode = MODE_TRANSPORT;
break;
case USE_BEET_MODE:
this->mode = MODE_BEET;
break;
case IPCOMP_SUPPORTED:
{
chunk_t data = notify_payload->get_notification_data(notify_payload);
u_int16_t cpi = *(u_int16_t*)data.ptr;
ipcomp_transform_t ipcomp = (ipcomp_transform_t)(*(data.ptr + 2));
switch(ipcomp)
{
case IPCOMP_DEFLATE:
this->other_cpi = cpi;
this->ipcomp_received = ipcomp;
break;
case IPCOMP_LZS:
case IPCOMP_LZJH:
default:
DBG1(DBG_IKE, "received IPCOMP_SUPPORTED notify with a transform"
" ID we don't support %N", ipcomp_transform_names, ipcomp);
break;
}
}
default:
break;
}
handle_notify(this, (notify_payload_t*)payload);
break;
default:
break;
@ -559,7 +625,7 @@ static status_t build_i(private_child_create_t *this, message_t *message)
host_t *me, *other, *vip;
bool propose_all = FALSE;
peer_cfg_t *peer_cfg;
switch (message->get_exchange_type(message))
{
case IKE_SA_INIT:
@ -641,7 +707,7 @@ static status_t build_i(private_child_create_t *this, message_t *message)
this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid,
this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY));
if (this->child_sa->alloc(this->child_sa, this->proposals) != SUCCESS)
if (!allocate_spi(this))
{
DBG1(DBG_IKE, "unable to allocate SPIs from kernel");
return FAILED;
@ -652,10 +718,10 @@ static status_t build_i(private_child_create_t *this, message_t *message)
this->dh = this->keymat->create_dh(this->keymat, this->dh_group);
}
if (this->config->use_ipcomp(this->config)) {
if (this->config->use_ipcomp(this->config))
{
/* IPCOMP_DEFLATE is the only transform we support at the moment */
this->ipcomp = IPCOMP_DEFLATE;
build_ipcomp_supported_notify(this, message);
add_ipcomp_notify(this, message, IPCOMP_DEFLATE);
}
build_payloads(this, message);
@ -821,16 +887,17 @@ static status_t build_r(private_child_create_t *this, message_t *message)
this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid,
this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY));
if (this->config->use_ipcomp(this->config) &&
this->ipcomp_received != IPCOMP_NONE)
if (this->ipcomp_received != IPCOMP_NONE)
{
this->ipcomp = this->ipcomp_received;
build_ipcomp_supported_notify(this, message);
}
else if (this->ipcomp_received != IPCOMP_NONE)
{
DBG1(DBG_IKE, "received %N notify but IPComp is disabled, ignoring",
notify_type_names, IPCOMP_SUPPORTED);
if (this->config->use_ipcomp(this->config))
{
add_ipcomp_notify(this, message, this->ipcomp_received);
}
else
{
DBG1(DBG_IKE, "received %N notify but IPComp is disabled, ignoring",
notify_type_names, IPCOMP_SUPPORTED);
}
}
switch (select_and_install(this, no_dh))
@ -1137,6 +1204,9 @@ child_create_t *child_create_create(ike_sa_t *ike_sa, child_cfg_t *config)
this->mode = MODE_TUNNEL;
this->ipcomp = IPCOMP_NONE;
this->ipcomp_received = IPCOMP_NONE;
this->my_spi = 0;
this->other_spi = 0;
this->my_cpi = 0;
this->other_cpi = 0;
this->reqid = 0;
this->established = FALSE;

View File

@ -251,7 +251,7 @@ static void update_children(private_ike_mobike_t *this)
iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
while (iterator->iterate(iterator, (void**)&child_sa))
{
if (child_sa->update_hosts(child_sa,
if (child_sa->update(child_sa,
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
this->ike_sa->get_virtual_ip(this->ike_sa, TRUE),