diff options
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r-- | src/libcharon/sa/child_sa.c | 560 | ||||
-rw-r--r-- | src/libcharon/sa/child_sa.h | 120 | ||||
-rw-r--r-- | src/libcharon/sa/eap/eap_manager.c | 53 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa.c | 57 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa_manager.c | 121 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/task_manager_v1.c | 31 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/quick_mode.c | 37 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/connect_manager.c | 164 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/task_manager_v2.c | 47 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/child_create.c | 96 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/child_delete.c | 229 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/child_rekey.c | 16 | ||||
-rw-r--r-- | src/libcharon/sa/shunt_manager.c | 22 | ||||
-rw-r--r-- | src/libcharon/sa/task_manager.c | 32 | ||||
-rw-r--r-- | src/libcharon/sa/task_manager.h | 16 | ||||
-rw-r--r-- | src/libcharon/sa/trap_manager.c | 72 |
16 files changed, 1204 insertions, 469 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index b9dd59b07..3d9f6133b 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -1,6 +1,6 @@ /* + * Copyright (C) 2006-2017 Tobias Brunner * Copyright (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 @@ -40,6 +40,12 @@ ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING, "DESTROYING", ); +ENUM(child_sa_outbound_state_names, CHILD_OUTBOUND_NONE, CHILD_OUTBOUND_INSTALLED, + "NONE", + "REGISTERED", + "INSTALLED", +); + typedef struct private_child_sa_t private_child_sa_t; /** @@ -92,6 +98,31 @@ struct private_child_sa_t { array_t *other_ts; /** + * Outbound encryption key cached during a rekeying + */ + chunk_t encr_r; + + /** + * Outbound integrity key cached during a rekeying + */ + chunk_t integ_r; + + /** + * Whether the outbound SA has only been registered yet during a rekeying + */ + child_sa_outbound_state_t outbound_state; + + /** + * Whether the peer supports TFCv3 + */ + bool tfcv3; + + /** + * The outbound SPI of the CHILD_SA that replaced this one during a rekeying + */ + uint32_t rekey_spi; + + /** * Protocol used to protect this SA, ESP|AH */ protocol_id_t protocol; @@ -265,6 +296,10 @@ METHOD(child_sa_t, get_config, child_cfg_t*, METHOD(child_sa_t, set_state, void, private_child_sa_t *this, child_sa_state_t state) { + DBG2(DBG_CHD, "CHILD_SA %s{%d} state change: %N => %N", + get_name(this), this->unique_id, + child_sa_state_names, this->state, + child_sa_state_names, state); charon->bus->child_state_change(charon->bus, &this->public, state); this->state = state; } @@ -275,6 +310,12 @@ METHOD(child_sa_t, get_state, child_sa_state_t, return this->state; } +METHOD(child_sa_t, get_outbound_state, child_sa_outbound_state_t, + private_child_sa_t *this) +{ + return this->outbound_state; +} + METHOD(child_sa_t, get_spi, uint32_t, private_child_sa_t *this, bool inbound) { @@ -394,10 +435,11 @@ struct policy_enumerator_t { }; METHOD(enumerator_t, policy_enumerate, bool, - policy_enumerator_t *this, traffic_selector_t **my_out, - traffic_selector_t **other_out) + policy_enumerator_t *this, va_list args) { - traffic_selector_t *other_ts; + traffic_selector_t *other_ts, **my_out, **other_out; + + VA_ARGS_VGET(args, my_out, other_out); while (this->ts || this->mine->enumerate(this->mine, &this->ts)) { @@ -446,7 +488,8 @@ METHOD(child_sa_t, create_policy_enumerator, enumerator_t*, INIT(e, .public = { - .enumerate = (void*)_policy_enumerate, + .enumerate = enumerator_enumerate_default, + .venumerate = _policy_enumerate, .destroy = _policy_destroy, }, .mine = array_create_enumerator(this->my_ts), @@ -504,7 +547,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) } else { - if (this->other_spi) + if (this->other_spi && this->outbound_state == CHILD_OUTBOUND_INSTALLED) { kernel_ipsec_sa_id_t id = { .src = this->my_addr, @@ -691,14 +734,16 @@ METHOD(child_sa_t, alloc_cpi, uint16_t, return 0; } -METHOD(child_sa_t, install, status_t, - 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) +/** + * Install the given SA in the kernel + */ +static status_t install_internal(private_child_sa_t *this, chunk_t encr, + chunk_t integ, uint32_t spi, uint16_t cpi, bool initiator, bool inbound, + bool tfcv3) { 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; + linked_list_t *my_ts, *other_ts, *src_ts, *dst_ts; time_t now; kernel_ipsec_sa_id_t id; kernel_ipsec_add_sa_t sa; @@ -708,6 +753,12 @@ METHOD(child_sa_t, install, status_t, status_t status; bool update = FALSE; + /* BEET requires the bound address from the traffic selectors */ + my_ts = linked_list_create_from_enumerator( + array_create_enumerator(this->my_ts)); + other_ts = linked_list_create_from_enumerator( + array_create_enumerator(this->other_ts)); + /* now we have to decide which spi to use. Use self allocated, if "in", * or the one in the proposal, if not "in" (others). Additionally, * source and dest host switch depending on the role */ @@ -721,6 +772,8 @@ METHOD(child_sa_t, install, status_t, } this->my_spi = spi; this->my_cpi = cpi; + dst_ts = my_ts; + src_ts = other_ts; } else { @@ -728,11 +781,14 @@ METHOD(child_sa_t, install, status_t, dst = this->other_addr; this->other_spi = spi; this->other_cpi = cpi; + src_ts = my_ts; + dst_ts = other_ts; if (tfcv3) { tfc = this->config->get_tfc(this->config); } + this->outbound_state = CHILD_OUTBOUND_INSTALLED; } DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound", @@ -748,12 +804,22 @@ METHOD(child_sa_t, install, status_t, this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS, &esn, NULL); + if (int_alg == AUTH_HMAC_SHA2_256_128 && + this->config->has_option(this->config, OPT_SHA256_96)) + { + DBG2(DBG_CHD, " using %N with 96-bit truncation", + integrity_algorithm_names, int_alg); + int_alg = AUTH_HMAC_SHA2_256_96; + } + if (!this->reqid_allocated && !this->static_reqid) { status = charon->kernel->alloc_reqid(charon->kernel, my_ts, other_ts, this->mark_in, this->mark_out, &this->reqid); if (status != SUCCESS) { + my_ts->destroy(my_ts); + other_ts->destroy(other_ts); return status; } this->reqid_allocated = TRUE; @@ -783,18 +849,6 @@ METHOD(child_sa_t, install, status_t, lifetime->time.rekey = 0; } - /* BEET requires the bound address from the traffic selectors */ - if (inbound) - { - dst_ts = my_ts; - src_ts = other_ts; - } - else - { - src_ts = my_ts; - dst_ts = other_ts; - } - id = (kernel_ipsec_sa_id_t){ .src = src, .dst = dst, @@ -818,6 +872,7 @@ METHOD(child_sa_t, install, status_t, .ipcomp = this->ipcomp, .cpi = cpi, .encap = this->encap, + .hw_offload = this->config->has_option(this->config, OPT_HW_OFFLOAD), .esn = esn, .initiator = initiator, .inbound = inbound, @@ -826,11 +881,21 @@ METHOD(child_sa_t, install, status_t, status = charon->kernel->add_sa(charon->kernel, &id, &sa); + my_ts->destroy(my_ts); + other_ts->destroy(other_ts); free(lifetime); return status; } +METHOD(child_sa_t, install, status_t, + private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi, + uint16_t cpi, bool initiator, bool inbound, bool tfcv3) +{ + return install_internal(this, encr, integ, spi, cpi, initiator, inbound, + tfcv3); +} + /** * Check kernel interface if policy updates are required */ @@ -887,34 +952,21 @@ static void prepare_sa_cfg(private_child_sa_t *this, ipsec_sa_cfg_t *my_sa, } /** - * Install 3 policies: out, in and forward + * Install inbound policie(s): in, fwd */ -static status_t install_policies_internal(private_child_sa_t *this, +static status_t install_policies_inbound(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, 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 = { + kernel_ipsec_policy_id_t 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 = { + kernel_ipsec_manage_policy_t in_policy = { .type = type, .prio = priority, .manual_prio = manual_prio, @@ -924,13 +976,45 @@ static status_t install_policies_internal(private_child_sa_t *this, }; status_t status = SUCCESS; - 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) { in_id.dir = POLICY_FWD; status |= charon->kernel->add_policy(charon->kernel, &in_id, &in_policy); + } + return status; +} +/** + * Install outbound policie(s): out, [fwd] + */ +static status_t install_policies_outbound(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, 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), + }; + kernel_ipsec_manage_policy_t out_policy = { + .type = type, + .prio = priority, + .manual_prio = manual_prio, + .src = my_addr, + .dst = other_addr, + .sa = other_sa, + }; + status_t status = SUCCESS; + + status |= charon->kernel->add_policy(charon->kernel, &out_id, &out_policy); + + if (this->mode != MODE_TRANSPORT && this->policies_fwd_out) + { /* 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 @@ -939,52 +1023,56 @@ static status_t install_policies_internal(private_child_sa_t *this, * 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. */ - if (this->policies_fwd_out) + out_id.dir = POLICY_FWD; + other_sa->reqid = 0; + if (priority == POLICY_PRIORITY_DEFAULT) { - 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; + 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; } /** - * Delete 3 policies: out, in and forward + * Install all policies */ -static void del_policies_internal(private_child_sa_t *this, +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, uint32_t manual_prio) +{ + status_t status = SUCCESS; + + status |= install_policies_inbound(this, my_addr, other_addr, my_ts, + other_ts, my_sa, other_sa, type, + priority, manual_prio); + status |= install_policies_outbound(this, my_addr, other_addr, my_ts, + other_ts, my_sa, other_sa, type, + priority, manual_prio); + return status; +} + +/** + * Delete inbound policies: in, fwd + */ +static void del_policies_inbound(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, 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 = { + kernel_ipsec_policy_id_t 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 = { + kernel_ipsec_manage_policy_t in_policy = { .type = type, .prio = priority, .manual_prio = manual_prio, @@ -993,49 +1081,83 @@ static void del_policies_internal(private_child_sa_t *this, .sa = my_sa, }; - 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) { in_id.dir = POLICY_FWD; charon->kernel->del_policy(charon->kernel, &in_id, &in_policy); + } +} - if (this->policies_fwd_out) +/** + * Delete outbound policies: out, [fwd] + */ +static void del_policies_outbound(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, 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), + }; + kernel_ipsec_manage_policy_t out_policy = { + .type = type, + .prio = priority, + .manual_prio = manual_prio, + .src = my_addr, + .dst = other_addr, + .sa = other_sa, + }; + + charon->kernel->del_policy(charon->kernel, &out_id, &out_policy); + + if (this->mode != MODE_TRANSPORT && this->policies_fwd_out) + { + out_id.dir = POLICY_FWD; + other_sa->reqid = 0; + if (priority == POLICY_PRIORITY_DEFAULT) { - 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; + out_policy.prio = POLICY_PRIORITY_ROUTED; } + charon->kernel->del_policy(charon->kernel, &out_id, &out_policy); + other_sa->reqid = this->reqid; } } -METHOD(child_sa_t, add_policies, status_t, +/** + * Delete in- and outbound policies + */ +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, uint32_t manual_prio) +{ + del_policies_outbound(this, my_addr, other_addr, my_ts, other_ts, my_sa, + other_sa, type, priority, manual_prio); + del_policies_inbound(this, my_addr, other_addr, my_ts, other_ts, my_sa, + other_sa, type, priority, manual_prio); +} + +METHOD(child_sa_t, set_policies, void, private_child_sa_t *this, linked_list_t *my_ts_list, linked_list_t *other_ts_list) { enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; - status_t status = SUCCESS; - if (!this->reqid_allocated && !this->static_reqid) + if (array_count(this->my_ts)) { - /* trap policy, get or confirm reqid */ - status = charon->kernel->alloc_reqid( - charon->kernel, my_ts_list, other_ts_list, - this->mark_in, this->mark_out, &this->reqid); - if (status != SUCCESS) - { - return status; - } - this->reqid_allocated = TRUE; + array_destroy_offset(this->my_ts, + offsetof(traffic_selector_t, destroy)); + this->my_ts = array_create(0, 0); } - - /* apply traffic selectors */ enumerator = my_ts_list->create_enumerator(my_ts_list); while (enumerator->enumerate(enumerator, &my_ts)) { @@ -1044,6 +1166,12 @@ METHOD(child_sa_t, add_policies, status_t, enumerator->destroy(enumerator); array_sort(this->my_ts, (void*)traffic_selector_cmp, NULL); + if (array_count(this->other_ts)) + { + array_destroy_offset(this->other_ts, + offsetof(traffic_selector_t, destroy)); + this->other_ts = array_create(0, 0); + } enumerator = other_ts_list->create_enumerator(other_ts_list); while (enumerator->enumerate(enumerator, &other_ts)) { @@ -1051,12 +1179,40 @@ METHOD(child_sa_t, add_policies, status_t, } enumerator->destroy(enumerator); array_sort(this->other_ts, (void*)traffic_selector_cmp, NULL); +} + +METHOD(child_sa_t, install_policies, status_t, + private_child_sa_t *this) +{ + enumerator_t *enumerator; + linked_list_t *my_ts_list, *other_ts_list; + traffic_selector_t *my_ts, *other_ts; + status_t status = SUCCESS; - if (this->config->install_policy(this->config)) + if (!this->reqid_allocated && !this->static_reqid) + { + my_ts_list = linked_list_create_from_enumerator( + array_create_enumerator(this->my_ts)); + other_ts_list = linked_list_create_from_enumerator( + array_create_enumerator(this->other_ts)); + status = charon->kernel->alloc_reqid( + charon->kernel, my_ts_list, other_ts_list, + this->mark_in, this->mark_out, &this->reqid); + my_ts_list->destroy(my_ts_list); + other_ts_list->destroy(other_ts_list); + if (status != SUCCESS) + { + return status; + } + this->reqid_allocated = TRUE; + } + + if (!this->config->has_option(this->config, OPT_NO_POLICIES)) { policy_priority_t priority; ipsec_sa_cfg_t my_sa, other_sa; uint32_t manual_prio; + bool install_outbound; prepare_sa_cfg(this, &my_sa, &other_sa); manual_prio = this->config->get_manual_prio(this->config); @@ -1066,6 +1222,7 @@ METHOD(child_sa_t, add_policies, status_t, this->trap = this->state == CHILD_CREATED; priority = this->trap ? POLICY_PRIORITY_ROUTED : POLICY_PRIORITY_DEFAULT; + install_outbound = this->outbound_state != CHILD_OUTBOUND_REGISTERED; /* enumerate pairs of traffic selectors */ enumerator = create_policy_enumerator(this); @@ -1074,20 +1231,27 @@ 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 && manual_prio == 0 && - require_policy_update()) + require_policy_update() && install_outbound) { - status |= install_policies_internal(this, this->my_addr, + status |= install_policies_outbound(this, this->my_addr, this->other_addr, my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP, POLICY_PRIORITY_FALLBACK, 0); } - /* install policies */ - status |= install_policies_internal(this, this->my_addr, + status |= install_policies_inbound(this, this->my_addr, this->other_addr, my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, priority, manual_prio); + if (install_outbound) + { + status |= install_policies_outbound(this, this->my_addr, + this->other_addr, my_ts, other_ts, + &my_sa, &other_sa, POLICY_IPSEC, + priority, manual_prio); + + } if (status != SUCCESS) { break; @@ -1103,13 +1267,150 @@ METHOD(child_sa_t, add_policies, status_t, return status; } -/** - * Callback to reinstall a virtual IP - */ -static void reinstall_vip(host_t *vip, host_t *me) +METHOD(child_sa_t, register_outbound, void, + private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi, + uint16_t cpi, bool tfcv3) +{ + DBG2(DBG_CHD, "registering outbound %N SA", protocol_id_names, + this->protocol); + DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), this->my_addr, + this->other_addr); + + this->other_spi = spi; + this->other_cpi = cpi; + this->encr_r = chunk_clone(encr); + this->integ_r = chunk_clone(integ); + this->tfcv3 = tfcv3; + this->outbound_state = CHILD_OUTBOUND_REGISTERED; +} + +METHOD(child_sa_t, install_outbound, status_t, + private_child_sa_t *this) +{ + enumerator_t *enumerator; + traffic_selector_t *my_ts, *other_ts; + status_t status; + + status = install_internal(this, this->encr_r, this->integ_r, + this->other_spi, this->other_cpi, FALSE, FALSE, + this->tfcv3); + chunk_clear(&this->encr_r); + chunk_clear(&this->integ_r); + if (status != SUCCESS) + { + return status; + } + if (!this->config->has_option(this->config, OPT_NO_POLICIES)) + { + 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); + + enumerator = create_policy_enumerator(this); + while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) + { + /* install outbound drop policy to avoid packets leaving unencrypted + * when updating policies */ + if (manual_prio == 0 && require_policy_update()) + { + status |= install_policies_outbound(this, this->my_addr, + this->other_addr, my_ts, other_ts, + &my_sa, &other_sa, POLICY_DROP, + POLICY_PRIORITY_FALLBACK, 0); + } + status |= install_policies_outbound(this, this->my_addr, + this->other_addr, my_ts, other_ts, + &my_sa, &other_sa, POLICY_IPSEC, + POLICY_PRIORITY_DEFAULT, manual_prio); + if (status != SUCCESS) + { + break; + } + } + enumerator->destroy(enumerator); + } + return status; +} + +METHOD(child_sa_t, remove_outbound, void, + private_child_sa_t *this) +{ + enumerator_t *enumerator; + traffic_selector_t *my_ts, *other_ts; + + switch (this->outbound_state) + { + case CHILD_OUTBOUND_INSTALLED: + break; + case CHILD_OUTBOUND_REGISTERED: + chunk_clear(&this->encr_r); + chunk_clear(&this->integ_r); + this->outbound_state = CHILD_OUTBOUND_NONE; + /* fall-through */ + case CHILD_OUTBOUND_NONE: + return; + } + + if (!this->config->has_option(this->config, OPT_NO_POLICIES)) + { + 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); + + enumerator = create_policy_enumerator(this); + while (enumerator->enumerate(enumerator, &my_ts, &other_ts)) + { + del_policies_outbound(this, this->my_addr, this->other_addr, + my_ts, other_ts, &my_sa, &other_sa, + POLICY_IPSEC, POLICY_PRIORITY_DEFAULT, + manual_prio); + if (manual_prio == 0 && require_policy_update()) + { + del_policies_outbound(this, this->my_addr, this->other_addr, + my_ts, other_ts, &my_sa, &other_sa, + POLICY_DROP, POLICY_PRIORITY_FALLBACK, 0); + } + } + enumerator->destroy(enumerator); + } + + 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); + this->outbound_state = CHILD_OUTBOUND_NONE; +} + +METHOD(child_sa_t, set_rekey_spi, void, + private_child_sa_t *this, uint32_t spi) +{ + this->rekey_spi = spi; +} + +METHOD(child_sa_t, get_rekey_spi, uint32_t, + private_child_sa_t *this) { + return this->rekey_spi; +} + +CALLBACK(reinstall_vip, void, + host_t *vip, va_list args) +{ + host_t *me; char *iface; + VA_ARGS_VGET(args, me); if (charon->kernel->get_interface(charon->kernel, me, &iface)) { charon->kernel->del_ip(charon->kernel, vip, -1, TRUE); @@ -1134,8 +1435,9 @@ METHOD(child_sa_t, update, status_t, old = this->state; set_state(this, CHILD_UPDATING); - transport_proxy_mode = this->config->use_proxy_mode(this->config) && - this->mode == MODE_TRANSPORT; + transport_proxy_mode = this->mode == MODE_TRANSPORT && + this->config->has_option(this->config, + OPT_PROXY_MODE); if (!transport_proxy_mode) { @@ -1189,7 +1491,8 @@ METHOD(child_sa_t, update, status_t, } } - if (this->config->install_policy(this->config) && require_policy_update()) + if (!this->config->has_option(this->config, OPT_NO_POLICIES) && + require_policy_update()) { if (!me->ip_equals(me, this->my_addr) || !other->ip_equals(other, this->other_addr)) @@ -1229,7 +1532,7 @@ METHOD(child_sa_t, update, status_t, /* we reinstall the virtual IP to handle interface roaming * correctly */ - vips->invoke_function(vips, (void*)reinstall_vip, me); + vips->invoke_function(vips, reinstall_vip, me); /* reinstall updated policies */ install_policies_internal(this, me, other, my_ts, other_ts, @@ -1239,12 +1542,12 @@ METHOD(child_sa_t, update, status_t, /* update fallback policies after the new policy is in place */ if (manual_prio == 0) { - del_policies_internal(this, this->my_addr, this->other_addr, + del_policies_outbound(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, + install_policies_outbound(this, me, other, my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP, POLICY_PRIORITY_FALLBACK, 0); } @@ -1287,25 +1590,35 @@ METHOD(child_sa_t, destroy, void, set_state(this, CHILD_DESTROYING); - if (this->config->install_policy(this->config)) + if (!this->config->has_option(this->config, OPT_NO_POLICIES)) { ipsec_sa_cfg_t my_sa, other_sa; uint32_t manual_prio; + bool del_outbound; prepare_sa_cfg(this, &my_sa, &other_sa); manual_prio = this->config->get_manual_prio(this->config); + del_outbound = this->trap || + this->outbound_state == CHILD_OUTBOUND_INSTALLED; /* 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, manual_prio); - if (priority == POLICY_PRIORITY_DEFAULT && manual_prio == 0 && - require_policy_update()) + if (del_outbound) { - del_policies_internal(this, this->my_addr, this->other_addr, + del_policies_outbound(this, this->my_addr, + this->other_addr, my_ts, other_ts, + &my_sa, &other_sa, POLICY_IPSEC, + priority, manual_prio); + } + del_policies_inbound(this, this->my_addr, this->other_addr, + my_ts, other_ts, &my_sa, &other_sa, + POLICY_IPSEC, priority, manual_prio); + if (!this->trap && manual_prio == 0 && require_policy_update() && + del_outbound) + { + del_policies_outbound(this, this->my_addr, this->other_addr, my_ts, other_ts, &my_sa, &other_sa, POLICY_DROP, POLICY_PRIORITY_FALLBACK, 0); } @@ -1327,7 +1640,7 @@ METHOD(child_sa_t, destroy, void, }; charon->kernel->del_sa(charon->kernel, &id, &sa); } - if (this->other_spi) + if (this->other_spi && this->outbound_state == CHILD_OUTBOUND_INSTALLED) { kernel_ipsec_sa_id_t id = { .src = this->my_addr, @@ -1357,6 +1670,8 @@ METHOD(child_sa_t, destroy, void, this->other_addr->destroy(this->other_addr); DESTROY_IF(this->proposal); this->config->destroy(this->config); + chunk_clear(&this->encr_r); + chunk_clear(&this->integ_r); free(this); } @@ -1414,6 +1729,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, .get_config = _get_config, .get_state = _get_state, .set_state = _set_state, + .get_outbound_state = _get_outbound_state, .get_spi = _get_spi, .get_cpi = _get_cpi, .get_protocol = _get_protocol, @@ -1436,8 +1752,14 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, .alloc_spi = _alloc_spi, .alloc_cpi = _alloc_cpi, .install = _install, + .register_outbound = _register_outbound, + .install_outbound = _install_outbound, + .remove_outbound = _remove_outbound, + .set_rekey_spi = _set_rekey_spi, + .get_rekey_spi = _get_rekey_spi, .update = _update, - .add_policies = _add_policies, + .set_policies = _set_policies, + .install_policies = _install_policies, .create_ts_enumerator = _create_ts_enumerator, .create_policy_enumerator = _create_policy_enumerator, .destroy = _destroy, @@ -1456,7 +1778,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, .mark_in = config->get_mark(config, TRUE), .mark_out = config->get_mark(config, FALSE), .install_time = time_monotonic(NULL), - .policies_fwd_out = config->install_fwd_out_policy(config), + .policies_fwd_out = config->has_option(config, OPT_FWD_OUT_POLICIES), ); this->config = config; @@ -1509,7 +1831,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, /* MIPv6 proxy transport mode sets SA endpoints to TS hosts */ if (config->get_mode(config) == MODE_TRANSPORT && - config->use_proxy_mode(config)) + config->has_option(config, OPT_PROXY_MODE)) { this->mode = MODE_TRANSPORT; diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h index bc7df996a..b9a913da1 100644 --- a/src/libcharon/sa/child_sa.h +++ b/src/libcharon/sa/child_sa.h @@ -1,8 +1,8 @@ /* - * Copyright (C) 2006-2008 Tobias Brunner + * Copyright (C) 2006-2017 Tobias Brunner * Copyright (C) 2006-2008 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger - * 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 @@ -24,6 +24,7 @@ #define CHILD_SA_H_ typedef enum child_sa_state_t child_sa_state_t; +typedef enum child_sa_outbound_state_t child_sa_outbound_state_t; typedef struct child_sa_t child_sa_t; #include <library.h> @@ -53,7 +54,7 @@ enum child_sa_state_t { CHILD_INSTALLING, /** - * Installed an in-use CHILD_SA + * Installed both SAs of a CHILD_SA */ CHILD_INSTALLED, @@ -94,6 +95,32 @@ enum child_sa_state_t { extern enum_name_t *child_sa_state_names; /** + * States of the outbound SA of a CHILD_SA + */ +enum child_sa_outbound_state_t { + + /** + * Outbound SA is not installed + */ + CHILD_OUTBOUND_NONE, + + /** + * Data for the outbound SA has been registered, but not installed yet + */ + CHILD_OUTBOUND_REGISTERED, + + /** + * The outbound SA is currently installed + */ + CHILD_OUTBOUND_INSTALLED, +}; + +/** + * enum strings for child_sa_outbound_state_t. + */ +extern enum_name_t *child_sa_outbound_state_names; + +/** * Represents an IPsec SAs between two hosts. * * A child_sa_t contains two SAs. SAs for both @@ -152,7 +179,14 @@ struct child_sa_t { * * @return CHILD_SA state */ - child_sa_state_t (*get_state) (child_sa_t *this); + child_sa_state_t (*get_state)(child_sa_t *this); + + /** + * Get the state of the outbound SA. + * + * @return outbound SA state + */ + child_sa_outbound_state_t (*get_outbound_state)(child_sa_t *this); /** * Set the state of the CHILD_SA. @@ -347,6 +381,8 @@ struct child_sa_t { /** * Install an IPsec SA for one direction. * + * set_policies() should be called before calling this. + * * @param encr encryption key, if any * @param integ integrity key * @param spi SPI to use, allocated for inbound @@ -354,26 +390,84 @@ struct child_sa_t { * @param initiator TRUE if initiator of exchange resulting in this SA * @param inbound TRUE to install an inbound SA, FALSE for outbound * @param tfcv3 TRUE if peer supports ESPv3 TFC - * @param my_ts negotiated local traffic selector list - * @param other_ts negotiated remote traffic selector list * @return SUCCESS or FAILED */ status_t (*install)(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); + bool initiator, bool inbound, bool tfcv3); + + /** + * Register data for the installation of an outbound SA as responder during + * a rekeying. + * + * The SA is not installed until install_outbound() is called. + * + * @param encr encryption key, if any (cloned) + * @param integ integrity key (cloned) + * @param spi SPI to use, allocated for inbound + * @param cpi CPI to use, allocated for outbound + * @param tfcv3 TRUE if peer supports ESPv3 TFC + */ + void (*register_outbound)(child_sa_t *this, chunk_t encr, chunk_t integ, + uint32_t spi, uint16_t cpi, bool tfcv3); + + /** + * Install the outbound SA and the outbound policies as responder during a + * rekeying. + * + * @return SUCCESS or FAILED + */ + status_t (*install_outbound)(child_sa_t *this); + + /** + * Remove the outbound SA and the outbound policies after a rekeying. + */ + void (*remove_outbound)(child_sa_t *this); + /** - * Install the policies using some traffic selectors. + * Configure the policies using some traffic selectors. * * Supplied lists of traffic_selector_t's specify the policies * to use for this child sa. * - * @param my_ts traffic selectors for local site - * @param other_ts traffic selectors for remote site + * Install the policies by calling install_policies(). + * + * This should be called before calling install() so the traffic selectors + * may be passed to the kernel interface when installing the SAs. + * + * @param my_ts traffic selectors for local site (cloned) + * @param other_ts traffic selectors for remote site (cloned) + */ + void (*set_policies)(child_sa_t *this, linked_list_t *my_ts_list, + linked_list_t *other_ts_list); + + /** + * Install the configured policies. + * + * If register_outbound() was called previously this only installs the + * inbound and forward policies, the outbound policies are installed when + * install_outbound() is called. + * * @return SUCCESS or FAILED */ - status_t (*add_policies)(child_sa_t *this, linked_list_t *my_ts_list, - linked_list_t *other_ts_list); + status_t (*install_policies)(child_sa_t *this); + + /** + * Set the outbound SPI of the CHILD_SA that replaced this CHILD_SA during + * a rekeying. + * + * @param spi outbound SPI of the CHILD_SA that replaced this CHILD_SA + */ + void (*set_rekey_spi)(child_sa_t *this, uint32_t spi); + + /** + * Get the outbound SPI of the CHILD_SA that replaced this CHILD_SA during + * a rekeying. + * + * @return outbound SPI of the CHILD_SA that replaced this CHILD_SA + */ + uint32_t (*get_rekey_spi)(child_sa_t *this); + /** * Update hosts and ecapulation mode in the kernel SAs and policies. * diff --git a/src/libcharon/sa/eap/eap_manager.c b/src/libcharon/sa/eap/eap_manager.c index e4fcbc8f0..b2a57ccfb 100644 --- a/src/libcharon/sa/eap/eap_manager.c +++ b/src/libcharon/sa/eap/eap_manager.c @@ -105,31 +105,38 @@ METHOD(eap_manager_t, remove_method, void, this->lock->unlock(this->lock); } -/** - * filter the registered methods - */ -static bool filter_methods(uintptr_t role, eap_entry_t **entry, - eap_type_t *type, void *in, uint32_t *vendor) +CALLBACK(filter_methods, bool, + uintptr_t role, enumerator_t *orig, va_list args) { - if ((*entry)->role != (eap_role_t)role) - { - return FALSE; - } - if ((*entry)->vendor == 0 && - ((*entry)->type < 4 || (*entry)->type == EAP_EXPANDED || - (*entry)->type > EAP_EXPERIMENTAL)) - { /* filter invalid types */ - return FALSE; - } - if (type) - { - *type = (*entry)->type; - } - if (vendor) + eap_entry_t *entry; + eap_type_t *type; + uint32_t *vendor; + + VA_ARGS_VGET(args, type, vendor); + + while (orig->enumerate(orig, &entry)) { - *vendor = (*entry)->vendor; + if (entry->role != (eap_role_t)role) + { + continue; + } + if (entry->vendor == 0 && + (entry->type < 4 || entry->type == EAP_EXPANDED || + entry->type > EAP_EXPERIMENTAL)) + { /* filter invalid types */ + continue; + } + if (type) + { + *type = entry->type; + } + if (vendor) + { + *vendor = entry->vendor; + } + return TRUE; } - return TRUE; + return FALSE; } METHOD(eap_manager_t, create_enumerator, enumerator_t*, @@ -139,7 +146,7 @@ METHOD(eap_manager_t, create_enumerator, enumerator_t*, return enumerator_create_cleaner( enumerator_create_filter( this->methods->create_enumerator(this->methods), - (void*)filter_methods, (void*)(uintptr_t)role, NULL), + filter_methods, (void*)(uintptr_t)role, NULL), (void*)this->lock->unlock, this->lock); } diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 76e10691f..045858792 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1200,12 +1200,20 @@ METHOD(ike_sa_t, generate_message, status_t, return status; } -static bool filter_fragments(private_ike_sa_t *this, packet_t **fragment, - packet_t **packet) +CALLBACK(filter_fragments, bool, + private_ike_sa_t *this, enumerator_t *orig, va_list args) { - *packet = (*fragment)->clone(*fragment); - set_dscp(this, *packet); - return TRUE; + packet_t *fragment, **packet; + + VA_ARGS_VGET(args, packet); + + if (orig->enumerate(orig, &fragment)) + { + *packet = fragment->clone(fragment); + set_dscp(this, *packet); + return TRUE; + } + return FALSE; } METHOD(ike_sa_t, generate_message_fragmented, status_t, @@ -1265,7 +1273,7 @@ METHOD(ike_sa_t, generate_message_fragmented, status_t, { charon->bus->message(charon->bus, message, FALSE, FALSE); } - *packets = enumerator_create_filter(fragments, (void*)filter_fragments, + *packets = enumerator_create_filter(fragments, filter_fragments, this, NULL); } return status; @@ -1699,8 +1707,11 @@ typedef struct { } child_enumerator_t; METHOD(enumerator_t, child_enumerate, bool, - child_enumerator_t *this, child_sa_t **child_sa) + child_enumerator_t *this, va_list args) { + child_sa_t **child_sa; + + VA_ARGS_VGET(args, child_sa); if (this->inner->enumerate(this->inner, &this->current)) { *child_sa = this->current; @@ -1723,7 +1734,8 @@ METHOD(ike_sa_t, create_child_sa_enumerator, enumerator_t*, INIT(enumerator, .public = { - .enumerate = (void*)_child_enumerate, + .enumerate = enumerator_enumerate_default, + .venumerate = _child_enumerate, .destroy = _child_enumerator_destroy, }, .inner = array_create_enumerator(this->child_sas), @@ -2619,24 +2631,31 @@ METHOD(ike_sa_t, add_configuration_attribute, void, array_insert(this->attributes, ARRAY_TAIL, &entry); } -/** - * Enumerator filter for attributes - */ -static bool filter_attribute(void *null, attribute_entry_t **in, - configuration_attribute_type_t *type, void *in2, - chunk_t *data, void *in3, bool *handled) +CALLBACK(filter_attribute, bool, + void *null, enumerator_t *orig, va_list args) { - *type = (*in)->type; - *data = (*in)->data; - *handled = (*in)->handler != NULL; - return TRUE; + attribute_entry_t *entry; + configuration_attribute_type_t *type; + chunk_t *data; + bool *handled; + + VA_ARGS_VGET(args, type, data, handled); + + if (orig->enumerate(orig, &entry)) + { + *type = entry->type; + *data = entry->data; + *handled = entry->handler != NULL; + return TRUE; + } + return FALSE; } METHOD(ike_sa_t, create_attribute_enumerator, enumerator_t*, private_ike_sa_t *this) { return enumerator_create_filter(array_create_enumerator(this->attributes), - (void*)filter_attribute, NULL, NULL); + filter_attribute, NULL, NULL); } METHOD(ike_sa_t, create_task_enumerator, enumerator_t*, diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index 6bd49a086..c0bfebb83 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -151,8 +151,10 @@ static entry_t *entry_create() /** * Function that matches entry_t objects by ike_sa_id_t. */ -static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id) +static bool entry_match_by_id(entry_t *entry, void *arg) { + ike_sa_id_t *id = arg; + if (id->equals(id, entry->ike_sa_id)) { return TRUE; @@ -172,7 +174,7 @@ static bool entry_match_by_id(entry_t *entry, ike_sa_id_t *id) /** * Function that matches entry_t objects by ike_sa_t pointers. */ -static bool entry_match_by_sa(entry_t *entry, ike_sa_t *ike_sa) +static bool entry_match_by_sa(entry_t *entry, void *ike_sa) { return entry->ike_sa == ike_sa; } @@ -276,9 +278,6 @@ typedef struct segment_t segment_t; struct segment_t { /** mutex to access a segment exclusively */ mutex_t *mutex; - - /** the number of entries in this segment */ - u_int count; }; typedef struct shareable_segment_t shareable_segment_t; @@ -371,6 +370,11 @@ struct private_ike_sa_manager_t { refcount_t half_open_count_responder; /** + * Total number of IKE_SAs registered with IKE_SA manager. + */ + refcount_t total_sa_count; + + /** * Hash table with connected_peers_t objects. */ table_item_t **connected_peers_table; @@ -511,8 +515,13 @@ struct private_enumerator_t { }; METHOD(enumerator_t, enumerate, bool, - private_enumerator_t *this, entry_t **entry, u_int *segment) + private_enumerator_t *this, va_list args) { + entry_t **entry; + u_int *segment; + + VA_ARGS_VGET(args, entry, segment); + if (this->entry) { this->entry->condvar->signal(this->entry->condvar); @@ -570,7 +579,8 @@ static enumerator_t* create_table_enumerator(private_ike_sa_manager_t *this) INIT(enumerator, .enumerator = { - .enumerate = (void*)_enumerate, + .enumerate = enumerator_enumerate_default, + .venumerate = _enumerate, .destroy = _enumerator_destroy, }, .manager = this, @@ -601,7 +611,7 @@ static u_int put_entry(private_ike_sa_manager_t *this, entry_t *entry) item->next = current; } this->ike_sa_table[row] = item; - this->segments[segment].count++; + ref_get(&this->total_sa_count); return segment; } @@ -612,10 +622,9 @@ static u_int put_entry(private_ike_sa_manager_t *this, entry_t *entry) static void remove_entry(private_ike_sa_manager_t *this, entry_t *entry) { table_item_t *item, *prev = NULL; - u_int row, segment; + u_int row; row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask; - segment = row & this->segment_mask; item = this->ike_sa_table[row]; while (item) { @@ -629,7 +638,7 @@ static void remove_entry(private_ike_sa_manager_t *this, entry_t *entry) { this->ike_sa_table[row] = item->next; } - this->segments[segment].count--; + ignore_result(ref_put(&this->total_sa_count)); free(item); break; } @@ -648,7 +657,7 @@ static void remove_entry_at(private_enumerator_t *this) { table_item_t *current = this->current; - this->manager->segments[this->segment].count--; + ignore_result(ref_put(&this->manager->total_sa_count)); this->current = this->prev; if (this->prev) @@ -670,7 +679,7 @@ static void remove_entry_at(private_enumerator_t *this) */ static status_t get_entry_by_match_function(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, entry_t **entry, u_int *segment, - linked_list_match_t match, void *param) + bool (*match)(entry_t*,void*), void *param) { table_item_t *item; u_int row, seg; @@ -703,7 +712,7 @@ static status_t get_entry_by_id(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, entry_t **entry, u_int *segment) { return get_entry_by_match_function(this, ike_sa_id, entry, segment, - (linked_list_match_t)entry_match_by_id, ike_sa_id); + entry_match_by_id, ike_sa_id); } /** @@ -714,7 +723,7 @@ static status_t get_entry_by_sa(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id, ike_sa_t *ike_sa, entry_t **entry, u_int *segment) { return get_entry_by_match_function(this, ike_sa_id, entry, segment, - (linked_list_match_t)entry_match_by_sa, ike_sa); + entry_match_by_sa, ike_sa); } /** @@ -851,6 +860,15 @@ static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry) lock->unlock(lock); } +CALLBACK(id_matches, bool, + ike_sa_id_t *a, va_list args) +{ + ike_sa_id_t *b; + + VA_ARGS_VGET(args, b); + return a->equals(a, b); +} + /** * Put an SA between two peers into the hash table. */ @@ -879,8 +897,7 @@ static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry) entry->other_id, family)) { if (connected_peers->sas->find_first(connected_peers->sas, - (linked_list_match_t)entry->ike_sa_id->equals, - NULL, entry->ike_sa_id) == SUCCESS) + id_matches, NULL, entry->ike_sa_id)) { lock->unlock(lock); return; @@ -1555,42 +1572,52 @@ METHOD(ike_sa_manager_t, checkout_by_name, ike_sa_t*, return ike_sa; } -/** - * enumerator filter function, waiting variant - */ -static bool enumerator_filter_wait(private_ike_sa_manager_t *this, - entry_t **in, ike_sa_t **out, u_int *segment) +CALLBACK(enumerator_filter_wait, bool, + private_ike_sa_manager_t *this, enumerator_t *orig, va_list args) { - if (wait_for_entry(this, *in, *segment)) + entry_t *entry; + u_int segment; + ike_sa_t **out; + + VA_ARGS_VGET(args, out); + + while (orig->enumerate(orig, &entry, &segment)) { - *out = (*in)->ike_sa; - charon->bus->set_sa(charon->bus, *out); - return TRUE; + if (wait_for_entry(this, entry, segment)) + { + *out = entry->ike_sa; + charon->bus->set_sa(charon->bus, *out); + return TRUE; + } } return FALSE; } -/** - * enumerator filter function, skipping variant - */ -static bool enumerator_filter_skip(private_ike_sa_manager_t *this, - entry_t **in, ike_sa_t **out, u_int *segment) +CALLBACK(enumerator_filter_skip, bool, + private_ike_sa_manager_t *this, enumerator_t *orig, va_list args) { - if (!(*in)->driveout_new_threads && - !(*in)->driveout_waiting_threads && - !(*in)->checked_out) + entry_t *entry; + u_int segment; + ike_sa_t **out; + + VA_ARGS_VGET(args, out); + + while (orig->enumerate(orig, &entry, &segment)) { - *out = (*in)->ike_sa; - charon->bus->set_sa(charon->bus, *out); - return TRUE; + if (!entry->driveout_new_threads && + !entry->driveout_waiting_threads && + !entry->checked_out) + { + *out = entry->ike_sa; + charon->bus->set_sa(charon->bus, *out); + return TRUE; + } } return FALSE; } -/** - * Reset threads SA after enumeration - */ -static void reset_sa(void *data) +CALLBACK(reset_sa, void, + void *data) { charon->bus->set_sa(charon->bus, NULL); } @@ -2034,17 +2061,7 @@ METHOD(ike_sa_manager_t, has_contact, bool, METHOD(ike_sa_manager_t, get_count, u_int, private_ike_sa_manager_t *this) { - u_int segment, count = 0; - mutex_t *mutex; - - for (segment = 0; segment < this->segment_count; segment++) - { - mutex = this->segments[segment & this->segment_mask].mutex; - mutex->lock(mutex); - count += this->segments[segment].count; - mutex->unlock(mutex); - } - return count; + return (u_int)ref_cur(&this->total_sa_count); } METHOD(ike_sa_manager_t, get_half_open_count, u_int, diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 1da17ee50..48ec3e7f5 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -210,6 +210,16 @@ struct private_task_manager_t { double retransmit_base; /** + * Jitter to apply to calculated retransmit timeout (in percent) + */ + u_int retransmit_jitter; + + /** + * Limit retransmit timeout to this value + */ + uint32_t retransmit_limit; + + /** * Sequence number for sending DPD requests */ uint32_t dpd_send; @@ -345,7 +355,7 @@ 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; - uint32_t t; + uint32_t t, max_jitter; array_get(packets, 0, &packet); if (retransmitted > this->retransmit_tries) @@ -356,6 +366,15 @@ static status_t retransmit_packet(private_task_manager_t *this, uint32_t seqnr, } t = (uint32_t)(this->retransmit_timeout * 1000.0 * pow(this->retransmit_base, retransmitted)); + if (this->retransmit_limit) + { + t = min(t, this->retransmit_limit); + } + if (this->retransmit_jitter) + { + max_jitter = (t / 100.0) * this->retransmit_jitter; + t -= max_jitter * (random() / (RAND_MAX + 1.0)); + } if (retransmitted) { DBG1(DBG_IKE, "sending retransmit %u of %s message ID %u, seq %u", @@ -2034,11 +2053,15 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa) .active_tasks = linked_list_create(), .passive_tasks = linked_list_create(), .retransmit_tries = lib->settings->get_int(lib->settings, - "%s.retransmit_tries", RETRANSMIT_TRIES, lib->ns), + "%s.retransmit_tries", RETRANSMIT_TRIES, lib->ns), .retransmit_timeout = lib->settings->get_double(lib->settings, - "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns), + "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns), .retransmit_base = lib->settings->get_double(lib->settings, - "%s.retransmit_base", RETRANSMIT_BASE, lib->ns), + "%s.retransmit_base", RETRANSMIT_BASE, lib->ns), + .retransmit_jitter = min(lib->settings->get_int(lib->settings, + "%s.retransmit_jitter", 0, lib->ns), RETRANSMIT_JITTER_MAX), + .retransmit_limit = lib->settings->get_int(lib->settings, + "%s.retransmit_limit", 0, lib->ns) * 1000, ); if (!this->rng) diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index bbb885850..8be82ebe2 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -325,6 +325,17 @@ static bool install(private_quick_mode_t *this) return FALSE; } + if (this->initiator) + { + this->child_sa->set_policies(this->child_sa, tsi, tsr); + } + else + { + this->child_sa->set_policies(this->child_sa, tsr, tsi); + } + tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); + tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); + if (this->keymat->derive_child_keys(this->keymat, this->proposal, this->dh, this->spi_i, this->spi_r, this->nonce_i, this->nonce_r, &encr_i, &integ_i, &encr_r, &integ_r)) @@ -333,19 +344,19 @@ static bool install(private_quick_mode_t *this) { status_i = this->child_sa->install(this->child_sa, encr_r, integ_r, this->spi_i, this->cpi_i, - this->initiator, TRUE, FALSE, tsi, tsr); + this->initiator, TRUE, FALSE); status_o = this->child_sa->install(this->child_sa, encr_i, integ_i, this->spi_r, this->cpi_r, - this->initiator, FALSE, FALSE, tsi, tsr); + this->initiator, FALSE, FALSE); } else { status_i = this->child_sa->install(this->child_sa, encr_i, integ_i, this->spi_r, this->cpi_r, - this->initiator, TRUE, FALSE, tsr, tsi); + this->initiator, TRUE, FALSE); status_o = this->child_sa->install(this->child_sa, encr_r, integ_r, this->spi_i, this->cpi_i, - this->initiator, FALSE, FALSE, tsr, tsi); + this->initiator, FALSE, FALSE); } } @@ -355,22 +366,12 @@ static bool install(private_quick_mode_t *this) (status_i != SUCCESS) ? "inbound " : "", (status_i != SUCCESS && status_o != SUCCESS) ? "and ": "", (status_o != SUCCESS) ? "outbound " : ""); - tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); - tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); status = FAILED; } else { - if (this->initiator) - { - status = this->child_sa->add_policies(this->child_sa, tsi, tsr); - } - else - { - status = this->child_sa->add_policies(this->child_sa, tsr, tsi); - } - tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy)); - tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy)); + status = this->child_sa->install_policies(this->child_sa); + if (status != SUCCESS) { DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); @@ -853,7 +854,7 @@ METHOD(task_t, build_i, status_t, add_nat_oa_payloads(this, message); } - if (this->config->use_ipcomp(this->config)) + if (this->config->has_option(this->config, OPT_IPCOMP)) { this->cpi_i = this->child_sa->alloc_cpi(this->child_sa); if (!this->cpi_i) @@ -1108,7 +1109,7 @@ METHOD(task_t, process_r, status_t, return send_notify(this, INVALID_ID_INFORMATION); } - if (this->config->use_ipcomp(this->config)) + if (this->config->has_option(this->config, OPT_IPCOMP)) { list = sa_payload->get_ipcomp_proposals(sa_payload, &this->cpi_i); diff --git a/src/libcharon/sa/ikev2/connect_manager.c b/src/libcharon/sa/ikev2/connect_manager.c index 280796d8c..35856788c 100644 --- a/src/libcharon/sa/ikev2/connect_manager.c +++ b/src/libcharon/sa/ikev2/connect_manager.c @@ -450,22 +450,21 @@ static initiate_data_t *initiate_data_create(check_list_t *checklist, return this; } -/** - * Find an initiated connection by the peers' ids - */ -static bool match_initiated_by_ids(initiated_t *current, identification_t *id, - identification_t *peer_id) +CALLBACK(match_initiated_by_ids, bool, + initiated_t *current, va_list args) { + identification_t *id, *peer_id; + + VA_ARGS_VGET(args, id, peer_id); return id->equals(id, current->id) && peer_id->equals(peer_id, current->peer_id); } -static status_t get_initiated_by_ids(private_connect_manager_t *this, - identification_t *id, - identification_t *peer_id, - initiated_t **initiated) +static bool get_initiated_by_ids(private_connect_manager_t *this, + identification_t *id, + identification_t *peer_id, + initiated_t **initiated) { - return this->initiated->find_first(this->initiated, - (linked_list_match_t)match_initiated_by_ids, + return this->initiated->find_first(this->initiated, match_initiated_by_ids, (void**)initiated, id, peer_id); } @@ -490,21 +489,20 @@ static void remove_initiated(private_connect_manager_t *this, enumerator->destroy(enumerator); } -/** - * Find the checklist with a specific connect ID - */ -static bool match_checklist_by_id(check_list_t *current, chunk_t *connect_id) +CALLBACK(match_checklist_by_id, bool, + check_list_t *current, va_list args) { - return chunk_equals(*connect_id, current->connect_id); + chunk_t connect_id; + + VA_ARGS_VGET(args, connect_id); + return chunk_equals(connect_id, current->connect_id); } -static status_t get_checklist_by_id(private_connect_manager_t *this, - chunk_t connect_id, - check_list_t **check_list) +static bool get_checklist_by_id(private_connect_manager_t *this, + chunk_t connect_id, check_list_t **check_list) { - return this->checklists->find_first(this->checklists, - (linked_list_match_t)match_checklist_by_id, - (void**)check_list, &connect_id); + return this->checklists->find_first(this->checklists, match_checklist_by_id, + (void**)check_list, connect_id); } /** @@ -528,19 +526,19 @@ static void remove_checklist(private_connect_manager_t *this, enumerator->destroy(enumerator); } -/** - * Checks if a list of endpoint_notify_t contains a certain host_t - */ -static bool match_endpoint_by_host(endpoint_notify_t *current, host_t *host) +CALLBACK(match_endpoint_by_host, bool, + endpoint_notify_t *current, va_list args) { + host_t *host; + + VA_ARGS_VGET(args, host); return host->equals(host, current->get_host(current)); } -static status_t endpoints_contain(linked_list_t *endpoints, host_t *host, +static bool endpoints_contain(linked_list_t *endpoints, host_t *host, endpoint_notify_t **endpoint) { - return endpoints->find_first(endpoints, - (linked_list_match_t)match_endpoint_by_host, + return endpoints->find_first(endpoints, match_endpoint_by_host, (void**)endpoint, host); } @@ -560,39 +558,44 @@ static void insert_pair_by_priority(linked_list_t *pairs, endpoint_pair_t *pair) enumerator->destroy(enumerator); } -/** - * Searches a list of endpoint_pair_t for a pair with specific host_ts - */ -static bool match_pair_by_hosts(endpoint_pair_t *current, host_t *local, - host_t *remote) +CALLBACK(match_pair_by_hosts, bool, + endpoint_pair_t *current, va_list args) { - return local->equals(local, current->local) && remote->equals(remote, current->remote); + host_t *local, *remote; + + VA_ARGS_VGET(args, local, remote); + return local->equals(local, current->local) && + remote->equals(remote, current->remote); } -static status_t get_pair_by_hosts(linked_list_t *pairs, host_t *local, - host_t *remote, endpoint_pair_t **pair) +static bool get_pair_by_hosts(linked_list_t *pairs, host_t *local, + host_t *remote, endpoint_pair_t **pair) { - return pairs->find_first(pairs, (linked_list_match_t)match_pair_by_hosts, - (void**)pair, local, remote); + return pairs->find_first(pairs, match_pair_by_hosts, (void**)pair, local, + remote); } -static bool match_pair_by_id(endpoint_pair_t *current, uint32_t *id) +CALLBACK(match_pair_by_id, bool, + endpoint_pair_t *current, va_list args) { - return current->id == *id; + uint32_t id; + + VA_ARGS_VGET(args, id); + return current->id == id; } /** * Searches for a pair with a specific id */ -static status_t get_pair_by_id(check_list_t *checklist, uint32_t id, - endpoint_pair_t **pair) +static bool get_pair_by_id(check_list_t *checklist, uint32_t id, + endpoint_pair_t **pair) { - return checklist->pairs->find_first(checklist->pairs, - (linked_list_match_t)match_pair_by_id, - (void**)pair, &id); + return checklist->pairs->find_first(checklist->pairs, match_pair_by_id, + (void**)pair, id); } -static bool match_succeeded_pair(endpoint_pair_t *current) +CALLBACK(match_succeeded_pair, bool, + endpoint_pair_t *current, va_list args) { return current->state == CHECK_SUCCEEDED; } @@ -600,15 +603,14 @@ static bool match_succeeded_pair(endpoint_pair_t *current) /** * Returns the best pair of state CHECK_SUCCEEDED from a checklist. */ -static status_t get_best_valid_pair(check_list_t *checklist, - endpoint_pair_t **pair) +static bool get_best_valid_pair(check_list_t *checklist, endpoint_pair_t **pair) { - return checklist->pairs->find_first(checklist->pairs, - (linked_list_match_t)match_succeeded_pair, - (void**)pair); + return checklist->pairs->find_first(checklist->pairs, match_succeeded_pair, + (void**)pair); } -static bool match_waiting_pair(endpoint_pair_t *current) +CALLBACK(match_waiting_pair, bool, + endpoint_pair_t *current, va_list args) { return current->state == CHECK_WAITING; } @@ -865,7 +867,7 @@ static job_requeue_t initiator_finish(callback_data_t *data) this->mutex->lock(this->mutex); check_list_t *checklist; - if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) + if (!get_checklist_by_id(this, data->connect_id, &checklist)) { DBG1(DBG_IKE, "checklist with id '%#B' not found, can't finish " "connectivity checks", &data->connect_id); @@ -953,7 +955,7 @@ static job_requeue_t retransmit(callback_data_t *data) this->mutex->lock(this->mutex); check_list_t *checklist; - if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) + if (!get_checklist_by_id(this, data->connect_id, &checklist)) { DBG1(DBG_IKE, "checklist with id '%#B' not found, can't retransmit " "connectivity check", &data->connect_id); @@ -962,7 +964,7 @@ static job_requeue_t retransmit(callback_data_t *data) } endpoint_pair_t *pair; - if (get_pair_by_id(checklist, data->mid, &pair) != SUCCESS) + if (!get_pair_by_id(checklist, data->mid, &pair)) { DBG1(DBG_IKE, "pair with id '%d' not found, can't retransmit " "connectivity check", data->mid); @@ -1108,7 +1110,7 @@ static job_requeue_t sender(callback_data_t *data) this->mutex->lock(this->mutex); check_list_t *checklist; - if (get_checklist_by_id(this, data->connect_id, &checklist) != SUCCESS) + if (!get_checklist_by_id(this, data->connect_id, &checklist)) { DBG1(DBG_IKE, "checklist with id '%#B' not found, can't send " "connectivity check", &data->connect_id); @@ -1124,9 +1126,8 @@ static job_requeue_t sender(callback_data_t *data) { DBG1(DBG_IKE, "no triggered check queued, sending an ordinary check"); - if (checklist->pairs->find_first(checklist->pairs, - (linked_list_match_t)match_waiting_pair, - (void**)&pair) != SUCCESS) + if (!checklist->pairs->find_first(checklist->pairs, match_waiting_pair, + (void**)&pair)) { this->mutex->unlock(this->mutex); DBG1(DBG_IKE, "no pairs in waiting state, aborting"); @@ -1182,7 +1183,7 @@ static job_requeue_t initiate_mediated(initiate_data_t *data) initiated_t *initiated = data->initiated; endpoint_pair_t *pair; - if (get_best_valid_pair(checklist, &pair) == SUCCESS) + if (get_best_valid_pair(checklist, &pair)) { ike_sa_id_t *waiting_sa; enumerator_t *enumerator = initiated->mediated->create_enumerator( @@ -1219,7 +1220,7 @@ static void finish_checks(private_connect_manager_t *this, check_list_t *checkli { initiated_t *initiated; if (get_initiated_by_ids(this, checklist->initiator.id, - checklist->responder.id, &initiated) == SUCCESS) + checklist->responder.id, &initiated)) { callback_job_t *job; @@ -1247,7 +1248,7 @@ static void process_response(private_connect_manager_t *this, check_t *check, check_list_t *checklist) { endpoint_pair_t *pair; - if (get_pair_by_id(checklist, check->mid, &pair) == SUCCESS) + if (get_pair_by_id(checklist, check->mid, &pair)) { if (pair->local->equals(pair->local, check->dst) && pair->remote->equals(pair->remote, check->src)) @@ -1261,9 +1262,9 @@ static void process_response(private_connect_manager_t *this, check_t *check, checklist->initiator.endpoints : checklist->responder.endpoints; endpoint_notify_t *local_endpoint; - if (endpoints_contain(local_endpoints, - check->endpoint->get_host(check->endpoint), - &local_endpoint) != SUCCESS) + if (!endpoints_contain(local_endpoints, + check->endpoint->get_host(check->endpoint), + &local_endpoint)) { local_endpoint = endpoint_notify_create_from_host(PEER_REFLEXIVE, check->endpoint->get_host(check->endpoint), pair->local); @@ -1302,15 +1303,14 @@ static void process_request(private_connect_manager_t *this, check_t *check, peer_reflexive->set_priority(peer_reflexive, check->endpoint->get_priority(check->endpoint)); - if (endpoints_contain(remote_endpoints, check->src, &remote_endpoint) != SUCCESS) + if (!endpoints_contain(remote_endpoints, check->src, &remote_endpoint)) { remote_endpoint = peer_reflexive->clone(peer_reflexive); remote_endpoints->insert_last(remote_endpoints, remote_endpoint); } endpoint_pair_t *pair; - if (get_pair_by_hosts(checklist->pairs, check->dst, check->src, - &pair) == SUCCESS) + if (get_pair_by_hosts(checklist->pairs, check->dst, check->src, &pair)) { switch(pair->state) { @@ -1389,7 +1389,7 @@ METHOD(connect_manager_t, process_check, void, this->mutex->lock(this->mutex); check_list_t *checklist; - if (get_checklist_by_id(this, check->connect_id, &checklist) != SUCCESS) + if (!get_checklist_by_id(this, check->connect_id, &checklist)) { DBG1(DBG_IKE, "checklist with id '%#B' not found", &check->connect_id); @@ -1423,6 +1423,15 @@ METHOD(connect_manager_t, process_check, void, check_destroy(check); } +CALLBACK(id_matches, bool, + ike_sa_id_t *a, va_list args) +{ + ike_sa_id_t *b; + + VA_ARGS_VGET(args, b); + return a->equals(a, b); +} + METHOD(connect_manager_t, check_and_register, bool, private_connect_manager_t *this, identification_t *id, identification_t *peer_id, ike_sa_id_t *mediated_sa) @@ -1432,7 +1441,7 @@ METHOD(connect_manager_t, check_and_register, bool, this->mutex->lock(this->mutex); - if (get_initiated_by_ids(this, id, peer_id, &initiated) != SUCCESS) + if (!get_initiated_by_ids(this, id, peer_id, &initiated)) { DBG2(DBG_IKE, "registered waiting mediated connection with '%Y'", peer_id); @@ -1441,9 +1450,8 @@ METHOD(connect_manager_t, check_and_register, bool, already_there = FALSE; } - if (initiated->mediated->find_first(initiated->mediated, - (linked_list_match_t)mediated_sa->equals, - NULL, mediated_sa) != SUCCESS) + if (!initiated->mediated->find_first(initiated->mediated, id_matches, + NULL, mediated_sa)) { initiated->mediated->insert_last(initiated->mediated, mediated_sa->clone(mediated_sa)); @@ -1462,7 +1470,7 @@ METHOD(connect_manager_t, check_and_initiate, void, this->mutex->lock(this->mutex); - if (get_initiated_by_ids(this, id, peer_id, &initiated) != SUCCESS) + if (!get_initiated_by_ids(this, id, peer_id, &initiated)) { DBG2(DBG_IKE, "no waiting mediated connections with '%Y'", peer_id); this->mutex->unlock(this->mutex); @@ -1492,7 +1500,7 @@ METHOD(connect_manager_t, set_initiator_data, status_t, this->mutex->lock(this->mutex); - if (get_checklist_by_id(this, connect_id, NULL) == SUCCESS) + if (get_checklist_by_id(this, connect_id, NULL)) { DBG1(DBG_IKE, "checklist with id '%#B' already exists, aborting", &connect_id); @@ -1517,7 +1525,7 @@ METHOD(connect_manager_t, set_responder_data, status_t, this->mutex->lock(this->mutex); - if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS) + if (!get_checklist_by_id(this, connect_id, &checklist)) { DBG1(DBG_IKE, "checklist with id '%#B' not found", &connect_id); @@ -1547,7 +1555,7 @@ METHOD(connect_manager_t, stop_checks, status_t, this->mutex->lock(this->mutex); - if (get_checklist_by_id(this, connect_id, &checklist) != SUCCESS) + if (!get_checklist_by_id(this, connect_id, &checklist)) { DBG1(DBG_IKE, "checklist with id '%#B' not found", &connect_id); diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index e4a16faf0..c2ddbc588 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -161,6 +161,16 @@ struct private_task_manager_t { double retransmit_base; /** + * Jitter to apply to calculated retransmit timeout (in percent) + */ + u_int retransmit_jitter; + + /** + * Limit retransmit timeout to this value + */ + uint32_t retransmit_limit; + + /** * Use make-before-break instead of break-before-make reauth? */ bool make_before_break; @@ -321,7 +331,7 @@ METHOD(task_manager_t, retransmit, status_t, if (message_id == this->initiating.mid && array_count(this->initiating.packets)) { - uint32_t timeout; + uint32_t timeout, max_jitter; job_t *job; enumerator_t *enumerator; packet_t *packet; @@ -351,6 +361,16 @@ METHOD(task_manager_t, retransmit, status_t, { timeout = (uint32_t)(this->retransmit_timeout * 1000.0 * pow(this->retransmit_base, this->initiating.retransmitted)); + + if (this->retransmit_limit) + { + timeout = min(timeout, this->retransmit_limit); + } + if (this->retransmit_jitter) + { + max_jitter = (timeout / 100.0) * this->retransmit_jitter; + timeout -= max_jitter * (random() / (RAND_MAX + 1.0)); + } } else { @@ -2059,13 +2079,20 @@ METHOD(task_manager_t, reset, void, this->reset = TRUE; } -/** - * Filter queued tasks - */ -static bool filter_queued(void *unused, queued_task_t **queued, task_t **task) +CALLBACK(filter_queued, bool, + void *unused, enumerator_t *orig, va_list args) { - *task = (*queued)->task; - return TRUE; + queued_task_t *queued; + task_t **task; + + VA_ARGS_VGET(args, task); + + if (orig->enumerate(orig, &queued)) + { + *task = queued->task; + return TRUE; + } + return FALSE; } METHOD(task_manager_t, create_task_enumerator, enumerator_t*, @@ -2080,7 +2107,7 @@ METHOD(task_manager_t, create_task_enumerator, enumerator_t*, case TASK_QUEUE_QUEUED: return enumerator_create_filter( array_create_enumerator(this->queued_tasks), - (void*)filter_queued, NULL, NULL); + filter_queued, NULL, NULL); default: return enumerator_create_empty(); } @@ -2151,6 +2178,10 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa) "%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns), .retransmit_base = lib->settings->get_double(lib->settings, "%s.retransmit_base", RETRANSMIT_BASE, lib->ns), + .retransmit_jitter = min(lib->settings->get_int(lib->settings, + "%s.retransmit_jitter", 0, lib->ns), RETRANSMIT_JITTER_MAX), + .retransmit_limit = lib->settings->get_int(lib->settings, + "%s.retransmit_limit", 0, lib->ns) * 1000, .make_before_break = lib->settings->get_bool(lib->settings, "%s.make_before_break", FALSE, lib->ns), ); diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 71cb6b8ea..896cabb2b 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2016 Tobias Brunner + * Copyright (C) 2008-2017 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -602,7 +602,7 @@ static status_t select_and_install(private_child_create_t *this, switch (this->mode) { case MODE_TRANSPORT: - if (!this->config->use_proxy_mode(this->config) && + if (!this->config->has_option(this->config, OPT_PROXY_MODE) && (!ts_list_is_host(this->tsi, other) || !ts_list_is_host(this->tsr, me)) ) @@ -630,6 +630,32 @@ static status_t select_and_install(private_child_create_t *this, default: break; } + /* use a copy of the traffic selectors, as the POST hook should not + * change payloads */ + my_ts = this->tsr->clone_offset(this->tsr, + offsetof(traffic_selector_t, clone)); + other_ts = this->tsi->clone_offset(this->tsi, + offsetof(traffic_selector_t, clone)); + charon->bus->narrow(charon->bus, this->child_sa, + NARROW_RESPONDER_POST, my_ts, other_ts); + + if (my_ts->get_count(my_ts) == 0 || other_ts->get_count(other_ts) == 0) + { + my_ts->destroy_offset(my_ts, + offsetof(traffic_selector_t, destroy)); + other_ts->destroy_offset(other_ts, + offsetof(traffic_selector_t, destroy)); + return NOT_FOUND; + } + } + + this->child_sa->set_policies(this->child_sa, my_ts, other_ts); + if (!this->initiator) + { + my_ts->destroy_offset(my_ts, + offsetof(traffic_selector_t, destroy)); + other_ts->destroy_offset(other_ts, + offsetof(traffic_selector_t, destroy)); } this->child_sa->set_state(this->child_sa, CHILD_INSTALLING); @@ -651,19 +677,30 @@ static status_t select_and_install(private_child_create_t *this, { status_i = this->child_sa->install(this->child_sa, encr_r, integ_r, this->my_spi, this->my_cpi, this->initiator, - TRUE, this->tfcv3, my_ts, other_ts); + TRUE, this->tfcv3); status_o = this->child_sa->install(this->child_sa, encr_i, integ_i, this->other_spi, this->other_cpi, this->initiator, - FALSE, this->tfcv3, my_ts, other_ts); + FALSE, this->tfcv3); } - else + else if (!this->rekey) { status_i = this->child_sa->install(this->child_sa, encr_i, integ_i, this->my_spi, this->my_cpi, this->initiator, - TRUE, this->tfcv3, my_ts, other_ts); + TRUE, this->tfcv3); status_o = this->child_sa->install(this->child_sa, encr_r, integ_r, this->other_spi, this->other_cpi, this->initiator, - FALSE, this->tfcv3, my_ts, other_ts); + FALSE, this->tfcv3); + } + else + { /* as responder during a rekeying we only install the inbound + * SA now, the outbound SA and policies are installed when we + * receive the delete for the old SA */ + status_i = this->child_sa->install(this->child_sa, encr_i, integ_i, + this->my_spi, this->my_cpi, this->initiator, + TRUE, this->tfcv3); + this->child_sa->register_outbound(this->child_sa, encr_r, integ_r, + this->other_spi, this->other_cpi, this->tfcv3); + status_o = SUCCESS; } } @@ -679,36 +716,8 @@ static status_t select_and_install(private_child_create_t *this, } else { - if (this->initiator) - { - status = this->child_sa->add_policies(this->child_sa, - my_ts, other_ts); - } - else - { - /* use a copy of the traffic selectors, as the POST hook should not - * change payloads */ - my_ts = this->tsr->clone_offset(this->tsr, - offsetof(traffic_selector_t, clone)); - other_ts = this->tsi->clone_offset(this->tsi, - offsetof(traffic_selector_t, clone)); - charon->bus->narrow(charon->bus, this->child_sa, - NARROW_RESPONDER_POST, my_ts, other_ts); - if (my_ts->get_count(my_ts) == 0 || - other_ts->get_count(other_ts) == 0) - { - status = FAILED; - } - else - { - status = this->child_sa->add_policies(this->child_sa, - my_ts, other_ts); - } - my_ts->destroy_offset(my_ts, - offsetof(traffic_selector_t, destroy)); - other_ts->destroy_offset(other_ts, - offsetof(traffic_selector_t, destroy)); - } + status = this->child_sa->install_policies(this->child_sa); + if (status != SUCCESS) { DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); @@ -736,7 +745,6 @@ static status_t select_and_install(private_child_create_t *this, charon->bus->child_keys(charon->bus, this->child_sa, this->initiator, this->dh, nonce_i, nonce_r); - /* add to IKE_SA, and remove from task */ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); this->established = TRUE; @@ -748,16 +756,17 @@ static status_t select_and_install(private_child_create_t *this, other_ts = linked_list_create_from_enumerator( this->child_sa->create_ts_enumerator(this->child_sa, FALSE)); - DBG0(DBG_IKE, "CHILD_SA %s{%d} established " + DBG0(DBG_IKE, "%sCHILD_SA %s{%d} established " "with SPIs %.8x_i %.8x_o and TS %#R === %#R", + this->rekey && !this->initiator ? "inbound " : "", this->child_sa->get_name(this->child_sa), this->child_sa->get_unique_id(this->child_sa), ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), - ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), my_ts, other_ts); + ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), + my_ts, other_ts); my_ts->destroy(my_ts); other_ts->destroy(other_ts); - return SUCCESS; } @@ -1073,7 +1082,7 @@ METHOD(task_t, build_i, status_t, this->dh_group); } - if (this->config->use_ipcomp(this->config)) + if (this->config->has_option(this->config, OPT_IPCOMP)) { /* IPCOMP_DEFLATE is the only transform we support at the moment */ add_ipcomp_notify(this, message, IPCOMP_DEFLATE); @@ -1327,7 +1336,7 @@ METHOD(task_t, build_r, status_t, if (this->ipcomp_received != IPCOMP_NONE) { - if (this->config->use_ipcomp(this->config)) + if (this->config->has_option(this->config, OPT_IPCOMP)) { add_ipcomp_notify(this, message, this->ipcomp_received); } @@ -1690,7 +1699,6 @@ METHOD(task_t, destroy, void, { this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy)); } - DESTROY_IF(this->config); DESTROY_IF(this->nonceg); free(this); diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c index 6fa8836ac..626796383 100644 --- a/src/libcharon/sa/ikev2/tasks/child_delete.c +++ b/src/libcharon/sa/ikev2/tasks/child_delete.c @@ -18,9 +18,14 @@ #include <daemon.h> #include <encoding/payloads/delete_payload.h> +#include <processing/jobs/delete_child_sa_job.h> #include <sa/ikev2/tasks/child_create.h> #include <sa/ikev2/tasks/child_rekey.h> +#ifndef DELETE_REKEYED_DELAY +#define DELETE_REKEYED_DELAY 5 +#endif + typedef struct private_child_delete_t private_child_delete_t; /** @@ -39,67 +44,80 @@ struct private_child_delete_t { ike_sa_t *ike_sa; /** - * Are we the initiator? + * Whether we are the initiator of the exchange */ bool initiator; /** - * Protocol of CHILD_SA to delete + * Protocol of CHILD_SA to delete (as initiator) */ protocol_id_t protocol; /** - * Inbound SPI of CHILD_SA to delete + * Inbound SPI of CHILD_SA to delete (as initiator) */ uint32_t spi; /** - * whether to enforce delete action policy - */ - bool check_delete_action; - - /** - * is this delete exchange following a rekey? - */ - bool rekeyed; - - /** - * CHILD_SA already expired? + * CHILD_SA already expired (as initiator) */ bool expired; /** - * CHILD_SAs which get deleted + * CHILD_SAs which get deleted, entry_t* */ linked_list_t *child_sas; }; /** + * Information about a deleted CHILD_SA + */ +typedef struct { + /** Deleted CHILD_SA */ + child_sa_t *child_sa; + /** Whether the CHILD_SA was rekeyed */ + bool rekeyed; + /** Whether to enforce any delete action policy */ + bool check_delete_action; +} entry_t; + +CALLBACK(match_child, bool, + entry_t *entry, va_list args) +{ + child_sa_t *child_sa; + + VA_ARGS_VGET(args, child_sa); + return entry->child_sa == child_sa; +} + +/** * build the delete payloads from the listed child_sas */ static void build_payloads(private_child_delete_t *this, message_t *message) { delete_payload_t *ah = NULL, *esp = NULL; enumerator_t *enumerator; - child_sa_t *child_sa; + entry_t *entry; + protocol_id_t protocol; + uint32_t spi; enumerator = this->child_sas->create_enumerator(this->child_sas); - while (enumerator->enumerate(enumerator, (void**)&child_sa)) + while (enumerator->enumerate(enumerator, (void**)&entry)) { - protocol_id_t protocol = child_sa->get_protocol(child_sa); - uint32_t spi = child_sa->get_spi(child_sa, TRUE); + protocol = entry->child_sa->get_protocol(entry->child_sa); + spi = entry->child_sa->get_spi(entry->child_sa, TRUE); switch (protocol) { case PROTO_ESP: - if (esp == NULL) + if (!esp) { esp = delete_payload_create(PLV2_DELETE, PROTO_ESP); message->add_payload(message, (payload_t*)esp); } esp->add_spi(esp, spi); DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x", - protocol_id_names, protocol, ntohl(spi)); + protocol_id_names, protocol, ntohl(spi)); break; case PROTO_AH: if (ah == NULL) @@ -109,12 +127,12 @@ static void build_payloads(private_child_delete_t *this, message_t *message) } ah->add_spi(ah, spi); DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x", - protocol_id_names, protocol, ntohl(spi)); + protocol_id_names, protocol, ntohl(spi)); break; default: break; } - child_sa->set_state(child_sa, CHILD_DELETING); + entry->child_sa->set_state(entry->child_sa, CHILD_DELETING); } enumerator->destroy(enumerator); } @@ -147,6 +165,57 @@ static bool is_redundant(private_child_delete_t *this, child_sa_t *child) } /** + * Install the outbound CHILD_SA with the given SPI + */ +static void install_outbound(private_child_delete_t *this, + protocol_id_t protocol, uint32_t spi) +{ + child_sa_t *child_sa; + linked_list_t *my_ts, *other_ts; + status_t status; + + child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, + spi, FALSE); + if (!child_sa) + { + DBG1(DBG_IKE, "CHILD_SA not found after rekeying"); + return; + } + if (this->initiator && is_redundant(this, child_sa)) + { /* if we won the rekey collision we don't want to install the + * redundant SA created by the peer */ + return; + } + + status = child_sa->install_outbound(child_sa); + if (status != SUCCESS) + { + DBG1(DBG_IKE, "unable to install outbound IPsec SA (SAD) in kernel"); + charon->bus->alert(charon->bus, ALERT_INSTALL_CHILD_SA_FAILED, + child_sa); + /* FIXME: delete the new child_sa? */ + return; + } + child_sa->set_state(child_sa, CHILD_INSTALLED); + + my_ts = linked_list_create_from_enumerator( + child_sa->create_ts_enumerator(child_sa, TRUE)); + other_ts = linked_list_create_from_enumerator( + child_sa->create_ts_enumerator(child_sa, FALSE)); + + DBG0(DBG_IKE, "outbound CHILD_SA %s{%d} established " + "with SPIs %.8x_i %.8x_o and TS %#R === %#R", + child_sa->get_name(child_sa), + child_sa->get_unique_id(child_sa), + ntohl(child_sa->get_spi(child_sa, TRUE)), + ntohl(child_sa->get_spi(child_sa, FALSE)), + my_ts, other_ts); + + my_ts->destroy(my_ts); + other_ts->destroy(other_ts); +} + +/** * read in payloads and find the children to delete */ static void process_payloads(private_child_delete_t *this, message_t *message) @@ -157,6 +226,7 @@ static void process_payloads(private_child_delete_t *this, message_t *message) uint32_t spi; protocol_id_t protocol; child_sa_t *child_sa; + entry_t *entry; payloads = message->create_payload_enumerator(message); while (payloads->enumerate(payloads, &payload)) @@ -174,27 +244,37 @@ static void process_payloads(private_child_delete_t *this, message_t *message) { child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, FALSE); - if (child_sa == NULL) + if (!child_sa) { - DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x, " - "but no such SA", protocol_id_names, protocol, ntohl(spi)); + DBG1(DBG_IKE, "received DELETE for unknown %N CHILD_SA with" + " SPI %.8x", protocol_id_names, protocol, ntohl(spi)); continue; } DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x", protocol_id_names, protocol, ntohl(spi)); + if (this->child_sas->find_first(this->child_sas, match_child, + NULL, child_sa)) + { + continue; + } + INIT(entry, + .child_sa = child_sa + ); switch (child_sa->get_state(child_sa)) { case CHILD_REKEYED: - this->rekeyed = TRUE; + entry->rekeyed = TRUE; break; case CHILD_DELETING: - /* we don't send back a delete if we initiated ourself */ + /* we don't send back a delete if we already initiated + * a delete ourself */ if (!this->initiator) { + free(entry); continue; } - /* fall through */ + break; case CHILD_REKEYING: /* we reply as usual, rekeying will fail */ case CHILD_INSTALLED: @@ -202,22 +282,18 @@ static void process_payloads(private_child_delete_t *this, message_t *message) { if (is_redundant(this, child_sa)) { - this->rekeyed = TRUE; + entry->rekeyed = TRUE; } else { - this->check_delete_action = TRUE; + entry->check_delete_action = TRUE; } } break; default: break; } - if (this->child_sas->find_first(this->child_sas, NULL, - (void**)&child_sa) != SUCCESS) - { - this->child_sas->insert_last(this->child_sas, child_sa); - } + this->child_sas->insert_last(this->child_sas, entry); } spis->destroy(spis); } @@ -231,29 +307,64 @@ static void process_payloads(private_child_delete_t *this, message_t *message) static status_t destroy_and_reestablish(private_child_delete_t *this) { enumerator_t *enumerator; + entry_t *entry; child_sa_t *child_sa; child_cfg_t *child_cfg; protocol_id_t protocol; - uint32_t spi, reqid; + uint32_t spi, reqid, rekey_spi; action_t action; status_t status = SUCCESS; + time_t now, expire; + u_int delay; + + now = time_monotonic(NULL); + delay = lib->settings->get_int(lib->settings, "%s.delete_rekeyed_delay", + DELETE_REKEYED_DELAY, lib->ns); enumerator = this->child_sas->create_enumerator(this->child_sas); - while (enumerator->enumerate(enumerator, (void**)&child_sa)) + while (enumerator->enumerate(enumerator, (void**)&entry)) { + child_sa = entry->child_sa; /* signal child down event if we weren't rekeying */ - if (!this->rekeyed) + protocol = child_sa->get_protocol(child_sa); + if (!entry->rekeyed) { charon->bus->child_updown(charon->bus, child_sa, FALSE); } + else + { + rekey_spi = child_sa->get_rekey_spi(child_sa); + if (rekey_spi) + { + install_outbound(this, protocol, rekey_spi); + } + /* for rekeyed CHILD_SAs we uninstall the outbound SA but don't + * immediately destroy it, by default, so we can process delayed + * packets */ + child_sa->remove_outbound(child_sa); + expire = child_sa->get_lifetime(child_sa, TRUE); + if (delay && (!expire || ((now + delay) < expire))) + { + lib->scheduler->schedule_job(lib->scheduler, + (job_t*)delete_child_sa_job_create_id( + child_sa->get_unique_id(child_sa)), delay); + continue; + } + else if (expire) + { /* let it expire naturally */ + continue; + } + /* no delay and no lifetime, destroy it immediately */ + } spi = child_sa->get_spi(child_sa, TRUE); reqid = child_sa->get_reqid(child_sa); - protocol = child_sa->get_protocol(child_sa); child_cfg = child_sa->get_config(child_sa); child_cfg->get_ref(child_cfg); action = child_sa->get_close_action(child_sa); + this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi); - if (this->check_delete_action) + + if (entry->check_delete_action) { /* enforce child_cfg policy if deleted passively */ switch (action) { @@ -288,12 +399,14 @@ static void log_children(private_child_delete_t *this) { linked_list_t *my_ts, *other_ts; enumerator_t *enumerator; + entry_t *entry; child_sa_t *child_sa; uint64_t bytes_in, bytes_out; enumerator = this->child_sas->create_enumerator(this->child_sas); - while (enumerator->enumerate(enumerator, (void**)&child_sa)) + while (enumerator->enumerate(enumerator, (void**)&entry)) { + child_sa = entry->child_sa; my_ts = linked_list_create_from_enumerator( child_sa->create_ts_enumerator(child_sa, TRUE)); other_ts = linked_list_create_from_enumerator( @@ -328,6 +441,7 @@ METHOD(task_t, build_i, status_t, private_child_delete_t *this, message_t *message) { child_sa_t *child_sa; + entry_t *entry; child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol, this->spi, TRUE); @@ -342,15 +456,24 @@ METHOD(task_t, build_i, status_t, /* we work only with the inbound SPI */ 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_REKEYED) - { - this->rekeyed = TRUE; + + if (child_sa->get_state(child_sa) == CHILD_DELETING) + { /* DELETEs for this CHILD_SA were already exchanged, but it was not yet + * destroyed to allow delayed packets to get processed */ + this->ike_sa->destroy_child_sa(this->ike_sa, this->protocol, this->spi); + message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED); + return SUCCESS; } + + INIT(entry, + .child_sa = child_sa, + .rekeyed = child_sa->get_state(child_sa) == CHILD_REKEYED, + ); + this->child_sas->insert_last(this->child_sas, entry); log_children(this); build_payloads(this, message); - if (!this->rekeyed && this->expired) + if (!entry->rekeyed && this->expired) { child_cfg_t *child_cfg; @@ -397,24 +520,28 @@ METHOD(child_delete_t , get_child, child_sa_t*, private_child_delete_t *this) { child_sa_t *child_sa = NULL; - this->child_sas->get_first(this->child_sas, (void**)&child_sa); + entry_t *entry; + + if (this->child_sas->get_first(this->child_sas, (void**)&entry) == SUCCESS) + { + child_sa = entry->child_sa; + } return child_sa; } METHOD(task_t, migrate, void, private_child_delete_t *this, ike_sa_t *ike_sa) { - this->check_delete_action = FALSE; this->ike_sa = ike_sa; - this->child_sas->destroy(this->child_sas); + this->child_sas->destroy_function(this->child_sas, free); this->child_sas = linked_list_create(); } METHOD(task_t, destroy, void, private_child_delete_t *this) { - this->child_sas->destroy(this->child_sas); + this->child_sas->destroy_function(this->child_sas, free); free(this); } diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c index c04ec141f..761c860e7 100644 --- a/src/libcharon/sa/ikev2/tasks/child_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c @@ -132,6 +132,7 @@ static void find_child(private_child_rekey_t *this, message_t *message) notify_payload_t *notify; protocol_id_t protocol; uint32_t spi; + child_sa_t *child_sa; notify = message->get_notify(message, REKEY_SA); if (notify) @@ -141,8 +142,15 @@ static void find_child(private_child_rekey_t *this, message_t *message) if (protocol == PROTO_ESP || protocol == PROTO_AH) { - this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, - spi, FALSE); + child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, + spi, FALSE); + if (child_sa && + child_sa->get_state(child_sa) == CHILD_DELETING && + child_sa->get_outbound_state(child_sa) == CHILD_OUTBOUND_NONE) + { /* ignore rekeyed CHILD_SAs we keep around */ + return; + } + this->child_sa = child_sa; } } } @@ -227,6 +235,7 @@ METHOD(task_t, build_r, status_t, child_cfg_t *config; uint32_t reqid; child_sa_state_t state; + child_sa_t *child_sa; if (!this->child_sa) { @@ -260,7 +269,10 @@ METHOD(task_t, build_r, status_t, return SUCCESS; } + child_sa = this->child_create->get_child(this->child_create); this->child_sa->set_state(this->child_sa, CHILD_REKEYED); + this->child_sa->set_rekey_spi(this->child_sa, + child_sa->get_spi(child_sa, FALSE)); /* invoke rekey hook */ charon->bus->child_rekey(charon->bus, this->child_sa, diff --git a/src/libcharon/sa/shunt_manager.c b/src/libcharon/sa/shunt_manager.c index b0162751d..ad12f0579 100644 --- a/src/libcharon/sa/shunt_manager.c +++ b/src/libcharon/sa/shunt_manager.c @@ -381,14 +381,24 @@ METHOD(shunt_manager_t, uninstall, bool, } CALLBACK(filter_entries, bool, - void *unused, entry_t **entry, char **ns, void **in, child_cfg_t **cfg) + void *unused, enumerator_t *orig, va_list args) { - if (ns) + entry_t *entry; + child_cfg_t **cfg; + char **ns; + + VA_ARGS_VGET(args, ns, cfg); + + if (orig->enumerate(orig, &entry)) { - *ns = (*entry)->ns; + if (ns) + { + *ns = entry->ns; + } + *cfg = entry->cfg; + return TRUE; } - *cfg = (*entry)->cfg; - return TRUE; + return FALSE; } METHOD(shunt_manager_t, create_enumerator, enumerator_t*, @@ -397,7 +407,7 @@ METHOD(shunt_manager_t, create_enumerator, enumerator_t*, this->lock->read_lock(this->lock); return enumerator_create_filter( this->shunts->create_enumerator(this->shunts), - (void*)filter_entries, this->lock, + filter_entries, this->lock, (void*)this->lock->unlock); } diff --git a/src/libcharon/sa/task_manager.c b/src/libcharon/sa/task_manager.c index c42008ba9..bd1191406 100644 --- a/src/libcharon/sa/task_manager.c +++ b/src/libcharon/sa/task_manager.c @@ -15,10 +15,40 @@ #include "task_manager.h" +#include <math.h> #include <sa/ikev1/task_manager_v1.h> #include <sa/ikev2/task_manager_v2.h> -/** +/* + * See header + */ +u_int task_manager_total_retransmit_timeout() +{ + double timeout, base, limit = 0, total = 0; + int tries, i; + + tries = lib->settings->get_int(lib->settings, "%s.retransmit_tries", + RETRANSMIT_TRIES, lib->ns); + base = lib->settings->get_double(lib->settings, "%s.retransmit_base", + RETRANSMIT_BASE, lib->ns); + timeout = lib->settings->get_double(lib->settings, "%s.retransmit_timeout", + RETRANSMIT_TIMEOUT, lib->ns); + limit = lib->settings->get_double(lib->settings, "%s.retransmit_limit", + 0, lib->ns); + + for (i = 0; i <= tries; i++) + { + double interval = timeout * pow(base, i); + if (limit) + { + interval = min(interval, limit); + } + total += interval; + } + return (u_int)total; +} + +/* * See header */ task_manager_t *task_manager_create(ike_sa_t *ike_sa) diff --git a/src/libcharon/sa/task_manager.h b/src/libcharon/sa/task_manager.h index 7e9262291..e3fddf39b 100644 --- a/src/libcharon/sa/task_manager.h +++ b/src/libcharon/sa/task_manager.h @@ -48,6 +48,11 @@ typedef enum task_queue_t task_queue_t; #define RETRANSMIT_TRIES 5 /** + * Maximum jitter in percent. + */ +#define RETRANSMIT_JITTER_MAX 20 + +/** * Interval for mobike routability checks in ms. */ #define ROUTEABILITY_CHECK_INTERVAL 2500 @@ -298,6 +303,17 @@ struct task_manager_t { }; /** + * Calculate total timeout of the retransmission mechanism. + * + * This is affected by modifications of retransmit_base, retransmit_timeout, + * retransmit_limit or retransmit_tries. The resulting value can then be used + * e.g. in kernel plugins to set the system's acquire timeout properly. + * + * @return calculated total retransmission timeout in seconds + */ +u_int task_manager_total_retransmit_timeout(); + +/** * Create a task manager instance for the correct IKE version. * * @param ike_sa IKE_SA to create a task manager for diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index 40a0682f2..f9fee5e7e 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -140,19 +140,21 @@ static void destroy_acquire(acquire_t *this) free(this); } -/** - * match an acquire entry by reqid - */ -static bool acquire_by_reqid(acquire_t *this, uint32_t *reqid) +CALLBACK(acquire_by_reqid, bool, + acquire_t *this, va_list args) { - return this->reqid == *reqid; + uint32_t reqid; + + VA_ARGS_VGET(args, reqid); + return this->reqid == reqid; } -/** - * match an acquire entry by destination address - */ -static bool acquire_by_dst(acquire_t *this, host_t *dst) +CALLBACK(acquire_by_dst, bool, + acquire_t *this, va_list args) { + host_t *dst; + + VA_ARGS_VGET(args, dst); return this->dst && this->dst->ip_equals(this->dst, dst); } @@ -272,7 +274,8 @@ METHOD(trap_manager_t, install, uint32_t, proposals->destroy_offset(proposals, offsetof(proposal_t, destroy)); child_sa->set_protocol(child_sa, proto); child_sa->set_mode(child_sa, child->get_mode(child)); - status = child_sa->add_policies(child_sa, my_ts, other_ts); + child_sa->set_policies(child_sa, my_ts, other_ts); + status = child_sa->install_policies(child_sa); my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy)); other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); if (status != SUCCESS) @@ -334,25 +337,32 @@ METHOD(trap_manager_t, uninstall, bool, return TRUE; } -/** - * convert enumerated entries to peer_cfg, child_sa - */ -static bool trap_filter(rwlock_t *lock, entry_t **entry, peer_cfg_t **peer_cfg, - void *none, child_sa_t **child_sa) +CALLBACK(trap_filter, bool, + rwlock_t *lock, enumerator_t *orig, va_list args) { - if (!(*entry)->child_sa) - { /* skip entries that are currently being installed */ - return FALSE; - } - if (peer_cfg) - { - *peer_cfg = (*entry)->peer_cfg; - } - if (child_sa) + entry_t *entry; + peer_cfg_t **peer_cfg; + child_sa_t **child_sa; + + VA_ARGS_VGET(args, peer_cfg, child_sa); + + while (orig->enumerate(orig, &entry)) { - *child_sa = (*entry)->child_sa; + if (!entry->child_sa) + { /* skip entries that are currently being installed */ + continue; + } + if (peer_cfg) + { + *peer_cfg = entry->peer_cfg; + } + if (child_sa) + { + *child_sa = entry->child_sa; + } + return TRUE; } - return TRUE; + return FALSE; } METHOD(trap_manager_t, create_enumerator, enumerator_t*, @@ -360,7 +370,7 @@ METHOD(trap_manager_t, create_enumerator, enumerator_t*, { this->lock->read_lock(this->lock); return enumerator_create_filter(this->traps->create_enumerator(this->traps), - (void*)trap_filter, this->lock, + trap_filter, this->lock, (void*)this->lock->unlock); } @@ -431,8 +441,8 @@ METHOD(trap_manager_t, acquire, void, uint8_t mask; dst->to_subnet(dst, &host, &mask); - if (this->acquires->find_first(this->acquires, (void*)acquire_by_dst, - (void**)&acquire, host) == SUCCESS) + if (this->acquires->find_first(this->acquires, acquire_by_dst, + (void**)&acquire, host)) { host->destroy(host); ignore = TRUE; @@ -448,8 +458,8 @@ METHOD(trap_manager_t, acquire, void, } else { - if (this->acquires->find_first(this->acquires, (void*)acquire_by_reqid, - (void**)&acquire, &reqid) == SUCCESS) + if (this->acquires->find_first(this->acquires, acquire_by_reqid, + (void**)&acquire, reqid)) { ignore = TRUE; } |