diff options
Diffstat (limited to 'src/libcharon/sa')
54 files changed, 1444 insertions, 794 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 56b7cb5a4..8a405d93c 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -1,9 +1,10 @@ /* - * Copyright (C) 2006-2015 Tobias Brunner + * Coypright (C) 2016 Andreas Steffen + * Copyright (C) 2006-2016 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -63,22 +64,22 @@ struct private_child_sa_t { /** * our actually used SPI, 0 if unused */ - u_int32_t my_spi; + uint32_t my_spi; /** * others used SPI, 0 if unused */ - u_int32_t other_spi; + uint32_t other_spi; /** * our Compression Parameter Index (CPI) used, 0 if unused */ - u_int16_t my_cpi; + uint16_t my_cpi; /** * others Compression Parameter Index (CPI) used, 0 if unused */ - u_int16_t other_cpi; + uint16_t other_cpi; /** * Array for local traffic selectors @@ -98,7 +99,7 @@ struct private_child_sa_t { /** * reqid used for this child_sa */ - u_int32_t reqid; + uint32_t reqid; /** * Did we allocate/confirm and must release the reqid? @@ -113,7 +114,7 @@ struct private_child_sa_t { /* * Unique CHILD_SA identifier */ - u_int32_t unique_id; + uint32_t unique_id; /** * inbound mark used for this child_sa @@ -198,28 +199,28 @@ struct private_child_sa_t { /** * last number of inbound bytes */ - u_int64_t my_usebytes; + uint64_t my_usebytes; /** * last number of outbound bytes */ - u_int64_t other_usebytes; + uint64_t other_usebytes; /** * last number of inbound packets */ - u_int64_t my_usepackets; + uint64_t my_usepackets; /** * last number of outbound bytes */ - u_int64_t other_usepackets; + uint64_t other_usepackets; }; /** * convert an IKEv2 specific protocol identifier to the IP protocol identifier. */ -static inline u_int8_t proto_ike2ip(protocol_id_t protocol) +static inline uint8_t proto_ike2ip(protocol_id_t protocol) { switch (protocol) { @@ -238,13 +239,13 @@ METHOD(child_sa_t, get_name, char*, return this->config->get_name(this->config); } -METHOD(child_sa_t, get_reqid, u_int32_t, +METHOD(child_sa_t, get_reqid, uint32_t, private_child_sa_t *this) { return this->reqid; } -METHOD(child_sa_t, get_unique_id, u_int32_t, +METHOD(child_sa_t, get_unique_id, uint32_t, private_child_sa_t *this) { return this->unique_id; @@ -269,13 +270,13 @@ METHOD(child_sa_t, get_state, child_sa_state_t, return this->state; } -METHOD(child_sa_t, get_spi, u_int32_t, +METHOD(child_sa_t, get_spi, uint32_t, private_child_sa_t *this, bool inbound) { return inbound ? this->my_spi : this->other_spi; } -METHOD(child_sa_t, get_cpi, u_int16_t, +METHOD(child_sa_t, get_cpi, uint16_t, private_child_sa_t *this, bool inbound) { return inbound ? this->my_cpi : this->other_cpi; @@ -461,17 +462,24 @@ METHOD(child_sa_t, create_policy_enumerator, enumerator_t*, static status_t update_usebytes(private_child_sa_t *this, bool inbound) { status_t status = FAILED; - u_int64_t bytes, packets; + uint64_t bytes, packets; time_t time; if (inbound) { if (this->my_spi) { - status = charon->kernel->query_sa(charon->kernel, this->other_addr, - this->my_addr, this->my_spi, - proto_ike2ip(this->protocol), this->mark_in, - &bytes, &packets, &time); + kernel_ipsec_sa_id_t id = { + .src = this->other_addr, + .dst = this->my_addr, + .spi = this->my_spi, + .proto = proto_ike2ip(this->protocol), + .mark = this->mark_in, + }; + kernel_ipsec_query_sa_t query = {}; + + status = charon->kernel->query_sa(charon->kernel, &id, &query, + &bytes, &packets, &time); if (status == SUCCESS) { if (bytes > this->my_usebytes) @@ -492,10 +500,17 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) { if (this->other_spi) { - status = charon->kernel->query_sa(charon->kernel, this->my_addr, - this->other_addr, this->other_spi, - proto_ike2ip(this->protocol), this->mark_out, - &bytes, &packets, &time); + kernel_ipsec_sa_id_t id = { + .src = this->my_addr, + .dst = this->other_addr, + .spi = this->other_spi, + .proto = proto_ike2ip(this->protocol), + .mark = this->mark_out, + }; + kernel_ipsec_query_sa_t query = {}; + + status = charon->kernel->query_sa(charon->kernel, &id, &query, + &bytes, &packets, &time); if (status == SUCCESS) { if (bytes > this->other_usebytes) @@ -531,15 +546,24 @@ static bool update_usetime(private_child_sa_t *this, bool inbound) if (inbound) { - if (charon->kernel->query_policy(charon->kernel, other_ts, - my_ts, POLICY_IN, this->mark_in, &in) == SUCCESS) + kernel_ipsec_policy_id_t id = { + .dir = POLICY_IN, + .src_ts = other_ts, + .dst_ts = my_ts, + .mark = this->mark_in, + }; + kernel_ipsec_query_policy_t query = {}; + + if (charon->kernel->query_policy(charon->kernel, &id, &query, + &in) == SUCCESS) { last_use = max(last_use, in); } if (this->mode != MODE_TRANSPORT) { - if (charon->kernel->query_policy(charon->kernel, other_ts, - my_ts, POLICY_FWD, this->mark_in, &fwd) == SUCCESS) + id.dir = POLICY_FWD; + if (charon->kernel->query_policy(charon->kernel, &id, &query, + &fwd) == SUCCESS) { last_use = max(last_use, fwd); } @@ -547,8 +571,17 @@ static bool update_usetime(private_child_sa_t *this, bool inbound) } else { - if (charon->kernel->query_policy(charon->kernel, my_ts, - other_ts, POLICY_OUT, this->mark_out, &out) == SUCCESS) + kernel_ipsec_policy_id_t id = { + .dir = POLICY_OUT, + .src_ts = my_ts, + .dst_ts = other_ts, + .mark = this->mark_out, + .interface = this->config->get_interface(this->config), + }; + kernel_ipsec_query_policy_t query = {}; + + if (charon->kernel->query_policy(charon->kernel, &id, &query, + &out) == SUCCESS) { last_use = max(last_use, out); } @@ -573,7 +606,7 @@ static bool update_usetime(private_child_sa_t *this, bool inbound) 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) + time_t *time, uint64_t *bytes, uint64_t *packets) { if ((!bytes && !packets) || update_usebytes(this, inbound) != FAILED) { @@ -625,7 +658,7 @@ METHOD(child_sa_t, get_installtime, time_t, return this->install_time; } -METHOD(child_sa_t, alloc_spi, u_int32_t, +METHOD(child_sa_t, alloc_spi, uint32_t, private_child_sa_t *this, protocol_id_t protocol) { if (charon->kernel->get_spi(charon->kernel, this->other_addr, this->my_addr, @@ -639,7 +672,7 @@ METHOD(child_sa_t, alloc_spi, u_int32_t, return 0; } -METHOD(child_sa_t, alloc_cpi, u_int16_t, +METHOD(child_sa_t, alloc_cpi, uint16_t, private_child_sa_t *this) { if (charon->kernel->get_cpi(charon->kernel, this->other_addr, this->my_addr, @@ -651,16 +684,18 @@ 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 initiator, bool inbound, bool tfcv3, + private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi, + uint16_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; + uint16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size; + uint16_t esn = NO_EXT_SEQ_NUMBERS; linked_list_t *src_ts = NULL, *dst_ts = NULL; time_t now; + kernel_ipsec_sa_id_t id; + kernel_ipsec_add_sa_t sa; lifetime_cfg_t *lifetime; - u_int32_t tfc = 0; + uint32_t tfc = 0; host_t *src, *dst; status_t status; bool update = FALSE; @@ -716,7 +751,7 @@ METHOD(child_sa_t, install, status_t, this->reqid_allocated = TRUE; } - lifetime = this->config->get_lifetime(this->config); + lifetime = this->config->get_lifetime(this->config, TRUE); now = time_monotonic(NULL); if (lifetime->time.rekey) @@ -752,12 +787,36 @@ METHOD(child_sa_t, install, status_t, dst_ts = other_ts; } - status = charon->kernel->add_sa(charon->kernel, - 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->config->get_replay_window(this->config), - initiator, this->encap, esn, inbound, update, src_ts, dst_ts); + id = (kernel_ipsec_sa_id_t){ + .src = src, + .dst = dst, + .spi = spi, + .proto = proto_ike2ip(this->protocol), + .mark = inbound ? this->mark_in : this->mark_out, + }; + sa = (kernel_ipsec_add_sa_t){ + .reqid = this->reqid, + .mode = this->mode, + .src_ts = src_ts, + .dst_ts = dst_ts, + .interface = inbound ? NULL : this->config->get_interface(this->config), + .lifetime = lifetime, + .enc_alg = enc_alg, + .enc_key = encr, + .int_alg = int_alg, + .int_key = integ, + .replay_window = this->config->get_replay_window(this->config), + .tfc = tfc, + .ipcomp = this->ipcomp, + .cpi = cpi, + .encap = this->encap, + .esn = esn, + .initiator = initiator, + .inbound = inbound, + .update = update, + }; + + status = charon->kernel->add_sa(charon->kernel, &id, &sa); free(lifetime); @@ -825,24 +884,62 @@ static void prepare_sa_cfg(private_child_sa_t *this, ipsec_sa_cfg_t *my_sa, static status_t install_policies_internal(private_child_sa_t *this, host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, - ipsec_sa_cfg_t *other_sa, policy_type_t type, policy_priority_t priority) + ipsec_sa_cfg_t *other_sa, policy_type_t type, + policy_priority_t priority, uint32_t manual_prio) { + kernel_ipsec_policy_id_t out_id = { + .dir = POLICY_OUT, + .src_ts = my_ts, + .dst_ts = other_ts, + .mark = this->mark_out, + .interface = this->config->get_interface(this->config), + }, in_id = { + .dir = POLICY_IN, + .src_ts = other_ts, + .dst_ts = my_ts, + .mark = this->mark_in, + }; + kernel_ipsec_manage_policy_t out_policy = { + .type = type, + .prio = priority, + .manual_prio = manual_prio, + .src = my_addr, + .dst = other_addr, + .sa = other_sa, + }, in_policy = { + .type = type, + .prio = priority, + .manual_prio = manual_prio, + .src = other_addr, + .dst = my_addr, + .sa = my_sa, + }; status_t status = SUCCESS; - status |= charon->kernel->add_policy(charon->kernel, - my_addr, other_addr, my_ts, other_ts, - POLICY_OUT, type, other_sa, - this->mark_out, priority); - - status |= charon->kernel->add_policy(charon->kernel, - other_addr, my_addr, other_ts, my_ts, - POLICY_IN, type, my_sa, - this->mark_in, priority); + + status |= charon->kernel->add_policy(charon->kernel, &out_id, &out_policy); + status |= charon->kernel->add_policy(charon->kernel, &in_id, &in_policy); if (this->mode != MODE_TRANSPORT) { - status |= charon->kernel->add_policy(charon->kernel, - other_addr, my_addr, other_ts, my_ts, - POLICY_FWD, type, my_sa, - this->mark_in, priority); + in_id.dir = POLICY_FWD; + status |= charon->kernel->add_policy(charon->kernel, &in_id, &in_policy); + + /* install an "outbound" FWD policy in case there is a drop policy + * matching outbound forwarded traffic, to allow another tunnel to use + * the reversed subnets and do the same we don't set a reqid (this also + * allows the kernel backend to distinguish between the two types of + * FWD policies). To avoid problems with symmetrically overlapping + * policies of two SAs we install them with reduced priority. As they + * basically act as bypass policies for drop policies we use a higher + * priority than is used for them. */ + out_id.dir = POLICY_FWD; + other_sa->reqid = 0; + if (priority == POLICY_PRIORITY_DEFAULT) + { + out_policy.prio = POLICY_PRIORITY_ROUTED; + } + status |= charon->kernel->add_policy(charon->kernel, &out_id, &out_policy); + /* reset the reqid for any other further policies */ + other_sa->reqid = this->reqid; } return status; } @@ -853,20 +950,52 @@ static status_t install_policies_internal(private_child_sa_t *this, static void del_policies_internal(private_child_sa_t *this, host_t *my_addr, host_t *other_addr, traffic_selector_t *my_ts, traffic_selector_t *other_ts, ipsec_sa_cfg_t *my_sa, - ipsec_sa_cfg_t *other_sa, policy_type_t type, policy_priority_t priority) + ipsec_sa_cfg_t *other_sa, policy_type_t type, + policy_priority_t priority, uint32_t manual_prio) { + kernel_ipsec_policy_id_t out_id = { + .dir = POLICY_OUT, + .src_ts = my_ts, + .dst_ts = other_ts, + .mark = this->mark_out, + .interface = this->config->get_interface(this->config), + }, in_id = { + .dir = POLICY_IN, + .src_ts = other_ts, + .dst_ts = my_ts, + .mark = this->mark_in, + }; + kernel_ipsec_manage_policy_t out_policy = { + .type = type, + .prio = priority, + .manual_prio = manual_prio, + .src = my_addr, + .dst = other_addr, + .sa = other_sa, + }, in_policy = { + .type = type, + .prio = priority, + .manual_prio = manual_prio, + .src = other_addr, + .dst = my_addr, + .sa = my_sa, + }; - charon->kernel->del_policy(charon->kernel, - my_addr, other_addr, my_ts, other_ts, POLICY_OUT, type, - other_sa, this->mark_out, priority); - charon->kernel->del_policy(charon->kernel, - other_addr, my_addr, other_ts, my_ts, POLICY_IN, - type, my_sa, this->mark_in, priority); + charon->kernel->del_policy(charon->kernel, &out_id, &out_policy); + charon->kernel->del_policy(charon->kernel, &in_id, &in_policy); if (this->mode != MODE_TRANSPORT) { - charon->kernel->del_policy(charon->kernel, - other_addr, my_addr, other_ts, my_ts, POLICY_FWD, - type, my_sa, this->mark_in, priority); + in_id.dir = POLICY_FWD; + charon->kernel->del_policy(charon->kernel, &in_id, &in_policy); + + out_id.dir = POLICY_FWD; + other_sa->reqid = 0; + if (priority == POLICY_PRIORITY_DEFAULT) + { + out_policy.prio = POLICY_PRIORITY_ROUTED; + } + charon->kernel->del_policy(charon->kernel, &out_id, &out_policy); + other_sa->reqid = this->reqid; } } @@ -912,8 +1041,10 @@ METHOD(child_sa_t, add_policies, status_t, { policy_priority_t priority; ipsec_sa_cfg_t my_sa, other_sa; + uint32_t manual_prio; prepare_sa_cfg(this, &my_sa, &other_sa); + manual_prio = this->config->get_manual_prio(this->config); /* if we're not in state CHILD_INSTALLING (i.e. if there is no SAD * entry) we install a trap policy */ @@ -927,18 +1058,20 @@ METHOD(child_sa_t, add_policies, status_t, { /* install outbound drop policy to avoid packets leaving unencrypted * when updating policies */ - if (priority == POLICY_PRIORITY_DEFAULT && require_policy_update()) + if (priority == POLICY_PRIORITY_DEFAULT && manual_prio == 0 && + require_policy_update()) { status |= install_policies_internal(this, this->my_addr, this->other_addr, my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP, - POLICY_PRIORITY_FALLBACK); + POLICY_PRIORITY_FALLBACK, 0); } /* install policies */ status |= install_policies_internal(this, this->my_addr, this->other_addr, my_ts, other_ts, - &my_sa, &other_sa, POLICY_IPSEC, priority); + &my_sa, &other_sa, POLICY_IPSEC, + priority, manual_prio); if (status != SUCCESS) { @@ -994,11 +1127,22 @@ METHOD(child_sa_t, update, status_t, /* update our (initiator) SA */ if (this->my_spi) { - if (charon->kernel->update_sa(charon->kernel, - this->my_spi, proto_ike2ip(this->protocol), - this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0, - this->other_addr, this->my_addr, other, me, - this->encap, encap, this->mark_in) == NOT_SUPPORTED) + kernel_ipsec_sa_id_t id = { + .src = this->other_addr, + .dst = this->my_addr, + .spi = this->my_spi, + .proto = proto_ike2ip(this->protocol), + .mark = this->mark_in, + }; + kernel_ipsec_update_sa_t sa = { + .cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0, + .new_src = other, + .new_dst = me, + .encap = this->encap, + .new_encap = encap, + }; + if (charon->kernel->update_sa(charon->kernel, &id, + &sa) == NOT_SUPPORTED) { set_state(this, old); return NOT_SUPPORTED; @@ -1008,11 +1152,22 @@ METHOD(child_sa_t, update, status_t, /* update his (responder) SA */ if (this->other_spi) { - if (charon->kernel->update_sa(charon->kernel, - this->other_spi, proto_ike2ip(this->protocol), - this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0, - this->my_addr, this->other_addr, me, other, - this->encap, encap, this->mark_out) == NOT_SUPPORTED) + kernel_ipsec_sa_id_t id = { + .src = this->my_addr, + .dst = this->other_addr, + .spi = this->other_spi, + .proto = proto_ike2ip(this->protocol), + .mark = this->mark_out, + }; + kernel_ipsec_update_sa_t sa = { + .cpi = this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0, + .new_src = me, + .new_dst = other, + .encap = this->encap, + .new_encap = encap, + }; + if (charon->kernel->update_sa(charon->kernel, &id, + &sa) == NOT_SUPPORTED) { set_state(this, old); return NOT_SUPPORTED; @@ -1028,18 +1183,21 @@ METHOD(child_sa_t, update, status_t, ipsec_sa_cfg_t my_sa, other_sa; enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; + uint32_t manual_prio; prepare_sa_cfg(this, &my_sa, &other_sa); + manual_prio = this->config->get_manual_prio(this->config); /* always use high priorities, as hosts getting updated are INSTALLED */ enumerator = create_policy_enumerator(this); while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) { traffic_selector_t *old_my_ts = NULL, *old_other_ts = NULL; + /* remove old policies first */ del_policies_internal(this, this->my_addr, this->other_addr, - my_ts, other_ts, &my_sa, &other_sa, - POLICY_IPSEC, POLICY_PRIORITY_DEFAULT); + my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, + POLICY_PRIORITY_DEFAULT, manual_prio); /* check if we have to update a "dynamic" traffic selector */ if (!me->ip_equals(me, this->my_addr) && @@ -1062,17 +1220,20 @@ METHOD(child_sa_t, update, status_t, /* reinstall updated policies */ install_policies_internal(this, me, other, my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, - POLICY_PRIORITY_DEFAULT); + POLICY_PRIORITY_DEFAULT, manual_prio); /* update fallback policies after the new policy is in place */ - del_policies_internal(this, this->my_addr, this->other_addr, - old_my_ts ?: my_ts, - old_other_ts ?: other_ts, - &my_sa, &other_sa, POLICY_DROP, - POLICY_PRIORITY_FALLBACK); - install_policies_internal(this, me, other, my_ts, other_ts, + if (manual_prio == 0) + { + del_policies_internal(this, this->my_addr, this->other_addr, + old_my_ts ?: my_ts, + old_other_ts ?: other_ts, + &my_sa, &other_sa, POLICY_DROP, + POLICY_PRIORITY_FALLBACK, 0); + install_policies_internal(this, me, other, my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP, - POLICY_PRIORITY_FALLBACK); + POLICY_PRIORITY_FALLBACK, 0); + } DESTROY_IF(old_my_ts); DESTROY_IF(old_other_ts); } @@ -1115,20 +1276,24 @@ METHOD(child_sa_t, destroy, void, if (this->config->install_policy(this->config)) { ipsec_sa_cfg_t my_sa, other_sa; + uint32_t manual_prio; prepare_sa_cfg(this, &my_sa, &other_sa); + manual_prio = this->config->get_manual_prio(this->config); /* delete all policies in the kernel */ enumerator = create_policy_enumerator(this); while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) { del_policies_internal(this, this->my_addr, this->other_addr, - my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, priority); - if (priority == POLICY_PRIORITY_DEFAULT && require_policy_update()) + my_ts, other_ts, &my_sa, &other_sa, + POLICY_IPSEC, priority, manual_prio); + if (priority == POLICY_PRIORITY_DEFAULT && manual_prio == 0 && + require_policy_update()) { del_policies_internal(this, this->my_addr, this->other_addr, - my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP, - POLICY_PRIORITY_FALLBACK); + my_ts, other_ts, &my_sa, &other_sa, + POLICY_DROP, POLICY_PRIORITY_FALLBACK, 0); } } enumerator->destroy(enumerator); @@ -1137,17 +1302,31 @@ METHOD(child_sa_t, destroy, void, /* delete SAs in the kernel, if they are set up */ if (this->my_spi) { - charon->kernel->del_sa(charon->kernel, - this->other_addr, this->my_addr, this->my_spi, - proto_ike2ip(this->protocol), this->my_cpi, - this->mark_in); + kernel_ipsec_sa_id_t id = { + .src = this->other_addr, + .dst = this->my_addr, + .spi = this->my_spi, + .proto = proto_ike2ip(this->protocol), + .mark = this->mark_in, + }; + kernel_ipsec_del_sa_t sa = { + .cpi = this->my_cpi, + }; + charon->kernel->del_sa(charon->kernel, &id, &sa); } if (this->other_spi) { - charon->kernel->del_sa(charon->kernel, - this->my_addr, this->other_addr, this->other_spi, - proto_ike2ip(this->protocol), this->other_cpi, - this->mark_out); + kernel_ipsec_sa_id_t id = { + .src = this->my_addr, + .dst = this->other_addr, + .spi = this->other_spi, + .proto = proto_ike2ip(this->protocol), + .mark = this->mark_out, + }; + kernel_ipsec_del_sa_t sa = { + .cpi = this->other_cpi, + }; + charon->kernel->del_sa(charon->kernel, &id, &sa); } if (this->reqid_allocated) @@ -1174,7 +1353,7 @@ METHOD(child_sa_t, destroy, void, static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local) { host_t *host = NULL; - u_int8_t mask; + uint8_t mask; enumerator_t *enumerator; linked_list_t *ts_list, *list; traffic_selector_t *ts; @@ -1207,11 +1386,12 @@ static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local) * 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, + child_cfg_t *config, uint32_t rekey, bool encap, u_int mark_in, u_int mark_out) { private_child_sa_t *this; - static refcount_t unique_id = 0, unique_mark = 0, mark; + static refcount_t unique_id = 0, unique_mark = 0; + refcount_t mark; INIT(this, .public = { diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h index debe8eb2c..bc7df996a 100644 --- a/src/libcharon/sa/child_sa.h +++ b/src/libcharon/sa/child_sa.h @@ -128,7 +128,7 @@ struct child_sa_t { * * @return reqid of the CHILD SA */ - u_int32_t (*get_reqid)(child_sa_t *this); + uint32_t (*get_reqid)(child_sa_t *this); /** * Get the unique numerical identifier for this CHILD_SA. @@ -138,7 +138,7 @@ struct child_sa_t { * * @return unique CHILD_SA identifier */ - u_int32_t (*get_unique_id)(child_sa_t *this); + uint32_t (*get_unique_id)(child_sa_t *this); /** * Get the config used to set up this child sa. @@ -171,7 +171,7 @@ struct child_sa_t { * @param inbound TRUE to get inbound SPI, FALSE for outbound. * @return SPI of the CHILD SA */ - u_int32_t (*get_spi) (child_sa_t *this, bool inbound); + uint32_t (*get_spi) (child_sa_t *this, bool inbound); /** * Get the CPI of this CHILD_SA. @@ -183,7 +183,7 @@ struct child_sa_t { * @param inbound TRUE to get inbound CPI, FALSE for outbound. * @return CPI of the CHILD SA */ - u_int16_t (*get_cpi) (child_sa_t *this, bool inbound); + uint16_t (*get_cpi) (child_sa_t *this, bool inbound); /** * Get the protocol which this CHILD_SA uses to protect traffic. @@ -300,7 +300,7 @@ struct child_sa_t { * @param[out] packets number of processed packets (NULL to ignore) */ void (*get_usestats)(child_sa_t *this, bool inbound, time_t *time, - u_int64_t *bytes, u_int64_t *packets); + uint64_t *bytes, uint64_t *packets); /** * Get the mark used with this CHILD_SA. @@ -335,14 +335,14 @@ struct child_sa_t { * @param spi SPI output pointer * @return SPI, 0 on failure */ - u_int32_t (*alloc_spi)(child_sa_t *this, protocol_id_t protocol); + uint32_t (*alloc_spi)(child_sa_t *this, protocol_id_t protocol); /** * Allocate a CPI to use for IPComp. * * @return CPI, 0 on failure */ - u_int16_t (*alloc_cpi)(child_sa_t *this); + uint16_t (*alloc_cpi)(child_sa_t *this); /** * Install an IPsec SA for one direction. @@ -359,7 +359,7 @@ 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, + uint32_t spi, uint16_t cpi, bool initiator, bool inbound, bool tfcv3, linked_list_t *my_ts, linked_list_t *other_ts); /** @@ -404,7 +404,7 @@ struct child_sa_t { * @return child_sa_t object */ child_sa_t * child_sa_create(host_t *me, host_t *other, child_cfg_t *config, - u_int32_t reqid, bool encap, + uint32_t reqid, bool encap, u_int mark_in, u_int mark_out); #endif /** CHILD_SA_H_ @}*/ diff --git a/src/libcharon/sa/child_sa_manager.c b/src/libcharon/sa/child_sa_manager.c index 071a119da..13f22cf5f 100644 --- a/src/libcharon/sa/child_sa_manager.c +++ b/src/libcharon/sa/child_sa_manager.c @@ -59,11 +59,11 @@ typedef struct { /** the associated IKE_SA */ ike_sa_id_t *ike_id; /** unique CHILD_SA identifier */ - u_int32_t unique_id; + uint32_t unique_id; /** inbound SPI */ - u_int32_t spi_in; + uint32_t spi_in; /** outbound SPI */ - u_int32_t spi_out; + uint32_t spi_out; /** inbound host address */ host_t *host_in; /** outbound host address and port */ @@ -202,7 +202,7 @@ METHOD(child_sa_manager_t, remove_, void, * Check out an IKE_SA for a given CHILD_SA */ static ike_sa_t *checkout_ikesa(private_child_sa_manager_t *this, - ike_sa_id_t *id, u_int32_t unique_id, child_sa_t **child_sa) + ike_sa_id_t *id, uint32_t unique_id, child_sa_t **child_sa) { enumerator_t *enumerator; child_sa_t *current; @@ -238,7 +238,7 @@ static ike_sa_t *checkout_ikesa(private_child_sa_manager_t *this, } METHOD(child_sa_manager_t, checkout_by_id, ike_sa_t*, - private_child_sa_manager_t *this, u_int32_t unique_id, + private_child_sa_manager_t *this, uint32_t unique_id, child_sa_t **child_sa) { ike_sa_id_t *id; @@ -262,11 +262,11 @@ METHOD(child_sa_manager_t, checkout_by_id, ike_sa_t*, } METHOD(child_sa_manager_t, checkout, ike_sa_t*, - private_child_sa_manager_t *this, protocol_id_t protocol, u_int32_t spi, + private_child_sa_manager_t *this, protocol_id_t protocol, uint32_t spi, host_t *dst, child_sa_t **child_sa) { ike_sa_id_t *id; - u_int32_t unique_id; + uint32_t unique_id; child_entry_t *entry, key = { .spi_in = spi, .spi_out = spi, diff --git a/src/libcharon/sa/child_sa_manager.h b/src/libcharon/sa/child_sa_manager.h index 4d57528e8..f1d6ad9e0 100644 --- a/src/libcharon/sa/child_sa_manager.h +++ b/src/libcharon/sa/child_sa_manager.h @@ -59,7 +59,7 @@ struct child_sa_manager_t { * @return IKE_SA, NULL if not found */ ike_sa_t *(*checkout)(child_sa_manager_t *this, - protocol_id_t protocol, u_int32_t spi, host_t *dst, + protocol_id_t protocol, uint32_t spi, host_t *dst, child_sa_t **child_sa); /** @@ -72,7 +72,7 @@ struct child_sa_manager_t { * @param child_sa returns CHILD_SA managed by IKE_SA * @return IKE_SA, NULL if not found */ - ike_sa_t *(*checkout_by_id)(child_sa_manager_t *this, u_int32_t unique_id, + ike_sa_t *(*checkout_by_id)(child_sa_manager_t *this, uint32_t unique_id, child_sa_t **child_sa); /** diff --git a/src/libcharon/sa/eap/eap_manager.c b/src/libcharon/sa/eap/eap_manager.c index 1886307e9..e4fcbc8f0 100644 --- a/src/libcharon/sa/eap/eap_manager.c +++ b/src/libcharon/sa/eap/eap_manager.c @@ -35,7 +35,7 @@ struct eap_entry_t { /** * vendor ID, 0 for default EAP methods */ - u_int32_t vendor; + uint32_t vendor; /** * Role of the method returned by the constructor, EAP_SERVER or EAP_PEER @@ -70,7 +70,7 @@ struct private_eap_manager_t { }; METHOD(eap_manager_t, add_method, void, - private_eap_manager_t *this, eap_type_t type, u_int32_t vendor, + private_eap_manager_t *this, eap_type_t type, uint32_t vendor, eap_role_t role, eap_constructor_t constructor) { eap_entry_t *entry = malloc_thing(eap_entry_t); @@ -109,7 +109,7 @@ METHOD(eap_manager_t, remove_method, void, * filter the registered methods */ static bool filter_methods(uintptr_t role, eap_entry_t **entry, - eap_type_t *type, void *in, u_int32_t *vendor) + eap_type_t *type, void *in, uint32_t *vendor) { if ((*entry)->role != (eap_role_t)role) { @@ -144,7 +144,7 @@ METHOD(eap_manager_t, create_enumerator, enumerator_t*, } METHOD(eap_manager_t, create_instance, eap_method_t*, - private_eap_manager_t *this, eap_type_t type, u_int32_t vendor, + private_eap_manager_t *this, eap_type_t type, uint32_t vendor, eap_role_t role, identification_t *server, identification_t *peer) { enumerator_t *enumerator; diff --git a/src/libcharon/sa/eap/eap_manager.h b/src/libcharon/sa/eap/eap_manager.h index e318ef57a..4ed1cae20 100644 --- a/src/libcharon/sa/eap/eap_manager.h +++ b/src/libcharon/sa/eap/eap_manager.h @@ -44,7 +44,7 @@ struct eap_manager_t { * @param role EAP role of the registered method * @param constructor constructor function, returns an eap_method_t */ - void (*add_method)(eap_manager_t *this, eap_type_t type, u_int32_t vendor, + void (*add_method)(eap_manager_t *this, eap_type_t type, uint32_t vendor, eap_role_t role, eap_constructor_t constructor); /** @@ -61,7 +61,7 @@ struct eap_manager_t { * even though it is registered as method with this manager). * * @param role EAP role of methods to enumerate - * @return enumerator over (eap_type_t type, u_int32_t vendor) + * @return enumerator over (eap_type_t type, uint32_t vendor) */ enumerator_t* (*create_enumerator)(eap_manager_t *this, eap_role_t role); @@ -76,7 +76,7 @@ struct eap_manager_t { * @return EAP method instance, NULL if no constructor found */ eap_method_t* (*create_instance)(eap_manager_t *this, eap_type_t type, - u_int32_t vendor, eap_role_t role, + uint32_t vendor, eap_role_t role, identification_t *server, identification_t *peer); diff --git a/src/libcharon/sa/eap/eap_method.h b/src/libcharon/sa/eap/eap_method.h index 689c0f990..8e25f7df8 100644 --- a/src/libcharon/sa/eap/eap_method.h +++ b/src/libcharon/sa/eap/eap_method.h @@ -96,7 +96,7 @@ struct eap_method_t { * @param vendor pointer receiving vendor identifier for type, 0 for none * @return type of the EAP method */ - eap_type_t (*get_type) (eap_method_t *this, u_int32_t *vendor); + eap_type_t (*get_type) (eap_method_t *this, uint32_t *vendor); /** * Check if this EAP method authenticates the server. @@ -126,7 +126,7 @@ struct eap_method_t { * * @return current EAP identifier */ - u_int8_t (*get_identifier) (eap_method_t *this); + uint8_t (*get_identifier) (eap_method_t *this); /** * Set the EAP identifier to a deterministic value, overwriting @@ -134,7 +134,7 @@ struct eap_method_t { * * @param identifier current EAP identifier */ - void (*set_identifier) (eap_method_t *this, u_int8_t identifier); + void (*set_identifier) (eap_method_t *this, uint8_t identifier); /** * Get authentication details performed by this EAP method. diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index bcbff3211..009277ddd 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1,9 +1,9 @@ /* - * Copyright (C) 2006-2015 Tobias Brunner + * Copyright (C) 2006-2016 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -71,6 +71,7 @@ ENUM(ike_sa_state_names, IKE_CREATED, IKE_DESTROYING, "ESTABLISHED", "PASSIVE", "REKEYING", + "REKEYED", "DELETING", "DESTROYING", ); @@ -101,7 +102,7 @@ struct private_ike_sa_t { /** * unique numerical ID for this IKE_SA. */ - u_int32_t unique_id; + uint32_t unique_id; /** * Current state of the IKE_SA @@ -233,12 +234,12 @@ struct private_ike_sa_t { /** * number pending UPDATE_SA_ADDRESS (MOBIKE) */ - u_int32_t pending_updates; + uint32_t pending_updates; /** * NAT keep alive interval */ - u_int32_t keepalive_interval; + uint32_t keepalive_interval; /** * The schedueld keep alive job, if any @@ -249,7 +250,7 @@ struct private_ike_sa_t { * interval for retries during initiation (e.g. if DNS resolution failed), * 0 to disable (default) */ - u_int32_t retry_initiate_interval; + uint32_t retry_initiate_interval; /** * TRUE if a retry_initiate_job has been queued @@ -259,12 +260,12 @@ struct private_ike_sa_t { /** * Timestamps for this IKE_SA */ - u_int32_t stats[STAT_MAX]; + uint32_t stats[STAT_MAX]; /** * how many times we have retried so far (keyingtries) */ - u_int32_t keyingtry; + uint32_t keyingtry; /** * local host address to be used for IKE, set via MIGRATE kernel message @@ -343,7 +344,7 @@ static time_t get_use_time(private_ike_sa_t* this, bool inbound) return use_time; } -METHOD(ike_sa_t, get_unique_id, u_int32_t, +METHOD(ike_sa_t, get_unique_id, uint32_t, private_ike_sa_t *this) { return this->unique_id; @@ -359,7 +360,7 @@ METHOD(ike_sa_t, get_name, char*, return "(unnamed)"; } -METHOD(ike_sa_t, get_statistic, u_int32_t, +METHOD(ike_sa_t, get_statistic, uint32_t, private_ike_sa_t *this, statistic_t kind) { if (kind < STAT_MAX) @@ -370,7 +371,7 @@ METHOD(ike_sa_t, get_statistic, u_int32_t, } METHOD(ike_sa_t, set_statistic, void, - private_ike_sa_t *this, statistic_t kind, u_int32_t value) + private_ike_sa_t *this, statistic_t kind, uint32_t value) { if (kind < STAT_MAX) { @@ -604,7 +605,7 @@ METHOD(ike_sa_t, set_proposal, void, } METHOD(ike_sa_t, set_message_id, void, - private_ike_sa_t *this, bool initiate, u_int32_t mid) + private_ike_sa_t *this, bool initiate, uint32_t mid) { if (initiate) { @@ -814,7 +815,7 @@ METHOD(ike_sa_t, set_state, void, this->state == IKE_PASSIVE) { job_t *job; - u_int32_t t; + uint32_t t; /* calculate rekey, reauth and lifetime */ this->stats[STAT_ESTABLISHED] = time_monotonic(NULL); @@ -1035,12 +1036,12 @@ METHOD(ike_sa_t, has_mapping_changed, bool, } METHOD(ike_sa_t, set_pending_updates, void, - private_ike_sa_t *this, u_int32_t updates) + private_ike_sa_t *this, uint32_t updates) { this->pending_updates = updates; } -METHOD(ike_sa_t, get_pending_updates, u_int32_t, +METHOD(ike_sa_t, get_pending_updates, uint32_t, private_ike_sa_t *this) { return this->pending_updates; @@ -1203,6 +1204,7 @@ METHOD(ike_sa_t, generate_message_fragmented, status_t, packet_t *packet; status_t status; bool use_frags = FALSE; + bool pre_generated = FALSE; if (this->ike_cfg) { @@ -1237,14 +1239,21 @@ METHOD(ike_sa_t, generate_message_fragmented, status_t, return SUCCESS; } + pre_generated = message->is_encoded(message); this->stats[STAT_OUTBOUND] = time_monotonic(NULL); message->set_ike_sa_id(message, this->ike_sa_id); - charon->bus->message(charon->bus, message, FALSE, TRUE); + if (!pre_generated) + { + charon->bus->message(charon->bus, message, FALSE, TRUE); + } status = message->fragment(message, this->keymat, this->fragment_size, &fragments); if (status == SUCCESS) { - charon->bus->message(charon->bus, message, FALSE, FALSE); + if (!pre_generated) + { + charon->bus->message(charon->bus, message, FALSE, FALSE); + } *packets = enumerator_create_filter(fragments, (void*)filter_fragments, this, NULL); } @@ -1432,7 +1441,7 @@ static void resolve_hosts(private_ike_sa_t *this) } METHOD(ike_sa_t, initiate, status_t, - private_ike_sa_t *this, child_cfg_t *child_cfg, u_int32_t reqid, + private_ike_sa_t *this, child_cfg_t *child_cfg, uint32_t reqid, traffic_selector_t *tsi, traffic_selector_t *tsr) { bool defer_initiate = FALSE; @@ -1642,7 +1651,7 @@ METHOD(ike_sa_t, add_child_sa, void, } METHOD(ike_sa_t, get_child_sa, child_sa_t*, - private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi, bool inbound) + private_ike_sa_t *this, protocol_id_t protocol, uint32_t spi, bool inbound) { enumerator_t *enumerator; child_sa_t *current, *found = NULL; @@ -1721,7 +1730,7 @@ METHOD(ike_sa_t, remove_child_sa, void, } METHOD(ike_sa_t, rekey_child_sa, status_t, - private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) + private_ike_sa_t *this, protocol_id_t protocol, uint32_t spi) { if (this->state == IKE_PASSIVE) { @@ -1732,7 +1741,7 @@ METHOD(ike_sa_t, rekey_child_sa, status_t, } METHOD(ike_sa_t, delete_child_sa, status_t, - private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi, bool expired) + private_ike_sa_t *this, protocol_id_t protocol, uint32_t spi, bool expired) { if (this->state == IKE_PASSIVE) { @@ -1744,7 +1753,7 @@ METHOD(ike_sa_t, delete_child_sa, status_t, } METHOD(ike_sa_t, destroy_child_sa, status_t, - private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) + private_ike_sa_t *this, protocol_id_t protocol, uint32_t spi) { enumerator_t *enumerator; child_sa_t *child_sa; @@ -2301,7 +2310,7 @@ METHOD(ike_sa_t, redirect, status_t, } METHOD(ike_sa_t, retransmit, status_t, - private_ike_sa_t *this, u_int32_t message_id) + private_ike_sa_t *this, uint32_t message_id) { if (this->state == IKE_PASSIVE) { @@ -2316,7 +2325,7 @@ METHOD(ike_sa_t, retransmit, status_t, case IKE_CONNECTING: { /* retry IKE_SA_INIT/Main Mode if we have multiple keyingtries */ - u_int32_t tries = this->peer_cfg->get_keyingtries(this->peer_cfg); + uint32_t tries = this->peer_cfg->get_keyingtries(this->peer_cfg); charon->bus->alert(charon->bus, ALERT_PEER_INIT_UNREACHABLE, this->keyingtry); this->keyingtry++; @@ -2348,7 +2357,8 @@ METHOD(ike_sa_t, retransmit, status_t, reestablish(this); break; } - if (this->state != IKE_CONNECTING) + if (this->state != IKE_CONNECTING && + this->state != IKE_REKEYED) { charon->bus->ike_updown(charon->bus, &this->public, FALSE); } @@ -2358,9 +2368,9 @@ METHOD(ike_sa_t, retransmit, status_t, } METHOD(ike_sa_t, set_auth_lifetime, status_t, - private_ike_sa_t *this, u_int32_t lifetime) + private_ike_sa_t *this, uint32_t lifetime) { - u_int32_t diff, hard, soft, now; + uint32_t diff, hard, soft, now; bool send_update; diff = this->peer_cfg->get_over_time(this->peer_cfg); @@ -2500,6 +2510,7 @@ METHOD(ike_sa_t, roam, status_t, case IKE_DELETING: case IKE_DESTROYING: case IKE_PASSIVE: + case IKE_REKEYED: return SUCCESS; default: break; @@ -2609,6 +2620,12 @@ METHOD(ike_sa_t, queue_task, void, this->task_manager->queue_task(this->task_manager, task); } +METHOD(ike_sa_t, queue_task_delayed, void, + private_ike_sa_t *this, task_t *task, uint32_t delay) +{ + this->task_manager->queue_task_delayed(this->task_manager, task, delay); +} + METHOD(ike_sa_t, inherit_pre, void, private_ike_sa_t *this, ike_sa_t *other_public) { @@ -2927,6 +2944,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .create_task_enumerator = _create_task_enumerator, .flush_queue = _flush_queue, .queue_task = _queue_task, + .queue_task_delayed = _queue_task_delayed, #ifdef ME .act_as_mediation_server = _act_as_mediation_server, .get_server_reflexive_host = _get_server_reflexive_host, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 836360e3c..6f5040d7c 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -1,9 +1,9 @@ /* - * Copyright (C) 2006-2015 Tobias Brunner + * Copyright (C) 2006-2016 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -58,12 +58,12 @@ typedef struct ike_sa_t ike_sa_t; /** * After which time rekeying should be retried if it failed, in seconds. */ -#define RETRY_INTERVAL 30 +#define RETRY_INTERVAL 15 /** * Jitter to subtract from RETRY_INTERVAL to randomize rekey retry. */ -#define RETRY_JITTER 20 +#define RETRY_JITTER 10 /** * Number of redirects allowed within REDIRECT_LOOP_DETECT_PERIOD. @@ -309,6 +309,11 @@ enum ike_sa_state_t { IKE_REKEYING, /** + * IKE_SA has been rekeyed (or is redundant) + */ + IKE_REKEYED, + + /** * IKE_SA is in progress of deletion */ IKE_DELETING, @@ -353,7 +358,7 @@ struct ike_sa_t { * * @return unique ID */ - u_int32_t (*get_unique_id) (ike_sa_t *this); + uint32_t (*get_unique_id) (ike_sa_t *this); /** * Get the state of the IKE_SA. @@ -382,7 +387,7 @@ struct ike_sa_t { * @param kind kind of requested value * @return value as integer */ - u_int32_t (*get_statistic)(ike_sa_t *this, statistic_t kind); + uint32_t (*get_statistic)(ike_sa_t *this, statistic_t kind); /** * Set statistic value of the IKE_SA. @@ -390,7 +395,7 @@ struct ike_sa_t { * @param kind kind of value to update * @param value value as integer */ - void (*set_statistic)(ike_sa_t *this, statistic_t kind, u_int32_t value); + void (*set_statistic)(ike_sa_t *this, statistic_t kind, uint32_t value); /** * Get the own host address. @@ -557,7 +562,7 @@ struct ike_sa_t { * @param initiate TRUE to set message ID for initiating * @param mid message id to set */ - void (*set_message_id)(ike_sa_t *this, bool initiate, u_int32_t mid); + void (*set_message_id)(ike_sa_t *this, bool initiate, uint32_t mid); /** * Add an additional address for the peer. @@ -630,14 +635,14 @@ struct ike_sa_t { * * @return number of pending updates */ - u_int32_t (*get_pending_updates)(ike_sa_t *this); + uint32_t (*get_pending_updates)(ike_sa_t *this); /** * Set the number of queued MOBIKE address updates. * * @param updates number of pending updates */ - void (*set_pending_updates)(ike_sa_t *this, u_int32_t updates); + void (*set_pending_updates)(ike_sa_t *this, uint32_t updates); #ifdef ME /** @@ -752,7 +757,7 @@ struct ike_sa_t { * - DESTROY_ME if initialization failed */ status_t (*initiate) (ike_sa_t *this, child_cfg_t *child_cfg, - u_int32_t reqid, traffic_selector_t *tsi, + uint32_t reqid, traffic_selector_t *tsi, traffic_selector_t *tsr); /** @@ -850,7 +855,7 @@ struct ike_sa_t { * - SUCCESS * - NOT_FOUND if request doesn't have to be retransmited */ - status_t (*retransmit) (ike_sa_t *this, u_int32_t message_id); + status_t (*retransmit) (ike_sa_t *this, uint32_t message_id); /** * Sends a DPD request to the peer. @@ -924,7 +929,7 @@ struct ike_sa_t { * @return child_sa, or NULL if none found */ child_sa_t* (*get_child_sa) (ike_sa_t *this, protocol_id_t protocol, - u_int32_t spi, bool inbound); + uint32_t spi, bool inbound); /** * Get the number of CHILD_SAs. @@ -958,7 +963,7 @@ struct ike_sa_t { * - NOT_FOUND, if IKE_SA has no such CHILD_SA * - SUCCESS, if rekeying initiated */ - status_t (*rekey_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); + status_t (*rekey_child_sa) (ike_sa_t *this, protocol_id_t protocol, uint32_t spi); /** * Close the CHILD SA with the specified protocol/SPI. @@ -975,7 +980,7 @@ struct ike_sa_t { * - SUCCESS, if delete message sent */ status_t (*delete_child_sa)(ike_sa_t *this, protocol_id_t protocol, - u_int32_t spi, bool expired); + uint32_t spi, bool expired); /** * Destroy a CHILD SA with the specified protocol/SPI. @@ -988,7 +993,7 @@ struct ike_sa_t { * - NOT_FOUND, if IKE_SA has no such CHILD_SA * - SUCCESS */ - status_t (*destroy_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi); + status_t (*destroy_child_sa) (ike_sa_t *this, protocol_id_t protocol, uint32_t spi); /** * Rekey the IKE_SA. @@ -1028,7 +1033,7 @@ struct ike_sa_t { * @param lifetime lifetime in seconds * @return DESTROY_ME to destroy the IKE_SA */ - status_t (*set_auth_lifetime)(ike_sa_t *this, u_int32_t lifetime); + status_t (*set_auth_lifetime)(ike_sa_t *this, uint32_t lifetime); /** * Add a virtual IP to use for this IKE_SA and its children. @@ -1119,6 +1124,15 @@ struct ike_sa_t { void (*queue_task)(ike_sa_t *this, task_t *task); /** + * Queue a task in the manager, but delay its initiation for at least the + * given number of seconds. + * + * @param task task to queue + * @param delay minimum delay in s before initiating the task + */ + void (*queue_task_delayed)(ike_sa_t *this, task_t *task, uint32_t delay); + + /** * Inherit required attributes to new SA before rekeying. * * Some properties of the SA must be applied before starting IKE_SA diff --git a/src/libcharon/sa/ike_sa_id.c b/src/libcharon/sa/ike_sa_id.c index e52086483..b4e66ed73 100644 --- a/src/libcharon/sa/ike_sa_id.c +++ b/src/libcharon/sa/ike_sa_id.c @@ -34,17 +34,17 @@ struct private_ike_sa_id_t { /** * Major IKE version of IKE_SA. */ - u_int8_t ike_version; + uint8_t ike_version; /** * SPI of initiator. */ - u_int64_t initiator_spi; + uint64_t initiator_spi; /** * SPI of responder. */ - u_int64_t responder_spi; + uint64_t responder_spi; /** * Role for specific IKE_SA. @@ -52,31 +52,31 @@ struct private_ike_sa_id_t { bool is_initiator_flag; }; -METHOD(ike_sa_id_t, get_ike_version, u_int8_t, +METHOD(ike_sa_id_t, get_ike_version, uint8_t, private_ike_sa_id_t *this) { return this->ike_version; } METHOD(ike_sa_id_t, set_responder_spi, void, - private_ike_sa_id_t *this, u_int64_t responder_spi) + private_ike_sa_id_t *this, uint64_t responder_spi) { this->responder_spi = responder_spi; } METHOD(ike_sa_id_t, set_initiator_spi, void, - private_ike_sa_id_t *this, u_int64_t initiator_spi) + private_ike_sa_id_t *this, uint64_t initiator_spi) { this->initiator_spi = initiator_spi; } -METHOD(ike_sa_id_t, get_initiator_spi, u_int64_t, +METHOD(ike_sa_id_t, get_initiator_spi, uint64_t, private_ike_sa_id_t *this) { return this->initiator_spi; } -METHOD(ike_sa_id_t, get_responder_spi, u_int64_t, +METHOD(ike_sa_id_t, get_responder_spi, uint64_t, private_ike_sa_id_t *this) { return this->responder_spi; @@ -134,8 +134,8 @@ METHOD(ike_sa_id_t, destroy, void, /* * Described in header. */ -ike_sa_id_t * ike_sa_id_create(u_int8_t ike_version, u_int64_t initiator_spi, - u_int64_t responder_spi, bool is_initiator_flag) +ike_sa_id_t * ike_sa_id_create(uint8_t ike_version, uint64_t initiator_spi, + uint64_t responder_spi, bool is_initiator_flag) { private_ike_sa_id_t *this; diff --git a/src/libcharon/sa/ike_sa_id.h b/src/libcharon/sa/ike_sa_id.h index 5eb754e95..b3a9ef61f 100644 --- a/src/libcharon/sa/ike_sa_id.h +++ b/src/libcharon/sa/ike_sa_id.h @@ -41,7 +41,7 @@ struct ike_sa_id_t { * * @return IKE version */ - u_int8_t (*get_ike_version) (ike_sa_id_t *this); + uint8_t (*get_ike_version) (ike_sa_id_t *this); /** * Set the SPI of the responder. @@ -50,28 +50,28 @@ struct ike_sa_id_t { * * @param responder_spi SPI of responder to set */ - void (*set_responder_spi) (ike_sa_id_t *this, u_int64_t responder_spi); + void (*set_responder_spi) (ike_sa_id_t *this, uint64_t responder_spi); /** * Set the SPI of the initiator. * * @param initiator_spi SPI to set */ - void (*set_initiator_spi) (ike_sa_id_t *this, u_int64_t initiator_spi); + void (*set_initiator_spi) (ike_sa_id_t *this, uint64_t initiator_spi); /** * Get the initiator SPI. * * @return SPI of the initiator */ - u_int64_t (*get_initiator_spi) (ike_sa_id_t *this); + uint64_t (*get_initiator_spi) (ike_sa_id_t *this); /** * Get the responder SPI. * * @return SPI of the responder */ - u_int64_t (*get_responder_spi) (ike_sa_id_t *this); + uint64_t (*get_responder_spi) (ike_sa_id_t *this); /** * Check if two ike_sa_id_t objects are equal. @@ -131,7 +131,7 @@ struct ike_sa_id_t { * @param is_initiaor TRUE if we are the original initiator * @return ike_sa_id_t object */ -ike_sa_id_t * ike_sa_id_create(u_int8_t ike_version, u_int64_t initiator_spi, - u_int64_t responder_spi, bool is_initiaor); +ike_sa_id_t * ike_sa_id_create(uint8_t ike_version, uint64_t initiator_spi, + uint64_t responder_spi, bool is_initiaor); #endif /** IKE_SA_ID_H_ @}*/ diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 307ea3b4a..ce44207c4 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -113,7 +113,7 @@ struct entry_t { /** * message ID or hash of currently processing message, -1 if none */ - u_int32_t processing; + uint32_t processing; }; /** @@ -265,7 +265,7 @@ struct init_hash_t { chunk_t hash; /** our SPI allocated for the IKE_SA based on this message */ - u_int64_t our_spi; + uint64_t our_spi; }; typedef struct segment_t segment_t; @@ -977,9 +977,9 @@ static void remove_connected_peers(private_ike_sa_manager_t *this, entry_t *entr /** * Get a random SPI for new IKE_SAs */ -static u_int64_t get_spi(private_ike_sa_manager_t *this) +static uint64_t get_spi(private_ike_sa_manager_t *this) { - u_int64_t spi; + uint64_t spi; this->spi_lock->read_lock(this->spi_lock); if (this->spi_cb.cb) @@ -987,7 +987,7 @@ static u_int64_t get_spi(private_ike_sa_manager_t *this) spi = this->spi_cb.cb(this->spi_cb.data); } else if (!this->rng || - !this->rng->get_bytes(this->rng, sizeof(spi), (u_int8_t*)&spi)) + !this->rng->get_bytes(this->rng, sizeof(spi), (uint8_t*)&spi)) { spi = 0; } @@ -1007,8 +1007,8 @@ static bool get_init_hash(hasher_t *hasher, message_t *message, chunk_t *hash) if (message->get_first_payload_type(message) == PLV1_FRAGMENT) { /* only hash the source IP, port and SPI for fragmented init messages */ - u_int16_t port; - u_int64_t spi; + uint16_t port; + uint64_t spi; src = message->get_source(message); if (!hasher->allocate_hash(hasher, src->get_address(src), NULL)) @@ -1050,13 +1050,13 @@ static bool get_init_hash(hasher_t *hasher, message_t *message, chunk_t *hash) * FAILED if the SPI allocation failed */ static status_t check_and_put_init_hash(private_ike_sa_manager_t *this, - chunk_t init_hash, u_int64_t *our_spi) + chunk_t init_hash, uint64_t *our_spi) { table_item_t *item; u_int row, segment; mutex_t *mutex; init_hash_t *init; - u_int64_t spi; + uint64_t spi; row = chunk_hash(init_hash) & this->table_mask; segment = row & this->segment_mask; @@ -1174,8 +1174,8 @@ METHOD(ike_sa_manager_t, checkout_new, ike_sa_t*, { ike_sa_id_t *ike_sa_id; ike_sa_t *ike_sa; - u_int8_t ike_version; - u_int64_t spi; + uint8_t ike_version; + uint64_t spi; ike_version = version == IKEV1 ? IKEV1_MAJOR_VERSION : IKEV2_MAJOR_VERSION; @@ -1208,7 +1208,7 @@ METHOD(ike_sa_manager_t, checkout_new, ike_sa_t*, /** * Get the message ID or message hash to detect early retransmissions */ -static u_int32_t get_message_id_or_hash(message_t *message) +static uint32_t get_message_id_or_hash(message_t *message) { if (message->get_major_version(message) == IKEV1_MAJOR_VERSION) { @@ -1273,7 +1273,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*, if (is_init) { hasher_t *hasher; - u_int64_t our_spi; + uint64_t our_spi; chunk_t hash; hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); @@ -1415,7 +1415,8 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*, { continue; } - if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING) + if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING || + entry->ike_sa->get_state(entry->ike_sa) == IKE_REKEYED) { /* skip IKE_SAs which are not usable, wake other waiting threads */ entry->condvar->signal(entry->condvar); continue; @@ -1455,7 +1456,7 @@ out: } METHOD(ike_sa_manager_t, checkout_by_id, ike_sa_t*, - private_ike_sa_manager_t *this, u_int32_t id) + private_ike_sa_manager_t *this, uint32_t id) { enumerator_t *enumerator; entry_t *entry; @@ -2094,10 +2095,41 @@ METHOD(ike_sa_manager_t, set_spi_cb, void, this->spi_lock->unlock(this->spi_lock); } +/** + * Destroy all entries + */ +static void destroy_all_entries(private_ike_sa_manager_t *this) +{ + enumerator_t *enumerator; + entry_t *entry; + u_int segment; + + enumerator = create_table_enumerator(this); + while (enumerator->enumerate(enumerator, &entry, &segment)) + { + charon->bus->set_sa(charon->bus, entry->ike_sa); + if (entry->half_open) + { + remove_half_open(this, entry); + } + if (entry->my_id && entry->other_id) + { + remove_connected_peers(this, entry); + } + if (entry->init_hash.ptr) + { + remove_init_hash(this, entry->init_hash); + } + remove_entry_at((private_enumerator_t*)enumerator); + entry_destroy(entry); + } + enumerator->destroy(enumerator); + charon->bus->set_sa(charon->bus, NULL); +} + METHOD(ike_sa_manager_t, flush, void, private_ike_sa_manager_t *this) { - /* destroy all list entries */ enumerator_t *enumerator; entry_t *entry; u_int segment; @@ -2153,31 +2185,11 @@ METHOD(ike_sa_manager_t, flush, void, DBG2(DBG_MGR, "destroy all entries"); /* Step 4: destroy all entries */ - enumerator = create_table_enumerator(this); - while (enumerator->enumerate(enumerator, &entry, &segment)) - { - charon->bus->set_sa(charon->bus, entry->ike_sa); - if (entry->half_open) - { - remove_half_open(this, entry); - } - if (entry->my_id && entry->other_id) - { - remove_connected_peers(this, entry); - } - if (entry->init_hash.ptr) - { - remove_init_hash(this, entry->init_hash); - } - remove_entry_at((private_enumerator_t*)enumerator); - entry_destroy(entry); - } - enumerator->destroy(enumerator); - charon->bus->set_sa(charon->bus, NULL); + destroy_all_entries(this); unlock_all_segments(this); this->spi_lock->write_lock(this->spi_lock); - this->rng->destroy(this->rng); + DESTROY_IF(this->rng); this->rng = NULL; this->spi_cb.cb = NULL; this->spi_cb.data = NULL; @@ -2189,7 +2201,11 @@ METHOD(ike_sa_manager_t, destroy, void, { u_int i; - /* these are already cleared in flush() above */ + /* in case new SAs were checked in after flush() was called */ + lock_all_segments(this); + destroy_all_entries(this); + unlock_all_segments(this); + free(this->ike_sa_table); free(this->half_open_table); free(this->connected_peers_table); diff --git a/src/libcharon/sa/ike_sa_manager.h b/src/libcharon/sa/ike_sa_manager.h index f1b7c2579..4298c54e2 100644 --- a/src/libcharon/sa/ike_sa_manager.h +++ b/src/libcharon/sa/ike_sa_manager.h @@ -38,7 +38,7 @@ typedef struct ike_sa_manager_t ike_sa_manager_t; * @param data data supplied during registration of the callback * @return allocated SPI, 0 on failure */ -typedef u_int64_t (*spi_cb_t)(void *data); +typedef uint64_t (*spi_cb_t)(void *data); /** * Manages and synchronizes access to all IKE_SAs. @@ -147,7 +147,7 @@ struct ike_sa_manager_t { * - checked out IKE_SA, if found * - NULL, if not found */ - ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, u_int32_t id); + ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, uint32_t id); /** * Check out an IKE_SA by the policy/connection name. diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c index e428966ad..be6b03bef 100644 --- a/src/libcharon/sa/ikev1/keymat_v1.c +++ b/src/libcharon/sa/ikev1/keymat_v1.c @@ -32,7 +32,7 @@ typedef struct private_keymat_v1_t private_keymat_v1_t; */ typedef struct { /** message ID */ - u_int32_t mid; + uint32_t mid; /** current IV */ chunk_t iv; /** last block of encrypted message */ @@ -128,7 +128,7 @@ static void iv_data_destroy(iv_data_t *this) */ typedef struct { /** message ID */ - u_int32_t mid; + uint32_t mid; /** Ni_b (Nonce from first message) */ chunk_t n_i; /** Nr_b (Nonce from second message) */ @@ -272,7 +272,7 @@ static bool expand_skeyid_e(chunk_t skeyid_e, size_t key_size, prf_t *prf, static aead_t *create_aead(proposal_t *proposal, prf_t *prf, chunk_t skeyid_e) { private_aead_t *this; - u_int16_t alg, key_size; + uint16_t alg, key_size; crypter_t *crypter; chunk_t ka; @@ -324,7 +324,7 @@ static aead_t *create_aead(proposal_t *proposal, prf_t *prf, chunk_t skeyid_e) /** * Converts integrity algorithm to PRF algorithm */ -static u_int16_t auth_to_prf(u_int16_t alg) +static uint16_t auth_to_prf(uint16_t alg) { switch (alg) { @@ -348,7 +348,7 @@ static u_int16_t auth_to_prf(u_int16_t alg) /** * Converts integrity algorithm to hash algorithm */ -static u_int16_t auth_to_hash(u_int16_t alg) +static uint16_t auth_to_hash(uint16_t alg) { switch (alg) { @@ -370,7 +370,7 @@ static u_int16_t auth_to_hash(u_int16_t alg) /** * Adjust the key length for PRF algorithms that expect a fixed key length. */ -static void adjust_keylen(u_int16_t alg, chunk_t *key) +static void adjust_keylen(uint16_t alg, chunk_t *key) { switch (alg) { @@ -393,10 +393,10 @@ METHOD(keymat_v1_t, derive_ike_keys, bool, { chunk_t g_xy, g_xi, g_xr, dh_me, spi_i, spi_r, nonces, data, skeyid_e; chunk_t skeyid; - u_int16_t alg; + uint16_t alg; - spi_i = chunk_alloca(sizeof(u_int64_t)); - spi_r = chunk_alloca(sizeof(u_int64_t)); + spi_i = chunk_alloca(sizeof(uint64_t)); + spi_r = chunk_alloca(sizeof(uint64_t)); if (!proposal->get_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, &alg, NULL)) { /* no PRF negotiated, use HMAC version of integrity algorithm instead */ @@ -431,8 +431,8 @@ METHOD(keymat_v1_t, derive_ike_keys, bool, } DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &g_xy); - *((u_int64_t*)spi_i.ptr) = id->get_initiator_spi(id); - *((u_int64_t*)spi_r.ptr) = id->get_responder_spi(id); + *((uint64_t*)spi_i.ptr) = id->get_initiator_spi(id); + *((uint64_t*)spi_r.ptr) = id->get_responder_spi(id); nonces = chunk_cata("cc", nonce_i, nonce_r); switch (auth) @@ -585,11 +585,11 @@ METHOD(keymat_v1_t, derive_ike_keys, bool, METHOD(keymat_v1_t, derive_child_keys, bool, private_keymat_v1_t *this, proposal_t *proposal, diffie_hellman_t *dh, - u_int32_t spi_i, u_int32_t spi_r, chunk_t nonce_i, chunk_t nonce_r, + uint32_t spi_i, uint32_t spi_r, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, chunk_t *encr_r, chunk_t *integ_r) { - u_int16_t enc_alg, int_alg, enc_size = 0, int_size = 0; - u_int8_t protocol; + uint16_t enc_alg, int_alg, enc_size = 0, int_size = 0; + uint8_t protocol; prf_plus_t *prf_plus; chunk_t seed, secret = chunk_empty; bool success = FALSE; @@ -725,7 +725,7 @@ failure: METHOD(keymat_v1_t, create_hasher, bool, private_keymat_v1_t *this, proposal_t *proposal) { - u_int16_t alg; + uint16_t alg; if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL) || (alg = auth_to_hash(alg)) == HASH_UNKNOWN) { @@ -754,7 +754,7 @@ METHOD(keymat_v1_t, get_hash, bool, ike_sa_id_t *ike_sa_id, chunk_t sa_i, chunk_t id, chunk_t *hash) { chunk_t data; - u_int64_t spi, spi_other; + uint64_t spi, spi_other; /* HASH_I = prf(SKEYID, g^xi | g^xr | CKY-I | CKY-R | SAi_b | IDii_b ) * HASH_R = prf(SKEYID, g^xr | g^xi | CKY-R | CKY-I | SAi_b | IDir_b ) @@ -810,7 +810,7 @@ static chunk_t get_message_data(message_t *message, generator_t *generator) { payload_t *payload, *next; enumerator_t *enumerator; - u_int32_t *lenpos; + uint32_t *lenpos; if (message->is_encoded(message)) { /* inbound, although the message is generated, we cannot access the @@ -850,7 +850,7 @@ static chunk_t get_message_data(message_t *message, generator_t *generator) * Try to find data about a Quick Mode with the given message ID, * if none is found, state is generated. */ -static qm_data_t *lookup_quick_mode(private_keymat_v1_t *this, u_int32_t mid) +static qm_data_t *lookup_quick_mode(private_keymat_v1_t *this, uint32_t mid) { enumerator_t *enumerator; qm_data_t *qm, *found = NULL; @@ -885,7 +885,7 @@ static qm_data_t *lookup_quick_mode(private_keymat_v1_t *this, u_int32_t mid) METHOD(keymat_v1_t, get_hash_phase2, bool, private_keymat_v1_t *this, message_t *message, chunk_t *hash) { - u_int32_t mid, mid_n; + uint32_t mid, mid_n; chunk_t data = chunk_empty; bool add_message = TRUE; char *name = "Hash"; @@ -993,7 +993,7 @@ static bool generate_iv(private_keymat_v1_t *this, iv_data_t *iv) else { /* initial phase 2 IV = hash(last_phase1_block | mid) */ - u_int32_t net;; + uint32_t net;; chunk_t data; net = htonl(iv->mid); @@ -1014,7 +1014,7 @@ static bool generate_iv(private_keymat_v1_t *this, iv_data_t *iv) /** * Try to find an IV for the given message ID, if not found, generate it. */ -static iv_data_t *lookup_iv(private_keymat_v1_t *this, u_int32_t mid) +static iv_data_t *lookup_iv(private_keymat_v1_t *this, uint32_t mid) { enumerator_t *enumerator; iv_data_t *iv, *found = NULL; @@ -1057,7 +1057,7 @@ static iv_data_t *lookup_iv(private_keymat_v1_t *this, u_int32_t mid) } METHOD(keymat_v1_t, get_iv, bool, - private_keymat_v1_t *this, u_int32_t mid, chunk_t *out) + private_keymat_v1_t *this, uint32_t mid, chunk_t *out) { iv_data_t *iv; @@ -1071,7 +1071,7 @@ METHOD(keymat_v1_t, get_iv, bool, } METHOD(keymat_v1_t, update_iv, bool, - private_keymat_v1_t *this, u_int32_t mid, chunk_t last_block) + private_keymat_v1_t *this, uint32_t mid, chunk_t last_block) { iv_data_t *iv = lookup_iv(this, mid); if (iv) @@ -1084,7 +1084,7 @@ METHOD(keymat_v1_t, update_iv, bool, } METHOD(keymat_v1_t, confirm_iv, bool, - private_keymat_v1_t *this, u_int32_t mid) + private_keymat_v1_t *this, uint32_t mid) { iv_data_t *iv = lookup_iv(this, mid); if (iv) diff --git a/src/libcharon/sa/ikev1/keymat_v1.h b/src/libcharon/sa/ikev1/keymat_v1.h index cc9f3b339..46eeea8b6 100644 --- a/src/libcharon/sa/ikev1/keymat_v1.h +++ b/src/libcharon/sa/ikev1/keymat_v1.h @@ -72,7 +72,7 @@ struct keymat_v1_t { * @param integ_r allocated responders integrity key */ bool (*derive_child_keys)(keymat_v1_t *this, proposal_t *proposal, - diffie_hellman_t *dh, u_int32_t spi_i, u_int32_t spi_r, + diffie_hellman_t *dh, uint32_t spi_i, uint32_t spi_r, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, chunk_t *encr_r, chunk_t *integ_r); @@ -127,7 +127,7 @@ struct keymat_v1_t { * @param iv chunk receiving IV, internal data * @return TRUE if IV allocated successfully */ - bool (*get_iv)(keymat_v1_t *this, u_int32_t mid, chunk_t *iv); + bool (*get_iv)(keymat_v1_t *this, uint32_t mid, chunk_t *iv); /** * Updates the IV for the next message with the given message ID. @@ -141,7 +141,7 @@ struct keymat_v1_t { * @param last_block last block of encrypted message (gets cloned) * @return TRUE if IV updated successfully */ - bool (*update_iv)(keymat_v1_t *this, u_int32_t mid, chunk_t last_block); + bool (*update_iv)(keymat_v1_t *this, uint32_t mid, chunk_t last_block); /** * Confirms the updated IV for the given message ID. @@ -152,7 +152,7 @@ struct keymat_v1_t { * @param mid message ID * @return TRUE if IV confirmed successfully */ - bool (*confirm_iv)(keymat_v1_t *this, u_int32_t mid); + bool (*confirm_iv)(keymat_v1_t *this, uint32_t mid); }; /** diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 3c601a4fa..b0c4f5f84 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1,7 +1,7 @@ /* - * Copyright (C) 2007-2015 Tobias Brunner + * Copyright (C) 2007-2016 Tobias Brunner * Copyright (C) 2007-2011 Martin Willi - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -67,7 +67,7 @@ struct exchange_t { /** * Message ID used for this transaction */ - u_int32_t mid; + uint32_t mid; /** * generated packet for retransmission @@ -104,12 +104,12 @@ struct private_task_manager_t { /** * Message ID of the last response */ - u_int32_t mid; + uint32_t mid; /** * Hash of a previously received message */ - u_int32_t hash; + uint32_t hash; /** * packet(s) for retransmission @@ -119,7 +119,7 @@ struct private_task_manager_t { /** * Sequence number of the last sent message */ - u_int32_t seqnr; + uint32_t seqnr; /** * how many times we have retransmitted so far @@ -135,12 +135,12 @@ struct private_task_manager_t { /** * Message ID of the exchange */ - u_int32_t mid; + uint32_t mid; /** * Hashes of old responses we can ignore */ - u_int32_t old_hashes[MAX_OLD_HASHES]; + uint32_t old_hashes[MAX_OLD_HASHES]; /** * Position in old hash array @@ -150,7 +150,7 @@ struct private_task_manager_t { /** * Sequence number of the last sent message */ - u_int32_t seqnr; + uint32_t seqnr; /** * how many times we have retransmitted so far @@ -212,12 +212,12 @@ struct private_task_manager_t { /** * Sequence number for sending DPD requests */ - u_int32_t dpd_send; + uint32_t dpd_send; /** * Sequence number for received DPD requests */ - u_int32_t dpd_recv; + uint32_t dpd_recv; }; /** @@ -341,11 +341,11 @@ static bool generate_message(private_task_manager_t *this, message_t *message, /** * Retransmit a packet (or its fragments) */ -static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr, +static status_t retransmit_packet(private_task_manager_t *this, uint32_t seqnr, u_int mid, u_int retransmitted, array_t *packets) { packet_t *packet; - u_int32_t t; + uint32_t t; array_get(packets, 0, &packet); if (retransmitted > this->retransmit_tries) @@ -354,14 +354,15 @@ static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr, charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_TIMEOUT, packet); return DESTROY_ME; } - t = (u_int32_t)(this->retransmit_timeout * 1000.0 * + t = (uint32_t)(this->retransmit_timeout * 1000.0 * pow(this->retransmit_base, retransmitted)); if (retransmitted) { DBG1(DBG_IKE, "sending retransmit %u of %s message ID %u, seq %u", retransmitted, seqnr < RESPONDING_SEQ ? "request" : "response", mid, seqnr < RESPONDING_SEQ ? seqnr : seqnr - RESPONDING_SEQ); - charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet); + charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet, + retransmitted); } send_packets(this, packets); lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*) @@ -370,7 +371,7 @@ static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr, } METHOD(task_manager_t, retransmit, status_t, - private_task_manager_t *this, u_int32_t seqnr) + private_task_manager_t *this, uint32_t seqnr) { status_t status = SUCCESS; @@ -514,26 +515,26 @@ METHOD(task_manager_t, initiate, status_t, new_mid = TRUE; break; } - if (!mode_config_expected(this) && - activate_task(this, TASK_QUICK_MODE)) + if (activate_task(this, TASK_ISAKMP_DELETE)) { - exchange = QUICK_MODE; + exchange = INFORMATIONAL_V1; new_mid = TRUE; break; } - if (activate_task(this, TASK_INFORMATIONAL)) + if (activate_task(this, TASK_QUICK_DELETE)) { exchange = INFORMATIONAL_V1; new_mid = TRUE; break; } - if (activate_task(this, TASK_QUICK_DELETE)) + if (!mode_config_expected(this) && + activate_task(this, TASK_QUICK_MODE)) { - exchange = INFORMATIONAL_V1; + exchange = QUICK_MODE; new_mid = TRUE; break; } - if (activate_task(this, TASK_ISAKMP_DELETE)) + if (activate_task(this, TASK_INFORMATIONAL)) { exchange = INFORMATIONAL_V1; new_mid = TRUE; @@ -807,7 +808,7 @@ static void send_notify(private_task_manager_t *this, message_t *request, message_t *response; array_t *packets = NULL; host_t *me, *other; - u_int32_t mid; + uint32_t mid; if (request->get_exchange_type(request) == INFORMATIONAL_V1) { /* don't respond to INFORMATIONAL requests to avoid a notify war */ @@ -857,7 +858,7 @@ static bool process_dpd(private_task_manager_t *this, message_t *message) { notify_payload_t *notify; notify_type_t type; - u_int32_t seq; + uint32_t seq; chunk_t data; type = DPD_R_U_THERE; @@ -910,7 +911,7 @@ static bool process_dpd(private_task_manager_t *this, message_t *message) * Check if we already have a quick mode task queued for the exchange with the * given message ID */ -static bool have_quick_mode_task(private_task_manager_t *this, u_int32_t mid) +static bool have_quick_mode_task(private_task_manager_t *this, uint32_t mid) { enumerator_t *enumerator; quick_mode_t *qm; @@ -935,9 +936,9 @@ static bool have_quick_mode_task(private_task_manager_t *this, u_int32_t mid) } /** - * Check if we still have an aggressive mode task queued + * Check if we still have a specific task queued */ -static bool have_aggressive_mode_task(private_task_manager_t *this) +static bool have_task_queued(private_task_manager_t *this, task_type_t type) { enumerator_t *enumerator; task_t *task; @@ -946,7 +947,7 @@ static bool have_aggressive_mode_task(private_task_manager_t *this) enumerator = this->passive_tasks->create_enumerator(this->passive_tasks); while (enumerator->enumerate(enumerator, &task)) { - if (task->get_type(task) == TASK_AGGRESSIVE_MODE) + if (task->get_type(task) == type) { found = TRUE; break; @@ -1180,6 +1181,12 @@ static status_t process_response(private_task_manager_t *this, } enumerator->destroy(enumerator); + if (this->initiating.retransmitted) + { + packet_t *packet = NULL; + array_get(this->initiating.packets, 0, &packet); + charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_CLEARED, packet); + } this->initiating.type = EXCHANGE_TYPE_UNDEFINED; clear_packets(this->initiating.packets); @@ -1305,7 +1312,7 @@ static status_t queue_message(private_task_manager_t *this, message_t *msg) METHOD(task_manager_t, process_message, status_t, private_task_manager_t *this, message_t *msg) { - u_int32_t hash, mid, i; + uint32_t hash, mid, i; host_t *me, *other; status_t status; @@ -1405,7 +1412,7 @@ METHOD(task_manager_t, process_message, status_t, /* drop XAuth/Mode Config/Quick Mode messages until we received the last * Aggressive Mode message. since Informational messages are not * retransmitted we queue them. */ - if (have_aggressive_mode_task(this)) + if (have_task_queued(this, TASK_AGGRESSIVE_MODE)) { if (msg->get_exchange_type(msg) == INFORMATIONAL_V1) { @@ -1427,6 +1434,13 @@ METHOD(task_manager_t, process_message, status_t, return queue_message(this, msg); } + /* some peers send INITIAL_CONTACT notifies during XAuth, cache it */ + if (have_task_queued(this, TASK_XAUTH) && + msg->get_exchange_type(msg) == INFORMATIONAL_V1) + { + return queue_message(this, msg); + } + msg->set_request(msg, TRUE); charon->bus->message(charon->bus, msg, TRUE, FALSE); status = parse_message(this, msg); @@ -1499,8 +1513,8 @@ static bool has_queued(private_task_manager_t *this, task_type_t type) return found; } -METHOD(task_manager_t, queue_task, void, - private_task_manager_t *this, task_t *task) +METHOD(task_manager_t, queue_task_delayed, void, + private_task_manager_t *this, task_t *task, uint32_t delay) { task_type_t type = task->get_type(task); @@ -1521,6 +1535,12 @@ METHOD(task_manager_t, queue_task, void, this->queued_tasks->insert_last(this->queued_tasks, task); } +METHOD(task_manager_t, queue_task, void, + private_task_manager_t *this, task_t *task) +{ + queue_task_delayed(this, task, 0); +} + METHOD(task_manager_t, queue_ike, void, private_task_manager_t *this) { @@ -1660,7 +1680,7 @@ METHOD(task_manager_t, queue_mobike, void, } METHOD(task_manager_t, queue_child, void, - private_task_manager_t *this, child_cfg_t *cfg, u_int32_t reqid, + private_task_manager_t *this, child_cfg_t *cfg, uint32_t reqid, traffic_selector_t *tsi, traffic_selector_t *tsr) { quick_mode_t *task; @@ -1739,7 +1759,7 @@ static traffic_selector_t* get_first_ts(child_sa_t *child_sa, bool local) } METHOD(task_manager_t, queue_child_rekey, void, - private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi) + private_task_manager_t *this, protocol_id_t protocol, uint32_t spi) { child_sa_t *child_sa; child_cfg_t *cfg; @@ -1754,6 +1774,7 @@ METHOD(task_manager_t, queue_child_rekey, void, { if (is_redundant(this, child_sa)) { + child_sa->set_state(child_sa, CHILD_REKEYED); queue_task(this, (task_t*)quick_delete_create(this->ike_sa, protocol, spi, FALSE, FALSE)); } @@ -1774,7 +1795,7 @@ METHOD(task_manager_t, queue_child_rekey, void, } METHOD(task_manager_t, queue_child_delete, void, - private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi, + private_task_manager_t *this, protocol_id_t protocol, uint32_t spi, bool expired) { queue_task(this, (task_t*)quick_delete_create(this->ike_sa, protocol, @@ -1785,7 +1806,7 @@ METHOD(task_manager_t, queue_dpd, void, private_task_manager_t *this) { peer_cfg_t *peer_cfg; - u_int32_t t, retransmit; + uint32_t t, retransmit; queue_task(this, (task_t*)isakmp_dpd_create(this->ike_sa, DPD_R_U_THERE, this->dpd_send++)); @@ -1798,7 +1819,7 @@ METHOD(task_manager_t, queue_dpd, void, /* use the same timeout as a retransmitting IKE message would have */ for (retransmit = 0; retransmit <= this->retransmit_tries; retransmit++) { - t += (u_int32_t)(this->retransmit_timeout * 1000.0 * + t += (uint32_t)(this->retransmit_timeout * 1000.0 * pow(this->retransmit_base, retransmit)); } } @@ -1871,7 +1892,7 @@ METHOD(task_manager_t, incr_mid, void, } METHOD(task_manager_t, reset, void, - private_task_manager_t *this, u_int32_t initiate, u_int32_t respond) + private_task_manager_t *this, uint32_t initiate, uint32_t respond) { enumerator_t *enumerator; task_t *task; @@ -1960,6 +1981,7 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .task_manager = { .process_message = _process_message, .queue_task = _queue_task, + .queue_task_delayed = _queue_task_delayed, .queue_ike = _queue_ike, .queue_ike_rekey = _queue_ike_rekey, .queue_ike_reauth = _queue_ike_reauth, diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c index 710bf1cd2..9b5f676a3 100644 --- a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c +++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c @@ -77,7 +77,7 @@ struct private_aggressive_mode_t { /** * Negotiated SA lifetime */ - u_int32_t lifetime; + uint32_t lifetime; /** * Negotiated authentication method @@ -164,7 +164,7 @@ static status_t send_notify(private_aggressive_mode_t *this, notify_type_t type) { notify_payload_t *notify; ike_sa_id_t *ike_sa_id; - u_int64_t spi_i, spi_r; + uint64_t spi_i, spi_r; chunk_t spi; notify = notify_payload_create_from_protocol_and_type(PLV1_NOTIFY, @@ -219,7 +219,7 @@ METHOD(task_t, build_i, status_t, linked_list_t *proposals; identification_t *id; packet_t *packet; - u_int16_t group; + uint16_t group; DBG0(DBG_IKE, "initiating Aggressive Mode IKE_SA %s[%d] to %H", this->ike_sa->get_name(this->ike_sa), @@ -377,7 +377,8 @@ METHOD(task_t, process_r, status_t, id_payload_t *id_payload; identification_t *id; linked_list_t *list; - u_int16_t group; + uint16_t group; + bool prefer_configured; this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); DBG0(DBG_IKE, "%H is initiating a Aggressive Mode IKE_SA", @@ -401,8 +402,10 @@ METHOD(task_t, process_r, status_t, } list = sa_payload->get_proposals(sa_payload); + prefer_configured = lib->settings->get_bool(lib->settings, + "%s.prefer_configured_proposals", TRUE, lib->ns); this->proposal = this->ike_cfg->select_proposal(this->ike_cfg, - list, FALSE); + list, FALSE, prefer_configured); list->destroy_offset(list, offsetof(proposal_t, destroy)); if (!this->proposal) { @@ -629,7 +632,7 @@ METHOD(task_t, process_i, status_t, id_payload_t *id_payload; identification_t *id, *cid; linked_list_t *list; - u_int32_t lifetime; + uint32_t lifetime; sa_payload = (sa_payload_t*)message->get_payload(message, PLV1_SECURITY_ASSOCIATION); @@ -640,7 +643,7 @@ METHOD(task_t, process_i, status_t, } list = sa_payload->get_proposals(sa_payload); this->proposal = this->ike_cfg->select_proposal(this->ike_cfg, - list, FALSE); + list, FALSE, TRUE); list->destroy_offset(list, offsetof(proposal_t, destroy)); if (!this->proposal) { diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_delete.c b/src/libcharon/sa/ikev1/tasks/isakmp_delete.c index a56805afb..df0293d4f 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_delete.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_delete.c @@ -81,7 +81,7 @@ METHOD(task_t, process_r, status_t, payload_t *payload; delete_payload_t *delete_payload; ike_sa_id_t *id; - u_int64_t spi_i, spi_r; + uint64_t spi_i, spi_r; bool found = FALSE; /* some peers send DELETE payloads for other IKE_SAs, e.g. those for expired diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_dpd.c b/src/libcharon/sa/ikev1/tasks/isakmp_dpd.c index 5522e9221..840d352b1 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_dpd.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_dpd.c @@ -33,7 +33,7 @@ struct private_isakmp_dpd_t { /** * Sequence number. */ - u_int32_t seqnr; + uint32_t seqnr; /** * DPD notify type @@ -51,8 +51,8 @@ METHOD(task_t, build, status_t, { notify_payload_t *notify; ike_sa_id_t *ike_sa_id; - u_int64_t spi_i, spi_r; - u_int32_t seqnr; + uint64_t spi_i, spi_r; + uint32_t seqnr; chunk_t spi; notify = notify_payload_create_from_protocol_and_type(PLV1_NOTIFY, @@ -100,7 +100,7 @@ METHOD(task_t, destroy, void, * Described in header. */ isakmp_dpd_t *isakmp_dpd_create(ike_sa_t *ike_sa, notify_type_t type, - u_int32_t seqnr) + uint32_t seqnr) { private_isakmp_dpd_t *this; diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_dpd.h b/src/libcharon/sa/ikev1/tasks/isakmp_dpd.h index 06a0175eb..9a69b423c 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_dpd.h +++ b/src/libcharon/sa/ikev1/tasks/isakmp_dpd.h @@ -47,6 +47,6 @@ struct isakmp_dpd_t { * @return ISAKMP_DPD task to handle by the task_manager */ isakmp_dpd_t *isakmp_dpd_create(ike_sa_t *ike_sa, notify_type_t type, - u_int32_t seqnr); + uint32_t seqnr); #endif /** ISAKMP_DPD_H_ @}*/ diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c index cb1a31371..d17948cd0 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c @@ -129,8 +129,8 @@ static chunk_t generate_natd_hash(private_isakmp_natd_t *this, { hasher_t *hasher; chunk_t natd_chunk, natd_hash; - u_int64_t spi_i, spi_r; - u_int16_t port; + uint64_t spi_i, spi_r; + uint16_t port; hasher = this->keymat->get_hasher(this->keymat); if (!hasher) diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c index 0162fd84e..f28b83e8a 100644 --- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c +++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c @@ -170,7 +170,7 @@ static struct { * for fragmentation of base ISAKMP messages (Cisco adds that and thus sends * 0xc0000000) */ -static const u_int32_t fragmentation_ike = 0x80000000; +static const uint32_t fragmentation_ike = 0x80000000; static bool is_known_vid(chunk_t data, int i) { diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c index 3ea4a2a85..628ea0de8 100644 --- a/src/libcharon/sa/ikev1/tasks/main_mode.c +++ b/src/libcharon/sa/ikev1/tasks/main_mode.c @@ -77,7 +77,7 @@ struct private_main_mode_t { /** * Negotiated SA lifetime */ - u_int32_t lifetime; + uint32_t lifetime; /** * Negotiated authentication method @@ -173,7 +173,7 @@ static status_t send_notify(private_main_mode_t *this, notify_type_t type) { notify_payload_t *notify; ike_sa_id_t *ike_sa_id; - u_int64_t spi_i, spi_r; + uint64_t spi_i, spi_r; chunk_t spi; notify = notify_payload_create_from_protocol_and_type(PLV1_NOTIFY, @@ -215,7 +215,7 @@ static void add_initial_contact(private_main_mode_t *this, message_t *message, host_t *host; notify_payload_t *notify; ike_sa_id_t *ike_sa_id; - u_int64_t spi_i, spi_r; + uint64_t spi_i, spi_r; chunk_t spi; idr = this->ph1->get_id(this->ph1, this->peer_cfg, FALSE); @@ -303,7 +303,7 @@ METHOD(task_t, build_i, status_t, } case MM_SA: { - u_int16_t group; + uint16_t group; if (!this->ph1->create_hasher(this->ph1)) { @@ -367,7 +367,7 @@ METHOD(task_t, process_r, status_t, { linked_list_t *list; sa_payload_t *sa_payload; - bool private; + bool private, prefer_configured; this->ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa); DBG0(DBG_IKE, "%H is initiating a Main Mode IKE_SA", @@ -392,9 +392,11 @@ METHOD(task_t, process_r, status_t, list = sa_payload->get_proposals(sa_payload); private = this->ike_sa->supports_extension(this->ike_sa, - EXT_STRONGSWAN); + EXT_STRONGSWAN); + prefer_configured = lib->settings->get_bool(lib->settings, + "%s.prefer_configured_proposals", TRUE, lib->ns); this->proposal = this->ike_cfg->select_proposal(this->ike_cfg, - list, private); + list, private, prefer_configured); list->destroy_offset(list, offsetof(proposal_t, destroy)); if (!this->proposal) { @@ -411,7 +413,7 @@ METHOD(task_t, process_r, status_t, } case MM_SA: { - u_int16_t group; + uint16_t group; if (!this->ph1->create_hasher(this->ph1)) { @@ -627,7 +629,7 @@ METHOD(task_t, process_i, status_t, linked_list_t *list; sa_payload_t *sa_payload; auth_method_t method; - u_int32_t lifetime; + uint32_t lifetime; bool private; sa_payload = (sa_payload_t*)message->get_payload(message, @@ -641,7 +643,7 @@ METHOD(task_t, process_i, status_t, private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); this->proposal = this->ike_cfg->select_proposal(this->ike_cfg, - list, private); + list, private, TRUE); list->destroy_offset(list, offsetof(proposal_t, destroy)); if (!this->proposal) { diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c index b9f924009..7098d24a2 100644 --- a/src/libcharon/sa/ikev1/tasks/mode_config.c +++ b/src/libcharon/sa/ikev1/tasks/mode_config.c @@ -58,7 +58,7 @@ struct private_mode_config_t { /** * Identifier to include in response */ - u_int16_t identifier; + uint16_t identifier; }; /** diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.c b/src/libcharon/sa/ikev1/tasks/quick_delete.c index ade59a2dd..66ef50811 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_delete.c +++ b/src/libcharon/sa/ikev1/tasks/quick_delete.c @@ -69,7 +69,7 @@ struct private_quick_delete_t { /** * Inbound SPI of CHILD_SA to delete */ - u_int32_t spi; + uint32_t spi; /** * Send delete even if SA does not exist @@ -86,9 +86,9 @@ 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, bool remote_close) + uint32_t spi, bool remote_close) { - u_int64_t bytes_in, bytes_out; + uint64_t bytes_in, bytes_out; child_sa_t *child_sa; linked_list_t *my_ts, *other_ts; child_cfg_t *child_cfg; @@ -200,7 +200,7 @@ METHOD(task_t, process_r, status_t, payload_t *payload; delete_payload_t *delete_payload; protocol_id_t protocol; - u_int32_t spi; + uint32_t spi; payloads = message->create_payload_enumerator(message); while (payloads->enumerate(payloads, &payload)) @@ -260,7 +260,7 @@ METHOD(task_t, destroy, void, * Described in header. */ quick_delete_t *quick_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol, - u_int32_t spi, bool force, bool expired) + uint32_t spi, bool force, bool expired) { private_quick_delete_t *this; diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.h b/src/libcharon/sa/ikev1/tasks/quick_delete.h index 4df30c8fe..6227b364b 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_delete.h +++ b/src/libcharon/sa/ikev1/tasks/quick_delete.h @@ -50,6 +50,6 @@ struct quick_delete_t { * @return quick_delete task to handle by the task_manager */ quick_delete_t *quick_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol, - u_int32_t spi, bool force, bool expired); + uint32_t spi, bool force, bool expired); #endif /** QUICK_DELETE_H_ @}*/ diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index b4fe04663..bbd1cb09f 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -98,22 +98,22 @@ struct private_quick_mode_t { /** * Initiators ESP SPI */ - u_int32_t spi_i; + uint32_t spi_i; /** * Responder ESP SPI */ - u_int32_t spi_r; + uint32_t spi_r; /** * Initiators IPComp CPI */ - u_int16_t cpi_i; + uint16_t cpi_i; /** * Responders IPComp CPI */ - u_int16_t cpi_r; + uint16_t cpi_r; /** * selected CHILD_SA proposal @@ -143,17 +143,17 @@ struct private_quick_mode_t { /** * Negotiated lifetime of new SA */ - u_int32_t lifetime; + uint32_t lifetime; /** - * Negotaited lifebytes of new SA + * Negotiated lifebytes of new SA */ - u_int64_t lifebytes; + uint64_t lifebytes; /** * Reqid to use, 0 for auto-allocate */ - u_int32_t reqid; + uint32_t reqid; /** * Explicit inbound mark value to use, if any @@ -168,7 +168,7 @@ struct private_quick_mode_t { /** * SPI of SA we rekey */ - u_int32_t rekey; + uint32_t rekey; /** * Delete old child after successful rekey @@ -193,7 +193,7 @@ struct private_quick_mode_t { /** * Message ID of handled quick mode exchange */ - u_int32_t mid; + uint32_t mid; /** states of quick mode */ enum { @@ -207,7 +207,7 @@ struct private_quick_mode_t { */ static void schedule_inactivity_timeout(private_quick_mode_t *this) { - u_int32_t timeout; + uint32_t timeout; bool close_ike; timeout = this->config->get_inactivity(this->config); @@ -722,12 +722,12 @@ static void get_lifetimes(private_quick_mode_t *this) { lifetime_cfg_t *lft; - lft = this->config->get_lifetime(this->config); + lft = this->config->get_lifetime(this->config, TRUE); if (lft->time.life) { this->lifetime = lft->time.life; } - else if (lft->bytes.life) + if (lft->bytes.life) { this->lifebytes = lft->bytes.life; } @@ -739,8 +739,8 @@ static void get_lifetimes(private_quick_mode_t *this) */ static void apply_lifetimes(private_quick_mode_t *this, sa_payload_t *sa_payload) { - u_int32_t lifetime; - u_int64_t lifebytes; + uint32_t lifetime; + uint64_t lifebytes; lifetime = sa_payload->get_lifetime(sa_payload); lifebytes = sa_payload->get_lifebytes(sa_payload); @@ -863,7 +863,7 @@ METHOD(task_t, build_i, status_t, if (group != MODP_NONE) { proposal_t *proposal; - u_int16_t preferred_group; + uint16_t preferred_group; proposal = this->ike_sa->get_proposal(this->ike_sa); proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP, @@ -1007,7 +1007,6 @@ static void check_for_rekeyed_child(private_quick_mode_t *this) { case CHILD_INSTALLED: case CHILD_REKEYING: - case CHILD_REKEYED: policies = child_sa->create_policy_enumerator(child_sa); if (policies->enumerate(policies, &local, &remote) && local->equals(local, this->tsr) && @@ -1026,9 +1025,10 @@ static void check_for_rekeyed_child(private_quick_mode_t *this) child_sa->get_unique_id(child_sa)); } policies->destroy(policies); - break; - default: - break; + break; + case CHILD_REKEYED: + default: + break; } } } @@ -1050,8 +1050,8 @@ METHOD(task_t, process_r, status_t, sa_payload_t *sa_payload; linked_list_t *tsi, *tsr, *hostsi, *hostsr, *list = NULL; peer_cfg_t *peer_cfg; - u_int16_t group; - bool private; + uint16_t group; + bool private, prefer_configured; sa_payload = (sa_payload_t*)message->get_payload(message, PLV1_SECURITY_ASSOCIATION); @@ -1109,8 +1109,10 @@ METHOD(task_t, process_r, status_t, } private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); - this->proposal = this->config->select_proposal(this->config, - list, FALSE, private); + prefer_configured = lib->settings->get_bool(lib->settings, + "%s.prefer_configured_proposals", TRUE, lib->ns); + this->proposal = this->config->select_proposal(this->config, list, + FALSE, private, prefer_configured); list->destroy_offset(list, offsetof(proposal_t, destroy)); get_lifetimes(this); @@ -1323,8 +1325,8 @@ METHOD(task_t, process_i, status_t, } private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); - this->proposal = this->config->select_proposal(this->config, - list, FALSE, private); + this->proposal = this->config->select_proposal(this->config, list, + FALSE, private, TRUE); list->destroy_offset(list, offsetof(proposal_t, destroy)); if (!this->proposal) { @@ -1365,14 +1367,14 @@ METHOD(task_t, get_type, task_type_t, return TASK_QUICK_MODE; } -METHOD(quick_mode_t, get_mid, u_int32_t, +METHOD(quick_mode_t, get_mid, uint32_t, private_quick_mode_t *this) { return this->mid; } METHOD(quick_mode_t, use_reqid, void, - private_quick_mode_t *this, u_int32_t reqid) + private_quick_mode_t *this, uint32_t reqid) { this->reqid = reqid; } @@ -1385,7 +1387,7 @@ METHOD(quick_mode_t, use_marks, void, } METHOD(quick_mode_t, rekey, void, - private_quick_mode_t *this, u_int32_t spi) + private_quick_mode_t *this, uint32_t spi) { this->rekey = spi; } diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.h b/src/libcharon/sa/ikev1/tasks/quick_mode.h index 062d63465..fe684568a 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.h +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.h @@ -46,14 +46,14 @@ struct quick_mode_t { * * @return message ID, or 0 (not defined yet or as initiator) */ - u_int32_t (*get_mid)(quick_mode_t *this); + uint32_t (*get_mid)(quick_mode_t *this); /** * Use a specific reqid to install this CHILD_SA. * * @param reqid reqid to use */ - void (*use_reqid)(quick_mode_t *this, u_int32_t reqid); + void (*use_reqid)(quick_mode_t *this, uint32_t reqid); /** * Use specific mark values, overriding configuration. @@ -68,7 +68,7 @@ struct quick_mode_t { * * @param spi spi of SA to rekey */ - void (*rekey)(quick_mode_t *this, u_int32_t spi); + void (*rekey)(quick_mode_t *this, uint32_t spi); }; /** diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c index ecdfc780d..968b4386c 100644 --- a/src/libcharon/sa/ikev1/tasks/xauth.c +++ b/src/libcharon/sa/ikev1/tasks/xauth.c @@ -68,7 +68,7 @@ struct private_xauth_t { /** * received identifier */ - u_int16_t identifier; + uint16_t identifier; /** * status of Xauth exchange diff --git a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c index 91f6187f9..3ab59fada 100644 --- a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c @@ -104,7 +104,7 @@ struct private_eap_authenticator_t { * load an EAP method */ static eap_method_t *load_method(private_eap_authenticator_t *this, - eap_type_t type, u_int32_t vendor, eap_role_t role) + eap_type_t type, uint32_t vendor, eap_role_t role) { identification_t *server, *peer, *aaa; auth_cfg_t *auth; @@ -143,7 +143,7 @@ static eap_payload_t* server_initiate_eap(private_eap_authenticator_t *this, auth_cfg_t *auth; eap_type_t type; identification_t *id; - u_int32_t vendor; + uint32_t vendor; eap_payload_t *out; char *action; @@ -237,7 +237,7 @@ static eap_payload_t* server_process_eap(private_eap_authenticator_t *this, eap_payload_t *in) { eap_type_t type, received_type, conf_type; - u_int32_t vendor, received_vendor, conf_vendor; + uint32_t vendor, received_vendor, conf_vendor; eap_payload_t *out; auth_cfg_t *auth; @@ -341,7 +341,7 @@ static eap_payload_t* client_process_eap(private_eap_authenticator_t *this, eap_payload_t *in) { eap_type_t type, conf_type; - u_int32_t vendor, conf_vendor; + uint32_t vendor, conf_vendor; auth_cfg_t *auth; eap_payload_t *out; identification_t *id; @@ -449,7 +449,7 @@ static bool verify_auth(private_eap_authenticator_t *this, message_t *message, auth_cfg_t *auth; keymat_v2_t *keymat; eap_type_t type; - u_int32_t vendor; + uint32_t vendor; auth_payload = (auth_payload_t*)message->get_payload(message, PLV2_AUTH); @@ -595,7 +595,7 @@ METHOD(authenticator_t, process_client, status_t, } if (this->require_mutual && !this->method->is_mutual(this->method)) { /* we require mutual authentication due to EAP-only */ - u_int32_t vendor; + uint32_t vendor; DBG1(DBG_IKE, "EAP-only authentication requires a mutual and " "MSK deriving EAP method, but %N is not", @@ -623,7 +623,7 @@ METHOD(authenticator_t, process_client, status_t, case EAP_SUCCESS: { eap_type_t type; - u_int32_t vendor; + uint32_t vendor; auth_cfg_t *cfg; if (this->method->get_msk(this->method, &this->msk) == SUCCESS) @@ -685,7 +685,7 @@ METHOD(authenticator_t, is_mutual, bool, { if (this->method) { - u_int32_t vendor; + uint32_t vendor; if (this->method->get_type(this->method, &vendor) != EAP_IDENTITY || vendor != 0) diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c index 04ccd4f4f..6fd34e0a6 100644 --- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c +++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c @@ -63,7 +63,7 @@ struct private_pubkey_authenticator_t { static bool parse_signature_auth_data(chunk_t *auth_data, key_type_t *key_type, signature_scheme_t *scheme) { - u_int8_t len; + uint8_t len; int oid; if (!auth_data->len) @@ -91,7 +91,7 @@ static bool build_signature_auth_data(chunk_t *auth_data, signature_scheme_t scheme) { chunk_t data; - u_int8_t len; + uint8_t len; int oid; oid = signature_scheme_to_oid(scheme); diff --git a/src/libcharon/sa/ikev2/connect_manager.c b/src/libcharon/sa/ikev2/connect_manager.c index 161c4fdaf..280796d8c 100644 --- a/src/libcharon/sa/ikev2/connect_manager.c +++ b/src/libcharon/sa/ikev2/connect_manager.c @@ -92,10 +92,10 @@ typedef struct endpoint_pair_t endpoint_pair_t; */ struct endpoint_pair_t { /** pair id */ - u_int32_t id; + uint32_t id; /** priority */ - u_int64_t priority; + uint64_t priority; /** local endpoint */ host_t *local; @@ -107,7 +107,7 @@ struct endpoint_pair_t { check_state_t state; /** number of retransmissions */ - u_int32_t retransmitted; + uint32_t retransmitted; /** the generated packet */ packet_t *packet; @@ -132,8 +132,8 @@ static endpoint_pair_t *endpoint_pair_create(endpoint_notify_t *initiator, { endpoint_pair_t *this; - u_int32_t pi = initiator->get_priority(initiator); - u_int32_t pr = responder->get_priority(responder); + uint32_t pi = initiator->get_priority(initiator); + uint32_t pr = responder->get_priority(responder); INIT(this, .priority = pow(2, 32) * min(pi, pr) + 2 * max(pi, pr) @@ -313,7 +313,7 @@ typedef struct check_t check_t; */ struct check_t { /** message id */ - u_int32_t mid; + uint32_t mid; /** source of the connectivity check */ host_t *src; @@ -375,7 +375,7 @@ struct callback_data_t { chunk_t connect_id; /** message (pair) id */ - u_int32_t mid; + uint32_t mid; }; /** @@ -406,7 +406,7 @@ static callback_data_t *callback_data_create(private_connect_manager_t *connect_ * Creates a new retransmission data object */ static callback_data_t *retransmit_data_create(private_connect_manager_t *connect_manager, - chunk_t connect_id, u_int32_t mid) + chunk_t connect_id, uint32_t mid) { callback_data_t *this = callback_data_create(connect_manager, connect_id); this->mid = mid; @@ -576,7 +576,7 @@ static status_t get_pair_by_hosts(linked_list_t *pairs, host_t *local, (void**)pair, local, remote); } -static bool match_pair_by_id(endpoint_pair_t *current, u_int32_t *id) +static bool match_pair_by_id(endpoint_pair_t *current, uint32_t *id) { return current->id == *id; } @@ -584,7 +584,7 @@ static bool match_pair_by_id(endpoint_pair_t *current, u_int32_t *id) /** * Searches for a pair with a specific id */ -static status_t get_pair_by_id(check_list_t *checklist, u_int32_t id, +static status_t get_pair_by_id(check_list_t *checklist, uint32_t id, endpoint_pair_t **pair) { return checklist->pairs->find_first(checklist->pairs, @@ -669,7 +669,7 @@ static void prune_pairs(linked_list_t *pairs) { enumerator_t *enumerator, *search; endpoint_pair_t *current, *other; - u_int32_t id = 0; + uint32_t id = 0; enumerator = pairs->create_enumerator(pairs); search = pairs->create_enumerator(pairs); @@ -826,7 +826,7 @@ static status_t process_payloads(message_t *message, check_t *check) static chunk_t build_signature(private_connect_manager_t *this, check_list_t *checklist, check_t *check, bool outbound) { - u_int32_t mid; + uint32_t mid; chunk_t mid_chunk, key_chunk, sig_chunk; chunk_t sig_hash; @@ -851,7 +851,7 @@ static chunk_t build_signature(private_connect_manager_t *this, } static void queue_retransmission(private_connect_manager_t *this, check_list_t *checklist, endpoint_pair_t *pair); -static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, u_int32_t time); +static void schedule_checks(private_connect_manager_t *this, check_list_t *checklist, uint32_t time); static void finish_checks(private_connect_manager_t *this, check_list_t *checklist); /** @@ -1019,11 +1019,11 @@ static void queue_retransmission(private_connect_manager_t *this, check_list_t * job = (job_t*)callback_job_create((callback_job_cb_t)retransmit, data, (callback_job_cleanup_t)callback_data_destroy, NULL); - u_int32_t retransmission = pair->retransmitted + 1; - u_int32_t rto = ME_INTERVAL; + uint32_t retransmission = pair->retransmitted + 1; + uint32_t rto = ME_INTERVAL; if (retransmission > ME_BOOST) { - rto = (u_int32_t)(ME_INTERVAL * pow(ME_RETRANS_BASE, retransmission - ME_BOOST)); + rto = (uint32_t)(ME_INTERVAL * pow(ME_RETRANS_BASE, retransmission - ME_BOOST)); } DBG2(DBG_IKE, "scheduling retransmission %d of pair '%d' in %dms", retransmission, pair->id, rto); @@ -1165,7 +1165,7 @@ static job_requeue_t sender(callback_data_t *data) * Schedules checks for a checklist (time in ms) */ static void schedule_checks(private_connect_manager_t *this, - check_list_t *checklist, u_int32_t time) + check_list_t *checklist, uint32_t time) { callback_data_t *data = callback_data_create(this, checklist->connect_id); checklist->sender = (job_t*)callback_job_create((callback_job_cb_t)sender, diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c index 55cb5dd9c..e37399841 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.c +++ b/src/libcharon/sa/ikev2/keymat_v2.c @@ -99,8 +99,8 @@ METHOD(keymat_t, create_nonce_gen, nonce_gen_t*, /** * Derive IKE keys for a combined AEAD algorithm */ -static bool derive_ike_aead(private_keymat_v2_t *this, u_int16_t alg, - u_int16_t key_size, prf_plus_t *prf_plus) +static bool derive_ike_aead(private_keymat_v2_t *this, uint16_t alg, + uint16_t key_size, prf_plus_t *prf_plus) { aead_t *aead_i, *aead_r; chunk_t key = chunk_empty; @@ -189,8 +189,8 @@ failure: /** * Derive IKE keys for traditional encryption and MAC algorithms */ -static bool derive_ike_traditional(private_keymat_v2_t *this, u_int16_t enc_alg, - u_int16_t enc_size, u_int16_t int_alg, prf_plus_t *prf_plus) +static bool derive_ike_traditional(private_keymat_v2_t *this, uint16_t enc_alg, + uint16_t enc_size, uint16_t int_alg, prf_plus_t *prf_plus) { crypter_t *crypter_i = NULL, *crypter_r = NULL; signer_t *signer_i, *signer_r; @@ -302,11 +302,11 @@ METHOD(keymat_v2_t, derive_ike_keys, bool, chunk_t skeyseed, key, secret, full_nonce, fixed_nonce, prf_plus_seed; chunk_t spi_i, spi_r; prf_plus_t *prf_plus = NULL; - u_int16_t alg, key_size, int_alg; + uint16_t alg, key_size, int_alg; prf_t *rekey_prf = NULL; - spi_i = chunk_alloca(sizeof(u_int64_t)); - spi_r = chunk_alloca(sizeof(u_int64_t)); + spi_i = chunk_alloca(sizeof(uint64_t)); + spi_r = chunk_alloca(sizeof(uint64_t)); if (!dh->get_shared_secret(dh, &secret)) { @@ -354,8 +354,8 @@ METHOD(keymat_v2_t, derive_ike_keys, bool, break; } fixed_nonce = chunk_cat("cc", nonce_i, nonce_r); - *((u_int64_t*)spi_i.ptr) = id->get_initiator_spi(id); - *((u_int64_t*)spi_r.ptr) = id->get_responder_spi(id); + *((uint64_t*)spi_i.ptr) = id->get_initiator_spi(id); + *((uint64_t*)spi_r.ptr) = id->get_responder_spi(id); prf_plus_seed = chunk_cat("ccc", full_nonce, spi_i, spi_r); /* KEYMAT = prf+ (SKEYSEED, Ni | Nr | SPIi | SPIr) @@ -489,7 +489,7 @@ METHOD(keymat_v2_t, derive_child_keys, bool, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, chunk_t *encr_r, chunk_t *integ_r) { - u_int16_t enc_alg, int_alg, enc_size = 0, int_size = 0; + uint16_t enc_alg, int_alg, enc_size = 0, int_size = 0; chunk_t seed, secret = chunk_empty; prf_plus_t *prf_plus; diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index c2f972ab1..41a4e1b75 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1,7 +1,7 @@ /* - * Copyright (C) 2007-2015 Tobias Brunner + * Copyright (C) 2007-2016 Tobias Brunner * Copyright (C) 2007-2010 Martin Willi - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -43,30 +43,14 @@ #include <encoding/payloads/unknown_payload.h> #include <processing/jobs/retransmit_job.h> #include <processing/jobs/delete_ike_sa_job.h> +#include <processing/jobs/initiate_tasks_job.h> #ifdef ME #include <sa/ikev2/tasks/ike_me.h> #endif -typedef struct exchange_t exchange_t; - -/** - * An exchange in the air, used do detect and handle retransmission - */ -struct exchange_t { - - /** - * Message ID used for this transaction - */ - u_int32_t mid; - - /** - * generated packet for retransmission - */ - packet_t *packet; -}; - typedef struct private_task_manager_t private_task_manager_t; +typedef struct queued_task_t queued_task_t; /** * private data of the task manager @@ -90,7 +74,7 @@ struct private_task_manager_t { /** * Message ID of the exchange */ - u_int32_t mid; + uint32_t mid; /** * packet(s) for retransmission @@ -111,7 +95,7 @@ struct private_task_manager_t { /** * Message ID of the exchange */ - u_int32_t mid; + uint32_t mid; /** * how many times we have retransmitted so far @@ -182,6 +166,22 @@ struct private_task_manager_t { }; /** + * Queued tasks + */ +struct queued_task_t { + + /** + * Queued task + */ + task_t *task; + + /** + * Time before which the task is not to be initiated + */ + timeval_t time; +}; + +/** * Reset retransmission packet list */ static void clear_packets(array_t *array) @@ -216,6 +216,12 @@ METHOD(task_manager_t, flush_queue, void, } while (array_remove(array, ARRAY_TAIL, &task)) { + if (queue == TASK_QUEUE_QUEUED) + { + queued_task_t *queued = (queued_task_t*)task; + task = queued->task; + free(queued); + } task->destroy(task); } } @@ -229,22 +235,28 @@ METHOD(task_manager_t, flush, void, } /** - * move a task of a specific type from the queue to the active list + * Move a task of a specific type from the queue to the active list, if it is + * not delayed. */ static bool activate_task(private_task_manager_t *this, task_type_t type) { enumerator_t *enumerator; - task_t *task; + queued_task_t *queued; + timeval_t now; bool found = FALSE; + time_monotonic(&now); + enumerator = array_create_enumerator(this->queued_tasks); - while (enumerator->enumerate(enumerator, (void**)&task)) + while (enumerator->enumerate(enumerator, (void**)&queued)) { - if (task->get_type(task) == type) + if (queued->task->get_type(queued->task) == type && + !timercmp(&now, &queued->time, <)) { DBG2(DBG_IKE, " activating %N task", task_type_names, type); array_remove_at(this->queued_tasks, enumerator); - array_insert(this->active_tasks, ARRAY_TAIL, task); + array_insert(this->active_tasks, ARRAY_TAIL, queued->task); + free(queued); found = TRUE; break; } @@ -303,12 +315,12 @@ static bool generate_message(private_task_manager_t *this, message_t *message, } METHOD(task_manager_t, retransmit, status_t, - private_task_manager_t *this, u_int32_t message_id) + private_task_manager_t *this, uint32_t message_id) { if (message_id == this->initiating.mid && array_count(this->initiating.packets)) { - u_int32_t timeout; + uint32_t timeout; job_t *job; enumerator_t *enumerator; packet_t *packet; @@ -336,7 +348,7 @@ METHOD(task_manager_t, retransmit, status_t, { if (this->initiating.retransmitted <= this->retransmit_tries) { - timeout = (u_int32_t)(this->retransmit_timeout * 1000.0 * + timeout = (uint32_t)(this->retransmit_timeout * 1000.0 * pow(this->retransmit_base, this->initiating.retransmitted)); } else @@ -352,7 +364,8 @@ METHOD(task_manager_t, retransmit, status_t, { DBG1(DBG_IKE, "retransmit %d of request with message ID %d", this->initiating.retransmitted, message_id); - charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet); + charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet, + this->initiating.retransmitted); } if (!mobike) { @@ -534,6 +547,7 @@ METHOD(task_manager_t, initiate, status_t, break; } case IKE_REKEYING: + case IKE_REKEYED: if (activate_task(this, TASK_IKE_DELETE)) { exchange = INFORMATIONAL; @@ -610,7 +624,8 @@ METHOD(task_manager_t, initiate, status_t, case FAILED: default: this->initiating.type = EXCHANGE_TYPE_UNDEFINED; - if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING) + if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING && + this->ike_sa->get_state(this->ike_sa) != IKE_REKEYED) { charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); } @@ -694,6 +709,13 @@ static status_t process_response(private_task_manager_t *this, } enumerator->destroy(enumerator); + if (this->initiating.retransmitted) + { + packet_t *packet = NULL; + array_get(this->initiating.packets, 0, &packet); + charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_CLEARED, packet); + } + /* catch if we get resetted while processing */ this->reset = FALSE; enumerator = array_create_enumerator(this->active_tasks); @@ -751,8 +773,7 @@ static bool handle_collisions(private_task_manager_t *this, task_t *task) /* do we have to check */ if (type == TASK_IKE_REKEY || type == TASK_CHILD_REKEY || - type == TASK_CHILD_DELETE || type == TASK_IKE_DELETE || - type == TASK_IKE_REAUTH) + type == TASK_CHILD_DELETE || type == TASK_IKE_DELETE) { /* find an exchange collision, and notify these tasks */ enumerator = array_create_enumerator(this->active_tasks); @@ -761,8 +782,7 @@ static bool handle_collisions(private_task_manager_t *this, task_t *task) switch (active->get_type(active)) { case TASK_IKE_REKEY: - if (type == TASK_IKE_REKEY || type == TASK_IKE_DELETE || - type == TASK_IKE_REAUTH) + if (type == TASK_IKE_REKEY || type == TASK_IKE_DELETE) { ike_rekey_t *rekey = (ike_rekey_t*)active; rekey->collide(rekey, task); @@ -799,7 +819,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request) host_t *me, *other; bool delete = FALSE, hook = FALSE; ike_sa_id_t *id = NULL; - u_int64_t responder_spi = 0; + uint64_t responder_spi = 0; bool result; me = request->get_destination(request); @@ -839,6 +859,10 @@ static status_t build_response(private_task_manager_t *this, message_t *request) /* FALL */ case DESTROY_ME: /* destroy IKE_SA, but SEND response first */ + if (handle_collisions(this, task)) + { + array_remove_at(this->passive_tasks, enumerator); + } delete = TRUE; break; } @@ -901,9 +925,11 @@ static status_t process_request(private_task_manager_t *this, payload_t *payload; notify_payload_t *notify; delete_payload_t *delete; + ike_sa_state_t state; if (array_count(this->passive_tasks) == 0) { /* create tasks depending on request type, if not already some queued */ + state = this->ike_sa->get_state(this->ike_sa); switch (message->get_exchange_type(message)) { case IKE_SA_INIT: @@ -939,8 +965,8 @@ static status_t process_request(private_task_manager_t *this, { /* FIXME: we should prevent this on mediation connections */ bool notify_found = FALSE, ts_found = FALSE; - if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED || - this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING) + if (state == IKE_CREATED || + state == IKE_CONNECTING) { DBG1(DBG_IKE, "received CREATE_CHILD_SA request for " "unestablished IKE_SA, rejected"); @@ -1005,6 +1031,14 @@ static status_t process_request(private_task_manager_t *this, case PLV2_NOTIFY: { notify = (notify_payload_t*)payload; + if (state == IKE_REKEYED) + { + DBG1(DBG_IKE, "received unexpected notify %N " + "for rekeyed IKE_SA, ignored", + notify_type_names, + notify->get_notify_type(notify)); + break; + } switch (notify->get_notify_type(notify)) { case ADDITIONAL_IP4_ADDRESS: @@ -1252,7 +1286,7 @@ static void send_notify_response(private_task_manager_t *this, static status_t parse_message(private_task_manager_t *this, message_t *msg) { status_t status; - u_int8_t type = 0; + uint8_t type = 0; status = msg->parse_body(msg, this->ike_sa->get_keymat(this->ike_sa)); @@ -1345,8 +1379,10 @@ METHOD(task_manager_t, process_message, status_t, { host_t *me, *other; status_t status; - u_int32_t mid; + uint32_t mid; bool schedule_delete_job = FALSE; + ike_sa_state_t state; + exchange_type_t type; charon->bus->message(charon->bus, msg, TRUE, FALSE); status = parse_message(this, msg); @@ -1387,15 +1423,16 @@ METHOD(task_manager_t, process_message, status_t, { if (mid == this->responding.mid) { - /* reject initial messages if not received in specific states */ - if ((msg->get_exchange_type(msg) == IKE_SA_INIT && - this->ike_sa->get_state(this->ike_sa) != IKE_CREATED) || - (msg->get_exchange_type(msg) == IKE_AUTH && - this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)) + /* reject initial messages if not received in specific states, + * after rekeying we only expect a DELETE in an INFORMATIONAL */ + type = msg->get_exchange_type(msg); + state = this->ike_sa->get_state(this->ike_sa); + if ((type == IKE_SA_INIT && state != IKE_CREATED) || + (type == IKE_AUTH && state != IKE_CONNECTING) || + (state == IKE_REKEYED && type != INFORMATIONAL)) { DBG1(DBG_IKE, "ignoring %N in IKE_SA state %N", - exchange_type_names, msg->get_exchange_type(msg), - ike_sa_state_names, this->ike_sa->get_state(this->ike_sa)); + exchange_type_names, type, ike_sa_state_names, state); return FAILED; } if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE)) @@ -1499,18 +1536,19 @@ METHOD(task_manager_t, process_message, status_t, return SUCCESS; } -METHOD(task_manager_t, queue_task, void, - private_task_manager_t *this, task_t *task) +METHOD(task_manager_t, queue_task_delayed, void, + private_task_manager_t *this, task_t *task, uint32_t delay) { + enumerator_t *enumerator; + queued_task_t *queued; + timeval_t time; + if (task->get_type(task) == TASK_IKE_MOBIKE) { /* there is no need to queue more than one mobike task */ - enumerator_t *enumerator; - task_t *current; - enumerator = array_create_enumerator(this->queued_tasks); - while (enumerator->enumerate(enumerator, ¤t)) + while (enumerator->enumerate(enumerator, &queued)) { - if (current->get_type(current) == TASK_IKE_MOBIKE) + if (queued->task->get_type(queued->task) == TASK_IKE_MOBIKE) { enumerator->destroy(enumerator); task->destroy(task); @@ -1519,8 +1557,35 @@ METHOD(task_manager_t, queue_task, void, } enumerator->destroy(enumerator); } - DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task)); - array_insert(this->queued_tasks, ARRAY_TAIL, task); + time_monotonic(&time); + if (delay) + { + job_t *job; + + DBG2(DBG_IKE, "queueing %N task (delayed by %us)", task_type_names, + task->get_type(task), delay); + time.tv_sec += delay; + + job = (job_t*)initiate_tasks_job_create( + this->ike_sa->get_id(this->ike_sa)); + lib->scheduler->schedule_job_tv(lib->scheduler, job, time); + } + else + { + DBG2(DBG_IKE, "queueing %N task", task_type_names, + task->get_type(task)); + } + INIT(queued, + .task = task, + .time = time, + ); + array_insert(this->queued_tasks, ARRAY_TAIL, queued); +} + +METHOD(task_manager_t, queue_task, void, + private_task_manager_t *this, task_t *task) +{ + queue_task_delayed(this, task, 0); } /** @@ -1530,12 +1595,12 @@ static bool has_queued(private_task_manager_t *this, task_type_t type) { enumerator_t *enumerator; bool found = FALSE; - task_t *task; + queued_task_t *queued; enumerator = array_create_enumerator(this->queued_tasks); - while (enumerator->enumerate(enumerator, &task)) + while (enumerator->enumerate(enumerator, &queued)) { - if (task->get_type(task) == type) + if (queued->task->get_type(queued->task) == type) { found = TRUE; break; @@ -1614,7 +1679,7 @@ static void trigger_mbb_reauth(private_task_manager_t *this) child_cfg_t *cfg; ike_sa_t *new; host_t *host; - task_t *task; + queued_task_t *queued; new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, this->ike_sa->get_version(this->ike_sa), TRUE); @@ -1645,13 +1710,14 @@ static void trigger_mbb_reauth(private_task_manager_t *this) enumerator->destroy(enumerator); enumerator = array_create_enumerator(this->queued_tasks); - while (enumerator->enumerate(enumerator, &task)) + while (enumerator->enumerate(enumerator, &queued)) { - if (task->get_type(task) == TASK_CHILD_CREATE) + if (queued->task->get_type(queued->task) == TASK_CHILD_CREATE) { - task->migrate(task, new); - new->queue_task(new, task); + queued->task->migrate(queued->task, new); + new->queue_task(new, queued->task); array_remove_at(this->queued_tasks, enumerator); + free(queued); } } enumerator->destroy(enumerator); @@ -1726,7 +1792,7 @@ METHOD(task_manager_t, queue_mobike, void, } METHOD(task_manager_t, queue_child, void, - private_task_manager_t *this, child_cfg_t *cfg, u_int32_t reqid, + private_task_manager_t *this, child_cfg_t *cfg, uint32_t reqid, traffic_selector_t *tsi, traffic_selector_t *tsr) { child_create_t *task; @@ -1740,13 +1806,13 @@ METHOD(task_manager_t, queue_child, void, } METHOD(task_manager_t, queue_child_rekey, void, - private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi) + private_task_manager_t *this, protocol_id_t protocol, uint32_t spi) { queue_task(this, (task_t*)child_rekey_create(this->ike_sa, protocol, spi)); } METHOD(task_manager_t, queue_child_delete, void, - private_task_manager_t *this, protocol_id_t protocol, u_int32_t spi, + private_task_manager_t *this, protocol_id_t protocol, uint32_t spi, bool expired) { queue_task(this, (task_t*)child_delete_create(this->ike_sa, @@ -1776,34 +1842,62 @@ METHOD(task_manager_t, adopt_tasks, void, private_task_manager_t *this, task_manager_t *other_public) { private_task_manager_t *other = (private_task_manager_t*)other_public; - task_t *task; + queued_task_t *queued; + timeval_t now; + + time_monotonic(&now); /* move queued tasks from other to this */ - while (array_remove(other->queued_tasks, ARRAY_TAIL, &task)) + while (array_remove(other->queued_tasks, ARRAY_TAIL, &queued)) { - DBG2(DBG_IKE, "migrating %N task", task_type_names, task->get_type(task)); - task->migrate(task, this->ike_sa); - array_insert(this->queued_tasks, ARRAY_HEAD, task); + DBG2(DBG_IKE, "migrating %N task", task_type_names, + queued->task->get_type(queued->task)); + queued->task->migrate(queued->task, this->ike_sa); + /* don't delay tasks on the new IKE_SA */ + queued->time = now; + array_insert(this->queued_tasks, ARRAY_HEAD, queued); } } /** - * Migrates child-creating tasks from src to dst + * Migrates child-creating tasks from other to this */ static void migrate_child_tasks(private_task_manager_t *this, - array_t *src, array_t *dst) + private_task_manager_t *other, + task_queue_t queue) { enumerator_t *enumerator; + array_t *array; task_t *task; - enumerator = array_create_enumerator(src); + switch (queue) + { + case TASK_QUEUE_ACTIVE: + array = other->active_tasks; + break; + case TASK_QUEUE_QUEUED: + array = other->queued_tasks; + break; + default: + return; + } + + enumerator = array_create_enumerator(array); while (enumerator->enumerate(enumerator, &task)) { + queued_task_t *queued = NULL; + + if (queue == TASK_QUEUE_QUEUED) + { + queued = (queued_task_t*)task; + task = queued->task; + } if (task->get_type(task) == TASK_CHILD_CREATE) { - array_remove_at(src, enumerator); + array_remove_at(array, enumerator); task->migrate(task, this->ike_sa); - array_insert(dst, ARRAY_TAIL, task); + queue_task(this, task); + free(queued); } } enumerator->destroy(enumerator); @@ -1815,9 +1909,9 @@ METHOD(task_manager_t, adopt_child_tasks, void, 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); + migrate_child_tasks(this, other, TASK_QUEUE_ACTIVE); /* do the same for queued tasks */ - migrate_child_tasks(this, other->queued_tasks, this->queued_tasks); + migrate_child_tasks(this, other, TASK_QUEUE_QUEUED); } METHOD(task_manager_t, busy, bool, @@ -1827,10 +1921,12 @@ METHOD(task_manager_t, busy, bool, } METHOD(task_manager_t, reset, void, - private_task_manager_t *this, u_int32_t initiate, u_int32_t respond) + private_task_manager_t *this, uint32_t initiate, uint32_t respond) { enumerator_t *enumerator; + queued_task_t *queued; task_t *task; + timeval_t now; /* reset message counters and retransmit packets */ clear_packets(this->responding.packets); @@ -1849,11 +1945,13 @@ METHOD(task_manager_t, reset, void, } this->initiating.type = EXCHANGE_TYPE_UNDEFINED; + time_monotonic(&now); /* reset queued tasks */ enumerator = array_create_enumerator(this->queued_tasks); - while (enumerator->enumerate(enumerator, &task)) + while (enumerator->enumerate(enumerator, &queued)) { - task->migrate(task, this->ike_sa); + queued->time = now; + queued->task->migrate(queued->task, this->ike_sa); } enumerator->destroy(enumerator); @@ -1861,12 +1959,25 @@ METHOD(task_manager_t, reset, void, while (array_remove(this->active_tasks, ARRAY_TAIL, &task)) { task->migrate(task, this->ike_sa); - array_insert(this->queued_tasks, ARRAY_HEAD, task); + INIT(queued, + .task = task, + .time = now, + ); + array_insert(this->queued_tasks, ARRAY_HEAD, queued); } this->reset = TRUE; } +/** + * Filter queued tasks + */ +static bool filter_queued(void *unused, queued_task_t **queued, task_t **task) +{ + *task = (*queued)->task; + return TRUE; +} + METHOD(task_manager_t, create_task_enumerator, enumerator_t*, private_task_manager_t *this, task_queue_t queue) { @@ -1877,7 +1988,9 @@ METHOD(task_manager_t, create_task_enumerator, enumerator_t*, case TASK_QUEUE_PASSIVE: return array_create_enumerator(this->passive_tasks); case TASK_QUEUE_QUEUED: - return array_create_enumerator(this->queued_tasks); + return enumerator_create_filter( + array_create_enumerator(this->queued_tasks), + (void*)filter_queued, NULL, NULL); default: return enumerator_create_empty(); } @@ -1913,6 +2026,7 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa) .task_manager = { .process_message = _process_message, .queue_task = _queue_task, + .queue_task_delayed = _queue_task_delayed, .queue_ike = _queue_ike, .queue_ike_rekey = _queue_ike_rekey, .queue_ike_reauth = _queue_ike_reauth, diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 3d4ded944..64a82850b 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -1,8 +1,8 @@ /* - * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008-2016 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -29,7 +29,7 @@ #include <encoding/payloads/delete_payload.h> #include <processing/jobs/delete_ike_sa_job.h> #include <processing/jobs/inactivity_job.h> - +#include <processing/jobs/initiate_tasks_job.h> typedef struct private_child_create_t private_child_create_t; @@ -151,27 +151,27 @@ struct private_child_create_t { /** * Own allocated SPI */ - u_int32_t my_spi; + uint32_t my_spi; /** * SPI received in proposal */ - u_int32_t other_spi; + uint32_t other_spi; /** * Own allocated Compression Parameter Index (CPI) */ - u_int16_t my_cpi; + uint16_t my_cpi; /** * Other Compression Parameter Index (CPI), received via IPCOMP_SUPPORTED */ - u_int16_t other_cpi; + uint16_t other_cpi; /** * reqid to use if we are rekeying */ - u_int32_t reqid; + uint32_t reqid; /** * Explicit inbound mark value @@ -205,6 +205,25 @@ struct private_child_create_t { }; /** + * Schedule a retry if creating the CHILD_SA temporary failed + */ +static void schedule_delayed_retry(private_child_create_t *this) +{ + child_create_t *task; + uint32_t retry; + + retry = RETRY_INTERVAL - (random() % RETRY_JITTER); + + task = child_create_create(this->ike_sa, + this->config->get_ref(this->config), FALSE, + this->packet_tsi, this->packet_tsr); + task->use_reqid(task, this->reqid); + DBG1(DBG_IKE, "creating CHILD_SA failed, trying again in %d seconds", + retry); + this->ike_sa->queue_task_delayed(this->ike_sa, (task_t*)task, retry); +} + +/** * get the nonce from a message */ static status_t get_nonce(message_t *message, chunk_t *nonce) @@ -306,7 +325,7 @@ static bool allocate_spi(private_child_create_t *this) */ static void schedule_inactivity_timeout(private_child_create_t *this) { - u_int32_t timeout, id; + uint32_t timeout, id; bool close_ike; timeout = this->config->get_inactivity(this->config); @@ -386,7 +405,7 @@ static linked_list_t* get_transport_nat_ts(private_child_create_t *this, linked_list_t *out; traffic_selector_t *ts; host_t *ike, *first = NULL; - u_int8_t mask; + uint8_t mask; if (local) { @@ -464,7 +483,7 @@ static status_t select_and_install(private_child_create_t *this, chunk_t integ_i = chunk_empty, integ_r = chunk_empty; linked_list_t *my_ts, *other_ts; host_t *me, *other; - bool private; + bool private, prefer_configured; if (this->proposals == NULL) { @@ -481,8 +500,10 @@ static status_t select_and_install(private_child_create_t *this, other = this->ike_sa->get_other_host(this->ike_sa); private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); + prefer_configured = lib->settings->get_bool(lib->settings, + "%s.prefer_configured_proposals", TRUE, lib->ns); this->proposal = this->config->select_proposal(this->config, - this->proposals, no_dh, private); + this->proposals, no_dh, private, prefer_configured); if (this->proposal == NULL) { DBG1(DBG_IKE, "no acceptable proposal found"); @@ -501,7 +522,7 @@ static status_t select_and_install(private_child_create_t *this, if (!this->proposal->has_dh_group(this->proposal, this->dh_group)) { - u_int16_t group; + uint16_t group; if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, &group, NULL)) @@ -798,7 +819,7 @@ static bool build_payloads(private_child_create_t *this, message_t *message) * Adds an IPCOMP_SUPPORTED notify to the message, allocating a CPI */ static void add_ipcomp_notify(private_child_create_t *this, - message_t *message, u_int8_t ipcomp) + message_t *message, uint8_t ipcomp) { this->my_cpi = this->child_sa->alloc_cpi(this->child_sa); if (this->my_cpi) @@ -838,11 +859,11 @@ static void handle_notify(private_child_create_t *this, notify_payload_t *notify case IPCOMP_SUPPORTED: { ipcomp_transform_t ipcomp; - u_int16_t cpi; + uint16_t cpi; chunk_t data; data = notify->get_notification_data(notify); - cpi = *(u_int16_t*)data.ptr; + cpi = *(uint16_t*)data.ptr; ipcomp = (ipcomp_transform_t)(*(data.ptr + 2)); switch (ipcomp) { @@ -1232,13 +1253,13 @@ METHOD(task_t, build_r, status_t, if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING) { DBG1(DBG_IKE, "unable to create CHILD_SA while rekeying IKE_SA"); - message->add_notify(message, TRUE, NO_ADDITIONAL_SAS, chunk_empty); + message->add_notify(message, TRUE, TEMPORARY_FAILURE, chunk_empty); return SUCCESS; } if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING) { DBG1(DBG_IKE, "unable to create CHILD_SA while deleting IKE_SA"); - message->add_notify(message, TRUE, NO_ADDITIONAL_SAS, chunk_empty); + message->add_notify(message, TRUE, TEMPORARY_FAILURE, chunk_empty); return SUCCESS; } @@ -1310,7 +1331,7 @@ METHOD(task_t, build_r, status_t, return SUCCESS; case INVALID_ARG: { - u_int16_t group = htons(this->dh_group); + uint16_t group = htons(this->dh_group); message->add_notify(message, FALSE, INVALID_KE_PAYLOAD, chunk_from_thing(group)); handle_child_sa_failure(this, message); @@ -1441,10 +1462,21 @@ METHOD(task_t, process_i, status_t, /* an error in CHILD_SA creation is not critical */ return SUCCESS; } + case TEMPORARY_FAILURE: + { + DBG1(DBG_IKE, "received %N notify, will retry later", + notify_type_names, type); + enumerator->destroy(enumerator); + if (!this->rekey) + { /* the rekey task will retry itself if necessary */ + schedule_delayed_retry(this); + } + return SUCCESS; + } case INVALID_KE_PAYLOAD: { chunk_t data; - u_int16_t group = MODP_NONE; + uint16_t group = MODP_NONE; data = notify->get_notification_data(notify); if (data.len == sizeof(group)) @@ -1529,7 +1561,7 @@ METHOD(task_t, process_i, status_t, } METHOD(child_create_t, use_reqid, void, - private_child_create_t *this, u_int32_t reqid) + private_child_create_t *this, uint32_t reqid) { this->reqid = reqid; } diff --git a/src/libcharon/sa/ikev2/tasks/child_create.h b/src/libcharon/sa/ikev2/tasks/child_create.h index 46d9403ee..f48d7b0a9 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.h +++ b/src/libcharon/sa/ikev2/tasks/child_create.h @@ -49,7 +49,7 @@ struct child_create_t { * * @param reqid reqid to use */ - void (*use_reqid) (child_create_t *this, u_int32_t reqid); + void (*use_reqid) (child_create_t *this, uint32_t reqid); /** * Use specific mark values to override configuration. diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c index 877ae0531..6fa8836ac 100644 --- a/src/libcharon/sa/ikev2/tasks/child_delete.c +++ b/src/libcharon/sa/ikev2/tasks/child_delete.c @@ -1,6 +1,7 @@ /* + * Copyright (C) 2009-2016 Tobias Brunner * Copyright (C) 2006-2007 Martin Willi - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -18,7 +19,7 @@ #include <daemon.h> #include <encoding/payloads/delete_payload.h> #include <sa/ikev2/tasks/child_create.h> - +#include <sa/ikev2/tasks/child_rekey.h> typedef struct private_child_delete_t private_child_delete_t; @@ -50,7 +51,7 @@ struct private_child_delete_t { /** * Inbound SPI of CHILD_SA to delete */ - u_int32_t spi; + uint32_t spi; /** * whether to enforce delete action policy @@ -86,7 +87,7 @@ static void build_payloads(private_child_delete_t *this, message_t *message) while (enumerator->enumerate(enumerator, (void**)&child_sa)) { protocol_id_t protocol = child_sa->get_protocol(child_sa); - u_int32_t spi = child_sa->get_spi(child_sa, TRUE); + uint32_t spi = child_sa->get_spi(child_sa, TRUE); switch (protocol) { @@ -119,6 +120,33 @@ static void build_payloads(private_child_delete_t *this, message_t *message) } /** + * Check if the given CHILD_SA is the redundant SA created in a rekey collision. + */ +static bool is_redundant(private_child_delete_t *this, child_sa_t *child) +{ + enumerator_t *tasks; + task_t *task; + + tasks = this->ike_sa->create_task_enumerator(this->ike_sa, + TASK_QUEUE_ACTIVE); + while (tasks->enumerate(tasks, &task)) + { + if (task->get_type(task) == TASK_CHILD_REKEY) + { + child_rekey_t *rekey = (child_rekey_t*)task; + + if (rekey->is_redundant(rekey, child)) + { + tasks->destroy(tasks); + return TRUE; + } + } + } + tasks->destroy(tasks); + return FALSE; +} + +/** * read in payloads and find the children to delete */ static void process_payloads(private_child_delete_t *this, message_t *message) @@ -126,7 +154,7 @@ static void process_payloads(private_child_delete_t *this, message_t *message) enumerator_t *payloads, *spis; payload_t *payload; delete_payload_t *delete_payload; - u_int32_t spi; + uint32_t spi; protocol_id_t protocol; child_sa_t *child_sa; @@ -157,24 +185,31 @@ static void process_payloads(private_child_delete_t *this, message_t *message) switch (child_sa->get_state(child_sa)) { - case CHILD_REKEYING: + case CHILD_REKEYED: this->rekeyed = TRUE; - /* we reply as usual, rekeying will fail */ break; case CHILD_DELETING: /* we don't send back a delete if we initiated ourself */ if (!this->initiator) { - this->ike_sa->destroy_child_sa(this->ike_sa, - protocol, spi); continue; } /* fall through */ + case CHILD_REKEYING: + /* we reply as usual, rekeying will fail */ case CHILD_INSTALLED: if (!this->initiator) - { /* reestablish installed children if required */ - this->check_delete_action = TRUE; + { + if (is_redundant(this, child_sa)) + { + this->rekeyed = TRUE; + } + else + { + this->check_delete_action = TRUE; + } } + break; default: break; } @@ -199,14 +234,14 @@ static status_t destroy_and_reestablish(private_child_delete_t *this) child_sa_t *child_sa; child_cfg_t *child_cfg; protocol_id_t protocol; - u_int32_t spi, reqid; + uint32_t spi, reqid; action_t action; status_t status = SUCCESS; enumerator = this->child_sas->create_enumerator(this->child_sas); while (enumerator->enumerate(enumerator, (void**)&child_sa)) { - /* signal child down event if we are not rekeying */ + /* signal child down event if we weren't rekeying */ if (!this->rekeyed) { charon->bus->child_updown(charon->bus, child_sa, FALSE); @@ -254,7 +289,7 @@ 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; + uint64_t bytes_in, bytes_out; enumerator = this->child_sas->create_enumerator(this->child_sas); while (enumerator->enumerate(enumerator, (void**)&child_sa)) @@ -308,7 +343,7 @@ METHOD(task_t, build_i, status_t, this->spi = child_sa->get_spi(child_sa, TRUE); } this->child_sas->insert_last(this->child_sas, child_sa); - if (child_sa->get_state(child_sa) == CHILD_REKEYING) + if (child_sa->get_state(child_sa) == CHILD_REKEYED) { this->rekeyed = TRUE; } @@ -347,11 +382,7 @@ METHOD(task_t, process_r, status_t, METHOD(task_t, build_r, status_t, private_child_delete_t *this, message_t *message) { - /* if we are rekeying, we send an empty informational */ - if (this->ike_sa->get_state(this->ike_sa) != IKE_REKEYING) - { - build_payloads(this, message); - } + build_payloads(this, message); DBG1(DBG_IKE, "CHILD_SA closed"); return destroy_and_reestablish(this); } @@ -391,7 +422,7 @@ METHOD(task_t, destroy, void, * Described in header. */ child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol, - u_int32_t spi, bool expired) + uint32_t spi, bool expired) { private_child_delete_t *this; diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.h b/src/libcharon/sa/ikev2/tasks/child_delete.h index 1ada0699e..1e9b2d2f7 100644 --- a/src/libcharon/sa/ikev2/tasks/child_delete.h +++ b/src/libcharon/sa/ikev2/tasks/child_delete.h @@ -56,6 +56,6 @@ struct child_delete_t { * @return child_delete task to handle by the task_manager */ child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol, - u_int32_t spi, bool expired); + uint32_t spi, bool expired); #endif /** CHILD_DELETE_H_ @}*/ diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c index 6f0c2b2c7..c04ec141f 100644 --- a/src/libcharon/sa/ikev2/tasks/child_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c @@ -1,7 +1,8 @@ /* + * Copyright (C) 2009-2016 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -54,7 +55,7 @@ struct private_child_rekey_t { /** * Inbound SPI of CHILD_SA to rekey */ - u_int32_t spi; + uint32_t spi; /** * the CHILD_CREATE task which is reused to simplify rekeying @@ -91,7 +92,7 @@ struct private_child_rekey_t { */ static void schedule_delayed_rekey(private_child_rekey_t *this) { - u_int32_t retry; + uint32_t retry; job_t *job; retry = RETRY_INTERVAL - (random() % RETRY_JITTER); @@ -130,7 +131,7 @@ static void find_child(private_child_rekey_t *this, message_t *message) { notify_payload_t *notify; protocol_id_t protocol; - u_int32_t spi; + uint32_t spi; notify = message->get_notify(message, REKEY_SA); if (notify) @@ -150,7 +151,7 @@ METHOD(task_t, build_i, status_t, private_child_rekey_t *this, message_t *message) { notify_payload_t *notify; - u_int32_t reqid; + uint32_t reqid; child_cfg_t *config; this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol, @@ -159,14 +160,21 @@ METHOD(task_t, build_i, status_t, { /* check if it is an outbound CHILD_SA */ this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol, this->spi, FALSE); - if (!this->child_sa) - { /* CHILD_SA is gone, unable to rekey. As an empty CREATE_CHILD_SA - * exchange is invalid, we fall back to an INFORMATIONAL exchange.*/ - message->set_exchange_type(message, INFORMATIONAL); - return SUCCESS; + if (this->child_sa) + { + /* we work only with the inbound SPI */ + this->spi = this->child_sa->get_spi(this->child_sa, TRUE); } - /* we work only with the inbound SPI */ - this->spi = this->child_sa->get_spi(this->child_sa, TRUE); + } + if (!this->child_sa || + (!this->child_create && + this->child_sa->get_state(this->child_sa) != CHILD_INSTALLED) || + (this->child_create && + this->child_sa->get_state(this->child_sa) != CHILD_REKEYING)) + { + /* CHILD_SA is gone or in the wrong state, unable to rekey */ + message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED); + return SUCCESS; } config = this->child_sa->get_config(this->child_sa); @@ -217,13 +225,19 @@ METHOD(task_t, build_r, status_t, private_child_rekey_t *this, message_t *message) { child_cfg_t *config; - u_int32_t reqid; + uint32_t reqid; + child_sa_state_t state; - if (this->child_sa == NULL || - this->child_sa->get_state(this->child_sa) == CHILD_DELETING) + if (!this->child_sa) { DBG1(DBG_IKE, "unable to rekey, CHILD_SA not found"); - message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); + message->add_notify(message, TRUE, CHILD_SA_NOT_FOUND, chunk_empty); + return SUCCESS; + } + if (this->child_sa->get_state(this->child_sa) == CHILD_DELETING) + { + DBG1(DBG_IKE, "unable to rekey, we are deleting the CHILD_SA"); + message->add_notify(message, TRUE, TEMPORARY_FAILURE, chunk_empty); return SUCCESS; } @@ -237,14 +251,16 @@ METHOD(task_t, build_r, status_t, this->child_create->set_config(this->child_create, config->get_ref(config)); this->child_create->task.build(&this->child_create->task, message); + state = this->child_sa->get_state(this->child_sa); + this->child_sa->set_state(this->child_sa, CHILD_REKEYING); + if (message->get_payload(message, PLV2_SECURITY_ASSOCIATION) == NULL) - { - /* rekeying failed, reuse old child */ - this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); + { /* rekeying failed, reuse old child */ + this->child_sa->set_state(this->child_sa, state); return SUCCESS; } - this->child_sa->set_state(this->child_sa, CHILD_REKEYING); + this->child_sa->set_state(this->child_sa, CHILD_REKEYED); /* invoke rekey hook */ charon->bus->child_rekey(charon->bus, this->child_sa, @@ -284,9 +300,9 @@ static child_sa_t *handle_collision(private_child_rekey_t *this) if (child_sa) { child_sa->set_close_action(child_sa, ACTION_NONE); - if (child_sa->get_state(child_sa) != CHILD_REKEYING) + if (child_sa->get_state(child_sa) != CHILD_REKEYED) { - child_sa->set_state(child_sa, CHILD_REKEYING); + child_sa->set_state(child_sa, CHILD_REKEYED); } } } @@ -324,7 +340,7 @@ METHOD(task_t, process_i, status_t, private_child_rekey_t *this, message_t *message) { protocol_id_t protocol; - u_int32_t spi; + uint32_t spi; child_sa_t *to_delete; if (message->get_notify(message, NO_ADDITIONAL_SAS)) @@ -337,6 +353,34 @@ METHOD(task_t, process_i, status_t, this->ike_sa->get_id(this->ike_sa), TRUE)); return SUCCESS; } + if (message->get_notify(message, CHILD_SA_NOT_FOUND)) + { + child_cfg_t *child_cfg; + uint32_t reqid; + + if (this->collision && + this->collision->get_type(this->collision) == TASK_CHILD_DELETE) + { /* ignore this error if we already deleted the CHILD_SA on the + * peer's behalf (could happen if the other peer does not detect + * the collision and did not respond with TEMPORARY_FAILURE) */ + return SUCCESS; + } + DBG1(DBG_IKE, "peer didn't find the CHILD_SA we tried to rekey"); + /* FIXME: according to RFC 7296 we should only create a new CHILD_SA if + * it does not exist yet, we currently have no good way of checking for + * that (we could go by name, but that might be tricky e.g. due to + * narrowing) */ + spi = this->child_sa->get_spi(this->child_sa, TRUE); + reqid = this->child_sa->get_reqid(this->child_sa); + protocol = this->child_sa->get_protocol(this->child_sa); + child_cfg = this->child_sa->get_config(this->child_sa); + child_cfg->get_ref(child_cfg); + charon->bus->child_updown(charon->bus, this->child_sa, FALSE); + this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); + return this->ike_sa->initiate(this->ike_sa, + child_cfg->get_ref(child_cfg), reqid, + NULL, NULL); + } if (this->child_create->task.process(&this->child_create->task, message) == NEED_MORE) @@ -346,10 +390,10 @@ METHOD(task_t, process_i, status_t, } if (message->get_payload(message, PLV2_SECURITY_ASSOCIATION) == NULL) { - /* establishing new child failed, reuse old. but not when we - * received a delete in the meantime */ - if (!(this->collision && - this->collision->get_type(this->collision) == TASK_CHILD_DELETE)) + /* establishing new child failed, reuse old and try again. but not when + * we received a delete in the meantime */ + if (!this->collision || + this->collision->get_type(this->collision) != TASK_CHILD_DELETE) { schedule_delayed_rekey(this); } @@ -377,9 +421,9 @@ METHOD(task_t, process_i, status_t, return SUCCESS; } /* disable updown event for redundant CHILD_SA */ - if (to_delete->get_state(to_delete) != CHILD_REKEYING) + if (to_delete->get_state(to_delete) != CHILD_REKEYED) { - to_delete->set_state(to_delete, CHILD_REKEYING); + to_delete->set_state(to_delete, CHILD_REKEYED); } spi = to_delete->get_spi(to_delete, TRUE); protocol = to_delete->get_protocol(to_delete); @@ -398,6 +442,18 @@ METHOD(task_t, get_type, task_type_t, return TASK_CHILD_REKEY; } +METHOD(child_rekey_t, is_redundant, bool, + private_child_rekey_t *this, child_sa_t *child) +{ + if (this->collision && + this->collision->get_type(this->collision) == TASK_CHILD_REKEY) + { + private_child_rekey_t *rekey = (private_child_rekey_t*)this->collision; + return child == rekey->child_create->get_child(rekey->child_create); + } + return FALSE; +} + METHOD(child_rekey_t, collide, void, private_child_rekey_t *this, task_t *other) { @@ -406,9 +462,18 @@ METHOD(child_rekey_t, collide, void, if (other->get_type(other) == TASK_CHILD_REKEY) { private_child_rekey_t *rekey = (private_child_rekey_t*)other; + child_sa_t *other_child; + if (rekey->child_sa != this->child_sa) + { /* not the same child => no collision */ + other->destroy(other); + return; + } + /* ignore passive tasks that did not successfully create a CHILD_SA */ + other_child = rekey->child_create->get_child(rekey->child_create); + if (!other_child || + other_child->get_state(other_child) != CHILD_INSTALLED) { - /* not the same child => no collision */ other->destroy(other); return; } @@ -416,19 +481,11 @@ METHOD(child_rekey_t, collide, void, else if (other->get_type(other) == TASK_CHILD_DELETE) { child_delete_t *del = (child_delete_t*)other; - if (this->collision && - this->collision->get_type(this->collision) == TASK_CHILD_REKEY) + if (is_redundant(this, del->get_child(del))) { - 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; - } + this->other_child_destroyed = TRUE; + other->destroy(other); + return; } if (del->get_child(del) != this->child_sa) { @@ -439,7 +496,7 @@ METHOD(child_rekey_t, collide, void, } else { - /* any other task is not critical for collisisions, ignore */ + /* any other task is not critical for collisions, ignore */ other->destroy(other); return; } @@ -485,7 +542,7 @@ METHOD(task_t, destroy, void, * Described in header. */ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol, - u_int32_t spi) + uint32_t spi) { private_child_rekey_t *this; @@ -496,6 +553,7 @@ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol, .migrate = _migrate, .destroy = _destroy, }, + .is_redundant = _is_redundant, .collide = _collide, }, .ike_sa = ike_sa, diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.h b/src/libcharon/sa/ikev2/tasks/child_rekey.h index 23384653d..0ad1a062d 100644 --- a/src/libcharon/sa/ikev2/tasks/child_rekey.h +++ b/src/libcharon/sa/ikev2/tasks/child_rekey.h @@ -1,6 +1,7 @@ /* + * Copyright (C) 2016 Tobias Brunner * Copyright (C) 2007 Martin Willi - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -39,13 +40,25 @@ struct child_rekey_t { task_t task; /** - * Register a rekeying task which collides with this one + * Check if the given SA is the redundant CHILD_SA created during a rekey + * collision. + * + * This is called if the other peer deletes the redundant SA before we were + * able to handle the CREATE_CHILD_SA response. + * + * @param child CHILD_SA to check + * @return TRUE if the SA is the redundant CHILD_SA + */ + bool (*is_redundant)(child_rekey_t *this, child_sa_t *child); + + /** + * Register a rekeying/delete task which collides with this one * * If two peers initiate rekeying at the same time, the collision must * be handled gracefully. The task manager is aware of what exchanges - * are going on and notifies the outgoing task by passing the incoming. + * are going on and notifies the active task by passing the passive. * - * @param other incoming task + * @param other passive task (adopted) */ void (*collide)(child_rekey_t* this, task_t *other); }; @@ -59,6 +72,6 @@ struct child_rekey_t { * @return child_rekey task to handle by the task_manager */ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol, - u_int32_t spi); + uint32_t spi); #endif /** CHILD_REKEY_H_ @}*/ diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 79a436fbf..036910d0e 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -186,7 +186,7 @@ static status_t collect_other_init_data(private_ike_auth_t *this, */ static void get_reserved_id_bytes(private_ike_auth_t *this, id_payload_t *id) { - u_int8_t *byte; + uint8_t *byte; int i; for (i = 0; i < countof(this->reserved); i++) @@ -564,6 +564,10 @@ METHOD(task_t, process_r, status_t, this->ike_sa->enable_extension(this->ike_sa, EXT_EAP_ONLY_AUTHENTICATION); } + if (message->get_notify(message, INITIAL_CONTACT)) + { + this->initial_contact = TRUE; + } } if (this->other_auth == NULL) @@ -652,14 +656,6 @@ METHOD(task_t, process_r, status_t, return NEED_MORE; } - /* If authenticated (with non-EAP) and received INITIAL_CONTACT, - * delete any existing IKE_SAs with that peer. */ - if (message->get_message_id(message) == 1 && - message->get_notify(message, INITIAL_CONTACT)) - { - this->initial_contact = TRUE; - } - /* another auth round done, invoke authorize hook */ if (!charon->bus->authorize(charon->bus, FALSE)) { @@ -749,13 +745,6 @@ METHOD(task_t, build_r, status_t, get_reserved_id_bytes(this, id_payload); message->add_payload(message, (payload_t*)id_payload); - if (this->initial_contact) - { - charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager, - this->ike_sa, TRUE); - this->initial_contact = FALSE; - } - if ((uintptr_t)cfg->get(cfg, AUTH_RULE_AUTH_CLASS) == AUTH_CLASS_EAP) { /* EAP-only authentication */ if (!this->ike_sa->supports_extension(this->ike_sa, @@ -830,7 +819,7 @@ METHOD(task_t, build_r, status_t, } if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager, - this->ike_sa, FALSE)) + this->ike_sa, this->initial_contact)) { DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy"); charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP); diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c b/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c index a7d162e68..47b0a3ed1 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth_lifetime.c @@ -45,14 +45,14 @@ struct private_ike_auth_lifetime_t { static void add_auth_lifetime(private_ike_auth_lifetime_t *this, message_t *message) { chunk_t chunk; - u_int32_t lifetime; + uint32_t lifetime; lifetime = this->ike_sa->get_statistic(this->ike_sa, STAT_REAUTH); if (lifetime) { lifetime -= time_monotonic(NULL); chunk = chunk_from_thing(lifetime); - *(u_int32_t*)chunk.ptr = htonl(lifetime); + *(uint32_t*)chunk.ptr = htonl(lifetime); message->add_notify(message, FALSE, AUTH_LIFETIME, chunk); } } @@ -64,13 +64,13 @@ static void process_payloads(private_ike_auth_lifetime_t *this, message_t *messa { notify_payload_t *notify; chunk_t data; - u_int32_t lifetime; + uint32_t lifetime; notify = message->get_notify(message, AUTH_LIFETIME); if (notify) { data = notify->get_notification_data(notify); - lifetime = ntohl(*(u_int32_t*)data.ptr); + lifetime = ntohl(*(uint32_t*)data.ptr); this->ike_sa->set_auth_lifetime(this->ike_sa, lifetime); } } diff --git a/src/libcharon/sa/ikev2/tasks/ike_delete.c b/src/libcharon/sa/ikev2/tasks/ike_delete.c index e972dba07..fd36b144a 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_delete.c +++ b/src/libcharon/sa/ikev2/tasks/ike_delete.c @@ -1,6 +1,7 @@ /* + * Copyright (C) 2016 Tobias Brunner * Copyright (C) 2006-2007 Martin Willi - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -17,7 +18,7 @@ #include <daemon.h> #include <encoding/payloads/delete_payload.h> - +#include <sa/ikev2/tasks/ike_rekey.h> typedef struct private_ike_delete_t private_ike_delete_t; @@ -45,11 +46,6 @@ struct private_ike_delete_t { * are we deleting a rekeyed SA? */ bool rekeyed; - - /** - * are we responding to a delete, but have initated our own? - */ - bool simultaneous; }; METHOD(task_t, build_i, status_t, @@ -68,7 +64,8 @@ METHOD(task_t, build_i, status_t, delete_payload = delete_payload_create(PLV2_DELETE, PROTO_IKE); message->add_payload(message, (payload_t*)delete_payload); - if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING) + if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING || + this->ike_sa->get_state(this->ike_sa) == IKE_REKEYED) { this->rekeyed = TRUE; } @@ -93,6 +90,33 @@ METHOD(task_t, process_i, status_t, return DESTROY_ME; } +/** + * Check if this delete happened after a rekey collsion + */ +static bool after_rekey_collision(private_ike_delete_t *this) +{ + enumerator_t *tasks; + task_t *task; + + tasks = this->ike_sa->create_task_enumerator(this->ike_sa, + TASK_QUEUE_ACTIVE); + while (tasks->enumerate(tasks, &task)) + { + if (task->get_type(task) == TASK_IKE_REKEY) + { + ike_rekey_t *rekey = (ike_rekey_t*)task; + + if (rekey->did_collide(rekey)) + { + tasks->destroy(tasks); + return TRUE; + } + } + } + tasks->destroy(tasks); + return FALSE; +} + METHOD(task_t, process_r, status_t, private_ike_delete_t *this, message_t *message) { @@ -119,16 +143,24 @@ METHOD(task_t, process_r, status_t, switch (this->ike_sa->get_state(this->ike_sa)) { + case IKE_REKEYING: + /* if the peer concurrently deleted the IKE_SA we treat this as + * regular delete. however, in case the peer did not detect a rekey + * collision it will delete the replaced IKE_SA if we are still in + * state IKE_REKEYING */ + if (after_rekey_collision(this)) + { + this->rekeyed = TRUE; + break; + } + /* fall-through */ case IKE_ESTABLISHED: this->ike_sa->set_state(this->ike_sa, IKE_DELETING); this->ike_sa->reestablish(this->ike_sa); return NEED_MORE; - case IKE_REKEYING: + case IKE_REKEYED: this->rekeyed = TRUE; break; - case IKE_DELETING: - this->simultaneous = TRUE; - break; default: break; } @@ -141,11 +173,6 @@ METHOD(task_t, build_r, status_t, { DBG0(DBG_IKE, "IKE_SA deleted"); - if (this->simultaneous) - { - /* wait for peer's response for our delete request */ - return SUCCESS; - } if (!this->rekeyed) { /* invoke ike_down() hook if SA has not been rekeyed */ charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE); @@ -164,7 +191,6 @@ METHOD(task_t, migrate, void, private_ike_delete_t *this, ike_sa_t *ike_sa) { this->ike_sa = ike_sa; - this->simultaneous = FALSE; } METHOD(task_t, destroy, void, diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 78579be95..801b6d8f3 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -221,7 +221,7 @@ static void handle_supported_hash_algorithms(private_ike_init_t *this, notify_payload_t *notify) { bio_reader_t *reader; - u_int16_t algo; + uint16_t algo; bool added = FALSE; reader = bio_reader_create(notify->get_notification_data(notify)); @@ -373,13 +373,15 @@ static void process_payloads(private_ike_init_t *this, message_t *message) { sa_payload_t *sa_payload = (sa_payload_t*)payload; linked_list_t *proposal_list; - bool private; + bool private, prefer_configured; proposal_list = sa_payload->get_proposals(sa_payload); private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); + prefer_configured = lib->settings->get_bool(lib->settings, + "%s.prefer_configured_proposals", TRUE, lib->ns); this->proposal = this->config->select_proposal(this->config, - proposal_list, private); + proposal_list, private, prefer_configured); if (!this->proposal) { charon->bus->alert(charon->bus, ALERT_PROPOSAL_MISMATCH_IKE, @@ -633,7 +635,7 @@ METHOD(task_t, build_r, status_t, if (this->dh == NULL || !this->proposal->has_dh_group(this->proposal, this->dh_group)) { - u_int16_t group; + uint16_t group; if (this->proposal->get_algorithm(this->proposal, DIFFIE_HELLMAN_GROUP, &group, NULL)) @@ -765,7 +767,7 @@ METHOD(task_t, process_i, status_t, bad_group = this->dh_group; data = notify->get_notification_data(notify); - this->dh_group = ntohs(*((u_int16_t*)data.ptr)); + this->dh_group = ntohs(*((uint16_t*)data.ptr)); DBG1(DBG_IKE, "peer didn't accept DH group %N, " "it requested %N", diffie_hellman_group_names, bad_group, diffie_hellman_group_names, this->dh_group); diff --git a/src/libcharon/sa/ikev2/tasks/ike_me.c b/src/libcharon/sa/ikev2/tasks/ike_me.c index 10d412ffd..f077ccfb5 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_me.c +++ b/src/libcharon/sa/ikev2/tasks/ike_me.c @@ -128,7 +128,7 @@ static void gather_and_add_endpoints(private_ike_me_t *this, message_t *message) { enumerator_t *enumerator; host_t *addr, *host; - u_int16_t port; + uint16_t port; /* get the port that is used to communicate with the ms */ host = this->ike_sa->get_my_host(this->ike_sa); diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c index 3f7bb175f..dc0f24fb8 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c +++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c @@ -299,7 +299,7 @@ static void update_children(private_ike_mobike_t *this) /** * Apply the port of the old host, if its ip equals the new, use port otherwise. */ -static void apply_port(host_t *host, host_t *old, u_int16_t port, bool local) +static void apply_port(host_t *host, host_t *old, uint16_t port, bool local) { if (host->ip_equals(host, old)) { diff --git a/src/libcharon/sa/ikev2/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c index 4bf5264dd..f3f32d7af 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_natd.c +++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c @@ -99,8 +99,8 @@ static chunk_t generate_natd_hash(private_ike_natd_t *this, { chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk; chunk_t natd_hash; - u_int64_t spi_i, spi_r; - u_int16_t port; + uint64_t spi_i, spi_r; + uint16_t port; /* prepare all required chunks */ spi_i = ike_sa_id->get_initiator_spi(ike_sa_id); @@ -142,7 +142,7 @@ static notify_payload_t *build_natd_payload(private_ike_natd_t *this, config = this->ike_sa->get_ike_cfg(this->ike_sa); if (force_encap(config) && type == NAT_DETECTION_SOURCE_IP) { - u_int32_t addr; + uint32_t addr; /* chunk_hash() is randomly keyed so this produces a random IPv4 address * that changes with every restart but otherwise stays the same */ diff --git a/src/libcharon/sa/ikev2/tasks/ike_rekey.c b/src/libcharon/sa/ikev2/tasks/ike_rekey.c index eaba04e3a..2f0552a33 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.c @@ -1,7 +1,8 @@ /* + * Copyright (C) 2015-2016 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -66,9 +67,30 @@ struct private_ike_rekey_t { * colliding task detected by the task manager */ task_t *collision; + + /** + * TRUE if rekeying can't be handled temporarily + */ + bool failed_temporarily; }; /** + * Schedule a retry if rekeying temporary failed + */ +static void schedule_delayed_rekey(private_ike_rekey_t *this) +{ + uint32_t retry; + job_t *job; + + retry = RETRY_INTERVAL - (random() % RETRY_JITTER); + job = (job_t*)rekey_ike_sa_job_create( + this->ike_sa->get_id(this->ike_sa), FALSE); + DBG1(DBG_IKE, "IKE_SA rekeying failed, trying again in %d seconds", retry); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + lib->scheduler->schedule_job(lib->scheduler, job, retry); +} + +/** * Check if an IKE_SA has any queued tasks, return initiation job */ static job_t* check_queued_tasks(ike_sa_t *ike_sa) @@ -83,7 +105,6 @@ static job_t* check_queued_tasks(ike_sa_t *ike_sa) job = (job_t*)initiate_tasks_job_create(ike_sa->get_id(ike_sa)); } enumerator->destroy(enumerator); - return job; } @@ -117,20 +138,9 @@ static void establish_new(private_ike_rekey_t *this) } this->new_sa = NULL; charon->bus->set_sa(charon->bus, this->ike_sa); - } -} -METHOD(task_t, process_r_delete, status_t, - private_ike_rekey_t *this, message_t *message) -{ - establish_new(this); - return this->ike_delete->task.process(&this->ike_delete->task, message); -} - -METHOD(task_t, build_r_delete, status_t, - private_ike_rekey_t *this, message_t *message) -{ - return this->ike_delete->task.build(&this->ike_delete->task, message); + this->ike_sa->set_state(this->ike_sa, IKE_REKEYED); + } } METHOD(task_t, build_i_delete, status_t, @@ -172,36 +182,59 @@ METHOD(task_t, build_i, status_t, return NEED_MORE; } -METHOD(task_t, process_r, status_t, - private_ike_rekey_t *this, message_t *message) +/** + * Check if there are any half-open children + */ +static bool have_half_open_children(private_ike_rekey_t *this) { enumerator_t *enumerator; child_sa_t *child_sa; - - if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING) - { - DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting"); - return NEED_MORE; - } + task_t *task; enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa); while (enumerator->enumerate(enumerator, (void**)&child_sa)) { switch (child_sa->get_state(child_sa)) { - case CHILD_CREATED: case CHILD_REKEYING: case CHILD_RETRYING: case CHILD_DELETING: - /* we do not allow rekeying while we have children in-progress */ - DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open"); enumerator->destroy(enumerator); - return NEED_MORE; + return TRUE; default: break; } } enumerator->destroy(enumerator); + enumerator = this->ike_sa->create_task_enumerator(this->ike_sa, + TASK_QUEUE_ACTIVE); + while (enumerator->enumerate(enumerator, (void**)&task)) + { + if (task->get_type(task) == TASK_CHILD_CREATE) + { + enumerator->destroy(enumerator); + return TRUE; + } + } + enumerator->destroy(enumerator); + return FALSE; +} + +METHOD(task_t, process_r, status_t, + private_ike_rekey_t *this, message_t *message) +{ + if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING) + { + DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting"); + this->failed_temporarily = TRUE; + return NEED_MORE; + } + if (have_half_open_children(this)) + { + DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open"); + this->failed_temporarily = TRUE; + return NEED_MORE; + } this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, this->ike_sa->get_version(this->ike_sa), FALSE); @@ -219,33 +252,57 @@ METHOD(task_t, process_r, status_t, METHOD(task_t, build_r, status_t, private_ike_rekey_t *this, message_t *message) { + if (this->failed_temporarily) + { + message->add_notify(message, TRUE, TEMPORARY_FAILURE, chunk_empty); + return SUCCESS; + } if (this->new_sa == NULL) { /* IKE_SA/a CHILD_SA is in an inacceptable state, deny rekeying */ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); return SUCCESS; } - if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED) { + this->ike_init->task.destroy(&this->ike_init->task); + this->ike_init = NULL; charon->bus->set_sa(charon->bus, this->ike_sa); return SUCCESS; } charon->bus->set_sa(charon->bus, this->ike_sa); - this->ike_sa->set_state(this->ike_sa, IKE_REKEYING); - /* rekeying successful, delete the IKE_SA using a subtask */ - this->ike_delete = ike_delete_create(this->ike_sa, FALSE); - this->public.task.build = _build_r_delete; - this->public.task.process = _process_r_delete; - - /* the peer does have to delete the IKE_SA. If it does not, we get a - * unusable IKE_SA in REKEYING state without a replacement. We consider - * this a timeout condition by the peer, and trigger a delete actively. */ - lib->scheduler->schedule_job(lib->scheduler, (job_t*) - delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE), 90); + if (this->ike_sa->get_state(this->ike_sa) != IKE_REKEYING) + { /* in case of a collision we let the initiating task handle this */ + establish_new(this); + /* make sure the IKE_SA is gone in case the peer fails to delete it */ + lib->scheduler->schedule_job(lib->scheduler, (job_t*) + delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE), + 90); + } + return SUCCESS; +} - return NEED_MORE; +/** + * Conclude any undetected rekey collision. + * + * If the peer does not detect the collision it will delete this IKE_SA. + * Depending on when our request reaches the peer and we receive the delete + * this may get called at different times. + * + * Returns TRUE if there was a collision, FALSE otherwise. + */ +static bool conclude_undetected_collision(private_ike_rekey_t *this) +{ + if (this->collision && + this->collision->get_type(this->collision) == TASK_IKE_REKEY) + { + DBG1(DBG_IKE, "peer did not notice IKE_SA rekey collision, abort " + "active rekeying"); + establish_new((private_ike_rekey_t*)this->collision); + return TRUE; + } + return FALSE; } METHOD(task_t, process_i, status_t, @@ -266,18 +323,9 @@ METHOD(task_t, process_i, status_t, { case FAILED: /* rekeying failed, fallback to old SA */ - if (!(this->collision && ( - this->collision->get_type(this->collision) == TASK_IKE_DELETE || - this->collision->get_type(this->collision) == TASK_IKE_REAUTH))) + if (!conclude_undetected_collision(this)) { - job_t *job; - u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER); - job = (job_t*)rekey_ike_sa_job_create( - this->ike_sa->get_id(this->ike_sa), FALSE); - DBG1(DBG_IKE, "IKE_SA rekeying failed, " - "trying again in %d seconds", retry); - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - lib->scheduler->schedule_job(lib->scheduler, job, retry); + schedule_delayed_rekey(this); } return SUCCESS; case NEED_MORE: @@ -293,55 +341,53 @@ METHOD(task_t, process_i, status_t, this->collision->get_type(this->collision) == TASK_IKE_REKEY) { private_ike_rekey_t *other = (private_ike_rekey_t*)this->collision; + host_t *host; + chunk_t this_nonce, other_nonce; - /* ike_init can be NULL, if child_sa is half-open */ - if (other->ike_init) - { - host_t *host; - chunk_t this_nonce, other_nonce; - - this_nonce = this->ike_init->get_lower_nonce(this->ike_init); - other_nonce = other->ike_init->get_lower_nonce(other->ike_init); + this_nonce = this->ike_init->get_lower_nonce(this->ike_init); + other_nonce = other->ike_init->get_lower_nonce(other->ike_init); - /* if we have the lower nonce, delete rekeyed SA. If not, delete - * the redundant. */ - if (memcmp(this_nonce.ptr, other_nonce.ptr, - min(this_nonce.len, other_nonce.len)) > 0) + /* if we have the lower nonce, delete rekeyed SA. If not, delete + * the redundant. */ + if (memcmp(this_nonce.ptr, other_nonce.ptr, + min(this_nonce.len, other_nonce.len)) < 0) + { + DBG1(DBG_IKE, "IKE_SA rekey collision lost, deleting redundant " + "IKE_SA %s[%d]", this->new_sa->get_name(this->new_sa), + this->new_sa->get_unique_id(this->new_sa)); + /* apply host for a proper delete */ + host = this->ike_sa->get_my_host(this->ike_sa); + this->new_sa->set_my_host(this->new_sa, host->clone(host)); + host = this->ike_sa->get_other_host(this->ike_sa); + this->new_sa->set_other_host(this->new_sa, host->clone(host)); + /* IKE_SAs in state IKE_REKEYED are silently deleted, so we use + * IKE_REKEYING */ + this->new_sa->set_state(this->new_sa, IKE_REKEYING); + if (this->new_sa->delete(this->new_sa) == DESTROY_ME) { - /* peer should delete this SA. Add a timeout just in case. */ - job_t *job = (job_t*)delete_ike_sa_job_create( - other->new_sa->get_id(other->new_sa), TRUE); - lib->scheduler->schedule_job(lib->scheduler, job, 10); - DBG1(DBG_IKE, "IKE_SA rekey collision won, waiting for delete"); - charon->ike_sa_manager->checkin(charon->ike_sa_manager, other->new_sa); - other->new_sa = NULL; + this->new_sa->destroy(this->new_sa); } else { - DBG1(DBG_IKE, "IKE_SA rekey collision lost, " - "deleting redundant IKE_SA"); - /* apply host for a proper delete */ - host = this->ike_sa->get_my_host(this->ike_sa); - this->new_sa->set_my_host(this->new_sa, host->clone(host)); - host = this->ike_sa->get_other_host(this->ike_sa); - this->new_sa->set_other_host(this->new_sa, host->clone(host)); - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - this->new_sa->set_state(this->new_sa, IKE_REKEYING); - if (this->new_sa->delete(this->new_sa) == DESTROY_ME) - { - this->new_sa->destroy(this->new_sa); - } - else - { - charon->ike_sa_manager->checkin( - charon->ike_sa_manager, this->new_sa); - } - charon->bus->set_sa(charon->bus, this->ike_sa); - this->new_sa = NULL; - establish_new(other); - return SUCCESS; + charon->ike_sa_manager->checkin(charon->ike_sa_manager, + this->new_sa); } + charon->bus->set_sa(charon->bus, this->ike_sa); + this->new_sa = NULL; + establish_new(other); + return SUCCESS; } + /* peer should delete this SA. Add a timeout just in case. */ + job_t *job = (job_t*)delete_ike_sa_job_create( + other->new_sa->get_id(other->new_sa), TRUE); + lib->scheduler->schedule_job(lib->scheduler, job, + HALF_OPEN_IKE_SA_TIMEOUT); + DBG1(DBG_IKE, "IKE_SA rekey collision won, waiting for delete for " + "redundant IKE_SA %s[%d]", other->new_sa->get_name(other->new_sa), + other->new_sa->get_unique_id(other->new_sa)); + other->new_sa->set_state(other->new_sa, IKE_REKEYED); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, other->new_sa); + other->new_sa = NULL; charon->bus->set_sa(charon->bus, this->ike_sa); } @@ -361,11 +407,41 @@ METHOD(task_t, get_type, task_type_t, return TASK_IKE_REKEY; } +METHOD(ike_rekey_t, did_collide, bool, + private_ike_rekey_t *this) +{ + return this->collision && + this->collision->get_type(this->collision) == TASK_IKE_REKEY; +} + METHOD(ike_rekey_t, collide, void, private_ike_rekey_t* this, task_t *other) { DBG1(DBG_IKE, "detected %N collision with %N", task_type_names, TASK_IKE_REKEY, task_type_names, other->get_type(other)); + + switch (other->get_type(other)) + { + case TASK_IKE_DELETE: + conclude_undetected_collision(this); + other->destroy(other); + return; + case TASK_IKE_REKEY: + { + private_ike_rekey_t *rekey = (private_ike_rekey_t*)other; + + if (!rekey->ike_init) + { + DBG1(DBG_IKE, "colliding exchange did not result in an IKE_SA, " + "ignore"); + other->destroy(other); + return; + } + break; + } + default: + break; + } DESTROY_IF(this->collision); this->collision = other; } @@ -425,6 +501,7 @@ ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator) .migrate = _migrate, .destroy = _destroy, }, + .did_collide = _did_collide, .collide = _collide, }, .ike_sa = ike_sa, diff --git a/src/libcharon/sa/ikev2/tasks/ike_rekey.h b/src/libcharon/sa/ikev2/tasks/ike_rekey.h index 6a12e9034..86b512c92 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_rekey.h +++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.h @@ -1,6 +1,7 @@ /* + * Copyright (C) 2016 Tobias Brunner * Copyright (C) 2007 Martin Willi - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -38,6 +39,13 @@ struct ike_rekey_t { task_t task; /** + * Check if there was a rekey collision. + * + * @return TRUE if there was a rekey collision before + */ + bool (*did_collide)(ike_rekey_t *this); + + /** * Register a rekeying task which collides with this one. * * If two peers initiate rekeying at the same time, the collision must diff --git a/src/libcharon/sa/redirect_manager.c b/src/libcharon/sa/redirect_manager.c index ff92ac29f..45b7e79df 100644 --- a/src/libcharon/sa/redirect_manager.c +++ b/src/libcharon/sa/redirect_manager.c @@ -248,7 +248,7 @@ identification_t *redirect_data_parse(chunk_t data, chunk_t *nonce) bio_reader_t *reader; id_type_t id_type; chunk_t gateway; - u_int8_t type; + uint8_t type; reader = bio_reader_create(data); if (!reader->read_uint8(reader, &type) || diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c index 0e9cf6e1f..40e291be5 100644 --- a/src/libcharon/sa/shunt_manager.c +++ b/src/libcharon/sa/shunt_manager.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2015 Tobias Brunner - * Copyright (C) 2011 Andreas Steffen + * Copyright (C) 2015-2016 Tobias Brunner + * Copyright (C) 2011-2016 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -68,6 +68,8 @@ static bool install_shunt_policy(child_cfg_t *child) policy_type_t policy_type; policy_priority_t policy_prio; status_t status = SUCCESS; + uint32_t manual_prio; + char *interface; ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT }; switch (child->get_mode(child)) @@ -92,6 +94,9 @@ static bool install_shunt_policy(child_cfg_t *child) other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts); hosts->destroy(hosts); + manual_prio = child->get_manual_prio(child); + interface = child->get_interface(child); + /* enumerate pairs of traffic selectors */ e_my_ts = my_ts_list->create_enumerator(my_ts_list); while (e_my_ts->enumerate(e_my_ts, &my_ts)) @@ -110,25 +115,37 @@ static bool install_shunt_policy(child_cfg_t *child) continue; } /* install out policy */ - status |= charon->kernel->add_policy(charon->kernel, - host_any, host_any, - my_ts, other_ts, POLICY_OUT, policy_type, - &sa, child->get_mark(child, FALSE), - policy_prio); - + kernel_ipsec_policy_id_t id = { + .dir = POLICY_OUT, + .src_ts = my_ts, + .dst_ts = other_ts, + .mark = child->get_mark(child, FALSE), + .interface = interface, + }; + kernel_ipsec_manage_policy_t policy = { + .type = policy_type, + .prio = policy_prio, + .manual_prio = manual_prio, + .src = host_any, + .dst = host_any, + .sa = &sa, + }; + status |= charon->kernel->add_policy(charon->kernel, &id, &policy); + /* install "outbound" forward policy */ + id.dir = POLICY_FWD; + status |= charon->kernel->add_policy(charon->kernel, &id, &policy); /* install in policy */ - status |= charon->kernel->add_policy(charon->kernel, - host_any, host_any, - other_ts, my_ts, POLICY_IN, policy_type, - &sa, child->get_mark(child, TRUE), - policy_prio); - - /* install forward policy */ - status |= charon->kernel->add_policy(charon->kernel, - host_any, host_any, - other_ts, my_ts, POLICY_FWD, policy_type, - &sa, child->get_mark(child, TRUE), - policy_prio); + id = (kernel_ipsec_policy_id_t){ + .dir = POLICY_IN, + .src_ts = other_ts, + .dst_ts = my_ts, + .mark = child->get_mark(child, TRUE), + .interface = interface, + }; + status |= charon->kernel->add_policy(charon->kernel, &id, &policy); + /* install "inbound" forward policy */ + id.dir = POLICY_FWD; + status |= charon->kernel->add_policy(charon->kernel, &id, &policy); } e_other_ts->destroy(e_other_ts); } @@ -205,6 +222,8 @@ static void uninstall_shunt_policy(child_cfg_t *child) policy_type_t policy_type; policy_priority_t policy_prio; status_t status = SUCCESS; + uint32_t manual_prio; + char *interface; ipsec_sa_cfg_t sa = { .mode = MODE_TRANSPORT }; switch (child->get_mode(child)) @@ -229,6 +248,9 @@ static void uninstall_shunt_policy(child_cfg_t *child) other_ts_list = child->get_traffic_selectors(child, FALSE, NULL, hosts); hosts->destroy(hosts); + manual_prio = child->get_manual_prio(child); + interface = child->get_interface(child); + /* enumerate pairs of traffic selectors */ e_my_ts = my_ts_list->create_enumerator(my_ts_list); while (e_my_ts->enumerate(e_my_ts, &my_ts)) @@ -247,25 +269,37 @@ static void uninstall_shunt_policy(child_cfg_t *child) continue; } /* uninstall out policy */ - status |= charon->kernel->del_policy(charon->kernel, - host_any, host_any, - my_ts, other_ts, POLICY_OUT, policy_type, - &sa, child->get_mark(child, FALSE), - policy_prio); - + kernel_ipsec_policy_id_t id = { + .dir = POLICY_OUT, + .src_ts = my_ts, + .dst_ts = other_ts, + .mark = child->get_mark(child, FALSE), + .interface = interface, + }; + kernel_ipsec_manage_policy_t policy = { + .type = policy_type, + .prio = policy_prio, + .manual_prio = manual_prio, + .src = host_any, + .dst = host_any, + .sa = &sa, + }; + status |= charon->kernel->del_policy(charon->kernel, &id, &policy); + /* uninstall "outbound" forward policy */ + id.dir = POLICY_FWD; + status |= charon->kernel->del_policy(charon->kernel, &id, &policy); /* uninstall in policy */ - status |= charon->kernel->del_policy(charon->kernel, - host_any, host_any, - other_ts, my_ts, POLICY_IN, policy_type, - &sa, child->get_mark(child, TRUE), - policy_prio); - - /* uninstall forward policy */ - status |= charon->kernel->del_policy(charon->kernel, - host_any, host_any, - other_ts, my_ts, POLICY_FWD, policy_type, - &sa, child->get_mark(child, TRUE), - policy_prio); + id = (kernel_ipsec_policy_id_t){ + .dir = POLICY_IN, + .src_ts = other_ts, + .dst_ts = my_ts, + .mark = child->get_mark(child, TRUE), + .interface = interface, + }; + status |= charon->kernel->del_policy(charon->kernel, &id, &policy); + /* uninstall "inbound" forward policy */ + id.dir = POLICY_FWD; + status |= charon->kernel->del_policy(charon->kernel, &id, &policy); } e_other_ts->destroy(e_other_ts); } diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h index e7a6bf463..86077d373 100644 --- a/src/libcharon/sa/task_manager.h +++ b/src/libcharon/sa/task_manager.h @@ -1,6 +1,7 @@ /* + * Copyright (C) 2013-2016 Tobias Brunner * Copyright (C) 2006 Martin Willi - * Hochschule fuer Technik Rapperswil + * HSR Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -122,7 +123,17 @@ struct task_manager_t { * * @param task task to queue */ - void (*queue_task) (task_manager_t *this, task_t *task); + void (*queue_task)(task_manager_t *this, task_t *task); + + /** + * Queue a task in the manager, but delay its initiation for at least the + * given number of seconds. + * + * @param task task to queue + * @param delay minimum delay in s before initiating the task + */ + void (*queue_task_delayed)(task_manager_t *this, task_t *task, + uint32_t delay); /** * Queue IKE_SA establishing tasks. @@ -160,7 +171,7 @@ struct task_manager_t { * @param tsi initiator traffic selector, if packet-triggered * @param tsr responder traffic selector, if packet-triggered */ - void (*queue_child)(task_manager_t *this, child_cfg_t *cfg, u_int32_t reqid, + void (*queue_child)(task_manager_t *this, child_cfg_t *cfg, uint32_t reqid, traffic_selector_t *tsi, traffic_selector_t *tsr); /** @@ -170,7 +181,7 @@ struct task_manager_t { * @param spi CHILD_SA SPI to rekey */ void (*queue_child_rekey)(task_manager_t *this, protocol_id_t protocol, - u_int32_t spi); + uint32_t spi); /** * Queue CHILD_SA delete tasks. @@ -180,7 +191,7 @@ struct task_manager_t { * @param expired TRUE if SA already expired */ void (*queue_child_delete)(task_manager_t *this, protocol_id_t protocol, - u_int32_t spi, bool expired); + uint32_t spi, bool expired); /** * Queue liveness checking tasks. @@ -199,7 +210,7 @@ struct task_manager_t { * - INVALID_STATE if retransmission not required * - SUCCESS if retransmission sent */ - status_t (*retransmit) (task_manager_t *this, u_int32_t message_id); + status_t (*retransmit) (task_manager_t *this, uint32_t message_id); /** * Migrate all queued tasks from other to this. @@ -242,7 +253,7 @@ struct task_manager_t { * @param initiate message ID / DPD seq to initiate exchanges (send) * @param respond message ID / DPD seq to respond to exchanges (expect) */ - void (*reset) (task_manager_t *this, u_int32_t initiate, u_int32_t respond); + void (*reset) (task_manager_t *this, uint32_t initiate, uint32_t respond); /** * Check if we are currently waiting for a reply. diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index 85e220775..40a0682f2 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -115,7 +115,7 @@ typedef struct { /** pending IKE_SA connecting upon acquire */ ike_sa_t *ike_sa; /** reqid of pending trap policy */ - u_int32_t reqid; + uint32_t reqid; /** destination address (wildcard case) */ host_t *dst; } acquire_t; @@ -143,7 +143,7 @@ static void destroy_acquire(acquire_t *this) /** * match an acquire entry by reqid */ -static bool acquire_by_reqid(acquire_t *this, u_int32_t *reqid) +static bool acquire_by_reqid(acquire_t *this, uint32_t *reqid) { return this->reqid == *reqid; } @@ -156,9 +156,9 @@ static bool acquire_by_dst(acquire_t *this, host_t *dst) return this->dst && this->dst->ip_equals(this->dst, dst); } -METHOD(trap_manager_t, install, u_int32_t, +METHOD(trap_manager_t, install, uint32_t, private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child, - u_int32_t reqid) + uint32_t reqid) { entry_t *entry, *found = NULL; ike_cfg_t *ike_cfg; @@ -197,9 +197,7 @@ METHOD(trap_manager_t, install, u_int32_t, me = charon->kernel->get_source_addr(charon->kernel, other, NULL); if (!me) { - DBG1(DBG_CFG, "installing trap failed, local address unknown"); - other->destroy(other); - return 0; + me = host_create_any(other->get_family(other)); } me->set_port(me, ike_cfg->get_my_port(ike_cfg)); } @@ -307,7 +305,7 @@ METHOD(trap_manager_t, install, u_int32_t, } METHOD(trap_manager_t, uninstall, bool, - private_trap_manager_t *this, u_int32_t reqid) + private_trap_manager_t *this, uint32_t reqid) { enumerator_t *enumerator; entry_t *entry, *found = NULL; @@ -366,12 +364,12 @@ METHOD(trap_manager_t, create_enumerator, enumerator_t*, (void*)this->lock->unlock); } -METHOD(trap_manager_t, find_reqid, u_int32_t, +METHOD(trap_manager_t, find_reqid, uint32_t, private_trap_manager_t *this, child_cfg_t *child) { enumerator_t *enumerator; entry_t *entry; - u_int32_t reqid = 0; + uint32_t reqid = 0; this->lock->read_lock(this->lock); enumerator = this->traps->create_enumerator(this->traps); @@ -392,7 +390,7 @@ METHOD(trap_manager_t, find_reqid, u_int32_t, } METHOD(trap_manager_t, acquire, void, - private_trap_manager_t *this, u_int32_t reqid, + private_trap_manager_t *this, uint32_t reqid, traffic_selector_t *src, traffic_selector_t *dst) { enumerator_t *enumerator; @@ -430,7 +428,7 @@ METHOD(trap_manager_t, acquire, void, if (wildcard) { /* for wildcard acquires we check that we don't have a pending acquire * with the same peer */ - u_int8_t mask; + uint8_t mask; dst->to_subnet(dst, &host, &mask); if (this->acquires->find_first(this->acquires, (void*)acquire_by_dst, @@ -483,8 +481,8 @@ METHOD(trap_manager_t, acquire, void, if (ike_sa) { ike_cfg_t *ike_cfg; - u_int16_t port; - u_int8_t mask; + uint16_t port; + uint8_t mask; ike_sa->set_peer_cfg(ike_sa, peer); ike_cfg = ike_sa->get_ike_cfg(ike_sa); diff --git a/src/libcharon/sa/trap_manager.h b/src/libcharon/sa/trap_manager.h index 0491107fd..083ea3dbf 100644 --- a/src/libcharon/sa/trap_manager.h +++ b/src/libcharon/sa/trap_manager.h @@ -40,8 +40,8 @@ struct trap_manager_t { * @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, u_int32_t reqid); + uint32_t (*install)(trap_manager_t *this, peer_cfg_t *peer, + child_cfg_t *child, uint32_t reqid); /** * Uninstall a trap policy. @@ -49,7 +49,7 @@ struct trap_manager_t { * @param id reqid of CHILD_SA to uninstall, returned by install() * @return TRUE if uninstalled successfully */ - bool (*uninstall)(trap_manager_t *this, u_int32_t reqid); + bool (*uninstall)(trap_manager_t *this, uint32_t reqid); /** * Create an enumerator over all installed traps. @@ -64,7 +64,7 @@ struct trap_manager_t { * @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); + uint32_t (*find_reqid)(trap_manager_t *this, child_cfg_t *child); /** * Acquire an SA triggered by an installed trap. @@ -73,7 +73,7 @@ struct trap_manager_t { * @param src source of the triggering packet * @param dst destination of the triggering packet */ - void (*acquire)(trap_manager_t *this, u_int32_t reqid, + void (*acquire)(trap_manager_t *this, uint32_t reqid, traffic_selector_t *src, traffic_selector_t *dst); /** |