diff options
Diffstat (limited to 'src/libcharon/sa')
25 files changed, 1078 insertions, 532 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 463ad2e22..46e4b6f7b 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -25,6 +25,7 @@ #include <hydra.h> #include <daemon.h> +#include <collections/array.h> ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING, "CREATED", @@ -79,14 +80,14 @@ struct private_child_sa_t { u_int16_t other_cpi; /** - * List for local traffic selectors + * Array for local traffic selectors */ - linked_list_t *my_ts; + array_t *my_ts; /** - * List for remote traffic selectors + * Array for remote traffic selectors */ - linked_list_t *other_ts; + array_t *other_ts; /** * Protocol used to protect this SA, ESP|AH @@ -331,10 +332,14 @@ METHOD(child_sa_t, set_proposal, void, this->proposal = proposal->clone(proposal); } -METHOD(child_sa_t, get_traffic_selectors, linked_list_t*, - private_child_sa_t *this, bool local) +METHOD(child_sa_t, create_ts_enumerator, enumerator_t*, + private_child_sa_t *this, bool local) { - return local ? this->my_ts : this->other_ts; + if (local) + { + return array_create_enumerator(this->my_ts); + } + return array_create_enumerator(this->other_ts); } typedef struct policy_enumerator_t policy_enumerator_t; @@ -349,8 +354,8 @@ struct policy_enumerator_t { enumerator_t *mine; /** enumerator over others TS */ enumerator_t *other; - /** list of others TS, to recreate enumerator */ - linked_list_t *list; + /** array of others TS, to recreate enumerator */ + array_t *array; /** currently enumerating TS for "me" side */ traffic_selector_t *ts; }; @@ -366,7 +371,7 @@ METHOD(enumerator_t, policy_enumerate, bool, 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->other = array_create_enumerator(this->array); this->ts = NULL; continue; } @@ -405,9 +410,9 @@ METHOD(child_sa_t, create_policy_enumerator, enumerator_t*, .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, + .mine = array_create_enumerator(this->my_ts), + .other = array_create_enumerator(this->other_ts), + .array = this->other_ts, .ts = NULL, ); @@ -424,6 +429,7 @@ 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) { @@ -432,13 +438,17 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) 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); + &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; @@ -452,13 +462,17 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) 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); + &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; @@ -471,7 +485,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) /** * updates the cached usetime */ -static void update_usetime(private_child_sa_t *this, bool inbound) +static bool update_usetime(private_child_sa_t *this, bool inbound) { enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; @@ -511,7 +525,7 @@ static void update_usetime(private_child_sa_t *this, bool inbound) if (last_use == 0) { - return; + return FALSE; } if (inbound) { @@ -521,18 +535,26 @@ static void update_usetime(private_child_sa_t *this, bool inbound) { this->other_usetime = last_use; } + return TRUE; } 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) { - if (update_usebytes(this, inbound) != FAILED) + 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. */ - update_usetime(this, inbound); + 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) { @@ -590,9 +612,9 @@ METHOD(child_sa_t, alloc_cpi, u_int16_t, } METHOD(child_sa_t, install, status_t, - private_child_sa_t *this, chunk_t encr, chunk_t integ, u_int32_t spi, - u_int16_t cpi, bool inbound, bool tfcv3, linked_list_t *my_ts, - linked_list_t *other_ts) + 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; @@ -668,28 +690,26 @@ METHOD(child_sa_t, install, status_t, lifetime->time.rekey = 0; } - if (this->mode == MODE_BEET || this->mode == MODE_TRANSPORT) + /* 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) { - /* 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); - } + 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, lifetime, enc_alg, encr, int_alg, integ, this->mode, - this->ipcomp, cpi, this->encap, esn, update, src_ts, dst_ts); + this->ipcomp, cpi, initiator, this->encap, esn, update, + src_ts, dst_ts); free(lifetime); @@ -757,13 +777,13 @@ METHOD(child_sa_t, add_policies, status_t, 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)); + array_insert(this->my_ts, ARRAY_TAIL, 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)); + array_insert(this->other_ts, ARRAY_TAIL, other_ts->clone(other_ts)); } enumerator->destroy(enumerator); @@ -1054,8 +1074,8 @@ METHOD(child_sa_t, destroy, void, 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)); + array_destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy)); + array_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); @@ -1064,12 +1084,47 @@ METHOD(child_sa_t, destroy, void, } /** + * Get proxy address for one side, if any + */ +static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local) +{ + host_t *host = NULL; + u_int8_t mask; + enumerator_t *enumerator; + linked_list_t *ts_list, *list; + traffic_selector_t *ts; + + list = linked_list_create_with_items(ike, NULL); + ts_list = config->get_traffic_selectors(config, local, NULL, list); + list->destroy(list); + + enumerator = ts_list->create_enumerator(ts_list); + while (enumerator->enumerate(enumerator, &ts)) + { + if (ts->is_host(ts, NULL) && ts->to_subnet(ts, &host, &mask)) + { + DBG1(DBG_CHD, "%s address: %H is a transport mode proxy for %H", + local ? "my" : "other", ike, host); + break; + } + } + enumerator->destroy(enumerator); + ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy)); + + if (!host) + { + host = ike->clone(ike); + } + return host; +} + +/** * 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; + static refcount_t reqid = 0; private_child_sa_t *this; INIT(this, @@ -1102,17 +1157,15 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, .install = _install, .update = _update, .add_policies = _add_policies, - .get_traffic_selectors = _get_traffic_selectors, + .create_ts_enumerator = _create_ts_enumerator, .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(), + .my_ts = array_create(0, 0), + .other_ts = array_create(0, 0), .protocol = PROTO_NONE, .mode = MODE_TUNNEL, .close_action = config->get_close_action(config), @@ -1128,7 +1181,18 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, if (!this->reqid) { /* reuse old reqid if we are rekeying an existing CHILD_SA */ - this->reqid = rekey ? rekey : ++reqid; + 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) @@ -1144,62 +1208,15 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, 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, *list; - traffic_selector_t *my_ts, *other_ts; - 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); - 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)); - - list = linked_list_create_with_items(other, NULL); - other_ts_list = config->get_traffic_selectors(config, FALSE, NULL, list); - list->destroy(list); - 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)); + this->my_addr = get_proxy_addr(config, me, TRUE); + this->other_addr = get_proxy_addr(config, other, FALSE); + } + else + { + this->my_addr = me->clone(me); + this->other_addr = other->clone(other); } - return &this->public; } diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h index 44511edf8..ed52d60b1 100644 --- a/src/libcharon/sa/child_sa.h +++ b/src/libcharon/sa/child_sa.h @@ -231,7 +231,7 @@ struct child_sa_t { /** * Override the DPD action specified by the CHILD_SA config. * - * @param close action to enforce + * @param dpd action to enforce */ void (*set_dpd_action)(child_sa_t *this, action_t action); @@ -284,17 +284,20 @@ struct child_sa_t { mark_t (*get_mark)(child_sa_t *this, bool inbound); /** - * Get the traffic selectors list added for one side. + * Create an enumerator over traffic selectors of one side. * - * @param local TRUE for own traffic selectors, FALSE for remote - * @return list of traffic selectors + * @param local TRUE for own traffic selectors, FALSE for remote. + * @return enumerator over traffic_selector_t* */ - linked_list_t* (*get_traffic_selectors) (child_sa_t *this, bool local); + enumerator_t* (*create_ts_enumerator)(child_sa_t *this, bool local); /** * Create an enumerator over installed policies. * - * @return enumerator over pairs of traffic selectors. + * The enumerated traffic selectors is a full mesh of compatible local + * and remote traffic selectors. + * + * @return enumerator over a pair of traffic_selector_t* */ enumerator_t* (*create_policy_enumerator)(child_sa_t *this); @@ -321,6 +324,7 @@ struct child_sa_t { * @param integ integrity key * @param spi SPI to use, allocated for inbound * @param cpi CPI to use, allocated for outbound + * @param initiator TRUE if initiator of exchange resulting in this SA * @param inbound TRUE to install an inbound SA, FALSE for outbound * @param tfcv3 TRUE if peer supports ESPv3 TFC * @param my_ts negotiated local traffic selector list @@ -328,7 +332,8 @@ struct child_sa_t { * @return SUCCESS or FAILED */ status_t (*install)(child_sa_t *this, chunk_t encr, chunk_t integ, - u_int32_t spi, u_int16_t cpi, bool inbound, bool tfcv3, + u_int32_t spi, u_int16_t cpi, + bool initiator, bool inbound, bool tfcv3, linked_list_t *my_ts, linked_list_t *other_ts); /** * Install the policies using some traffic selectors. @@ -348,7 +353,7 @@ struct child_sa_t { * @param me the new local host * @param other the new remote host * @param vips list of local virtual IPs - * @param TRUE to use UDP encapsulation for NAT traversal + * @param encap TRUE to use UDP encapsulation for NAT traversal * @return SUCCESS or FAILED */ status_t (*update)(child_sa_t *this, host_t *me, host_t *other, diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 63c04d9c0..2f4e1123c 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2012 Tobias Brunner + * Copyright (C) 2006-2013 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -26,7 +26,7 @@ #include <library.h> #include <hydra.h> #include <daemon.h> -#include <collections/linked_list.h> +#include <collections/array.h> #include <utils/lexparser.h> #include <processing/jobs/retransmit_job.h> #include <processing/jobs/delete_ike_sa_job.h> @@ -95,24 +95,24 @@ struct private_ike_sa_t { peer_cfg_t *peer_cfg; /** - * currently used authentication ruleset, local (as auth_cfg_t) + * currently used authentication ruleset, local */ auth_cfg_t *my_auth; /** - * list of completed local authentication rounds + * currently used authentication constraints, remote */ - linked_list_t *my_auths; + auth_cfg_t *other_auth; /** - * list of completed remote authentication rounds + * Array of completed local authentication rounds (as auth_cfg_t) */ - linked_list_t *other_auths; + array_t *my_auths; /** - * currently used authentication constraints, remote (as auth_cfg_t) + * Array of completed remote authentication rounds (as auth_cfg_t) */ - auth_cfg_t *other_auth; + array_t *other_auths; /** * Selected IKE proposal @@ -172,9 +172,9 @@ struct private_ike_sa_t { ike_condition_t conditions; /** - * Linked List containing the child sa's of the current IKE_SA. + * Array containing the child sa's of the current IKE_SA. */ - linked_list_t *child_sas; + array_t *child_sas; /** * keymat of this IKE_SA @@ -184,22 +184,22 @@ struct private_ike_sa_t { /** * Virtual IPs on local host */ - linked_list_t *my_vips; + array_t *my_vips; /** * Virtual IPs on remote host */ - linked_list_t *other_vips; + array_t *other_vips; /** * List of configuration attributes (attribute_entry_t) */ - linked_list_t *attributes; + array_t *attributes; /** * list of peer's addresses, additional ones transmitted via MOBIKE */ - linked_list_t *peer_addresses; + array_t *peer_addresses; /** * previously value of received DESTINATION_IP hash @@ -282,7 +282,8 @@ static time_t get_use_time(private_ike_sa_t* this, bool inbound) { use_time = this->stats[STAT_OUTBOUND]; } - enumerator = this->child_sas->create_enumerator(this->child_sas); + + enumerator = array_create_enumerator(this->child_sas); while (enumerator->enumerate(enumerator, &child_sa)) { child_sa->get_usestats(child_sa, inbound, ¤t, NULL, NULL); @@ -389,11 +390,11 @@ METHOD(ike_sa_t, add_auth_cfg, void, { if (local) { - this->my_auths->insert_last(this->my_auths, cfg); + array_insert(this->my_auths, ARRAY_TAIL, cfg); } else { - this->other_auths->insert_last(this->other_auths, cfg); + array_insert(this->other_auths, ARRAY_TAIL, cfg); } } @@ -402,9 +403,9 @@ METHOD(ike_sa_t, create_auth_cfg_enumerator, enumerator_t*, { if (local) { - return this->my_auths->create_enumerator(this->my_auths); + return array_create_enumerator(this->my_auths); } - return this->other_auths->create_enumerator(this->other_auths); + return array_create_enumerator(this->other_auths); } /** @@ -417,13 +418,11 @@ static void flush_auth_cfgs(private_ike_sa_t *this) this->my_auth->purge(this->my_auth, FALSE); this->other_auth->purge(this->other_auth, FALSE); - while (this->my_auths->remove_last(this->my_auths, - (void**)&cfg) == SUCCESS) + while (array_remove(this->my_auths, ARRAY_TAIL, &cfg)) { cfg->destroy(cfg); } - while (this->other_auths->remove_last(this->other_auths, - (void**)&cfg) == SUCCESS) + while (array_remove(this->other_auths, ARRAY_TAIL, &cfg)) { cfg->destroy(cfg); } @@ -750,7 +749,7 @@ METHOD(ike_sa_t, add_virtual_ip, void, if (hydra->kernel_interface->add_ip(hydra->kernel_interface, ip, -1, iface) == SUCCESS) { - this->my_vips->insert_last(this->my_vips, ip->clone(ip)); + array_insert_create(&this->my_vips, ARRAY_TAIL, ip->clone(ip)); } else { @@ -765,7 +764,7 @@ METHOD(ike_sa_t, add_virtual_ip, void, } else { - this->other_vips->insert_last(this->other_vips, ip->clone(ip)); + array_insert_create(&this->other_vips, ARRAY_TAIL, ip->clone(ip)); } } @@ -773,14 +772,15 @@ METHOD(ike_sa_t, add_virtual_ip, void, METHOD(ike_sa_t, clear_virtual_ips, void, private_ike_sa_t *this, bool local) { - linked_list_t *vips = local ? this->my_vips : this->other_vips; + array_t *vips; host_t *vip; - if (!local && vips->get_count(vips)) + vips = local ? this->my_vips : this->other_vips; + if (!local && array_count(vips)) { charon->bus->assign_vips(charon->bus, &this->public, FALSE); } - while (vips->remove_first(vips, (void**)&vip) == SUCCESS) + while (array_remove(vips, ARRAY_HEAD, &vip)) { if (local) { @@ -796,23 +796,23 @@ METHOD(ike_sa_t, create_virtual_ip_enumerator, enumerator_t*, { if (local) { - return this->my_vips->create_enumerator(this->my_vips); + return array_create_enumerator(this->my_vips); } - return this->other_vips->create_enumerator(this->other_vips); + return array_create_enumerator(this->other_vips); } METHOD(ike_sa_t, add_peer_address, void, private_ike_sa_t *this, host_t *host) { - this->peer_addresses->insert_last(this->peer_addresses, host); + array_insert_create(&this->peer_addresses, ARRAY_TAIL, host); } METHOD(ike_sa_t, create_peer_address_enumerator, enumerator_t*, private_ike_sa_t *this) { - if (this->peer_addresses->get_count(this->peer_addresses)) + if (this->peer_addresses) { - return this->peer_addresses->create_enumerator(this->peer_addresses); + return array_create_enumerator(this->peer_addresses); } /* in case we don't have MOBIKE */ return enumerator_create_single(this->other_host, NULL); @@ -821,17 +821,8 @@ METHOD(ike_sa_t, create_peer_address_enumerator, enumerator_t*, METHOD(ike_sa_t, clear_peer_addresses, void, private_ike_sa_t *this) { - enumerator_t *enumerator; - host_t *host; - - enumerator = this->peer_addresses->create_enumerator(this->peer_addresses); - while (enumerator->enumerate(enumerator, (void**)&host)) - { - this->peer_addresses->remove_at(this->peer_addresses, - enumerator); - host->destroy(host); - } - enumerator->destroy(enumerator); + array_destroy_offset(this->peer_addresses, offsetof(host_t, destroy)); + this->peer_addresses = NULL; } METHOD(ike_sa_t, has_mapping_changed, bool, @@ -927,13 +918,16 @@ METHOD(ike_sa_t, update_hosts, void, { enumerator_t *enumerator; child_sa_t *child_sa; + linked_list_t *vips; - enumerator = this->child_sas->create_enumerator(this->child_sas); - while (enumerator->enumerate(enumerator, (void**)&child_sa)) + vips = linked_list_create_from_enumerator( + array_create_enumerator(this->my_vips)); + + enumerator = array_create_enumerator(this->child_sas); + while (enumerator->enumerate(enumerator, &child_sa)) { - if (child_sa->update(child_sa, this->my_host, - this->other_host, this->my_vips, - has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED) + if (child_sa->update(child_sa, this->my_host, this->other_host, + vips, has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED) { this->public.rekey_child_sa(&this->public, child_sa->get_protocol(child_sa), @@ -941,6 +935,8 @@ METHOD(ike_sa_t, update_hosts, void, } } enumerator->destroy(enumerator); + + vips->destroy(vips); } } @@ -1081,6 +1077,20 @@ METHOD(ike_sa_t, initiate_mediated, status_t, static void resolve_hosts(private_ike_sa_t *this) { host_t *host; + int family = 0; + + switch (charon->socket->supported_families(charon->socket)) + { + case SOCKET_FAMILY_IPV4: + family = AF_INET; + break; + case SOCKET_FAMILY_IPV6: + family = AF_INET6; + break; + case SOCKET_FAMILY_BOTH: + case SOCKET_FAMILY_NONE: + break; + } if (this->remote_host) { @@ -1094,7 +1104,7 @@ static void resolve_hosts(private_ike_sa_t *this) other_addr = this->ike_cfg->get_other_addr(this->ike_cfg, NULL); other_port = this->ike_cfg->get_other_port(this->ike_cfg); - host = host_create_from_dns(other_addr, 0, other_port); + host = host_create_from_dns(other_addr, family, other_port); } if (host) { @@ -1110,7 +1120,6 @@ static void resolve_hosts(private_ike_sa_t *this) { char *my_addr; u_int16_t my_port; - int family = 0; /* use same address family as for other */ if (!this->other_host->is_anyaddr(this->other_host)) @@ -1133,7 +1142,7 @@ static void resolve_hosts(private_ike_sa_t *this) } else { /* fallback to address family specific %any(6), if configured */ - host = host_create_from_dns(my_addr, 0, my_port); + host = host_create_from_dns(my_addr, family, my_port); } } } @@ -1313,7 +1322,7 @@ METHOD(ike_sa_t, get_other_eap_id, identification_t*, enumerator_t *enumerator; auth_cfg_t *cfg; - enumerator = this->other_auths->create_enumerator(this->other_auths); + enumerator = array_create_enumerator(this->other_auths); while (enumerator->enumerate(enumerator, &cfg)) { /* prefer EAP-Identity of last round */ @@ -1350,7 +1359,7 @@ METHOD(ike_sa_t, set_other_id, void, METHOD(ike_sa_t, add_child_sa, void, private_ike_sa_t *this, child_sa_t *child_sa) { - this->child_sas->insert_last(this->child_sas, child_sa); + array_insert_create(&this->child_sas, ARRAY_TAIL, child_sa); } METHOD(ike_sa_t, get_child_sa, child_sa_t*, @@ -1359,7 +1368,7 @@ METHOD(ike_sa_t, get_child_sa, child_sa_t*, enumerator_t *enumerator; child_sa_t *current, *found = NULL; - enumerator = this->child_sas->create_enumerator(this->child_sas); + enumerator = array_create_enumerator(this->child_sas); while (enumerator->enumerate(enumerator, (void**)¤t)) { if (current->get_spi(current, inbound) == spi && @@ -1375,19 +1384,19 @@ METHOD(ike_sa_t, get_child_sa, child_sa_t*, METHOD(ike_sa_t, get_child_count, int, private_ike_sa_t *this) { - return this->child_sas->get_count(this->child_sas); + return array_count(this->child_sas); } METHOD(ike_sa_t, create_child_sa_enumerator, enumerator_t*, private_ike_sa_t *this) { - return this->child_sas->create_enumerator(this->child_sas); + return array_create_enumerator(this->child_sas); } METHOD(ike_sa_t, remove_child_sa, void, private_ike_sa_t *this, enumerator_t *enumerator) { - this->child_sas->remove_at(this->child_sas, enumerator); + array_remove_at(this->child_sas, enumerator); } METHOD(ike_sa_t, rekey_child_sa, status_t, @@ -1420,13 +1429,13 @@ METHOD(ike_sa_t, destroy_child_sa, status_t, child_sa_t *child_sa; status_t status = NOT_FOUND; - enumerator = this->child_sas->create_enumerator(this->child_sas); + enumerator = array_create_enumerator(this->child_sas); while (enumerator->enumerate(enumerator, (void**)&child_sa)) { if (child_sa->get_protocol(child_sa) == protocol && child_sa->get_spi(child_sa, TRUE) == spi) { - this->child_sas->remove_at(this->child_sas, enumerator); + array_remove_at(this->child_sas, enumerator); child_sa->destroy(child_sa); status = SUCCESS; break; @@ -1493,7 +1502,7 @@ METHOD(ike_sa_t, reauth, status_t, if (!has_condition(this, COND_ORIGINAL_INITIATOR)) { DBG1(DBG_IKE, "initiator did not reauthenticate as requested"); - if (this->other_vips->get_count(this->other_vips) != 0 || + if (array_count(this->other_vips) != 0 || has_condition(this, COND_XAUTH_AUTHENTICATED) || has_condition(this, COND_EAP_AUTHENTICATED) #ifdef ME @@ -1526,6 +1535,30 @@ METHOD(ike_sa_t, reauth, status_t, return this->task_manager->initiate(this->task_manager); } +/** + * Check if tasks to create CHILD_SAs are queued in the given queue + */ +static bool is_child_queued(private_ike_sa_t *this, task_queue_t queue) +{ + enumerator_t *enumerator; + task_t *task; + bool found = FALSE; + + enumerator = this->task_manager->create_task_enumerator(this->task_manager, + queue); + while (enumerator->enumerate(enumerator, &task)) + { + if (task->get_type(task) == TASK_CHILD_CREATE || + task->get_type(task) == TASK_QUICK_MODE) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + return found; +} + METHOD(ike_sa_t, reestablish, status_t, private_ike_sa_t *this) { @@ -1540,7 +1573,7 @@ METHOD(ike_sa_t, reestablish, status_t, if (has_condition(this, COND_REAUTHENTICATING)) { /* only reauthenticate if we have children */ - if (this->child_sas->get_count(this->child_sas) == 0 + if (array_count(this->child_sas) == 0 #ifdef ME /* allow reauth of mediation connections without CHILD_SAs */ && !this->peer_cfg->is_mediation(this->peer_cfg) @@ -1557,7 +1590,7 @@ METHOD(ike_sa_t, reestablish, status_t, } else { /* check if we have children to keep up at all */ - enumerator = this->child_sas->create_enumerator(this->child_sas); + enumerator = array_create_enumerator(this->child_sas); while (enumerator->enumerate(enumerator, (void**)&child_sa)) { if (this->state == IKE_DELETING) @@ -1575,13 +1608,20 @@ METHOD(ike_sa_t, reestablish, status_t, break; case ACTION_ROUTE: charon->traps->install(charon->traps, this->peer_cfg, - child_sa->get_config(child_sa)); + child_sa->get_config(child_sa), + child_sa->get_reqid(child_sa)); break; default: break; } } enumerator->destroy(enumerator); + /* check if we have tasks that recreate children */ + if (!restart) + { + restart = is_child_queued(this, TASK_QUEUE_ACTIVE) || + is_child_queued(this, TASK_QUEUE_QUEUED); + } #ifdef ME /* mediation connections have no children, keep them up anyway */ if (this->peer_cfg->is_mediation(this->peer_cfg)) @@ -1597,7 +1637,7 @@ METHOD(ike_sa_t, reestablish, status_t, /* check if we are able to reestablish this IKE_SA */ if (!has_condition(this, COND_ORIGINAL_INITIATOR) && - (this->other_vips->get_count(this->other_vips) != 0 || + (array_count(this->other_vips) != 0 || has_condition(this, COND_EAP_AUTHENTICATED) #ifdef ME || this->is_mediation_server @@ -1620,7 +1660,7 @@ METHOD(ike_sa_t, reestablish, status_t, host = this->my_host; new->set_my_host(new, host->clone(host)); /* if we already have a virtual IP, we reuse it */ - enumerator = this->my_vips->create_enumerator(this->my_vips); + enumerator = array_create_enumerator(this->my_vips); while (enumerator->enumerate(enumerator, &host)) { new->add_virtual_ip(new, TRUE, host); @@ -1635,7 +1675,8 @@ METHOD(ike_sa_t, reestablish, status_t, else #endif /* ME */ { - enumerator = this->child_sas->create_enumerator(this->child_sas); + /* handle existing CHILD_SAs */ + enumerator = array_create_enumerator(this->child_sas); while (enumerator->enumerate(enumerator, (void**)&child_sa)) { if (has_condition(this, COND_REAUTHENTICATING)) @@ -1644,7 +1685,7 @@ METHOD(ike_sa_t, reestablish, status_t, { case CHILD_ROUTED: { /* move routed child directly */ - this->child_sas->remove_at(this->child_sas, enumerator); + array_remove_at(this->child_sas, enumerator); new->add_child_sa(new, child_sa); action = ACTION_NONE; break; @@ -1674,7 +1715,8 @@ METHOD(ike_sa_t, reestablish, status_t, DBG1(DBG_IKE, "restarting CHILD_SA %s", child_cfg->get_name(child_cfg)); child_cfg->get_ref(child_cfg); - status = new->initiate(new, child_cfg, 0, NULL, NULL); + status = new->initiate(new, child_cfg, + child_sa->get_reqid(child_sa), NULL, NULL); break; default: continue; @@ -1685,6 +1727,16 @@ METHOD(ike_sa_t, reestablish, status_t, } } enumerator->destroy(enumerator); + /* adopt any active or queued CHILD-creating tasks */ + if (status != DESTROY_ME) + { + task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager; + other_tasks->adopt_child_tasks(other_tasks, this->task_manager); + if (new->get_state(new) == IKE_CREATED) + { + status = new->initiate(new, NULL, 0, NULL, NULL); + } + } } if (status == DESTROY_ME) @@ -1774,7 +1826,7 @@ METHOD(ike_sa_t, set_auth_lifetime, status_t, * We send the notify in IKE_AUTH if not yet ESTABLISHED. */ send_update = this->state == IKE_ESTABLISHED && this->version == IKEV2 && !has_condition(this, COND_ORIGINAL_INITIATOR) && - (this->other_vips->get_count(this->other_vips) != 0 || + (array_count(this->other_vips) != 0 || has_condition(this, COND_EAP_AUTHENTICATED)); if (lifetime < diff) @@ -1948,13 +2000,12 @@ METHOD(ike_sa_t, add_configuration_attribute, void, private_ike_sa_t *this, attribute_handler_t *handler, configuration_attribute_type_t type, chunk_t data) { - attribute_entry_t *entry = malloc_thing(attribute_entry_t); - - entry->handler = handler; - entry->type = type; - entry->data = chunk_clone(data); - - this->attributes->insert_last(this->attributes, entry); + attribute_entry_t entry = { + .handler = handler, + .type = type, + .data = chunk_clone(data), + }; + array_insert(this->attributes, ARRAY_TAIL, &entry); } METHOD(ike_sa_t, create_task_enumerator, enumerator_t*, @@ -1980,8 +2031,8 @@ METHOD(ike_sa_t, inherit, void, { private_ike_sa_t *other = (private_ike_sa_t*)other_public; child_sa_t *child_sa; - attribute_entry_t *entry; enumerator_t *enumerator; + attribute_entry_t entry; auth_cfg_t *cfg; host_t *vip; @@ -1996,35 +2047,33 @@ METHOD(ike_sa_t, inherit, void, this->other_id = other->other_id->clone(other->other_id); /* apply assigned virtual IPs... */ - while (other->my_vips->remove_last(other->my_vips, (void**)&vip) == SUCCESS) + while (array_remove(other->my_vips, ARRAY_HEAD, &vip)) { - this->my_vips->insert_first(this->my_vips, vip); + array_insert_create(&this->my_vips, ARRAY_TAIL, vip); } - while (other->other_vips->remove_last(other->other_vips, - (void**)&vip) == SUCCESS) + while (array_remove(other->other_vips, ARRAY_HEAD, &vip)) { - this->other_vips->insert_first(this->other_vips, vip); + array_insert_create(&this->other_vips, ARRAY_TAIL, vip); } /* authentication information */ - enumerator = other->my_auths->create_enumerator(other->my_auths); + enumerator = array_create_enumerator(other->my_auths); while (enumerator->enumerate(enumerator, &cfg)) { - this->my_auths->insert_last(this->my_auths, cfg->clone(cfg)); + array_insert(this->my_auths, ARRAY_TAIL, cfg->clone(cfg)); } enumerator->destroy(enumerator); - enumerator = other->other_auths->create_enumerator(other->other_auths); + enumerator = array_create_enumerator(other->other_auths); while (enumerator->enumerate(enumerator, &cfg)) { - this->other_auths->insert_last(this->other_auths, cfg->clone(cfg)); + array_insert(this->other_auths, ARRAY_TAIL, cfg->clone(cfg)); } enumerator->destroy(enumerator); /* ... and configuration attributes */ - while (other->attributes->remove_last(other->attributes, - (void**)&entry) == SUCCESS) + while (array_remove(other->attributes, ARRAY_HEAD, &entry)) { - this->attributes->insert_first(this->attributes, entry); + array_insert(this->attributes, ARRAY_TAIL, &entry); } /* inherit all conditions */ @@ -2047,10 +2096,9 @@ METHOD(ike_sa_t, inherit, void, #endif /* ME */ /* adopt all children */ - while (other->child_sas->remove_last(other->child_sas, - (void**)&child_sa) == SUCCESS) + while (array_remove(other->child_sas, ARRAY_HEAD, &child_sa)) { - this->child_sas->insert_first(this->child_sas, (void*)child_sa); + array_insert_create(&this->child_sas, ARRAY_TAIL, child_sa); } /* move pending tasks to the new IKE_SA */ @@ -2077,7 +2125,8 @@ METHOD(ike_sa_t, inherit, void, METHOD(ike_sa_t, destroy, void, private_ike_sa_t *this) { - attribute_entry_t *entry; + attribute_entry_t entry; + child_sa_t *child_sa; host_t *vip; charon->bus->set_sa(charon->bus, &this->public); @@ -2086,35 +2135,28 @@ METHOD(ike_sa_t, destroy, void, DESTROY_IF(this->task_manager); /* remove attributes first, as we pass the IKE_SA to the handler */ - while (this->attributes->remove_last(this->attributes, - (void**)&entry) == SUCCESS) + while (array_remove(this->attributes, ARRAY_TAIL, &entry)) { - hydra->attributes->release(hydra->attributes, entry->handler, - this->other_id, entry->type, entry->data); - free(entry->data.ptr); - free(entry); + hydra->attributes->release(hydra->attributes, entry.handler, + this->other_id, entry.type, entry.data); + free(entry.data.ptr); } - this->attributes->destroy(this->attributes); - - this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy)); - - /* unset SA after here to avoid usage by the listeners */ - charon->bus->set_sa(charon->bus, NULL); - - DESTROY_IF(this->keymat); - - while (this->my_vips->remove_last(this->my_vips, (void**)&vip) == SUCCESS) + /* uninstall CHILD_SAs before virtual IPs, otherwise we might kill + * routes that the CHILD_SA tries to uninstall. */ + while (array_remove(this->child_sas, ARRAY_TAIL, &child_sa)) + { + child_sa->destroy(child_sa); + } + while (array_remove(this->my_vips, ARRAY_TAIL, &vip)) { hydra->kernel_interface->del_ip(hydra->kernel_interface, vip, -1, TRUE); vip->destroy(vip); } - this->my_vips->destroy(this->my_vips); - if (this->other_vips->get_count(this->other_vips)) + if (array_count(this->other_vips)) { charon->bus->assign_vips(charon->bus, &this->public, FALSE); } - while (this->other_vips->remove_last(this->other_vips, - (void**)&vip) == SUCCESS) + while (array_remove(this->other_vips, ARRAY_TAIL, &vip)) { if (this->peer_cfg) { @@ -2129,9 +2171,16 @@ METHOD(ike_sa_t, destroy, void, } vip->destroy(vip); } - this->other_vips->destroy(this->other_vips); - this->peer_addresses->destroy_offset(this->peer_addresses, - offsetof(host_t, destroy)); + + /* unset SA after here to avoid usage by the listeners */ + charon->bus->set_sa(charon->bus, NULL); + + array_destroy(this->child_sas); + DESTROY_IF(this->keymat); + array_destroy(this->attributes); + array_destroy(this->my_vips); + array_destroy(this->other_vips); + array_destroy_offset(this->peer_addresses, offsetof(host_t, destroy)); #ifdef ME if (this->is_mediation_server) { @@ -2155,10 +2204,8 @@ METHOD(ike_sa_t, destroy, void, DESTROY_IF(this->proposal); this->my_auth->destroy(this->my_auth); this->other_auth->destroy(this->other_auth); - this->my_auths->destroy_offset(this->my_auths, - offsetof(auth_cfg_t, destroy)); - this->other_auths->destroy_offset(this->other_auths, - offsetof(auth_cfg_t, destroy)); + array_destroy_offset(this->my_auths, offsetof(auth_cfg_t, destroy)); + array_destroy_offset(this->other_auths, offsetof(auth_cfg_t, destroy)); this->ike_sa_id->destroy(this->ike_sa_id); free(this); @@ -2171,7 +2218,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, ike_version_t version) { private_ike_sa_t *this; - static u_int32_t unique_id = 0; + static refcount_t unique_id = 0; if (version == IKE_ANY) { /* prefer IKEv2 if protocol not specified */ @@ -2270,7 +2317,6 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, }, .ike_sa_id = ike_sa_id->clone(ike_sa_id), .version = version, - .child_sas = linked_list_create(), .my_host = host_create_any(AF_INET), .other_host = host_create_any(AF_INET), .my_id = identification_create_from_encoding(ID_ANY, chunk_empty), @@ -2281,13 +2327,10 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .stats[STAT_OUTBOUND] = time_monotonic(NULL), .my_auth = auth_cfg_create(), .other_auth = auth_cfg_create(), - .my_auths = linked_list_create(), - .other_auths = linked_list_create(), - .unique_id = ++unique_id, - .peer_addresses = linked_list_create(), - .my_vips = linked_list_create(), - .other_vips = linked_list_create(), - .attributes = linked_list_create(), + .my_auths = array_create(0, 0), + .other_auths = array_create(0, 0), + .attributes = array_create(sizeof(attribute_entry_t), 0), + .unique_id = ref_get(&unique_id), .keepalive_interval = lib->settings->get_time(lib->settings, "%s.keep_alive", KEEPALIVE_INTERVAL, charon->name), .retry_initiate_interval = lib->settings->get_time(lib->settings, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 625859a3f..00c16c05e 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -200,11 +200,11 @@ enum ike_condition_t { enum statistic_t { /** Timestamp of SA establishement */ STAT_ESTABLISHED = 0, - /** Timestamp of scheudled rekeying */ + /** Timestamp of scheduled rekeying */ STAT_REKEY, - /** Timestamp of scheudled reauthentication */ + /** Timestamp of scheduled reauthentication */ STAT_REAUTH, - /** Timestamp of scheudled delete */ + /** Timestamp of scheduled delete */ STAT_DELETE, /** Timestamp of last inbound IKE packet */ STAT_INBOUND, @@ -812,10 +812,8 @@ struct ike_sa_t { /** * Sends a keep alive packet. * - * To refresh NAT tables in a NAT router - * between the peers, periodic empty - * UDP packets are sent if no other traffic - * was sent. + * To refresh NAT tables in a NAT router between the peers, periodic empty + * UDP packets are sent if no other traffic was sent. */ void (*send_keepalive) (ike_sa_t *this); diff --git a/src/libcharon/sa/ike_sa_id.h b/src/libcharon/sa/ike_sa_id.h index 227683d1c..5eb754e95 100644 --- a/src/libcharon/sa/ike_sa_id.h +++ b/src/libcharon/sa/ike_sa_id.h @@ -86,7 +86,7 @@ struct ike_sa_id_t { bool (*equals) (ike_sa_id_t *this, ike_sa_id_t *other); /** - * Replace all values of a given ike_sa_id_t object with values. + * Replace all values of a given ike_sa_id_t object with values * from another ike_sa_id_t object. * * After calling this function, both objects are equal. diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 709033cb5..857cb027e 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1753,21 +1753,22 @@ METHOD(task_manager_t, queue_child, void, /** * Check if two CHILD_SAs have the same traffic selector */ -static bool have_equal_ts(child_sa_t *a, child_sa_t *b, bool local) +static bool have_equal_ts(child_sa_t *child1, child_sa_t *child2, bool local) { - linked_list_t *list; - traffic_selector_t *ts_a, *ts_b; + enumerator_t *e1, *e2; + traffic_selector_t *ts1, *ts2; + bool equal = FALSE; - list = a->get_traffic_selectors(a, local); - if (list->get_first(list, (void**)&ts_a) == SUCCESS) + e1 = child1->create_ts_enumerator(child1, local); + e2 = child2->create_ts_enumerator(child2, local); + if (e1->enumerate(e1, &ts1) && e2->enumerate(e2, &ts2)) { - list = b->get_traffic_selectors(b, local); - if (list->get_first(list, (void**)&ts_b) == SUCCESS) - { - return ts_a->equals(ts_a, ts_b); - } + equal = ts1->equals(ts1, ts2); } - return FALSE; + e1->destroy(e1); + e1->destroy(e1); + + return equal; } /** @@ -1806,14 +1807,13 @@ static bool is_redundant(private_task_manager_t *this, child_sa_t *child_sa) static traffic_selector_t* get_first_ts(child_sa_t *child_sa, bool local) { traffic_selector_t *ts = NULL; - linked_list_t *list; + enumerator_t *enumerator; - list = child_sa->get_traffic_selectors(child_sa, local); - if (list->get_first(list, (void**)&ts) == SUCCESS) - { - return ts; - } - return NULL; + enumerator = child_sa->create_ts_enumerator(child_sa, local); + enumerator->enumerate(enumerator, &ts); + enumerator->destroy(enumerator); + + return ts; } METHOD(task_manager_t, queue_child_rekey, void, @@ -1900,6 +1900,39 @@ METHOD(task_manager_t, adopt_tasks, void, } } +/** + * Migrates child-creating tasks from src to dst + */ +static void migrate_child_tasks(private_task_manager_t *this, + linked_list_t *src, linked_list_t *dst) +{ + enumerator_t *enumerator; + task_t *task; + + enumerator = src->create_enumerator(src); + while (enumerator->enumerate(enumerator, &task)) + { + if (task->get_type(task) == TASK_QUICK_MODE) + { + src->remove_at(src, enumerator); + task->migrate(task, this->ike_sa); + dst->insert_last(dst, task); + } + } + enumerator->destroy(enumerator); +} + +METHOD(task_manager_t, adopt_child_tasks, void, + private_task_manager_t *this, task_manager_t *other_public) +{ + private_task_manager_t *other = (private_task_manager_t*)other_public; + + /* move active child tasks from other to this */ + migrate_child_tasks(this, other->active_tasks, this->queued_tasks); + /* do the same for queued tasks */ + migrate_child_tasks(this, other->queued_tasks, this->queued_tasks); +} + METHOD(task_manager_t, busy, bool, private_task_manager_t *this) { @@ -2014,6 +2047,7 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .incr_mid = _incr_mid, .reset = _reset, .adopt_tasks = _adopt_tasks, + .adopt_child_tasks = _adopt_child_tasks, .busy = _busy, .create_task_enumerator = _create_task_enumerator, .flush_queue = _flush_queue, diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_delete.c b/src/libcharon/sa/ikev1/tasks/isakmp_delete.c index 0640d13b1..a44f3c4a9 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_delete.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_delete.c @@ -85,6 +85,11 @@ METHOD(task_t, process_r, status_t, this->ike_sa->get_other_host(this->ike_sa), this->ike_sa->get_other_id(this->ike_sa)); + if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) + { + this->ike_sa->set_state(this->ike_sa, IKE_DELETING); + this->ike_sa->reestablish(this->ike_sa); + } this->ike_sa->set_state(this->ike_sa, IKE_DELETING); charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); return DESTROY_ME; diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c index 5a779ff62..fc6ac0771 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c @@ -97,6 +97,20 @@ struct private_isakmp_natd_t { }; /** + * Check if UDP encapsulation has to be forced either by config or required + * by the kernel interface + */ +static bool force_encap(ike_cfg_t *ike_cfg) +{ + if (!ike_cfg->force_encap(ike_cfg)) + { + return hydra->kernel_interface->get_features(hydra->kernel_interface) & + KERNEL_REQUIRE_UDP_ENCAPSULATION; + } + return TRUE; +} + +/** * Get NAT-D payload type (RFC 3947 or RFC 3947 drafts). */ static payload_type_t get_nat_d_payload_type(ike_sa_t *ike_sa) @@ -183,7 +197,7 @@ static hash_payload_t *build_natd_payload(private_isakmp_natd_t *this, bool src, chunk_t hash; config = this->ike_sa->get_ike_cfg(this->ike_sa); - if (src && config->force_encap(config)) + if (src && force_encap(config)) { hash = generate_natd_hash_faked(this); } @@ -297,7 +311,7 @@ static void process_payloads(private_isakmp_natd_t *this, message_t *message) !this->src_matched); config = this->ike_sa->get_ike_cfg(this->ike_sa); if (this->dst_matched && this->src_matched && - config->force_encap(config)) + force_encap(config)) { this->ike_sa->set_condition(this->ike_sa, COND_NAT_FAKE, TRUE); } diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c index 2ff2b55e9..11155b287 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c @@ -67,6 +67,11 @@ struct private_isakmp_vendor_t { * Index of best nat traversal VID found */ int best_natt_ext; + + /** + * Number of times we have been invoked + */ + int count; }; /** @@ -175,8 +180,10 @@ static bool fragmentation_supported(chunk_t data, int i) return FALSE; } -METHOD(task_t, build, status_t, - private_isakmp_vendor_t *this, message_t *message) +/** + * Add supported vendor ID payloads + */ +static void build(private_isakmp_vendor_t *this, message_t *message) { vendor_id_payload_t *vid_payload; bool strongswan, cisco_unity, fragmentation; @@ -219,11 +226,12 @@ METHOD(task_t, build, status_t, message->add_payload(message, &vid_payload->payload_interface); } } - return this->initiator ? NEED_MORE : SUCCESS; } -METHOD(task_t, process, status_t, - private_isakmp_vendor_t *this, message_t *message) +/** + * Process vendor ID payloads + */ +static void process(private_isakmp_vendor_t *this, message_t *message) { enumerator_t *enumerator; payload_t *payload; @@ -289,14 +297,64 @@ METHOD(task_t, process, status_t, this->ike_sa->enable_extension(this->ike_sa, vendor_natt_ids[this->best_natt_ext].extension); } +} - return this->initiator ? SUCCESS : NEED_MORE; +METHOD(task_t, build_i, status_t, + private_isakmp_vendor_t *this, message_t *message) +{ + if (this->count++ == 0) + { + build(this, message); + } + if (message->get_exchange_type(message) == AGGRESSIVE && this->count > 1) + { + return SUCCESS; + } + return NEED_MORE; +} + +METHOD(task_t, process_r, status_t, + private_isakmp_vendor_t *this, message_t *message) +{ + this->count++; + process(this, message); + if (message->get_exchange_type(message) == AGGRESSIVE && this->count > 1) + { + return SUCCESS; + } + return NEED_MORE; +} + +METHOD(task_t, build_r, status_t, + private_isakmp_vendor_t *this, message_t *message) +{ + if (this->count == 1) + { + build(this, message); + } + if (message->get_exchange_type(message) == ID_PROT && this->count > 2) + { + return SUCCESS; + } + return NEED_MORE; +} + +METHOD(task_t, process_i, status_t, + private_isakmp_vendor_t *this, message_t *message) +{ + process(this, message); + if (message->get_exchange_type(message) == ID_PROT && this->count > 2) + { + return SUCCESS; + } + return NEED_MORE; } METHOD(task_t, migrate, void, private_isakmp_vendor_t *this, ike_sa_t *ike_sa) { this->ike_sa = ike_sa; + this->count = 0; } METHOD(task_t, get_type, task_type_t, @@ -321,8 +379,6 @@ isakmp_vendor_t *isakmp_vendor_create(ike_sa_t *ike_sa, bool initiator) INIT(this, .public = { .task = { - .build = _build, - .process = _process, .migrate = _migrate, .get_type = _get_type, .destroy = _destroy, @@ -333,5 +389,16 @@ isakmp_vendor_t *isakmp_vendor_create(ike_sa_t *ike_sa, bool initiator) .best_natt_ext = -1, ); + if (initiator) + { + this->public.task.build = _build_i; + this->public.task.process = _process_i; + } + else + { + this->public.task.build = _build_r; + this->public.task.process = _process_r; + } + return &this->public; } diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.c b/src/libcharon/sa/ikev1/tasks/quick_delete.c index e9f06cbe3..1a2cdb777 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_delete.c +++ b/src/libcharon/sa/ikev1/tasks/quick_delete.c @@ -12,6 +12,27 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ +/* + * Copyright (C) 2013 Oliver Smith + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ #include "quick_delete.h" @@ -64,11 +85,13 @@ struct private_quick_delete_t { /** * Delete the specified CHILD_SA, if found */ -static bool delete_child(private_quick_delete_t *this, - protocol_id_t protocol, u_int32_t spi) +static bool delete_child(private_quick_delete_t *this, protocol_id_t protocol, + u_int32_t spi, bool remote_close) { u_int64_t bytes_in, bytes_out; child_sa_t *child_sa; + linked_list_t *my_ts, *other_ts; + child_cfg_t *child_cfg; bool rekeyed; child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, TRUE); @@ -85,15 +108,17 @@ static bool delete_child(private_quick_delete_t *this, rekeyed = child_sa->get_state(child_sa) == CHILD_REKEYING; child_sa->set_state(child_sa, CHILD_DELETING); + my_ts = linked_list_create_from_enumerator( + child_sa->create_ts_enumerator(child_sa, TRUE)); + other_ts = linked_list_create_from_enumerator( + child_sa->create_ts_enumerator(child_sa, FALSE)); if (this->expired) { DBG0(DBG_IKE, "closing expired CHILD_SA %s{%d} " "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", child_sa->get_name(child_sa), child_sa->get_reqid(child_sa), ntohl(child_sa->get_spi(child_sa, TRUE)), - ntohl(child_sa->get_spi(child_sa, FALSE)), - child_sa->get_traffic_selectors(child_sa, TRUE), - child_sa->get_traffic_selectors(child_sa, FALSE)); + ntohl(child_sa->get_spi(child_sa, FALSE)), my_ts, other_ts); } else { @@ -105,18 +130,39 @@ static bool delete_child(private_quick_delete_t *this, child_sa->get_name(child_sa), child_sa->get_reqid(child_sa), ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in, ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out, - child_sa->get_traffic_selectors(child_sa, TRUE), - child_sa->get_traffic_selectors(child_sa, FALSE)); + my_ts, other_ts); } + my_ts->destroy(my_ts); + other_ts->destroy(other_ts); if (!rekeyed) { charon->bus->child_updown(charon->bus, child_sa, FALSE); - } - this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); + if (remote_close) + { + child_cfg = child_sa->get_config(child_sa); + child_cfg->get_ref(child_cfg); - /* TODO-IKEv1: handle close action? */ + switch (child_sa->get_close_action(child_sa)) + { + case ACTION_RESTART: + child_cfg->get_ref(child_cfg); + this->ike_sa->initiate(this->ike_sa, child_cfg, + child_sa->get_reqid(child_sa), NULL, NULL); + break; + case ACTION_ROUTE: + charon->traps->install(charon->traps, + this->ike_sa->get_peer_cfg(this->ike_sa), + child_cfg, child_sa->get_reqid(child_sa)); + break; + default: + break; + } + child_cfg->destroy(child_cfg); + } + } + this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); return TRUE; } @@ -124,7 +170,7 @@ static bool delete_child(private_quick_delete_t *this, METHOD(task_t, build_i, status_t, private_quick_delete_t *this, message_t *message) { - if (delete_child(this, this->protocol, this->spi) || this->force) + if (delete_child(this, this->protocol, this->spi, FALSE) || this->force) { delete_payload_t *delete_payload; @@ -172,7 +218,7 @@ METHOD(task_t, process_r, status_t, { DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x", protocol_id_names, protocol, ntohl(spi)); - if (!delete_child(this, protocol, spi)) + if (!delete_child(this, protocol, spi, TRUE)) { DBG1(DBG_IKE, "CHILD_SA not found, ignored"); continue; diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 7a0fb5788..6271e5b05 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -259,7 +259,7 @@ static bool install(private_quick_mode_t *this) { status_t status, status_i, status_o; chunk_t encr_i, encr_r, integ_i, integ_r; - linked_list_t *tsi, *tsr; + linked_list_t *tsi, *tsr, *my_ts, *other_ts; child_sa_t *old = NULL; this->child_sa->set_proposal(this->child_sa, this->proposal); @@ -306,17 +306,21 @@ static bool install(private_quick_mode_t *this) { if (this->initiator) { - status_i = this->child_sa->install(this->child_sa, encr_r, integ_r, - this->spi_i, this->cpi_i, TRUE, FALSE, tsi, tsr); - status_o = this->child_sa->install(this->child_sa, encr_i, integ_i, - this->spi_r, this->cpi_r, FALSE, FALSE, tsi, tsr); + status_i = this->child_sa->install(this->child_sa, + encr_r, integ_r, this->spi_i, this->cpi_i, + this->initiator, TRUE, FALSE, tsi, tsr); + status_o = this->child_sa->install(this->child_sa, + encr_i, integ_i, this->spi_r, this->cpi_r, + this->initiator, FALSE, FALSE, tsi, tsr); } else { - status_i = this->child_sa->install(this->child_sa, encr_i, integ_i, - this->spi_r, this->cpi_r, TRUE, FALSE, tsr, tsi); - status_o = this->child_sa->install(this->child_sa, encr_r, integ_r, - this->spi_i, this->cpi_i, FALSE, FALSE, tsr, tsi); + status_i = this->child_sa->install(this->child_sa, + encr_i, integ_i, this->spi_r, this->cpi_r, + this->initiator, TRUE, FALSE, tsr, tsi); + status_o = this->child_sa->install(this->child_sa, + encr_r, integ_r, this->spi_i, this->cpi_i, + this->initiator, FALSE, FALSE, tsr, tsi); } } chunk_clear(&integ_i); @@ -358,14 +362,20 @@ static bool install(private_quick_mode_t *this) this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); + my_ts = linked_list_create_from_enumerator( + this->child_sa->create_ts_enumerator(this->child_sa, TRUE)); + other_ts = linked_list_create_from_enumerator( + this->child_sa->create_ts_enumerator(this->child_sa, FALSE)); + DBG0(DBG_IKE, "CHILD_SA %s{%d} established " "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", this->child_sa->get_name(this->child_sa), this->child_sa->get_reqid(this->child_sa), ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), - ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), - this->child_sa->get_traffic_selectors(this->child_sa, TRUE), - this->child_sa->get_traffic_selectors(this->child_sa, FALSE)); + ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), my_ts, other_ts); + + my_ts->destroy(my_ts); + other_ts->destroy(other_ts); if (this->rekey) { @@ -500,33 +510,11 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local, static void add_ts(private_quick_mode_t *this, message_t *message) { id_payload_t *id_payload; - host_t *hsi, *hsr; - if (this->initiator) - { - hsi = this->ike_sa->get_my_host(this->ike_sa); - hsr = this->ike_sa->get_other_host(this->ike_sa); - } - else - { - hsr = this->ike_sa->get_my_host(this->ike_sa); - hsi = this->ike_sa->get_other_host(this->ike_sa); - } - /* add ID payload only if negotiating non host2host tunnels */ - if (!this->tsi->is_host(this->tsi, hsi) || - !this->tsr->is_host(this->tsr, hsr) || - this->tsi->get_protocol(this->tsi) || - this->tsr->get_protocol(this->tsr) || - this->tsi->get_from_port(this->tsi) || - this->tsr->get_from_port(this->tsr) || - this->tsi->get_to_port(this->tsi) != 65535 || - this->tsr->get_to_port(this->tsr) != 65535) - { - id_payload = id_payload_create_from_ts(this->tsi); - message->add_payload(message, &id_payload->payload_interface); - id_payload = id_payload_create_from_ts(this->tsr); - message->add_payload(message, &id_payload->payload_interface); - } + id_payload = id_payload_create_from_ts(this->tsi); + message->add_payload(message, &id_payload->payload_interface); + id_payload = id_payload_create_from_ts(this->tsr); + message->add_payload(message, &id_payload->payload_interface); } /** @@ -774,19 +762,11 @@ METHOD(task_t, build_i, status_t, if (this->config->use_ipcomp(this->config)) { - if (this->udp) - { - DBG1(DBG_IKE, "IPComp is not supported if either peer is " - "natted, IPComp disabled"); - } - else + this->cpi_i = this->child_sa->alloc_cpi(this->child_sa); + if (!this->cpi_i) { - this->cpi_i = this->child_sa->alloc_cpi(this->child_sa); - if (!this->cpi_i) - { - DBG1(DBG_IKE, "unable to allocate a CPI from kernel, " - "IPComp disabled"); - } + DBG1(DBG_IKE, "unable to allocate a CPI from kernel, " + "IPComp disabled"); } } @@ -1009,21 +989,13 @@ METHOD(task_t, process_r, status_t, if (this->config->use_ipcomp(this->config)) { - 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"); - } - else + list = sa_payload->get_ipcomp_proposals(sa_payload, + &this->cpi_i); + if (!list->get_count(list)) { - list = sa_payload->get_ipcomp_proposals(sa_payload, - &this->cpi_i); - if (!list->get_count(list)) - { - DBG1(DBG_IKE, "expected IPComp proposal but peer did " - "not send one, IPComp disabled"); - this->cpi_i = 0; - } + DBG1(DBG_IKE, "expected IPComp proposal but peer did " + "not send one, IPComp disabled"); + this->cpi_i = 0; } } if (!list || !list->get_count(list)) diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 5298abf79..a6af744fc 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -18,6 +18,7 @@ #include <math.h> +#include <collections/array.h> #include <daemon.h> #include <sa/ikev2/tasks/ike_init.h> #include <sa/ikev2/tasks/ike_natd.h> @@ -122,19 +123,19 @@ struct private_task_manager_t { } initiating; /** - * List of queued tasks not yet in action + * Array of queued tasks not yet in action */ - linked_list_t *queued_tasks; + array_t *queued_tasks; /** - * List of active tasks, initiated by ourselve + * Array of active tasks, initiated by ourselve */ - linked_list_t *active_tasks; + array_t *active_tasks; /** - * List of tasks initiated by peer + * Array of tasks initiated by peer */ - linked_list_t *passive_tasks; + array_t *passive_tasks; /** * the task manager has been reset @@ -160,24 +161,24 @@ struct private_task_manager_t { METHOD(task_manager_t, flush_queue, void, private_task_manager_t *this, task_queue_t queue) { - linked_list_t *list; + array_t *array; task_t *task; switch (queue) { case TASK_QUEUE_ACTIVE: - list = this->active_tasks; + array = this->active_tasks; break; case TASK_QUEUE_PASSIVE: - list = this->passive_tasks; + array = this->passive_tasks; break; case TASK_QUEUE_QUEUED: - list = this->queued_tasks; + array = this->queued_tasks; break; default: return; } - while (list->remove_last(list, (void**)&task) == SUCCESS) + while (array_remove(array, ARRAY_TAIL, &task)) { task->destroy(task); } @@ -202,14 +203,14 @@ static bool activate_task(private_task_manager_t *this, task_type_t type) task_t *task; bool found = FALSE; - enumerator = this->queued_tasks->create_enumerator(this->queued_tasks); + enumerator = array_create_enumerator(this->queued_tasks); while (enumerator->enumerate(enumerator, (void**)&task)) { if (task->get_type(task) == type) { DBG2(DBG_IKE, " activating %N task", task_type_names, type); - this->queued_tasks->remove_at(this->queued_tasks, enumerator); - this->active_tasks->insert_last(this->active_tasks, task); + array_remove_at(this->queued_tasks, enumerator); + array_insert(this->active_tasks, ARRAY_TAIL, task); found = TRUE; break; } @@ -231,7 +232,7 @@ METHOD(task_manager_t, retransmit, status_t, ike_mobike_t *mobike = NULL; /* check if we are retransmitting a MOBIKE routability check */ - enumerator = this->active_tasks->create_enumerator(this->active_tasks); + enumerator = array_create_enumerator(this->active_tasks); while (enumerator->enumerate(enumerator, (void*)&task)) { if (task->get_type(task) == TASK_IKE_MOBIKE) @@ -319,7 +320,7 @@ METHOD(task_manager_t, initiate, status_t, return SUCCESS; } - if (this->active_tasks->get_count(this->active_tasks) == 0) + if (array_count(this->active_tasks) == 0) { DBG2(DBG_IKE, "activating new tasks"); switch (this->ike_sa->get_state(this->ike_sa)) @@ -414,8 +415,8 @@ METHOD(task_manager_t, initiate, status_t, else { DBG2(DBG_IKE, "reinitiating already active tasks"); - enumerator = this->active_tasks->create_enumerator(this->active_tasks); - while (enumerator->enumerate(enumerator, (void**)&task)) + enumerator = array_create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, &task)) { DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task)); switch (task->get_type(task)) @@ -460,14 +461,14 @@ METHOD(task_manager_t, initiate, status_t, this->initiating.type = exchange; this->initiating.retransmitted = 0; - enumerator = this->active_tasks->create_enumerator(this->active_tasks); - while (enumerator->enumerate(enumerator, (void*)&task)) + enumerator = array_create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, &task)) { switch (task->build(task, message)) { case SUCCESS: /* task completed, remove it */ - this->active_tasks->remove_at(this->active_tasks, enumerator); + array_remove_at(this->active_tasks, enumerator); task->destroy(task); break; case NEED_MORE: @@ -507,6 +508,9 @@ METHOD(task_manager_t, initiate, status_t, } message->destroy(message); + array_compress(this->active_tasks); + array_compress(this->queued_tasks); + return retransmit(this, this->initiating.mid); } @@ -530,14 +534,14 @@ static status_t process_response(private_task_manager_t *this, /* catch if we get resetted while processing */ this->reset = FALSE; - enumerator = this->active_tasks->create_enumerator(this->active_tasks); - while (enumerator->enumerate(enumerator, (void*)&task)) + enumerator = array_create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, &task)) { switch (task->process(task, message)) { case SUCCESS: /* task completed, remove it */ - this->active_tasks->remove_at(this->active_tasks, enumerator); + array_remove_at(this->active_tasks, enumerator); task->destroy(task); break; case NEED_MORE: @@ -549,7 +553,7 @@ static status_t process_response(private_task_manager_t *this, /* FALL */ case DESTROY_ME: /* critical failure, destroy IKE_SA */ - this->active_tasks->remove_at(this->active_tasks, enumerator); + array_remove_at(this->active_tasks, enumerator); enumerator->destroy(enumerator); task->destroy(task); return DESTROY_ME; @@ -568,6 +572,8 @@ static status_t process_response(private_task_manager_t *this, this->initiating.packet->destroy(this->initiating.packet); this->initiating.packet = NULL; + array_compress(this->active_tasks); + return initiate(this); } @@ -588,8 +594,8 @@ static bool handle_collisions(private_task_manager_t *this, task_t *task) type == TASK_IKE_REAUTH) { /* find an exchange collision, and notify these tasks */ - enumerator = this->active_tasks->create_enumerator(this->active_tasks); - while (enumerator->enumerate(enumerator, (void**)&active)) + enumerator = array_create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, &active)) { switch (active->get_type(active)) { @@ -646,14 +652,14 @@ static status_t build_response(private_task_manager_t *this, message_t *request) message->set_message_id(message, this->responding.mid); message->set_request(message, FALSE); - enumerator = this->passive_tasks->create_enumerator(this->passive_tasks); + enumerator = array_create_enumerator(this->passive_tasks); while (enumerator->enumerate(enumerator, (void*)&task)) { switch (task->build(task, message)) { case SUCCESS: /* task completed, remove it */ - this->passive_tasks->remove_at(this->passive_tasks, enumerator); + array_remove_at(this->passive_tasks, enumerator); if (!handle_collisions(this, task)) { task->destroy(task); @@ -663,8 +669,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request) /* processed, but task needs another exchange */ if (handle_collisions(this, task)) { - this->passive_tasks->remove_at(this->passive_tasks, - enumerator); + array_remove_at(this->passive_tasks, enumerator); } break; case FAILED: @@ -721,6 +726,9 @@ static status_t build_response(private_task_manager_t *this, message_t *request) } return DESTROY_ME; } + + array_compress(this->passive_tasks); + return SUCCESS; } @@ -736,37 +744,37 @@ static status_t process_request(private_task_manager_t *this, notify_payload_t *notify; delete_payload_t *delete; - if (this->passive_tasks->get_count(this->passive_tasks) == 0) + if (array_count(this->passive_tasks) == 0) { /* create tasks depending on request type, if not already some queued */ switch (message->get_exchange_type(message)) { case IKE_SA_INIT: { task = (task_t*)ike_vendor_create(this->ike_sa, FALSE); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); task = (task_t*)ike_natd_create(this->ike_sa, FALSE); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); task = (task_t*)ike_cert_pre_create(this->ike_sa, FALSE); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); #ifdef ME task = (task_t*)ike_me_create(this->ike_sa, FALSE); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); #endif /* ME */ task = (task_t*)ike_auth_create(this->ike_sa, FALSE); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); task = (task_t*)ike_cert_post_create(this->ike_sa, FALSE); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); task = (task_t*)ike_config_create(this->ike_sa, FALSE); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); task = (task_t*)child_create_create(this->ike_sa, NULL, FALSE, NULL, NULL); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); task = (task_t*)ike_auth_lifetime_create(this->ike_sa, FALSE); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); task = (task_t*)ike_mobike_create(this->ike_sa, FALSE); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); break; } case CREATE_CHILD_SA: @@ -817,7 +825,7 @@ static status_t process_request(private_task_manager_t *this, { task = (task_t*)ike_rekey_create(this->ike_sa, FALSE); } - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); break; } case INFORMATIONAL: @@ -849,6 +857,12 @@ static status_t process_request(private_task_manager_t *this, task = (task_t*)ike_auth_lifetime_create( this->ike_sa, FALSE); break; + case AUTHENTICATION_FAILED: + /* initiator failed to authenticate us. + * We use ike_delete to handle this, which + * invokes all the required hooks. */ + task = (task_t*)ike_delete_create( + this->ike_sa, FALSE); default: break; } @@ -883,14 +897,14 @@ static status_t process_request(private_task_manager_t *this, { task = (task_t*)ike_dpd_create(FALSE); } - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); break; } #ifdef ME case ME_CONNECT: { task = (task_t*)ike_me_create(this->ike_sa, FALSE); - this->passive_tasks->insert_last(this->passive_tasks, task); + array_insert(this->passive_tasks, ARRAY_TAIL, task); } #endif /* ME */ default: @@ -899,14 +913,14 @@ static status_t process_request(private_task_manager_t *this, } /* let the tasks process the message */ - enumerator = this->passive_tasks->create_enumerator(this->passive_tasks); + enumerator = array_create_enumerator(this->passive_tasks); while (enumerator->enumerate(enumerator, (void*)&task)) { switch (task->process(task, message)) { case SUCCESS: /* task completed, remove it */ - this->passive_tasks->remove_at(this->passive_tasks, enumerator); + array_remove_at(this->passive_tasks, enumerator); task->destroy(task); break; case NEED_MORE: @@ -918,7 +932,7 @@ static status_t process_request(private_task_manager_t *this, /* FALL */ case DESTROY_ME: /* critical failure, destroy IKE_SA */ - this->passive_tasks->remove_at(this->passive_tasks, enumerator); + array_remove_at(this->passive_tasks, enumerator); enumerator->destroy(enumerator); task->destroy(task); return DESTROY_ME; @@ -1078,6 +1092,7 @@ METHOD(task_manager_t, process_message, status_t, host_t *me, *other; status_t status; u_int32_t mid; + bool schedule_delete_job = FALSE; charon->bus->message(charon->bus, msg, TRUE, FALSE); status = parse_message(this, msg); @@ -1092,9 +1107,8 @@ METHOD(task_manager_t, process_message, status_t, /* if this IKE_SA is virgin, we check for a config */ if (this->ike_sa->get_ike_cfg(this->ike_sa) == NULL) { - ike_sa_id_t *ike_sa_id; ike_cfg_t *ike_cfg; - job_t *job; + ike_cfg = charon->backends->get_ike_cfg(charon->backends, me, other, IKEV2); if (ike_cfg == NULL) @@ -1109,12 +1123,7 @@ METHOD(task_manager_t, process_message, status_t, this->ike_sa->set_ike_cfg(this->ike_sa, ike_cfg); ike_cfg->destroy(ike_cfg); /* add a timeout if peer does not establish it completely */ - ike_sa_id = this->ike_sa->get_id(this->ike_sa); - job = (job_t*)delete_ike_sa_job_create(ike_sa_id, FALSE); - lib->scheduler->schedule_job(lib->scheduler, job, - lib->settings->get_int(lib->settings, - "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT, - charon->name)); + schedule_delete_job = TRUE; } this->ike_sa->set_statistic(this->ike_sa, STAT_INBOUND, time_monotonic(NULL)); @@ -1213,6 +1222,19 @@ METHOD(task_manager_t, process_message, status_t, return SUCCESS; } } + + if (schedule_delete_job) + { + ike_sa_id_t *ike_sa_id; + job_t *job; + + ike_sa_id = this->ike_sa->get_id(this->ike_sa); + job = (job_t*)delete_ike_sa_job_create(ike_sa_id, FALSE); + lib->scheduler->schedule_job(lib->scheduler, job, + lib->settings->get_int(lib->settings, + "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT, + charon->name)); + } return SUCCESS; } @@ -1224,8 +1246,8 @@ METHOD(task_manager_t, queue_task, void, enumerator_t *enumerator; task_t *current; - enumerator = this->queued_tasks->create_enumerator(this->queued_tasks); - while (enumerator->enumerate(enumerator, (void**)¤t)) + enumerator = array_create_enumerator(this->queued_tasks); + while (enumerator->enumerate(enumerator, ¤t)) { if (current->get_type(current) == TASK_IKE_MOBIKE) { @@ -1237,7 +1259,7 @@ METHOD(task_manager_t, queue_task, void, enumerator->destroy(enumerator); } DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task)); - this->queued_tasks->insert_last(this->queued_tasks, task); + array_insert(this->queued_tasks, ARRAY_TAIL, task); } /** @@ -1249,7 +1271,7 @@ static bool has_queued(private_task_manager_t *this, task_type_t type) bool found = FALSE; task_t *task; - enumerator = this->queued_tasks->create_enumerator(this->queued_tasks); + enumerator = array_create_enumerator(this->queued_tasks); while (enumerator->enumerate(enumerator, &task)) { if (task->get_type(task) == type) @@ -1404,19 +1426,51 @@ METHOD(task_manager_t, adopt_tasks, void, task_t *task; /* move queued tasks from other to this */ - while (other->queued_tasks->remove_last(other->queued_tasks, - (void**)&task) == SUCCESS) + while (array_remove(other->queued_tasks, ARRAY_TAIL, &task)) { DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task)); task->migrate(task, this->ike_sa); - this->queued_tasks->insert_first(this->queued_tasks, task); + array_insert(this->queued_tasks, ARRAY_HEAD, task); + } +} + +/** + * Migrates child-creating tasks from src to dst + */ +static void migrate_child_tasks(private_task_manager_t *this, + array_t *src, array_t *dst) +{ + enumerator_t *enumerator; + task_t *task; + + enumerator = array_create_enumerator(src); + while (enumerator->enumerate(enumerator, &task)) + { + if (task->get_type(task) == TASK_CHILD_CREATE) + { + array_remove_at(src, enumerator); + task->migrate(task, this->ike_sa); + array_insert(dst, ARRAY_TAIL, task); + } } + enumerator->destroy(enumerator); +} + +METHOD(task_manager_t, adopt_child_tasks, void, + private_task_manager_t *this, task_manager_t *other_public) +{ + private_task_manager_t *other = (private_task_manager_t*)other_public; + + /* move active child tasks from other to this */ + migrate_child_tasks(this, other->active_tasks, this->queued_tasks); + /* do the same for queued tasks */ + migrate_child_tasks(this, other->queued_tasks, this->queued_tasks); } METHOD(task_manager_t, busy, bool, private_task_manager_t *this) { - return (this->active_tasks->get_count(this->active_tasks) > 0); + return array_count(this->active_tasks) > 0; } METHOD(task_manager_t, reset, void, @@ -1441,7 +1495,7 @@ METHOD(task_manager_t, reset, void, this->initiating.type = EXCHANGE_TYPE_UNDEFINED; /* reset queued tasks */ - enumerator = this->queued_tasks->create_enumerator(this->queued_tasks); + enumerator = array_create_enumerator(this->queued_tasks); while (enumerator->enumerate(enumerator, &task)) { task->migrate(task, this->ike_sa); @@ -1449,11 +1503,10 @@ METHOD(task_manager_t, reset, void, enumerator->destroy(enumerator); /* reset active tasks */ - while (this->active_tasks->remove_last(this->active_tasks, - (void**)&task) == SUCCESS) + while (array_remove(this->active_tasks, ARRAY_TAIL, &task)) { task->migrate(task, this->ike_sa); - this->queued_tasks->insert_first(this->queued_tasks, task); + array_insert(this->queued_tasks, ARRAY_HEAD, task); } this->reset = TRUE; @@ -1465,11 +1518,11 @@ METHOD(task_manager_t, create_task_enumerator, enumerator_t*, switch (queue) { case TASK_QUEUE_ACTIVE: - return this->active_tasks->create_enumerator(this->active_tasks); + return array_create_enumerator(this->active_tasks); case TASK_QUEUE_PASSIVE: - return this->passive_tasks->create_enumerator(this->passive_tasks); + return array_create_enumerator(this->passive_tasks); case TASK_QUEUE_QUEUED: - return this->queued_tasks->create_enumerator(this->queued_tasks); + return array_create_enumerator(this->queued_tasks); default: return enumerator_create_empty(); } @@ -1480,9 +1533,9 @@ METHOD(task_manager_t, destroy, void, { flush(this); - this->active_tasks->destroy(this->active_tasks); - this->queued_tasks->destroy(this->queued_tasks); - this->passive_tasks->destroy(this->passive_tasks); + array_destroy(this->active_tasks); + array_destroy(this->queued_tasks); + array_destroy(this->passive_tasks); DESTROY_IF(this->responding.packet); DESTROY_IF(this->initiating.packet); @@ -1515,6 +1568,7 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa) .incr_mid = _incr_mid, .reset = _reset, .adopt_tasks = _adopt_tasks, + .adopt_child_tasks = _adopt_child_tasks, .busy = _busy, .create_task_enumerator = _create_task_enumerator, .flush_queue = _flush_queue, @@ -1523,9 +1577,9 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa) }, .ike_sa = ike_sa, .initiating.type = EXCHANGE_TYPE_UNDEFINED, - .queued_tasks = linked_list_create(), - .active_tasks = linked_list_create(), - .passive_tasks = linked_list_create(), + .queued_tasks = array_create(0, 0), + .active_tasks = array_create(0, 0), + .passive_tasks = array_create(0, 0), .retransmit_tries = lib->settings->get_int(lib->settings, "%s.retransmit_tries", RETRANSMIT_TRIES, charon->name), .retransmit_timeout = lib->settings->get_double(lib->settings, diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 32c0e8c4a..8ae36af84 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -27,6 +27,7 @@ #include <encoding/payloads/ts_payload.h> #include <encoding/payloads/nonce_payload.h> #include <encoding/payloads/notify_payload.h> +#include <encoding/payloads/delete_payload.h> #include <processing/jobs/delete_ike_sa_job.h> #include <processing/jobs/inactivity_job.h> @@ -342,6 +343,79 @@ static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local) } /** + * Substitude any host address with NATed address in traffic selector + */ +static linked_list_t* get_transport_nat_ts(private_child_create_t *this, + bool local, linked_list_t *in) +{ + enumerator_t *enumerator; + linked_list_t *out; + traffic_selector_t *ts; + host_t *ike, *first = NULL; + u_int8_t mask; + + if (local) + { + ike = this->ike_sa->get_my_host(this->ike_sa); + } + else + { + ike = this->ike_sa->get_other_host(this->ike_sa); + } + + out = linked_list_create(); + + enumerator = in->create_enumerator(in); + while (enumerator->enumerate(enumerator, &ts)) + { + /* require that all selectors match the first "host" selector */ + if (ts->is_host(ts, first)) + { + if (!first) + { + ts->to_subnet(ts, &first, &mask); + } + ts = ts->clone(ts); + ts->set_address(ts, ike); + out->insert_last(out, ts); + } + } + enumerator->destroy(enumerator); + DESTROY_IF(first); + + return out; +} + +/** + * Narrow received traffic selectors with configuration + */ +static linked_list_t* narrow_ts(private_child_create_t *this, bool local, + linked_list_t *in) +{ + linked_list_t *hosts, *nat, *ts; + ike_condition_t cond; + + cond = local ? COND_NAT_HERE : COND_NAT_THERE; + hosts = get_dynamic_hosts(this->ike_sa, local); + + if (this->mode == MODE_TRANSPORT && + this->ike_sa->has_condition(this->ike_sa, cond)) + { + nat = get_transport_nat_ts(this, local, in); + ts = this->config->get_traffic_selectors(this->config, local, nat, hosts); + nat->destroy_offset(nat, offsetof(traffic_selector_t, destroy)); + } + else + { + ts = this->config->get_traffic_selectors(this->config, local, in, hosts); + } + + hosts->destroy(hosts); + + return ts; +} + +/** * Install a CHILD_SA for usage, return value: * - FAILED: no acceptable proposal * - INVALID_ARG: diffie hellman group inacceptable @@ -354,7 +428,7 @@ static status_t select_and_install(private_child_create_t *this, chunk_t nonce_i, nonce_r; chunk_t encr_i = chunk_empty, encr_r = chunk_empty; chunk_t integ_i = chunk_empty, integ_r = chunk_empty; - linked_list_t *my_ts, *other_ts, *list; + linked_list_t *my_ts, *other_ts; host_t *me, *other; bool private; @@ -415,24 +489,16 @@ static status_t select_and_install(private_child_create_t *this, { nonce_i = this->my_nonce; nonce_r = this->other_nonce; - my_ts = this->tsi; - other_ts = this->tsr; + my_ts = narrow_ts(this, TRUE, this->tsi); + other_ts = narrow_ts(this, FALSE, this->tsr); } else { nonce_r = this->my_nonce; nonce_i = this->other_nonce; - my_ts = this->tsr; - other_ts = this->tsi; + my_ts = narrow_ts(this, TRUE, this->tsr); + other_ts = narrow_ts(this, FALSE, this->tsi); } - list = get_dynamic_hosts(this->ike_sa, TRUE); - my_ts = this->config->get_traffic_selectors(this->config, - TRUE, my_ts, list); - list->destroy(list); - list = get_dynamic_hosts(this->ike_sa, FALSE); - other_ts = this->config->get_traffic_selectors(this->config, - FALSE, other_ts, list); - list->destroy(list); if (this->initiator) { @@ -489,10 +555,9 @@ static status_t select_and_install(private_child_create_t *this, this->mode = MODE_TUNNEL; DBG1(DBG_IKE, "not using transport mode, not host-to-host"); } - else if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) + if (this->config->get_mode(this->config) != MODE_TRANSPORT) { this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using transport mode, connection NATed"); } break; case MODE_BEET: @@ -502,6 +567,10 @@ static status_t select_and_install(private_child_create_t *this, this->mode = MODE_TUNNEL; DBG1(DBG_IKE, "not using BEET mode, not host-to-host"); } + if (this->config->get_mode(this->config) != MODE_BEET) + { + this->mode = MODE_TUNNEL; + } break; default: break; @@ -525,20 +594,20 @@ static status_t select_and_install(private_child_create_t *this, { if (this->initiator) { - status_i = this->child_sa->install(this->child_sa, - encr_r, integ_r, this->my_spi, this->my_cpi, + status_i = this->child_sa->install(this->child_sa, encr_r, integ_r, + this->my_spi, this->my_cpi, this->initiator, TRUE, this->tfcv3, my_ts, other_ts); - status_o = this->child_sa->install(this->child_sa, - encr_i, integ_i, this->other_spi, this->other_cpi, + status_o = this->child_sa->install(this->child_sa, encr_i, integ_i, + this->other_spi, this->other_cpi, this->initiator, FALSE, this->tfcv3, my_ts, other_ts); } else { - status_i = this->child_sa->install(this->child_sa, - encr_i, integ_i, this->my_spi, this->my_cpi, + status_i = this->child_sa->install(this->child_sa, encr_i, integ_i, + this->my_spi, this->my_cpi, this->initiator, TRUE, this->tfcv3, my_ts, other_ts); - status_o = this->child_sa->install(this->child_sa, - encr_r, integ_r, this->other_spi, this->other_cpi, + status_o = this->child_sa->install(this->child_sa, encr_r, integ_r, + this->other_spi, this->other_cpi, this->initiator, FALSE, this->tfcv3, my_ts, other_ts); } } @@ -604,6 +673,22 @@ static status_t select_and_install(private_child_create_t *this, { /* a rekeyed SA uses the same reqid, no need for a new job */ schedule_inactivity_timeout(this); } + + my_ts = linked_list_create_from_enumerator( + this->child_sa->create_ts_enumerator(this->child_sa, TRUE)); + other_ts = linked_list_create_from_enumerator( + this->child_sa->create_ts_enumerator(this->child_sa, FALSE)); + + DBG0(DBG_IKE, "CHILD_SA %s{%d} established " + "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", + this->child_sa->get_name(this->child_sa), + this->child_sa->get_reqid(this->child_sa), + ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), + ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), my_ts, other_ts); + + my_ts->destroy(my_ts); + other_ts->destroy(other_ts); + return SUCCESS; } @@ -678,13 +763,6 @@ static void build_payloads(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) { - 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"); - return; - } - this->my_cpi = this->child_sa->alloc_cpi(this->child_sa); if (this->my_cpi) { @@ -901,12 +979,6 @@ METHOD(task_t, build_i, status_t, this->proposals = this->config->get_proposals(this->config, this->dh_group == MODP_NONE); this->mode = this->config->get_mode(this->config); - if (this->mode == MODE_TRANSPORT && - this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) - { - this->mode = MODE_TUNNEL; - DBG1(DBG_IKE, "not using transport mode, connection NATed"); - } this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid, @@ -1002,10 +1074,77 @@ static void handle_child_sa_failure(private_child_create_t *this, } } +/** + * Substitute transport mode NAT selectors, if applicable + */ +static linked_list_t* get_ts_if_nat_transport(private_child_create_t *this, + bool local, linked_list_t *in) +{ + linked_list_t *out = NULL; + ike_condition_t cond; + + if (this->mode == MODE_TRANSPORT) + { + cond = local ? COND_NAT_HERE : COND_NAT_THERE; + if (this->ike_sa->has_condition(this->ike_sa, cond)) + { + out = get_transport_nat_ts(this, local, in); + if (out->get_count(out) == 0) + { + out->destroy(out); + out = NULL; + } + } + } + return out; +} + +/** + * Select a matching CHILD config as responder + */ +static child_cfg_t* select_child_cfg(private_child_create_t *this) +{ + peer_cfg_t *peer_cfg; + child_cfg_t *child_cfg = NULL;; + + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); + if (peer_cfg && this->tsi && this->tsr) + { + linked_list_t *listr, *listi, *tsr, *tsi; + + tsr = get_ts_if_nat_transport(this, TRUE, this->tsr); + tsi = get_ts_if_nat_transport(this, FALSE, this->tsi); + + listr = get_dynamic_hosts(this->ike_sa, TRUE); + listi = get_dynamic_hosts(this->ike_sa, FALSE); + child_cfg = peer_cfg->select_child_cfg(peer_cfg, + tsr ?: this->tsr, tsi ?: this->tsi, + listr, listi); + if ((tsi || tsr) && child_cfg && + child_cfg->get_mode(child_cfg) != MODE_TRANSPORT) + { + /* found a CHILD config, but it doesn't use transport mode */ + child_cfg->destroy(child_cfg); + child_cfg = NULL; + } + if (!child_cfg && (tsi || tsr)) + { + /* no match for the substituted NAT selectors, try it without */ + child_cfg = peer_cfg->select_child_cfg(peer_cfg, + this->tsr, this->tsi, listr, listi); + } + listr->destroy(listr); + listi->destroy(listi); + DESTROY_OFFSET_IF(tsi, offsetof(traffic_selector_t, destroy)); + DESTROY_OFFSET_IF(tsr, offsetof(traffic_selector_t, destroy)); + } + + return child_cfg; +} + METHOD(task_t, build_r, status_t, private_child_create_t *this, message_t *message) { - peer_cfg_t *peer_cfg; payload_t *payload; enumerator_t *enumerator; bool no_dh = TRUE, ike_auth = FALSE; @@ -1040,19 +1179,10 @@ METHOD(task_t, build_r, status_t, return SUCCESS; } - peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - if (!this->config && peer_cfg && this->tsi && this->tsr) + if (this->config == NULL) { - linked_list_t *listr, *listi; - - listr = get_dynamic_hosts(this->ike_sa, TRUE); - listi = get_dynamic_hosts(this->ike_sa, FALSE); - this->config = peer_cfg->select_child_cfg(peer_cfg, - this->tsr, this->tsi, listr, listi); - listr->destroy(listr); - listi->destroy(listi); + this->config = select_child_cfg(this); } - if (this->config == NULL) { DBG1(DBG_IKE, "traffic selectors %#R=== %#R inacceptable", @@ -1131,15 +1261,6 @@ METHOD(task_t, build_r, status_t, build_payloads(this, message); - DBG0(DBG_IKE, "CHILD_SA %s{%d} established " - "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", - this->child_sa->get_name(this->child_sa), - this->child_sa->get_reqid(this->child_sa), - ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), - ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), - this->child_sa->get_traffic_selectors(this->child_sa, TRUE), - this->child_sa->get_traffic_selectors(this->child_sa, FALSE)); - if (!this->rekey) { /* invoke the child_up() hook if we are not rekeying */ charon->bus->child_updown(charon->bus, this->child_sa, TRUE); @@ -1147,6 +1268,57 @@ METHOD(task_t, build_r, status_t, return SUCCESS; } +/** + * Raise alerts for received notify errors + */ +static void raise_alerts(private_child_create_t *this, notify_type_t type) +{ + linked_list_t *list; + + switch (type) + { + case NO_PROPOSAL_CHOSEN: + list = this->config->get_proposals(this->config, FALSE); + charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_CHILD, list); + list->destroy_offset(list, offsetof(proposal_t, destroy)); + break; + default: + break; + } +} + +METHOD(task_t, build_i_delete, status_t, + private_child_create_t *this, message_t *message) +{ + message->set_exchange_type(message, INFORMATIONAL); + if (this->child_sa && this->proposal) + { + protocol_id_t proto; + delete_payload_t *del; + u_int32_t spi; + + proto = this->proposal->get_protocol(this->proposal); + spi = this->child_sa->get_spi(this->child_sa, TRUE); + del = delete_payload_create(DELETE, proto); + del->add_spi(del, spi); + message->add_payload(message, (payload_t*)del); + + DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x", + protocol_id_names, proto, ntohl(spi)); + } + return NEED_MORE; +} + +/** + * Change task to delete the failed CHILD_SA as initiator + */ +static status_t delete_failed_sa(private_child_create_t *this) +{ + this->public.task.build = _build_i_delete; + this->public.task.process = (void*)return_success; + return NEED_MORE; +} + METHOD(task_t, process_i, status_t, private_child_create_t *this, message_t *message) { @@ -1195,6 +1367,7 @@ METHOD(task_t, process_i, status_t, DBG1(DBG_IKE, "received %N notify, no CHILD_SA built", notify_type_names, type); enumerator->destroy(enumerator); + raise_alerts(this, type); handle_child_sa_failure(this, message); /* an error in CHILD_SA creation is not critical */ return SUCCESS; @@ -1247,7 +1420,7 @@ METHOD(task_t, process_i, status_t, DBG1(DBG_IKE, "received an IPCOMP_SUPPORTED notify without requesting" " one, no CHILD_SA built"); handle_child_sa_failure(this, message); - return SUCCESS; + return delete_failed_sa(this); } else if (this->ipcomp != IPCOMP_NONE && this->ipcomp_received == IPCOMP_NONE) { @@ -1260,20 +1433,11 @@ METHOD(task_t, process_i, status_t, DBG1(DBG_IKE, "received an IPCOMP_SUPPORTED notify we didn't propose, " "no CHILD_SA built"); handle_child_sa_failure(this, message); - return SUCCESS; + return delete_failed_sa(this); } if (select_and_install(this, no_dh, ike_auth) == SUCCESS) { - DBG0(DBG_IKE, "CHILD_SA %s{%d} established " - "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", - this->child_sa->get_name(this->child_sa), - this->child_sa->get_reqid(this->child_sa), - ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), - ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), - this->child_sa->get_traffic_selectors(this->child_sa, TRUE), - this->child_sa->get_traffic_selectors(this->child_sa, FALSE)); - if (!this->rekey) { /* invoke the child_up() hook if we are not rekeying */ charon->bus->child_updown(charon->bus, this->child_sa, TRUE); @@ -1282,6 +1446,7 @@ METHOD(task_t, process_i, status_t, else { handle_child_sa_failure(this, message); + return delete_failed_sa(this); } return SUCCESS; } diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c index 8652942ad..eaaca2039 100644 --- a/src/libcharon/sa/ikev2/tasks/child_delete.c +++ b/src/libcharon/sa/ikev2/tasks/child_delete.c @@ -177,8 +177,11 @@ static void process_payloads(private_child_delete_t *this, message_t *message) default: break; } - - this->child_sas->insert_last(this->child_sas, child_sa); + if (this->child_sas->find_first(this->child_sas, NULL, + (void**)&child_sa) != SUCCESS) + { + this->child_sas->insert_last(this->child_sas, child_sa); + } } spis->destroy(spis); } @@ -219,12 +222,13 @@ static status_t destroy_and_reestablish(private_child_delete_t *this) { case ACTION_RESTART: child_cfg->get_ref(child_cfg); - status = this->ike_sa->initiate(this->ike_sa, child_cfg, 0, - NULL, NULL); + status = this->ike_sa->initiate(this->ike_sa, child_cfg, + child_sa->get_reqid(child_sa), NULL, NULL); break; case ACTION_ROUTE: charon->traps->install(charon->traps, - this->ike_sa->get_peer_cfg(this->ike_sa), child_cfg); + this->ike_sa->get_peer_cfg(this->ike_sa), child_cfg, + child_sa->get_reqid(child_sa)); break; default: break; @@ -245,6 +249,7 @@ static status_t destroy_and_reestablish(private_child_delete_t *this) */ static void log_children(private_child_delete_t *this) { + linked_list_t *my_ts, *other_ts; enumerator_t *enumerator; child_sa_t *child_sa; u_int64_t bytes_in, bytes_out; @@ -252,15 +257,17 @@ static void log_children(private_child_delete_t *this) enumerator = this->child_sas->create_enumerator(this->child_sas); while (enumerator->enumerate(enumerator, (void**)&child_sa)) { + my_ts = linked_list_create_from_enumerator( + child_sa->create_ts_enumerator(child_sa, TRUE)); + other_ts = linked_list_create_from_enumerator( + child_sa->create_ts_enumerator(child_sa, FALSE)); if (this->expired) { DBG0(DBG_IKE, "closing expired CHILD_SA %s{%d} " "with SPIs %.8x_i %.8x_o and TS %#R=== %#R", child_sa->get_name(child_sa), child_sa->get_reqid(child_sa), ntohl(child_sa->get_spi(child_sa, TRUE)), - ntohl(child_sa->get_spi(child_sa, FALSE)), - child_sa->get_traffic_selectors(child_sa, TRUE), - child_sa->get_traffic_selectors(child_sa, FALSE)); + ntohl(child_sa->get_spi(child_sa, FALSE)), my_ts, other_ts); } else { @@ -272,9 +279,10 @@ static void log_children(private_child_delete_t *this) child_sa->get_name(child_sa), child_sa->get_reqid(child_sa), ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in, ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out, - child_sa->get_traffic_selectors(child_sa, TRUE), - child_sa->get_traffic_selectors(child_sa, FALSE)); + my_ts, other_ts); } + my_ts->destroy(my_ts); + other_ts->destroy(other_ts); } enumerator->destroy(enumerator); } @@ -310,10 +318,6 @@ METHOD(task_t, build_i, status_t, METHOD(task_t, process_i, status_t, private_child_delete_t *this, message_t *message) { - /* flush the list before adding new SAs */ - this->child_sas->destroy(this->child_sas); - this->child_sas = linked_list_create(); - process_payloads(this, message); DBG1(DBG_IKE, "CHILD_SA closed"); return destroy_and_reestablish(this); diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c index 262cb10e0..d2003bb45 100644 --- a/src/libcharon/sa/ikev2/tasks/child_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c @@ -399,12 +399,19 @@ METHOD(child_rekey_t, collide, void, else if (other->get_type(other) == TASK_CHILD_DELETE) { child_delete_t *del = (child_delete_t*)other; - if (del->get_child(del) == this->child_create->get_child(this->child_create)) + if (this->collision && + this->collision->get_type(this->collision) == TASK_CHILD_REKEY) { - /* peer deletes redundant child created in collision */ - this->other_child_destroyed = TRUE; - other->destroy(other); - return; + private_child_rekey_t *rekey; + + rekey = (private_child_rekey_t*)this->collision; + if (del->get_child(del) == rekey->child_create->get_child(rekey->child_create)) + { + /* peer deletes redundant child created in collision */ + this->other_child_destroyed = TRUE; + other->destroy(other); + return; + } } if (del->get_child(del) != this->child_sa) { diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 942f97cf5..8f83c4884 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -852,6 +852,33 @@ local_auth_failed: return FAILED; } +/** + * Send an INFORMATIONAL message with an AUTH_FAILED before closing IKE_SA + */ +static void send_auth_failed_informational(private_ike_auth_t *this, + message_t *reply) +{ + message_t *message; + packet_t *packet; + host_t *host; + + message = message_create(IKEV2_MAJOR_VERSION, IKEV2_MINOR_VERSION); + message->set_message_id(message, reply->get_message_id(reply) + 1); + host = this->ike_sa->get_my_host(this->ike_sa); + message->set_source(message, host->clone(host)); + host = this->ike_sa->get_other_host(this->ike_sa); + message->set_destination(message, host->clone(host)); + message->set_exchange_type(message, INFORMATIONAL); + message->add_notify(message, FALSE, AUTHENTICATION_FAILED, chunk_empty); + + if (this->ike_sa->generate_message(this->ike_sa, message, + &packet) == SUCCESS) + { + charon->sender->send(charon->sender, packet); + } + message->destroy(message); +} + METHOD(task_t, process_i, status_t, private_ike_auth_t *this, message_t *message) { @@ -908,6 +935,7 @@ METHOD(task_t, process_i, status_t, DBG1(DBG_IKE, "received %N notify error", notify_type_names, type); enumerator->destroy(enumerator); + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); return FAILED; } DBG2(DBG_IKE, "received %N notify", @@ -1004,6 +1032,7 @@ METHOD(task_t, process_i, status_t, break; default: charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); + send_auth_failed_informational(this, message); return FAILED; } } @@ -1048,6 +1077,7 @@ METHOD(task_t, process_i, status_t, peer_auth_failed: charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); + send_auth_failed_informational(this, message); return FAILED; } diff --git a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.h b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.h index ac1a85c29..c1f8635ce 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.h +++ b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.h @@ -28,7 +28,7 @@ typedef struct ike_cert_pre_t ike_cert_pre_t; #include <sa/task.h> /** - * Task of type ike_cert_post, certificate processing before authentication. + * Task of type ike_cert_pre, certificate processing before authentication. */ struct ike_cert_pre_t { diff --git a/src/libcharon/sa/ikev2/tasks/ike_delete.c b/src/libcharon/sa/ikev2/tasks/ike_delete.c index f127b0c15..9bc62bf2a 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_delete.c +++ b/src/libcharon/sa/ikev2/tasks/ike_delete.c @@ -109,6 +109,14 @@ METHOD(task_t, process_r, status_t, this->ike_sa->get_other_host(this->ike_sa), this->ike_sa->get_other_id(this->ike_sa)); + if (message->get_exchange_type(message) == INFORMATIONAL && + message->get_notify(message, AUTHENTICATION_FAILED)) + { + /* a late AUTHENTICATION_FAILED notify from the initiator after + * we have established the IKE_SA: signal auth failure */ + charon->bus->alert(charon->bus, ALERT_LOCAL_AUTH_FAILED); + } + switch (this->ike_sa->get_state(this->ike_sa)) { case IKE_ESTABLISHED: diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 7542937b3..278bdc3f2 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -420,6 +420,25 @@ METHOD(task_t, build_r, status_t, return SUCCESS; } +/** + * Raise alerts for received notify errors + */ +static void raise_alerts(private_ike_init_t *this, notify_type_t type) +{ + linked_list_t *list; + + switch (type) + { + case NO_PROPOSAL_CHOSEN: + list = this->config->get_proposals(this->config); + charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE, list); + list->destroy_offset(list, offsetof(proposal_t, destroy)); + break; + default: + break; + } +} + METHOD(task_t, process_i, status_t, private_ike_init_t *this, message_t *message) { @@ -482,6 +501,7 @@ METHOD(task_t, process_i, status_t, DBG1(DBG_IKE, "received %N notify error", notify_type_names, type); enumerator->destroy(enumerator); + raise_alerts(this, type); return FAILED; } DBG2(DBG_IKE, "received %N notify", diff --git a/src/libcharon/sa/ikev2/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c index 0a93db9ed..4fc968f25 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_natd.c +++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c @@ -78,6 +78,19 @@ struct private_ike_natd_t { bool mapping_changed; }; +/** + * Check if UDP encapsulation has to be forced either by config or required + * by the kernel interface + */ +static bool force_encap(ike_cfg_t *ike_cfg) +{ + if (!ike_cfg->force_encap(ike_cfg)) + { + return hydra->kernel_interface->get_features(hydra->kernel_interface) & + KERNEL_REQUIRE_UDP_ENCAPSULATION; + } + return TRUE; +} /** * Build NAT detection hash for a host @@ -147,7 +160,7 @@ static notify_payload_t *build_natd_payload(private_ike_natd_t *this, ike_sa_id = this->ike_sa->get_id(this->ike_sa); config = this->ike_sa->get_ike_cfg(this->ike_sa); - if (config->force_encap(config) && type == NAT_DETECTION_SOURCE_IP) + if (force_encap(config) && type == NAT_DETECTION_SOURCE_IP) { hash = generate_natd_hash_faked(this); } @@ -256,7 +269,7 @@ static void process_payloads(private_ike_natd_t *this, message_t *message) !this->src_matched); config = this->ike_sa->get_ike_cfg(this->ike_sa); if (this->dst_matched && this->src_matched && - config->force_encap(config)) + force_encap(config)) { this->ike_sa->set_condition(this->ike_sa, COND_NAT_FAKE, TRUE); } @@ -316,7 +329,7 @@ METHOD(task_t, build_i, status_t, * 3. Include all possbile addresses */ host = message->get_source(message); - if (!host->is_anyaddr(host) || ike_cfg->force_encap(ike_cfg)) + if (!host->is_anyaddr(host) || force_encap(ike_cfg)) { /* 1. or if we force UDP encap, as it doesn't matter if it's %any */ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host); if (notify) diff --git a/src/libcharon/sa/keymat.h b/src/libcharon/sa/keymat.h index 02db5ca58..bc40b3d92 100644 --- a/src/libcharon/sa/keymat.h +++ b/src/libcharon/sa/keymat.h @@ -79,7 +79,7 @@ struct keymat_t { */ nonce_gen_t* (*create_nonce_gen)(keymat_t *this); - /* + /** * Get a AEAD transform to en-/decrypt and sign/verify IKE messages. * * @param in TRUE for inbound (decrypt), FALSE for outbound (encrypt) diff --git a/src/libcharon/sa/task.h b/src/libcharon/sa/task.h index c37221a77..f2c4299cc 100644 --- a/src/libcharon/sa/task.h +++ b/src/libcharon/sa/task.h @@ -67,7 +67,7 @@ enum task_type_t { TASK_CHILD_CREATE, /** delete an established CHILD_SA */ TASK_CHILD_DELETE, - /** rekey an CHILD_SA */ + /** rekey a CHILD_SA */ TASK_CHILD_REKEY, /** IKEv1 main mode */ TASK_MAIN_MODE, diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h index c649cf78e..a1ebb4117 100644 --- a/src/libcharon/sa/task_manager.h +++ b/src/libcharon/sa/task_manager.h @@ -202,7 +202,7 @@ struct task_manager_t { status_t (*retransmit) (task_manager_t *this, u_int32_t message_id); /** - * Migrate all tasks from other to this. + * Migrate all queued tasks from other to this. * * To rekey or reestablish an IKE_SA completely, all queued or active * tasks should get migrated to the new IKE_SA. @@ -212,6 +212,13 @@ struct task_manager_t { void (*adopt_tasks) (task_manager_t *this, task_manager_t *other); /** + * Migrate all active or queued CHILD_SA-creating tasks from other to this. + * + * @param other manager which gives away its tasks + */ + void (*adopt_child_tasks) (task_manager_t *this, task_manager_t *other); + + /** * Increment a message ID counter, in- or outbound. * * If a message is processed outside of the manager, this call increments diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index 6c0ae19c7..37426fc47 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -92,7 +92,8 @@ static void destroy_entry(entry_t *entry) } METHOD(trap_manager_t, install, u_int32_t, - private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child) + private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child, + u_int32_t reqid) { entry_t *entry, *found = NULL; ike_cfg_t *ike_cfg; @@ -101,7 +102,6 @@ METHOD(trap_manager_t, install, u_int32_t, linked_list_t *my_ts, *other_ts, *list; enumerator_t *enumerator; status_t status; - u_int32_t reqid = 0; /* try to resolve addresses */ ike_cfg = peer->get_ike_cfg(peer); @@ -109,6 +109,7 @@ METHOD(trap_manager_t, install, u_int32_t, 0, ike_cfg->get_other_port(ike_cfg)); if (!other || other->is_anyaddr(other)) { + DESTROY_IF(other); DBG1(DBG_CFG, "installing trap failed, remote address unknown"); return 0; } @@ -141,6 +142,8 @@ METHOD(trap_manager_t, install, u_int32_t, } } enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + if (found) { /* config might have changed so update everything */ DBG1(DBG_CFG, "updating already routed CHILD_SA '%s'", @@ -179,10 +182,11 @@ METHOD(trap_manager_t, install, u_int32_t, .child_sa = child_sa, .peer_cfg = peer->get_ref(peer), ); + this->lock->write_lock(this->lock); this->traps->insert_last(this->traps, entry); + this->lock->unlock(this->lock); reqid = child_sa->get_reqid(child_sa); } - this->lock->unlock(this->lock); if (status != SUCCESS) { @@ -251,6 +255,31 @@ METHOD(trap_manager_t, create_enumerator, enumerator_t*, (void*)this->lock->unlock); } +METHOD(trap_manager_t, find_reqid, u_int32_t, + private_trap_manager_t *this, child_cfg_t *child) +{ + enumerator_t *enumerator; + child_cfg_t *current; + entry_t *entry; + u_int32_t reqid = 0; + + this->lock->read_lock(this->lock); + enumerator = this->traps->create_enumerator(this->traps); + while (enumerator->enumerate(enumerator, &entry)) + { + current = entry->child_sa->get_config(entry->child_sa); + if (streq(current->get_name(current), child->get_name(child))) + { + reqid = entry->child_sa->get_reqid(entry->child_sa); + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + return reqid; +} + METHOD(trap_manager_t, acquire, void, private_trap_manager_t *this, u_int32_t reqid, traffic_selector_t *src, traffic_selector_t *dst) @@ -319,8 +348,7 @@ METHOD(trap_manager_t, acquire, void, } else { - charon->ike_sa_manager->checkin_and_destroy( - charon->ike_sa_manager, ike_sa); + ike_sa->destroy(ike_sa); } } peer->destroy(peer); @@ -417,6 +445,7 @@ trap_manager_t *trap_manager_create(void) .install = _install, .uninstall = _uninstall, .create_enumerator = _create_enumerator, + .find_reqid = _find_reqid, .acquire = _acquire, .flush = _flush, .destroy = _destroy, @@ -435,4 +464,3 @@ trap_manager_t *trap_manager_create(void) return &this->public; } - diff --git a/src/libcharon/sa/trap_manager.h b/src/libcharon/sa/trap_manager.h index e3d355662..0491107fd 100644 --- a/src/libcharon/sa/trap_manager.h +++ b/src/libcharon/sa/trap_manager.h @@ -37,10 +37,11 @@ struct trap_manager_t { * * @param peer peer configuration to initiate on trap * @param child child configuration to install as a trap + * @param reqid optional reqid to use * @return reqid of installed CHILD_SA, 0 if failed */ u_int32_t (*install)(trap_manager_t *this, peer_cfg_t *peer, - child_cfg_t *child); + child_cfg_t *child, u_int32_t reqid); /** * Uninstall a trap policy. @@ -58,6 +59,14 @@ struct trap_manager_t { enumerator_t* (*create_enumerator)(trap_manager_t *this); /** + * Find the reqid of a child config installed as a trap. + * + * @param child CHILD_SA config to get the reqid for + * @return reqid of trap, 0 if not found + */ + u_int32_t (*find_reqid)(trap_manager_t *this, child_cfg_t *child); + + /** * Acquire an SA triggered by an installed trap. * * @param reqid requid of the triggering CHILD_SA |