diff options
Diffstat (limited to 'src/libcharon/sa/child_sa.c')
-rw-r--r-- | src/libcharon/sa/child_sa.c | 1015 |
1 files changed, 1015 insertions, 0 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c new file mode 100644 index 000000000..3fdfb51ad --- /dev/null +++ b/src/libcharon/sa/child_sa.c @@ -0,0 +1,1015 @@ +/* + * Copyright (C) 2006-2009 Tobias Brunner + * Copyright (C) 2005-2008 Martin Willi + * Copyright (C) 2006 Daniel Roethlisberger + * 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 +#include "child_sa.h" + +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <daemon.h> + +ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING, + "CREATED", + "ROUTED", + "INSTALLING", + "INSTALLED", + "UPDATING", + "REKEYING", + "DELETING", + "DESTROYING", +); + +typedef struct private_child_sa_t private_child_sa_t; + +/** + * Private data of a child_sa_t object. + */ +struct private_child_sa_t { + /** + * 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; + + /** + * others used SPI, 0 if unused + */ + 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; + + /** + * absolute time when rekeying is scheduled + */ + time_t rekey_time; + + /** + * absolute time when the SA expires + */ + time_t expire_time; + + /** + * state of the CHILD_SA + */ + child_sa_state_t state; + + /** + * Specifies if UDP encapsulation is enabled (NAT traversal) + */ + bool encap; + + /** + * Specifies the IPComp transform used (IPCOMP_NONE if disabled) + */ + ipcomp_transform_t ipcomp; + + /** + * mode this SA uses, tunnel/transport + */ + ipsec_mode_t mode; + + /** + * selected proposal + */ + proposal_t *proposal; + + /** + * config used to create this child + */ + 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; +}; + +/** + * Implementation of child_sa_t.get_name + */ +static char *get_name(private_child_sa_t *this) +{ + return this->config->get_name(this->config); +} + +/** + * Implements child_sa_t.get_reqid + */ +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 + */ +u_int32_t get_spi(private_child_sa_t *this, bool inbound) +{ + return inbound ? this->my_spi : this->other_spi; +} + +/** + * Implements child_sa_t.get_cpi + */ +u_int16_t get_cpi(private_child_sa_t *this, bool inbound) +{ + return inbound ? this->my_cpi : this->other_cpi; +} + +/** + * Implements child_sa_t.get_protocol + */ +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 + */ +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 + */ +static bool has_encap(private_child_sa_t *this) +{ + return this->encap; +} + +/** + * Implementation of child_sa_t.get_ipcomp + */ +static ipcomp_transform_t get_ipcomp(private_child_sa_t *this) +{ + return this->ipcomp; +} + +/** + * Implementation of child_sa_t.set_ipcomp. + */ +static void set_ipcomp(private_child_sa_t *this, ipcomp_transform_t ipcomp) +{ + this->ipcomp = ipcomp; +} + +/** + * 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.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; + +/** + * 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; +}; + +/** + * enumerator function of create_policy_enumerator() + */ +static bool policy_enumerate(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; +} + +/** + * destroy function of create_policy_enumerator() + */ +static void policy_destroy(policy_enumerator_t *this) +{ + this->mine->destroy(this->mine); + this->other->destroy(this->other); + free(this); +} + +/** + * Implementation of child_sa_t.create_policy_enumerator + */ +static enumerator_t* create_policy_enumerator(private_child_sa_t *this) +{ + policy_enumerator_t *e = malloc_thing(policy_enumerator_t); + + e->public.enumerate = (void*)policy_enumerate; + e->public.destroy = (void*)policy_destroy; + e->mine = this->my_ts->create_enumerator(this->my_ts); + e->other = this->other_ts->create_enumerator(this->other_ts); + e->list = this->other_ts; + e->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; + + if (inbound) + { + if (this->my_spi) + { + status = charon->kernel_interface->query_sa( + charon->kernel_interface, + this->other_addr, this->my_addr, + this->my_spi, this->protocol, &bytes); + if (status == SUCCESS) + { + if (bytes > this->my_usebytes) + { + this->my_usebytes = bytes; + return SUCCESS; + } + return FAILED; + } + } + } + else + { + if (this->other_spi) + { + status = charon->kernel_interface->query_sa( + charon->kernel_interface, + this->my_addr, this->other_addr, + this->other_spi, this->protocol, &bytes); + if (status == SUCCESS) + { + if (bytes > this->other_usebytes) + { + this->other_usebytes = bytes; + return SUCCESS; + } + return FAILED; + } + } + } + return status; +} + +/** + * updates the cached usetime + */ +static void update_usetime(private_child_sa_t *this, bool inbound) +{ + enumerator_t *enumerator; + 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)) + { + u_int32_t in, out, fwd; + + if (inbound) + { + if (charon->kernel_interface->query_policy(charon->kernel_interface, + other_ts, my_ts, POLICY_IN, &in) == SUCCESS) + { + last_use = max(last_use, in); + } + if (this->mode != MODE_TRANSPORT) + { + if (charon->kernel_interface->query_policy(charon->kernel_interface, + other_ts, my_ts, POLICY_FWD, &fwd) == SUCCESS) + { + last_use = max(last_use, fwd); + } + } + } + else + { + if (charon->kernel_interface->query_policy(charon->kernel_interface, + my_ts, other_ts, POLICY_OUT, &out) == SUCCESS) + { + last_use = max(last_use, out); + } + } + } + enumerator->destroy(enumerator); + + if (last_use == 0) + { + return; + } + if (inbound) + { + this->my_usetime = last_use; + } + else + { + this->other_usetime = last_use; + } +} + +/** + * Implementation of child_sa_t.get_usestats + */ +static void get_usestats(private_child_sa_t *this, bool inbound, + time_t *time, u_int64_t *bytes) +{ + if (update_usebytes(this, inbound) != FAILED) + { + /* there was traffic since last update or the kernel interface + * does not support querying the number of usebytes. + */ + update_usetime(this, inbound); + } + if (time) + { + *time = inbound ? this->my_usetime : this->other_usetime; + } + if (bytes) + { + *bytes = inbound ? this->my_usebytes : this->other_usebytes; + } +} + +/** + * Implementation of child_sa_t.get_lifetime + */ +static time_t get_lifetime(private_child_sa_t *this, bool hard) +{ + return hard ? this->expire_time : this->rekey_time; +} + +/** + * Implementation of child_sa_t.alloc_spi + */ +static u_int32_t alloc_spi(private_child_sa_t *this, protocol_id_t protocol) +{ + if (charon->kernel_interface->get_spi(charon->kernel_interface, + this->other_addr, this->my_addr, protocol, + this->reqid, &this->my_spi) == SUCCESS) + { + return this->my_spi; + } + return 0; +} + +/** + * Implementation of child_sa_t.alloc_cpi + */ +static u_int16_t alloc_cpi(private_child_sa_t *this) +{ + if (charon->kernel_interface->get_cpi(charon->kernel_interface, + this->other_addr, this->my_addr, this->reqid, + &this->my_cpi) == SUCCESS) + { + return this->my_cpi; + } + return 0; +} + +/** + * Implementation of child_sa_t.install + */ +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, + linked_list_t *my_ts, linked_list_t *other_ts) +{ + u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size; + traffic_selector_t *src_ts = NULL, *dst_ts = NULL; + time_t now; + lifetime_cfg_t *lifetime; + 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) + { + 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 + { + src = this->my_addr; + dst = this->other_addr; + this->other_spi = spi; + this->other_cpi = cpi; + } + + 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); + + this->proposal->get_algorithm(this->proposal, ENCRYPTION_ALGORITHM, + &enc_alg, &size); + this->proposal->get_algorithm(this->proposal, INTEGRITY_ALGORITHM, + &int_alg, &size); + + lifetime = this->config->get_lifetime(this->config); + + now = time_monotonic(NULL); + if (lifetime->time.rekey) + { + 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; + } + + if (this->mode == MODE_BEET) + { + /* 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 = charon->kernel_interface->add_sa(charon->kernel_interface, + src, dst, spi, this->protocol, this->reqid, lifetime, + enc_alg, encr, int_alg, integ, this->mode, this->ipcomp, cpi, + this->encap, update, src_ts, dst_ts); + + free(lifetime); + + return status; +} + +/** + * 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) +{ + enumerator_t *enumerator; + traffic_selector_t *my_ts, *other_ts; + status_t status = SUCCESS; + bool routed = (this->state == CHILD_CREATED); + + /* apply traffic selectors */ + enumerator = my_ts_list->create_enumerator(my_ts_list); + while (enumerator->enumerate(enumerator, &my_ts)) + { + 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); + + if (this->config->install_policy(this->config)) + { + /* enumerate pairs of traffic selectors */ + enumerator = create_policy_enumerator(this); + while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) + { + /* 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, 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, 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, this->mode, + this->ipcomp, this->my_cpi, routed); + } + + if (status != SUCCESS) + { + break; + } + } + enumerator->destroy(enumerator); + } + + 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.update. + */ +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; + + /* anything changed at all? */ + if (me->equals(me, this->my_addr) && + other->equals(other, this->other_addr) && this->encap == encap) + { + 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 (initator) SA */ + if (this->my_spi) + { + if (charon->kernel_interface->update_sa(charon->kernel_interface, + this->my_spi, this->protocol, + this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0, + this->other_addr, this->my_addr, other, me, + this->encap, encap) == NOT_SUPPORTED) + { + return NOT_SUPPORTED; + } + } + + /* update his (responder) SA */ + if (this->other_spi) + { + if (charon->kernel_interface->update_sa(charon->kernel_interface, + this->other_spi, this->protocol, + this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0, + this->my_addr, this->other_addr, me, other, + this->encap, encap) == NOT_SUPPORTED) + { + return NOT_SUPPORTED; + } + } + } + + if (this->config->install_policy(this->config)) + { + /* update policies */ + if (!me->ip_equals(me, this->my_addr) || + !other->ip_equals(other, this->other_addr)) + { + enumerator_t *enumerator; + traffic_selector_t *my_ts, *other_ts; + + /* always use high priorities, as hosts getting updated are INSTALLED */ + enumerator = create_policy_enumerator(this); + while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) + { + /* remove old policies first */ + charon->kernel_interface->del_policy(charon->kernel_interface, + my_ts, other_ts, POLICY_OUT, FALSE); + charon->kernel_interface->del_policy(charon->kernel_interface, + other_ts, my_ts, POLICY_IN, FALSE); + if (this->mode != MODE_TRANSPORT) + { + charon->kernel_interface->del_policy(charon->kernel_interface, + other_ts, my_ts, POLICY_FWD, FALSE); + } + + /* check whether we have to update a "dynamic" traffic selector */ + if (!me->ip_equals(me, this->my_addr) && + my_ts->is_host(my_ts, this->my_addr)) + { + my_ts->set_address(my_ts, me); + } + if (!other->ip_equals(other, this->other_addr) && + other_ts->is_host(other_ts, this->other_addr)) + { + other_ts->set_address(other_ts, other); + } + + /* we reinstall the virtual IP to handle interface roaming + * correctly */ + if (vip) + { + charon->kernel_interface->del_ip(charon->kernel_interface, vip); + charon->kernel_interface->add_ip(charon->kernel_interface, vip, me); + } + + /* reinstall updated policies */ + charon->kernel_interface->add_policy(charon->kernel_interface, + me, other, my_ts, other_ts, POLICY_OUT, this->other_spi, + this->protocol, this->reqid, this->mode, this->ipcomp, + this->other_cpi, FALSE); + charon->kernel_interface->add_policy(charon->kernel_interface, + other, me, other_ts, my_ts, POLICY_IN, this->my_spi, + this->protocol, this->reqid, this->mode, this->ipcomp, + this->my_cpi, FALSE); + if (this->mode != MODE_TRANSPORT) + { + charon->kernel_interface->add_policy(charon->kernel_interface, + other, me, other_ts, my_ts, POLICY_FWD, this->my_spi, + this->protocol, this->reqid, this->mode, this->ipcomp, + this->my_cpi, FALSE); + } + } + enumerator->destroy(enumerator); + } + } + + 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); + + return SUCCESS; +} + +/** + * Implementation of child_sa_t.destroy. + */ +static void destroy(private_child_sa_t *this) +{ + enumerator_t *enumerator; + traffic_selector_t *my_ts, *other_ts; + bool unrouted = (this->state == CHILD_ROUTED); + + set_state(this, CHILD_DESTROYING); + + /* delete SAs in the kernel, if they are set up */ + if (this->my_spi) + { + /* 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; + } + charon->kernel_interface->del_sa(charon->kernel_interface, + this->other_addr, this->my_addr, this->my_spi, + this->protocol, this->my_cpi); + } + if (this->other_spi) + { + charon->kernel_interface->del_sa(charon->kernel_interface, + this->my_addr, this->other_addr, this->other_spi, + this->protocol, this->other_cpi); + } + + if (this->config->install_policy(this->config)) + { + /* delete all policies in the kernel */ + enumerator = create_policy_enumerator(this); + while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) + { + charon->kernel_interface->del_policy(charon->kernel_interface, + my_ts, other_ts, POLICY_OUT, unrouted); + charon->kernel_interface->del_policy(charon->kernel_interface, + other_ts, my_ts, POLICY_IN, unrouted); + if (this->mode != MODE_TRANSPORT) + { + charon->kernel_interface->del_policy(charon->kernel_interface, + other_ts, my_ts, POLICY_FWD, unrouted); + } + } + 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. + */ +child_sa_t * child_sa_create(host_t *me, host_t* other, + child_cfg_t *config, u_int32_t rekey, bool encap) +{ + static u_int32_t reqid = 0; + private_child_sa_t *this = malloc_thing(private_child_sa_t); + + /* 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.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 = (time_t(*)(child_sa_t*, bool))get_lifetime; + this->public.get_usestats = (void(*)(child_sa_t*,bool,time_t*,u_int64_t*))get_usestats; + 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, linked_list_t *my_ts_list, linked_list_t *other_ts_list))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.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->other_spi = 0; + this->my_cpi = 0; + this->other_cpi = 0; + this->encap = encap; + this->ipcomp = IPCOMP_NONE; + this->state = CHILD_CREATED; + this->my_usetime = 0; + this->other_usetime = 0; + this->my_usebytes = 0; + this->other_usebytes = 0; + /* reuse old reqid if we are rekeying an existing CHILD_SA */ + this->reqid = rekey ? rekey : ++reqid; + this->my_ts = linked_list_create(); + this->other_ts = linked_list_create(); + this->protocol = PROTO_NONE; + this->mode = MODE_TUNNEL; + this->proposal = NULL; + this->rekey_time = 0; + this->expire_time = 0; + 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)) + { + ts_type_t type; + int family; + chunk_t addr; + host_t *host; + 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)) + { + 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); + 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)); + + 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)) + { + 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); + 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)); + } + + return &this->public; +} |