diff options
author | Martin Willi <martin@strongswan.org> | 2006-09-05 14:07:25 +0000 |
---|---|---|
committer | Martin Willi <martin@strongswan.org> | 2006-09-05 14:07:25 +0000 |
commit | a655f5c09c2ba180b7d393dbdfc8b8057293d9ab (patch) | |
tree | e645a61c178ebcb932a56f09e4bdcca80b230431 | |
parent | da8ab11e918353293953636abea73f12bf8f956e (diff) | |
download | strongswan-a655f5c09c2ba180b7d393dbdfc8b8057293d9ab.tar.bz2 strongswan-a655f5c09c2ba180b7d393dbdfc8b8057293d9ab.tar.xz |
reuse reqid when a ROUTED child_sa gets INSTALLED
fixed a bug in retransmission code
added support for the "keyingtries" ipsec.conf parameter
added support for the "dpddelay" ipsec.conf parameter
done some work for "dpdaction" behavior
some other cleanups and fixes
35 files changed, 552 insertions, 477 deletions
diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am index 024205ac0..330aefcca 100644 --- a/src/charon/Makefile.am +++ b/src/charon/Makefile.am @@ -39,10 +39,10 @@ encoding/payloads/vendor_id_payload.h encoding/payloads/proposal_substructure.c encoding/parser.h encoding/message.c encoding/generator.c encoding/message.h encoding/generator.h \ encoding/parser.c daemon.c daemon.h network/packet.c \ network/socket.c network/packet.h network/socket.h queues/jobs/job.h queues/jobs/job.c \ -queues/jobs/delete_established_ike_sa_job.c queues/jobs/retransmit_request_job.h queues/jobs/initiate_job.h \ -queues/jobs/incoming_packet_job.c queues/jobs/delete_half_open_ike_sa_job.c \ -queues/jobs/delete_established_ike_sa_job.h queues/jobs/delete_half_open_ike_sa_job.h \ -queues/jobs/incoming_packet_job.h queues/jobs/retransmit_request_job.c queues/jobs/initiate_job.c \ +queues/jobs/retransmit_request_job.h queues/jobs/initiate_job.h \ +queues/jobs/incoming_packet_job.h queues/jobs/incoming_packet_job.c \ +queues/jobs/delete_ike_sa_job.c queues/jobs/delete_ike_sa_job.h \ +queues/jobs/retransmit_request_job.c queues/jobs/initiate_job.c \ queues/jobs/send_keepalive_job.c queues/jobs/send_keepalive_job.h \ queues/jobs/rekey_child_sa_job.c queues/jobs/rekey_child_sa_job.h queues/jobs/delete_child_sa_job.c queues/jobs/delete_child_sa_job.h \ queues/jobs/send_dpd_job.c queues/jobs/send_dpd_job.h queues/jobs/route_job.c queues/jobs/route_job.h \ diff --git a/src/charon/config/configuration.c b/src/charon/config/configuration.c index 2dcf0bb72..e983e5968 100755 --- a/src/charon/config/configuration.c +++ b/src/charon/config/configuration.c @@ -29,38 +29,77 @@ /** * Timeout in milliseconds after that a half open IKE_SA gets deleted. - * Set to zero to disable */ #define HALF_OPEN_IKE_SA_TIMEOUT 30000 /** + * The retransmission algorithm uses a multiple sequences. + * Each sequence contains multiple retransmits. Those retransmits + * are sent using a exponential backoff algorithm. The sequences + * are retried with linear timings: + * + * <------sequence---------><------sequence---------><------sequence---------> + * + * T-R---R-----R---------R--R-R---R-----R---------R--R-R---R-----R---------R--X + * + * T = first transmit + * R = retransmit + * X = giving up, peer is dead + * + * if (retransmit >= TRIES * sequences) + * => abort + * TIMEOUT * (BASE ** (try % TRIES)) + * + * Using an initial TIMEOUT of 4s, a BASE of 1.8, 5 TRIES + * per sequnce and 3 sequences, this gives us: + * + * | relative | absolute + * --------------------------------------------------------- + * 4s * (1.8 ** (0 % 5)) = 4s 4s + * 4s * (1.8 ** (1 % 5)) = 7s 11s + * 4s * (1.8 ** (2 % 5)) = 13s 24s + * 4s * (1.8 ** (3 % 5)) = 23s 47s + * 4s * (1.8 ** (4 % 5)) = 42s 89s + * 4s * (1.8 ** (5 % 5)) = 76s 165s + * 4s * (1.8 ** (6 % 5)) = 4s 169s + * 4s * (1.8 ** (7 % 5)) = 7s 176s + * 4s * (1.8 ** (8 % 5)) = 13s 189s + * 4s * (1.8 ** (9 % 5)) = 23s 212s + * 4s * (1.8 ** (10 % 5)) = 42s 254s + * 4s * (1.8 ** (11 % 5)) = 76s 330s + * 4s * (1.8 ** (12 % 5)) = 4s 334 + * 4s * (1.8 ** (13 % 5)) = 7s 341s + * 4s * (1.8 ** (14 % 5)) = 13s 354s + * 4s * (1.8 ** (15 % 5)) = 23s 377s + * 4s * (1.8 ** (16 % 5)) = 42s 419s + * 4s * (1.8 ** (17 % 5)) = 76s 495s + * + * If the configuration uses 1 sequence, the peer is considered dead + * after 2min 45s when no reply comes in. If it uses 3 sequences, after + * 8min 15s the DPD action is executed... + */ + +/** * First retransmit timeout in milliseconds. * Timeout value is increasing in each retransmit round. */ -#define RETRANSMIT_TIMEOUT 6000 +#define RETRANSMIT_TIMEOUT 4000 /** * Base which is raised to the power of the retransmission count. */ -#define RETRANSMIT_BASE 1.5 +#define RETRANSMIT_BASE 1.8 /** - * Max retransmit count. - * 0 for infinite. The max time a half open IKE_SA is alive is set by - * RETRANSMIT_TIMEOUT. + * Number of retransmits done in a retransmit sequence */ -#define MAX_RETRANSMIT_COUNT 6 +#define RETRANSMIT_TRIES 5 /** * Keepalive interval in seconds. */ #define KEEPALIVE_INTERVAL 20 -/** - * DPD interval in seconds. - */ -#define DPD_INTERVAL 60 - typedef struct private_configuration_t private_configuration_t; @@ -79,13 +118,18 @@ struct private_configuration_t { /** * Implementation of configuration_t.get_retransmit_timeout. */ -static u_int32_t get_retransmit_timeout (private_configuration_t *this, u_int32_t retransmit_count) +static u_int32_t get_retransmit_timeout (private_configuration_t *this, + u_int32_t retransmit_count, + u_int32_t max_sequences) { - if (retransmit_count > MAX_RETRANSMIT_COUNT && MAX_RETRANSMIT_COUNT != 0) + if (max_sequences != 0 && + retransmit_count >= RETRANSMIT_TRIES * max_sequences) { + /* give up */ return 0; } - return (u_int32_t)(RETRANSMIT_TIMEOUT * pow(RETRANSMIT_BASE, retransmit_count)); + return (u_int32_t)(RETRANSMIT_TIMEOUT * + pow(RETRANSMIT_BASE, retransmit_count % RETRANSMIT_TRIES)); } /** @@ -105,14 +149,6 @@ static u_int32_t get_keepalive_interval (private_configuration_t *this) } /** - * Implementation of configuration_t.get_dpd_interval. - */ -static u_int32_t get_dpd_interval (private_configuration_t *this) -{ - return DPD_INTERVAL; -} - -/** * Implementation of configuration_t.destroy. */ static void destroy(private_configuration_t *this) @@ -129,10 +165,9 @@ configuration_t *configuration_create() /* public functions */ this->public.destroy = (void(*)(configuration_t*))destroy; - this->public.get_retransmit_timeout = (u_int32_t (*) (configuration_t *, u_int32_t retransmit_count))get_retransmit_timeout; - this->public.get_half_open_ike_sa_timeout = (u_int32_t (*) (configuration_t *)) get_half_open_ike_sa_timeout; - this->public.get_keepalive_interval = (u_int32_t (*) (configuration_t *)) get_keepalive_interval; - this->public.get_dpd_interval = (u_int32_t (*) (configuration_t *)) get_dpd_interval; + this->public.get_retransmit_timeout = (u_int32_t (*) (configuration_t*,u_int32_t,u_int32_t))get_retransmit_timeout; + this->public.get_half_open_ike_sa_timeout = (u_int32_t (*) (configuration_t*)) get_half_open_ike_sa_timeout; + this->public.get_keepalive_interval = (u_int32_t (*) (configuration_t*)) get_keepalive_interval; return (&this->public); } diff --git a/src/charon/config/configuration.h b/src/charon/config/configuration.h index 553a01edd..ee25872fc 100755 --- a/src/charon/config/configuration.h +++ b/src/charon/config/configuration.h @@ -36,34 +36,33 @@ typedef struct configuration_t configuration_t; * * @ingroup config */ -struct configuration_t { +struct configuration_t { /** * @brief Returns the retransmit timeout. * - * A return value of zero means the request should not retransmitted again. - * The timeout values are managed by the configuration, so - * another backoff algorithm may be implemented here. + * A return value of zero means the request should not be retransmitted again. + * The retransmission algorithm uses sequences of retransmits, in which + * every sequence contains exponential delayed retransmits. These + * sequences are compareable to the keyingtries mechanism used in pluto. * * @param this calling object - * @param retransmit_count number of times a message was retransmitted so far + * @param retransmitted number of times a message was retransmitted so far + * @param max_sequences maximum number of retransmission sequences to allow * @return time in milliseconds, when to schedule next retransmit */ - u_int32_t (*get_retransmit_timeout) (configuration_t *this, u_int32_t retransmit_count); + u_int32_t (*get_retransmit_timeout) (configuration_t *this, + u_int32_t retransmitted, + u_int32_t max_sequences); /** * @brief Returns the timeout for an half open IKE_SA in ms. - * - * Half open means that the IKE_SA is still in one of the following states: - * - INITIATOR_INIT - * - RESPONDER_INIT - * - IKE_SA_INIT_REQUESTED - * - IKE_SA_INIT_RESPONDED - * - IKE_AUTH_REQUESTED - * + * + * Half open means that the IKE_SA is still on a not established state + * * @param this calling object * @return timeout in milliseconds (ms) - */ + */ u_int32_t (*get_half_open_ike_sa_timeout) (configuration_t *this); /** @@ -78,17 +77,6 @@ struct configuration_t { u_int32_t (*get_keepalive_interval) (configuration_t *this); /** - * @brief Returns the DPD interval in ms. - * - * The DPD interval defines the time after which a - * DPD request packet should be sent. - * - * @param this calling object - * @return interval in seconds - */ - u_int32_t (*get_dpd_interval) (configuration_t *this); - - /** * @brief Destroys a configuration_t object. * * @param this calling object diff --git a/src/charon/config/connections/connection.c b/src/charon/config/connections/connection.c index 281d43d9c..6b8192beb 100644 --- a/src/charon/config/connections/connection.c +++ b/src/charon/config/connections/connection.c @@ -106,6 +106,16 @@ struct private_connection_t { auth_method_t auth_method; /** + * Interval to send DPD liveness checks on inactivity + */ + u_int32_t dpd_delay; + + /** + * Number of retransmission sequences to send bevore giving up + */ + u_int32_t retrans_sequences; + + /** * Supported proposals */ linked_list_t *proposals; @@ -249,6 +259,22 @@ static auth_method_t get_auth_method(private_connection_t *this) } /** + * Implementation of connection_t.get_dpd_delay. + */ +static u_int32_t get_dpd_delay(private_connection_t *this) +{ + return this->dpd_delay; +} + +/** + * Implementation of connection_t.get_retrans_seq. + */ +static u_int32_t get_retrans_seq(private_connection_t *this) +{ + return this->retrans_sequences; +} + +/** * Implementation of connection_t.get_dh_group. */ static diffie_hellman_group_t get_dh_group(private_connection_t *this) @@ -359,6 +385,8 @@ connection_t * connection_create(char *name, bool ikev2, cert_policy_t certreq_policy, host_t *my_host, host_t *other_host, auth_method_t auth_method, + u_int32_t dpd_delay, + u_int32_t retrans_sequences, u_int32_t hard_lifetime, u_int32_t soft_lifetime, u_int32_t jitter) { @@ -375,6 +403,8 @@ connection_t * connection_create(char *name, bool ikev2, this->public.select_proposal = (proposal_t*(*)(connection_t*,linked_list_t*))select_proposal; this->public.add_proposal = (void(*)(connection_t*, proposal_t*)) add_proposal; this->public.get_auth_method = (auth_method_t(*)(connection_t*)) get_auth_method; + this->public.get_dpd_delay = (u_int32_t(*)(connection_t*)) get_dpd_delay; + this->public.get_retrans_seq = (u_int32_t(*)(connection_t*)) get_retrans_seq; this->public.get_dh_group = (diffie_hellman_group_t(*)(connection_t*)) get_dh_group; this->public.check_dh_group = (bool(*)(connection_t*,diffie_hellman_group_t)) check_dh_group; this->public.get_soft_lifetime = (u_int32_t (*) (connection_t *))get_soft_lifetime; @@ -391,6 +421,8 @@ connection_t * connection_create(char *name, bool ikev2, this->my_host = my_host; this->other_host = other_host; this->auth_method = auth_method; + this->dpd_delay = dpd_delay; + this->retrans_sequences = retrans_sequences; this->hard_lifetime = hard_lifetime; this->soft_lifetime = soft_lifetime; this->jitter = jitter; diff --git a/src/charon/config/connections/connection.h b/src/charon/config/connections/connection.h index 61ca9cfc0..c425590ae 100644 --- a/src/charon/config/connections/connection.h +++ b/src/charon/config/connections/connection.h @@ -164,7 +164,7 @@ struct connection_t { proposal_t *(*select_proposal) (connection_t *this, linked_list_t *proposals); /** - * @brief Get the authentication method to use + * @brief Get the authentication method to use. * * @param this calling object * @return authentication method @@ -172,6 +172,25 @@ struct connection_t { auth_method_t (*get_auth_method) (connection_t *this); /** + * @brief Get the DPD check interval. + * + * @param this calling object + * @return dpd_delay in seconds + */ + u_int32_t (*get_dpd_delay) (connection_t *this); + + /** + * @brief Get the max number of retransmission sequences. + * + * After this number of sequences, a not responding peer is considered + * dead. + * + * @param this calling object + * @return max number of retransmission sequences + */ + u_int32_t (*get_retrans_seq) (connection_t *this); + + /** * @brief Get the connection name. * * Name must not be freed, since it points to @@ -281,9 +300,12 @@ struct connection_t { /** * @brief Creates a connection_t object. * - * Supplied hosts become owned by connection, so - * do not modify or destroy them after a call to + * Supplied hosts become owned by connection, so + * do not modify or destroy them after a call to * connection_create(). Name gets cloned internally. + * The retrasmit sequence number says how fast we give up when the peer + * does not respond. A high value may bridge-over temporary connection + * problems, a small value can detect dead peers faster. * * @param name connection identifier * @param ikev2 TRUE if this is an IKEv2 connection @@ -292,6 +314,8 @@ struct connection_t { * @param my_host host_t representing local address * @param other_host host_t representing remote address * @param auth_method Authentication method to use for our(!) auth data + * @param dpd_delay interval of DPD liveness checks + * @param retrans_sequences number of retransmit sequences to use * @param hard_lifetime lifetime before deleting an IKE_SA * @param soft_lifetime lifetime before rekeying an IKE_SA * @param jitter range of randomization time @@ -302,7 +326,9 @@ struct connection_t { connection_t * connection_create(char *name, bool ikev2, cert_policy_t cert_pol, cert_policy_t req_pol, host_t *my_host, host_t *other_host, - auth_method_t auth_method, u_int32_t hard_lifetime, - u_int32_t soft_lifetime, u_int32_t jitter); + auth_method_t auth_method, + u_int32_t dpd_delay, u_int32_t retrans_sequences, + u_int32_t hard_lifetime, u_int32_t soft_lifetime, + u_int32_t jitter); #endif /* CONNECTION_H_ */ diff --git a/src/charon/config/policies/policy.c b/src/charon/config/policies/policy.c index 49983ade7..71fadbdfa 100644 --- a/src/charon/config/policies/policy.c +++ b/src/charon/config/policies/policy.c @@ -110,6 +110,11 @@ struct private_policy_t { u_int32_t jitter; /** + * Should the SA get ROUTED when peer detected as dead? + */ + bool dpd_route; + + /** * logger */ logger_t *logger; @@ -441,7 +446,7 @@ static void destroy(private_policy_t *this) */ policy_t *policy_create(char *name, identification_t *my_id, identification_t *other_id, u_int32_t hard_lifetime, u_int32_t soft_lifetime, - u_int32_t jitter, char *updown) + u_int32_t jitter, char *updown, bool dpd_route) { private_policy_t *this = malloc_thing(private_policy_t); @@ -473,6 +478,7 @@ policy_t *policy_create(char *name, identification_t *my_id, identification_t *o this->soft_lifetime = soft_lifetime; this->jitter = jitter; this->updown = (updown == NULL) ? NULL : strdup(updown); + this->dpd_route = dpd_route; /* initialize private members*/ this->refcount = 1; diff --git a/src/charon/config/policies/policy.h b/src/charon/config/policies/policy.h index 454f69c12..9505e53fd 100644 --- a/src/charon/config/policies/policy.h +++ b/src/charon/config/policies/policy.h @@ -264,6 +264,7 @@ struct policy_t { * @param soft_lifetime lifetime before rekeying an SA * @param jitter range of randomization time * @param updown updown script to execute on up/down event + * @param dpd_route should the connection go to routed state if DPD detected? * @return policy_t object * * @ingroup config @@ -271,6 +272,6 @@ struct policy_t { policy_t *policy_create(char *name, identification_t *my_id, identification_t *other_id, u_int32_t hard_lifetime, u_int32_t soft_lifetime, - u_int32_t jitter, char *updown); + u_int32_t jitter, char *updown, bool dpd_route); #endif /* POLICY_H_ */ diff --git a/src/charon/config/traffic_selector.c b/src/charon/config/traffic_selector.c index 24cf7eaad..d8293b808 100644 --- a/src/charon/config/traffic_selector.c +++ b/src/charon/config/traffic_selector.c @@ -464,24 +464,16 @@ static u_int8_t get_protocol(private_traffic_selector_t *this) */ static void update_address_range(private_traffic_selector_t *this, host_t *host) { - if (host->get_family(host) == AF_INET && this->type == TS_IPV4_ADDR_RANGE) + if ((this->type == TS_IPV4_ADDR_RANGE && this->from4[0] == 0) || + (this->type == TS_IPV6_ADDR_RANGE && this->from6[0] == 0 && + this->from6[1] == 0 && this->from6[2] == 0 && this->from6[3] == 0)) { - if (this->from4[0] == 0) - { - chunk_t from = host->get_address(host); - memcpy(this->from4, from.ptr, from.len); - memcpy(this->to4, from.ptr, from.len); - } - } - if (host->get_family(host) == AF_INET6 && this->type == TS_IPV6_ADDR_RANGE) - { - if (this->from6[0] == 0 && this->from6[1] == 0 && - this->from6[2] == 0 && this->from6[3] == 0) - { - chunk_t from = host->get_address(host); - memcpy(this->from6, from.ptr, from.len); - memcpy(this->to6, from.ptr, from.len); - } + this->type = host->get_family(host) == AF_INET ? + TS_IPV4_ADDR_RANGE : TS_IPV6_ADDR_RANGE; + + chunk_t from = host->get_address(host); + memcpy(this->from, from.ptr, from.len); + memcpy(this->to, from.ptr, from.len); } update_string(this); } diff --git a/src/charon/doc/Todo-list.txt b/src/charon/doc/Todo-list.txt index 10cd2f930..1d21d927e 100644 --- a/src/charon/doc/Todo-list.txt +++ b/src/charon/doc/Todo-list.txt @@ -63,9 +63,12 @@ - configure flag which allows to ommit vendor id in pluto - use dpdaction/dpddelay parameters from ipsec.conf - ikelifetime should optionally enforce reauthentication +- cookies/DDoS prevention - implement a mechanism against thread exhaustion when a blocked IKE_SA receives a lot of messages - add a crl fetch mechanism which synchronizes equal fetches - add support for CERTREQs - use same reqid for routed connections when they are set up +- if a CHILD_SA gets created, check if it is already ROUTED somewhere: + - remove that route and reuse reqid - add firewall script support diff --git a/src/charon/queues/jobs/delete_established_ike_sa_job.c b/src/charon/queues/jobs/delete_established_ike_sa_job.c deleted file mode 100644 index e5230e532..000000000 --- a/src/charon/queues/jobs/delete_established_ike_sa_job.c +++ /dev/null @@ -1,99 +0,0 @@ -/** - * @file delete_established_ike_sa_job.c - * - * @brief Implementation of delete_established_ike_sa_job_t. - * - */ - -/* - * Copyright (C) 2005-2006 Martin Willi - * Copyright (C) 2005 Jan Hutter - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include "delete_established_ike_sa_job.h" - -#include <daemon.h> - - -typedef struct private_delete_established_ike_sa_job_t private_delete_established_ike_sa_job_t; - -/** - * Private data of an delete_established_ike_sa_job_t object. - */ -struct private_delete_established_ike_sa_job_t { - /** - * Public delete_established_ike_sa_job_t interface. - */ - delete_established_ike_sa_job_t public; - - /** - * ID of the ike_sa to delete. - */ - ike_sa_id_t *ike_sa_id; - - /** - * Logger ref - */ - logger_t *logger; -}; - -/** - * Implementation of job_t.get_type. - */ -static job_type_t get_type(private_delete_established_ike_sa_job_t *this) -{ - return DELETE_ESTABLISHED_IKE_SA; -} - - -/** - * Implementation of job_t.execute. - */ -static status_t execute(private_delete_established_ike_sa_job_t *this) -{ - if (charon->ike_sa_manager->delete(charon->ike_sa_manager, - this->ike_sa_id) != SUCCESS) - { - this->logger->log(this->logger, ERROR|LEVEL1, "IKE SA didn't exist anymore"); - } - return DESTROY_ME; -} - -/** - * Implementation of job_t.destroy. - */ -static void destroy(private_delete_established_ike_sa_job_t *this) -{ - this->ike_sa_id->destroy(this->ike_sa_id); - free(this); -} - -/* - * Described in header - */ -delete_established_ike_sa_job_t *delete_established_ike_sa_job_create(ike_sa_id_t *ike_sa_id) -{ - private_delete_established_ike_sa_job_t *this = malloc_thing(private_delete_established_ike_sa_job_t); - - /* interface functions */ - this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; - this->public.job_interface.execute = (status_t (*) (job_t *)) execute; - this->public.job_interface.destroy = (void (*)(job_t*)) destroy; - - /* private variables */ - this->ike_sa_id = ike_sa_id->clone(ike_sa_id); - this->logger = logger_manager->get_logger(logger_manager, WORKER); - - return &(this->public); -} diff --git a/src/charon/queues/jobs/delete_established_ike_sa_job.h b/src/charon/queues/jobs/delete_established_ike_sa_job.h deleted file mode 100644 index 9f04c7846..000000000 --- a/src/charon/queues/jobs/delete_established_ike_sa_job.h +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @file delete_established_ike_sa_job.h - * - * @brief Interface of delete_established_ike_sa_job_t. - * - */ - -/* - * Copyright (C) 2005-2006 Martin Willi - * Copyright (C) 2005 Jan Hutter - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#ifndef DELETE_ESTABLISHED_IKE_SA_JOB_H_ -#define DELETE_ESTABLISHED_IKE_SA_JOB_H_ - -#include <types.h> -#include <sa/ike_sa_id.h> -#include <queues/jobs/job.h> - - -typedef struct delete_established_ike_sa_job_t delete_established_ike_sa_job_t; - -/** - * @brief Class representing an DELETE_ESTABLISHED_IKE_SA Job. - * - * This job initiates the deletion of an IKE_SA. The SA - * to delete is specified via an ike_sa_id_t. - * - * @b Constructors: - * - delete_established_ike_sa_job_create() - * - * @ingroup jobs - */ -struct delete_established_ike_sa_job_t { - /** - * The job_t interface. - */ - job_t job_interface; -}; - -/** - * @brief Creates a job of type DELETE_ESTABLISHED_IKE_SA. - * - * @param ike_sa_id id of the IKE_SA to delete - * @return delete_established_ike_sa_job_t object - * - * @ingroup jobs - */ -delete_established_ike_sa_job_t *delete_established_ike_sa_job_create(ike_sa_id_t *ike_sa_id); - -#endif /*DELETE_ESTABLISHED_IKE_SA_JOB_H_*/ diff --git a/src/charon/queues/jobs/delete_half_open_ike_sa_job.c b/src/charon/queues/jobs/delete_half_open_ike_sa_job.c deleted file mode 100644 index 7b79480bf..000000000 --- a/src/charon/queues/jobs/delete_half_open_ike_sa_job.c +++ /dev/null @@ -1,117 +0,0 @@ -/** - * @file delete_half_open_ike_sa_job.c - * - * @brief Implementation of delete_half_open_ike_sa_job_t. - * - */ - -/* - * Copyright (C) 2005-2006 Martin Willi - * Copyright (C) 2005 Jan Hutter - * Hochschule fuer Technik Rapperswil - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#include "delete_half_open_ike_sa_job.h" - -#include <daemon.h> - -typedef struct private_delete_half_open_ike_sa_job_t private_delete_half_open_ike_sa_job_t; - -/** - * Private data of an delete_half_open_ike_sa_job_t Object - */ -struct private_delete_half_open_ike_sa_job_t { - /** - * public delete_half_open_ike_sa_job_t interface - */ - delete_half_open_ike_sa_job_t public; - - /** - * ID of the ike_sa to delete - */ - ike_sa_id_t *ike_sa_id; - - /** - * logger ref - */ - logger_t *logger; -}; - -/** - * Implements job_t.get_type. - */ -static job_type_t get_type(private_delete_half_open_ike_sa_job_t *this) -{ - return DELETE_HALF_OPEN_IKE_SA; -} - -/** - * Implementation of job_t.execute. - */ -static status_t execute(private_delete_half_open_ike_sa_job_t *this) -{ - ike_sa_t *ike_sa; - - ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->ike_sa_id); - if (ike_sa == NULL) - { - /* hm, somebody was faster ;-) */ - return DESTROY_ME; - } - - switch (ike_sa->get_state(ike_sa)) - { - case IKE_ESTABLISHED: - { - /* IKE_SA is established and so is not getting destroyed */ - charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); - return DESTROY_ME; - } - default: - { - /* IKE_SA is half open and gets destroyed */ - this->logger->log(this->logger, AUDIT, - "deleting half open IKE_SA after timeout"); - charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); - return DESTROY_ME; - } - } -} - -/** - * Implements job_t.destroy. - */ -static void destroy(private_delete_half_open_ike_sa_job_t *this) -{ - this->ike_sa_id->destroy(this->ike_sa_id); - free(this); -} - -/* - * Described in header - */ -delete_half_open_ike_sa_job_t *delete_half_open_ike_sa_job_create(ike_sa_id_t *ike_sa_id) -{ - private_delete_half_open_ike_sa_job_t *this = malloc_thing(private_delete_half_open_ike_sa_job_t); - - /* interface functions */ - this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; - this->public.job_interface.execute = (status_t (*) (job_t *)) execute; - this->public.job_interface.destroy = (void (*)(job_t *)) destroy;; - - /* private variables */ - this->ike_sa_id = ike_sa_id->clone(ike_sa_id); - this->logger = logger_manager->get_logger(logger_manager, WORKER); - - return &(this->public); -} diff --git a/src/charon/queues/jobs/delete_ike_sa_job.c b/src/charon/queues/jobs/delete_ike_sa_job.c new file mode 100644 index 000000000..e2a861757 --- /dev/null +++ b/src/charon/queues/jobs/delete_ike_sa_job.c @@ -0,0 +1,136 @@ +/** + * @file delete_ike_sa_job.c + * + * @brief Implementation of delete_ike_sa_job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * Copyright (C) 2005 Jan Hutter + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "delete_ike_sa_job.h" + +#include <daemon.h> + +typedef struct private_delete_ike_sa_job_t private_delete_ike_sa_job_t; + +/** + * Private data of an delete_ike_sa_job_t Object + */ +struct private_delete_ike_sa_job_t { + /** + * public delete_ike_sa_job_t interface + */ + delete_ike_sa_job_t public; + + /** + * ID of the ike_sa to delete + */ + ike_sa_id_t *ike_sa_id; + + /** + * Should the IKE_SA be deleted if it is in ESTABLISHED state? + */ + bool delete_if_established; + + /** + * logger ref + */ + logger_t *logger; +}; + +/** + * Implements job_t.get_type. + */ +static job_type_t get_type(private_delete_ike_sa_job_t *this) +{ + return DELETE_IKE_SA; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_delete_ike_sa_job_t *this) +{ + ike_sa_t *ike_sa; + + if (this->delete_if_established) + { + if (charon->ike_sa_manager->delete(charon->ike_sa_manager, + this->ike_sa_id) != SUCCESS) + { + this->logger->log(this->logger, ERROR|LEVEL1, "IKE SA didn't exist anymore"); + } + return DESTROY_ME; + } + else + { + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->ike_sa_id); + if (ike_sa == NULL) + { + /* hm, somebody was faster ;-) */ + return DESTROY_ME; + } + + switch (ike_sa->get_state(ike_sa)) + { + case IKE_ESTABLISHED: + { + /* IKE_SA is established and so is not getting destroyed */ + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; + } + default: + { + /* IKE_SA is half open and gets destroyed */ + this->logger->log(this->logger, AUDIT, + "deleting half open IKE_SA after timeout"); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; + } + } + } +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_delete_ike_sa_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +delete_ike_sa_job_t *delete_ike_sa_job_create(ike_sa_id_t *ike_sa_id, + bool delete_if_established) +{ + private_delete_ike_sa_job_t *this = malloc_thing(private_delete_ike_sa_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t *)) destroy;; + + /* private variables */ + this->ike_sa_id = ike_sa_id->clone(ike_sa_id); + this->delete_if_established = delete_if_established; + this->logger = logger_manager->get_logger(logger_manager, WORKER); + + return &(this->public); +} diff --git a/src/charon/queues/jobs/delete_half_open_ike_sa_job.h b/src/charon/queues/jobs/delete_ike_sa_job.h index 3f11d1aac..8ef68b1c6 100644 --- a/src/charon/queues/jobs/delete_half_open_ike_sa_job.h +++ b/src/charon/queues/jobs/delete_ike_sa_job.h @@ -1,7 +1,7 @@ /** - * @file delete_half_open_ike_sa_job.h + * @file delete_ike_sa_job.h * - * @brief Interface of delete_half_open_ike_sa_job_t. + * @brief Interface of delete_ike_sa_job_t. * */ @@ -21,29 +21,29 @@ * for more details. */ -#ifndef DELETE_HALF_OPEN_IKE_SA_JOB_H_ -#define DELETE_HALF_OPEN_IKE_SA_JOB_H_ +#ifndef DELETE_IKE_SA_JOB_H_ +#define DELETE_IKE_SA_JOB_H_ #include <types.h> #include <sa/ike_sa_id.h> #include <queues/jobs/job.h> -typedef struct delete_half_open_ike_sa_job_t delete_half_open_ike_sa_job_t; +typedef struct delete_ike_sa_job_t delete_ike_sa_job_t; /** - * @brief Class representing an DELETE_HALF_OPEN_IKE_SA Job. + * @brief Class representing an DELETE_IKE_SA Job. * - * This job is responsible for deleting of half open IKE_SAs. A half - * open IKE_SA is every IKE_SA which hasn't reache the SA_ESTABLISHED + * This job is responsible for deleting established or half open IKE_SAs. + * A half open IKE_SA is every IKE_SA which hasn't reache the SA_ESTABLISHED * state. * * @b Constructors: - * - delete_half_open_ike_sa_job_create() + * - delete_ike_sa_job_create() * * @ingroup jobs */ -struct delete_half_open_ike_sa_job_t { +struct delete_ike_sa_job_t { /** * The job_t interface. @@ -52,13 +52,15 @@ struct delete_half_open_ike_sa_job_t { }; /** - * @brief Creates a job of type DELETE_HALF_OPEN_IKE_SA. + * @brief Creates a job of type DELETE_IKE_SA. * - * @param ike_sa_id id of the IKE_SA to delete - * @return created delete_half_open_ike_sa_job_t object + * @param ike_sa_id id of the IKE_SA to delete + * @param delete_if_established should the IKE_SA be deleted if it is established? + * @return created delete_ike_sa_job_t object * * @ingroup jobs */ -delete_half_open_ike_sa_job_t *delete_half_open_ike_sa_job_create(ike_sa_id_t *ike_sa_id); +delete_ike_sa_job_t *delete_ike_sa_job_create(ike_sa_id_t *ike_sa_id, + bool delete_if_established); -#endif /* DELETE_HALF_OPEN_IKE_SA_JOB_H_ */ +#endif /* DELETE_IKE_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/incoming_packet_job.c b/src/charon/queues/jobs/incoming_packet_job.c index 81e0366c8..f773a57f4 100644 --- a/src/charon/queues/jobs/incoming_packet_job.c +++ b/src/charon/queues/jobs/incoming_packet_job.c @@ -25,7 +25,6 @@ #include "incoming_packet_job.h" #include <daemon.h> -#include <queues/jobs/delete_half_open_ike_sa_job.h> typedef struct private_incoming_packet_job_t private_incoming_packet_job_t; diff --git a/src/charon/queues/jobs/initiate_job.c b/src/charon/queues/jobs/initiate_job.c index 4b5b704ae..00c4ef32e 100644 --- a/src/charon/queues/jobs/initiate_job.c +++ b/src/charon/queues/jobs/initiate_job.c @@ -27,7 +27,6 @@ #include "initiate_job.h" #include <daemon.h> -#include <queues/jobs/delete_half_open_ike_sa_job.h> typedef struct private_initiate_job_t private_initiate_job_t; @@ -71,9 +70,11 @@ static status_t execute(private_initiate_job_t *this) { ike_sa_t *ike_sa; - ike_sa = charon->ike_sa_manager->checkout_by_ids(charon->ike_sa_manager, - this->policy->get_my_id(this->policy), - this->policy->get_other_id(this->policy)); + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->connection->get_my_host(this->connection), + this->connection->get_other_host(this->connection), + this->policy->get_my_id(this->policy), + this->policy->get_other_id(this->policy)); this->connection->get_ref(this->connection); this->policy->get_ref(this->policy); diff --git a/src/charon/queues/jobs/job.c b/src/charon/queues/jobs/job.c index f99ea14c1..fe9127a75 100644 --- a/src/charon/queues/jobs/job.c +++ b/src/charon/queues/jobs/job.c @@ -31,8 +31,7 @@ mapping_t job_type_m[] = { {INITIATE, "INITIATE"}, {ROUTE, "ROUTE"}, {ACQUIRE, "ACQUIRE"}, - {DELETE_ESTABLISHED_IKE_SA, "DELETE_ESTABLISHED_IKE_SA"}, - {DELETE_HALF_OPEN_IKE_SA, "DELETE_HALF_OPEN_IKE_SA"}, + {DELETE_IKE_SA, "DELETE_IKE_SA"}, {DELETE_CHILD_SA, "DELETE_CHILD_SA"}, {REKEY_CHILD_SA, "REKEY_CHILD_SA"}, {REKEY_IKE_SA, "REKEY_IKE_SA"}, diff --git a/src/charon/queues/jobs/job.h b/src/charon/queues/jobs/job.h index d1b0a5c82..879b9ab12 100644 --- a/src/charon/queues/jobs/job.h +++ b/src/charon/queues/jobs/job.h @@ -72,35 +72,28 @@ enum job_type_t { ACQUIRE, /** - * Delete an ike sa which is still not established. + * Delete an IKE_SA. * - * Job is implemented in class delete_half_open_ike_sa_job_t + * Job is implemented in class delete_ike_sa_job_t */ - DELETE_HALF_OPEN_IKE_SA, - - /** - * Delete an ike sa which is established. - * - * Job is implemented in class delete_established_ike_sa_job_t - */ - DELETE_ESTABLISHED_IKE_SA, + DELETE_IKE_SA, /** - * Delete a child sa. + * Delete a CHILD_SA. * * Job is implemented in class delete_child_sa_job_t */ DELETE_CHILD_SA, /** - * Rekey a child sa. + * Rekey a CHILD_SA. * * Job is implemented in class rekey_child_sa_job_t */ REKEY_CHILD_SA, /** - * Rekey an IKE_SA + * Rekey an IKE_SA. * * Job is implemented in class rekey_ike_sa_job_t */ diff --git a/src/charon/queues/jobs/retransmit_request_job.c b/src/charon/queues/jobs/retransmit_request_job.c index 6a533ec7a..494897e41 100644 --- a/src/charon/queues/jobs/retransmit_request_job.c +++ b/src/charon/queues/jobs/retransmit_request_job.c @@ -66,7 +66,7 @@ static job_type_t get_type(private_retransmit_request_job_t *this) static status_t execute(private_retransmit_request_job_t *this) { ike_sa_t *ike_sa; - + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->ike_sa_id); if (ike_sa == NULL) { diff --git a/src/charon/queues/jobs/route_job.c b/src/charon/queues/jobs/route_job.c index ae773afdf..b60f117d7 100644 --- a/src/charon/queues/jobs/route_job.c +++ b/src/charon/queues/jobs/route_job.c @@ -74,9 +74,11 @@ static status_t execute(private_route_job_t *this) { ike_sa_t *ike_sa; - ike_sa = charon->ike_sa_manager->checkout_by_ids(charon->ike_sa_manager, - this->policy->get_my_id(this->policy), - this->policy->get_other_id(this->policy)); + ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager, + this->connection->get_my_host(this->connection), + this->connection->get_other_host(this->connection), + this->policy->get_my_id(this->policy), + this->policy->get_other_id(this->policy)); if (this->route) { if (ike_sa->route(ike_sa, this->connection, this->policy) != SUCCESS) diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index fe8e05207..6488cc20c 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -51,8 +51,7 @@ #include <sa/transactions/dead_peer_detection.h> #include <sa/transactions/rekey_ike_sa.h> #include <queues/jobs/retransmit_request_job.h> -#include <queues/jobs/delete_established_ike_sa_job.h> -#include <queues/jobs/delete_half_open_ike_sa_job.h> +#include <queues/jobs/delete_ike_sa_job.h> #include <queues/jobs/send_dpd_job.h> #include <queues/jobs/send_keepalive_job.h> #include <queues/jobs/rekey_ike_sa_job.h> @@ -204,6 +203,16 @@ struct private_ike_sa_t { } time; /** + * interval to send DPD liveness check + */ + time_t dpd_delay; + + /** + * number of retransmit sequences to go through before giving up (keyingtries) + */ + u_int32_t retrans_sequences; + + /** * List of queued transactions to process */ linked_list_t *transaction_queue; @@ -428,13 +437,19 @@ static status_t transmit_request(private_ike_sa_t *this) u_int32_t transmitted; u_int32_t timeout; transaction_t *transaction = this->transaction_out; - u_int32_t message_id = transaction->get_message_id(transaction); + u_int32_t message_id; + + + this->logger->log(this->logger, CONTROL, + "transmitting request"); transmitted = transaction->requested(transaction); timeout = charon->configuration->get_retransmit_timeout(charon->configuration, - transmitted); + transmitted, + this->retrans_sequences); if (timeout == 0) { + /* giving up. TODO: check for childrens with dpdaction=hold */ this->logger->log(this->logger, ERROR, "giving up after %d retransmits, deleting IKE_SA", transmitted - 1); @@ -444,8 +459,11 @@ static status_t transmit_request(private_ike_sa_t *this) status = transaction->get_request(transaction, &request); if (status != SUCCESS) { + this->logger->log(this->logger, ERROR, + "generating request failed"); return status; } + message_id = transaction->get_message_id(transaction); /* if we retransmit, the request is already generated */ if (transmitted == 0) { @@ -484,6 +502,8 @@ static status_t retransmit_request(private_ike_sa_t *this, u_int32_t message_id) if (this->transaction_out == NULL || this->transaction_out->get_message_id(this->transaction_out) != message_id) { + if (this->transaction_out) + printf("trans_out->mid = %d, mid = %d\n", this->transaction_out->get_message_id(this->transaction_out), message_id); /* no retransmit necessary, transaction did already complete */ return SUCCESS; } @@ -668,8 +688,7 @@ static status_t process_response(private_ike_sa_t *this, message_t *response) current = this->transaction_out; /* check if message ID is that of our currently active transaction */ if (current == NULL || - current->get_message_id(current) != - response->get_message_id(response)) + current->get_message_id(current) != response->get_message_id(response)) { this->logger->log(this->logger, ERROR, "received response with message ID %d not requested, ignored"); @@ -839,6 +858,8 @@ static status_t initiate(private_ike_sa_t *this, DESTROY_IF(this->other_host); this->other_host = connection->get_other_host(connection); this->other_host = this->other_host->clone(this->other_host); + this->retrans_sequences = connection->get_retrans_seq(connection); + this->dpd_delay = connection->get_dpd_delay(connection); this->message_id_out = 1; ike_sa_init = ike_sa_init_create(&this->public); @@ -958,6 +979,8 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) this->message_id_out = 1; ike_sa_init = ike_sa_init_create(&this->public); ike_sa_init->set_config(ike_sa_init, connection, policy); + /* reuse existing reqid */ + ike_sa_init->set_reqid(ike_sa_init, reqid); return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE); } case IKE_CONNECTING: @@ -971,6 +994,8 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) create_child = create_child_sa_create(&this->public); create_child->set_policy(create_child, policy); + /* reuse existing reqid */ + create_child->set_reqid(create_child, reqid); return queue_transaction(this, (transaction_t*)create_child, FALSE); } default: @@ -1090,6 +1115,8 @@ static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t this->other_host = this->other_host->clone(this->other_host); } set_name(this, connection->get_name(connection)); + this->retrans_sequences = connection->get_retrans_seq(connection); + this->dpd_delay = connection->get_dpd_delay(connection); break; case IKE_ESTABLISHED: case IKE_REKEYING: @@ -1102,6 +1129,7 @@ static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t } child_sa = child_sa_create(0, this->my_host, this->other_host, 0, 0, FALSE); + child_sa->set_name(child_sa, policy->get_name(policy)); my_ts = policy->get_my_traffic_selectors(policy, this->my_host); other_ts = policy->get_other_traffic_selectors(policy, this->other_host); status = child_sa->add_policies(child_sa, my_ts, other_ts); @@ -1162,10 +1190,13 @@ static status_t unroute(private_ike_sa_t *this, policy_t *policy) static status_t send_dpd(private_ike_sa_t *this) { send_dpd_job_t *job; - time_t diff, interval; - status_t status = SUCCESS; + time_t diff; - interval = charon->configuration->get_dpd_interval(charon->configuration); + if (this->dpd_delay == 0) + { + /* DPD disabled */ + return SUCCESS; + } if (this->transaction_out) { @@ -1179,20 +1210,20 @@ static status_t send_dpd(private_ike_sa_t *this) last_in = get_time_inbound(this); now = time(NULL); diff = now - last_in; - if (diff >= interval) + if (diff >= this->dpd_delay) { /* to long ago, initiate dead peer detection */ dead_peer_detection_t *dpd; this->logger->log(this->logger, CONTROL, "sending DPD request"); dpd = dead_peer_detection_create(&this->public); - status = queue_transaction(this, (transaction_t*)dpd, FALSE); + queue_transaction(this, (transaction_t*)dpd, FALSE); diff = 0; } } /* recheck in "interval" seconds */ job = send_dpd_job_create(this->ike_sa_id); charon->event_queue->add_relative(charon->event_queue, (job_t*)job, - (interval - diff) * 1000); + (this->dpd_delay - diff) * 1000); return SUCCESS; } @@ -1657,7 +1688,7 @@ static void set_lifetimes(private_ike_sa_t *this, if (hard_lifetime) { this->time.delete = this->time.established + hard_lifetime; - job = (job_t*)delete_established_ike_sa_job_create(this->ike_sa_id); + job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE); charon->event_queue->add_relative(charon->event_queue, job, hard_lifetime * 1000); } @@ -1982,6 +2013,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->time.established = 0; this->time.rekey = 0; this->time.delete = 0; + this->dpd_delay = 0; + this->retrans_sequences = 0; return &this->public; } diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c index 7e9ce1f57..46bf3a1bb 100644 --- a/src/charon/sa/ike_sa_manager.c +++ b/src/charon/sa/ike_sa_manager.c @@ -375,11 +375,13 @@ static u_int64_t get_next_spi(private_ike_sa_manager_t *this) } /** - * Implementation of of ike_sa_manager.create_and_checkout. + * Implementation of of ike_sa_manager.checkout_by_id. */ -static ike_sa_t* checkout_by_ids(private_ike_sa_manager_t *this, - identification_t *my_id, - identification_t *other_id) +static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, + host_t *my_host, + host_t *other_host, + identification_t *my_id, + identification_t *other_id) { iterator_t *iterator; ike_sa_t *ike_sa = NULL; @@ -391,6 +393,7 @@ static ike_sa_t* checkout_by_ids(private_ike_sa_manager_t *this, { ike_sa_entry_t *entry; identification_t *found_my_id, *found_other_id; + host_t *found_my_host, *found_other_host; int wc; iterator->current(iterator, (void**)&entry); @@ -401,6 +404,8 @@ static ike_sa_t* checkout_by_ids(private_ike_sa_manager_t *this, found_my_id = entry->ike_sa->get_my_id(entry->ike_sa); found_other_id = entry->ike_sa->get_other_id(entry->ike_sa); + found_my_host = entry->ike_sa->get_my_host(entry->ike_sa); + found_other_host = entry->ike_sa->get_other_host(entry->ike_sa); if (found_my_id->get_type(found_my_id) == ID_ANY && found_other_id->get_type(found_other_id) == ID_ANY) @@ -409,12 +414,19 @@ static ike_sa_t* checkout_by_ids(private_ike_sa_manager_t *this, continue; } - if (found_my_id->matches(found_my_id, my_id, &wc) && + /* compare ID and hosts. Supplied ID may contain wildcards, and IP + * may be %any. */ + if ((found_my_host->is_anyaddr(found_my_host) || + my_host->ip_equals(my_host, found_my_host)) && + (found_other_host->is_anyaddr(found_other_host) || + other_host->ip_equals(other_host, found_other_host)) && + found_my_id->matches(found_my_id, my_id, &wc) && found_other_id->matches(found_other_id, other_id, &wc)) { /* looks good, we take this one */ this->logger->log(this->logger, CONTROL|LEVEL1, - "found an existing IKE_SA for IDs %s - %s", + "found an existing IKE_SA for %s[%s]...%s[%s]", + my_host->get_string(my_host), other_host->get_string(other_host), my_id->get_string(my_id), other_id->get_string(other_id)); entry->checked_out = TRUE; ike_sa = entry->ike_sa; @@ -931,7 +943,7 @@ ike_sa_manager_t *ike_sa_manager_create() /* assign public functions */ this->public.destroy = (void(*)(ike_sa_manager_t*))destroy; - this->public.checkout_by_ids = (ike_sa_t*(*)(ike_sa_manager_t*,identification_t*,identification_t*))checkout_by_ids; + this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_id; this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout; this->public.checkout_by_child = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t))checkout_by_child; this->public.get_ike_sa_list = (linked_list_t*(*)(ike_sa_manager_t*))get_ike_sa_list; diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h index f327be09c..f0c90f38f 100644 --- a/src/charon/sa/ike_sa_manager.h +++ b/src/charon/sa/ike_sa_manager.h @@ -69,18 +69,22 @@ struct ike_sa_manager_t { ike_sa_t* (*checkout) (ike_sa_manager_t* this, ike_sa_id_t *sa_id); /** - * @brief Checkout an IKE_SA by two identifications. + * @brief Checkout an existing IKE_SA by hosts and identifications. * - * Allows the lookup of an IKE_SA by two user IDs. It returns the - * first found occurence, if there are multiple canddates. Supplied IDs - * may contain wildcards. If no IKE_SA is found, a new one is created. + * Allows the lookup of an IKE_SA by user IDs and hosts. It returns the + * first found occurence, if there are multiple candidates. Supplied IDs + * may contain wildcards, hosts may be %any. + * If no IKE_SA is found, a new one is created. * * @param this the manager object + * @param my_host address of our host + * @param other_id address of remote host * @param my_id ID used by us - * @param other_id ID used by other + * @param other_id ID used by remote * @return checked out/created IKE_SA */ - ike_sa_t* (*checkout_by_ids) (ike_sa_manager_t* this, + ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, + host_t *my_host, host_t* other_host, identification_t *my_id, identification_t *other_id); diff --git a/src/charon/sa/transactions/create_child_sa.c b/src/charon/sa/transactions/create_child_sa.c index bd6efd3b0..d88c2341b 100644 --- a/src/charon/sa/transactions/create_child_sa.c +++ b/src/charon/sa/transactions/create_child_sa.c @@ -70,6 +70,11 @@ struct private_create_child_sa_t { u_int32_t rekey_spi; /** + * reqid to use for new CHILD_SA + */ + u_int32_t reqid; + + /** * policy definition used */ policy_t *policy; @@ -155,6 +160,14 @@ static void set_policy(private_create_child_sa_t *this, policy_t *policy) } /** + * Implementation of create_child_sa_t.set_reqid. + */ +static void set_reqid(private_create_child_sa_t *this, u_int32_t reqid) +{ + this->reqid = reqid; +} + +/** * Implementation of create_child_sa_t.rekeys_child. */ static void rekeys_child(private_create_child_sa_t *this, child_sa_t *child_sa) @@ -252,7 +265,6 @@ static status_t get_request(private_create_child_sa_t *this, message_t **result) sa_payload_t *sa_payload; linked_list_t *proposals; bool use_natt; - u_int32_t reqid = 0; /* get a policy, if we are rekeying */ if (this->rekeyed_sa) @@ -270,20 +282,20 @@ static status_t get_request(private_create_child_sa_t *this, message_t **result) my_ts, other_ts, me, other); - reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa); + this->reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa); if (this->policy == NULL) { this->logger->log(this->logger, ERROR, "no policy found to rekey CHILD_SA with reqid %d", - reqid); + this->reqid); return FAILED; } } proposals = this->policy->get_proposals(this->policy); use_natt = this->ike_sa->is_natt_enabled(this->ike_sa); - this->child_sa = child_sa_create(reqid, me, other, + this->child_sa = child_sa_create(this->reqid, me, other, this->policy->get_soft_lifetime(this->policy), this->policy->get_hard_lifetime(this->policy), use_natt); @@ -686,16 +698,14 @@ static status_t get_response(private_create_child_sa_t *this, message_t *request } else { /* create child sa */ - u_int32_t reqid = 0; - if (this->rekeyed_sa) { - reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa); + this->reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa); } soft_lifetime = this->policy->get_soft_lifetime(this->policy); hard_lifetime = this->policy->get_hard_lifetime(this->policy); use_natt = this->ike_sa->is_natt_enabled(this->ike_sa); - this->child_sa = child_sa_create(reqid, me, other, + this->child_sa = child_sa_create(this->reqid, me, other, soft_lifetime, hard_lifetime, use_natt); this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy)); @@ -950,6 +960,7 @@ create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa) /* public functions */ this->public.set_policy = (void(*)(create_child_sa_t*,policy_t*))set_policy; + this->public.set_reqid = (void(*)(create_child_sa_t*,u_int32_t))set_reqid; this->public.rekeys_child = (void(*)(create_child_sa_t*,child_sa_t*))rekeys_child; this->public.cancel = (void(*)(create_child_sa_t*))cancel; @@ -959,6 +970,7 @@ create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa) this->message = NULL; this->requested = 0; this->rekey_spi = 0; + this->reqid = 0; this->nonce_i = CHUNK_INITIALIZER; this->nonce_r = CHUNK_INITIALIZER; this->nonce_s = CHUNK_INITIALIZER; diff --git a/src/charon/sa/transactions/create_child_sa.h b/src/charon/sa/transactions/create_child_sa.h index d45bfcc6b..f35b25246 100644 --- a/src/charon/sa/transactions/create_child_sa.h +++ b/src/charon/sa/transactions/create_child_sa.h @@ -41,21 +41,21 @@ typedef struct create_child_sa_t create_child_sa_t; * but includes an additional REKEY_SA notify and deletes the old * one (in a separate transaction). * - * ¦__________ _________¦ - * ¦ Cyq \/ Czq ¦ - * ¦__________/\_________¦ - * detect ¦__________ _________¦ detect - * ¦ Czp \/ Czp ¦ - * compare nonces, won ¦__________/\_________¦ compare nonces, lost - * ¦ ¦ - * delete old ¦__________ ¦ - * ¦ Dxq \__________¦ - * ¦ __________¦ - * ¦__________/ Dxp ¦ - * ¦ __________¦ delete created - * ¦__________/ Dzq ¦ - * ¦__________ ¦ - * ¦ Dzp \__________¦ + * __________ _________ + * Cyq \/ Czq + * __________/\_________ + * detect __________ _________ detect + * Czp \/ Czp + * compare nonces, won __________/\_________ compare nonces, lost + * + * delete old __________ + * Dxq \__________ + * __________ + * __________/ Dxp + * __________ delete created + * __________/ Dzq + * __________ + * Dzp \__________ * * * @b Constructors: @@ -78,6 +78,17 @@ struct create_child_sa_t { * @param policy policy for CHILD_SA */ void (*set_policy) (create_child_sa_t* this, policy_t *policy); + + /** + * @brief Set the reqid used for CHILD_SA setup. + * + * If we acquire, we must use the same reqid as the + * installed policy. + * + * @param this calling object + * @param reqid reqid to use for the CHILD_SA + */ + void (*set_reqid) (create_child_sa_t* this, u_int32_t reqid); /** * @brief Set the CHILD_SA which gets rekeyed by the new one. diff --git a/src/charon/sa/transactions/ike_auth.c b/src/charon/sa/transactions/ike_auth.c index 68abba814..1d15daebb 100644 --- a/src/charon/sa/transactions/ike_auth.c +++ b/src/charon/sa/transactions/ike_auth.c @@ -125,6 +125,11 @@ struct private_ike_auth_t { bool build_child; /** + * reqid to use for CHILD_SA setup + */ + u_int32_t reqid; + + /** * Assigned logger. */ logger_t *logger; @@ -157,6 +162,14 @@ static void set_config(private_ike_auth_t *this, } /** + * Implementation of transaction_t.set_reqid. + */ +static void set_reqid(private_ike_auth_t *this, u_int32_t reqid) +{ + this->reqid = reqid; +} + +/** * Implementation of transaction_t.set_nonces. */ static void set_nonces(private_ike_auth_t *this, chunk_t nonce_i, chunk_t nonce_r) @@ -303,7 +316,7 @@ static status_t get_request(private_ike_auth_t *this, message_t **result) proposal_list = this->policy->get_proposals(this->policy); soft_lifetime = this->policy->get_soft_lifetime(this->policy); hard_lifetime = this->policy->get_hard_lifetime(this->policy); - this->child_sa = child_sa_create(0, me, other, soft_lifetime, hard_lifetime, + this->child_sa = child_sa_create(this->reqid, me, other, soft_lifetime, hard_lifetime, this->ike_sa->is_natt_enabled(this->ike_sa)); this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy)); if (this->child_sa->alloc(this->child_sa, proposal_list) != SUCCESS) @@ -768,7 +781,7 @@ static status_t get_response(private_ike_auth_t *this, message_t *request, soft_lifetime = this->policy->get_soft_lifetime(this->policy); hard_lifetime = this->policy->get_hard_lifetime(this->policy); use_natt = this->ike_sa->is_natt_enabled(this->ike_sa); - this->child_sa = child_sa_create(0, me, other, + this->child_sa = child_sa_create(this->reqid, me, other, soft_lifetime, hard_lifetime, use_natt); this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy)); @@ -1006,6 +1019,7 @@ ike_auth_t *ike_auth_create(ike_sa_t *ike_sa) /* public functions */ this->public.set_config = (void(*)(ike_auth_t*,connection_t*,policy_t*))set_config; + this->public.set_reqid = (void(*)(ike_auth_t*,u_int32_t))set_reqid; this->public.set_nonces = (void(*)(ike_auth_t*,chunk_t,chunk_t))set_nonces; this->public.set_init_messages = (void(*)(ike_auth_t*,chunk_t,chunk_t))set_init_messages; @@ -1023,6 +1037,7 @@ ike_auth_t *ike_auth_create(ike_sa_t *ike_sa) this->tsi = NULL; this->tsr = NULL; this->build_child = TRUE; + this->reqid = 0; this->logger = logger_manager->get_logger(logger_manager, IKE_SA); return &this->public; diff --git a/src/charon/sa/transactions/ike_auth.h b/src/charon/sa/transactions/ike_auth.h index 89263d47c..4c6fd822c 100644 --- a/src/charon/sa/transactions/ike_auth.h +++ b/src/charon/sa/transactions/ike_auth.h @@ -62,6 +62,18 @@ struct ike_auth_t { */ void (*set_config) (ike_auth_t* this, connection_t *connection, policy_t *policy); + + /** + * @brief Set the reqid used for CHILD_SA setup. + * + * The first two message exchanges may set up an associated + * CHILD_SA. If we acquire, we must use the same reqid as the + * installed policy. + * + * @param this calling object + * @param reqid reqid to use for the CHILD_SA + */ + void (*set_reqid) (ike_auth_t* this, u_int32_t reqid); /** * @brief Set the nonces used in the previous ike_sa_init transaction. diff --git a/src/charon/sa/transactions/ike_sa_init.c b/src/charon/sa/transactions/ike_sa_init.c index fed03fa49..7a7866970 100644 --- a/src/charon/sa/transactions/ike_sa_init.c +++ b/src/charon/sa/transactions/ike_sa_init.c @@ -33,8 +33,7 @@ #include <encoding/payloads/ke_payload.h> #include <encoding/payloads/nonce_payload.h> #include <sa/transactions/ike_auth.h> -#include <queues/jobs/delete_half_open_ike_sa_job.h> -#include <queues/jobs/delete_established_ike_sa_job.h> +#include <queues/jobs/delete_ike_sa_job.h> #include <queues/jobs/rekey_ike_sa_job.h> @@ -107,6 +106,11 @@ struct private_ike_sa_init_t { proposal_t *proposal; /** + * Reqid to pass to IKE_AUTH, used for created CHILD_SA + */ + u_int32_t reqid; + + /** * Randomizer to generate nonces */ randomizer_t *randomizer; @@ -179,6 +183,14 @@ static void set_config(private_ike_sa_init_t *this, } /** + * Implementation of ike_sa_init_t.set_reqid. + */ +static void set_reqid(private_ike_sa_init_t *this, u_int32_t reqid) +{ + this->reqid = reqid; +} + +/** * Implementation of transaction_t.get_message_id. */ static u_int32_t get_message_id(private_ike_sa_init_t *this) @@ -796,6 +808,7 @@ static status_t get_response(private_ike_sa_init_t *this, /* create next transaction, for which we except a message */ ike_auth = ike_auth_create(this->ike_sa); ike_auth->set_config(ike_auth, this->connection, this->policy); + ike_auth->set_reqid(ike_auth, this->reqid); this->connection = NULL; this->policy = NULL; ike_auth->set_nonces(ike_auth, @@ -809,7 +822,8 @@ static status_t get_response(private_ike_sa_init_t *this, timeout = charon->configuration->get_half_open_ike_sa_timeout(charon->configuration); if (timeout) { - job_t *job = (job_t*)delete_half_open_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa)); + job_t *job = (job_t*)delete_ike_sa_job_create( + this->ike_sa->get_id(this->ike_sa), FALSE); charon->event_queue->add_relative(charon->event_queue, job, timeout); } /* set new state */ @@ -1029,6 +1043,7 @@ static status_t conclude(private_ike_sa_init_t *this, message_t *response, /* create next transaction, for which we except a message */ ike_auth = ike_auth_create(this->ike_sa); ike_auth->set_config(ike_auth, this->connection, this->policy); + ike_auth->set_reqid(ike_auth, this->reqid); this->connection = NULL; this->policy = NULL; ike_auth->set_nonces(ike_auth, @@ -1074,6 +1089,7 @@ ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa) /* public functions */ this->public.set_config = (void(*)(ike_sa_init_t*,connection_t*,policy_t*))set_config; + this->public.set_reqid = (void(*)(ike_sa_init_t*,u_int32_t))set_reqid; this->public.use_dh_group = (bool(*)(ike_sa_init_t*,diffie_hellman_group_t))use_dh_group; /* private data */ @@ -1087,6 +1103,7 @@ ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa) this->connection = NULL; this->policy = NULL; this->proposal = NULL; + this->reqid = 0; this->randomizer = randomizer_create(); this->nat_hasher = hasher_create(HASH_SHA1); this->natd_src_hash = CHUNK_INITIALIZER; diff --git a/src/charon/sa/transactions/ike_sa_init.h b/src/charon/sa/transactions/ike_sa_init.h index 3bc049db3..c7a493ccf 100644 --- a/src/charon/sa/transactions/ike_sa_init.h +++ b/src/charon/sa/transactions/ike_sa_init.h @@ -58,6 +58,19 @@ struct ike_sa_init_t { */ void (*set_config) (ike_sa_init_t* this, connection_t *connection, policy_t *policy); + + /** + * @brief Set the reqid used for CHILD_SA setup. + * + * The first two message exchanges may set up an associated + * CHILD_SA. If we acquire, we must use the same reqid as the + * installed policy. This requid is passed to the ike_auth + * transaction which creates the CHILD_AS. + * + * @param this calling object + * @param reqid reqid to use for the CHILD_SA + */ + void (*set_reqid) (ike_sa_init_t* this, u_int32_t reqid); /** * @brief Set the Diffie Hellman group to use for initiating. diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c index b09070390..796cd04a8 100644 --- a/src/charon/threads/kernel_interface.c +++ b/src/charon/threads/kernel_interface.c @@ -1009,11 +1009,8 @@ static status_t add_policy(private_kernel_interface_t *this, if (!update) { current->refcount++; - iterator->destroy(iterator); - pthread_mutex_unlock(&this->pol_mutex); this->logger->log(this->logger, CONTROL|LEVEL1, "policy already exists, increasing refcount"); - return SUCCESS; } policy = current; found = TRUE; diff --git a/src/charon/threads/stroke_interface.c b/src/charon/threads/stroke_interface.c index b095b605c..55a3b79c3 100755 --- a/src/charon/threads/stroke_interface.c +++ b/src/charon/threads/stroke_interface.c @@ -363,6 +363,8 @@ static void stroke_add_conn(private_stroke_t *this, stroke_msg_t *msg) msg->add_conn.other.sendcert, my_host, other_host, RSA_DIGITAL_SIGNATURE, + msg->add_conn.dpd.delay, + msg->add_conn.rekey.tries, msg->add_conn.rekey.ike_lifetime, msg->add_conn.rekey.ike_lifetime - msg->add_conn.rekey.margin, msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100); @@ -410,7 +412,7 @@ static void stroke_add_conn(private_stroke_t *this, stroke_msg_t *msg) msg->add_conn.rekey.ipsec_lifetime, msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin, msg->add_conn.rekey.margin * msg->add_conn.rekey.fuzz / 100, - msg->add_conn.me.updown); + msg->add_conn.me.updown, msg->add_conn.dpd.route); policy->add_my_traffic_selector(policy, my_ts); policy->add_other_traffic_selector(policy, other_ts); policy->add_authorities(policy, my_ca, other_ca); diff --git a/src/starter/ipsec.conf.5 b/src/starter/ipsec.conf.5 index bb8942f82..a0e926078 100644 --- a/src/starter/ipsec.conf.5 +++ b/src/starter/ipsec.conf.5 @@ -498,27 +498,11 @@ A value of prevents IPsec from proposing compression; a proposal to compress will still be accepted. .TP -.B disablearrivalcheck -whether KLIPS's normal tunnel-exit check -(that a packet emerging from a tunnel has plausible addresses in its header) -should be disabled; -acceptable values are -.B yes -and -.B no -(the default). -Tunnel-exit checks improve security and do not break any normal configuration. -Relevant only locally, other end need not agree on it. -.TP .B dpdaction controls the use of the Dead Peer Detection protocol (DPD, RFC 3706) where -R_U_THERE IKE notification messages are periodically sent in order to check the -liveliness of the IPsec peer. The default is.. -.B none -which disables the active sending of R_U_THERE notifications. -Nevertheless pluto will always send the DPD Vendor ID during connection set up -in order to signal the readiness to act passively as a responder if the peer -wants to use DPD. The values +R_U_THERE notification messages (IKEv1) or empty INFORMATIONAL messages (IKEv2) +are periodically sent in order to check the +liveliness of the IPsec peer. The values .B clear and .B hold @@ -527,14 +511,31 @@ are stopped and unrouted ( .B clear ) or put in the hold state ( .B hold +). For +.B IKEv1 +, the default is +.B none +which disables the active sending of R_U_THERE notifications. +Nevertheless pluto will always send the DPD Vendor ID during connection set up +in order to signal the readiness to act passively as a responder if the peer +wants to use DPD. For +.B IKEv2, none +does't make sense, as all messages are used to detect dead peers. If specified, +it has the same meaning as the default ( +.B clear ). .TP .B dpddelay -defines the period time interval with which R_U_THERE messages are sent to the peer. +defines the period time interval with which R_U_THERE messages/INFORMATIONAL +exchanges are sent to the peer. These are only sent if no other traffic is +received. In IKEv2, a value of 0 sends no additional INFORMATIONAL +messages and uses only standard messages (such as those to rekey) to detect +dead peers. .TP .B dpdtimeout defines the timeout interval, after which all connections to a peer are deleted -in case of inactivity. +in case of inactivity. This only applies to IKEv1, in IKEv2 the default +retransmission timeout applies, as every exchange is used to detect dead peers. .TP .B failureshunt what to do with packets when negotiation fails. diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c index 0c05ebaf3..32866be05 100644 --- a/src/starter/starterstroke.c +++ b/src/starter/starterstroke.c @@ -180,6 +180,8 @@ int starter_stroke_add_conn(starter_conn_t *conn) } msg.add_conn.algorithms.ike = push_string(&msg, conn->ike); msg.add_conn.algorithms.esp = push_string(&msg, conn->esp); + msg.add_conn.dpd.delay = conn->dpd_delay; + msg.add_conn.dpd.route = conn->dpd_action == DPD_ACTION_HOLD ? 1 : 0; starter_stroke_add_end(&msg, &msg.add_conn.me, &conn->right); starter_stroke_add_end(&msg, &msg.add_conn.other, &conn->left); diff --git a/src/stroke/stroke.c b/src/stroke/stroke.c index e29a58813..03a5ca245 100644 --- a/src/stroke/stroke.c +++ b/src/stroke/stroke.c @@ -116,6 +116,9 @@ static int add_connection(char *name, msg.add_conn.algorithms.ike = NULL; msg.add_conn.algorithms.esp = NULL; + msg.add_conn.dpd.delay = 0; + msg.add_conn.dpd.route = 0; + msg.add_conn.me.id = push_string(&msg, my_id); msg.add_conn.me.address = push_string(&msg, my_addr); msg.add_conn.me.subnet = push_string(&msg, my_net); diff --git a/src/stroke/stroke.h b/src/stroke/stroke.h index b71e19921..320284229 100644 --- a/src/stroke/stroke.h +++ b/src/stroke/stroke.h @@ -145,6 +145,10 @@ struct stroke_msg_t { unsigned long tries; unsigned long fuzz; } rekey; + struct { + time_t delay; + bool route; + } dpd; stroke_end_t me, other; } add_conn; |