diff options
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; |