strongswan/src/libcharon/sa/child_sa.c

1232 lines
29 KiB
C
Raw Normal View History

/*
* Copyright (C) 2006-2011 Tobias Brunner
2008-10-14 08:52:13 +00:00
* Copyright (C) 2005-2008 Martin Willi
2008-05-08 16:19:11 +00:00
* Copyright (C) 2006 Daniel Roethlisberger
2006-07-07 08:49:06 +00:00
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* 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.
*/
#define _GNU_SOURCE
2006-04-26 12:28:02 +00:00
#include "child_sa.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <hydra.h>
2006-02-10 08:20:06 +00:00
#include <daemon.h>
2008-10-14 08:52:13 +00:00
ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING,
"CREATED",
"ROUTED",
"INSTALLING",
"INSTALLED",
"UPDATING",
"REKEYING",
"DELETING",
2008-10-14 08:52:13 +00:00
"DESTROYING",
);
typedef struct private_child_sa_t private_child_sa_t;
/**
* Private data of a child_sa_t object.
*/
struct private_child_sa_t {
/**
2005-12-07 08:13:22 +00:00
* Public interface of child_sa_t.
*/
child_sa_t public;
/**
* address of us
*/
host_t *my_addr;
/**
* address of remote
*/
host_t *other_addr;
/**
* our actually used SPI, 0 if unused
*/
u_int32_t my_spi;
2006-02-10 08:20:06 +00:00
/**
* others used SPI, 0 if unused
2006-02-10 08:20:06 +00:00
*/
u_int32_t other_spi;
/**
* our Compression Parameter Index (CPI) used, 0 if unused
*/
u_int16_t my_cpi;
/**
* others Compression Parameter Index (CPI) used, 0 if unused
*/
u_int16_t other_cpi;
/**
* List for local traffic selectors
*/
linked_list_t *my_ts;
/**
* List for remote traffic selectors
*/
linked_list_t *other_ts;
/**
* Protocol used to protect this SA, ESP|AH
*/
protocol_id_t protocol;
/**
* reqid used for this child_sa
*/
u_int32_t reqid;
2010-07-02 21:45:57 +00:00
/**
* inbound mark used for this child_sa
*/
mark_t mark_in;
/**
* outbound mark used for this child_sa
*/
mark_t mark_out;
/**
2008-10-24 08:02:35 +00:00
* absolute time when rekeying is scheduled
*/
2008-10-24 08:02:35 +00:00
time_t rekey_time;
/**
2008-10-24 08:02:35 +00:00
* absolute time when the SA expires
*/
2008-10-24 08:02:35 +00:00
time_t expire_time;
/**
* state of the CHILD_SA
*/
child_sa_state_t state;
2006-09-25 05:58:45 +00:00
/**
* TRUE if this CHILD_SA is used to install trap policies
*/
bool trap;
/**
* Specifies if UDP encapsulation is enabled (NAT traversal)
*/
bool encap;
2008-05-08 16:19:11 +00:00
/**
* Specifies the IPComp transform used (IPCOMP_NONE if disabled)
*/
ipcomp_transform_t ipcomp;
/**
* mode this SA uses, tunnel/transport
*/
ipsec_mode_t mode;
/**
* Action to enforce if peer closes the CHILD_SA
*/
action_t close_action;
/**
* Action to enforce if peer is considered dead
*/
action_t dpd_action;
/**
* selected proposal
*/
proposal_t *proposal;
2007-02-28 14:04:36 +00:00
/**
* config used to create this child
2007-02-28 14:04:36 +00:00
*/
child_cfg_t *config;
/**
* time of last use in seconds (inbound)
*/
u_int32_t my_usetime;
/**
* time of last use in seconds (outbound)
*/
u_int32_t other_usetime;
/**
* last number of inbound bytes
*/
u_int64_t my_usebytes;
/**
* last number of outbound bytes
*/
u_int64_t other_usebytes;
/**
* last number of inbound packets
*/
u_int64_t my_usepackets;
/**
* last number of outbound bytes
*/
u_int64_t other_usepackets;
};
/**
* convert an IKEv2 specific protocol identifier to the IP protocol identifier.
*/
static inline u_int8_t proto_ike2ip(protocol_id_t protocol)
{
switch (protocol)
{
case PROTO_ESP:
return IPPROTO_ESP;
case PROTO_AH:
return IPPROTO_AH;
default:
return protocol;
}
}
METHOD(child_sa_t, get_name, char*,
private_child_sa_t *this)
{
return this->config->get_name(this->config);
}
METHOD(child_sa_t, get_reqid, u_int32_t,
private_child_sa_t *this)
{
return this->reqid;
}
METHOD(child_sa_t, get_config, child_cfg_t*,
private_child_sa_t *this)
{
return this->config;
}
METHOD(child_sa_t, set_state, void,
private_child_sa_t *this, child_sa_state_t state)
{
charon->bus->child_state_change(charon->bus, &this->public, state);
this->state = state;
}
METHOD(child_sa_t, get_state, child_sa_state_t,
private_child_sa_t *this)
{
return this->state;
}
METHOD(child_sa_t, get_spi, u_int32_t,
private_child_sa_t *this, bool inbound)
{
return inbound ? this->my_spi : this->other_spi;
}
METHOD(child_sa_t, get_cpi, u_int16_t,
private_child_sa_t *this, bool inbound)
2008-07-22 12:03:58 +00:00
{
return inbound ? this->my_cpi : this->other_cpi;
2008-07-22 12:03:58 +00:00
}
METHOD(child_sa_t, get_protocol, protocol_id_t,
private_child_sa_t *this)
{
return this->protocol;
}
METHOD(child_sa_t, set_protocol, void,
private_child_sa_t *this, protocol_id_t protocol)
{
this->protocol = protocol;
}
METHOD(child_sa_t, get_mode, ipsec_mode_t,
private_child_sa_t *this)
{
return this->mode;
}
METHOD(child_sa_t, set_mode, void,
private_child_sa_t *this, ipsec_mode_t mode)
{
this->mode = mode;
}
METHOD(child_sa_t, has_encap, bool,
private_child_sa_t *this)
{
return this->encap;
}
METHOD(child_sa_t, get_ipcomp, ipcomp_transform_t,
private_child_sa_t *this)
{
return this->ipcomp;
}
METHOD(child_sa_t, set_ipcomp, void,
private_child_sa_t *this, ipcomp_transform_t ipcomp)
{
this->ipcomp = ipcomp;
}
METHOD(child_sa_t, set_close_action, void,
private_child_sa_t *this, action_t action)
{
this->close_action = action;
}
METHOD(child_sa_t, get_close_action, action_t,
private_child_sa_t *this)
{
return this->close_action;
}
METHOD(child_sa_t, set_dpd_action, void,
private_child_sa_t *this, action_t action)
{
this->dpd_action = action;
}
METHOD(child_sa_t, get_dpd_action, action_t,
private_child_sa_t *this)
{
return this->dpd_action;
}
METHOD(child_sa_t, get_proposal, proposal_t*,
private_child_sa_t *this)
2007-02-28 14:04:36 +00:00
{
return this->proposal;
}
METHOD(child_sa_t, set_proposal, void,
private_child_sa_t *this, proposal_t *proposal)
{
this->proposal = proposal->clone(proposal);
}
METHOD(child_sa_t, get_traffic_selectors, linked_list_t*,
private_child_sa_t *this, bool local)
{
return local ? this->my_ts : this->other_ts;
2007-02-28 14:04:36 +00:00
}
typedef struct policy_enumerator_t policy_enumerator_t;
/**
* Private policy enumerator
*/
struct policy_enumerator_t {
/** implements enumerator_t */
enumerator_t public;
/** enumerator over own TS */
enumerator_t *mine;
/** enumerator over others TS */
enumerator_t *other;
/** list of others TS, to recreate enumerator */
linked_list_t *list;
/** currently enumerating TS for "me" side */
traffic_selector_t *ts;
};
METHOD(enumerator_t, policy_enumerate, bool,
policy_enumerator_t *this, traffic_selector_t **my_out,
traffic_selector_t **other_out)
{
traffic_selector_t *other_ts;
while (this->ts || this->mine->enumerate(this->mine, &this->ts))
{
if (!this->other->enumerate(this->other, &other_ts))
{ /* end of others list, restart with new of mine */
this->other->destroy(this->other);
this->other = this->list->create_enumerator(this->list);
this->ts = NULL;
continue;
}
if (this->ts->get_type(this->ts) != other_ts->get_type(other_ts))
{ /* family mismatch */
continue;
}
if (this->ts->get_protocol(this->ts) &&
other_ts->get_protocol(other_ts) &&
this->ts->get_protocol(this->ts) != other_ts->get_protocol(other_ts))
{ /* protocol mismatch */
continue;
}
*my_out = this->ts;
*other_out = other_ts;
return TRUE;
}
return FALSE;
}
METHOD(enumerator_t, policy_destroy, void,
policy_enumerator_t *this)
{
this->mine->destroy(this->mine);
this->other->destroy(this->other);
free(this);
}
METHOD(child_sa_t, create_policy_enumerator, enumerator_t*,
private_child_sa_t *this)
{
policy_enumerator_t *e;
INIT(e,
.public = {
.enumerate = (void*)_policy_enumerate,
.destroy = _policy_destroy,
},
.mine = this->my_ts->create_enumerator(this->my_ts),
.other = this->other_ts->create_enumerator(this->other_ts),
.list = this->other_ts,
.ts = NULL,
);
return &e->public;
}
/**
* update the cached usebytes
* returns SUCCESS if the usebytes have changed, FAILED if not or no SPIs
* are available, and NOT_SUPPORTED if the kernel interface does not support
* querying the usebytes.
*/
static status_t update_usebytes(private_child_sa_t *this, bool inbound)
{
status_t status = FAILED;
u_int64_t bytes, packets;
u_int32_t time;
if (inbound)
{
if (this->my_spi)
{
status = hydra->kernel_interface->query_sa(hydra->kernel_interface,
this->other_addr, this->my_addr, this->my_spi,
proto_ike2ip(this->protocol), this->mark_in,
&bytes, &packets, &time);
if (status == SUCCESS)
{
if (bytes > this->my_usebytes)
{
this->my_usebytes = bytes;
this->my_usepackets = packets;
if (time)
{
this->my_usetime = time;
}
return SUCCESS;
}
return FAILED;
}
}
}
else
{
if (this->other_spi)
{
status = hydra->kernel_interface->query_sa(hydra->kernel_interface,
this->my_addr, this->other_addr, this->other_spi,
proto_ike2ip(this->protocol), this->mark_out,
&bytes, &packets, &time);
if (status == SUCCESS)
{
if (bytes > this->other_usebytes)
{
this->other_usebytes = bytes;
this->other_usepackets = packets;
if (time)
{
this->other_usetime = time;
}
return SUCCESS;
}
return FAILED;
}
}
}
return status;
}
/**
* updates the cached usetime
*/
static bool update_usetime(private_child_sa_t *this, bool inbound)
{
enumerator_t *enumerator;
2008-10-24 08:02:35 +00:00
traffic_selector_t *my_ts, *other_ts;
u_int32_t last_use = 0;
enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
2008-10-24 08:02:35 +00:00
u_int32_t in, out, fwd;
if (inbound)
{
if (hydra->kernel_interface->query_policy(hydra->kernel_interface,
2010-07-02 21:45:57 +00:00
other_ts, my_ts, POLICY_IN, this->mark_in, &in) == SUCCESS)
2008-10-24 08:02:35 +00:00
{
last_use = max(last_use, in);
}
if (this->mode != MODE_TRANSPORT)
2008-10-24 08:02:35 +00:00
{
if (hydra->kernel_interface->query_policy(hydra->kernel_interface,
2010-07-02 21:45:57 +00:00
other_ts, my_ts, POLICY_FWD, this->mark_in, &fwd) == SUCCESS)
2008-11-11 06:37:37 +00:00
{
last_use = max(last_use, fwd);
}
2008-10-24 08:02:35 +00:00
}
}
2008-10-24 08:02:35 +00:00
else
{
if (hydra->kernel_interface->query_policy(hydra->kernel_interface,
2010-07-02 21:45:57 +00:00
my_ts, other_ts, POLICY_OUT, this->mark_out, &out) == SUCCESS)
2008-10-24 08:02:35 +00:00
{
last_use = max(last_use, out);
}
}
}
enumerator->destroy(enumerator);
if (last_use == 0)
{
return FALSE;
}
if (inbound)
{
this->my_usetime = last_use;
}
else
{
this->other_usetime = last_use;
}
return TRUE;
2008-10-24 08:02:35 +00:00
}
METHOD(child_sa_t, get_usestats, void,
private_child_sa_t *this, bool inbound,
time_t *time, u_int64_t *bytes, u_int64_t *packets)
2009-07-30 19:33:19 +00:00
{
if ((!bytes && !packets) || update_usebytes(this, inbound) != FAILED)
{
/* there was traffic since last update or the kernel interface
* does not support querying the number of usebytes.
*/
if (time)
{
if (!update_usetime(this, inbound) && !bytes && !packets)
{
/* if policy query did not yield a usetime, query SAs instead */
update_usebytes(this, inbound);
}
}
}
if (time)
{
*time = inbound ? this->my_usetime : this->other_usetime;
}
if (bytes)
{
*bytes = inbound ? this->my_usebytes : this->other_usebytes;
}
if (packets)
{
*packets = inbound ? this->my_usepackets : this->other_usepackets;
}
2009-07-30 19:33:19 +00:00
}
2012-03-21 15:54:24 +00:00
METHOD(child_sa_t, get_mark, mark_t,
private_child_sa_t *this, bool inbound)
{
if (inbound)
{
return this->mark_in;
}
return this->mark_out;
}
METHOD(child_sa_t, get_lifetime, time_t,
private_child_sa_t *this, bool hard)
2008-10-24 08:02:35 +00:00
{
return hard ? this->expire_time : this->rekey_time;
}
METHOD(child_sa_t, alloc_spi, u_int32_t,
private_child_sa_t *this, protocol_id_t protocol)
2006-06-22 06:36:28 +00:00
{
if (hydra->kernel_interface->get_spi(hydra->kernel_interface,
this->other_addr, this->my_addr,
proto_ike2ip(protocol), this->reqid,
&this->my_spi) == SUCCESS)
{
return this->my_spi;
2006-06-22 06:36:28 +00:00
}
return 0;
2006-06-22 06:36:28 +00:00
}
METHOD(child_sa_t, alloc_cpi, u_int16_t,
private_child_sa_t *this)
{
if (hydra->kernel_interface->get_cpi(hydra->kernel_interface,
this->other_addr, this->my_addr,
this->reqid, &this->my_cpi) == SUCCESS)
{
return this->my_cpi;
}
return 0;
}
METHOD(child_sa_t, install, status_t,
kernel-interface: add an exchange initiator parameter to add_sa() This new flag gives the kernel-interface a hint how it should priorize the use of newly installed SAs during rekeying. Consider the following rekey procedure in IKEv2: Initiator --- Responder I1 -------CREATE-------> R1 I2 <------CREATE-------- -------DELETE-------> R2 I3 <------DELETE-------- SAs are always handled as pairs, the following happens at the SA level: * Initiator starts the exchange at I1 * Responder installs new SA pair at R1 * Initiator installs new SA pair at I2 * Responder removes old SA pair at R2 * Initiator removes old SA pair at I3 This makes sure SAs get installed/removed overlapping during rekeying. However, to avoid any packet loss, it is crucial that the new outbound SA gets activated at the correct position: * as exchange initiator, in I2 * as exchange responder, in R2 This should guarantee that we don't use the new outbound SA before the peer could install its corresponding inbound SA. The new parameter allows the kernel backend to install the new SA with appropriate priorities, i.e. it should: * as exchange inititator, have the new outbound SA installed with higher priority than the old SA * as exchange responder, have the new outbound SA installed with lower priority than the old SA While we could split up the SA installation at the responder, this approach has another advantage: it allows the kernel backend to switch SAs based on other criteria, for example when receiving traffic on the new inbound SA.
2013-05-08 08:31:06 +00:00
private_child_sa_t *this, chunk_t encr, chunk_t integ, u_int32_t spi,
u_int16_t cpi, bool initiator, bool inbound, bool tfcv3,
linked_list_t *my_ts, linked_list_t *other_ts)
{
u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size;
u_int16_t esn = NO_EXT_SEQ_NUMBERS;
traffic_selector_t *src_ts = NULL, *dst_ts = NULL;
time_t now;
lifetime_cfg_t *lifetime;
u_int32_t tfc = 0;
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 (inbound)
2006-02-10 08:20:06 +00:00
{
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;
2006-02-10 08:20:06 +00:00
}
else
{
src = this->my_addr;
dst = this->other_addr;
this->other_spi = spi;
this->other_cpi = cpi;
if (tfcv3)
{
tfc = this->config->get_tfc(this->config);
}
}
DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound",
protocol_id_names, this->protocol);
/* send SA down to the kernel */
2006-10-26 09:46:56 +00:00
DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst);
this->proposal->get_algorithm(this->proposal, ENCRYPTION_ALGORITHM,
&enc_alg, &size);
this->proposal->get_algorithm(this->proposal, INTEGRITY_ALGORITHM,
&int_alg, &size);
this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS,
&esn, NULL);
lifetime = this->config->get_lifetime(this->config);
now = time_monotonic(NULL);
if (lifetime->time.rekey)
{
if (this->rekey_time)
{
this->rekey_time = min(this->rekey_time, now + lifetime->time.rekey);
}
else
{
this->rekey_time = now + lifetime->time.rekey;
}
}
if (lifetime->time.life)
{
this->expire_time = now + lifetime->time.life;
}
if (!lifetime->time.jitter && !inbound)
{ /* avoid triggering multiple rekey events */
lifetime->time.rekey = 0;
}
/* BEET requires the bound address from the traffic selectors.
* TODO: We add just the first traffic selector for now, as the
* kernel accepts a single TS per SA only */
if (inbound)
{
my_ts->get_first(my_ts, (void**)&dst_ts);
other_ts->get_first(other_ts, (void**)&src_ts);
}
else
{
my_ts->get_first(my_ts, (void**)&src_ts);
other_ts->get_first(other_ts, (void**)&dst_ts);
}
status = hydra->kernel_interface->add_sa(hydra->kernel_interface,
src, dst, spi, proto_ike2ip(this->protocol), this->reqid,
inbound ? this->mark_in : this->mark_out, tfc,
2010-07-02 21:45:57 +00:00
lifetime, enc_alg, encr, int_alg, integ, this->mode,
kernel-interface: add an exchange initiator parameter to add_sa() This new flag gives the kernel-interface a hint how it should priorize the use of newly installed SAs during rekeying. Consider the following rekey procedure in IKEv2: Initiator --- Responder I1 -------CREATE-------> R1 I2 <------CREATE-------- -------DELETE-------> R2 I3 <------DELETE-------- SAs are always handled as pairs, the following happens at the SA level: * Initiator starts the exchange at I1 * Responder installs new SA pair at R1 * Initiator installs new SA pair at I2 * Responder removes old SA pair at R2 * Initiator removes old SA pair at I3 This makes sure SAs get installed/removed overlapping during rekeying. However, to avoid any packet loss, it is crucial that the new outbound SA gets activated at the correct position: * as exchange initiator, in I2 * as exchange responder, in R2 This should guarantee that we don't use the new outbound SA before the peer could install its corresponding inbound SA. The new parameter allows the kernel backend to install the new SA with appropriate priorities, i.e. it should: * as exchange inititator, have the new outbound SA installed with higher priority than the old SA * as exchange responder, have the new outbound SA installed with lower priority than the old SA While we could split up the SA installation at the responder, this approach has another advantage: it allows the kernel backend to switch SAs based on other criteria, for example when receiving traffic on the new inbound SA.
2013-05-08 08:31:06 +00:00
this->ipcomp, cpi, initiator, this->encap, esn, update,
src_ts, dst_ts);
free(lifetime);
return status;
}
/**
* Install 3 policies: out, in and forward
*/
static status_t install_policies_internal(private_child_sa_t *this,
host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts,
traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa,
ipsec_sa_cfg_t *other_sa, policy_type_t type, policy_priority_t priority)
{
status_t status = SUCCESS;
status |= hydra->kernel_interface->add_policy(hydra->kernel_interface,
my_addr, other_addr, my_ts, other_ts,
POLICY_OUT, type, other_sa,
this->mark_out, priority);
status |= hydra->kernel_interface->add_policy(hydra->kernel_interface,
other_addr, my_addr, other_ts, my_ts,
POLICY_IN, type, my_sa,
this->mark_in, priority);
if (this->mode != MODE_TRANSPORT)
{
status |= hydra->kernel_interface->add_policy(hydra->kernel_interface,
other_addr, my_addr, other_ts, my_ts,
POLICY_FWD, type, my_sa,
this->mark_in, priority);
}
return status;
}
/**
* Delete 3 policies: out, in and forward
*/
static void del_policies_internal(private_child_sa_t *this,
traffic_selector_t *my_ts, traffic_selector_t *other_ts,
policy_priority_t priority)
{
hydra->kernel_interface->del_policy(hydra->kernel_interface,
my_ts, other_ts, POLICY_OUT, this->reqid,
this->mark_out, priority);
hydra->kernel_interface->del_policy(hydra->kernel_interface,
other_ts, my_ts, POLICY_IN, this->reqid,
this->mark_in, priority);
if (this->mode != MODE_TRANSPORT)
{
hydra->kernel_interface->del_policy(hydra->kernel_interface,
other_ts, my_ts, POLICY_FWD, this->reqid,
this->mark_in, priority);
}
}
METHOD(child_sa_t, add_policies, status_t,
private_child_sa_t *this, linked_list_t *my_ts_list,
linked_list_t *other_ts_list)
2006-02-22 16:14:40 +00:00
{
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
status_t status = SUCCESS;
/* apply traffic selectors */
enumerator = my_ts_list->create_enumerator(my_ts_list);
while (enumerator->enumerate(enumerator, &my_ts))
2006-02-22 16:14:40 +00:00
{
this->my_ts->insert_last(this->my_ts, my_ts->clone(my_ts));
}
enumerator->destroy(enumerator);
enumerator = other_ts_list->create_enumerator(other_ts_list);
while (enumerator->enumerate(enumerator, &other_ts))
{
this->other_ts->insert_last(this->other_ts, other_ts->clone(other_ts));
}
enumerator->destroy(enumerator);
2008-11-11 06:37:37 +00:00
if (this->config->install_policy(this->config))
{
policy_priority_t priority;
ipsec_sa_cfg_t my_sa = {
.mode = this->mode,
.reqid = this->reqid,
.ipcomp = {
.transform = this->ipcomp,
},
}, other_sa = my_sa;
my_sa.ipcomp.cpi = this->my_cpi;
other_sa.ipcomp.cpi = this->other_cpi;
if (this->protocol == PROTO_ESP)
{
my_sa.esp.use = TRUE;
my_sa.esp.spi = this->my_spi;
other_sa.esp.use = TRUE;
other_sa.esp.spi = this->other_spi;
}
else
{
my_sa.ah.use = TRUE;
my_sa.ah.spi = this->my_spi;
other_sa.ah.use = TRUE;
other_sa.ah.spi = this->other_spi;
}
/* if we're not in state CHILD_INSTALLING (i.e. if there is no SAD
* entry) we install a trap policy */
this->trap = this->state == CHILD_CREATED;
priority = this->trap ? POLICY_PRIORITY_ROUTED
: POLICY_PRIORITY_DEFAULT;
2008-11-11 06:37:37 +00:00
/* enumerate pairs of traffic selectors */
enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
/* install outbound drop policy to avoid packets leaving unencrypted
* when updating policies */
if (priority == POLICY_PRIORITY_DEFAULT)
{
status |= install_policies_internal(this, this->my_addr,
this->other_addr, my_ts, other_ts,
&my_sa, &other_sa, POLICY_DROP,
POLICY_PRIORITY_FALLBACK);
}
/* install policies */
status |= install_policies_internal(this, this->my_addr,
this->other_addr, my_ts, other_ts,
&my_sa, &other_sa, POLICY_IPSEC, priority);
2008-11-11 06:37:37 +00:00
if (status != SUCCESS)
{
break;
}
}
2008-11-11 06:37:37 +00:00
enumerator->destroy(enumerator);
}
if (status == SUCCESS && this->trap)
{
set_state(this, CHILD_ROUTED);
}
return status;
}
/**
* Callback to reinstall a virtual IP
*/
static void reinstall_vip(host_t *vip, host_t *me)
{
char *iface;
if (hydra->kernel_interface->get_interface(hydra->kernel_interface,
me, &iface))
{
hydra->kernel_interface->del_ip(hydra->kernel_interface, vip, -1, TRUE);
hydra->kernel_interface->add_ip(hydra->kernel_interface, vip, -1, iface);
free(iface);
}
}
METHOD(child_sa_t, update, status_t,
private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips,
bool encap)
2006-06-22 06:36:28 +00:00
{
child_sa_state_t old;
bool transport_proxy_mode;
/* anything changed at all? */
if (me->equals(me, this->my_addr) &&
other->equals(other, this->other_addr) && this->encap == encap)
2006-06-22 06:36:28 +00:00
{
return SUCCESS;
}
old = this->state;
set_state(this, CHILD_UPDATING);
transport_proxy_mode = this->config->use_proxy_mode(this->config) &&
this->mode == MODE_TRANSPORT;
if (!transport_proxy_mode)
{
/* update our (initiator) SA */
if (this->my_spi)
{
if (hydra->kernel_interface->update_sa(hydra->kernel_interface,
this->my_spi, proto_ike2ip(this->protocol),
this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0,
this->other_addr, this->my_addr, other, me,
2010-07-02 21:45:57 +00:00
this->encap, encap, this->mark_in) == NOT_SUPPORTED)
{
return NOT_SUPPORTED;
}
}
/* update his (responder) SA */
if (this->other_spi)
{
if (hydra->kernel_interface->update_sa(hydra->kernel_interface,
this->other_spi, proto_ike2ip(this->protocol),
2009-09-04 12:58:05 +00:00
this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0,
this->my_addr, this->other_addr, me, other,
2010-07-02 21:45:57 +00:00
this->encap, encap, this->mark_out) == NOT_SUPPORTED)
{
return NOT_SUPPORTED;
}
}
}
2008-11-11 06:37:37 +00:00
if (this->config->install_policy(this->config))
2006-06-22 06:36:28 +00:00
{
ipsec_sa_cfg_t my_sa = {
.mode = this->mode,
.reqid = this->reqid,
.ipcomp = {
.transform = this->ipcomp,
},
}, other_sa = my_sa;
my_sa.ipcomp.cpi = this->my_cpi;
other_sa.ipcomp.cpi = this->other_cpi;
if (this->protocol == PROTO_ESP)
{
my_sa.esp.use = TRUE;
my_sa.esp.spi = this->my_spi;
other_sa.esp.use = TRUE;
other_sa.esp.spi = this->other_spi;
}
else
{
my_sa.ah.use = TRUE;
my_sa.ah.spi = this->my_spi;
other_sa.ah.use = TRUE;
other_sa.ah.spi = this->other_spi;
}
2008-11-11 06:37:37 +00:00
/* update policies */
if (!me->ip_equals(me, this->my_addr) ||
!other->ip_equals(other, this->other_addr))
2006-06-22 06:36:28 +00:00
{
2008-11-11 06:37:37 +00:00
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
2008-11-11 06:37:37 +00:00
/* always use high priorities, as hosts getting updated are INSTALLED */
enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
traffic_selector_t *old_my_ts = NULL, *old_other_ts = NULL;
2008-11-11 06:37:37 +00:00
/* remove old policies first */
del_policies_internal(this, my_ts, other_ts,
POLICY_PRIORITY_DEFAULT);
/* check if we have to update a "dynamic" traffic selector */
2008-11-11 06:37:37 +00:00
if (!me->ip_equals(me, this->my_addr) &&
my_ts->is_host(my_ts, this->my_addr))
{
old_my_ts = my_ts->clone(my_ts);
2008-11-11 06:37:37 +00:00
my_ts->set_address(my_ts, me);
}
if (!other->ip_equals(other, this->other_addr) &&
other_ts->is_host(other_ts, this->other_addr))
{
old_other_ts = other_ts->clone(other_ts);
2008-11-11 06:37:37 +00:00
other_ts->set_address(other_ts, other);
}
2008-11-11 06:37:37 +00:00
/* we reinstall the virtual IP to handle interface roaming
* correctly */
vips->invoke_function(vips, (void*)reinstall_vip, me);
2008-11-11 06:37:37 +00:00
/* reinstall updated policies */
install_policies_internal(this, me, other, my_ts, other_ts,
&my_sa, &other_sa, POLICY_IPSEC,
POLICY_PRIORITY_DEFAULT);
/* update fallback policies after the new policy is in place */
if (old_my_ts || old_other_ts)
{
del_policies_internal(this, old_my_ts ?: my_ts,
old_other_ts ?: other_ts,
POLICY_PRIORITY_FALLBACK);
install_policies_internal(this, me, other, my_ts, other_ts,
&my_sa, &other_sa, POLICY_DROP,
POLICY_PRIORITY_FALLBACK);
DESTROY_IF(old_my_ts);
DESTROY_IF(old_other_ts);
}
2008-11-11 06:37:37 +00:00
}
enumerator->destroy(enumerator);
2006-06-22 06:36:28 +00:00
}
}
if (!transport_proxy_mode)
{
/* apply hosts */
if (!me->equals(me, this->my_addr))
{
this->my_addr->destroy(this->my_addr);
this->my_addr = me->clone(me);
}
if (!other->equals(other, this->other_addr))
{
this->other_addr->destroy(this->other_addr);
this->other_addr = other->clone(other);
}
}
this->encap = encap;
set_state(this, old);
2006-06-22 06:36:28 +00:00
return SUCCESS;
}
METHOD(child_sa_t, destroy, void,
private_child_sa_t *this)
{
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
policy_priority_t priority;
priority = this->trap ? POLICY_PRIORITY_ROUTED : POLICY_PRIORITY_DEFAULT;
2008-10-14 08:52:13 +00:00
set_state(this, CHILD_DESTROYING);
/* delete SAs in the kernel, if they are set up */
if (this->my_spi)
{
2009-08-25 16:12:55 +00:00
/* if CHILD was not established, use PROTO_ESP used during alloc_spi().
* TODO: For AH support, we have to store protocol specific SPI.s */
if (this->protocol == PROTO_NONE)
{
this->protocol = PROTO_ESP;
}
hydra->kernel_interface->del_sa(hydra->kernel_interface,
this->other_addr, this->my_addr, this->my_spi,
proto_ike2ip(this->protocol), this->my_cpi,
this->mark_in);
}
if (this->other_spi)
{
hydra->kernel_interface->del_sa(hydra->kernel_interface,
this->my_addr, this->other_addr, this->other_spi,
proto_ike2ip(this->protocol), this->other_cpi,
this->mark_out);
2008-05-08 16:19:11 +00:00
}
2008-11-11 06:37:37 +00:00
if (this->config->install_policy(this->config))
{
2008-11-11 06:37:37 +00:00
/* delete all policies in the kernel */
enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
del_policies_internal(this, my_ts, other_ts, priority);
if (priority == POLICY_PRIORITY_DEFAULT)
{
del_policies_internal(this, my_ts, other_ts,
POLICY_PRIORITY_FALLBACK);
}
2008-11-11 06:37:37 +00:00
}
enumerator->destroy(enumerator);
}
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_addr->destroy(this->my_addr);
this->other_addr->destroy(this->other_addr);
DESTROY_IF(this->proposal);
this->config->destroy(this->config);
free(this);
}
/**
* Described in header.
*/
2007-02-28 14:04:36 +00:00
child_sa_t * child_sa_create(host_t *me, host_t* other,
child_cfg_t *config, u_int32_t rekey, bool encap)
{
static refcount_t reqid = 0;
private_child_sa_t *this;
INIT(this,
.public = {
.get_name = _get_name,
.get_reqid = _get_reqid,
.get_config = _get_config,
.get_state = _get_state,
.set_state = _set_state,
.get_spi = _get_spi,
.get_cpi = _get_cpi,
.get_protocol = _get_protocol,
.set_protocol = _set_protocol,
.get_mode = _get_mode,
.set_mode = _set_mode,
.get_proposal = _get_proposal,
.set_proposal = _set_proposal,
.get_lifetime = _get_lifetime,
.get_usestats = _get_usestats,
2012-03-21 15:54:24 +00:00
.get_mark = _get_mark,
.has_encap = _has_encap,
.get_ipcomp = _get_ipcomp,
.set_ipcomp = _set_ipcomp,
.get_close_action = _get_close_action,
.set_close_action = _set_close_action,
.get_dpd_action = _get_dpd_action,
.set_dpd_action = _set_dpd_action,
.alloc_spi = _alloc_spi,
.alloc_cpi = _alloc_cpi,
.install = _install,
.update = _update,
.add_policies = _add_policies,
.get_traffic_selectors = _get_traffic_selectors,
.create_policy_enumerator = _create_policy_enumerator,
.destroy = _destroy,
},
.my_addr = me->clone(me),
.other_addr = other->clone(other),
.encap = encap,
.ipcomp = IPCOMP_NONE,
.state = CHILD_CREATED,
.my_ts = linked_list_create(),
.other_ts = linked_list_create(),
.protocol = PROTO_NONE,
.mode = MODE_TUNNEL,
.close_action = config->get_close_action(config),
.dpd_action = config->get_dpd_action(config),
.reqid = config->get_reqid(config),
.mark_in = config->get_mark(config, TRUE),
.mark_out = config->get_mark(config, FALSE),
);
this->config = config;
config->get_ref(config);
2010-07-02 21:45:57 +00:00
if (!this->reqid)
{
/* reuse old reqid if we are rekeying an existing CHILD_SA */
if (rekey)
{
this->reqid = rekey;
}
else
{
this->reqid = charon->traps->find_reqid(charon->traps, config);
if (!this->reqid)
{
this->reqid = ref_get(&reqid);
}
}
}
if (this->mark_in.value == MARK_REQID)
{
this->mark_in.value = this->reqid;
}
if (this->mark_out.value == MARK_REQID)
{
this->mark_out.value = this->reqid;
}
/* MIPv6 proxy transport mode sets SA endpoints to TS hosts */
2008-11-11 06:37:37 +00:00
if (config->get_mode(config) == MODE_TRANSPORT &&
config->use_proxy_mode(config))
2008-11-11 06:37:37 +00:00
{
ts_type_t type;
int family;
chunk_t addr;
host_t *host;
enumerator_t *enumerator;
linked_list_t *my_ts_list, *other_ts_list, *list;
2008-11-11 06:37:37 +00:00
traffic_selector_t *my_ts, *other_ts;
2008-11-11 06:37:37 +00:00
this->mode = MODE_TRANSPORT;
list = linked_list_create_with_items(me, NULL);
my_ts_list = config->get_traffic_selectors(config, TRUE, NULL, list);
list->destroy(list);
2008-11-11 06:37:37 +00:00
enumerator = my_ts_list->create_enumerator(my_ts_list);
if (enumerator->enumerate(enumerator, &my_ts))
{
if (my_ts->is_host(my_ts, NULL) &&
!my_ts->is_host(my_ts, this->my_addr))
{
type = my_ts->get_type(my_ts);
family = (type == TS_IPV4_ADDR_RANGE) ? AF_INET : AF_INET6;
addr = my_ts->get_from_address(my_ts);
host = host_create_from_chunk(family, addr, 0);
free(addr.ptr);
DBG1(DBG_CHD, "my address: %H is a transport mode proxy for %H",
this->my_addr, host);
2008-11-11 06:37:37 +00:00
this->my_addr->destroy(this->my_addr);
this->my_addr = host;
}
}
enumerator->destroy(enumerator);
my_ts_list->destroy_offset(my_ts_list, offsetof(traffic_selector_t, destroy));
list = linked_list_create_with_items(other, NULL);
other_ts_list = config->get_traffic_selectors(config, FALSE, NULL, list);
list->destroy(list);
2008-11-11 06:37:37 +00:00
enumerator = other_ts_list->create_enumerator(other_ts_list);
if (enumerator->enumerate(enumerator, &other_ts))
{
if (other_ts->is_host(other_ts, NULL) &&
!other_ts->is_host(other_ts, this->other_addr))
{
type = other_ts->get_type(other_ts);
family = (type == TS_IPV4_ADDR_RANGE) ? AF_INET : AF_INET6;
addr = other_ts->get_from_address(other_ts);
host = host_create_from_chunk(family, addr, 0);
free(addr.ptr);
DBG1(DBG_CHD, "other address: %H is a transport mode proxy for %H",
this->other_addr, host);
2008-11-11 06:37:37 +00:00
this->other_addr->destroy(this->other_addr);
this->other_addr = host;
}
}
enumerator->destroy(enumerator);
other_ts_list->destroy_offset(other_ts_list, offsetof(traffic_selector_t, destroy));
}
2006-06-22 06:36:28 +00:00
return &this->public;
}