diff options
author | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2009-02-28 22:02:31 +0000 |
---|---|---|
committer | Rene Mayrhofer <rene@mayrhofer.eu.org> | 2009-02-28 22:02:31 +0000 |
commit | 19364e11c66714324bd3d5d0dc9212db397085cb (patch) | |
tree | fe7f5e55f0474dad1d0c29ba7c0a6f4546c99c3a /src/charon/sa | |
parent | c7f1b0530b85bc7654e68992f25ed8ced5d0a80d (diff) | |
download | vyos-strongswan-19364e11c66714324bd3d5d0dc9212db397085cb.tar.gz vyos-strongswan-19364e11c66714324bd3d5d0dc9212db397085cb.zip |
[svn-upgrade] Integrating new upstream version, strongswan (4.2.12)
Diffstat (limited to 'src/charon/sa')
23 files changed, 2109 insertions, 1168 deletions
diff --git a/src/charon/sa/authenticators/eap/eap_method.c b/src/charon/sa/authenticators/eap/eap_method.c index 11b12fb49..6babab212 100644 --- a/src/charon/sa/authenticators/eap/eap_method.c +++ b/src/charon/sa/authenticators/eap/eap_method.c @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: eap_method.c 4269 2008-08-21 12:10:07Z martin $ + * $Id: eap_method.c 4882 2009-02-18 19:57:15Z tobias $ */ #include "eap_method.h" @@ -28,7 +28,9 @@ ENUM_NEXT(eap_type_names, EAP_SIM, EAP_SIM, EAP_GTC, "EAP_SIM"); ENUM_NEXT(eap_type_names, EAP_AKA, EAP_AKA, EAP_SIM, "EAP_AKA"); -ENUM_NEXT(eap_type_names, EAP_EXPANDED, EAP_EXPERIMENTAL, EAP_AKA, +ENUM_NEXT(eap_type_names, EAP_MSCHAPV2, EAP_MSCHAPV2, EAP_AKA, + "EAP_MSCHAPV2"); +ENUM_NEXT(eap_type_names, EAP_EXPANDED, EAP_EXPERIMENTAL, EAP_MSCHAPV2, "EAP_EXPANDED", "EAP_EXPERIMENTAL"); ENUM_END(eap_type_names, EAP_EXPERIMENTAL); diff --git a/src/charon/sa/authenticators/eap/eap_method.h b/src/charon/sa/authenticators/eap/eap_method.h index 663117931..1fd7bd24b 100644 --- a/src/charon/sa/authenticators/eap/eap_method.h +++ b/src/charon/sa/authenticators/eap/eap_method.h @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: eap_method.h 4276 2008-08-22 10:44:51Z martin $ + * $Id: eap_method.h 4882 2009-02-18 19:57:15Z tobias $ */ /** @@ -56,6 +56,7 @@ enum eap_type_t { EAP_GTC = 6, EAP_SIM = 18, EAP_AKA = 23, + EAP_MSCHAPV2 = 26, EAP_EXPANDED = 254, EAP_EXPERIMENTAL = 255, }; diff --git a/src/charon/sa/authenticators/eap_authenticator.c b/src/charon/sa/authenticators/eap_authenticator.c index 5c22f3df2..0c0abcf2e 100644 --- a/src/charon/sa/authenticators/eap_authenticator.c +++ b/src/charon/sa/authenticators/eap_authenticator.c @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: eap_authenticator.c 4495 2008-10-28 16:07:06Z martin $ + * $Id: eap_authenticator.c 4754 2008-12-04 10:09:21Z martin $ */ #include <string.h> @@ -360,6 +360,7 @@ static status_t process_eap_identity(private_eap_authenticator_t *this, } /* restart EAP exchange, but with real method */ this->method->destroy(this->method); + this->method = NULL; this->do_eap_identity = FALSE; return initiate(this, this->type, this->vendor, out); } diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c index d7a63d5e8..022b9149a 100644 --- a/src/charon/sa/child_sa.c +++ b/src/charon/sa/child_sa.c @@ -15,7 +15,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: child_sa.c 4665 2008-11-17 00:01:34Z andreas $ + * $Id: child_sa.c 4677 2008-11-19 15:31:27Z martin $ */ #define _GNU_SOURCE @@ -90,16 +90,6 @@ struct private_child_sa_t { linked_list_t *other_ts; /** - * Allocated SPI for a ESP proposal candidates - */ - u_int32_t alloc_esp_spi; - - /** - * Allocated SPI for a AH proposal candidates - */ - u_int32_t alloc_ah_spi; - - /** * Protocol used to protect this SA, ESP|AH */ protocol_id_t protocol; @@ -135,11 +125,6 @@ struct private_child_sa_t { ipcomp_transform_t ipcomp; /** - * TRUE if we allocated (or tried to allocate) a CPI - */ - bool cpi_allocated; - - /** * mode this SA uses, tunnel/transport */ ipsec_mode_t mode; @@ -170,7 +155,32 @@ static u_int32_t get_reqid(private_child_sa_t *this) { return this->reqid; } - + +/** + * Implements child_sa_t.get_config + */ +static child_cfg_t* get_config(private_child_sa_t *this) +{ + return this->config; +} + +/** + * Implements child_sa_t.set_state + */ +static void set_state(private_child_sa_t *this, child_sa_state_t state) +{ + charon->bus->child_state_change(charon->bus, &this->public, state); + this->state = state; +} + +/** + * Implements child_sa_t.get_state + */ +static child_sa_state_t get_state(private_child_sa_t *this) +{ + return this->state; +} + /** * Implements child_sa_t.get_spi */ @@ -196,6 +206,14 @@ protocol_id_t get_protocol(private_child_sa_t *this) } /** + * Implementation of child_sa_t.set_protocol + */ +static void set_protocol(private_child_sa_t *this, protocol_id_t protocol) +{ + this->protocol = protocol; +} + +/** * Implementation of child_sa_t.get_mode */ static ipsec_mode_t get_mode(private_child_sa_t *this) @@ -204,6 +222,14 @@ static ipsec_mode_t get_mode(private_child_sa_t *this) } /** + * Implementation of child_sa_t.set_mode + */ +static void set_mode(private_child_sa_t *this, ipsec_mode_t mode) +{ + this->mode = mode; +} + +/** * Implementation of child_sa_t.has_encap */ static bool has_encap(private_child_sa_t *this) @@ -220,19 +246,35 @@ static ipcomp_transform_t get_ipcomp(private_child_sa_t *this) } /** - * Implements child_sa_t.get_state + * Implementation of child_sa_t.set_ipcomp. */ -static child_sa_state_t get_state(private_child_sa_t *this) +static void set_ipcomp(private_child_sa_t *this, ipcomp_transform_t ipcomp) { - return this->state; + this->ipcomp = ipcomp; } /** - * Implements child_sa_t.get_config + * Implementation of child_sa_t.get_proposal */ -static child_cfg_t* get_config(private_child_sa_t *this) +static proposal_t* get_proposal(private_child_sa_t *this) { - return this->config; + return this->proposal; +} + +/** + * Implementation of child_sa_t.set_proposal + */ +static void set_proposal(private_child_sa_t *this, proposal_t *proposal) +{ + this->proposal = proposal->clone(proposal); +} + +/** + * Implementation of child_sa_t.get_traffic_selectors. + */ +static linked_list_t *get_traffic_selectors(private_child_sa_t *this, bool local) +{ + return local ? this->my_ts : this->other_ts; } typedef struct policy_enumerator_t policy_enumerator_t; @@ -366,143 +408,100 @@ static u_int32_t get_lifetime(private_child_sa_t *this, bool hard) } /** - * Implements child_sa_t.set_state - */ -static void set_state(private_child_sa_t *this, child_sa_state_t state) -{ - charon->bus->child_state_change(charon->bus, &this->public, state); - this->state = state; -} - -/** - * Allocate SPI for a single proposal + * Implementation of child_sa_t.alloc_spi */ -static status_t alloc_proposal(private_child_sa_t *this, proposal_t *proposal) +static u_int32_t alloc_spi(private_child_sa_t *this, protocol_id_t protocol) { - protocol_id_t protocol = proposal->get_protocol(proposal); - - if (protocol == PROTO_AH) + switch (protocol) { - /* get a new spi for AH, if not already done */ - if (this->alloc_ah_spi == 0) - { - if (charon->kernel_interface->get_spi( - charon->kernel_interface, - this->other_addr, this->my_addr, - PROTO_AH, this->reqid, - &this->alloc_ah_spi) != SUCCESS) + case PROTO_AH: + if (charon->kernel_interface->get_spi(charon->kernel_interface, + this->other_addr, this->my_addr, PROTO_AH, + this->reqid, &this->my_spi) == SUCCESS) { - return FAILED; + return this->my_spi; } - } - proposal->set_spi(proposal, this->alloc_ah_spi); - } - if (protocol == PROTO_ESP) - { - /* get a new spi for ESP, if not already done */ - if (this->alloc_esp_spi == 0) - { - if (charon->kernel_interface->get_spi( - charon->kernel_interface, - this->other_addr, this->my_addr, - PROTO_ESP, this->reqid, - &this->alloc_esp_spi) != SUCCESS) + break; + case PROTO_ESP: + if (charon->kernel_interface->get_spi(charon->kernel_interface, + this->other_addr, this->my_addr, PROTO_ESP, + this->reqid, &this->my_spi) == SUCCESS) { - return FAILED; + return this->my_spi; } - } - proposal->set_spi(proposal, this->alloc_esp_spi); + break; + default: + break; } - return SUCCESS; + return 0; } /** - * Implements child_sa_t.alloc + * Implementation of child_sa_t.alloc_cpi */ -static status_t alloc(private_child_sa_t *this, linked_list_t *proposals) +static u_int16_t alloc_cpi(private_child_sa_t *this) { - iterator_t *iterator; - proposal_t *proposal; - - /* iterator through proposals to update spis */ - iterator = proposals->create_iterator(proposals, TRUE); - while(iterator->iterate(iterator, (void**)&proposal)) + if (charon->kernel_interface->get_cpi(charon->kernel_interface, + this->other_addr, this->my_addr, this->reqid, + &this->my_cpi) == SUCCESS) { - if (alloc_proposal(this, proposal) != SUCCESS) - { - iterator->destroy(iterator); - return FAILED; - } + return this->my_cpi; } - iterator->destroy(iterator); - return SUCCESS; + return 0; } /** - * Install an SA for one direction + * Implementation of child_sa_t.install */ -static status_t install(private_child_sa_t *this, proposal_t *proposal, - ipsec_mode_t mode, chunk_t integ, chunk_t encr, bool in) +static status_t install(private_child_sa_t *this, chunk_t encr, chunk_t integ, + u_int32_t spi, u_int16_t cpi, bool inbound) { u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size; - u_int32_t spi, soft, hard, now; + u_int32_t soft, hard, now; host_t *src, *dst; status_t status; + bool update = FALSE; /* now we have to decide which spi to use. Use self allocated, if "in", * or the one in the proposal, if not "in" (others). Additionally, * source and dest host switch depending on the role */ - if (in) + if (inbound) { - /* if we have allocated SPIs for AH and ESP, we must delete the unused - * one. */ - if (this->protocol == PROTO_ESP) - { - this->my_spi = this->alloc_esp_spi; - if (this->alloc_ah_spi) - { - charon->kernel_interface->del_sa(charon->kernel_interface, - this->my_addr, this->alloc_ah_spi, 0, PROTO_AH); - } - } - else - { - this->my_spi = this->alloc_ah_spi; - if (this->alloc_esp_spi) - { - charon->kernel_interface->del_sa(charon->kernel_interface, - this->my_addr, this->alloc_esp_spi, 0, PROTO_ESP); - } - } - spi = this->my_spi; dst = this->my_addr; src = this->other_addr; + if (this->my_spi == spi) + { /* alloc_spi has been called, do an SA update */ + update = TRUE; + } + this->my_spi = spi; + this->my_cpi = cpi; } else { - this->other_spi = proposal->get_spi(proposal); - spi = this->other_spi; src = this->my_addr; dst = this->other_addr; + this->other_spi = spi; + this->other_cpi = cpi; } - DBG2(DBG_CHD, "adding %s %N SA", in ? "inbound" : "outbound", + DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound", protocol_id_names, this->protocol); /* send SA down to the kernel */ DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst); - proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_alg, &size); - proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_alg, &size); + this->proposal->get_algorithm(this->proposal, ENCRYPTION_ALGORITHM, + &enc_alg, &size); + this->proposal->get_algorithm(this->proposal, INTEGRITY_ALGORITHM, + &int_alg, &size); soft = this->config->get_lifetime(this->config, TRUE); hard = this->config->get_lifetime(this->config, FALSE); - + status = charon->kernel_interface->add_sa(charon->kernel_interface, src, dst, spi, this->protocol, this->reqid, - in ? soft : 0, hard, enc_alg, encr, int_alg, integ, - mode, this->ipcomp, in ? this->my_cpi : this->other_cpi, - this->encap, in); + inbound ? soft : 0, hard, enc_alg, encr, int_alg, integ, + this->mode, this->ipcomp, cpi, this->encap, update); now = time(NULL); this->rekey_time = now + soft; @@ -511,83 +510,16 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal, } /** - * Implementation of child_sa_t.add - */ -static status_t add(private_child_sa_t *this, - proposal_t *proposal, ipsec_mode_t mode, - chunk_t integ_in, chunk_t integ_out, - chunk_t encr_in, chunk_t encr_out) -{ - this->proposal = proposal->clone(proposal); - this->protocol = proposal->get_protocol(proposal); - - /* get SPIs for inbound SAs, write to proposal */ - if (alloc_proposal(this, proposal) != SUCCESS) - { - return FAILED; - } - /* install inbound SAs using allocated SPI */ - if (install(this, proposal, mode, integ_in, encr_in, TRUE) != SUCCESS) - { - return FAILED; - } - /* install outbound SAs using received SPI*/ - if (install(this, this->proposal, mode, integ_out, encr_out, FALSE) != SUCCESS) - { - return FAILED; - } - return SUCCESS; -} - -/** - * Implementation of child_sa_t.update - */ -static status_t update(private_child_sa_t *this, - proposal_t *proposal, ipsec_mode_t mode, - chunk_t integ_in, chunk_t integ_out, - chunk_t encr_in, chunk_t encr_out) -{ - this->proposal = proposal->clone(proposal); - this->protocol = proposal->get_protocol(proposal); - - /* install outbound SAs */ - if (install(this, proposal, mode, integ_out, encr_out, FALSE) != SUCCESS) - { - return FAILED; - } - /* install inbound SAs */ - if (install(this, proposal, mode, integ_in, encr_in, TRUE) != SUCCESS) - { - return FAILED; - } - return SUCCESS; -} - -/** - * Implementation of child_sa_t.get_proposal - */ -static proposal_t* get_proposal(private_child_sa_t *this) -{ - return this->proposal; -} - -/** * Implementation of child_sa_t.add_policies */ static status_t add_policies(private_child_sa_t *this, - linked_list_t *my_ts_list, linked_list_t *other_ts_list, - ipsec_mode_t mode, protocol_id_t proto) + linked_list_t *my_ts_list, linked_list_t *other_ts_list) { enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; status_t status = SUCCESS; bool routed = (this->state == CHILD_CREATED); - if (this->protocol == PROTO_NONE) - { /* update if not set yet */ - this->protocol = proto; - } - /* apply traffic selectors */ enumerator = my_ts_list->create_enumerator(my_ts_list); while (enumerator->enumerate(enumerator, &my_ts)) @@ -611,19 +543,19 @@ static status_t add_policies(private_child_sa_t *this, /* install 3 policies: out, in and forward */ status |= charon->kernel_interface->add_policy(charon->kernel_interface, this->my_addr, this->other_addr, my_ts, other_ts, POLICY_OUT, - this->other_spi, this->protocol, this->reqid, mode, this->ipcomp, - this->other_cpi, routed); + this->other_spi, this->protocol, this->reqid, this->mode, + this->ipcomp, this->other_cpi, routed); status |= charon->kernel_interface->add_policy(charon->kernel_interface, this->other_addr, this->my_addr, other_ts, my_ts, POLICY_IN, - this->my_spi, this->protocol, this->reqid, mode, this->ipcomp, - this->my_cpi, routed); - if (mode != MODE_TRANSPORT) + this->my_spi, this->protocol, this->reqid, this->mode, + this->ipcomp, this->my_cpi, routed); + if (this->mode != MODE_TRANSPORT) { status |= charon->kernel_interface->add_policy(charon->kernel_interface, this->other_addr, this->my_addr, other_ts, my_ts, POLICY_FWD, - this->my_spi, this->protocol, this->reqid, mode, this->ipcomp, - this->my_cpi, routed); + this->my_spi, this->protocol, this->reqid, this->mode, + this->ipcomp, this->my_cpi, routed); } if (status != SUCCESS) @@ -634,32 +566,18 @@ static status_t add_policies(private_child_sa_t *this, enumerator->destroy(enumerator); } - if (status == SUCCESS) - { - /* switch to routed state if no SAD entry set up */ - if (this->state == CHILD_CREATED) - { - set_state(this, CHILD_ROUTED); - } - /* needed to update hosts */ - this->mode = mode; + if (status == SUCCESS && this->state == CHILD_CREATED) + { /* switch to routed state if no SAD entry set up */ + set_state(this, CHILD_ROUTED); } return status; } /** - * Implementation of child_sa_t.get_traffic_selectors. + * Implementation of child_sa_t.update. */ -static linked_list_t *get_traffic_selectors(private_child_sa_t *this, bool local) -{ - return local ? this->my_ts : this->other_ts; -} - -/** - * Implementation of child_sa_t.update_hosts. - */ -static status_t update_hosts(private_child_sa_t *this, - host_t *me, host_t *other, host_t *vip, bool encap) +static status_t update(private_child_sa_t *this, host_t *me, host_t *other, + host_t *vip, bool encap) { child_sa_state_t old; bool transport_proxy_mode; @@ -792,30 +710,6 @@ static status_t update_hosts(private_child_sa_t *this, } /** - * Implementation of child_sa_t.activate_ipcomp. - */ -static void activate_ipcomp(private_child_sa_t *this, ipcomp_transform_t ipcomp, - u_int16_t other_cpi) -{ - this->ipcomp = ipcomp; - this->other_cpi = other_cpi; -} - -/** - * Implementation of child_sa_t.allocate_cpi. - */ -static u_int16_t allocate_cpi(private_child_sa_t *this) -{ - if (!this->cpi_allocated) - { - charon->kernel_interface->get_cpi(charon->kernel_interface, - this->other_addr, this->my_addr, this->reqid, &this->my_cpi); - this->cpi_allocated = TRUE; - } - return this->my_cpi; -} - -/** * Implementation of child_sa_t.destroy. */ static void destroy(private_child_sa_t *this) @@ -833,16 +727,6 @@ static void destroy(private_child_sa_t *this) this->my_addr, this->my_spi, this->protocol, this->my_cpi); } - if (this->alloc_esp_spi && this->alloc_esp_spi != this->my_spi) - { - charon->kernel_interface->del_sa(charon->kernel_interface, - this->my_addr, this->alloc_esp_spi, PROTO_ESP, 0); - } - if (this->alloc_ah_spi && this->alloc_ah_spi != this->my_spi) - { - charon->kernel_interface->del_sa(charon->kernel_interface, - this->my_addr, this->alloc_ah_spi, PROTO_AH, 0); - } if (this->other_spi) { charon->kernel_interface->del_sa(charon->kernel_interface, @@ -890,40 +774,39 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, /* public functions */ this->public.get_name = (char*(*)(child_sa_t*))get_name; this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid; + this->public.get_config = (child_cfg_t*(*)(child_sa_t*))get_config; + this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state; + this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state; this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi; this->public.get_cpi = (u_int16_t(*)(child_sa_t*, bool))get_cpi; this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol; + this->public.set_protocol = (void(*)(child_sa_t*, protocol_id_t protocol))set_protocol; this->public.get_mode = (ipsec_mode_t(*)(child_sa_t*))get_mode; - this->public.get_ipcomp = (ipcomp_transform_t(*)(child_sa_t*))get_ipcomp; - this->public.has_encap = (bool(*)(child_sa_t*))has_encap; + this->public.set_mode = (void(*)(child_sa_t*, ipsec_mode_t mode))set_mode; + this->public.get_proposal = (proposal_t*(*)(child_sa_t*))get_proposal; + this->public.set_proposal = (void(*)(child_sa_t*, proposal_t *proposal))set_proposal; this->public.get_lifetime = (u_int32_t(*)(child_sa_t*, bool))get_lifetime; this->public.get_usetime = (u_int32_t(*)(child_sa_t*, bool))get_usetime; - this->public.alloc = (status_t(*)(child_sa_t*,linked_list_t*))alloc; - this->public.add = (status_t(*)(child_sa_t*,proposal_t*,ipsec_mode_t,chunk_t,chunk_t,chunk_t,chunk_t))add; - this->public.update = (status_t(*)(child_sa_t*,proposal_t*,ipsec_mode_t,chunk_t,chunk_t,chunk_t,chunk_t))update; - this->public.get_proposal = (proposal_t*(*)(child_sa_t*))get_proposal; - this->public.update_hosts = (status_t (*)(child_sa_t*,host_t*,host_t*,host_t*,bool))update_hosts; - this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*,ipsec_mode_t,protocol_id_t))add_policies; + this->public.has_encap = (bool(*)(child_sa_t*))has_encap; + this->public.get_ipcomp = (ipcomp_transform_t(*)(child_sa_t*))get_ipcomp; + this->public.set_ipcomp = (void(*)(child_sa_t*,ipcomp_transform_t))set_ipcomp; + this->public.alloc_spi = (u_int32_t(*)(child_sa_t*, protocol_id_t protocol))alloc_spi; + this->public.alloc_cpi = (u_int16_t(*)(child_sa_t*))alloc_cpi; + this->public.install = (status_t(*)(child_sa_t*, chunk_t encr, chunk_t integ, u_int32_t spi, u_int16_t cpi, bool inbound))install; + this->public.update = (status_t (*)(child_sa_t*,host_t*,host_t*,host_t*,bool))update; + this->public.add_policies = (status_t (*)(child_sa_t*, linked_list_t*,linked_list_t*))add_policies; this->public.get_traffic_selectors = (linked_list_t*(*)(child_sa_t*,bool))get_traffic_selectors; this->public.create_policy_enumerator = (enumerator_t*(*)(child_sa_t*))create_policy_enumerator; - this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state; - this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state; - this->public.get_config = (child_cfg_t*(*)(child_sa_t*))get_config; - this->public.activate_ipcomp = (void(*)(child_sa_t*,ipcomp_transform_t,u_int16_t))activate_ipcomp; - this->public.allocate_cpi = (u_int16_t(*)(child_sa_t*))allocate_cpi; this->public.destroy = (void(*)(child_sa_t*))destroy; - + /* private data */ this->my_addr = me->clone(me); this->other_addr = other->clone(other); this->my_spi = 0; - this->my_cpi = 0; this->other_spi = 0; + this->my_cpi = 0; this->other_cpi = 0; - this->alloc_ah_spi = 0; - this->alloc_esp_spi = 0; this->encap = encap; - this->cpi_allocated = FALSE; this->ipcomp = IPCOMP_NONE; this->state = CHILD_CREATED; /* reuse old reqid if we are rekeying an existing CHILD_SA */ @@ -935,7 +818,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, this->proposal = NULL; this->config = config; config->get_ref(config); - + /* MIPv6 proxy transport mode sets SA endpoints to TS hosts */ if (config->get_mode(config) == MODE_TRANSPORT && config->use_proxy_mode(config)) @@ -947,9 +830,9 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, enumerator_t *enumerator; linked_list_t *my_ts_list, *other_ts_list; traffic_selector_t *my_ts, *other_ts; - + this->mode = MODE_TRANSPORT; - + my_ts_list = config->get_traffic_selectors(config, TRUE, NULL, me); enumerator = my_ts_list->create_enumerator(my_ts_list); if (enumerator->enumerate(enumerator, &my_ts)) @@ -970,7 +853,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, } enumerator->destroy(enumerator); my_ts_list->destroy_offset(my_ts_list, offsetof(traffic_selector_t, destroy)); - + other_ts_list = config->get_traffic_selectors(config, FALSE, NULL, other); enumerator = other_ts_list->create_enumerator(other_ts_list); if (enumerator->enumerate(enumerator, &other_ts)) diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h index 7109de5cd..277fd0a79 100644 --- a/src/charon/sa/child_sa.h +++ b/src/charon/sa/child_sa.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2006-2008 Tobias Brunner - * Copyright (C) 2006-2007 Martin Willi + * Copyright (C) 2006-2008 Martin Willi * Copyright (C) 2006 Daniel Roethlisberger * Hochschule fuer Technik Rapperswil * @@ -14,7 +14,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: child_sa.h 4618 2008-11-11 09:22:00Z tobias $ + * $Id: child_sa.h 4677 2008-11-19 15:31:27Z martin $ */ /** @@ -93,12 +93,13 @@ extern enum_name_t *child_sa_state_names; * SAs and the policies have the same reqid. * * The procedure for child sa setup is as follows: - * - A gets SPIs for a proposal via child_sa_t.alloc - * - A send the updated proposal to B + * - A gets SPIs for a all protocols in its proposals via child_sa_t.alloc + * - A send the proposals with the allocated SPIs to B * - B selects a suitable proposal - * - B calls child_sa_t.add to add and update the selected proposal - * - B sends the updated proposal to A - * - A calls child_sa_t.update to update the already allocated SPIs with the chosen proposal + * - B allocates an SPI for the selected protocol + * - B calls child_sa_t.install for both, the allocated and received SPI + * - B sends the proposal with the allocated SPI to A + * - A calls child_sa_t.install for both, the allocated and recevied SPI * * Once SAs are set up, policies can be added using add_policies. */ @@ -122,6 +123,27 @@ struct child_sa_t { u_int32_t (*get_reqid)(child_sa_t *this); /** + * Get the config used to set up this child sa. + * + * @return child_cfg + */ + child_cfg_t* (*get_config) (child_sa_t *this); + + /** + * Get the state of the CHILD_SA. + * + * @return CHILD_SA state + */ + child_sa_state_t (*get_state) (child_sa_t *this); + + /** + * Set the state of the CHILD_SA. + * + * @param state state to set on CHILD_SA + */ + void (*set_state) (child_sa_t *this, child_sa_state_t state); + + /** * Get the SPI of this CHILD_SA. * * Set the boolean parameter inbound to TRUE to @@ -153,6 +175,13 @@ struct child_sa_t { protocol_id_t (*get_protocol) (child_sa_t *this); /** + * Set the negotiated protocol to use for this CHILD_SA. + * + * @param protocol AH | ESP + */ + void (*set_protocol)(child_sa_t *this, protocol_id_t protocol); + + /** * Get the IPsec mode of this CHILD_SA. * * @return TUNNEL | TRANSPORT | BEET @@ -160,6 +189,13 @@ struct child_sa_t { ipsec_mode_t (*get_mode)(child_sa_t *this); /** + * Set the negotiated IPsec mode to use. + * + * @param mode TUNNEL | TRANPORT | BEET + */ + void (*set_mode)(child_sa_t *this, ipsec_mode_t mode); + + /** * Get the used IPComp algorithm. * * @return IPComp compression algorithm. @@ -167,6 +203,27 @@ struct child_sa_t { ipcomp_transform_t (*get_ipcomp)(child_sa_t *this); /** + * Set the IPComp algorithm to use. + * + * @param ipcomp the IPComp transform to use + */ + void (*set_ipcomp)(child_sa_t *this, ipcomp_transform_t ipcomp); + + /** + * Get the selected proposal. + * + * @return selected proposal + */ + proposal_t* (*get_proposal)(child_sa_t *this); + + /** + * Set the negotiated proposal. + * + * @param proposal selected proposal + */ + void (*set_proposal)(child_sa_t *this, proposal_t *proposal); + + /** * Check if this CHILD_SA uses UDP encapsulation. * * @return TRUE if SA encapsulates ESP packets @@ -190,69 +247,48 @@ struct child_sa_t { u_int32_t (*get_usetime)(child_sa_t *this, bool inbound); /** - * Allocate SPIs for given proposals. - * - * Since the kernel manages SPIs for us, we need - * to allocate them. If a proposal contains more - * than one protocol, for each protocol an SPI is - * allocated. SPIs are stored internally and written - * back to the proposal. + * Get the traffic selectors list added for one side. * - * @param proposals list of proposals for which SPIs are allocated - */ - status_t (*alloc)(child_sa_t *this, linked_list_t* proposals); + * @param local TRUE for own traffic selectors, FALSE for remote + * @return list of traffic selectors + */ + linked_list_t* (*get_traffic_selectors) (child_sa_t *this, bool local); /** - * Install the kernel SAs for a proposal, without previous SPI allocation. + * Create an enumerator over installed policies. * - * @param proposal proposal for which SPIs are allocated - * @param mode mode for the CHILD_SA - * @param integ_in integrity key for inbound traffic - * @param integ_out integrity key for outbound traffic - * @param encr_in encryption key for inbound traffic - * @param enc_out encryption key for outbound traffic - * @return SUCCESS or FAILED + * @return enumerator over pairs of traffic selectors. */ - status_t (*add)(child_sa_t *this, proposal_t *proposal, ipsec_mode_t mode, - chunk_t integ_in, chunk_t integ_out, - chunk_t encr_in, chunk_t encr_out); + enumerator_t* (*create_policy_enumerator)(child_sa_t *this); + /** - * Install the kernel SAs for a proposal, after SPIs have been allocated. - * - * Updates an SA, for which SPIs are already allocated via alloc(). + * Allocate an SPI to include in a proposal. * - * @param proposal proposal for which SPIs are allocated - * @param mode mode for the CHILD_SA - * @param integ_in integrity key for inbound traffic - * @param integ_out integrity key for outbound traffic - * @param encr_in encryption key for inbound traffic - * @param enc_out encryption key for outbound traffic - * @return SUCCESS or FAILED + * @param protocol protocol to allocate SPI for (ESP|AH) + * @param spi SPI output pointer + * @return SPI, 0 on failure */ - status_t (*update)(child_sa_t *this, proposal_t *proposal, ipsec_mode_t mode, - chunk_t integ_in, chunk_t integ_out, - chunk_t encr_in, chunk_t encr_out); + u_int32_t (*alloc_spi)(child_sa_t *this, protocol_id_t protocol); + /** - * Get the selected proposal passed to add()/update(). + * Allocate a CPI to use for IPComp. * - * @return selected proposal + * @return CPI, 0 on failure */ - proposal_t* (*get_proposal)(child_sa_t *this); + u_int16_t (*alloc_cpi)(child_sa_t *this); /** - * Update the hosts in the kernel SAs and policies. + * Install an IPsec SA for one direction. * - * The CHILD must be INSTALLED to do this update. - * - * @param me the new local host - * @param other the new remote host - * @param vip virtual IP, if any - * @param TRUE to use UDP encapsulation for NAT traversal + * @param encr encryption key, if any + * @param integ integrity key + * @param spi SPI to use, allocated for inbound + * @param cpi CPI to use, allocated for outbound + * @param inbound TRUE to install an inbound SA, FALSE for outbound * @return SUCCESS or FAILED */ - status_t (*update_hosts)(child_sa_t *this, host_t *me, host_t *other, - host_t *vip, bool encap); - + status_t (*install)(child_sa_t *this, chunk_t encr, chunk_t integ, + u_int32_t spi, u_int16_t cpi, bool inbound); /** * Install the policies using some traffic selectors. * @@ -261,64 +297,21 @@ struct child_sa_t { * * @param my_ts traffic selectors for local site * @param other_ts traffic selectors for remote site - * @param mode mode for the SA: tunnel/transport - * @param proto protocol for policy, ESP/AH * @return SUCCESS or FAILED */ status_t (*add_policies)(child_sa_t *this, linked_list_t *my_ts_list, - linked_list_t *other_ts_list, ipsec_mode_t mode, - protocol_id_t proto); - - /** - * Get the traffic selectors of added policies of local host. - * - * @param local TRUE for own traffic selectors, FALSE for remote - * @return list of traffic selectors - */ - linked_list_t* (*get_traffic_selectors) (child_sa_t *this, bool local); - + linked_list_t *other_ts_list); /** - * Create an enumerator over installed policies. + * Update hosts and ecapulation mode in the kernel SAs and policies. * - * @return enumerator over pairs of traffic selectors. - */ - enumerator_t* (*create_policy_enumerator)(child_sa_t *this); - - /** - * Get the state of the CHILD_SA. - */ - child_sa_state_t (*get_state) (child_sa_t *this); - - /** - * Set the state of the CHILD_SA. - * - * @param state state to set on CHILD_SA - */ - void (*set_state) (child_sa_t *this, child_sa_state_t state); - - /** - * Get the config used to set up this child sa. - * - * @return child_cfg - */ - child_cfg_t* (*get_config) (child_sa_t *this); - - /** - * Activate IPComp by setting the transform ID and CPI values. - * - * @param ipcomp the IPComp transform to use - * @param other_cpi other Compression Parameter Index - */ - void (*activate_ipcomp) (child_sa_t *this, ipcomp_transform_t ipcomp, - u_int16_t other_cpi); - - /** - * Returns the Compression Parameter Index (CPI) allocated from the kernel. - * - * @return allocated CPI + * @param me the new local host + * @param other the new remote host + * @param vip virtual IP, if any + * @param TRUE to use UDP encapsulation for NAT traversal + * @return SUCCESS or FAILED */ - u_int16_t (*allocate_cpi) (child_sa_t *this); - + status_t (*update)(child_sa_t *this, host_t *me, host_t *other, + host_t *vip, bool encap); /** * Destroys a child_sa. */ diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index d9bb01c60..82dd479ca 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -15,7 +15,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: ike_sa.c 4652 2008-11-14 08:38:53Z martin $ + * $Id: ike_sa.c 4808 2008-12-16 15:48:36Z martin $ */ #include <sys/time.h> @@ -66,6 +66,7 @@ ENUM(ike_sa_state_names, IKE_CREATED, IKE_DESTROYING, "CREATED", "CONNECTING", "ESTABLISHED", + "PASSIVE", "REKEYING", "DELETING", "DESTROYING", @@ -410,6 +411,21 @@ static void set_proposal(private_ike_sa_t *this, proposal_t *proposal) } /** + * Implementation of ike_sa_t.set_message_id + */ +static void set_message_id(private_ike_sa_t *this, bool initiate, u_int32_t mid) +{ + if (initiate) + { + this->task_manager->reset(this->task_manager, mid, UINT_MAX); + } + else + { + this->task_manager->reset(this->task_manager, UINT_MAX, mid); + } +} + +/** * Implementation of ike_sa_t.send_keepalive */ static void send_keepalive(private_ike_sa_t *this) @@ -621,7 +637,8 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state) { case IKE_ESTABLISHED: { - if (this->state == IKE_CONNECTING) + if (this->state == IKE_CONNECTING || + this->state == IKE_PASSIVE) { job_t *job; u_int32_t t; @@ -708,7 +725,7 @@ static void reset(private_ike_sa_t *this) set_state(this, IKE_CREATED); - this->task_manager->reset(this->task_manager); + this->task_manager->reset(this->task_manager, 0, 0); } /** @@ -874,7 +891,7 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->iterate(iterator, (void**)&child_sa)) { - if (child_sa->update_hosts(child_sa, this->my_host, + if (child_sa->update(child_sa, this->my_host, this->other_host, this->my_virtual_ip, has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED) { @@ -1199,11 +1216,17 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) iterator_t *iterator; child_sa_t *current, *child_sa = NULL; - if (this->state == IKE_DELETING) + switch (this->state) { - DBG1(DBG_IKE, "acquiring CHILD_SA {reqid %d} failed: " - "IKE_SA is deleting", reqid); - return FAILED; + case IKE_DELETING: + DBG1(DBG_IKE, "acquiring CHILD_SA {reqid %d} failed: " + "IKE_SA is deleting", reqid); + return FAILED; + case IKE_PASSIVE: + /* do not process acquires if passive */ + return FAILED; + default: + break; } /* find CHILD_SA */ @@ -1265,6 +1288,7 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) case IKE_CREATED: case IKE_CONNECTING: case IKE_ESTABLISHED: + case IKE_PASSIVE: default: break; } @@ -1288,8 +1312,8 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg) my_ts = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, me); other_ts = child_cfg->get_traffic_selectors(child_cfg, FALSE, NULL, other); - status = child_sa->add_policies(child_sa, my_ts, other_ts, - child_cfg->get_mode(child_cfg), PROTO_NONE); + child_sa->set_mode(child_sa, child_cfg->get_mode(child_cfg)); + status = child_sa->add_policies(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)); @@ -1353,6 +1377,11 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) status_t status; bool is_request; + if (this->state == IKE_PASSIVE) + { /* do not handle messages in passive state */ + return FAILED; + } + is_request = message->get_request(message); status = message->parse_body(message, @@ -1366,7 +1395,7 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) switch (status) { case NOT_SUPPORTED: - DBG1(DBG_IKE, "ciritcal unknown payloads found"); + DBG1(DBG_IKE, "critical unknown payloads found"); if (is_request) { send_notify_response(this, message, UNSUPPORTED_CRITICAL_PAYLOAD); @@ -1449,6 +1478,14 @@ static status_t process_message(private_ike_sa_t *this, message_t *message) status = this->task_manager->process_message(this->task_manager, message); if (status != DESTROY_ME) { + if (message->get_exchange_type(message) == IKE_AUTH && + this->state == IKE_ESTABLISHED) + { + /* purge auth items if SA is up, as they contain certs + * and other memory wasting elements */ + this->my_auth->purge(this->my_auth); + this->other_auth->purge(this->other_auth); + } return status; } /* if IKE_SA gets closed for any reasons, reroute routed children */ @@ -1594,37 +1631,27 @@ static iterator_t* create_child_sa_iterator(private_ike_sa_t *this) /** * Implementation of ike_sa_t.rekey_child_sa. */ -static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) +static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, + u_int32_t spi) { - child_sa_t *child_sa; child_rekey_t *child_rekey; - child_sa = get_child_sa(this, protocol, spi, TRUE); - if (child_sa) - { - child_rekey = child_rekey_create(&this->public, child_sa); - this->task_manager->queue_task(this->task_manager, &child_rekey->task); - return this->task_manager->initiate(this->task_manager); - } - return FAILED; + child_rekey = child_rekey_create(&this->public, protocol, spi); + this->task_manager->queue_task(this->task_manager, &child_rekey->task); + return this->task_manager->initiate(this->task_manager); } /** * Implementation of ike_sa_t.delete_child_sa. */ -static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi) +static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, + u_int32_t spi) { - child_sa_t *child_sa; child_delete_t *child_delete; - child_sa = get_child_sa(this, protocol, spi, TRUE); - if (child_sa) - { - child_delete = child_delete_create(&this->public, child_sa); - this->task_manager->queue_task(this->task_manager, &child_delete->task); - return this->task_manager->initiate(this->task_manager); - } - return FAILED; + child_delete = child_delete_create(&this->public, protocol, spi); + this->task_manager->queue_task(this->task_manager, &child_delete->task); + return this->task_manager->initiate(this->task_manager); } /** @@ -1670,6 +1697,8 @@ static status_t delete_(private_ike_sa_t *this) case IKE_CREATED: DBG1(DBG_IKE, "deleting unestablished IKE_SA"); break; + case IKE_PASSIVE: + break; default: DBG1(DBG_IKE, "destroying IKE_SA in state %N " "without notification", ike_sa_state_names, this->state); @@ -1943,6 +1972,7 @@ static status_t roam(private_ike_sa_t *this, bool address) { case IKE_CREATED: case IKE_DELETING: + case IKE_PASSIVE: return SUCCESS; default: break; @@ -2239,7 +2269,7 @@ static void destroy(private_ike_sa_t *this) { charon->attributes->release_address(charon->attributes, this->peer_cfg->get_pool(this->peer_cfg), - this->other_virtual_ip); + this->other_virtual_ip, this->other_id); } this->other_virtual_ip->destroy(this->other_virtual_ip); } @@ -2308,6 +2338,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.set_my_host = (void (*)(ike_sa_t*,host_t*)) set_my_host; this->public.get_other_host = (host_t* (*)(ike_sa_t*)) get_other_host; this->public.set_other_host = (void (*)(ike_sa_t*,host_t*)) set_other_host; + this->public.set_message_id = (void(*)(ike_sa_t*, bool inbound, u_int32_t mid))set_message_id; this->public.update_hosts = (void(*)(ike_sa_t*, host_t *me, host_t *other))update_hosts; this->public.get_my_id = (identification_t* (*)(ike_sa_t*)) get_my_id; this->public.set_my_id = (void (*)(ike_sa_t*,identification_t*)) set_my_id; @@ -2365,6 +2396,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->ike_sa_id = ike_sa_id->clone(ike_sa_id); this->child_sas = linked_list_create(); this->my_host = host_create_any(AF_INET); + this->my_host->set_port(this->my_host, IKEV2_UDP_PORT); this->other_host = host_create_any(AF_INET); this->my_id = identification_create_from_encoding(ID_ANY, chunk_empty); this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty); diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h index 5aa458704..d3976527b 100644 --- a/src/charon/sa/ike_sa.h +++ b/src/charon/sa/ike_sa.h @@ -15,7 +15,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: ike_sa.h 4640 2008-11-12 16:07:17Z martin $ + * $Id: ike_sa.h 4810 2008-12-16 17:21:28Z tobias $ */ /** @@ -200,6 +200,11 @@ enum ike_sa_state_t { IKE_ESTABLISHED, /** + * IKE_SA is managed externally and does not process messages + */ + IKE_PASSIVE, + + /** * IKE_SA rekeying in progress */ IKE_REKEYING, @@ -414,6 +419,17 @@ struct ike_sa_t { void (*set_proposal)(ike_sa_t *this, proposal_t *proposal); /** + * Set the message id of the IKE_SA. + * + * The IKE_SA stores two message IDs, one for initiating exchanges (send) + * and one to respond to exchanges (expect). + * + * @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); + + /** * Add an additional address for the peer. * * In MOBIKE, a peer may transmit additional addresses where it is @@ -641,9 +657,9 @@ struct ike_sa_t { * * @return * - SUCCESS if deletion is initialized - * - INVALID_STATE, if the IKE_SA is not in + * - DESTROY_ME, if the IKE_SA is not in * an established state and can not be - * delete (but destroyed). + * deleted (but destroyed). */ status_t (*delete) (ike_sa_t *this); diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c index a760409c0..447fa2dd5 100644 --- a/src/charon/sa/ike_sa_manager.c +++ b/src/charon/sa/ike_sa_manager.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -13,7 +14,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: ike_sa_manager.c 4579 2008-11-05 11:29:56Z martin $ + * $Id: ike_sa_manager.c 4811 2008-12-17 09:00:22Z martin $ */ #include <string.h> @@ -27,6 +28,15 @@ #include <utils/linked_list.h> #include <crypto/hashers/hasher.h> +/* the default size of the hash table (MUST be a power of 2) */ +#define DEFAULT_HASHTABLE_SIZE 1 + +/* the maximum size of the hash table (MUST be a power of 2) */ +#define MAX_HASHTABLE_SIZE (1 << 30) + +/* the default number of segments (MUST be a power of 2) */ +#define DEFAULT_SEGMENT_COUNT 1 + typedef struct entry_t entry_t; /** @@ -60,7 +70,7 @@ struct entry_t { bool driveout_waiting_threads; /** - * Identifiaction of an IKE_SA (SPIs). + * Identification of an IKE_SA (SPIs). */ ike_sa_id_t *ike_sa_id; @@ -80,6 +90,11 @@ struct entry_t { host_t *other; /** + * As responder: Is this SA half-open? + */ + bool half_open; + + /** * own identity, required for duplicate checking */ identification_t *my_id; @@ -115,7 +130,7 @@ static status_t entry_destroy(entry_t *this) /** * Creates a new entry for the ike_sa_t list. */ -static entry_t *entry_create(ike_sa_id_t *ike_sa_id) +static entry_t *entry_create() { entry_t *this = malloc_thing(entry_t); @@ -129,18 +144,151 @@ static entry_t *entry_create(ike_sa_id_t *ike_sa_id) this->message_id = -1; this->init_hash = chunk_empty; this->other = NULL; + this->half_open = FALSE; this->my_id = NULL; this->other_id = NULL; + this->ike_sa_id = NULL; + this->ike_sa = NULL; - /* ike_sa_id is always cloned */ - this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + return this; +} - /* create new ike_sa */ - this->ike_sa = ike_sa_create(ike_sa_id); +/** + * Function that matches entry_t objects by initiator SPI and the hash of the + * IKE_SA_INIT message. + */ +static bool entry_match_by_hash(entry_t *entry, ike_sa_id_t *id, chunk_t *hash) +{ + return id->get_responder_spi(id) == 0 && + id->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id) && + id->get_initiator_spi(id) == entry->ike_sa_id->get_initiator_spi(entry->ike_sa_id) && + chunk_equals(*hash, entry->init_hash); +} - return this; +/** + * 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) +{ + if (id->equals(id, entry->ike_sa_id)) + { + return TRUE; + } + if ((id->get_responder_spi(id) == 0 || + entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0) && + id->is_initiator(id) == entry->ike_sa_id->is_initiator(entry->ike_sa_id) && + id->get_initiator_spi(id) == entry->ike_sa_id->get_initiator_spi(entry->ike_sa_id)) + { + /* this is TRUE for IKE_SAs that we initiated but have not yet received a response */ + return TRUE; + } + return FALSE; +} + +/** + * 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) +{ + return entry->ike_sa == ike_sa; +} + +/** + * Hash function for ike_sa_id_t objects. + */ +static u_int ike_sa_id_hash(ike_sa_id_t *ike_sa_id) +{ + /* we always use initiator spi as key */ + return ike_sa_id->get_initiator_spi(ike_sa_id); } +typedef struct half_open_t half_open_t; + +/** + * Struct to manage half-open IKE_SAs per peer. + */ +struct half_open_t { + /** chunk of remote host address */ + chunk_t other; + + /** the number of half-open IKE_SAs with that host */ + u_int count; +}; + +/** + * Destroys a half_open_t object. + */ +static void half_open_destroy(half_open_t *this) +{ + chunk_free(&this->other); + free(this); +} + +/** + * Function that matches half_open_t objects by the given IP address chunk. + */ +static bool half_open_match(half_open_t *half_open, chunk_t *addr) +{ + return chunk_equals(*addr, half_open->other); +} + +typedef struct connected_peers_t connected_peers_t; + +struct connected_peers_t { + /** own identity */ + identification_t *my_id; + + /** remote identity */ + identification_t *other_id; + + /** list of ike_sa_id_t objects of IKE_SAs between the two identities */ + linked_list_t *sas; +}; + +static void connected_peers_destroy(connected_peers_t *this) +{ + this->my_id->destroy(this->my_id); + this->other_id->destroy(this->other_id); + this->sas->destroy(this->sas); + free(this); +} + +/** + * Function that matches connected_peers_t objects by the given ids. + */ +static bool connected_peers_match(connected_peers_t *connected_peers, + identification_t *my_id, identification_t *other_id) +{ + return my_id->equals(my_id, connected_peers->my_id) && + other_id->equals(other_id, connected_peers->other_id); +} + +typedef struct segment_t segment_t; + +/** + * Struct to manage segments of the hash table. + */ +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; + +/** + * Struct to manage segments of the "half-open" and "connected peers" hash tables. + */ +struct shareable_segment_t { + /** rwlock to access a segment non-/exclusively */ + rwlock_t *lock; + + /** the number of entries in this segment - in case of the "half-open table" + * it's the sum of all half_open_t.count in a segment. */ + u_int count; +}; typedef struct private_ike_sa_manager_t private_ike_sa_manager_t; @@ -154,14 +302,54 @@ struct private_ike_sa_manager_t { ike_sa_manager_t public; /** - * Lock for exclusivly accessing the manager. + * Hash table with entries for the ike_sa_t objects. */ - mutex_t *mutex; - + linked_list_t **ike_sa_table; + + /** + * The size of the hash table. + */ + u_int table_size; + + /** + * Mask to map the hashes to table rows. + */ + u_int table_mask; + + /** + * Segments of the hash table. + */ + segment_t *segments; + + /** + * The number of segments. + */ + u_int segment_count; + + /** + * Mask to map a table row to a segment. + */ + u_int segment_mask; + + /** + * Hash table with half_open_t objects. + */ + linked_list_t **half_open_table; + /** - * Linked list with entries for the ike_sa_t objects. + * Segments of the "half-open" hash table. */ - linked_list_t *ike_sa_list; + shareable_segment_t *half_open_segments; + + /** + * Hash table with connected_peers_t objects. + */ + linked_list_t **connected_peers_table; + + /** + * Segments of the "connected peers" hash table. + */ + shareable_segment_t *connected_peers_segments; /** * RNG to get random SPIs for our side @@ -180,126 +368,304 @@ struct private_ike_sa_manager_t { }; /** - * Implementation of private_ike_sa_manager_t.get_entry_by_id. + * Acquire a lock to access the segment of the table row with the given index. + * It also works with the segment index directly. */ -static status_t get_entry_by_id(private_ike_sa_manager_t *this, - ike_sa_id_t *ike_sa_id, entry_t **entry) +static void lock_single_segment(private_ike_sa_manager_t *this, u_int index) { - enumerator_t *enumerator; - entry_t *current; - status_t status; + mutex_t *lock = this->segments[index & this->segment_mask].mutex; - /* create enumerator over list of ike_sa's */ - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + lock->lock(lock); +} - /* default status */ - status = NOT_FOUND; +/** + * Release the lock required to access the segment of the table row with the given index. + * It also works with the segment index directly. + */ +static void unlock_single_segment(private_ike_sa_manager_t *this, u_int index) +{ + mutex_t *lock = this->segments[index & this->segment_mask].mutex; - while (enumerator->enumerate(enumerator, ¤t)) + lock->unlock(lock); +} + +/** + * Lock all segments + */ +static void lock_all_segments(private_ike_sa_manager_t *this) +{ + u_int i; + + for (i = 0; i < this->segment_count; ++i) { - if (current->ike_sa_id->equals(current->ike_sa_id, ike_sa_id)) - { - DBG2(DBG_MGR, "found entry by both SPIs"); - *entry = current; - status = SUCCESS; - break; - } - if (ike_sa_id->get_responder_spi(ike_sa_id) == 0 || - current->ike_sa_id->get_responder_spi(current->ike_sa_id) == 0) - { - /* seems to be a half ready ike_sa */ - if ((current->ike_sa_id->get_initiator_spi(current->ike_sa_id) == - ike_sa_id->get_initiator_spi(ike_sa_id)) && - (current->ike_sa_id->is_initiator(ike_sa_id) == - ike_sa_id->is_initiator(current->ike_sa_id))) - { - DBG2(DBG_MGR, "found entry by initiator SPI"); - *entry = current; - status = SUCCESS; - break; - } - } + this->segments[i].mutex->lock(this->segments[i].mutex); } - - enumerator->destroy(enumerator); - return status; } /** - * Implementation of private_ike_sa_manager_t.get_entry_by_sa. + * Unlock all segments */ -static status_t get_entry_by_sa(private_ike_sa_manager_t *this, - ike_sa_t *ike_sa, entry_t **entry) +static void unlock_all_segments(private_ike_sa_manager_t *this) { - enumerator_t *enumerator; - entry_t *current; - status_t status; + u_int i; + + for (i = 0; i < this->segment_count; ++i) + { + this->segments[i].mutex->unlock(this->segments[i].mutex); + } +} + +typedef struct private_enumerator_t private_enumerator_t; + +/** + * hash table enumerator implementation + */ +struct private_enumerator_t { + + /** + * implements enumerator interface + */ + enumerator_t enumerator; + + /** + * associated ike_sa_manager_t + */ + private_ike_sa_manager_t *manager; + + /** + * current segment index + */ + u_int segment; - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + /** + * currently enumerating entry + */ + entry_t *entry; - /* default status */ - status = NOT_FOUND; + /** + * current table row index + */ + u_int row; - while (enumerator->enumerate(enumerator, ¤t)) + /** + * enumerator for the current table row + */ + enumerator_t *current; +}; + +/** + * Implementation of private_enumerator_t.enumerator.enumerate. + */ +static bool enumerate(private_enumerator_t *this, entry_t **entry, u_int *segment) +{ + if (this->entry) + { + this->entry->condvar->signal(this->entry->condvar); + this->entry = NULL; + } + while (this->segment < this->manager->segment_count) { - /* only pointers are compared */ - if (current->ike_sa == ike_sa) + while (this->row < this->manager->table_size) { - DBG2(DBG_MGR, "found entry by pointer"); - *entry = current; - status = SUCCESS; - break; + if (this->current) + { + entry_t *item; + + if (this->current->enumerate(this->current, &item)) + { + *entry = this->entry = item; + *segment = this->segment; + return TRUE; + } + this->current->destroy(this->current); + this->current = NULL; + unlock_single_segment(this->manager, this->segment); + } + else + { + linked_list_t *list; + + lock_single_segment(this->manager, this->segment); + if ((list = this->manager->ike_sa_table[this->row]) != NULL && + list->get_count(list)) + { + this->current = list->create_enumerator(list); + continue; + } + unlock_single_segment(this->manager, this->segment); + } + this->row += this->manager->segment_count; } + this->segment++; + this->row = this->segment; } - enumerator->destroy(enumerator); + return FALSE; +} + +/** + * Implementation of private_enumerator_t.enumerator.destroy. + */ +static void enumerator_destroy(private_enumerator_t *this) +{ + if (this->entry) + { + this->entry->condvar->signal(this->entry->condvar); + } + if (this->current) + { + this->current->destroy(this->current); + unlock_single_segment(this->manager, this->segment); + } + free(this); +} + +/** + * Creates an enumerator to enumerate the entries in the hash table. + */ +static enumerator_t* create_table_enumerator(private_ike_sa_manager_t *this) +{ + private_enumerator_t *enumerator = malloc_thing(private_enumerator_t); + + enumerator->enumerator.enumerate = (void*)enumerate; + enumerator->enumerator.destroy = (void*)enumerator_destroy; + enumerator->manager = this; + enumerator->segment = 0; + enumerator->entry = NULL; + enumerator->row = 0; + enumerator->current = NULL; - return status; + return &enumerator->enumerator; } /** - * Implementation of private_ike_sa_manager_s.delete_entry. + * Put an entry into the hash table. + * Note: The caller has to unlock the returned segment. */ -static status_t delete_entry(private_ike_sa_manager_t *this, entry_t *entry) +static u_int put_entry(private_ike_sa_manager_t *this, entry_t *entry) { - enumerator_t *enumerator; - entry_t *current; - status_t status; + linked_list_t *list; + u_int row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask; + u_int segment = row & this->segment_mask; - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); + lock_single_segment(this, segment); + if ((list = this->ike_sa_table[row]) == NULL) + { + list = this->ike_sa_table[row] = linked_list_create(); + } + list->insert_last(list, entry); + this->segments[segment].count++; + return segment; +} - status = NOT_FOUND; +/** + * Remove an entry from the hash table. + * Note: The caller MUST have a lock on the segment of this entry. + */ +static void remove_entry(private_ike_sa_manager_t *this, entry_t *entry) +{ + linked_list_t *list; + u_int row = ike_sa_id_hash(entry->ike_sa_id) & this->table_mask; + u_int segment = row & this->segment_mask; - while (enumerator->enumerate(enumerator, ¤t)) + if ((list = this->ike_sa_table[row]) != NULL) { - if (current == entry) + entry_t *current; + + enumerator_t *enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, ¤t)) { - /* mark it, so now new threads can get this entry */ - entry->driveout_new_threads = TRUE; - /* wait until all workers have done their work */ - while (entry->waiting_threads) + if (current == entry) { - /* wake up all */ - entry->condvar->broadcast(entry->condvar); - /* they will wake us again when their work is done */ - entry->condvar->wait(entry->condvar, this->mutex); + list->remove_at(list, enumerator); + this->segments[segment].count--; + break; } - - DBG2(DBG_MGR, "found entry by pointer, deleting it"); - this->ike_sa_list->remove_at(this->ike_sa_list, enumerator); - entry_destroy(entry); - status = SUCCESS; - break; } + enumerator->destroy(enumerator); } - enumerator->destroy(enumerator); - return status; +} + +/** + * Remove the entry at the current enumerator position. + */ +static void remove_entry_at(private_enumerator_t *this) +{ + this->entry = NULL; + if (this->current) + { + linked_list_t *list = this->manager->ike_sa_table[this->row]; + list->remove_at(list, this->current); + this->manager->segments[this->segment].count--; + } +} + +/** + * Find an entry using the provided match function to compare the entries for + * equality. + */ +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 *p1, void *p2) +{ + entry_t *current; + linked_list_t *list; + u_int row = ike_sa_id_hash(ike_sa_id) & this->table_mask; + u_int seg = row & this->segment_mask; + + lock_single_segment(this, seg); + if ((list = this->ike_sa_table[row]) != NULL) + { + if (list->find_first(list, match, (void**)¤t, p1, p2) == SUCCESS) + { + *entry = current; + *segment = seg; + /* the locked segment has to be unlocked by the caller */ + return SUCCESS; + } + } + unlock_single_segment(this, seg); + return NOT_FOUND; +} + +/** + * Find an entry by ike_sa_id_t. + * Note: On SUCCESS, the caller has to unlock the segment. + */ +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, NULL); +} + +/** + * Find an entry by initiator SPI and IKE_SA_INIT hash. + * Note: On SUCCESS, the caller has to unlock the segment. + */ +static status_t get_entry_by_hash(private_ike_sa_manager_t *this, + ike_sa_id_t *ike_sa_id, chunk_t hash, 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_hash, ike_sa_id, &hash); +} + +/** + * Find an entry by IKE_SA pointer. + * Note: On SUCCESS, the caller has to unlock the segment. + */ +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, NULL); } /** * Wait until no other thread is using an IKE_SA, return FALSE if entry not - * acquireable + * acquirable. */ -static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry) +static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry, + u_int segment) { if (entry->driveout_new_threads) { @@ -311,7 +677,7 @@ static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry) /* so wait until we can get it for us. * we register us as waiting. */ entry->waiting_threads++; - entry->condvar->wait(entry->condvar, this->mutex); + entry->condvar->wait(entry->condvar, this->segments[segment].mutex); entry->waiting_threads--; } /* hm, a deletion request forbids us to get this SA, get next one */ @@ -325,6 +691,176 @@ static bool wait_for_entry(private_ike_sa_manager_t *this, entry_t *entry) } /** + * Put a half-open SA into the hash table. + */ +static void put_half_open(private_ike_sa_manager_t *this, entry_t *entry) +{ + half_open_t *half_open = NULL; + linked_list_t *list; + chunk_t addr = entry->other->get_address(entry->other); + u_int row = chunk_hash(addr) & this->table_mask; + u_int segment = row & this->segment_mask; + + rwlock_t *lock = this->half_open_segments[segment].lock; + lock->write_lock(lock); + if ((list = this->half_open_table[row]) == NULL) + { + list = this->half_open_table[row] = linked_list_create(); + } + else + { + half_open_t *current; + if (list->find_first(list, (linked_list_match_t)half_open_match, + (void**)¤t, &addr) == SUCCESS) + { + half_open = current; + half_open->count++; + this->half_open_segments[segment].count++; + } + } + + if (!half_open) + { + half_open = malloc_thing(half_open_t); + half_open->other = chunk_clone(addr); + half_open->count = 1; + list->insert_last(list, half_open); + this->half_open_segments[segment].count++; + } + lock->unlock(lock); +} + +/** + * Remove a half-open SA from the hash table. + */ +static void remove_half_open(private_ike_sa_manager_t *this, entry_t *entry) +{ + linked_list_t *list; + chunk_t addr = entry->other->get_address(entry->other); + u_int row = chunk_hash(addr) & this->table_mask; + u_int segment = row & this->segment_mask; + + rwlock_t *lock = this->half_open_segments[segment].lock; + lock->write_lock(lock); + if ((list = this->half_open_table[row]) != NULL) + { + half_open_t *current; + enumerator_t *enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (half_open_match(current, &addr)) + { + if (--current->count == 0) + { + list->remove_at(list, enumerator); + half_open_destroy(current); + } + this->half_open_segments[segment].count--; + break; + } + } + enumerator->destroy(enumerator); + } + lock->unlock(lock); +} + +/** + * Put an SA between two peers into the hash table. + */ +static void put_connected_peers(private_ike_sa_manager_t *this, entry_t *entry) +{ + linked_list_t *list; + connected_peers_t *connected_peers = NULL; + chunk_t my_id = entry->my_id->get_encoding(entry->my_id), + other_id = entry->other_id->get_encoding(entry->other_id); + u_int row = chunk_hash_inc(other_id, chunk_hash(my_id)) & this->table_mask; + u_int segment = row & this->segment_mask; + + rwlock_t *lock = this->connected_peers_segments[segment].lock; + lock->write_lock(lock); + if ((list = this->connected_peers_table[row]) == NULL) + { + list = this->connected_peers_table[row] = linked_list_create(); + } + else + { + connected_peers_t *current; + if (list->find_first(list, (linked_list_match_t)connected_peers_match, + (void**)¤t, entry->my_id, entry->other_id) == SUCCESS) + { + connected_peers = current; + if (connected_peers->sas->find_first(connected_peers->sas, + (linked_list_match_t)entry->ike_sa_id->equals, + NULL, entry->ike_sa_id) == SUCCESS) + { + lock->unlock(lock); + return; + } + } + } + + if (!connected_peers) + { + connected_peers = malloc_thing(connected_peers_t); + connected_peers->my_id = entry->my_id->clone(entry->my_id); + connected_peers->other_id = entry->other_id->clone(entry->other_id); + connected_peers->sas = linked_list_create(); + list->insert_last(list, connected_peers); + } + connected_peers->sas->insert_last(connected_peers->sas, + entry->ike_sa_id->clone(entry->ike_sa_id)); + this->connected_peers_segments[segment].count++; + lock->unlock(lock); +} + +/** + * Remove an SA between two peers from the hash table. + */ +static void remove_connected_peers(private_ike_sa_manager_t *this, entry_t *entry) +{ + linked_list_t *list; + chunk_t my_id = entry->my_id->get_encoding(entry->my_id), + other_id = entry->other_id->get_encoding(entry->other_id); + u_int row = chunk_hash_inc(other_id, chunk_hash(my_id)) & this->table_mask; + u_int segment = row & this->segment_mask; + + rwlock_t *lock = this->connected_peers_segments[segment].lock; + lock->write_lock(lock); + if ((list = this->connected_peers_table[row]) != NULL) + { + connected_peers_t *current; + enumerator_t *enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (connected_peers_match(current, entry->my_id, entry->other_id)) + { + ike_sa_id_t *ike_sa_id; + enumerator_t *inner = current->sas->create_enumerator(current->sas); + while (inner->enumerate(inner, &ike_sa_id)) + { + if (ike_sa_id->equals(ike_sa_id, entry->ike_sa_id)) + { + current->sas->remove_at(current->sas, inner); + ike_sa_id->destroy(ike_sa_id); + this->connected_peers_segments[segment].count--; + break; + } + } + inner->destroy(inner); + if (current->sas->get_count(current->sas) == 0) + { + list->remove_at(list, enumerator); + connected_peers_destroy(current); + } + break; + } + } + enumerator->destroy(enumerator); + } + lock->unlock(lock); +} + +/** * Implementation of private_ike_sa_manager_t.get_next_spi. */ static u_int64_t get_next_spi(private_ike_sa_manager_t *this) @@ -342,21 +878,20 @@ static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id { ike_sa_t *ike_sa = NULL; entry_t *entry; + u_int segment; - DBG2(DBG_MGR, "checkout IKE_SA, %d IKE_SAs in manager", - this->ike_sa_list->get_count(this->ike_sa_list)); + DBG2(DBG_MGR, "checkout IKE_SA"); - this->mutex->lock(this->mutex); - if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS) + if (get_entry_by_id(this, ike_sa_id, &entry, &segment) == SUCCESS) { - if (wait_for_entry(this, entry)) + if (wait_for_entry(this, entry, segment)) { DBG2(DBG_MGR, "IKE_SA successfully checked out"); entry->checked_out = TRUE; ike_sa = entry->ike_sa; } + unlock_single_segment(this, segment); } - this->mutex->unlock(this->mutex); charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; } @@ -367,24 +902,24 @@ static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id static ike_sa_t *checkout_new(private_ike_sa_manager_t* this, bool initiator) { entry_t *entry; - ike_sa_id_t *id; + u_int segment; + entry = entry_create(); if (initiator) { - id = ike_sa_id_create(get_next_spi(this), 0, TRUE); + entry->ike_sa_id = ike_sa_id_create(get_next_spi(this), 0, TRUE); } else { - id = ike_sa_id_create(0, get_next_spi(this), FALSE); + entry->ike_sa_id = ike_sa_id_create(0, get_next_spi(this), FALSE); } - entry = entry_create(id); - id->destroy(id); - this->mutex->lock(this->mutex); - this->ike_sa_list->insert_last(this->ike_sa_list, entry); + entry->ike_sa = ike_sa_create(entry->ike_sa_id); + + segment = put_entry(this, entry); entry->checked_out = TRUE; - this->mutex->unlock(this->mutex); - DBG2(DBG_MGR, "created IKE_SA, %d IKE_SAs in manager", - this->ike_sa_list->get_count(this->ike_sa_list)); + unlock_single_segment(this, segment); + + DBG2(DBG_MGR, "created IKE_SA"); return entry->ike_sa; } @@ -394,53 +929,45 @@ static ike_sa_t *checkout_new(private_ike_sa_manager_t* this, bool initiator) static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, message_t *message) { + u_int segment; entry_t *entry; ike_sa_t *ike_sa = NULL; ike_sa_id_t *id = message->get_ike_sa_id(message); + id = id->clone(id); id->switch_initiator(id); - DBG2(DBG_MGR, "checkout IKE_SA by message, %d IKE_SAs in manager", - this->ike_sa_list->get_count(this->ike_sa_list)); + DBG2(DBG_MGR, "checkout IKE_SA by message"); if (message->get_request(message) && message->get_exchange_type(message) == IKE_SA_INIT) { /* IKE_SA_INIT request. Check for an IKE_SA with such a message hash. */ - enumerator_t *enumerator; chunk_t data, hash; - + data = message->get_packet_data(message); this->hasher->allocate_hash(this->hasher, data, &hash); chunk_free(&data); - this->mutex->lock(this->mutex); - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); - while (enumerator->enumerate(enumerator, &entry)) + if (get_entry_by_hash(this, id, hash, &entry, &segment) == SUCCESS) { - if (chunk_equals(hash, entry->init_hash)) + if (entry->message_id == 0) { - if (entry->message_id == 0) - { - enumerator->destroy(enumerator); - this->mutex->unlock(this->mutex); - chunk_free(&hash); - id->destroy(id); - DBG1(DBG_MGR, "ignoring IKE_SA_INIT, already processing"); - return NULL; - } - else if (wait_for_entry(this, entry)) - { - DBG2(DBG_MGR, "IKE_SA checked out by hash"); - entry->checked_out = TRUE; - entry->message_id = message->get_message_id(message); - ike_sa = entry->ike_sa; - } - break; + unlock_single_segment(this, segment); + chunk_free(&hash); + id->destroy(id); + DBG1(DBG_MGR, "ignoring IKE_SA_INIT, already processing"); + return NULL; + } + else if (wait_for_entry(this, entry, segment)) + { + DBG2(DBG_MGR, "IKE_SA checked out by hash"); + entry->checked_out = TRUE; + entry->message_id = message->get_message_id(message); + ike_sa = entry->ike_sa; } + unlock_single_segment(this, segment); } - enumerator->destroy(enumerator); - this->mutex->unlock(this->mutex); if (ike_sa == NULL) { @@ -449,15 +976,19 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, { /* no IKE_SA found, create a new one */ id->set_responder_spi(id, get_next_spi(this)); - entry = entry_create(id); + entry = entry_create(); + entry->ike_sa = ike_sa_create(id); + entry->ike_sa_id = id->clone(id); - this->mutex->lock(this->mutex); - this->ike_sa_list->insert_last(this->ike_sa_list, entry); + segment = put_entry(this, entry); entry->checked_out = TRUE; - entry->message_id = message->get_message_id(message); - this->mutex->unlock(this->mutex); + unlock_single_segment(this, segment); + + entry->message_id = message->get_message_id(message); entry->init_hash = hash; ike_sa = entry->ike_sa; + + DBG2(DBG_MGR, "created IKE_SA"); } else { @@ -474,8 +1005,7 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, return ike_sa; } - this->mutex->lock(this->mutex); - if (get_entry_by_id(this, id, &entry) == SUCCESS) + if (get_entry_by_id(this, id, &entry, &segment) == SUCCESS) { /* only check out if we are not processing this request */ if (message->get_request(message) && @@ -484,7 +1014,7 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, DBG1(DBG_MGR, "ignoring request with ID %d, already processing", entry->message_id); } - else if (wait_for_entry(this, entry)) + else if (wait_for_entry(this, entry, segment)) { ike_sa_id_t *ike_id = entry->ike_sa->get_id(entry->ike_sa); DBG2(DBG_MGR, "IKE_SA successfully checked out"); @@ -496,8 +1026,8 @@ static ike_sa_t* checkout_by_message(private_ike_sa_manager_t* this, } ike_sa = entry->ike_sa; } + unlock_single_segment(this, segment); } - this->mutex->unlock(this->mutex); id->destroy(id); charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; @@ -515,6 +1045,14 @@ static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this, identification_t *my_id, *other_id; host_t *my_host, *other_host; ike_cfg_t *ike_cfg; + u_int segment; + + if (!this->reuse_ikesa) + { /* IKE_SA reuse disable by config */ + ike_sa = checkout_new(this, TRUE); + charon->bus->set_sa(charon->bus, ike_sa); + return ike_sa; + } ike_cfg = peer_cfg->get_ike_cfg(peer_cfg); my_id = peer_cfg->get_my_id(peer_cfg); @@ -522,24 +1060,22 @@ static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this, my_host = host_create_from_dns(ike_cfg->get_my_addr(ike_cfg), 0, 0); other_host = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg), 0, 0); - this->mutex->lock(this->mutex); - - if (my_host && other_host && this->reuse_ikesa) + if (my_host && other_host) { - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); - while (enumerator->enumerate(enumerator, &entry)) + enumerator = create_table_enumerator(this); + while (enumerator->enumerate(enumerator, &entry, &segment)) { identification_t *found_my_id, *found_other_id; host_t *found_my_host, *found_other_host; - if (!wait_for_entry(this, entry)) + if (!wait_for_entry(this, entry, segment)) { continue; } if (entry->ike_sa->get_state(entry->ike_sa) == IKE_DELETING) { - /* skip IKE_SA which are not useable */ + /* skip IKE_SAs which are not usable */ continue; } @@ -584,28 +1120,9 @@ static ike_sa_t* checkout_by_config(private_ike_sa_manager_t *this, DESTROY_IF(other_host); if (!ike_sa) - { - u_int64_t initiator_spi; - entry_t *new_entry; - ike_sa_id_t *new_ike_sa_id; - - initiator_spi = get_next_spi(this); - new_ike_sa_id = ike_sa_id_create(0, 0, TRUE); - new_ike_sa_id->set_initiator_spi(new_ike_sa_id, initiator_spi); - - /* create entry */ - new_entry = entry_create(new_ike_sa_id); - DBG2(DBG_MGR, "created IKE_SA"); - new_ike_sa_id->destroy(new_ike_sa_id); - - this->ike_sa_list->insert_last(this->ike_sa_list, new_entry); - - /* check ike_sa out */ - DBG2(DBG_MGR, "new IKE_SA created for IDs [%D]...[%D]", my_id, other_id); - new_entry->checked_out = TRUE; - ike_sa = new_entry->ike_sa; + { /* no IKE_SA using such a config, hand out a new */ + ike_sa = checkout_new(this, TRUE); } - this->mutex->unlock(this->mutex); charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; } @@ -621,13 +1138,12 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, entry_t *entry; ike_sa_t *ike_sa = NULL; child_sa_t *child_sa; + u_int segment; - this->mutex->lock(this->mutex); - - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); - while (enumerator->enumerate(enumerator, &entry)) + enumerator = create_table_enumerator(this); + while (enumerator->enumerate(enumerator, &entry, &segment)) { - if (wait_for_entry(this, entry)) + if (wait_for_entry(this, entry, segment)) { /* look for a child with such a reqid ... */ if (child) @@ -659,7 +1175,6 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id, } } enumerator->destroy(enumerator); - this->mutex->unlock(this->mutex); charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; @@ -676,13 +1191,12 @@ static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, entry_t *entry; ike_sa_t *ike_sa = NULL; child_sa_t *child_sa; + u_int segment; - this->mutex->lock(this->mutex); - - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); - while (enumerator->enumerate(enumerator, &entry)) + enumerator = create_table_enumerator(this); + while (enumerator->enumerate(enumerator, &entry, &segment)) { - if (wait_for_entry(this, entry)) + if (wait_for_entry(this, entry, segment)) { /* look for a child with such a policy name ... */ if (child) @@ -714,68 +1228,18 @@ static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name, } } enumerator->destroy(enumerator); - this->mutex->unlock(this->mutex); charon->bus->set_sa(charon->bus, ike_sa); return ike_sa; } - -/** - * Implementation of ike_sa_manager_t.checkout_duplicate. - */ -static ike_sa_t* checkout_duplicate(private_ike_sa_manager_t *this, - ike_sa_t *ike_sa) -{ - enumerator_t *enumerator; - entry_t *entry; - ike_sa_t *duplicate = NULL; - identification_t *me, *other; - - me = ike_sa->get_my_id(ike_sa); - other = ike_sa->get_other_id(ike_sa); - - this->mutex->lock(this->mutex); - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); - while (enumerator->enumerate(enumerator, &entry)) - { - if (entry->ike_sa == ike_sa) - { /* self is not a duplicate */ - continue; - } - if (entry->my_id && me->equals(me, entry->my_id) && - entry->other_id && other->equals(other, entry->other_id)) - { - /* we are sure that the other entry is not calling - * checkout_duplicate here, as the identities in entry would not - * have been set yet. Otherwise we would risk a deadlock. */ - if (wait_for_entry(this, entry)) - { - duplicate = entry->ike_sa; - entry->checked_out = TRUE; - break; - } - } - } - enumerator->destroy(enumerator); - this->mutex->unlock(this->mutex); - return duplicate; -} - -/** - * enumerator cleanup function - */ -static void enumerator_unlock(private_ike_sa_manager_t *this) -{ - this->mutex->unlock(this->mutex); -} /** * enumerator filter function */ static bool enumerator_filter(private_ike_sa_manager_t *this, - entry_t **in, ike_sa_t **out) + entry_t **in, ike_sa_t **out, u_int *segment) { - if (wait_for_entry(this, *in)) + if (wait_for_entry(this, *in, *segment)) { *out = (*in)->ike_sa; return TRUE; @@ -784,126 +1248,266 @@ static bool enumerator_filter(private_ike_sa_manager_t *this, } /** - * Implementation of ike_sa_manager_t.create_iterator. + * Implementation of ike_sa_manager_t.create_enumerator. */ static enumerator_t *create_enumerator(private_ike_sa_manager_t* this) { - this->mutex->lock(this->mutex); return enumerator_create_filter( - this->ike_sa_list->create_enumerator(this->ike_sa_list), - (void*)enumerator_filter, this, (void*)enumerator_unlock); + create_table_enumerator(this), + (void*)enumerator_filter, this, NULL); } /** * Implementation of ike_sa_manager_t.checkin. */ -static status_t checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) +static void checkin(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) { /* to check the SA back in, we look for the pointer of the ike_sa * in all entries. - * We can't search by SPI's since the MAY have changed (e.g. on reception - * of a IKE_SA_INIT response). Updating of the SPI MAY be necessary... + * The lookup is done by initiator SPI, so even if the SPI has changed (e.g. + * on reception of a IKE_SA_INIT response) the lookup will work but + * updating of the SPI MAY be necessary... */ - status_t retval; entry_t *entry; ike_sa_id_t *ike_sa_id; host_t *other; identification_t *my_id, *other_id; + u_int segment; ike_sa_id = ike_sa->get_id(ike_sa); + my_id = ike_sa->get_my_id(ike_sa); + other_id = ike_sa->get_other_id(ike_sa); + other = ike_sa->get_other_host(ike_sa); DBG2(DBG_MGR, "checkin IKE_SA"); - this->mutex->lock(this->mutex); - /* look for the entry */ - if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) + if (get_entry_by_sa(this, ike_sa_id, ike_sa, &entry, &segment) == SUCCESS) { /* ike_sa_id must be updated */ entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa)); /* signal waiting threads */ entry->checked_out = FALSE; entry->message_id = -1; - /* apply remote address for DoS detection */ - other = ike_sa->get_other_host(ike_sa); - if (!entry->other || !other->equals(other, entry->other)) + /* check if this SA is half-open */ + if (entry->half_open && ike_sa->get_state(ike_sa) != IKE_CONNECTING) { - DESTROY_IF(entry->other); - entry->other = other->clone(other); + /* not half open anymore */ + entry->half_open = FALSE; + remove_half_open(this, entry); } - /* apply identities for diplicate test */ - my_id = ike_sa->get_my_id(ike_sa); - other_id = ike_sa->get_other_id(ike_sa); - if (!entry->my_id || - entry->my_id->get_type(entry->my_id) == ID_ANY) + else if (entry->half_open && !other->ip_equals(other, entry->other)) { - DESTROY_IF(entry->my_id); - entry->my_id = my_id->clone(my_id); + /* the other host's IP has changed, we must update the hash table */ + remove_half_open(this, entry); + DESTROY_IF(entry->other); + entry->other = other->clone(other); + put_half_open(this, entry); } - if (!entry->other_id || - entry->other_id->get_type(entry->other_id) == ID_ANY) + else if (!entry->half_open && + !entry->ike_sa_id->is_initiator(entry->ike_sa_id) && + ike_sa->get_state(ike_sa) == IKE_CONNECTING) { - DESTROY_IF(entry->other_id); - entry->other_id = other_id->clone(other_id); + /* this is a new half-open SA */ + entry->half_open = TRUE; + entry->other = other->clone(other); + put_half_open(this, entry); } DBG2(DBG_MGR, "check-in of IKE_SA successful."); entry->condvar->signal(entry->condvar); - retval = SUCCESS; } else { - DBG2(DBG_MGR, "tried to check in nonexisting IKE_SA"); - /* this SA is no more, this REALLY should not happen */ - retval = NOT_FOUND; + entry = entry_create(); + entry->ike_sa_id = ike_sa_id->clone(ike_sa_id); + entry->ike_sa = ike_sa; + segment = put_entry(this, entry); + } + + /* apply identities for duplicate test (only as responder) */ + if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) && + (!entry->my_id || !entry->other_id)) + { + if (!entry->my_id && my_id->get_type(my_id) != ID_ANY) + { + entry->my_id = my_id->clone(my_id); + } + if (!entry->other_id && other_id->get_type(other_id) != ID_ANY) + { + entry->other_id = other_id->clone(other_id); + } + if (entry->my_id && entry->other_id) + { + put_connected_peers(this, entry); + } } - DBG2(DBG_MGR, "%d IKE_SAs in manager now", - this->ike_sa_list->get_count(this->ike_sa_list)); - this->mutex->unlock(this->mutex); + unlock_single_segment(this, segment); charon->bus->set_sa(charon->bus, NULL); - return retval; } - /** * Implementation of ike_sa_manager_t.checkin_and_destroy. */ -static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) +static void checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) { - /* deletion is a bit complex, we must garant that no thread is waiting for + /* deletion is a bit complex, we must ensure that no thread is waiting for * this SA. - * We take this SA from the list, and start signaling while threads + * We take this SA from the table, and start signaling while threads * are in the condvar. */ entry_t *entry; - status_t retval; ike_sa_id_t *ike_sa_id; + u_int segment; ike_sa_id = ike_sa->get_id(ike_sa); + DBG2(DBG_MGR, "checkin and destroy IKE_SA"); - - this->mutex->lock(this->mutex); - - if (get_entry_by_sa(this, ike_sa, &entry) == SUCCESS) + + if (get_entry_by_sa(this, ike_sa_id, ike_sa, &entry, &segment) == SUCCESS) { /* drive out waiting threads, as we are in hurry */ entry->driveout_waiting_threads = TRUE; + /* mark it, so no new threads can get this entry */ + entry->driveout_new_threads = TRUE; + /* wait until all workers have done their work */ + while (entry->waiting_threads) + { + /* wake up all */ + entry->condvar->broadcast(entry->condvar); + /* they will wake us again when their work is done */ + entry->condvar->wait(entry->condvar, this->segments[segment].mutex); + } + remove_entry(this, entry); + unlock_single_segment(this, segment); + + if (entry->half_open) + { + remove_half_open(this, entry); + } + if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) && + entry->my_id && entry->other_id) + { + remove_connected_peers(this, entry); + } - delete_entry(this, entry); + entry_destroy(entry); DBG2(DBG_MGR, "check-in and destroy of IKE_SA successful"); - retval = SUCCESS; } else { - DBG2(DBG_MGR, "tried to check-in and delete nonexisting IKE_SA"); - retval = NOT_FOUND; + DBG1(DBG_MGR, "tried to check-in and delete nonexisting IKE_SA"); + ike_sa->destroy(ike_sa); } charon->bus->set_sa(charon->bus, NULL); +} + + +/** + * Implementation of ike_sa_manager_t.check_uniqueness. + */ +static bool check_uniqueness(private_ike_sa_manager_t *this, ike_sa_t *ike_sa) +{ + bool cancel = FALSE; + peer_cfg_t *peer_cfg; + unique_policy_t policy; + linked_list_t *list, *duplicate_ids = NULL; + enumerator_t *enumerator; + ike_sa_id_t *duplicate_id = NULL; + identification_t *me, *other; + u_int row, segment; + rwlock_t *lock; + + peer_cfg = ike_sa->get_peer_cfg(ike_sa); + policy = peer_cfg->get_unique_policy(peer_cfg); + if (policy == UNIQUE_NO) + { + return FALSE; + } + + me = ike_sa->get_my_id(ike_sa); + other = ike_sa->get_other_id(ike_sa); + + row = chunk_hash_inc(other->get_encoding(other), + chunk_hash(me->get_encoding(me))) & this->table_mask; + segment = row & this->segment_mask; + + lock = this->connected_peers_segments[segment & this->segment_mask].lock; + lock->read_lock(lock); + if ((list = this->connected_peers_table[row]) != NULL) + { + connected_peers_t *current; + + if (list->find_first(list, (linked_list_match_t)connected_peers_match, + (void**)¤t, me, other) == SUCCESS) + { + /* clone the list, so we can release the lock */ + duplicate_ids = current->sas->clone_offset(current->sas, + offsetof(ike_sa_id_t, clone)); + } + } + lock->unlock(lock); + + if (!duplicate_ids) + { + return FALSE; + } - this->mutex->unlock(this->mutex); - return retval; + enumerator = duplicate_ids->create_enumerator(duplicate_ids); + while (enumerator->enumerate(enumerator, &duplicate_id)) + { + status_t status = SUCCESS; + ike_sa_t *duplicate; + + duplicate = checkout(this, duplicate_id); + if (!duplicate) + { + continue; + } + peer_cfg = duplicate->get_peer_cfg(duplicate); + if (peer_cfg && peer_cfg->equals(peer_cfg, ike_sa->get_peer_cfg(ike_sa))) + { + switch (duplicate->get_state(duplicate)) + { + case IKE_ESTABLISHED: + case IKE_REKEYING: + switch (policy) + { + case UNIQUE_REPLACE: + DBG1(DBG_IKE, "deleting duplicate IKE_SA due" + " uniqueness policy"); + status = duplicate->delete(duplicate); + break; + case UNIQUE_KEEP: + cancel = TRUE; + /* we keep the first IKE_SA and delete all + * other duplicates that might exist */ + policy = UNIQUE_REPLACE; + break; + default: + break; + } + break; + default: + break; + } + } + if (status == DESTROY_ME) + { + checkin_and_destroy(this, duplicate); + } + else + { + checkin(this, duplicate); + } + } + enumerator->destroy(enumerator); + duplicate_ids->destroy_offset(duplicate_ids, offsetof(ike_sa_id_t, destroy)); + /* reset thread's current IKE_SA after checkin */ + charon->bus->set_sa(charon->bus, ike_sa); + return cancel; } /** @@ -911,35 +1515,43 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik */ static int get_half_open_count(private_ike_sa_manager_t *this, host_t *ip) { - enumerator_t *enumerator; - entry_t *entry; int count = 0; - this->mutex->lock(this->mutex); - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); - while (enumerator->enumerate(enumerator, &entry)) + if (ip) { - /* we check if we have a responder CONNECTING IKE_SA without checkout */ - if (!entry->ike_sa_id->is_initiator(entry->ike_sa_id) && - entry->ike_sa->get_state(entry->ike_sa) == IKE_CONNECTING) + linked_list_t *list; + chunk_t addr = ip->get_address(ip); + u_int row = chunk_hash(addr) & this->table_mask; + u_int segment = row & this->segment_mask; + + rwlock_t *lock = this->half_open_segments[segment & this->segment_mask].lock; + lock->read_lock(lock); + if ((list = this->half_open_table[row]) != NULL) { - /* if we have a host, count only matching IKE_SAs */ - if (ip) - { - if (entry->other && ip->ip_equals(ip, entry->other)) - { - count++; - } - } - else + half_open_t *current; + + if (list->find_first(list, (linked_list_match_t)half_open_match, + (void**)¤t, &addr) == SUCCESS) { - count++; + count = current->count; } } + lock->unlock(lock); + } + else + { + u_int segment; + + for (segment = 0; segment < this->segment_count; ++segment) + { + rwlock_t *lock; + lock = this->half_open_segments[segment & this->segment_mask].lock; + lock->read_lock(lock); + count += this->half_open_segments[segment].count; + lock->unlock(lock); + } } - enumerator->destroy(enumerator); - this->mutex->unlock(this->mutex); return count; } @@ -951,13 +1563,14 @@ static void flush(private_ike_sa_manager_t *this) /* destroy all list entries */ enumerator_t *enumerator; entry_t *entry; + u_int segment; - this->mutex->lock(this->mutex); + lock_all_segments(this); DBG2(DBG_MGR, "going to destroy IKE_SA manager and all managed IKE_SA's"); /* Step 1: drive out all waiting threads */ DBG2(DBG_MGR, "set driveout flags for all stored IKE_SA's"); - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); - while (enumerator->enumerate(enumerator, &entry)) + enumerator = create_table_enumerator(this); + while (enumerator->enumerate(enumerator, &entry, &segment)) { /* do not accept new threads, drive out waiting threads */ entry->driveout_new_threads = TRUE; @@ -966,22 +1579,22 @@ static void flush(private_ike_sa_manager_t *this) enumerator->destroy(enumerator); DBG2(DBG_MGR, "wait for all threads to leave IKE_SA's"); /* Step 2: wait until all are gone */ - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); - while (enumerator->enumerate(enumerator, &entry)) + enumerator = create_table_enumerator(this); + while (enumerator->enumerate(enumerator, &entry, &segment)) { - while (entry->waiting_threads) + while (entry->waiting_threads || entry->checked_out) { /* wake up all */ entry->condvar->broadcast(entry->condvar); /* go sleeping until they are gone */ - entry->condvar->wait(entry->condvar, this->mutex); + entry->condvar->wait(entry->condvar, this->segments[segment].mutex); } } enumerator->destroy(enumerator); DBG2(DBG_MGR, "delete all IKE_SA's"); /* Step 3: initiate deletion of all IKE_SAs */ - enumerator = this->ike_sa_list->create_enumerator(this->ike_sa_list); - while (enumerator->enumerate(enumerator, &entry)) + enumerator = create_table_enumerator(this); + while (enumerator->enumerate(enumerator, &entry, &segment)) { charon->bus->set_sa(charon->bus, entry->ike_sa); entry->ike_sa->delete(entry->ike_sa); @@ -990,14 +1603,25 @@ static void flush(private_ike_sa_manager_t *this) DBG2(DBG_MGR, "destroy all entries"); /* Step 4: destroy all entries */ - while (this->ike_sa_list->remove_last(this->ike_sa_list, - (void**)&entry) == SUCCESS) + 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->ike_sa_id->is_initiator(entry->ike_sa_id) && + entry->my_id && entry->other_id) + { + remove_connected_peers(this, entry); + } + remove_entry_at((private_enumerator_t*)enumerator); entry_destroy(entry); } + enumerator->destroy(enumerator); charon->bus->set_sa(charon->bus, NULL); - this->mutex->unlock(this->mutex); + unlock_all_segments(this); } /** @@ -1005,18 +1629,67 @@ static void flush(private_ike_sa_manager_t *this) */ static void destroy(private_ike_sa_manager_t *this) { - this->ike_sa_list->destroy(this->ike_sa_list); + u_int i; + + for (i = 0; i < this->table_size; ++i) + { + linked_list_t *list; + + if ((list = this->ike_sa_table[i]) != NULL) + { + list->destroy(list); + } + if ((list = this->half_open_table[i]) != NULL) + { + list->destroy(list); + } + if ((list = this->connected_peers_table[i]) != NULL) + { + list->destroy(list); + } + } + free(this->ike_sa_table); + free(this->half_open_table); + free(this->connected_peers_table); + for (i = 0; i < this->segment_count; ++i) + { + this->segments[i].mutex->destroy(this->segments[i].mutex); + this->half_open_segments[i].lock->destroy(this->half_open_segments[i].lock); + this->connected_peers_segments[i].lock->destroy(this->connected_peers_segments[i].lock); + } + free(this->segments); + free(this->half_open_segments); + free(this->connected_peers_segments); + this->rng->destroy(this->rng); this->hasher->destroy(this->hasher); - this->mutex->destroy(this->mutex); free(this); } +/** + * This function returns the next-highest power of two for the given number. + * The algorithm works by setting all bits on the right-hand side of the most + * significant 1 to 1 and then increments the whole number so it rolls over + * to the nearest power of two. Note: returns 0 for n == 0 + */ +static u_int get_nearest_powerof2(u_int n) +{ + u_int i; + + --n; + for (i = 1; i < sizeof(u_int) * 8; i <<= 1) + { + n |= n >> i; + } + return ++n; +} + /* * Described in header. */ ike_sa_manager_t *ike_sa_manager_create() { + u_int i; private_ike_sa_manager_t *this = malloc_thing(private_ike_sa_manager_t); /* assign public functions */ @@ -1028,10 +1701,10 @@ ike_sa_manager_t *ike_sa_manager_create() this->public.checkout_by_config = (ike_sa_t*(*)(ike_sa_manager_t*,peer_cfg_t*))checkout_by_config; this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id; this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name; - this->public.checkout_duplicate = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_t *ike_sa))checkout_duplicate; + this->public.check_uniqueness = (bool(*)(ike_sa_manager_t*, ike_sa_t *ike_sa))check_uniqueness; this->public.create_enumerator = (enumerator_t*(*)(ike_sa_manager_t*))create_enumerator; - this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin; - this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy; + this->public.checkin = (void(*)(ike_sa_manager_t*,ike_sa_t*))checkin; + this->public.checkin_and_destroy = (void(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy; this->public.get_half_open_count = (int(*)(ike_sa_manager_t*,host_t*))get_half_open_count; /* initialize private variables */ @@ -1050,10 +1723,44 @@ ike_sa_manager_t *ike_sa_manager_create() free(this); return NULL; } - this->ike_sa_list = linked_list_create(); - this->mutex = mutex_create(MUTEX_DEFAULT); + this->table_size = get_nearest_powerof2(lib->settings->get_int(lib->settings, + "charon.ikesa_table_size", DEFAULT_HASHTABLE_SIZE)); + this->table_size = max(1, min(this->table_size, MAX_HASHTABLE_SIZE)); + this->table_mask = this->table_size - 1; + + this->segment_count = get_nearest_powerof2(lib->settings->get_int(lib->settings, + "charon.ikesa_table_segments", DEFAULT_SEGMENT_COUNT)); + this->segment_count = max(1, min(this->segment_count, this->table_size)); + this->segment_mask = this->segment_count - 1; + + this->ike_sa_table = calloc(this->table_size, sizeof(linked_list_t*)); + + this->segments = (segment_t*)calloc(this->segment_count, sizeof(segment_t)); + for (i = 0; i < this->segment_count; ++i) + { + this->segments[i].mutex = mutex_create(MUTEX_RECURSIVE); + this->segments[i].count = 0; + } + + /* we use the same table parameters for the table to track half-open SAs */ + this->half_open_table = calloc(this->table_size, sizeof(linked_list_t*)); + this->half_open_segments = calloc(this->segment_count, sizeof(shareable_segment_t)); + for (i = 0; i < this->segment_count; ++i) + { + this->half_open_segments[i].lock = rwlock_create(RWLOCK_DEFAULT); + this->half_open_segments[i].count = 0; + } + + /* also for the hash table used for duplicate tests */ + this->connected_peers_table = calloc(this->table_size, sizeof(linked_list_t*)); + this->connected_peers_segments = calloc(this->segment_count, sizeof(shareable_segment_t)); + for (i = 0; i < this->segment_count; ++i) + { + this->connected_peers_segments[i].lock = rwlock_create(RWLOCK_DEFAULT); + this->connected_peers_segments[i].count = 0; + } + this->reuse_ikesa = lib->settings->get_bool(lib->settings, "charon.reuse_ikesa", TRUE); return &this->public; } - diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h index 3f0752cc8..6b6d5a32d 100644 --- a/src/charon/sa/ike_sa_manager.h +++ b/src/charon/sa/ike_sa_manager.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2005-2008 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil * @@ -13,7 +14,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: ike_sa_manager.h 4624 2008-11-11 13:11:44Z tobias $ + * $Id: ike_sa_manager.h 4811 2008-12-17 09:00:22Z martin $ */ /** @@ -32,16 +33,11 @@ typedef struct ike_sa_manager_t ike_sa_manager_t; #include <config/peer_cfg.h> /** - * The IKE_SA-Manager is responsible for managing all initiated and responded IKE_SA's. + * Manages and synchronizes access to all IKE_SAs. * - * To avoid access from multiple threads, IKE_SAs must be checked out from - * the manager, and checked in after usage. - * The manager also handles deletion of SAs. - * - * @todo checking of double-checkouts from the same threads would be nice. - * This could be done by comparing thread-ids via pthread_self()... - * - * @todo Managing of ike_sa_t objects in a hash table instead of linked list. + * To synchronize access to thread-unsave IKE_SAs, they are checked out for + * use and checked in afterwards. A checked out SA is exclusively accessible + * by the owning thread. */ struct ike_sa_manager_t { @@ -57,7 +53,7 @@ struct ike_sa_manager_t { /** * Create and check out a new IKE_SA. - * + * * @param initiator TRUE for initiator, FALSE otherwise * @returns created and checked out IKE_SA */ @@ -103,12 +99,19 @@ struct ike_sa_manager_t { peer_cfg_t *peer_cfg); /** - * Check out a duplicate if ike_sa to do uniqueness tests. - * - * @param ike_sa ike_sa to get a duplicate from - * @return checked out duplicate + * Check for duplicates of the given IKE_SA. + * + * Measures are taken according to the uniqueness policy of the IKE_SA. + * The return value indicates whether duplicates have been found and if + * further measures should be taken (e.g. cancelling an IKE_AUTH exchange). + * check_uniqueness() must be called before the IKE_SA is complete, + * deadlocks occur otherwise. + * + * @param ike_sa ike_sa to check + * @return TRUE, if the given IKE_SA has duplicates and + * should be deleted */ - ike_sa_t* (*checkout_duplicate)(ike_sa_manager_t *this, ike_sa_t *ike_sa); + bool (*check_uniqueness)(ike_sa_manager_t *this, ike_sa_t *ike_sa); /** * Check out an IKE_SA a unique ID. @@ -130,8 +133,8 @@ struct ike_sa_manager_t { /** * Check out an IKE_SA by the policy/connection name. * - * Check out the IKE_SA by the connections name or by a CHILD_SAs policy - * name. + * Check out the IKE_SA by the configuration name, either from the IKE- or + * one of its CHILD_SAs. * * @param name name of the connection/policy * @param child TRUE to use policy name, FALSE to use conn name @@ -145,8 +148,8 @@ struct ike_sa_manager_t { /** * Create an enumerator over all stored IKE_SAs. * - * The avoid synchronization issues, the enumerator locks access - * to the manager exclusively, until it gets destroyed. + * While enumerating an IKE_SA, it is temporarily checked out and + * automatically checked in after the current enumeration step. * * @return enumerator over all IKE_SAs. */ @@ -154,17 +157,13 @@ struct ike_sa_manager_t { /** * Checkin the SA after usage. - * - * @warning the SA pointer MUST NOT be used after checkin! - * The SA must be checked out again! - * + * + * If the IKE_SA is not registered in the manager, a new entry is created. + * * @param ike_sa_id the SA identifier, will be updated * @param ike_sa checked out SA - * @returns - * - SUCCESS if checked in - * - NOT_FOUND when not found (shouldn't happen!) */ - status_t (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa); + void (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa); /** * Destroy a checked out SA. @@ -177,11 +176,8 @@ struct ike_sa_manager_t { * risk that another thread can get the SA. * * @param ike_sa SA to delete - * @returns - * - SUCCESS if found - * - NOT_FOUND when no such SA is available */ - status_t (*checkin_and_destroy) (ike_sa_manager_t* this, ike_sa_t *ike_sa); + void (*checkin_and_destroy) (ike_sa_manager_t* this, ike_sa_t *ike_sa); /** * Get the number of IKE_SAs which are in the connecting state. @@ -214,7 +210,7 @@ struct ike_sa_manager_t { }; /** - * Create a manager. + * Create the IKE_SA manager. * * @returns ike_sa_manager_t object, NULL if initialization fails */ diff --git a/src/charon/sa/keymat.c b/src/charon/sa/keymat.c index c65bfc3b7..b2e646c93 100644 --- a/src/charon/sa/keymat.c +++ b/src/charon/sa/keymat.c @@ -63,6 +63,11 @@ struct private_keymat_t { prf_t *prf; /** + * Negotiated PRF algorithm + */ + pseudo_random_function_t prf_alg; + + /** * Key to derive key material from for CHILD_SAs, rekeying */ chunk_t skd; @@ -145,7 +150,8 @@ static diffie_hellman_t* create_dh(private_keymat_t *this, static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, - private_keymat_t *rekey) + pseudo_random_function_t rekey_function, + chunk_t rekey_skd) { chunk_t skeyseed, key, secret, full_nonce, fixed_nonce, prf_plus_seed; chunk_t spi_i, spi_r; @@ -153,6 +159,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, signer_t *signer_i, *signer_r; prf_plus_t *prf_plus; u_int16_t alg, key_size; + prf_t *rekey_prf = NULL; spi_i = chunk_alloca(sizeof(u_int64_t)); spi_r = chunk_alloca(sizeof(u_int64_t)); @@ -169,6 +176,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, transform_type_names, PSEUDO_RANDOM_FUNCTION); return FALSE; } + this->prf_alg = alg; this->prf = lib->crypto->create_prf(lib->crypto, alg); if (this->prf == NULL) { @@ -205,7 +213,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, * * if we are rekeying, SKEYSEED is built on another way */ - if (rekey == NULL) /* not rekeying */ + if (rekey_function == PRF_UNDEFINED) /* not rekeying */ { /* SKEYSEED = prf(Ni | Nr, g^ir) */ this->prf->set_key(this->prf, fixed_nonce); @@ -217,11 +225,21 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, { /* SKEYSEED = prf(SK_d (old), [g^ir (new)] | Ni | Nr) * use OLD SAs PRF functions for both prf_plus and prf */ + rekey_prf = lib->crypto->create_prf(lib->crypto, rekey_function); + if (!rekey_prf) + { + DBG1(DBG_IKE, "PRF of old SA %N not supported!", + pseudo_random_function_names, rekey_function); + chunk_free(&full_nonce); + chunk_free(&fixed_nonce); + chunk_clear(&prf_plus_seed); + return FALSE; + } secret = chunk_cat("mc", secret, full_nonce); - rekey->prf->set_key(rekey->prf, rekey->skd); - rekey->prf->allocate_bytes(rekey->prf, secret, &skeyseed); - rekey->prf->set_key(rekey->prf, skeyseed); - prf_plus = prf_plus_create(rekey->prf, prf_plus_seed); + rekey_prf->set_key(rekey_prf, rekey_skd); + rekey_prf->allocate_bytes(rekey_prf, secret, &skeyseed); + rekey_prf->set_key(rekey_prf, skeyseed); + prf_plus = prf_plus_create(rekey_prf, prf_plus_seed); } DBG4(DBG_IKE, "SKEYSEED %B", &skeyseed); @@ -243,6 +261,8 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, { DBG1(DBG_IKE, "no %N selected", transform_type_names, INTEGRITY_ALGORITHM); + prf_plus->destroy(prf_plus); + DESTROY_IF(rekey_prf); return FALSE; } signer_i = lib->crypto->create_signer(lib->crypto, alg); @@ -253,6 +273,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, transform_type_names, INTEGRITY_ALGORITHM, integrity_algorithm_names ,alg); prf_plus->destroy(prf_plus); + DESTROY_IF(rekey_prf); return FALSE; } key_size = signer_i->get_key_size(signer_i); @@ -284,6 +305,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, DBG1(DBG_IKE, "no %N selected", transform_type_names, ENCRYPTION_ALGORITHM); prf_plus->destroy(prf_plus); + DESTROY_IF(rekey_prf); return FALSE; } crypter_i = lib->crypto->create_crypter(lib->crypto, alg, key_size / 8); @@ -294,6 +316,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, transform_type_names, ENCRYPTION_ALGORITHM, encryption_algorithm_names, alg, key_size); prf_plus->destroy(prf_plus); + DESTROY_IF(rekey_prf); return FALSE; } key_size = crypter_i->get_key_size(crypter_i); @@ -344,6 +367,7 @@ static bool derive_ike_keys(private_keymat_t *this, proposal_t *proposal, /* all done, prf_plus not needed anymore */ prf_plus->destroy(prf_plus); + DESTROY_IF(rekey_prf); return TRUE; } @@ -382,9 +406,9 @@ static bool derive_child_keys(private_keymat_t *this, { enc_size = lookup_keylen(keylen_enc, enc_alg); } - if (!enc_size) + if (enc_alg != ENCR_NULL && !enc_size) { - DBG1(DBG_CHD, "no keylenth defined for %N", + DBG1(DBG_CHD, "no keylength defined for %N", encryption_algorithm_names, enc_alg); return FALSE; } @@ -421,7 +445,7 @@ static bool derive_child_keys(private_keymat_t *this, } if (!int_size) { - DBG1(DBG_CHD, "no keylenth defined for %N", + DBG1(DBG_CHD, "no keylength defined for %N", integrity_algorithm_names, int_alg); return FALSE; } @@ -443,6 +467,15 @@ static bool derive_child_keys(private_keymat_t *this, } /** + * Implementation of keymat_t.get_skd + */ +static pseudo_random_function_t get_skd(private_keymat_t *this, chunk_t *skd) +{ + *skd = this->skd; + return this->prf_alg; +} + +/** * Implementation of keymat_t.get_signer */ static signer_t* get_signer(private_keymat_t *this, bool in) @@ -544,8 +577,9 @@ keymat_t *keymat_create(bool initiator) private_keymat_t *this = malloc_thing(private_keymat_t); this->public.create_dh = (diffie_hellman_t*(*)(keymat_t*, diffie_hellman_group_t group))create_dh; - this->public.derive_ike_keys = (bool(*)(keymat_t*, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, keymat_t *rekey))derive_ike_keys; + this->public.derive_ike_keys = (bool(*)(keymat_t*, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, ike_sa_id_t *id, pseudo_random_function_t,chunk_t))derive_ike_keys; this->public.derive_child_keys = (bool(*)(keymat_t*, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, chunk_t nonce_r, chunk_t *encr_i, chunk_t *integ_i, chunk_t *encr_r, chunk_t *integ_r))derive_child_keys; + this->public.get_skd = (pseudo_random_function_t(*)(keymat_t*, chunk_t *skd))get_skd; this->public.get_signer = (signer_t*(*)(keymat_t*, bool in))get_signer; this->public.get_crypter = (crypter_t*(*)(keymat_t*, bool in))get_crypter; this->public.get_auth_octets = (chunk_t(*)(keymat_t *, bool verify, chunk_t ike_sa_init, chunk_t nonce, identification_t *id))get_auth_octets; @@ -559,6 +593,7 @@ keymat_t *keymat_create(bool initiator) this->crypter_in = NULL; this->crypter_out = NULL; this->prf = NULL; + this->prf_alg = PRF_UNDEFINED; this->skd = chunk_empty; this->skp_verify = chunk_empty; this->skp_build = chunk_empty; diff --git a/src/charon/sa/keymat.h b/src/charon/sa/keymat.h index 3ca25da9e..0d6d08f51 100644 --- a/src/charon/sa/keymat.h +++ b/src/charon/sa/keymat.h @@ -61,12 +61,15 @@ struct keymat_t { * @param nonce_i initiators nonce value * @param nonce_r responders nonce value * @param id IKE_SA identifier - * @param rekey keymat of old SA if we are rekeying + * @param rekey_prf PRF of old SA if rekeying, PRF_UNDEFINED otherwise + * @param rekey_sdk SKd of old SA if rekeying * @return TRUE on success */ bool (*derive_ike_keys)(keymat_t *this, proposal_t *proposal, diffie_hellman_t *dh, chunk_t nonce_i, - chunk_t nonce_r, ike_sa_id_t *id, keymat_t *rekey); + chunk_t nonce_r, ike_sa_id_t *id, + pseudo_random_function_t rekey_function, + chunk_t rekey_skd); /** * Derive keys for a CHILD_SA. * @@ -91,6 +94,14 @@ struct keymat_t { chunk_t *encr_i, chunk_t *integ_i, chunk_t *encr_r, chunk_t *integ_r); /** + * Get SKd to pass to derive_ikey_keys() during rekeying. + * + * @param skd chunk to write SKd to (internal data) + * @return PRF function to derive keymat + */ + pseudo_random_function_t (*get_skd)(keymat_t *this, chunk_t *skd); + + /** * Get a signer to sign/verify IKE messages. * * @param in TRUE for inbound (verify), FALSE for outbound (sign) diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c index 0630647c9..e5c5fe178 100644 --- a/src/charon/sa/task_manager.c +++ b/src/charon/sa/task_manager.c @@ -13,7 +13,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: task_manager.c 4484 2008-10-27 11:13:33Z martin $ + * $Id: task_manager.c 4857 2009-02-09 10:45:51Z martin $ */ #include "task_manager.h" @@ -48,12 +48,12 @@ 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 */ @@ -66,17 +66,17 @@ typedef struct private_task_manager_t private_task_manager_t; * private data of the task manager */ struct private_task_manager_t { - + /** * public functions */ task_manager_t public; - + /** * associated IKE_SA we are serving */ ike_sa_t *ike_sa; - + /** * Exchange we are currently handling as responder */ @@ -85,14 +85,14 @@ struct private_task_manager_t { * Message ID of the exchange */ u_int32_t mid; - + /** * packet for retransmission */ packet_t *packet; } responding; - + /** * Exchange we are currently handling as initiator */ @@ -118,17 +118,17 @@ struct private_task_manager_t { exchange_type_t type; } initiating; - + /** * List of queued tasks not yet in action */ linked_list_t *queued_tasks; - + /** * List of active tasks, initiated by ourselve */ linked_list_t *active_tasks; - + /** * List of tasks initiated by peer */ @@ -417,43 +417,48 @@ static status_t build_request(private_task_manager_t *this) message->set_exchange_type(message, exchange); this->initiating.type = exchange; this->initiating.retransmitted = 0; - + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); while (iterator->iterate(iterator, (void*)&task)) { - switch (task->build(task, message)) - { - case SUCCESS: - /* task completed, remove it */ - iterator->remove(iterator); - task->destroy(task); - break; - case NEED_MORE: - /* processed, but task needs another exchange */ - break; - case FAILED: - default: - /* critical failure, destroy IKE_SA */ - iterator->destroy(iterator); + switch (task->build(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->destroy(iterator); message->destroy(message); flush(this); - return DESTROY_ME; - } + return DESTROY_ME; + } } iterator->destroy(iterator); - - DESTROY_IF(this->initiating.packet); + + /* update exchange type if a task changed it */ + this->initiating.type = message->get_exchange_type(message); + status = this->ike_sa->generate_message(this->ike_sa, message, &this->initiating.packet); - message->destroy(message); if (status != SUCCESS) { - /* message generation failed. There is nothing more to do than to + /* message generation failed. There is nothing more to do than to * close the SA */ + message->destroy(message); flush(this); - return DESTROY_ME; + return DESTROY_ME; } + charon->bus->message(charon->bus, message, FALSE); + message->destroy(message); + return retransmit(this, this->initiating.mid); } @@ -473,32 +478,34 @@ static status_t process_response(private_task_manager_t *this, exchange_type_names, this->initiating.type); return DESTROY_ME; } - + /* catch if we get resetted while processing */ this->reset = FALSE; iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); while (iterator->iterate(iterator, (void*)&task)) { - switch (task->process(task, message)) - { - case SUCCESS: - /* task completed, remove it */ - iterator->remove(iterator); - task->destroy(task); - break; - case NEED_MORE: - /* processed, but task needs another exchange */ - break; - case FAILED: - default: - /* critical failure, destroy IKE_SA */ - iterator->destroy(iterator); - return DESTROY_ME; - } - if (this->reset) - { /* start all over again if we were reset */ - this->reset = FALSE; - iterator->destroy(iterator); + switch (task->process(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->remove(iterator); + iterator->destroy(iterator); + task->destroy(task); + return DESTROY_ME; + } + if (this->reset) + { /* start all over again if we were reset */ + this->reset = FALSE; + iterator->destroy(iterator); return build_request(this); } } @@ -506,7 +513,9 @@ static status_t process_response(private_task_manager_t *this, this->initiating.mid++; this->initiating.type = EXCHANGE_TYPE_UNDEFINED; - + this->initiating.packet->destroy(this->initiating.packet); + this->initiating.packet = NULL; + return build_request(this); } @@ -525,34 +534,34 @@ static void handle_collisions(private_task_manager_t *this, task_t *task) if (type == IKE_REKEY || type == CHILD_REKEY || type == CHILD_DELETE || type == IKE_DELETE || type == IKE_REAUTH) { - /* find an exchange collision, and notify these tasks */ - iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); - while (iterator->iterate(iterator, (void**)&active)) - { - switch (active->get_type(active)) - { - case IKE_REKEY: - if (type == IKE_REKEY || type == IKE_DELETE || - type == IKE_REAUTH) - { - ike_rekey_t *rekey = (ike_rekey_t*)active; - rekey->collide(rekey, task); - break; - } - continue; - case CHILD_REKEY: - if (type == CHILD_REKEY || type == CHILD_DELETE) - { - child_rekey_t *rekey = (child_rekey_t*)active; - rekey->collide(rekey, task); - break; - } - continue; - default: - continue; - } - iterator->destroy(iterator); - return; + /* find an exchange collision, and notify these tasks */ + iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE); + while (iterator->iterate(iterator, (void**)&active)) + { + switch (active->get_type(active)) + { + case IKE_REKEY: + if (type == IKE_REKEY || type == IKE_DELETE || + type == IKE_REAUTH) + { + ike_rekey_t *rekey = (ike_rekey_t*)active; + rekey->collide(rekey, task); + break; + } + continue; + case CHILD_REKEY: + if (type == CHILD_REKEY || type == CHILD_DELETE) + { + child_rekey_t *rekey = (child_rekey_t*)active; + rekey->collide(rekey, task); + break; + } + continue; + default: + continue; + } + iterator->destroy(iterator); + return; } iterator->destroy(iterator); } @@ -571,10 +580,10 @@ static status_t build_response(private_task_manager_t *this, message_t *request) host_t *me, *other; bool delete = FALSE; status_t status; - + me = request->get_destination(request); other = request->get_source(request); - + message = message_create(); message->set_exchange_type(message, request->get_exchange_type(request)); /* send response along the path the request came in */ @@ -582,29 +591,29 @@ static status_t build_response(private_task_manager_t *this, message_t *request) message->set_destination(message, other->clone(other)); message->set_message_id(message, this->responding.mid); message->set_request(message, FALSE); - + iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE); while (iterator->iterate(iterator, (void*)&task)) { - switch (task->build(task, message)) - { - case SUCCESS: - /* task completed, remove it */ - iterator->remove(iterator); + switch (task->build(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); handle_collisions(this, task); - case NEED_MORE: - /* processed, but task needs another exchange */ - break; - case FAILED: - default: - /* destroy IKE_SA, but SEND response first */ - delete = TRUE; - break; - } - if (delete) - { - break; - } + case NEED_MORE: + /* processed, but task needs another exchange */ + break; + case FAILED: + default: + /* destroy IKE_SA, but SEND response first */ + delete = TRUE; + break; + } + if (delete) + { + break; + } } iterator->destroy(iterator); @@ -614,7 +623,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request) ike_sa_id_t *id = this->ike_sa->get_id(this->ike_sa); id->set_responder_spi(id, 0); } - + /* message complete, send it */ DESTROY_IF(this->responding.packet); status = this->ike_sa->generate_message(this->ike_sa, message, @@ -623,7 +632,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request) message->destroy(message); if (status != SUCCESS) { - return DESTROY_ME; + return DESTROY_ME; } charon->sender->send(charon->sender, @@ -646,7 +655,7 @@ static status_t process_request(private_task_manager_t *this, payload_t *payload; notify_payload_t *notify; delete_payload_t *delete; - + /* create tasks depending on request type */ switch (message->get_exchange_type(message)) { @@ -713,7 +722,8 @@ static status_t process_request(private_task_manager_t *this, { if (notify_found) { - task = (task_t*)child_rekey_create(this->ike_sa, NULL); + task = (task_t*)child_rekey_create(this->ike_sa, + PROTO_NONE, 0); } else { @@ -770,7 +780,8 @@ static status_t process_request(private_task_manager_t *this, } else { - task = (task_t*)child_delete_create(this->ike_sa, NULL); + task = (task_t*)child_delete_create(this->ike_sa, + PROTO_NONE, 0); } break; } @@ -801,30 +812,32 @@ static status_t process_request(private_task_manager_t *this, default: break; } - + /* let the tasks process the message */ iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE); while (iterator->iterate(iterator, (void*)&task)) { - switch (task->process(task, message)) - { - case SUCCESS: - /* task completed, remove it */ - iterator->remove(iterator); - task->destroy(task); - break; - case NEED_MORE: - /* processed, but task needs at least another call to build() */ - break; - case FAILED: - default: - /* critical failure, destroy IKE_SA */ - iterator->destroy(iterator); - return DESTROY_ME; - } + switch (task->process(task, message)) + { + case SUCCESS: + /* task completed, remove it */ + iterator->remove(iterator); + task->destroy(task); + break; + case NEED_MORE: + /* processed, but task needs at least another call to build() */ + break; + case FAILED: + default: + /* critical failure, destroy IKE_SA */ + iterator->remove(iterator); + iterator->destroy(iterator); + task->destroy(task); + return DESTROY_ME; + } } iterator->destroy(iterator); - + return build_response(this, message); } @@ -834,7 +847,7 @@ static status_t process_request(private_task_manager_t *this, static status_t process_message(private_task_manager_t *this, message_t *msg) { u_int32_t mid = msg->get_message_id(msg); - + if (msg->get_request(msg)) { if (mid == this->responding.mid) @@ -919,7 +932,7 @@ static void queue_task(private_task_manager_t *this, task_t *task) static void adopt_tasks(private_task_manager_t *this, private_task_manager_t *other) { task_t *task; - + /* move queued tasks from other to this */ while (other->queued_tasks->remove_last(other->queued_tasks, (void**)&task) == SUCCESS) @@ -950,7 +963,8 @@ static bool busy(private_task_manager_t *this) /** * Implementation of task_manager_t.reset */ -static void reset(private_task_manager_t *this) +static void reset(private_task_manager_t *this, + u_int32_t initiate, u_int32_t respond) { task_t *task; @@ -959,8 +973,14 @@ static void reset(private_task_manager_t *this) DESTROY_IF(this->initiating.packet); this->responding.packet = NULL; this->initiating.packet = NULL; - this->responding.mid = 0; - this->initiating.mid = 0; + if (initiate != UINT_MAX) + { + this->initiating.mid = initiate; + } + if (respond != UINT_MAX) + { + this->responding.mid = respond; + } this->initiating.type = EXCHANGE_TYPE_UNDEFINED; /* reset active tasks */ @@ -996,16 +1016,16 @@ static void destroy(private_task_manager_t *this) task_manager_t *task_manager_create(ike_sa_t *ike_sa) { private_task_manager_t *this = malloc_thing(private_task_manager_t); - + this->public.process_message = (status_t(*)(task_manager_t*,message_t*))process_message; this->public.queue_task = (void(*)(task_manager_t*,task_t*))queue_task; this->public.initiate = (status_t(*)(task_manager_t*))build_request; this->public.retransmit = (status_t(*)(task_manager_t*,u_int32_t))retransmit; - this->public.reset = (void(*)(task_manager_t*))reset; + this->public.reset = (void(*)(task_manager_t*,u_int32_t,u_int32_t))reset; this->public.adopt_tasks = (void(*)(task_manager_t*,task_manager_t*))adopt_tasks; this->public.busy = (bool(*)(task_manager_t*))busy; this->public.destroy = (void(*)(task_manager_t*))destroy; - + this->ike_sa = ike_sa; this->responding.packet = NULL; this->initiating.packet = NULL; @@ -1016,6 +1036,6 @@ task_manager_t *task_manager_create(ike_sa_t *ike_sa) this->active_tasks = linked_list_create(); this->passive_tasks = linked_list_create(); this->reset = FALSE; - + return &this->public; } diff --git a/src/charon/sa/task_manager.h b/src/charon/sa/task_manager.h index 6243ac888..2aa6018fd 100644 --- a/src/charon/sa/task_manager.h +++ b/src/charon/sa/task_manager.h @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: task_manager.h 3589 2008-03-13 14:14:44Z martin $ + * $Id: task_manager.h 4689 2008-11-24 12:46:06Z martin $ */ /** @@ -25,6 +25,8 @@ typedef struct task_manager_t task_manager_t; +#include <limits.h> + #include <library.h> #include <encoding/message.h> #include <sa/ike_sa.h> @@ -125,7 +127,7 @@ struct task_manager_t { * - SUCCESS if retransmission sent */ status_t (*retransmit) (task_manager_t *this, u_int32_t message_id); - + /** * Migrate all tasks from other to this. * @@ -143,10 +145,12 @@ struct task_manager_t { * reset to zero (INVALID_KE_PAYLOAD, COOKIES, ...). The reset() method * resets the message IDs and resets all active tasks using the migrate() * method. - * - * @param other manager which gives away its tasks + * Use a value of UINT_MAX to keep the current message ID. + * + * @param initiate message ID to initiate exchanges (send) + * @param respond message ID to respond to exchanges (expect) */ - void (*reset) (task_manager_t *this); + void (*reset) (task_manager_t *this, u_int32_t initiate, u_int32_t respond); /** * Check if we are currently waiting for a reply. diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c index 767ceef55..f6043979f 100644 --- a/src/charon/sa/tasks/child_create.c +++ b/src/charon/sa/tasks/child_create.c @@ -14,7 +14,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: child_create.c 4618 2008-11-11 09:22:00Z tobias $ + * $Id: child_create.c 4860 2009-02-11 13:09:52Z martin $ */ #include "child_create.h" @@ -117,7 +117,22 @@ struct private_child_create_t { ipcomp_transform_t ipcomp_received; /** - * Other Compression Parameter Index (CPI) + * Own allocated SPI + */ + u_int32_t my_spi; + + /** + * SPI received in proposal + */ + u_int32_t other_spi; + + /** + * Own allocated Compression Parameter Index (CPI) + */ + u_int16_t my_cpi; + + /** + * Other Compression Parameter Index (CPI), received via IPCOMP_SUPPORTED */ u_int16_t other_cpi; @@ -189,6 +204,36 @@ static bool ts_list_is_host(linked_list_t *list, host_t *host) } /** + * Allocate SPIs and update proposals + */ +static bool allocate_spi(private_child_create_t *this) +{ + enumerator_t *enumerator; + proposal_t *proposal; + + /* TODO: allocate additional SPI for AH if we have such proposals */ + this->my_spi = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP); + if (this->my_spi) + { + if (this->initiator) + { + enumerator = this->proposals->create_enumerator(this->proposals); + while (enumerator->enumerate(enumerator, &proposal)) + { + proposal->set_spi(proposal, this->my_spi); + } + enumerator->destroy(enumerator); + } + else + { + this->proposal->set_spi(this->proposal, this->my_spi); + } + return TRUE; + } + return FALSE; +} + +/** * Install a CHILD_SA for usage, return value: * - FAILED: no acceptable proposal * - INVALID_ARG: diffie hellman group inacceptable @@ -197,7 +242,9 @@ static bool ts_list_is_host(linked_list_t *list, host_t *host) static status_t select_and_install(private_child_create_t *this, bool no_dh) { status_t status; - chunk_t nonce_i, nonce_r, encr_i, integ_i, encr_r, integ_r; + chunk_t nonce_i, nonce_r; + chunk_t encr_i = chunk_empty, encr_r = chunk_empty; + chunk_t integ_i = chunk_empty, integ_r = chunk_empty; linked_list_t *my_ts, *other_ts; host_t *me, *other, *other_vip, *my_vip; @@ -216,7 +263,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) other = this->ike_sa->get_other_host(this->ike_sa); my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - + this->proposal = this->config->select_proposal(this->config, this->proposals, no_dh); if (this->proposal == NULL) @@ -224,6 +271,14 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) DBG1(DBG_IKE, "no acceptable proposal found"); return FAILED; } + this->other_spi = this->proposal->get_spi(this->proposal); + + if (!this->initiator && !allocate_spi(this)) + { /* responder has no SPI allocated yet */ + DBG1(DBG_IKE, "allocating SPI failed"); + return FAILED; + } + this->child_sa->set_proposal(this->child_sa, this->proposal); if (!this->proposal->has_dh_group(this->proposal, this->dh_group)) { @@ -328,26 +383,33 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) } this->child_sa->set_state(this->child_sa, CHILD_INSTALLING); + this->child_sa->set_ipcomp(this->child_sa, this->ipcomp); + this->child_sa->set_mode(this->child_sa, this->mode); + this->child_sa->set_protocol(this->child_sa, + this->proposal->get_protocol(this->proposal)); - if (this->ipcomp != IPCOMP_NONE) + if (this->my_cpi == 0 || this->other_cpi == 0 || this->ipcomp == IPCOMP_NONE) { - this->child_sa->activate_ipcomp(this->child_sa, this->ipcomp, - this->other_cpi); + this->my_cpi = this->other_cpi = 0; + this->ipcomp = IPCOMP_NONE; } - status = FAILED; if (this->keymat->derive_child_keys(this->keymat, this->proposal, this->dh, nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r)) { if (this->initiator) { - status = this->child_sa->update(this->child_sa, this->proposal, - this->mode, integ_r, integ_i, encr_r, encr_i); + status = this->child_sa->install(this->child_sa, encr_r, integ_r, + this->my_spi, this->my_cpi, TRUE); + status = this->child_sa->install(this->child_sa, encr_i, integ_i, + this->other_spi, this->other_cpi, FALSE); } else { - status = this->child_sa->add(this->child_sa, this->proposal, - this->mode, integ_i, integ_r, encr_i, encr_r); + status = this->child_sa->install(this->child_sa, encr_i, integ_i, + this->my_spi, this->my_cpi, TRUE); + status = this->child_sa->install(this->child_sa, encr_r, integ_r, + this->other_spi, this->other_cpi, FALSE); } } chunk_clear(&integ_i); @@ -361,8 +423,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh) return FAILED; } - status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts, - this->mode, this->proposal->get_protocol(this->proposal)); + status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts); if (status != SUCCESS) { DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel"); @@ -436,33 +497,71 @@ static void build_payloads(private_child_create_t *this, message_t *message) } /** - * Adds an IPCOMP_SUPPORTED notify to the message, if possible + * Adds an IPCOMP_SUPPORTED notify to the message, allocating a CPI */ -static void build_ipcomp_supported_notify(private_child_create_t *this, - message_t *message) +static void add_ipcomp_notify(private_child_create_t *this, + message_t *message, u_int8_t ipcomp) { - u_int16_t cpi; - u_int8_t tid; - if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) { DBG1(DBG_IKE, "IPComp is not supported if either peer is natted, " "IPComp disabled"); - this->ipcomp = IPCOMP_NONE; return; } - cpi = this->child_sa->allocate_cpi(this->child_sa); - tid = this->ipcomp; - if (cpi) + this->my_cpi = this->child_sa->alloc_cpi(this->child_sa); + if (this->my_cpi) { - message->add_notify(message, FALSE, IPCOMP_SUPPORTED, - chunk_cata("cc", chunk_from_thing(cpi), chunk_from_thing(tid))); + this->ipcomp = ipcomp; + message->add_notify(message, FALSE, IPCOMP_SUPPORTED, + chunk_cata("cc", chunk_from_thing(this->my_cpi), + chunk_from_thing(ipcomp))); } else { DBG1(DBG_IKE, "unable to allocate a CPI from kernel, IPComp disabled"); - this->ipcomp = IPCOMP_NONE; + } +} + +/** + * handle a received notify payload + */ +static void handle_notify(private_child_create_t *this, notify_payload_t *notify) +{ + switch (notify->get_notify_type(notify)) + { + case USE_TRANSPORT_MODE: + this->mode = MODE_TRANSPORT; + break; + case USE_BEET_MODE: + this->mode = MODE_BEET; + break; + case IPCOMP_SUPPORTED: + { + ipcomp_transform_t ipcomp; + u_int16_t cpi; + chunk_t data; + + data = notify->get_notification_data(notify); + cpi = *(u_int16_t*)data.ptr; + ipcomp = (ipcomp_transform_t)(*(data.ptr + 2)); + switch (ipcomp) + { + case IPCOMP_DEFLATE: + this->other_cpi = cpi; + this->ipcomp_received = ipcomp; + break; + case IPCOMP_LZS: + case IPCOMP_LZJH: + default: + DBG1(DBG_IKE, "received IPCOMP_SUPPORTED notify with a " + "transform ID we don't support %N", + ipcomp_transform_names, ipcomp); + break; + } + } + default: + break; } } @@ -476,7 +575,6 @@ static void process_payloads(private_child_create_t *this, message_t *message) sa_payload_t *sa_payload; ke_payload_t *ke_payload; ts_payload_t *ts_payload; - notify_payload_t *notify_payload; /* defaults to TUNNEL mode */ this->mode = MODE_TUNNEL; @@ -512,37 +610,7 @@ static void process_payloads(private_child_create_t *this, message_t *message) this->tsr = ts_payload->get_traffic_selectors(ts_payload); break; case NOTIFY: - notify_payload = (notify_payload_t*)payload; - switch (notify_payload ->get_notify_type(notify_payload )) - { - case USE_TRANSPORT_MODE: - this->mode = MODE_TRANSPORT; - break; - case USE_BEET_MODE: - this->mode = MODE_BEET; - break; - case IPCOMP_SUPPORTED: - { - chunk_t data = notify_payload->get_notification_data(notify_payload); - u_int16_t cpi = *(u_int16_t*)data.ptr; - ipcomp_transform_t ipcomp = (ipcomp_transform_t)(*(data.ptr + 2)); - switch(ipcomp) - { - case IPCOMP_DEFLATE: - this->other_cpi = cpi; - this->ipcomp_received = ipcomp; - break; - case IPCOMP_LZS: - case IPCOMP_LZJH: - default: - DBG1(DBG_IKE, "received IPCOMP_SUPPORTED notify with a transform" - " ID we don't support %N", ipcomp_transform_names, ipcomp); - break; - } - } - default: - break; - } + handle_notify(this, (notify_payload_t*)payload); break; default: break; @@ -557,9 +625,8 @@ static void process_payloads(private_child_create_t *this, message_t *message) static status_t build_i(private_child_create_t *this, message_t *message) { host_t *me, *other, *vip; - bool propose_all = FALSE; peer_cfg_t *peer_cfg; - + switch (message->get_exchange_type(message)) { case IKE_SA_INIT: @@ -610,23 +677,18 @@ static status_t build_i(private_child_create_t *this, message_t *message) } /* check if we want a virtual IP, but don't have one */ - if (!this->reqid) + peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); + vip = peer_cfg->get_virtual_ip(peer_cfg); + if (!this->reqid && vip) { - peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - vip = peer_cfg->get_virtual_ip(peer_cfg); - if (vip) - { - propose_all = TRUE; - } - } - - if (propose_all) - { /* propose a 0.0.0.0/0 subnet when we use virtual ip */ + /* propose a 0.0.0.0/0 or ::/0 subnet when we use virtual ip */ + vip = host_create_any(vip->get_family(vip)); this->tsi = this->config->get_traffic_selectors(this->config, TRUE, - NULL, NULL); + NULL, vip); + vip->destroy(vip); } else - { /* but shorten a 0.0.0.0/0 subnet for host2host/we already have a vip */ + { /* but narrow it for host2host / if we already have a vip */ this->tsi = this->config->get_traffic_selectors(this->config, TRUE, NULL, me); } @@ -641,7 +703,7 @@ static status_t build_i(private_child_create_t *this, message_t *message) this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid, this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)); - if (this->child_sa->alloc(this->child_sa, this->proposals) != SUCCESS) + if (!allocate_spi(this)) { DBG1(DBG_IKE, "unable to allocate SPIs from kernel"); return FAILED; @@ -652,10 +714,10 @@ static status_t build_i(private_child_create_t *this, message_t *message) this->dh = this->keymat->create_dh(this->keymat, this->dh_group); } - if (this->config->use_ipcomp(this->config)) { + if (this->config->use_ipcomp(this->config)) + { /* IPCOMP_DEFLATE is the only transform we support at the moment */ - this->ipcomp = IPCOMP_DEFLATE; - build_ipcomp_supported_notify(this, message); + add_ipcomp_notify(this, message, IPCOMP_DEFLATE); } build_payloads(this, message); @@ -821,16 +883,17 @@ static status_t build_r(private_child_create_t *this, message_t *message) this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid, this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)); - if (this->config->use_ipcomp(this->config) && - this->ipcomp_received != IPCOMP_NONE) + if (this->ipcomp_received != IPCOMP_NONE) { - this->ipcomp = this->ipcomp_received; - build_ipcomp_supported_notify(this, message); - } - else if (this->ipcomp_received != IPCOMP_NONE) - { - DBG1(DBG_IKE, "received %N notify but IPComp is disabled, ignoring", - notify_type_names, IPCOMP_SUPPORTED); + if (this->config->use_ipcomp(this->config)) + { + add_ipcomp_notify(this, message, this->ipcomp_received); + } + else + { + DBG1(DBG_IKE, "received %N notify but IPComp is disabled, ignoring", + notify_type_names, IPCOMP_SUPPORTED); + } } switch (select_and_install(this, no_dh)) @@ -1052,6 +1115,8 @@ static void migrate(private_child_create_t *this, ike_sa_t *ike_sa) } this->ike_sa = ike_sa; + this->keymat = ike_sa->get_keymat(ike_sa); + this->proposal = NULL; this->proposals = NULL; this->tsi = NULL; this->tsr = NULL; @@ -1137,6 +1202,9 @@ child_create_t *child_create_create(ike_sa_t *ike_sa, child_cfg_t *config) this->mode = MODE_TUNNEL; this->ipcomp = IPCOMP_NONE; this->ipcomp_received = IPCOMP_NONE; + this->my_spi = 0; + this->other_spi = 0; + this->my_cpi = 0; this->other_cpi = 0; this->reqid = 0; this->established = FALSE; diff --git a/src/charon/sa/tasks/child_delete.c b/src/charon/sa/tasks/child_delete.c index cab1d63f0..0fd4a056b 100644 --- a/src/charon/sa/tasks/child_delete.c +++ b/src/charon/sa/tasks/child_delete.c @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: child_delete.c 4434 2008-10-14 08:52:13Z martin $ + * $Id: child_delete.c 4730 2008-12-01 18:38:28Z martin $ */ #include "child_delete.h" @@ -44,9 +44,19 @@ struct private_child_delete_t { bool initiator; /** - * wheter to enforce delete action policy - */ - bool check_delete_action; + * Protocol of CHILD_SA to delete + */ + protocol_id_t protocol; + + /** + * Inbound SPI of CHILD_SA to delete + */ + u_int32_t spi; + + /** + * wheter to enforce delete action policy + */ + bool check_delete_action; /** * CHILD_SAs which get deleted @@ -238,6 +248,16 @@ static void log_children(private_child_delete_t *this) */ static status_t build_i(private_child_delete_t *this, message_t *message) { + child_sa_t *child_sa; + + child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol, + this->spi, TRUE); + if (!child_sa) + { /* child does not exist anymore */ + return SUCCESS; + } + this->child_sas->insert_last(this->child_sas, child_sa); + log_children(this); build_payloads(this, message); return NEED_MORE; @@ -323,7 +343,8 @@ static void destroy(private_child_delete_t *this) /* * Described in header. */ -child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa) +child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol, + u_int32_t spi) { private_child_delete_t *this = malloc_thing(private_child_delete_t); @@ -335,13 +356,14 @@ child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa) this->ike_sa = ike_sa; this->check_delete_action = FALSE; this->child_sas = linked_list_create(); + this->protocol = protocol; + this->spi = spi; - if (child_sa != NULL) + if (protocol != PROTO_NONE) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; this->initiator = TRUE; - this->child_sas->insert_last(this->child_sas, child_sa); } else { diff --git a/src/charon/sa/tasks/child_delete.h b/src/charon/sa/tasks/child_delete.h index c304ea9d8..c5ebec338 100644 --- a/src/charon/sa/tasks/child_delete.h +++ b/src/charon/sa/tasks/child_delete.h @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: child_delete.h 3589 2008-03-13 14:14:44Z martin $ + * $Id: child_delete.h 4730 2008-12-01 18:38:28Z martin $ */ /** @@ -52,9 +52,11 @@ struct child_delete_t { * Create a new child_delete task. * * @param ike_sa IKE_SA this task works for - * @param child_sa CHILD_SA to delete, or NULL as responder + * @param protocol protocol of CHILD_SA to delete, PROTO_NONE as responder + * @param spi inbound SPI of CHILD_SA to delete * @return child_delete task to handle by the task_manager */ -child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa); +child_delete_t *child_delete_create(ike_sa_t *ike_sa, protocol_id_t protocol, + u_int32_t spi); #endif /* CHILD_DELETE_H_ @} */ diff --git a/src/charon/sa/tasks/child_rekey.c b/src/charon/sa/tasks/child_rekey.c index e50ad33be..0d8cf2db7 100644 --- a/src/charon/sa/tasks/child_rekey.c +++ b/src/charon/sa/tasks/child_rekey.c @@ -13,7 +13,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: child_rekey.c 4659 2008-11-14 14:05:47Z martin $ + * $Id: child_rekey.c 4730 2008-12-01 18:38:28Z martin $ */ #include "child_rekey.h" @@ -49,11 +49,26 @@ struct private_child_rekey_t { bool initiator; /** + * Protocol of CHILD_SA to rekey + */ + protocol_id_t protocol; + + /** + * Inbound SPI of CHILD_SA to rekey + */ + u_int32_t spi; + + /** * the CHILD_CREATE task which is reused to simplify rekeying */ child_create_t *child_create; /** + * the CHILD_DELETE task to delete rekeyed CHILD_SA + */ + child_delete_t *child_delete; + + /** * CHILD_SA which gets rekeyed */ child_sa_t *child_sa; @@ -65,6 +80,25 @@ struct private_child_rekey_t { }; /** + * Implementation of task_t.build for initiator, after rekeying + */ +static status_t build_i_delete(private_child_rekey_t *this, message_t *message) +{ + /* update exchange type to INFORMATIONAL for the delete */ + message->set_exchange_type(message, INFORMATIONAL); + + return this->child_delete->task.build(&this->child_delete->task, message); +} + +/** + * Implementation of task_t.process for initiator, after rekeying + */ +static status_t process_i_delete(private_child_rekey_t *this, message_t *message) +{ + return this->child_delete->task.process(&this->child_delete->task, message); +} + +/** * find a child using the REKEY_SA notify */ static void find_child(private_child_rekey_t *this, message_t *message) @@ -104,25 +138,33 @@ static void find_child(private_child_rekey_t *this, message_t *message) * Implementation of task_t.build for initiator */ static status_t build_i(private_child_rekey_t *this, message_t *message) -{ +{ notify_payload_t *notify; - protocol_id_t protocol; - u_int32_t spi, reqid; + u_int32_t reqid; + child_cfg_t *config; + + this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol, + this->spi, TRUE); + if (!this->child_sa) + { /* CHILD_SA is gone, unable to rekey */ + return SUCCESS; + } + config = this->child_sa->get_config(this->child_sa); /* we just need the rekey notify ... */ - protocol = this->child_sa->get_protocol(this->child_sa); - spi = this->child_sa->get_spi(this->child_sa, TRUE); - notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA); - notify->set_spi(notify, spi); + notify = notify_payload_create_from_protocol_and_type(this->protocol, + REKEY_SA); + notify->set_spi(notify, this->spi); message->add_payload(message, (payload_t*)notify); - + /* ... our CHILD_CREATE task does the hard work for us. */ reqid = this->child_sa->get_reqid(this->child_sa); + this->child_create = child_create_create(this->ike_sa, config); this->child_create->use_reqid(this->child_create, reqid); this->child_create->task.build(&this->child_create->task, message); this->child_sa->set_state(this->child_sa, CHILD_REKEYING); - + return NEED_MORE; } @@ -133,7 +175,7 @@ static status_t process_r(private_child_rekey_t *this, message_t *message) { /* let the CHILD_CREATE task process the message */ this->child_create->task.process(&this->child_create->task, message); - + find_child(this, message); return NEED_MORE; @@ -265,11 +307,13 @@ static status_t process_i(private_child_rekey_t *this, message_t *message) spi = to_delete->get_spi(to_delete, TRUE); protocol = to_delete->get_protocol(to_delete); - if (this->ike_sa->delete_child_sa(this->ike_sa, protocol, spi) != SUCCESS) - { - return FAILED; - } - return SUCCESS; + + /* rekeying done, delete the obsolete CHILD_SA using a subtask */ + this->child_delete = child_delete_create(this->ike_sa, protocol, spi); + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i_delete; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i_delete; + + return NEED_MORE; } /** @@ -319,9 +363,16 @@ static void collide(private_child_rekey_t *this, task_t *other) */ static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa) { - this->child_create->task.migrate(&this->child_create->task, ike_sa); + if (this->child_create) + { + this->child_create->task.migrate(&this->child_create->task, ike_sa); + } + if (this->child_delete) + { + this->child_delete->task.migrate(&this->child_delete->task, ike_sa); + } DESTROY_IF(this->collision); - + this->ike_sa = ike_sa; this->collision = NULL; } @@ -331,7 +382,14 @@ static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa) */ static void destroy(private_child_rekey_t *this) { - this->child_create->task.destroy(&this->child_create->task); + if (this->child_create) + { + this->child_create->task.destroy(&this->child_create->task); + } + if (this->child_delete) + { + this->child_delete->task.destroy(&this->child_delete->task); + } DESTROY_IF(this->collision); free(this); } @@ -339,22 +397,21 @@ static void destroy(private_child_rekey_t *this) /* * Described in header. */ -child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa) +child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol, + u_int32_t spi) { - child_cfg_t *config; private_child_rekey_t *this = malloc_thing(private_child_rekey_t); - + this->public.collide = (void (*)(child_rekey_t*,task_t*))collide; this->public.task.get_type = (task_type_t(*)(task_t*))get_type; this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; - if (child_sa != NULL) + if (protocol != PROTO_NONE) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; this->public.task.process = (status_t(*)(task_t*,message_t*))process_i; this->initiator = TRUE; - config = child_sa->get_config(child_sa); - this->child_create = child_create_create(ike_sa, config); + this->child_create = NULL; } else { @@ -365,8 +422,11 @@ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa) } this->ike_sa = ike_sa; - this->child_sa = child_sa; + this->child_sa = NULL; + this->protocol = protocol; + this->spi = spi; this->collision = NULL; + this->child_delete = NULL; return &this->public; } diff --git a/src/charon/sa/tasks/child_rekey.h b/src/charon/sa/tasks/child_rekey.h index b386ef3c6..37b61a9ef 100644 --- a/src/charon/sa/tasks/child_rekey.h +++ b/src/charon/sa/tasks/child_rekey.h @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: child_rekey.h 3589 2008-03-13 14:14:44Z martin $ + * $Id: child_rekey.h 4730 2008-12-01 18:38:28Z martin $ */ /** @@ -56,9 +56,11 @@ struct child_rekey_t { * Create a new CHILD_REKEY task. * * @param ike_sa IKE_SA this task works for - * @param child_sa child_sa to rekey, NULL if responder + * @param protocol protocol of CHILD_SA to rekey, PROTO_NONE as responder + * @param spi inbound SPI of CHILD_SA to rekey * @return child_rekey task to handle by the task_manager */ -child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa); +child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol, + u_int32_t spi); #endif /* CHILD_REKEY_H_ @} */ diff --git a/src/charon/sa/tasks/ike_auth.c b/src/charon/sa/tasks/ike_auth.c index 5c3f33cbd..93b145755 100644 --- a/src/charon/sa/tasks/ike_auth.c +++ b/src/charon/sa/tasks/ike_auth.c @@ -13,7 +13,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details * - * $Id: ike_auth.c 4463 2008-10-20 11:38:16Z martin $ + * $Id: ike_auth.c 4858 2009-02-10 17:21:44Z martin $ */ #include "ike_auth.h" @@ -88,70 +88,6 @@ struct private_ike_auth_t { }; /** - * check uniqueness and delete duplicates - */ -static bool check_uniqueness(private_ike_auth_t *this) -{ - ike_sa_t *duplicate; - unique_policy_t policy; - status_t status = SUCCESS; - peer_cfg_t *peer_cfg; - bool cancel = FALSE; - - peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - policy = peer_cfg->get_unique_policy(peer_cfg); - if (policy == UNIQUE_NO) - { - return FALSE; - } - duplicate = charon->ike_sa_manager->checkout_duplicate( - charon->ike_sa_manager, this->ike_sa); - if (duplicate) - { - peer_cfg = duplicate->get_peer_cfg(duplicate); - if (peer_cfg && - peer_cfg->equals(peer_cfg, this->ike_sa->get_peer_cfg(this->ike_sa))) - { - switch (duplicate->get_state(duplicate)) - { - case IKE_ESTABLISHED: - case IKE_REKEYING: - switch (policy) - { - case UNIQUE_REPLACE: - DBG1(DBG_IKE, "deleting duplicate IKE_SA due " - "uniqueness policy"); - status = duplicate->delete(duplicate); - break; - case UNIQUE_KEEP: - DBG1(DBG_IKE, "cancelling IKE_SA setup due " - "uniqueness policy"); - cancel = TRUE; - break; - default: - break; - } - break; - default: - break; - } - } - if (status == DESTROY_ME) - { - charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, - duplicate); - } - else - { - charon->ike_sa_manager->checkin(charon->ike_sa_manager, duplicate); - } - } - /* set threads active IKE_SA after checkin */ - charon->bus->set_sa(charon->bus, this->ike_sa); - return cancel; -} - -/** * get the authentication class of a config */ auth_class_t get_auth_class(peer_cfg_t *config) @@ -400,6 +336,12 @@ static status_t build_auth_eap(private_ike_auth_t *this, message_t *message) authenticator_t *auth; auth_payload_t *auth_payload; + if (!this->initiator && !this->peer_authenticated) + { + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); + return FAILED; + } + auth = (authenticator_t*)this->eap_auth; if (auth->build(auth, this->my_packet->get_data(this->my_packet), this->other_nonce, &auth_payload) != SUCCESS) @@ -681,8 +623,10 @@ static status_t build_r(private_ike_auth_t *this, message_t *message) return FAILED; } - if (check_uniqueness(this)) + if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager, + this->ike_sa)) { + DBG1(DBG_IKE, "cancelling IKE_SA setup due uniqueness policy"); message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); return FAILED; } diff --git a/src/charon/sa/tasks/ike_config.c b/src/charon/sa/tasks/ike_config.c index e89f381d3..b890e93ba 100644 --- a/src/charon/sa/tasks/ike_config.c +++ b/src/charon/sa/tasks/ike_config.c @@ -13,7 +13,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: ike_config.c 4129 2008-07-01 06:36:52Z martin $ + * $Id: ike_config.c 4867 2009-02-13 11:57:50Z andreas $ */ #include "ike_config.h" @@ -21,6 +21,9 @@ #include <daemon.h> #include <encoding/payloads/cp_payload.h> +#define DNS_SERVER_MAX 2 +#define NBNS_SERVER_MAX 2 + typedef struct private_ike_config_t private_ike_config_t; /** @@ -52,6 +55,11 @@ struct private_ike_config_t { * list of DNS servers */ linked_list_t *dns; + + /** + * list of WINS servers + */ + linked_list_t *nbns; }; /** @@ -121,7 +129,10 @@ static void build_payloads(private_ike_config_t *this, message_t *message, else { host_t *ip; - iterator_t *iterator = this->dns->create_iterator(this->dns, TRUE); + iterator_t *iterator; + + /* Add internal DNS servers */ + iterator = this->dns->create_iterator(this->dns, TRUE); while (iterator->iterate(iterator, (void**)&ip)) { ca = configuration_attribute_create(); @@ -138,6 +149,25 @@ static void build_payloads(private_ike_config_t *this, message_t *message, cp->add_configuration_attribute(cp, ca); } iterator->destroy(iterator); + + /* Add internal WINS servers */ + iterator = this->nbns->create_iterator(this->nbns, TRUE); + while (iterator->iterate(iterator, (void**)&ip)) + { + ca = configuration_attribute_create(); + if (ip->get_family(ip) == AF_INET) + { + ca->set_type(ca, INTERNAL_IP4_NBNS); + } + else + { + ca->set_type(ca, INTERNAL_IP6_NBNS); + } + chunk = ip->get_address(ip); + ca->set_value(ca, chunk); + cp->add_configuration_attribute(cp, ca); + } + iterator->destroy(iterator); } message->add_payload(message, (payload_t*)cp); } @@ -201,7 +231,22 @@ static void process_attribute(private_ike_config_t *this, } case INTERNAL_IP4_NBNS: case INTERNAL_IP6_NBNS: - /* TODO */ + { + addr = ca->get_value(ca); + if (addr.len == 0) + { + ip = host_create_any(family); + } + else + { + ip = host_create_from_chunk(family, addr, 0); + } + if (ip) + { + this->nbns->insert_last(this->nbns, ip); + } + break; + } default: DBG1(DBG_IKE, "ignoring %N config attribute", configuration_attribute_type_names, @@ -351,7 +396,7 @@ static status_t process_i(private_ike_config_t *this, message_t *message) process_payloads(this, message); if (this->virtual_ip == NULL) - { /* force a configured virtual IP, even server didn't return one */ + { /* force a configured virtual IP, even if server didn't return one */ config = this->ike_sa->get_peer_cfg(this->ike_sa); this->virtual_ip = config->get_virtual_ip(config); if (this->virtual_ip) @@ -406,6 +451,7 @@ static void destroy(private_ike_config_t *this) { DESTROY_IF(this->virtual_ip); this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + this->nbns->destroy_offset(this->nbns, offsetof(host_t, destroy)); free(this); } @@ -420,6 +466,12 @@ ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator) this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate; this->public.task.destroy = (void(*)(task_t*))destroy; + this->initiator = initiator; + this->ike_sa = ike_sa; + this->virtual_ip = NULL; + this->dns = linked_list_create(); + this->nbns = linked_list_create(); + if (initiator) { this->public.task.build = (status_t(*)(task_t*,message_t*))build_i; @@ -427,13 +479,49 @@ ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator) } else { + int i; + + /* assign DNS servers */ + for (i = 1; i <= DNS_SERVER_MAX; i++) + { + char dns_key[16], *dns_str; + + snprintf(dns_key, sizeof(dns_key), "charon.dns%d", i); + dns_str = lib->settings->get_str(lib->settings, dns_key, NULL); + if (dns_str) + { + host_t *dns = host_create_from_string(dns_str, 0); + + if (dns) + { + DBG2(DBG_CFG, "assigning DNS server %H to peer", dns); + this->dns->insert_last(this->dns, dns); + } + } + } + + /* assign WINS servers */ + for (i = 1; i <= NBNS_SERVER_MAX; i++) + { + char nbns_key[16], *nbns_str; + + snprintf(nbns_key, sizeof(nbns_key), "charon.nbns%d", i); + nbns_str = lib->settings->get_str(lib->settings, nbns_key, NULL); + if (nbns_str) + { + host_t *nbns = host_create_from_string(nbns_str, 0); + + if (nbns) + { + DBG2(DBG_CFG, "assigning NBNS server %H to peer", nbns); + this->nbns->insert_last(this->nbns, nbns); + } + } + } + this->public.task.build = (status_t(*)(task_t*,message_t*))build_r; this->public.task.process = (status_t(*)(task_t*,message_t*))process_r; } - this->initiator = initiator; - this->ike_sa = ike_sa; - this->virtual_ip = NULL; - this->dns = linked_list_create(); - + return &this->public; } diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c index bd2cd39bb..139107480 100644 --- a/src/charon/sa/tasks/ike_init.c +++ b/src/charon/sa/tasks/ike_init.c @@ -14,7 +14,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: ike_init.c 4531 2008-10-30 12:58:54Z martin $ + * $Id: ike_init.c 4717 2008-11-28 09:51:44Z martin $ */ #include "ike_init.h" @@ -370,13 +370,46 @@ static status_t process_r(private_ike_init_t *this, message_t *message) } /** - * Implementation of task_t.build for responder + * Derive the keymat for the IKE_SA */ -static status_t build_r(private_ike_init_t *this, message_t *message) +static bool derive_keys(private_ike_init_t *this, + chunk_t nonce_i, chunk_t nonce_r) { - keymat_t *old_keymat = NULL; + keymat_t *old_keymat; + pseudo_random_function_t prf_alg = PRF_UNDEFINED; + chunk_t skd = chunk_empty; ike_sa_id_t *id; + id = this->ike_sa->get_id(this->ike_sa); + if (this->old_sa) + { + /* rekeying: Include old SKd, use old PRF, apply SPI */ + old_keymat = this->old_sa->get_keymat(this->old_sa); + prf_alg = old_keymat->get_skd(old_keymat, &skd); + if (this->initiator) + { + id->set_responder_spi(id, this->proposal->get_spi(this->proposal)); + } + else + { + id->set_initiator_spi(id, this->proposal->get_spi(this->proposal)); + } + } + if (!this->keymat->derive_ike_keys(this->keymat, this->proposal, this->dh, + nonce_i, nonce_r, id, prf_alg, skd)) + { + return FALSE; + } + charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, + nonce_i, nonce_r, this->old_sa); + return TRUE; +} + +/** + * Implementation of task_t.build for responder + */ +static status_t build_r(private_ike_init_t *this, message_t *message) +{ /* check if we have everything we need */ if (this->proposal == NULL || this->other_nonce.len == 0 || this->my_nonce.len == 0) @@ -410,23 +443,12 @@ static status_t build_r(private_ike_init_t *this, message_t *message) return FAILED; } - id = this->ike_sa->get_id(this->ike_sa); - if (this->old_sa) - { /* rekeying: Apply SPI, include keymat from old SA in key derivation */ - id->set_initiator_spi(id, this->proposal->get_spi(this->proposal)); - old_keymat = this->old_sa->get_keymat(this->old_sa); - } - if (!this->keymat->derive_ike_keys(this->keymat, this->proposal, this->dh, - this->other_nonce, this->my_nonce, id, old_keymat)) + if (!derive_keys(this, this->other_nonce, this->my_nonce)) { DBG1(DBG_IKE, "key derivation failed"); message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty); return FAILED; } - - charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, - this->other_nonce, this->my_nonce, this->old_sa); - build_payloads(this, message); return SUCCESS; } @@ -436,8 +458,6 @@ static status_t build_r(private_ike_init_t *this, message_t *message) */ static status_t process_i(private_ike_init_t *this, message_t *message) { - keymat_t *old_keymat = NULL; - ike_sa_id_t *id; iterator_t *iterator; payload_t *payload; @@ -521,22 +541,11 @@ static status_t process_i(private_ike_init_t *this, message_t *message) return FAILED; } - id = this->ike_sa->get_id(this->ike_sa); - if (this->old_sa) - { /* rekeying: Apply SPI, include keymat from old SA in key derivation */ - id->set_responder_spi(id, this->proposal->get_spi(this->proposal)); - old_keymat = this->old_sa->get_keymat(this->old_sa); - } - if (!this->keymat->derive_ike_keys(this->keymat, this->proposal, this->dh, - this->my_nonce, this->other_nonce, id, old_keymat)) + if (!derive_keys(this, this->my_nonce, this->other_nonce)) { DBG1(DBG_IKE, "key derivation failed"); return FAILED; } - - charon->bus->ike_keys(charon->bus, this->ike_sa, this->dh, - this->my_nonce, this->other_nonce, this->old_sa); - return SUCCESS; } diff --git a/src/charon/sa/tasks/ike_mobike.c b/src/charon/sa/tasks/ike_mobike.c index a791d1892..b5e065081 100644 --- a/src/charon/sa/tasks/ike_mobike.c +++ b/src/charon/sa/tasks/ike_mobike.c @@ -12,7 +12,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: ike_mobike.c 4618 2008-11-11 09:22:00Z tobias $ + * $Id: ike_mobike.c 4816 2008-12-19 14:34:40Z martin $ */ #include "ike_mobike.h" @@ -24,6 +24,7 @@ #include <encoding/payloads/notify_payload.h> #define COOKIE2_SIZE 16 +#define MAX_ADDITIONAL_ADDRS 8 typedef struct private_ike_mobike_t private_ike_mobike_t; @@ -191,8 +192,8 @@ static void build_address_list(private_ike_mobike_t *this, message_t *message) enumerator_t *enumerator; host_t *host, *me; notify_type_t type; - bool additional = FALSE; - + int added = 0; + me = this->ike_sa->get_my_host(this->ike_sa); enumerator = charon->kernel_interface->create_address_enumerator( charon->kernel_interface, FALSE, FALSE); @@ -214,9 +215,13 @@ static void build_address_list(private_ike_mobike_t *this, message_t *message) continue; } message->add_notify(message, FALSE, type, host->get_address(host)); - additional = TRUE; + if (++added >= MAX_ADDITIONAL_ADDRS) + { /* limit number of notifys, some implementations do not like too + * many of them (f.e. strongSwan ;-) */ + break; + } } - if (!additional) + if (!added) { message->add_notify(message, FALSE, NO_ADDITIONAL_ADDRESSES, chunk_empty); } @@ -251,7 +256,7 @@ static void update_children(private_ike_mobike_t *this) iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa); while (iterator->iterate(iterator, (void**)&child_sa)) { - if (child_sa->update_hosts(child_sa, + if (child_sa->update(child_sa, this->ike_sa->get_my_host(this->ike_sa), this->ike_sa->get_other_host(this->ike_sa), this->ike_sa->get_virtual_ip(this->ike_sa, TRUE), @@ -516,6 +521,10 @@ static status_t process_i(private_ike_mobike_t *this, message_t *message) /* start the update with the same task */ this->check = FALSE; this->address = FALSE; + if (this->natd) + { + this->natd->task.destroy(&this->natd->task); + } this->natd = ike_natd_create(this->ike_sa, this->initiator); this->ike_sa->set_pending_updates(this->ike_sa, 1); return NEED_MORE; diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c index 28d63cca7..e61d161bc 100644 --- a/src/charon/sa/tasks/ike_rekey.c +++ b/src/charon/sa/tasks/ike_rekey.c @@ -13,7 +13,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * $Id: ike_rekey.c 4659 2008-11-14 14:05:47Z martin $ + * $Id: ike_rekey.c 4730 2008-12-01 18:38:28Z martin $ */ #include "ike_rekey.h" @@ -21,6 +21,7 @@ #include <daemon.h> #include <encoding/payloads/notify_payload.h> #include <sa/tasks/ike_init.h> +#include <sa/tasks/ike_delete.h> #include <processing/jobs/delete_ike_sa_job.h> #include <processing/jobs/rekey_ike_sa_job.h> @@ -58,12 +59,36 @@ struct private_ike_rekey_t { ike_init_t *ike_init; /** + * IKE_DELETE task to delete the old IKE_SA after rekeying was successful + */ + ike_delete_t *ike_delete; + + /** * colliding task detected by the task manager */ task_t *collision; }; /** + * Implementation of task_t.build for initiator, after rekeying + */ +static status_t build_i_delete(private_ike_rekey_t *this, message_t *message) +{ + /* update exchange type to INFORMATIONAL for the delete */ + message->set_exchange_type(message, INFORMATIONAL); + + return this->ike_delete->task.build(&this->ike_delete->task, message); +} + +/** + * Implementation of task_t.process for initiator, after rekeying + */ +static status_t process_i_delete(private_ike_rekey_t *this, message_t *message) +{ + return this->ike_delete->task.process(&this->ike_delete->task, message); +} + +/** * Implementation of task_t.build for initiator */ static status_t build_i(private_ike_rekey_t *this, message_t *message) @@ -168,7 +193,6 @@ static status_t build_r(private_ike_rekey_t *this, message_t *message) */ static status_t process_i(private_ike_rekey_t *this, message_t *message) { - job_t *job; ike_sa_id_t *to_delete; iterator_t *iterator; payload_t *payload; @@ -271,10 +295,12 @@ static status_t process_i(private_ike_rekey_t *this, message_t *message) charon->bus->set_sa(charon->bus, this->ike_sa); } - job = (job_t*)delete_ike_sa_job_create(to_delete, TRUE); - charon->processor->queue_job(charon->processor, job); + /* rekeying successful, delete the IKE_SA using a subtask */ + this->ike_delete = ike_delete_create(this->ike_sa, TRUE); + this->public.task.build = (status_t(*)(task_t*,message_t*))build_i_delete; + this->public.task.process = (status_t(*)(task_t*,message_t*))process_i_delete; - return SUCCESS; + return NEED_MORE; } /** @@ -300,6 +326,10 @@ static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa) { this->ike_init->task.destroy(&this->ike_init->task); } + if (this->ike_delete) + { + this->ike_delete->task.destroy(&this->ike_delete->task); + } if (this->new_sa) { charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, @@ -308,11 +338,12 @@ static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa) charon->bus->set_sa(charon->bus, this->ike_sa); } DESTROY_IF(this->collision); - + this->collision = NULL; this->ike_sa = ike_sa; this->new_sa = NULL; this->ike_init = NULL; + this->ike_delete = NULL; } /** @@ -339,6 +370,10 @@ static void destroy(private_ike_rekey_t *this) { this->ike_init->task.destroy(&this->ike_init->task); } + if (this->ike_delete) + { + this->ike_delete->task.destroy(&this->ike_delete->task); + } DESTROY_IF(this->collision); free(this); } @@ -368,6 +403,7 @@ ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator) this->ike_sa = ike_sa; this->new_sa = NULL; this->ike_init = NULL; + this->ike_delete = NULL; this->initiator = initiator; this->collision = NULL; |