diff options
Diffstat (limited to 'src/charon')
43 files changed, 2867 insertions, 758 deletions
diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am index 6bf2c0406..54028b0e1 100644 --- a/src/charon/Makefile.am +++ b/src/charon/Makefile.am @@ -36,13 +36,15 @@ encoding/payloads/sa_payload.h encoding/payloads/vendor_id_payload.c encoding/pa encoding/payloads/vendor_id_payload.h encoding/payloads/proposal_substructure.c encoding/payloads/payload.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 \ +network/interfaces.c network/interfaces.h 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_ike_sa_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_ike_sa_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/job_queue.c queues/event_queue.c queues/send_queue.h queues/job_queue.h queues/event_queue.h \ queues/send_queue.c threads/kernel_interface.c threads/thread_pool.c threads/scheduler.c threads/sender.c \ threads/sender.h threads/kernel_interface.h threads/scheduler.h threads/receiver.c threads/stroke_interface.c \ @@ -50,4 +52,4 @@ threads/thread_pool.h threads/receiver.h threads/stroke_interface.h INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon -I$(top_srcdir)/src/stroke AM_CFLAGS = -DIPSEC_CONFDIR=\"${confdir}\" -DIPSEC_PIDDIR=\"${piddir}\" -charon_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lgmp -lpthread +charon_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lgmp -lpthread -lm diff --git a/src/charon/config/configuration.c b/src/charon/config/configuration.c index 34c040b56..9e44a0ef9 100755 --- a/src/charon/config/configuration.c +++ b/src/charon/config/configuration.c @@ -21,28 +21,50 @@ */ #include <stdlib.h> +#include <math.h> #include "configuration.h" #include <types.h> /** + * Timeout in milliseconds after that a half open IKE_SA gets deleted. + */ +#define HALF_OPEN_IKE_SA_TIMEOUT 30000 + +/** * First retransmit timeout in milliseconds. * Timeout value is increasing in each retransmit round. */ -#define RETRANSMIT_TIMEOUT 2500 +#define RETRANSMIT_TIMEOUT 6000 /** - * Timeout in milliseconds after that a half open IKE_SA gets deleted. + * Base which is raised to the power of the retransmission count. */ -#define HALF_OPEN_IKE_SA_TIMEOUT 30000 +#define RETRANSMIT_BASE 1.5 /** * Max retransmit count. * 0 for infinite. The max time a half open IKE_SA is alive is set by * RETRANSMIT_TIMEOUT. */ -#define MAX_RETRANSMIT_COUNT 3 +#define MAX_RETRANSMIT_COUNT 6 + +/** + * Keepalive interval in milliseconds. + */ +#define KEEPALIVE_INTERVAL 2000000 + +/** + * Keepalive timeout in milliseconds. + * Not implemented yet. + */ +#define KEEPALIVE_TIMEOUT 30000000 + +/** + * DPD interval in milliseconds. + */ +#define DPD_INTERVAL 6000000 typedef struct private_configuration_t private_configuration_t; @@ -64,19 +86,13 @@ struct private_configuration_t { */ static status_t get_retransmit_timeout (private_configuration_t *this, u_int32_t retransmit_count, u_int32_t *timeout) { - int new_timeout = RETRANSMIT_TIMEOUT, i; - if (retransmit_count >= MAX_RETRANSMIT_COUNT && MAX_RETRANSMIT_COUNT != 0) + if (retransmit_count > MAX_RETRANSMIT_COUNT && MAX_RETRANSMIT_COUNT != 0) { return FAILED; } - for (i = 0; i < retransmit_count; i++) - { - new_timeout *= 2; - } - - *timeout = new_timeout; - + *timeout = (u_int32_t)(RETRANSMIT_TIMEOUT * pow(RETRANSMIT_BASE, retransmit_count)); + return SUCCESS; } @@ -89,6 +105,30 @@ static u_int32_t get_half_open_ike_sa_timeout (private_configuration_t *this) } /** + * Implementation of configuration_t.get_keepalive_interval. + */ +static u_int32_t get_keepalive_interval (private_configuration_t *this) +{ + return KEEPALIVE_INTERVAL; +} + +/** + * Implementation of configuration_t.get_keepalive_timeout. + */ +static u_int32_t get_keepalive_timeout (private_configuration_t *this) +{ + return KEEPALIVE_TIMEOUT; +} + +/** + * 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) @@ -107,6 +147,9 @@ configuration_t *configuration_create() this->public.destroy = (void(*)(configuration_t*))destroy; this->public.get_retransmit_timeout = (status_t (*) (configuration_t *, u_int32_t retransmit_count, u_int32_t *timeout))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_keepalive_timeout = (u_int32_t (*) (configuration_t *)) get_keepalive_timeout; + this->public.get_dpd_interval = (u_int32_t (*) (configuration_t *)) get_dpd_interval; return (&this->public); } diff --git a/src/charon/config/configuration.h b/src/charon/config/configuration.h index 066475a12..f0696328d 100755 --- a/src/charon/config/configuration.h +++ b/src/charon/config/configuration.h @@ -70,6 +70,39 @@ struct configuration_t { u_int32_t (*get_half_open_ike_sa_timeout) (configuration_t *this); /** + * @brief Returns the keepalive interval in ms. + * + * The keepalive interval defines the idle time after which a + * NAT keepalive packet should be sent. + * + * @param this calling object + * @return interval in milliseconds (ms) + */ + u_int32_t (*get_keepalive_interval) (configuration_t *this); + + /** + * @brief Returns the keepalive timeout in ms. + * + * The keepalive timeout defines how long we should keep sending + * NAT keepalives after closing an IKE_SA. + * + * @param this calling object + * @return timeout in milliseconds (ms) + */ + u_int32_t (*get_keepalive_timeout) (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 milliseconds (ms) + */ + u_int32_t (*get_dpd_interval) (configuration_t *this); + + /** * @brief Destroys a configuration_t object. * * @param this calling object diff --git a/src/charon/daemon.c b/src/charon/daemon.c index 16216d416..73d131ca1 100644 --- a/src/charon/daemon.c +++ b/src/charon/daemon.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -170,7 +171,8 @@ static void initialize(private_daemon_t *this, bool strict) credential_store_t* credentials; this->public.configuration = configuration_create(); - this->public.socket = socket_create(IKEV2_UDP_PORT); + this->public.socket = socket_create(IKEV2_UDP_PORT, IKEV2_NATT_PORT); + this->public.interfaces = interfaces_create(IKEV2_UDP_PORT); this->public.ike_sa_manager = ike_sa_manager_create(); this->public.job_queue = job_queue_create(); this->public.event_queue = event_queue_create(); @@ -237,6 +239,10 @@ static void destroy(private_daemon_t *this) { this->public.event_queue->destroy(this->public.event_queue); } + if (this->public.interfaces != NULL) + { + this->public.interfaces->destroy(this->public.interfaces); + } if (this->public.configuration != NULL) { this->public.configuration->destroy(this->public.configuration); @@ -311,6 +317,7 @@ private_daemon_t *daemon_create(void) /* NULL members for clean destruction */ this->public.socket = NULL; + this->public.interfaces = NULL; this->public.ike_sa_manager = NULL; this->public.job_queue = NULL; this->public.event_queue = NULL; diff --git a/src/charon/daemon.h b/src/charon/daemon.h index 4f5fbba3d..2c7941f5a 100644 --- a/src/charon/daemon.h +++ b/src/charon/daemon.h @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -30,6 +31,7 @@ #include <threads/thread_pool.h> #include <threads/stroke_interface.h> #include <network/socket.h> +#include <network/interfaces.h> #include <sa/ike_sa_manager.h> #include <queues/send_queue.h> #include <queues/job_queue.h> @@ -196,6 +198,13 @@ #define IKEV2_UDP_PORT 500 /** + * UDP Port to which the daemon will float to if NAT is detected. + * + * @ingroup charon + */ +#define IKEV2_NATT_PORT 4500 + +/** * PID file, in which charon stores its process id * * @ingroup charon @@ -264,6 +273,10 @@ struct daemon_t { * A socket_t instance. */ socket_t *socket; + /** + * A interfaces_t instance. + */ + interfaces_t *interfaces; /** * A send_queue_t instance. diff --git a/src/charon/encoding/message.c b/src/charon/encoding/message.c index ece29094f..031382cee 100644 --- a/src/charon/encoding/message.c +++ b/src/charon/encoding/message.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -439,6 +440,14 @@ static u_int32_t get_message_id (private_message_t *this) } /** + * Implementation of message_t.get_initiator_spi. + */ +static u_int64_t get_initiator_spi (private_message_t *this) +{ + return (this->ike_sa_id->get_initiator_spi(this->ike_sa_id)); +} + +/** * Implementation of message_t.get_responder_spi. */ static u_int64_t get_responder_spi (private_message_t *this) @@ -826,11 +835,13 @@ static status_t parse_body(private_message_t *this, crypter_t *crypter, signer_t } if (current_payload_type == ENCRYPTED) - status = this->decrypt_payloads(this,crypter,signer); - if (status != SUCCESS) { - this->logger->log(this->logger, ERROR, "could not decrypt payloads"); - return status; + status = this->decrypt_payloads(this,crypter,signer); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Could not decrypt payloads"); + return status; + } } status = this->verify(this); @@ -1202,7 +1213,8 @@ message_t *message_create_from_packet(packet_t *packet) this->public.get_minor_version = (u_int8_t(*)(message_t*))get_minor_version; this->public.set_message_id = (void(*)(message_t*, u_int32_t))set_message_id; this->public.get_message_id = (u_int32_t(*)(message_t*))get_message_id; - this->public.get_responder_spi = (u_int64_t(*)(message_t*))get_responder_spi; + this->public.get_initiator_spi = (u_int64_t(*)(message_t*))get_initiator_spi; + this->public.get_responder_spi = (u_int64_t(*)(message_t*))get_responder_spi; this->public.set_ike_sa_id = (void(*)(message_t*, ike_sa_id_t *))set_ike_sa_id; this->public.get_ike_sa_id = (status_t(*)(message_t*, ike_sa_id_t **))get_ike_sa_id; this->public.set_exchange_type = (void(*)(message_t*, exchange_type_t))set_exchange_type; diff --git a/src/charon/encoding/message.h b/src/charon/encoding/message.h index 4b3f8e997..e32cf68d4 100644 --- a/src/charon/encoding/message.h +++ b/src/charon/encoding/message.h @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -101,6 +102,14 @@ struct message_t { u_int32_t (*get_message_id) (message_t *this); /** + * @brief Gets the initiator SPI of the message. + * + * @param this message_t object + * @return initiator spi of the message + */ + u_int64_t (*get_initiator_spi) (message_t *this); + + /** * @brief Gets the responder SPI of the message. * * @param this message_t object diff --git a/src/charon/encoding/payloads/notify_payload.c b/src/charon/encoding/payloads/notify_payload.c index 575b4e5c9..4242b4c1e 100644 --- a/src/charon/encoding/payloads/notify_payload.c +++ b/src/charon/encoding/payloads/notify_payload.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -48,6 +49,8 @@ mapping_t notify_message_type_m[] = { {INVALID_SELECTORS, "INVALID_SELECTORS"}, {INITIAL_CONTACT, "INITIAL_CONTACT"}, {SET_WINDOW_SIZE, "SET_WINDOW_SIZE"}, + {NAT_DETECTION_SOURCE_IP, "NAT_DETECTION_SOURCE_IP"}, + {NAT_DETECTION_DESTINATION_IP, "NAT_DETECTION_DESTINATION_IP"}, {MAPPING_END, NULL} }; diff --git a/src/charon/encoding/payloads/notify_payload.h b/src/charon/encoding/payloads/notify_payload.h index e15493691..2d2e4ba45 100644 --- a/src/charon/encoding/payloads/notify_payload.h +++ b/src/charon/encoding/payloads/notify_payload.h @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -65,6 +66,8 @@ enum notify_message_type_t { INITIAL_CONTACT = 16384, SET_WINDOW_SIZE = 16385, + NAT_DETECTION_SOURCE_IP = 16388, + NAT_DETECTION_DESTINATION_IP = 16389, REKEY_SA = 16393, }; diff --git a/src/charon/network/interfaces.c b/src/charon/network/interfaces.c new file mode 100644 index 000000000..a36ba4d0e --- /dev/null +++ b/src/charon/network/interfaces.c @@ -0,0 +1,153 @@ +/** + * @file interfaces.c + * + * @brief Implementation of interfaces_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * 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 <net/if.h> +#include <ifaddrs.h> +#include <string.h> + +#include "interfaces.h" + +typedef struct private_interfaces_t private_interfaces_t; + +/** + * Private data of an interfaces_t object. + */ +struct private_interfaces_t { + + /** + * Public part of a interfaces_t object. + */ + interfaces_t public; + + /** + * port that gets added to the host_t obbjects + */ + u_int16_t port; + + /** + * list of addresses + */ + linked_list_t *addresses; +}; + +/** + * Implements interfaces_t.get_addresses + */ +static linked_list_t* get_addresses(private_interfaces_t *this) +{ + return this->addresses; +} + +/** + * Implements interfaces_t.is_local_address + */ +static bool is_local_address(private_interfaces_t *this, host_t *host) +{ + iterator_t *iterator; + host_t *lhost; + + if (host->is_anyaddr(host)) + { + return FALSE; + } + + iterator = this->addresses->create_iterator(this->addresses, TRUE); + while (iterator->iterate(iterator, (void**)&lhost)) + { + if (host->get_family(host) == lhost->get_family(lhost) && + streq(host->get_address(host), lhost->get_address(lhost))) + { + iterator->destroy(iterator); + return TRUE; + } + } + + iterator->destroy(iterator); + return FALSE; +} + +/** + * Implements interfaces_t.destroy. + */ +static void destroy(private_interfaces_t *this) +{ + host_t *host; + while (this->addresses->remove_last(this->addresses, (void**)&host) == SUCCESS) + { + host->destroy(host); + } + this->addresses->destroy(this->addresses); + free(this); +} + +static status_t initialize(private_interfaces_t *this) +{ + struct ifaddrs *list; + struct ifaddrs *cur; + host_t *host; + + if (getifaddrs(&list) < 0) + { + return FAILED; + } + + for (cur = list; cur != NULL; cur = cur->ifa_next) + { + if (!(cur->ifa_flags & IFF_UP)) + continue; + + if (cur->ifa_addr == NULL || cur->ifa_addr->sa_family != AF_INET) + continue; + + host = host_create_from_sockaddr(cur->ifa_addr); + if (host) { + host->set_port(host, this->port); + this->addresses->insert_last(this->addresses, (void*) host); + } + } + + freeifaddrs(list); + return SUCCESS; +} + +/* + * Documented in header + */ +interfaces_t *interfaces_create(u_int16_t port) +{ + private_interfaces_t *this = malloc_thing(private_interfaces_t); + + this->port = port; + + this->public.get_addresses = (linked_list_t* (*) (interfaces_t*)) get_addresses; + this->public.is_local_address = (bool (*) (interfaces_t*, host_t*)) is_local_address; + this->public.destroy = (void (*) (interfaces_t*)) destroy; + + this->addresses = linked_list_create(); + + if (initialize(this) != SUCCESS) + { + destroy(this); + } + + return &this->public; +} diff --git a/src/charon/network/interfaces.h b/src/charon/network/interfaces.h new file mode 100644 index 000000000..6500460cf --- /dev/null +++ b/src/charon/network/interfaces.h @@ -0,0 +1,80 @@ +/** + * @file interfaces.h + * + * @brief Interface of interfaces_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * 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 INTERFACES_H_ +#define INTERFACES_H_ + +#include <utils/linked_list.h> +#include <utils/host.h> + +typedef struct interfaces_t interfaces_t; + +/** + * @brief Provides methods to enumerate local interfaces + * + * @b Constructors: + * - interfaces_create() + * + * @todo Handle changes in interface list. + * + * @ingroup network + */ +struct interfaces_t { + + /** + * @brief Get addresses of local interfaces + * + * @param this calling object + * @return linked_list_t of host_t objects + */ + linked_list_t* (*get_addresses) (interfaces_t *ifaces); + + /** + * @brief Check if address is associated with a local interface + * + * @param this calling object + * @param host address to set as destination + * @return TRUE if address is associated with a local interface, FALSE otherwise + */ + bool (*is_local_address) (interfaces_t *ifaces, host_t *host); + + /** + * @brief Destroy the object, freeing contained data. + * + * @param this object to destroy + */ + void (*destroy) (interfaces_t *ifaces); +}; + +/** + * @brief Create an object of type interfaces_t + * + * @param port the port that gets added to the addresses + * + * @return interfaces_t object + * + * @ingroup network + */ +interfaces_t *interfaces_create(u_int16_t port); + + +#endif /*INTERFACES_H_*/ diff --git a/src/charon/network/socket.c b/src/charon/network/socket.c index 89e21a267..dc5aff8d0 100644 --- a/src/charon/network/socket.c +++ b/src/charon/network/socket.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * Copyright (C) 1998-2002 D. Hugh Redelmeier. @@ -32,9 +33,11 @@ #include <unistd.h> #include <stdlib.h> #include <fcntl.h> -#include <net/if.h> #include <sys/ioctl.h> #include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/udp.h> +#include <linux/ipsec.h> #include <linux/filter.h> #include "socket.h" @@ -42,66 +45,20 @@ #include <daemon.h> #include <utils/logger_manager.h> - -#define IP_HEADER_LENGTH 20 -#define UDP_HEADER_LENGTH 8 - - -/** - * This filter code filters out all non-IKEv2 traffic on - * a SOCK_RAW IP_PROTP_UDP socket. Handling of other - * IKE versions is done in pluto. - */ -struct sock_filter ikev2_filter_code[] = -{ - /* Protocol must be UDP */ - BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 9), - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_UDP, 0, 7), - /* Destination Port must be 500 */ - BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 22), - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 500, 0, 5), - /* IKE version must be 2.0 */ - BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 45), - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x20, 0, 3), - /* packet length is length in IKEv2 header + ip header + udp header */ - BPF_STMT(BPF_LD+BPF_W+BPF_ABS, 52), - BPF_STMT(BPF_ALU+BPF_ADD+BPF_K, IP_HEADER_LENGTH + UDP_HEADER_LENGTH), - BPF_STMT(BPF_RET+BPF_A, 0), - /* packet doesn't match IKEv2, ignore */ - BPF_STMT(BPF_RET+BPF_K, 0), -}; - -/** - * Filter struct to use with setsockopt - */ -struct sock_fprog ikev2_filter = { - sizeof(ikev2_filter_code) / sizeof(struct sock_filter), - ikev2_filter_code -}; - - -typedef struct interface_t interface_t; - -/** - * An interface on which we listen. - */ -struct interface_t { - - /** - * Name of the interface - */ - char name[IFNAMSIZ]; - - /** - * Associated socket - */ - int socket_fd; - - /** - * Host with listening address - */ - host_t *address; -}; +/* constants for packet handling */ +#define IP_LEN sizeof(struct iphdr) +#define UDP_LEN sizeof(struct udphdr) +#define MARKER_LEN sizeof(u_int32_t) + +/* offsets for packet handling */ +#define IP 0 +#define UDP IP + IP_LEN +#define IKE UDP + UDP_LEN + +/* from linux/in.h */ +#ifndef IP_IPSEC_POLICY +#define IP_IPSEC_POLICY 16 +#endif /*IP_IPSEC_POLICY*/ typedef struct private_socket_t private_socket_t; @@ -113,21 +70,52 @@ struct private_socket_t{ * public functions */ socket_t public; - + /** - * Master socket + * regular port */ - int master_fd; + int port; + + /** + * port used for nat-t + */ + int natt_port; /** - * List of all socket to listen + * raw socket (receiver) */ - linked_list_t* interfaces; + int raw_fd; + + /** + * send socket on regular port + */ + int send_fd; + + /** + * send socket on nat-t port + */ + int natt_fd; /** * logger for this socket */ logger_t *logger; + + /** + * Setup a send socket + * + * @param this calling object + * @param port the port + * @param send_fd returns the file descriptor of this new socket + */ + status_t (*setup_send_socket) (private_socket_t *this, u_int16_t port, int *send_fd); + + /** + * Initialize + * + * @param this calling object + */ + status_t (*initialize) (private_socket_t *this); }; /** @@ -138,96 +126,51 @@ static status_t receiver(private_socket_t *this, packet_t **packet) char buffer[MAX_PACKET]; chunk_t data; packet_t *pkt; + struct iphdr *ip; + struct udphdr *udp; host_t *source, *dest; int bytes_read = 0; + int data_offset, oldstate; - - while (bytes_read >= 0) + this->logger->log(this->logger, CONTROL|LEVEL1, "receive from raw socket"); + /* allow cancellation while blocking on recv() */ + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); + bytes_read = recv(this->raw_fd, buffer, MAX_PACKET, 0); + pthread_setcancelstate(oldstate, NULL); + + if (bytes_read < 0) { - int max_fd = 1; - fd_set readfds; - iterator_t *iterator; - int oldstate; - interface_t *interface; - - /* build fd_set */ - FD_ZERO(&readfds); - iterator = this->interfaces->create_iterator(this->interfaces, TRUE); - while (iterator->has_next(iterator)) - { - iterator->current(iterator, (void**)&interface); - FD_SET(interface->socket_fd, &readfds); - if (interface->socket_fd > max_fd) - { - max_fd = interface->socket_fd + 1; - } - } - iterator->destroy(iterator); - - this->logger->log(this->logger, CONTROL|LEVEL1, "waiting on sockets"); - - /* allow cancellation while select()-ing */ - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate); - bytes_read = select(max_fd, &readfds, NULL, NULL, NULL); - pthread_setcancelstate(oldstate, NULL); - - /* read on the first nonblocking socket */ - bytes_read = 0; - iterator = this->interfaces->create_iterator(this->interfaces, TRUE); - while (iterator->has_next(iterator)) - { - iterator->current(iterator, (void**)&interface); - if (FD_ISSET(interface->socket_fd, &readfds)) - { - /* do the read */ - bytes_read = recv(interface->socket_fd, buffer, MAX_PACKET, 0); - break; - } - } - iterator->destroy(iterator); - - if (bytes_read < 0) - { - this->logger->log(this->logger, ERROR, "error reading from socket: %s", strerror(errno)); - continue; - } - /* insert a delay to simulate small bandwith/RTT */ -#ifdef PACKET_RECV_DELAY - usleep(PACKET_RECV_DELAY * 1000); -#endif - /* simulate packet loss of every PACKET_RECV_LOSS'th packet */ -#ifdef PACKET_RECV_LOSS - srandom(time(NULL) + getpid()); - if (random() % PACKET_RECV_LOSS == 0) - { - return SUCCESS; - } -#endif - if (bytes_read > IP_HEADER_LENGTH + UDP_HEADER_LENGTH) - { - /* read source/dest from raw IP/UDP header */ - chunk_t source_chunk = {buffer + 12, 4}; - chunk_t dest_chunk = {buffer + 16, 4}; - u_int16_t source_port = ntohs(*(u_int16_t*)(buffer + 20)); - u_int16_t dest_port = ntohs(*(u_int16_t*)(buffer + 22)); - source = host_create_from_chunk(AF_INET, source_chunk, source_port); - dest = host_create_from_chunk(AF_INET, dest_chunk, dest_port); - pkt = packet_create(); - pkt->set_source(pkt, source); - pkt->set_destination(pkt, dest); - break; - } - this->logger->log(this->logger, ERROR|LEVEL1, "too short packet received"); + this->logger->log(this->logger, ERROR, "error reading from socket: %s", strerror(errno)); + return FAILED; } + + /* read source/dest from raw IP/UDP header */ + ip = (struct iphdr*) buffer; + udp = (struct udphdr*) (buffer + IP_LEN); + source = host_create_from_hdr(ip->saddr, udp->source); + dest = host_create_from_hdr(ip->daddr, udp->dest); + + pkt = packet_create(); + pkt->set_source(pkt, source); + pkt->set_destination(pkt, dest); + this->logger->log(this->logger, CONTROL, "received packet: from %s:%d to %s:%d", source->get_address(source), source->get_port(source), dest->get_address(dest), dest->get_port(dest)); + data_offset = IP_LEN + UDP_LEN; + + /* remove non esp marker */ + if (dest->get_port(dest) == this->natt_port) + { + data_offset += MARKER_LEN; + } + /* fill in packet */ - data.len = bytes_read - IP_HEADER_LENGTH - UDP_HEADER_LENGTH; + data.len = bytes_read - data_offset; data.ptr = malloc(data.len); - memcpy(data.ptr, buffer + IP_HEADER_LENGTH + UDP_HEADER_LENGTH, data.len); + memcpy(data.ptr, buffer + data_offset, data.len); pkt->set_data(pkt, data); /* return packet */ @@ -241,8 +184,9 @@ static status_t receiver(private_socket_t *this, packet_t **packet) */ status_t sender(private_socket_t *this, packet_t *packet) { + int sport, fd; ssize_t bytes_sent; - chunk_t data; + chunk_t data, marked; host_t *src, *dst; src = packet->get_source(packet); @@ -252,20 +196,39 @@ status_t sender(private_socket_t *this, packet_t *packet) this->logger->log(this->logger, CONTROL, "sending packet: from %s:%d to %s:%d", src->get_address(src), src->get_port(src), dst->get_address(dst), dst->get_port(dst)); - /* insert a delay to simulate small bandwith/RTT */ -#ifdef PACKET_SEND_DELAY - usleep(PACKET_SEND_DELAY * 1000); -#endif - /* simulate packet loss of every PACKET_LOSS'th packet */ -#ifdef PACKET_SEND_LOSS - srandom(time(NULL) + getpid()); - if (random() % PACKET_SEND_LOSS == 0) + + /* send data */ + sport = src->get_port(src); + if (sport == this->port) { - return SUCCESS; + fd = this->send_fd; } -#endif - /* send data */ - bytes_sent = sendto(this->master_fd, data.ptr, data.len, 0, + else if (sport == this->natt_port) + { + fd = this->natt_fd; + /* NAT keepalives without marker */ + if (data.len != 1 || data.ptr[0] != 0xFF) + { + /* add non esp marker to packet */ + if (data.len > MAX_PACKET - MARKER_LEN) + { + this->logger->log(this->logger, ERROR, "unable to send packet: it's too big"); + return FAILED; + } + marked = chunk_alloc(data.len + MARKER_LEN); + memset(marked.ptr, 0, MARKER_LEN); + memcpy(marked.ptr + MARKER_LEN, data.ptr, data.len); + packet->set_data(packet, marked); /* let the packet do the clean up for us */ + data = marked; + } + } + else + { + this->logger->log(this->logger, ERROR, "unable to locate a send socket for port: %d", sport); + return FAILED; + } + + bytes_sent = sendto(fd, data.ptr, data.len, 0, dst->get_sockaddr(dst), *(dst->get_sockaddr_len(dst))); if (bytes_sent != data.len) @@ -273,160 +236,153 @@ status_t sender(private_socket_t *this, packet_t *packet) this->logger->log(this->logger, ERROR, "error writing to socket: %s", strerror(errno)); return FAILED; } + return SUCCESS; } /** - * Find all suitable interfaces, bind them and add them to the list + * setup a send socket on a specified port */ -static status_t build_interface_list(private_socket_t *this, u_int16_t port) -{ +static status_t setup_send_socket(private_socket_t *this, u_int16_t port, int *send_fd) { int on = TRUE; - int i; struct sockaddr_in addr; - struct ifconf ifconf; - struct ifreq buf[300]; - - /* master socket for querying socket for a specific interfaces */ - this->master_fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (this->master_fd == -1) + int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { - this->logger->log(this->logger, ERROR, "could not open IPv4 master socket!"); + this->logger->log(this->logger, ERROR, "could not open IPv4 send socket!"); return FAILED; } - /* allow binding of multiplo sockets */ - if (setsockopt(this->master_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) { - this->logger->log(this->logger, ERROR, "unable to set SO_REUSEADDR on master socket!"); + this->logger->log(this->logger, ERROR, "unable to set SO_REUSEADDR on send socket!"); + close(fd); return FAILED; } + + struct sadb_x_policy policy; + int level, opt; - /* bind the master socket */ - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(port); - if (bind(this->master_fd,(struct sockaddr*)&addr, sizeof(addr)) < 0) + policy.sadb_x_policy_len = sizeof(policy) / sizeof(u_int64_t); + policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY; + policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS; + policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND; + policy.sadb_x_policy_reserved = 0; + policy.sadb_x_policy_id = 0; + + /* ipv6 + * level = IPPROTO_IPV6; + * opt = IPV6_IPSEC_POLICY; + */ + level = IPPROTO_IP; + opt = IP_IPSEC_POLICY; + + if (setsockopt(fd, level, opt, &policy, sizeof(policy)) < 0) { - this->logger->log(this->logger, ERROR, "unable to bind master socket: %s!", strerror(errno)); + this->logger->log(this->logger, ERROR, "unable to set IPSEC_POLICY on send socket!"); + close(fd); return FAILED; } - /* get all interfaces */ - ifconf.ifc_len = sizeof(buf); - ifconf.ifc_buf = (void*) buf; - memset(buf, 0, sizeof(buf)); - if (ioctl(this->master_fd, SIOCGIFCONF, &ifconf) == -1) + policy.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND; + + if (setsockopt(fd, level, opt, &policy, sizeof(policy)) < 0) { - this->logger->log(this->logger, ERROR, "unable to get interfaces!"); + this->logger->log(this->logger, ERROR, "unable to set IPSEC_POLICY on send socket!"); + close(fd); return FAILED; } - /* add every interesting interfaces to our interface list */ - for (i = 0; (i+1) * sizeof(*buf) <= (size_t)ifconf.ifc_len; i++) - { - struct sockaddr_in *current = (struct sockaddr_in*) &buf[i].ifr_addr; - struct ifreq auxinfo; - int skt; - interface_t *interface; - - if (current->sin_family != AF_INET && current->sin_family != AF_INET6) - { - /* ignore all but IPv4 and IPv6 interfaces */ - continue; - } - - /* get auxilary info about socket */ - memset(&auxinfo, 0, sizeof(auxinfo)); - memcpy(auxinfo.ifr_name, buf[i].ifr_name, IFNAMSIZ); - if (ioctl(this->master_fd, SIOCGIFFLAGS, &auxinfo) == -1) - { - this->logger->log(this->logger, ERROR, "unable to SIOCGIFFLAGS master socket!"); - continue; - } - if (!(auxinfo.ifr_flags & IFF_UP)) - { - /* ignore an interface that isn't up */ - continue; - } - if (current->sin_addr.s_addr == 0) - { - /* ignore unconfigured interfaces */ - continue; - } - - /* set up interface socket */ - skt = socket(current->sin_family, SOCK_RAW, IPPROTO_UDP); - if (socket < 0) - { - this->logger->log(this->logger, ERROR, "unable to open interface socket!"); - continue; - } - if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0) - { - this->logger->log(this->logger, ERROR, "unable to set SO_REUSEADDR on interface socket!"); - close(skt); - continue; - } - current->sin_port = htons(port); - - if (bind(skt, (struct sockaddr*)current, sizeof(struct sockaddr_in)) < 0) - { - this->logger->log(this->logger, ERROR, "unable to bind interface socket!"); - close(skt); - continue; - } - - if (setsockopt(skt, SOL_SOCKET, SO_ATTACH_FILTER, &ikev2_filter, sizeof(ikev2_filter)) < 0) - { - this->logger->log(this->logger, ERROR, "unable to attack IKEv2 filter to interface socket!"); - close(skt); - continue; - } - - /* add socket with interface name to list */ - interface = malloc_thing(interface_t); - strncpy(interface->name, buf[i].ifr_name, IFNAMSIZ); - interface->socket_fd = skt; - interface->address = host_create_from_sockaddr((struct sockaddr*)current); - this->logger->log(this->logger, CONTROL, "listening on %s (%s)", - interface->name, interface->address->get_address(interface->address)); - this->interfaces->insert_last(this->interfaces, (void*)interface); - } - - if (this->interfaces->get_count(this->interfaces) == 0) + /* bind the send socket */ + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(port); + if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) { - this->logger->log(this->logger, ERROR, "unable to find any usable interface!"); + this->logger->log(this->logger, ERROR, "unable to bind send socket: %s!", strerror(errno)); return FAILED; } + + *send_fd = fd; return SUCCESS; } /** - * implementation of socket_t.is_listening_on + * Initialize all sub sockets */ -static bool is_listening_on(private_socket_t *this, host_t *host) +static status_t initialize(private_socket_t *this) { - iterator_t *iterator; - - /* listening on wildcard 0.0.0.0 is always FALSE */ - if (host->is_anyaddr(host)) - return FALSE; + /* This filter code filters out all non-IKEv2 traffic on + * a SOCK_RAW IP_PROTP_UDP socket. Handling of other + * IKE versions is done in pluto. + */ + struct sock_filter ikev2_filter_code[] = + { + /* Protocol must be UDP */ + BPF_STMT(BPF_LD+BPF_B+BPF_ABS, IP + 9), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_UDP, 0, 15), + /* Destination Port must be either port or natt_port */ + BPF_STMT(BPF_LD+BPF_H+BPF_ABS, UDP + 2), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, this->port, 1, 0), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, this->natt_port, 5, 12), + /* port */ + /* IKE version must be 2.0 */ + BPF_STMT(BPF_LD+BPF_B+BPF_ABS, IKE + 17), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x20, 0, 10), + /* packet length is length in IKEv2 header + ip header + udp header */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, IKE + 24), + BPF_STMT(BPF_ALU+BPF_ADD+BPF_K, IP_LEN + UDP_LEN), + BPF_STMT(BPF_RET+BPF_A, 0), + /* natt_port */ + /* nat-t: check for marker */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, IKE), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 5), + /* nat-t: IKE version must be 2.0 */ + BPF_STMT(BPF_LD+BPF_B+BPF_ABS, IKE + MARKER_LEN + 17), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x20, 0, 3), + /* nat-t: packet length is length in IKEv2 header + ip header + udp header + non esp marker */ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, IKE + MARKER_LEN + 24), + BPF_STMT(BPF_ALU+BPF_ADD+BPF_K, IP_LEN + UDP_LEN + MARKER_LEN), + BPF_STMT(BPF_RET+BPF_A, 0), + /* packet doesn't match, ignore */ + BPF_STMT(BPF_RET+BPF_K, 0), + }; + + /* Filter struct to use with setsockopt */ + struct sock_fprog ikev2_filter = { + sizeof(ikev2_filter_code) / sizeof(struct sock_filter), + ikev2_filter_code + }; + + /* set up raw socket */ + this->raw_fd = socket(PF_INET, SOCK_RAW, IPPROTO_UDP); + if (this->raw_fd < 0) + { + this->logger->log(this->logger, ERROR, "unable to create raw socket!"); + return FAILED; + } - /* compare host with all interfaces */ - iterator = this->interfaces->create_iterator(this->interfaces, TRUE); - while (iterator->has_next(iterator)) + if (setsockopt(this->raw_fd, SOL_SOCKET, SO_ATTACH_FILTER, &ikev2_filter, sizeof(ikev2_filter)) < 0) { - interface_t *interface; - iterator->current(iterator, (void**)&interface); - if (host->equals(host, interface->address)) - { - iterator->destroy(iterator); - return TRUE; - } + this->logger->log(this->logger, ERROR, "unable to attach IKEv2 filter to raw socket!"); + close(this->raw_fd); + return FAILED; + } + + /* setup the send sockets */ + if (this->setup_send_socket(this, this->port, &this->send_fd) != SUCCESS) + { + this->logger->log(this->logger, ERROR, "unable to setup send socket on port %d!", this->port); + return FAILED; + } + + if (this->setup_send_socket(this, this->natt_port, &this->natt_fd) != SUCCESS) + { + this->logger->log(this->logger, ERROR, "unable to setup send socket on port %d!", this->natt_port); + return FAILED; } - iterator->destroy(iterator); - return FALSE; + + return SUCCESS; } /** @@ -434,39 +390,37 @@ static bool is_listening_on(private_socket_t *this, host_t *host) */ static void destroy(private_socket_t *this) { - interface_t *interface; - while (this->interfaces->remove_last(this->interfaces, (void**)&interface) == SUCCESS) - { - interface->address->destroy(interface->address); - close(interface->socket_fd); - free(interface); - } - this->interfaces->destroy(this->interfaces); - close(this->master_fd); + close(this->natt_fd); + close(this->send_fd); + close(this->raw_fd); free(this); } /* * See header for description */ -socket_t *socket_create(u_int16_t port) +socket_t *socket_create(u_int16_t port, u_int16_t natt_port) { private_socket_t *this = malloc_thing(private_socket_t); + /* private functions */ + this->initialize = (status_t(*)(private_socket_t*))initialize; + this->setup_send_socket = (status_t(*)(private_socket_t*,u_int16_t, int*))setup_send_socket; + /* public functions */ this->public.send = (status_t(*)(socket_t*, packet_t*))sender; this->public.receive = (status_t(*)(socket_t*, packet_t**))receiver; - this->public.is_listening_on = (bool (*)(socket_t*,host_t*))is_listening_on; this->public.destroy = (void(*)(socket_t*)) destroy; - + this->logger = logger_manager->get_logger(logger_manager, SOCKET); - this->interfaces = linked_list_create(); - if (build_interface_list(this, port) != SUCCESS) + this->port = port; + this->natt_port = natt_port; + + if (this->initialize(this) != SUCCESS) { - this->interfaces->destroy(this->interfaces); free(this); - charon->kill(charon, "could not bind any interface!"); + charon->kill(charon, "could not init socket!"); } return (socket_t*)this; diff --git a/src/charon/network/socket.h b/src/charon/network/socket.h index 498e7700a..b231b302f 100644 --- a/src/charon/network/socket.h +++ b/src/charon/network/socket.h @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -93,15 +94,6 @@ struct socket_t { status_t (*send) (socket_t *sock, packet_t *packet); /** - * @brief Check if socket listens on an address. - * - * @param sock socket_t object to work on - * @param host address to check - * @return TRUE if listening on host, FALSE otherwise - */ - bool (*is_listening_on) (socket_t *sock, host_t *host); - - /** * @brief Destroy sockets. * * close sockets and destroy socket_t object @@ -114,15 +106,15 @@ struct socket_t { /** * @brief Create a socket_t, wich binds multiple sockets. * - * currently creates one socket, listening on all addresses - * on "port". + * currently creates a raw socket and two send sockets * * @param port port to bind socket to + * @param natt_port port to float to in NAT-T * @return socket_t object * * @ingroup network */ -socket_t *socket_create(u_int16_t port); +socket_t *socket_create(u_int16_t port, u_int16_t natt_port); #endif /*SOCKET_H_*/ diff --git a/src/charon/queues/event_queue.h b/src/charon/queues/event_queue.h index 638887dac..576d6aaaf 100644 --- a/src/charon/queues/event_queue.h +++ b/src/charon/queues/event_queue.h @@ -88,8 +88,8 @@ struct event_queue_t { * removes the job. * * @param event_queue calling object - * @param[in] job job to add to the queue (job is not copied) - * @param[in] absolute time time, when the event has to get fired + * @param[in] job job to add to the queue (job is not copied) + * @param[in] time absolute time, when the event has to get fired */ void (*add_absolute) (event_queue_t *event_queue, job_t *job, timeval_t time); diff --git a/src/charon/queues/jobs/job.c b/src/charon/queues/jobs/job.c index df739f9e5..2eaa2e1f6 100644 --- a/src/charon/queues/jobs/job.c +++ b/src/charon/queues/jobs/job.c @@ -29,6 +29,8 @@ mapping_t job_type_m[] = { {RETRANSMIT_REQUEST, "RETRANSMIT_REQUEST"}, {INITIATE_IKE_SA, "INITIATE_IKE_SA"}, {DELETE_HALF_OPEN_IKE_SA, "DELETE_HALF_OPEN_IKE_SA"}, - {DELETE_ESTABLISHED_IKE_SA, "DELETE_ESTABLISHED_IKE_SA"}, + {DELETE_ESTABLISHED_IKE_SA, "DELETE_ESTABLISHED_IKE_SA"}, + {SEND_KEEPALIVE, "SEND_KEEPALIVE"}, + {SEND_DPD, "SEND_DPD"}, {MAPPING_END, NULL} }; diff --git a/src/charon/queues/jobs/job.h b/src/charon/queues/jobs/job.h index d7531531d..a2e888878 100644 --- a/src/charon/queues/jobs/job.h +++ b/src/charon/queues/jobs/job.h @@ -83,6 +83,20 @@ enum job_type_t { * Job is implemented in class rekey_child_sa_job_t */ REKEY_CHILD_SA, + + /** + * Send a keepalive packet. + * + * Job is implemented in class type send_keepalive_job_t + */ + SEND_KEEPALIVE, + + /** + * Send a DPD packet. + * + * Job is implemented in class type send_dpd_job_t + */ + SEND_DPD }; /** diff --git a/src/charon/queues/jobs/retransmit_request_job.c b/src/charon/queues/jobs/retransmit_request_job.c index f89d2adaf..40f2ffd94 100644 --- a/src/charon/queues/jobs/retransmit_request_job.c +++ b/src/charon/queues/jobs/retransmit_request_job.c @@ -69,7 +69,7 @@ static job_type_t get_type(private_retransmit_request_job_t *this) */ static status_t execute(private_retransmit_request_job_t *this) { - bool stop_retransmitting = FALSE; + bool stop_retransmitting = FALSE, timed_out = FALSE; u_int32_t timeout; ike_sa_t *ike_sa; status_t status; @@ -87,14 +87,29 @@ static status_t execute(private_retransmit_request_job_t *this) return DESTROY_ME; } - status = ike_sa->retransmit_request(ike_sa, this->message_id); - if (status != SUCCESS) + this->retransmit_count++; + status = charon->configuration->get_retransmit_timeout(charon->configuration, + this->retransmit_count, &timeout); + timed_out = (status != SUCCESS); + + if (ike_sa->retransmit_possible(ike_sa, this->message_id)) + { + if (!timed_out) + { + status = ike_sa->retransmit_request(ike_sa, this->message_id); + if (status != SUCCESS) + { + this->logger->log(this->logger, CONTROL|LEVEL3, + "Message doesn't have to be retransmitted"); + stop_retransmitting = TRUE; + } + } + } + else { - this->logger->log(this->logger, CONTROL|LEVEL3, - "Message doesn't have to be retransmitted"); stop_retransmitting = TRUE; } - + this->logger->log(this->logger, CONTROL|LEVEL2, "Checkin IKE SA %lld:%lld, role %s", this->ike_sa_id->get_initiator_spi(this->ike_sa_id), this->ike_sa_id->get_responder_spi(this->ike_sa_id), @@ -106,19 +121,25 @@ static status_t execute(private_retransmit_request_job_t *this) this->logger->log(this->logger, ERROR, "Checkin of IKE SA failed!"); } - if (stop_retransmitting) + if (timed_out) { + /* + * XXX: We should act depending on DPD policy here, or not act at all. + */ + this->logger->log(this->logger, CONTROL|LEVEL2, "Timeout: Deleting SA!"); + status = charon->ike_sa_manager->delete(charon->ike_sa_manager, this->ike_sa_id); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR|LEVEL1, "Cannot delete SA!"); + } return DESTROY_ME; } - this->retransmit_count++; - status = charon->configuration->get_retransmit_timeout(charon->configuration, - this->retransmit_count, &timeout); - if (status != SUCCESS) + if (stop_retransmitting) { - this->logger->log(this->logger, CONTROL|LEVEL2, "Message will not be retransmitted anymore"); return DESTROY_ME; } + charon->event_queue->add_relative(charon->event_queue, (job_t *)this, timeout); return SUCCESS; } diff --git a/src/charon/queues/jobs/send_dpd_job.c b/src/charon/queues/jobs/send_dpd_job.c new file mode 100644 index 000000000..acbde70c3 --- /dev/null +++ b/src/charon/queues/jobs/send_dpd_job.c @@ -0,0 +1,132 @@ +/** + * @file send_dpd_job.c + * + * @brief Implementation of send_dpd_job_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * 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 <stdlib.h> + +#include "send_dpd_job.h" + +#include <sa/ike_sa.h> +#include <daemon.h> + + +typedef struct private_send_dpd_job_t private_send_dpd_job_t; + +/** + * Private data of an send_dpd_job_t Object + */ +struct private_send_dpd_job_t { + /** + * public send_dpd_job_t interface + */ + send_dpd_job_t public; + + /** + * ID of the IKE_SA which the message belongs to. + */ + ike_sa_id_t *ike_sa_id; + + /** + * Logger reference. + */ + logger_t *logger; +}; + +/** + * Implements send_dpd_job_t.get_type. + */ +static job_type_t get_type(private_send_dpd_job_t *this) +{ + return SEND_DPD; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_send_dpd_job_t *this) +{ + ike_sa_t *ike_sa; + status_t status; + + this->logger->log(this->logger, CONTROL|LEVEL2, "Checking out IKE SA %lld:%lld, role %s", + this->ike_sa_id->get_initiator_spi(this->ike_sa_id), + this->ike_sa_id->get_responder_spi(this->ike_sa_id), + this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); + + status = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id, &ike_sa); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "IKE SA could not be checked out. Already deleted?"); + return DESTROY_ME; + } + + ike_sa->send_dpd_request(ike_sa); + this->logger->log(this->logger, CONTROL|LEVEL1, + "DPD request packet scheduled"); + + this->logger->log(this->logger, CONTROL|LEVEL2, + "Checkin IKE SA %lld:%lld, role %s", + this->ike_sa_id->get_initiator_spi(this->ike_sa_id), + this->ike_sa_id->get_responder_spi(this->ike_sa_id), + this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); + + status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Checkin of IKE SA failed!"); + } + + return SUCCESS; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_send_dpd_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +send_dpd_job_t *send_dpd_job_create(ike_sa_id_t *ike_sa_id) +{ + private_send_dpd_job_t *this = malloc_thing(private_send_dpd_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + + /* public functions */ + this->public.destroy = (void (*)(send_dpd_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/send_dpd_job.h b/src/charon/queues/jobs/send_dpd_job.h new file mode 100644 index 000000000..31758b05e --- /dev/null +++ b/src/charon/queues/jobs/send_dpd_job.h @@ -0,0 +1,69 @@ +/** + * @file send_dpd_job.h + * + * @brief Interface of send_dpd_job_t. + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * 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 SEND_DPD_JOB_H_ +#define SEND_DPD_JOB_H_ + +#include <types.h> +#include <queues/jobs/job.h> +#include <config/connections/connection.h> +#include <sa/ike_sa_id.h> + + +typedef struct send_dpd_job_t send_dpd_job_t; + +/** + * @brief Class representing a SEND_DPD Job. + * + * Job to periodically send a Dead Peer Detection (DPD) request, + * ie. an IKE request with no payloads other than the encrypted payload + * required by the syntax. + * + * @b Constructors: + * - send_dpd_job_create() + * + * @ingroup jobs + */ +struct send_dpd_job_t { + /** + * implements job_t interface + */ + job_t job_interface; + + /** + * @brief Destroys an send_dpd_job_t object. + * + * @param this send_dpd_job_t object to destroy + */ + void (*destroy) (send_dpd_job_t *this); +}; + +/** + * @brief Creates a job of type SEND_DPD. + * + * @param ike_sa_id identification of the ike_sa as ike_sa_id_t object (gets cloned) + * @return initiate_ike_sa_job_t object + * + * @ingroup jobs + */ +send_dpd_job_t *send_dpd_job_create(ike_sa_id_t *ike_sa_id); + +#endif /*SEND_DPD_JOB_H_*/ diff --git a/src/charon/queues/jobs/send_keepalive_job.c b/src/charon/queues/jobs/send_keepalive_job.c new file mode 100644 index 000000000..4fc7f3d56 --- /dev/null +++ b/src/charon/queues/jobs/send_keepalive_job.c @@ -0,0 +1,164 @@ +/** + * @file send_keepalive_job.c + * + * @brief Implementation of send_keepalive_job_t. + * + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * 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 <stdlib.h> + +#include "send_keepalive_job.h" + +#include <sa/ike_sa.h> +#include <daemon.h> + + +typedef struct private_send_keepalive_job_t private_send_keepalive_job_t; + +/** + * Private data of an send_keepalive_job_t Object + */ +struct private_send_keepalive_job_t { + /** + * public send_keepalive_job_t interface + */ + send_keepalive_job_t public; + + /** + * ID of the IKE_SA which the message belongs to. + */ + ike_sa_id_t *ike_sa_id; + + /** + * Logger reference. + */ + logger_t *logger; +}; + +/** + * Implements send_keepalive_job_t.get_type. + */ +static job_type_t get_type(private_send_keepalive_job_t *this) +{ + return SEND_KEEPALIVE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_send_keepalive_job_t *this) +{ + ike_sa_t *ike_sa; + status_t status; + u_int32_t dt; + u_int32_t interval = charon->configuration->get_keepalive_interval(charon->configuration); + u_int32_t timeout = charon->configuration->get_keepalive_timeout(charon->configuration); + struct timeval last_msg_tv, current_tv; + packet_t *packet; + host_t *host; + connection_t *connection; + chunk_t data; + + this->logger->log(this->logger, CONTROL|LEVEL2, "Checking out IKE SA %lld:%lld, role %s", + this->ike_sa_id->get_initiator_spi(this->ike_sa_id), + this->ike_sa_id->get_responder_spi(this->ike_sa_id), + this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); + + status = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id, &ike_sa); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "IKE SA could not be checked out. Already deleted?"); + return DESTROY_ME; + } + + last_msg_tv = ike_sa->get_last_msg_tv(ike_sa); + if (0 > gettimeofday(¤t_tv, NULL) ) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "Warning: Failed to get time of day."); + } + dt = (current_tv.tv_sec - last_msg_tv.tv_sec) * 1000 + + (current_tv.tv_usec - last_msg_tv.tv_usec) / 1000; + + if (dt >= interval) + { + packet = packet_create(); + connection = ike_sa->get_connection(ike_sa); + host = connection->get_my_host(connection); + packet->set_source(packet, host->clone(host)); + host = connection->get_other_host(connection); + packet->set_destination(packet, host->clone(host)); + data = chunk_alloc(1); + data.ptr[0] = 0xFF; + packet->set_data(packet, data); + charon->send_queue->add(charon->send_queue, packet); + dt = 0; + this->logger->log(this->logger, CONTROL|LEVEL1, + "NAT keepalive packet scheduled"); + } + charon->event_queue->add_relative(charon->event_queue, + (job_t*) this, interval - dt); + + this->logger->log(this->logger, CONTROL|LEVEL2, + "Checkin IKE SA %lld:%lld, role %s", + this->ike_sa_id->get_initiator_spi(this->ike_sa_id), + this->ike_sa_id->get_responder_spi(this->ike_sa_id), + this->ike_sa_id->is_initiator(this->ike_sa_id) ? "initiator" : "responder"); + + status = charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + if (status != SUCCESS) + { + this->logger->log(this->logger, ERROR, "Checkin of IKE SA failed!"); + } + + return SUCCESS; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_send_keepalive_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + free(this); +} + +/* + * Described in header + */ +send_keepalive_job_t *send_keepalive_job_create(ike_sa_id_t *ike_sa_id) +{ + private_send_keepalive_job_t *this = malloc_thing(private_send_keepalive_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + + /* public functions */ + this->public.destroy = (void (*)(send_keepalive_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/send_keepalive_job.h b/src/charon/queues/jobs/send_keepalive_job.h new file mode 100644 index 000000000..4c217309f --- /dev/null +++ b/src/charon/queues/jobs/send_keepalive_job.h @@ -0,0 +1,68 @@ +/** + * @file send_keepalive_job.h + * + * @brief Interface of send_keepalive_job_t. + */ + +/* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * 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 SEND_KEEPALIVE_JOB_H_ +#define SEND_KEEPALIVE_JOB_H_ + +#include <types.h> +#include <queues/jobs/job.h> +#include <config/connections/connection.h> +#include <sa/ike_sa_id.h> + + +typedef struct send_keepalive_job_t send_keepalive_job_t; + +/** + * @brief Class representing a SEND_KEEPALIVE Job. + * + * This job will send a NAT keepalive packet if the IKE SA is still alive, + * and reinsert itself into the event queue. + * + * @b Constructors: + * - send_keepalive_job_create() + * + * @ingroup jobs + */ +struct send_keepalive_job_t { + /** + * implements job_t interface + */ + job_t job_interface; + + /** + * @brief Destroys an send_keepalive_job_t object. + * + * @param this send_keepalive_job_t object to destroy + */ + void (*destroy) (send_keepalive_job_t *this); +}; + +/** + * @brief Creates a job of type SEND_KEEPALIVE. + * + * @param ike_sa_id identification of the ike_sa as ike_sa_id_t object (gets cloned) + * @return initiate_ike_sa_job_t object + * + * @ingroup jobs + */ +send_keepalive_job_t *send_keepalive_job_create(ike_sa_id_t *ike_sa_id); + +#endif /*SEND_KEEPALIVE_JOB_H_*/ diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c index 7db6ef78f..554078cda 100644 --- a/src/charon/sa/child_sa.c +++ b/src/charon/sa/child_sa.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -111,6 +112,11 @@ struct private_child_sa_t { * has this CHILD_SA been rekeyed? */ bool rekeyed; + + /** + * Specifies if NAT traversal is used + */ + bool use_natt; /** * CHILD_SAs own logger @@ -147,11 +153,53 @@ protocol_id_t get_protocol(private_child_sa_t *this) } /** + * Allocate SPI for a single proposal + */ +static status_t alloc_proposal(private_child_sa_t *this, proposal_t *proposal) +{ + protocol_id_t protocol = proposal->get_protocol(proposal); + + if (protocol == PROTO_AH) + { + /* get a new spi for AH, if not already done */ + if (this->alloc_ah_spi == 0) + { + if (charon->kernel_interface->get_spi( + charon->kernel_interface, + this->other.addr, this->me.addr, + PROTO_AH, this->reqid, + &this->alloc_ah_spi) != SUCCESS) + { + return FAILED; + } + } + proposal->set_spi(proposal, this->alloc_ah_spi); + } + if (protocol == PROTO_ESP) + { + /* get a new spi for ESP, if not already done */ + if (this->alloc_esp_spi == 0) + { + if (charon->kernel_interface->get_spi( + charon->kernel_interface, + this->other.addr, this->me.addr, + PROTO_ESP, this->reqid, + &this->alloc_esp_spi) != SUCCESS) + { + return FAILED; + } + } + proposal->set_spi(proposal, this->alloc_esp_spi); + } + return SUCCESS; +} + + +/** * Implements child_sa_t.alloc */ static status_t alloc(private_child_sa_t *this, linked_list_t *proposals) { - protocol_id_t protocol; iterator_t *iterator; proposal_t *proposal; @@ -160,41 +208,11 @@ static status_t alloc(private_child_sa_t *this, linked_list_t *proposals) while(iterator->has_next(iterator)) { iterator->current(iterator, (void**)&proposal); - protocol = proposal->get_protocol(proposal); - - if (protocol == PROTO_AH) + if (alloc_proposal(this, proposal) != SUCCESS) { - /* get a new spi for AH, if not already done */ - if (this->alloc_ah_spi == 0) - { - if (charon->kernel_interface->get_spi( - charon->kernel_interface, - this->other.addr, this->me.addr, - PROTO_AH, this->reqid, - &this->alloc_ah_spi) != SUCCESS) - { - return FAILED; - } - } - proposal->set_spi(proposal, this->alloc_ah_spi); + iterator->destroy(iterator); + return FAILED; } - if (protocol == PROTO_ESP) - { - /* get a new spi for ESP, if not already done */ - if (this->alloc_esp_spi == 0) - { - if (charon->kernel_interface->get_spi( - charon->kernel_interface, - this->other.addr, this->me.addr, - PROTO_ESP, this->reqid, - &this->alloc_esp_spi) != SUCCESS) - { - return FAILED; - } - } - proposal->set_spi(proposal, this->alloc_esp_spi); - } - } iterator->destroy(iterator); return SUCCESS; @@ -208,6 +226,7 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus algorithm_t int_algo_none = {AUTH_UNDEFINED, 0}; host_t *src; host_t *dst; + natt_conf_t *natt; status_t status; this->protocol = proposal->get_protocol(proposal); @@ -275,17 +294,31 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal, prf_plus int_algo = &int_algo_none; } + /* setup nat-t */ + if (this->use_natt) + { + natt = alloca(sizeof(natt_conf_t)); + natt->sport = src->get_port(src); + natt->dport = dst->get_port(dst); + } + else + { + natt = NULL; + } + + /* send SA down to the kernel */ this->logger->log(this->logger, CONTROL|LEVEL2, " SPI 0x%.8x, src %s dst %s", ntohl(spi), src->get_address(src), dst->get_address(dst)); status = charon->kernel_interface->add_sa(charon->kernel_interface, - src, dst, - spi, this->protocol, - this->reqid, - mine ? 0 : this->soft_lifetime, - this->hard_lifetime, - enc_algo, int_algo, prf_plus, mine); + src, dst, + spi, this->protocol, + this->reqid, + mine ? 0 : this->soft_lifetime, + this->hard_lifetime, + enc_algo, int_algo, + prf_plus, natt, mine); this->install_time = time(NULL); @@ -301,14 +334,10 @@ static status_t add(private_child_sa_t *this, proposal_t *proposal, prf_plus_t * outbound_spi = proposal->get_spi(proposal); /* get SPIs inbound SAs */ - list = linked_list_create(); - list->insert_last(list, proposal); - if (alloc(this, list) != SUCCESS) + if (alloc_proposal(this, proposal) != SUCCESS) { - list->destroy(list); return FAILED; } - list->destroy(list); inbound_spi = proposal->get_spi(proposal); /* install inbound SAs */ @@ -409,24 +438,21 @@ static status_t add_policies(private_child_sa_t *this, linked_list_t *my_ts_list policy->me.net, policy->other.net, policy->me.net_mask, policy->other.net_mask, XFRM_POLICY_OUT, policy->upper_proto, - this->protocol, - this->reqid); + this->protocol, this->reqid); status |= charon->kernel_interface->add_policy(charon->kernel_interface, this->other.addr, this->me.addr, policy->other.net, policy->me.net, policy->other.net_mask, policy->me.net_mask, XFRM_POLICY_IN, policy->upper_proto, - this->protocol, - this->reqid); + this->protocol, this->reqid); status |= charon->kernel_interface->add_policy(charon->kernel_interface, this->other.addr, this->me.addr, policy->other.net, policy->me.net, policy->other.net_mask, policy->me.net_mask, XFRM_POLICY_FWD, policy->upper_proto, - this->protocol, - this->reqid); + this->protocol, this->reqid); if (status != SUCCESS) { @@ -514,6 +540,159 @@ static void log_status(private_child_sa_t *this, logger_t *logger, char* name) } /** + * Update the host adress/port of a SA + */ +static status_t update_sa_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other, + int my_changes, int other_changes, bool mine) +{ + host_t *src, *dst, *new_src, *new_dst; + int src_changes, dst_changes; + status_t status; + u_int32_t spi; + + if (mine) + { + src = this->me.addr; + dst = this->other.addr; + new_src = new_me; + new_dst = new_other; + src_changes = my_changes; + dst_changes = other_changes; + spi = this->me.spi; + } + else + { + src = this->other.addr; + dst = this->me.addr; + new_src = new_other; + new_dst = new_me; + src_changes = other_changes; + dst_changes = my_changes; + spi = this->other.spi; + } + + this->logger->log(this->logger, CONTROL|LEVEL1, + "updating %s SA 0x%x, from %s:%d..%s:%d to %s:%d..%s:%d", + mapping_find(protocol_id_m, this->protocol), ntohl(spi), + src->get_address(src), src->get_port(src), + dst->get_address(dst), dst->get_port(dst), + new_src->get_address(new_src), new_src->get_port(new_src), + new_dst->get_address(new_dst), new_dst->get_port(new_dst)); + + status = charon->kernel_interface->update_sa_hosts( + charon->kernel_interface, + src, dst, new_src, new_dst, + src_changes, dst_changes, + spi, this->protocol); + + if (status != SUCCESS) + { + return FAILED; + } + return SUCCESS; +} + +/** + * Update the host adress/port of a policy + */ +static status_t update_policy_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other) +{ + iterator_t *iterator; + sa_policy_t *policy; + status_t status; + + iterator = this->policies->create_iterator(this->policies, TRUE); + while (iterator->iterate(iterator, (void**)&policy)) + { + this->logger->log(this->logger, CONTROL|LEVEL1, + "updating policy: %s/%d====%s/%d", + policy->me.net->get_address(policy->me.net), policy->me.net_mask, + policy->other.net->get_address(policy->other.net), policy->other.net_mask); + + status = charon->kernel_interface->add_policy( + charon->kernel_interface, + new_me, new_other, + policy->me.net, policy->other.net, + policy->me.net_mask, policy->other.net_mask, + XFRM_POLICY_OUT, policy->upper_proto, + this->protocol, this->reqid); + + status |= charon->kernel_interface->add_policy( + charon->kernel_interface, + new_other, new_me, + policy->other.net, policy->me.net, + policy->other.net_mask, policy->me.net_mask, + XFRM_POLICY_IN, policy->upper_proto, + this->protocol, this->reqid); + + status |= charon->kernel_interface->add_policy( + charon->kernel_interface, + new_other, new_me, + policy->other.net, policy->me.net, + policy->other.net_mask, policy->me.net_mask, + XFRM_POLICY_FWD, policy->upper_proto, + this->protocol, this->reqid); + + if (status != SUCCESS) + { + iterator->destroy(iterator); + return FAILED; + } + } + iterator->destroy(iterator); + + return SUCCESS; +} + +/** + * Implementation of child_sa_t.update_hosts. + */ +static status_t update_hosts(private_child_sa_t *this, host_t *new_me, host_t *new_other, + int my_changes, int other_changes) +{ + if (!my_changes || !other_changes) + { + return SUCCESS; + } + + /* update our (initator) SAs */ + if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, TRUE) != SUCCESS) + { + return FAILED; + } + + /* update his (responder) SAs */ + if (update_sa_hosts(this, new_me, new_other, my_changes, other_changes, FALSE) != SUCCESS) + { + return FAILED; + } + + /* update policies */ + if (my_changes & HOST_DIFF_ADDR || other_changes & HOST_DIFF_ADDR) + { + if (update_policy_hosts(this, new_me, new_other) != SUCCESS) + { + return FAILED; + } + } + + /* update hosts */ + if (my_changes) + { + this->me.addr->destroy(this->me.addr); + this->me.addr = new_me->clone(new_me); + } + + if (other_changes) + { + this->other.addr->destroy(this->other.addr); + this->other.addr = new_other->clone(new_other); + } + + return SUCCESS; +} + +/** * Implementation of child_sa_t.destroy. */ static void destroy(private_child_sa_t *this) @@ -579,9 +758,10 @@ static void destroy(private_child_sa_t *this) * Described in header. */ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other, - u_int32_t soft_lifetime, u_int32_t hard_lifetime) + u_int32_t soft_lifetime, u_int32_t hard_lifetime, + bool use_natt) { - static u_int32_t reqid = 2000000000; + static u_int32_t reqid = REQID_START; private_child_sa_t *this = malloc_thing(private_child_sa_t); /* public functions */ @@ -604,6 +784,7 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other, this->other.spi = 0; this->alloc_ah_spi = 0; this->alloc_esp_spi = 0; + this->use_natt = use_natt; this->soft_lifetime = soft_lifetime; this->hard_lifetime = hard_lifetime; /* reuse old reqid if we are rekeying an existing CHILD_SA */ @@ -612,5 +793,5 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other, this->protocol = PROTO_NONE; this->rekeyed = FALSE; - return (&this->public); + return &this->public; } diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h index 97789427e..a93d81e59 100644 --- a/src/charon/sa/child_sa.h +++ b/src/charon/sa/child_sa.h @@ -6,7 +6,8 @@ */ /* - * Copyright (C) 2005 Martin Willi + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger + * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -30,16 +31,19 @@ #include <config/proposal.h> #include <utils/logger.h> +/** + * Where we should start with reqid enumeration + */ +#define REQID_START 2000000000 + typedef struct child_sa_t child_sa_t; /** * @brief Represents multiple IPsec SAs between two hosts. * - * A child_sa_t contains multiple SAs. SAs for both - * directions are managed in one child_sa_t object, and - * if both AH and ESP is set up, both protocols are managed - * by one child_sa_t. This means we can have two or - * in the AH+ESP case four IPsec-SAs in one child_sa_t. + * A child_sa_t contains two SAs. SAs for both + * directions are managed in one child_sa_t object. Both + * SAs and the policies have the same reqid. * * The procedure for child sa setup is as follows: * - A gets SPIs for a proposal via child_sa_t.alloc @@ -92,45 +96,53 @@ struct child_sa_t { protocol_id_t (*get_protocol) (child_sa_t *this); /** - * @brief Allocate SPIs for a given proposals. + * @brief Allocate SPIs for given proposals. * * Since the kernel manages SPIs for us, we need - * to allocate them. If the proposal contains more + * to allocate them. If a proposal contains more * than one protocol, for each protocol an SPI is * allocated. SPIs are stored internally and written * back to the proposal. * * @param this calling object - * @param proposal proposal for which SPIs are allocated + * @param proposals list of proposals for which SPIs are allocated */ status_t (*alloc)(child_sa_t *this, linked_list_t* proposals); /** - * @brief Install the kernel SAs for a proposal. - * - * Since the kernel manages SPIs for us, we need - * to allocate them. If the proposal contains more - * than one protocol, for each protocol an SPI is - * allocated. SPIs are stored internally and written - * back to the proposal. + * @brief Install the kernel SAs for a proposal, without previous SPI allocation. * * @param this calling object * @param proposal proposal for which SPIs are allocated * @param prf_plus key material to use for key derivation + * @return SUCCESS or FAILED */ status_t (*add)(child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus); /** - * @brief Install the kernel SAs for a proposal, if SPIs already allocated. - * - * This one updates the SAs in the kernel, which are - * allocated via alloc, with a selected proposals. + * @brief Install the kernel SAs for a proposal, after SPIs have been allocated. + * + * Updates an SA, for which SPIs are already allocated via alloc(). * * @param this calling object * @param proposal proposal for which SPIs are allocated * @param prf_plus key material to use for key derivation + * @return SUCCESS or FAILED */ status_t (*update)(child_sa_t *this, proposal_t *proposal, prf_plus_t *prf_plus); + + /** + * @brief Update the hosts in the kernel SAs and policies + * + * @warning only call this after update() has been called. + * + * @param this calling object + * @param new_me the new local host + * @param new_other the new remote host + * @return SUCCESS or FAILED + */ + status_t (*update_hosts) (child_sa_t *this, host_t *new_me, host_t *new_other, + int my_changes, int other_changes); /** * @brief Install the policies using some traffic selectors. @@ -187,11 +199,13 @@ struct child_sa_t { * @param other remote address * @param soft_lifetime time before rekeying * @param hard_lifteime time before delete + * @param use_natt TRUE if NAT traversal is used * @return child_sa_t object * * @ingroup sa */ child_sa_t * child_sa_create(u_int32_t rekey_reqid, host_t *me, host_t *other, - u_int32_t soft_lifetime, u_int32_t hard_lifetime); + u_int32_t soft_lifetime, u_int32_t hard_lifetime, + bool use_natt); #endif /*CHILD_SA_H_*/ diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index 0d270179f..84c751454 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -19,6 +20,8 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ + +#include <sys/time.h> #include <string.h> #include "ike_sa.h" @@ -32,6 +35,7 @@ #include <crypto/diffie_hellman.h> #include <crypto/prf_plus.h> #include <crypto/crypters/crypter.h> +#include <crypto/hashers/hasher.h> #include <encoding/payloads/sa_payload.h> #include <encoding/payloads/nonce_payload.h> #include <encoding/payloads/ke_payload.h> @@ -173,6 +177,31 @@ struct private_ike_sa_t { * A logger for this IKE_SA. */ logger_t *logger; + + /** + * NAT hasher. + */ + hasher_t *nat_hasher; + + /** + * NAT status of local host. + */ + bool nat_here; + + /** + * NAT status of remote host. + */ + bool nat_there; + + /** + * Timestamp of last IKE message sent or received on this SA + */ + struct timeval last_msg_tv; + + /* + * Message ID of last DPD message + */ + u_int32_t last_dpd_message_id; }; /** @@ -321,23 +350,24 @@ static identification_t* get_other_id(private_ike_sa_t *this) } /** + * Implementation of ike_sa_t.retransmit_possible. + */ +static bool retransmit_possible(private_ike_sa_t *this, u_int32_t message_id) +{ + return ((this->last_requested_message) + && (message_id != this->last_replied_message_id) + && (message_id == this->last_requested_message->get_message_id( + this->last_requested_message))); +} + +/** * Implementation of ike_sa_t.retransmit_request. */ -status_t retransmit_request (private_ike_sa_t *this, u_int32_t message_id) +static status_t retransmit_request(private_ike_sa_t *this, u_int32_t message_id) { packet_t *packet; - - if (this->last_requested_message == NULL) - { - return NOT_FOUND; - } - - if (message_id == this->last_replied_message_id) - { - return NOT_FOUND; - } - - if ((this->last_requested_message->get_message_id(this->last_requested_message)) != message_id) + + if (!this->protected.public.retransmit_possible(&this->protected.public, message_id)) { return NOT_FOUND; } @@ -349,7 +379,6 @@ status_t retransmit_request (private_ike_sa_t *this, u_int32_t message_id) return SUCCESS; } - /** * Implementation of protected_ike_sa_t.build_transforms. */ @@ -648,6 +677,12 @@ static status_t send_request(private_ike_sa_t *this, message_t *message) "Increase message counter for outgoing messages from %d", this->message_id_out); this->message_id_out++; + + /* bump last message sent timestamp */ + if (gettimeofday(&this->last_msg_tv, NULL) < 0) + { + this->logger->log(this->logger, ERROR|LEVEL1, "failed to get time of day"); + } return SUCCESS; } @@ -870,6 +905,158 @@ static status_t initiate_connection(private_ike_sa_t *this, connection_t *connec } /** + * Implementation of protected_ike_sa_t.update_connection_hosts. + * + * Quoting RFC 4306: + * + * 2.11. Address and Port Agility + * + * IKE runs over UDP ports 500 and 4500, and implicitly sets up ESP and + * AH associations for the same IP addresses it runs over. The IP + * addresses and ports in the outer header are, however, not themselves + * cryptographically protected, and IKE is designed to work even through + * Network Address Translation (NAT) boxes. An implementation MUST + * accept incoming requests even if the source port is not 500 or 4500, + * and MUST respond to the address and port from which the request was + * received. It MUST specify the address and port at which the request + * was received as the source address and port in the response. IKE + * functions identically over IPv4 or IPv6. + * + * [...] + * + * There are cases where a NAT box decides to remove mappings that + * are still alive (for example, the keepalive interval is too long, + * or the NAT box is rebooted). To recover in these cases, hosts + * that are not behind a NAT SHOULD send all packets (including + * retransmission packets) to the IP address and port from the last + * valid authenticated packet from the other end (i.e., dynamically + * update the address). A host behind a NAT SHOULD NOT do this + * because it opens a DoS attack possibility. Any authenticated IKE + * packet or any authenticated UDP-encapsulated ESP packet can be + * used to detect that the IP address or the port has changed. + */ +static status_t update_connection_hosts(private_ike_sa_t *this, host_t *me, host_t *other) +{ + host_t *old_other = NULL; + iterator_t *iterator = NULL; + child_sa_t *child_sa = NULL; + int my_changes, other_changes; + ike_sa_state_t s; + + my_changes = me->get_differences(me, this->connection->get_my_host(this->connection)); + + old_other = this->connection->get_other_host(this->connection); + other_changes = other->get_differences(other, old_other); + + if (!my_changes && !other_changes) { + return SUCCESS; + } + + if (my_changes) + { + this->connection->update_my_host(this->connection, me->clone(me)); + } + + s = this->protected.public.get_state(&this->protected.public); + + if (s == RESPONDER_INIT || s == IKE_SA_INIT_REQUESTED || !this->nat_here) + { + if (other_changes) + { + this->connection->update_other_host(this->connection, other->clone(other)); + } + } + else + { + if (other_changes & HOST_DIFF_ADDR) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "Destination ip changed from %s to %s. As we are NATed this is not allowed!", + old_other->get_address(old_other), other->get_address(other)); + return DESTROY_ME; + } + else if (other_changes & HOST_DIFF_PORT) + { + old_other->set_port(old_other, other->get_port(other)); + } + } + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + child_sa->update_hosts(child_sa, + this->connection->get_my_host(this->connection), + this->connection->get_other_host(this->connection), + my_changes, other_changes); + /* XXX error handling */ + } + iterator->destroy(iterator); + + return SUCCESS; +} + +/** + * Implementation of protected_ike_sa_t.build_transforms. + * TODO: IPv6 support. + */ +static chunk_t generate_natd_hash(private_ike_sa_t *this, u_int64_t spi_i, u_int64_t spi_r, host_t *host) +{ + chunk_t natd_string; + chunk_t natd_hash; + void *p; + struct sockaddr_in* sai; + char buf[512]; + + natd_hash = chunk_alloc(this->nat_hasher->get_hash_size(this->nat_hasher)); + natd_string = chunk_alloc(8 + 8 + 4 + 2); + + sai = (struct sockaddr_in*)host->get_sockaddr(host); + p = natd_string.ptr; + *(u_int64_t*)p = spi_i; p += sizeof(spi_i); + *(u_int64_t*)p = spi_r; p += sizeof(spi_r); + *(u_int32_t*)p = sai->sin_addr.s_addr; p += sizeof(sai->sin_addr.s_addr); + *(u_int16_t*)p = sai->sin_port; p += sizeof(sai->sin_port); + + this->nat_hasher->get_hash(this->nat_hasher, natd_string, natd_hash.ptr); + this->nat_hasher->reset(this->nat_hasher); + + sprintf(buf, "natd_hash(%016llx %016llx %s:%d)\n == SHA1(", spi_i, spi_r, + host->get_address(host), host->get_port(host)); + chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_string); + strcat(buf, ") == "); + chunk_to_hex(buf + strlen(buf), sizeof(buf) - strlen(buf), natd_hash); + this->logger->log(this->logger, CONTROL|LEVEL3, buf); + + chunk_free(&natd_string); + return natd_hash; +} + +/** + * Implementation of ike_sa_t.send_dpd_request. + */ +static status_t send_dpd_request(private_ike_sa_t *this) +{ + message_t *dpd_msg; + status_t status; + this->protected.build_message(&this->protected, INFORMATIONAL, TRUE, &dpd_msg); + status = this->protected.send_request(&this->protected, dpd_msg); + if (status != SUCCESS) + { + dpd_msg->destroy(dpd_msg); + } + this->last_dpd_message_id = dpd_msg->get_message_id(dpd_msg); + return status; +} + +/** + * Implementation of ike_sa_t.get_last_dpd_message_id + */ +static u_int32_t get_last_dpd_message_id(private_ike_sa_t *this) +{ + return this->last_dpd_message_id; +} + +/** * Implementation of ike_sa_t.get_child_sa. */ static child_sa_t *get_child_sa(private_ike_sa_t *this, u_int32_t reqid) @@ -1026,7 +1213,8 @@ static status_t rekey_child_sa(private_ike_sa_t *this, u_int32_t reqid) this->connection->get_my_host(this->connection), this->connection->get_other_host(this->connection), this->policy->get_soft_lifetime(this->policy), - this->policy->get_hard_lifetime(this->policy)); + this->policy->get_hard_lifetime(this->policy), + this->nat_here || this->nat_there); child_sa->alloc(child_sa, proposals); sa_payload = sa_payload_create_from_proposal_list(proposals); request->add_payload(request, (payload_t*)sa_payload); @@ -1184,6 +1372,57 @@ static status_t delete_(private_ike_sa_t *this) } /** + * Implementation of ike_sa_t.is_my_host_behind_nat. + */ +static bool is_my_host_behind_nat (private_ike_sa_t *this) +{ + return this->nat_here; +} + +/** + * Implementation of ike_sa_t.is_other_host_behind_nat. + */ +static bool is_other_host_behind_nat (private_ike_sa_t *this) +{ + return this->nat_there; +} + +/** + * Implementation of ike_sa_t.is_any_host_behind_nat. + */ +static bool is_any_host_behind_nat (private_ike_sa_t *this) +{ + return this->nat_here || this->nat_there; +} + +/** + * Implementation of protected_ike_sa_t.set_my_host_behind_nat. + */ +static void set_my_host_behind_nat (private_ike_sa_t *this, bool nat) +{ + this->nat_here = nat; +} + +/** + * Implementation of protected_ike_sa_t.set_other_host_behind_nat. + */ +static void set_other_host_behind_nat (private_ike_sa_t *this, bool nat) +{ + this->nat_there = nat; +} + +/** + * Implementation of ike_sa_t.get_last_msg_tv. + */ +static struct timeval get_last_msg_tv (private_ike_sa_t *this) +{ + /* + * XXX: query kernel for last activity time + */ + return this->last_msg_tv; +} + +/** * Implementation of protected_ike_sa_t.destroy. */ static void destroy(private_ike_sa_t *this) @@ -1269,6 +1508,7 @@ static void destroy(private_ike_sa_t *this) { this->last_responded_message->destroy(this->last_responded_message); } + this->nat_hasher->destroy(this->nat_hasher); this->ike_sa_id->destroy(this->ike_sa_id); this->randomizer->destroy(this->randomizer); this->current_state->destroy(this->current_state); @@ -1294,11 +1534,17 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->protected.public.get_my_id = (identification_t*(*)(ike_sa_t*)) get_my_id; this->protected.public.get_other_id = (identification_t*(*)(ike_sa_t*)) get_other_id; this->protected.public.get_connection = (connection_t*(*)(ike_sa_t*)) get_connection; + this->protected.public.retransmit_possible = (bool (*) (ike_sa_t *, u_int32_t)) retransmit_possible; this->protected.public.retransmit_request = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit_request; this->protected.public.get_state = (ike_sa_state_t (*) (ike_sa_t *this)) get_state; this->protected.public.log_status = (void (*) (ike_sa_t*,logger_t*,char*))log_status; this->protected.public.delete = (status_t(*)(ike_sa_t*))delete_; this->protected.public.destroy = (void(*)(ike_sa_t*))destroy; + this->protected.public.is_my_host_behind_nat = (bool(*)(ike_sa_t*)) is_my_host_behind_nat; + this->protected.public.is_other_host_behind_nat = (bool(*)(ike_sa_t*)) is_other_host_behind_nat; + this->protected.public.is_any_host_behind_nat = (bool(*)(ike_sa_t*)) is_any_host_behind_nat; + this->protected.public.get_last_msg_tv = (struct timeval (*)(ike_sa_t*)) get_last_msg_tv; + this->protected.public.send_dpd_request = (status_t (*)(ike_sa_t*)) send_dpd_request; /* protected functions */ this->protected.build_message = (void (*) (protected_ike_sa_t *, exchange_type_t,bool,message_t**)) build_message; @@ -1327,6 +1573,11 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->protected.set_last_replied_message_id = (void (*) (protected_ike_sa_t *,u_int32_t)) set_last_replied_message_id; this->protected.destroy_child_sa = (u_int32_t (*)(protected_ike_sa_t*,u_int32_t))destroy_child_sa; this->protected.get_child_sa = (child_sa_t* (*)(protected_ike_sa_t*,u_int32_t))get_child_sa_by_spi; + this->protected.set_my_host_behind_nat = (void(*)(protected_ike_sa_t*, bool)) set_my_host_behind_nat; + this->protected.set_other_host_behind_nat = (void(*)(protected_ike_sa_t*, bool)) set_other_host_behind_nat; + this->protected.generate_natd_hash = (chunk_t (*) (protected_ike_sa_t *, u_int64_t, u_int64_t, host_t*)) generate_natd_hash; + this->protected.get_last_dpd_message_id = (u_int32_t (*) (protected_ike_sa_t*)) get_last_dpd_message_id; + this->protected.update_connection_hosts = (status_t (*) (protected_ike_sa_t *, host_t*, host_t*)) update_connection_hosts; /* initialize private fields */ this->logger = logger_manager->get_logger(logger_manager, IKE_SA); @@ -1350,7 +1601,13 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->child_prf = NULL; this->connection = NULL; this->policy = NULL; - + this->nat_hasher = hasher_create(HASH_SHA1); + this->nat_here = FALSE; + this->nat_there = FALSE; + this->last_msg_tv.tv_sec = 0; + this->last_msg_tv.tv_usec = 0; + this->last_dpd_message_id = 0; + /* at creation time, IKE_SA is in a initiator state */ if (ike_sa_id->is_initiator(ike_sa_id)) { diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h index 6b9d9b662..aed8ff3fe 100644 --- a/src/charon/sa/ike_sa.h +++ b/src/charon/sa/ike_sa.h @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -93,6 +94,17 @@ struct ike_sa_t { status_t (*initiate_connection) (ike_sa_t *this, connection_t *connection); /** + * @brief Checks whether retransmission is possible. + * + * @param this calling object + * @param message_id ID of the request to retransmit + * @return + * - TRUE if retransmit is possible + * - FALSE if not + */ + bool (*retransmit_possible) (ike_sa_t *this, u_int32_t message_id); + + /** * @brief Retransmits a request. * * @param this calling object @@ -200,6 +212,38 @@ struct ike_sa_t { connection_t* (*get_connection) (ike_sa_t *this); /** + * @brief Query NAT detection status for local host. + * + * @param this calling object + * @return TRUE if this host is behind NAT + */ + bool (*is_my_host_behind_nat) (ike_sa_t *this); + + /** + * @brief Query NAT detection status for remote host. + * + * @param this calling object + * @return TRUE if other host is behind NAT + */ + bool (*is_other_host_behind_nat) (ike_sa_t *this); + + /** + * @brief Query NAT detection status for any host. + * + * @param this calling object + * @return TRUE if this or other host is behind NAT + */ + bool (*is_any_host_behind_nat) (ike_sa_t *this); + + /** + * @brief Query timeval of last message sent. + * + * @param this calling object + * @return time when the last message was sent + */ + struct timeval (*get_last_msg_tv) (ike_sa_t *this); + + /** * @brief Get the state of type of associated state object. * * @param this calling object @@ -208,6 +252,13 @@ struct ike_sa_t { ike_sa_state_t (*get_state) (ike_sa_t *this); /** + * @brief Sends a DPD request to the peer. + * + * @param this calling object + */ + status_t (*send_dpd_request) (ike_sa_t *this); + + /** * @brief Log the status of a the ike sa to a logger. * * The status of the IKE SA and all child SAs is logged. @@ -507,6 +558,52 @@ struct protected_ike_sa_t { * @param this calling object */ void (*reset_message_buffers) (protected_ike_sa_t *this); + + /** + * @brief Set NAT detection status for local host. + * + * @param this calling object + * @param nat if TRUE, local host is behing NAT + */ + void (*set_my_host_behind_nat) (protected_ike_sa_t *this, bool nat); + + /** + * @brief Set NAT detection status for remote host. + * + * @param this calling object + * @param nat if TRUE, remote host is behing NAT + */ + void (*set_other_host_behind_nat) (protected_ike_sa_t *this, bool nat); + + /** + * @brief Generate NAT-D payload hash. + * + * @param this calling object + * @param spi_i IKE SPI of initiator + * @param spi_r IKE SPI of responder + * @param host address and port of the host/interface + * @return chunk containing calculated NAT-D hash + */ + chunk_t (*generate_natd_hash) (protected_ike_sa_t *this, u_int64_t spi_i, u_int64_t spi_r, host_t *host); + + /** + * @brief Dynamically update hosts on the associated connection. + * + * Warning: me and other host are cloned. + * + * @param this calling object + * @param me local address and port + * @param other remote address and port + */ + status_t (*update_connection_hosts) (protected_ike_sa_t *this, host_t *me, host_t *other); + + /** + * @brief Return the message id of the last DPD message + * + * @param this calling object + * @return the messages id + */ + u_int32_t (*get_last_dpd_message_id) (protected_ike_sa_t *this); }; diff --git a/src/charon/sa/states/ike_auth_requested.c b/src/charon/sa/states/ike_auth_requested.c index 1cbec5b52..e7797d5ea 100644 --- a/src/charon/sa/states/ike_auth_requested.c +++ b/src/charon/sa/states/ike_auth_requested.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -286,6 +287,14 @@ static status_t process_message(private_ike_auth_requested_t *this, message_t *i return DESTROY_ME; } + status = this->ike_sa->update_connection_hosts(this->ike_sa, + ike_auth_reply->get_destination(ike_auth_reply), + ike_auth_reply->get_source(ike_auth_reply)); + if (status != SUCCESS) + { + return status; + } + /* process all payloads */ status = this->process_idr_payload(this, idr_payload); if (status != SUCCESS) diff --git a/src/charon/sa/states/ike_sa_established.c b/src/charon/sa/states/ike_sa_established.c index df31801b3..c97254eee 100644 --- a/src/charon/sa/states/ike_sa_established.c +++ b/src/charon/sa/states/ike_sa_established.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -31,7 +32,7 @@ #include <encoding/payloads/nonce_payload.h> #include <sa/child_sa.h> #include <sa/states/delete_ike_sa_requested.h> - +#include <queues/jobs/send_dpd_job.h> typedef struct private_ike_sa_established_t private_ike_sa_established_t; @@ -86,6 +87,21 @@ struct private_ike_sa_established_t { }; /** + * Schedule send dpd job + */ +static void schedule_dpd_job(private_ike_sa_established_t *this) +{ + u_int32_t interval = charon->configuration->get_dpd_interval(charon->configuration); + + if (interval) + { + charon->event_queue->add_relative(charon->event_queue, + (job_t*)send_dpd_job_create(this->ike_sa->public.get_id(&this->ike_sa->public)), + interval); + } +} + +/** * Implementation of private_ike_sa_established_t.build_sa_payload. */ static status_t build_sa_payload(private_ike_sa_established_t *this, sa_payload_t *request, message_t *response) @@ -99,6 +115,7 @@ static status_t build_sa_payload(private_ike_sa_established_t *this, sa_payload_ connection_t *connection; policy_t *policy; u_int32_t reqid = 0; + bool use_natt; /* prepare reply */ sa_response = sa_payload_create(); @@ -142,11 +159,13 @@ static status_t build_sa_payload(private_ike_sa_established_t *this, sa_payload_ { /* reuse old reqid if we are rekeying */ reqid = this->old_child_sa->get_reqid(this->old_child_sa); } + use_natt = this->ike_sa->public.is_any_host_behind_nat(&this->ike_sa->public); this->child_sa = child_sa_create(reqid, connection->get_my_host(connection), connection->get_other_host(connection), policy->get_soft_lifetime(policy), - policy->get_hard_lifetime(policy)); + policy->get_hard_lifetime(policy), + use_natt); status = this->child_sa->add(this->child_sa, proposal, prf_plus); prf_plus->destroy(prf_plus); @@ -404,6 +423,11 @@ static status_t process_informational(private_ike_sa_established_t *this, messag { delete_payload_t *delete_request = NULL; iterator_t *payloads = request->get_payload_iterator(request); + + if (!payloads->get_count(payloads)) + { + this->logger->log(this->logger, CONTROL, "DPD request received."); + } while (payloads->has_next(payloads)) { @@ -434,7 +458,7 @@ static status_t process_informational(private_ike_sa_established_t *this, messag if (delete_request->get_protocol_id(delete_request) == PROTO_IKE) { this->logger->log(this->logger, CONTROL, "DELETE request for IKE_SA received"); - /* switch to delete_ike_sa_requested. This is not absolutly correct, but we + /* switch to delete_ike_sa_requested. This is not absolutely correct, but we * allow the clean destruction of an SA only in this state. */ this->ike_sa->set_new_state(this->ike_sa, (state_t*)delete_ike_sa_requested_create(this->ike_sa)); this->public.state_interface.destroy(&(this->public.state_interface)); @@ -473,6 +497,53 @@ static status_t process_informational(private_ike_sa_established_t *this, messag } /** + * Process an informational response + */ +static status_t process_informational_response(private_ike_sa_established_t *this, message_t *message) +{ + iterator_t *payloads = message->get_payload_iterator(message); + + if (!payloads->get_count(payloads)) + { + if (message->get_message_id(message) + != this->ike_sa->get_last_dpd_message_id(this->ike_sa)) + { + this->logger->log(this->logger, ERROR|LEVEL1, "DPD response received that does not match our last sent dpd message."); + payloads->destroy(payloads); + return FAILED; + } + + this->logger->log(this->logger, CONTROL, "DPD response received. Schedule job."); + schedule_dpd_job(this); + + payloads->destroy(payloads); + return SUCCESS; + } + + while (payloads->has_next(payloads)) + { + payload_t *payload; + payloads->current(payloads, (void**)&payload); + + switch (payload->get_type(payload)) + { + default: + { + this->logger->log(this->logger, ERROR|LEVEL1, "Ignoring Payload %s (%d)", + mapping_find(payload_type_m, payload->get_type(payload)), + payload->get_type(payload)); + break; + } + } + } + /* iterator can be destroyed */ + payloads->destroy(payloads); + + return SUCCESS; +} + +/** + * Implements state_t.get_state * Implements state_t.process_message */ static status_t process_message(private_ike_sa_established_t *this, message_t *message) @@ -513,6 +584,13 @@ static status_t process_message(private_ike_sa_established_t *this, message_t *m return status; } + status = this->ike_sa->update_connection_hosts(this->ike_sa, + message->get_destination(message), message->get_source(message)); + if (status != SUCCESS) + { + return status; + } + /* prepare a reply of the same type */ this->ike_sa->build_message(this->ike_sa, message->get_exchange_type(message), FALSE, &response); @@ -570,6 +648,9 @@ ike_sa_established_t *ike_sa_established_create(protected_ike_sa_t *ike_sa) this->nonce_i = CHUNK_INITIALIZER; this->nonce_r = CHUNK_INITIALIZER; this->old_child_sa = NULL; + + /* schedule initial dpd job */ + schedule_dpd_job(this); return &(this->public); } diff --git a/src/charon/sa/states/ike_sa_init_requested.c b/src/charon/sa/states/ike_sa_init_requested.c index 60288ae84..1383ac4c6 100644 --- a/src/charon/sa/states/ike_sa_init_requested.c +++ b/src/charon/sa/states/ike_sa_init_requested.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -90,6 +91,36 @@ struct private_ike_sa_init_requested_t { */ logger_t *logger; + /** + * Precomputed NAT-D hash for initiator. + */ + chunk_t natd_hash_i; + + /** + * Flag indicating that an initiator NAT-D hash matched. + */ + bool natd_hash_i_matched; + + /** + * NAT-D payload count for NAT_DETECTION_SOURCE_IP. + */ + int natd_seen_i; + + /** + * Precomputed NAT-D hash of responder. + */ + chunk_t natd_hash_r; + + /** + * Flag indicating that a responder NAT-D hash matched. + */ + bool natd_hash_r_matched; + + /** + * NAT-D payload count for NAT_DETECTION_DESTINATION_IP. + */ + int natd_seen_r; + /** * Process NONCE payload of IKE_SA_INIT response. @@ -271,6 +302,26 @@ static status_t process_message(private_ike_sa_init_requested_t *this, message_t ike_sa_id = this->ike_sa->public.get_id(&(this->ike_sa->public)); ike_sa_id->set_responder_spi(ike_sa_id,responder_spi); + /* + * Precompute NAT-D hashes. + * Even though there SHOULD only be a single payload of each + * Notify type, we precompute both hashes. + */ + this->natd_hash_i = this->ike_sa->generate_natd_hash(this->ike_sa, + ike_sa_init_reply->get_initiator_spi(ike_sa_init_reply), + ike_sa_init_reply->get_responder_spi(ike_sa_init_reply), + ike_sa_init_reply->get_source(ike_sa_init_reply)); + this->natd_hash_i_matched = FALSE; + this->natd_seen_i = 0; + this->natd_hash_r = this->ike_sa->generate_natd_hash(this->ike_sa, + ike_sa_init_reply->get_initiator_spi(ike_sa_init_reply), + ike_sa_init_reply->get_responder_spi(ike_sa_init_reply), + ike_sa_init_reply->get_destination(ike_sa_init_reply)); + this->natd_hash_r_matched = FALSE; + this->natd_seen_r = 0; + this->ike_sa->set_my_host_behind_nat(this->ike_sa, FALSE); + this->ike_sa->set_other_host_behind_nat(this->ike_sa, FALSE); + /* Iterate over all payloads. * * The message is already checked for the right payload types. @@ -354,12 +405,59 @@ static status_t process_message(private_ike_sa_init_requested_t *this, message_t return DESTROY_ME; } - /* apply the address on wich we really received the packet */ + /* NAT-D */ + if ((!this->natd_seen_i && this->natd_seen_r > 0) + || (this->natd_seen_i > 0 && !this->natd_seen_r)) + { + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request contained wrong number of NAT-D payloads. Deleting IKE_SA"); + return DESTROY_ME; + } + if (this->natd_seen_r > 1) + { + this->logger->log(this->logger, AUDIT, "Warning: IKE_SA_INIT request contained multiple Notify(NAT_DETECTION_DESTINATION_IP) payloads."); + } + if (this->natd_seen_i > 0 && !this->natd_hash_i_matched) + { + this->logger->log(this->logger, AUDIT, "Remote host is behind NAT, using NAT-T."); + this->ike_sa->set_other_host_behind_nat(this->ike_sa, TRUE); + } + if (this->natd_seen_r > 0 && !this->natd_hash_r_matched) + { + this->logger->log(this->logger, AUDIT, "Local host is behind NAT, using NAT-T."); + this->ike_sa->set_my_host_behind_nat(this->ike_sa, TRUE); + } + + /* apply the address on wich we really received the packet, + * and switch to port 4500 when using NAT-T and NAT was detected. + */ connection = this->ike_sa->get_connection(this->ike_sa); me = ike_sa_init_reply->get_destination(ike_sa_init_reply); other = ike_sa_init_reply->get_source(ike_sa_init_reply); - connection->update_my_host(connection, me->clone(me)); - connection->update_other_host(connection, other->clone(other)); + + if (this->ike_sa->public.is_any_host_behind_nat((ike_sa_t*)this->ike_sa)) + { + me->set_port(me, IKEV2_NATT_PORT); + other->set_port(other, IKEV2_NATT_PORT); + this->logger->log(this->logger, AUDIT, "Switching to port %d.", IKEV2_NATT_PORT); + } + else + { + this->logger->log(this->logger, AUDIT, "No NAT detected, not using NAT-T."); + } + + if (this->ike_sa->public.is_my_host_behind_nat(&this->ike_sa->public)) + { + charon->event_queue->add_relative(charon->event_queue, + (job_t*)send_keepalive_job_create(this->ike_sa->public.get_id((ike_sa_t*)this->ike_sa)), + charon->configuration->get_keepalive_interval(charon->configuration)); + } + + status = this->ike_sa->update_connection_hosts(this->ike_sa, me, other); + if (status != SUCCESS) + { + return status; + } + policy = this->ike_sa->get_policy(this->ike_sa); policy->update_my_ts(policy, me); policy->update_other_ts(policy, other); @@ -575,7 +673,8 @@ static status_t build_sa_payload (private_ike_sa_init_requested_t *this, message connection->get_my_host(connection), connection->get_other_host(connection), policy->get_soft_lifetime(policy), - policy->get_hard_lifetime(policy)); + policy->get_hard_lifetime(policy), + this->ike_sa->public.is_any_host_behind_nat(&this->ike_sa->public)); if (this->child_sa->alloc(this->child_sa, proposal_list) != SUCCESS) { this->logger->log(this->logger, AUDIT, "Could not install CHILD_SA! Deleting IKE_SA"); @@ -633,6 +732,7 @@ static status_t build_tsr_payload (private_ike_sa_init_requested_t *this, messag */ static status_t process_notify_payload(private_ike_sa_init_requested_t *this, notify_payload_t *notify_payload) { + chunk_t notification_data; notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload); this->logger->log(this->logger, CONTROL|LEVEL1, "Process notify type %s", @@ -701,6 +801,44 @@ static status_t process_notify_payload(private_ike_sa_init_requested_t *this, no } return FAILED; } + case NAT_DETECTION_DESTINATION_IP: + { + this->natd_seen_r++; + if (this->natd_hash_r_matched) + return SUCCESS; + + notification_data = notify_payload->get_notification_data(notify_payload); + if (chunk_equals(notification_data, this->natd_hash_r)) + { + this->natd_hash_r_matched = TRUE; + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match"); + } + else + { + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch"); + } + + return SUCCESS; + } + case NAT_DETECTION_SOURCE_IP: + { + this->natd_seen_i++; + if (this->natd_hash_i_matched) + return SUCCESS; + + notification_data = notify_payload->get_notification_data(notify_payload); + if (chunk_equals(notification_data, this->natd_hash_i)) + { + this->natd_hash_i_matched = TRUE; + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match"); + } + else + { + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch"); + } + + return SUCCESS; + } default: { /* diff --git a/src/charon/sa/states/ike_sa_init_responded.c b/src/charon/sa/states/ike_sa_init_responded.c index ae4a084fa..d8f380552 100644 --- a/src/charon/sa/states/ike_sa_init_responded.c +++ b/src/charon/sa/states/ike_sa_init_responded.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -295,7 +296,14 @@ static status_t process_message(private_ike_sa_init_responded_t *this, message_t this->logger->log(this->logger, AUDIT, "IKE_AUTH reply did not contain all required payloads. Deleting IKE_SA"); return DESTROY_ME; } - + + status = this->ike_sa->update_connection_hosts(this->ike_sa, + request->get_destination(request), request->get_source(request)); + if (status != SUCCESS) + { + return status; + } + /* build response */ this->ike_sa->build_message(this->ike_sa, IKE_AUTH, FALSE, &response); @@ -442,6 +450,7 @@ static status_t build_sa_payload(private_ike_sa_init_responded_t *this, sa_paylo status_t status; connection_t *connection; policy_t *policy; + bool use_natt; /* prepare reply */ sa_response = sa_payload_create(); @@ -477,11 +486,13 @@ static status_t build_sa_payload(private_ike_sa_init_responded_t *this, sa_paylo policy = this->ike_sa->get_policy(this->ike_sa); connection = this->ike_sa->get_connection(this->ike_sa); + use_natt = this->ike_sa->public.is_any_host_behind_nat(&this->ike_sa->public); this->child_sa = child_sa_create(0, connection->get_my_host(connection), connection->get_other_host(connection), policy->get_soft_lifetime(policy), - policy->get_hard_lifetime(policy)); + policy->get_hard_lifetime(policy), + use_natt); status = this->child_sa->add(this->child_sa, proposal, prf_plus); prf_plus->destroy(prf_plus); diff --git a/src/charon/sa/states/initiator_init.c b/src/charon/sa/states/initiator_init.c index eb8b33f32..b12091851 100644 --- a/src/charon/sa/states/initiator_init.c +++ b/src/charon/sa/states/initiator_init.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -28,6 +29,7 @@ #include <sa/states/ike_sa_init_requested.h> #include <queues/jobs/retransmit_request_job.h> #include <crypto/diffie_hellman.h> +#include <crypto/hashers/hasher.h> #include <encoding/payloads/sa_payload.h> #include <encoding/payloads/ke_payload.h> #include <encoding/payloads/nonce_payload.h> @@ -92,7 +94,24 @@ struct private_initiator_init_t { * @param request message_t object to add the NONCE payload */ status_t (*build_nonce_payload) (private_initiator_init_t *this,message_t *request); - + /** + * Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and + * Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state. + * + * @param this calling object + * @param request message_t object to add the Notify payloads + */ + void (*build_natd_payload) (private_initiator_init_t *this, message_t *request, notify_message_type_t type, host_t *host); + + /** + * Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and + * Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state. + * + * @param this calling object + * @param request message_t object to add the Notify payloads + */ + void (*build_natd_payloads) (private_initiator_init_t *this, message_t *request); + /** * Destroy function called internally of this class after state change to state * IKE_SA_INIT_REQUESTED succeeded. @@ -187,6 +206,10 @@ status_t retry_initiate_connection (private_initiator_init_t *this, diffie_hellm message->destroy(message); return DESTROY_ME; } + + /* build Notify(NAT-D) payloads */ + this->build_natd_payloads(this, message); + /* message can now be sent (must not be destroyed) */ status = this->ike_sa->send_request(this->ike_sa, message); if (status != SUCCESS) @@ -287,6 +310,57 @@ static status_t build_nonce_payload(private_initiator_init_t *this, message_t *r } /** + * Implementation of private_initiator_init_t.build_natd_payload. + */ +static void build_natd_payload(private_initiator_init_t *this, message_t *request, notify_message_type_t type, host_t *host) +{ + chunk_t hash; + this->logger->log(this->logger, CONTROL|LEVEL1, "Building Notify(NAT-D) payload"); + notify_payload_t *notify_payload; + notify_payload = notify_payload_create(); + /*notify_payload->set_protocol_id(notify_payload, NULL);*/ + /*notify_payload->set_spi(notify_payload, NULL);*/ + notify_payload->set_notify_message_type(notify_payload, type); + hash = this->ike_sa->generate_natd_hash(this->ike_sa, + request->get_initiator_spi(request), + request->get_responder_spi(request), + host); + notify_payload->set_notification_data(notify_payload, hash); + chunk_free(&hash); + this->logger->log(this->logger, CONTROL|LEVEL2, "Add Notify(NAT-D) payload to message"); + request->add_payload(request, (payload_t *) notify_payload); +} + +/** + * Implementation of private_initiator_init_t.build_natd_payloads. + */ +static void build_natd_payloads(private_initiator_init_t *this, message_t *request) +{ + connection_t *connection; + linked_list_t *hostlist; + iterator_t *hostiter; + host_t *host; + + /* + * N(NAT_DETECTION_SOURCE_IP)+ + */ + hostlist = charon->interfaces->get_addresses(charon->interfaces); + hostiter = hostlist->create_iterator(hostlist, TRUE); + while(hostiter->iterate(hostiter, (void**)&host)) { + this->build_natd_payload(this, request, NAT_DETECTION_SOURCE_IP, + host); + } + hostiter->destroy(hostiter); + + /* + * N(NAT_DETECTION_DESTINATION_IP) + */ + connection = this->ike_sa->get_connection(this->ike_sa); + this->build_natd_payload(this, request, NAT_DETECTION_DESTINATION_IP, + connection->get_other_host(connection)); +} + +/** * Implementation of state_t.process_message. */ static status_t process_message(private_initiator_init_t *this, message_t *message) @@ -352,6 +426,8 @@ initiator_init_t *initiator_init_create(protected_ike_sa_t *ike_sa) this->build_nonce_payload = build_nonce_payload; this->build_sa_payload = build_sa_payload; this->build_ke_payload = build_ke_payload; + this->build_natd_payload = build_natd_payload; + this->build_natd_payloads = build_natd_payloads; /* private data */ this->ike_sa = ike_sa; diff --git a/src/charon/sa/states/responder_init.c b/src/charon/sa/states/responder_init.c index 809dd6e29..3e85ea5fa 100644 --- a/src/charon/sa/states/responder_init.c +++ b/src/charon/sa/states/responder_init.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -30,6 +31,7 @@ #include <encoding/payloads/nonce_payload.h> #include <encoding/payloads/notify_payload.h> #include <crypto/diffie_hellman.h> +#include <queues/jobs/send_keepalive_job.h> typedef struct private_responder_init_t private_responder_init_t; @@ -91,6 +93,37 @@ struct private_responder_init_t { logger_t *logger; /** + * Precomputed NAT-D hash for initiator. + */ + chunk_t natd_hash_i; + + /** + * Flag indicating that an initiator NAT-D hash matched. + */ + bool natd_hash_i_matched; + + /** + * NAT-D payload count for NAT_DETECTION_SOURCE_IP. + */ + int natd_seen_i; + + /** + * Precomputed NAT-D hash of responder. + */ + chunk_t natd_hash_r; + + /** + * Flag indicating that a responder NAT-D hash matched. + */ + bool natd_hash_r_matched; + + /** + * NAT-D payload count for NAT_DETECTION_DESTINATION_IP. + */ + int natd_seen_r; + + + /** * Handles received SA payload and builds the SA payload for the response. * * @param this calling object @@ -125,6 +158,24 @@ struct private_responder_init_t { status_t (*build_nonce_payload) (private_responder_init_t *this,nonce_payload_t *nonce_request, message_t *response); /** + * Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and + * Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state. + * + * @param this calling object + * @param request message_t object to add the Notify payloads + */ + void (*build_natd_payload) (private_responder_init_t *this, message_t *request, notify_message_type_t type, host_t *host); + + /** + * Builds the NAT-T Notify(NAT_DETECTION_SOURCE_IP) and + * Notify(NAT_DETECTION_DESTINATION_IP) payloads for this state. + * + * @param this calling object + * @param request message_t object to add the Notify payloads + */ + void (*build_natd_payloads) (private_responder_init_t *this, message_t *request); + + /** * Sends a IKE_SA_INIT reply containing a notify payload. * * @param this calling object @@ -185,7 +236,13 @@ static status_t process_message(private_responder_init_t *this, message_t *messa /* TODO: inform requestor */ return DESTROY_ME; } - this->ike_sa->set_connection(this->ike_sa,connection); + this->ike_sa->set_connection(this->ike_sa, connection); + status = this->ike_sa->update_connection_hosts(this->ike_sa, + destination, source); + if (status != SUCCESS) + { + return status; + } /* parse incoming message */ status = message->parse_body(message, NULL, NULL); @@ -204,7 +261,31 @@ static status_t process_message(private_responder_init_t *this, message_t *messa return DESTROY_ME; } - payloads = message->get_payload_iterator(message); + /* + * Precompute NAT-D hashes. + * Even though there SHOULD only be a single payload of Notify type + * NAT_DETECTION_DESTINATION_IP we precompute both hashes. + */ + this->natd_hash_i = this->ike_sa->generate_natd_hash(this->ike_sa, + message->get_initiator_spi(message), + message->get_responder_spi(message), + message->get_source(message)); + this->natd_hash_i_matched = FALSE; + this->natd_seen_i = 0; + this->natd_hash_r = this->ike_sa->generate_natd_hash(this->ike_sa, + message->get_initiator_spi(message), + message->get_responder_spi(message), + message->get_destination(message)); + this->natd_hash_r_matched = FALSE; + this->natd_seen_r = 0; + this->ike_sa->set_my_host_behind_nat(this->ike_sa, FALSE); + this->ike_sa->set_other_host_behind_nat(this->ike_sa, FALSE); + + /* Iterate over all payloads. + * + * The message is already checked for the right payload types. + */ + payloads = message->get_payload_iterator(message); while (payloads->has_next(payloads)) { payload_t *payload; @@ -237,6 +318,7 @@ static status_t process_message(private_responder_init_t *this, message_t *messa payloads->destroy(payloads); return status; } + break; } default: { @@ -251,10 +333,39 @@ static status_t process_message(private_responder_init_t *this, message_t *messa /* check if we have all payloads */ if (!(sa_request && ke_request && nonce_request)) { - this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain all required payloads. deleting IKE_SA"); + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request did not contain all required payloads. Deleting IKE_SA"); return DESTROY_ME; } + /* NAT-D */ + if ((!this->natd_seen_i && this->natd_seen_r > 0) + || (this->natd_seen_i > 0 && !this->natd_seen_r)) + { + this->logger->log(this->logger, AUDIT, "IKE_SA_INIT request contained wrong number of NAT-D payloads. Deleting IKE_SA"); + return DESTROY_ME; + } + if (this->natd_seen_r > 1) + { + this->logger->log(this->logger, AUDIT, "Warning: IKE_SA_INIT request contained multiple Notify(NAT_DETECTION_DESTINATION_IP) payloads."); + } + if (this->natd_seen_i > 0 && !this->natd_hash_i_matched) + { + this->logger->log(this->logger, AUDIT, "Remote host is behind NAT, using NAT-T."); + this->ike_sa->set_other_host_behind_nat(this->ike_sa, TRUE); + } + if (this->natd_seen_r > 0 && !this->natd_hash_r_matched) + { + this->logger->log(this->logger, AUDIT, "Local host is behind NAT, using NAT-T."); + this->ike_sa->set_my_host_behind_nat(this->ike_sa, TRUE); + charon->event_queue->add_relative(charon->event_queue, + (job_t*)send_keepalive_job_create(this->ike_sa->public.get_id((ike_sa_t*)this->ike_sa)), + charon->configuration->get_keepalive_interval(charon->configuration)); + } + if (!this->ike_sa->public.is_any_host_behind_nat((ike_sa_t*)this->ike_sa)) + { + this->logger->log(this->logger, AUDIT, "No NAT detected, not using NAT-T."); + } + this->ike_sa->build_message(this->ike_sa, IKE_SA_INIT, FALSE, &response); status = this->build_sa_payload(this, sa_request, response); @@ -277,7 +388,9 @@ static status_t process_message(private_responder_init_t *this, message_t *messa response->destroy(response); return status; } - + /* build Notify(NAT-D) payloads */ + this->build_natd_payloads(this, response); + /* derive all the keys used in the IKE_SA */ status = this->ike_sa->build_transforms(this->ike_sa, this->proposal, this->diffie_hellman, this->received_nonce, this->sent_nonce); if (status != SUCCESS) @@ -459,26 +572,94 @@ static status_t build_nonce_payload(private_responder_init_t *this,nonce_payload } /** + * Implementation of private_initiator_init_t.build_natd_payload. + */ +static void build_natd_payload(private_responder_init_t *this, message_t *request, notify_message_type_t type, host_t *host) +{ + chunk_t hash; + this->logger->log(this->logger, CONTROL|LEVEL1, "Building Notify(NAT-D) payload"); + notify_payload_t *notify_payload; + notify_payload = notify_payload_create(); + /*notify_payload->set_protocol_id(notify_payload, NULL);*/ + /*notify_payload->set_spi(notify_payload, NULL);*/ + notify_payload->set_notify_message_type(notify_payload, type); + hash = this->ike_sa->generate_natd_hash(this->ike_sa, + request->get_initiator_spi(request), + request->get_responder_spi(request), + host); + notify_payload->set_notification_data(notify_payload, hash); + chunk_free(&hash); + this->logger->log(this->logger, CONTROL|LEVEL2, "Add Notify(NAT-D) payload to message"); + request->add_payload(request, (payload_t *) notify_payload); +} + +/** + * Implementation of private_initiator_init_t.build_natd_payloads. + */ +static void build_natd_payloads(private_responder_init_t *this, message_t *request) +{ + connection_t *connection; + connection = this->ike_sa->get_connection(this->ike_sa); + this->build_natd_payload(this, request, NAT_DETECTION_SOURCE_IP, + connection->get_my_host(connection)); + this->build_natd_payload(this, request, NAT_DETECTION_DESTINATION_IP, + connection->get_other_host(connection)); +} + +/** * Implementation of private_responder_init_t.process_notify_payload. */ static status_t process_notify_payload(private_responder_init_t *this, notify_payload_t *notify_payload) { + chunk_t notification_data; notify_message_type_t notify_message_type = notify_payload->get_notify_message_type(notify_payload); this->logger->log(this->logger, CONTROL|LEVEL1, "process notify type %s", mapping_find(notify_message_type_m, notify_message_type)); - - if (notify_payload->get_protocol_id(notify_payload) != PROTO_IKE) - { - this->logger->log(this->logger, ERROR | LEVEL1, "notify reply not for IKE protocol."); - return FAILED; - } switch (notify_message_type) { + case NAT_DETECTION_DESTINATION_IP: + { + this->natd_seen_r++; + if (this->natd_hash_r_matched) + return SUCCESS; + + notification_data = notify_payload->get_notification_data(notify_payload); + if (chunk_equals(notification_data, this->natd_hash_r)) + { + this->natd_hash_r_matched = TRUE; + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match"); + } + else + { + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch"); + } + + return SUCCESS; + } + case NAT_DETECTION_SOURCE_IP: + { + this->natd_seen_i++; + if (this->natd_hash_i_matched) + return SUCCESS; + + notification_data = notify_payload->get_notification_data(notify_payload); + if (chunk_equals(notification_data, this->natd_hash_i)) + { + this->natd_hash_i_matched = TRUE; + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash match"); + } + else + { + this->logger->log(this->logger, CONTROL|LEVEL3, "NAT-D hash mismatch"); + } + + return SUCCESS; + } default: { - this->logger->log(this->logger, CONTROL, "IKE_SA_INIT request contained a notify (%d), ignored.", - notify_message_type); + this->logger->log(this->logger, CONTROL, "IKE_SA_INIT request contained a notify (%d), ignored.", + notify_message_type); return SUCCESS; } } @@ -501,8 +682,12 @@ static void destroy(private_responder_init_t *this) this->logger->log(this->logger, CONTROL | LEVEL2, "destroy nonces"); chunk_free(&(this->sent_nonce)); + this->logger->log(this->logger, CONTROL | LEVEL2, "destroy received nonce"); chunk_free(&(this->received_nonce)); + chunk_free(&(this->natd_hash_i)); + chunk_free(&(this->natd_hash_r)); + if (this->diffie_hellman != NULL) { this->logger->log(this->logger, CONTROL | LEVEL2, "destroy diffie_hellman_t hellman object"); @@ -521,7 +706,10 @@ static void destroy(private_responder_init_t *this) */ static void destroy_after_state_change (private_responder_init_t *this) { - this->logger->log(this->logger, CONTROL | LEVEL1, "going to destroy responder_init_t state object"); + this->logger->log(this->logger, CONTROL | LEVEL1, "Going to destroy responder_init_t state object"); + + chunk_free(&(this->natd_hash_i)); + chunk_free(&(this->natd_hash_r)); /* destroy diffie hellman object */ if (this->diffie_hellman != NULL) @@ -556,6 +744,8 @@ responder_init_t *responder_init_create(protected_ike_sa_t *ike_sa) this->build_nonce_payload = build_nonce_payload; this->destroy_after_state_change = destroy_after_state_change; this->process_notify_payload = process_notify_payload; + this->build_natd_payload = build_natd_payload; + this->build_natd_payloads = build_natd_payloads; /* private data */ this->ike_sa = ike_sa; @@ -565,6 +755,12 @@ responder_init_t *responder_init_create(protected_ike_sa_t *ike_sa) this->dh_group_number = MODP_NONE; this->diffie_hellman = NULL; this->proposal = NULL; + this->natd_hash_i = CHUNK_INITIALIZER; + this->natd_hash_i_matched = FALSE; + this->natd_seen_i = 0; + this->natd_hash_r = CHUNK_INITIALIZER; + this->natd_hash_r_matched = FALSE; + this->natd_seen_r = 0; return &(this->public); } diff --git a/src/charon/sa/states/state.c b/src/charon/sa/states/state.c index e79d33e03..5436e7d44 100644 --- a/src/charon/sa/states/state.c +++ b/src/charon/sa/states/state.c @@ -38,3 +38,4 @@ mapping_t ike_sa_state_m[] = { {DELETE_CHILD_SA_REQUESTED, "DELETE_CHILD_SA_REQUESTED"}, {MAPPING_END, NULL} }; + diff --git a/src/charon/testing/Makefile.am b/src/charon/testing/Makefile.am index 32f80d2de..0397a37e2 100644 --- a/src/charon/testing/Makefile.am +++ b/src/charon/testing/Makefile.am @@ -14,13 +14,13 @@ hasher_test.h sender_test.c certificate_test.c job_queue_test.h scheduler_test.h rsa_test.c sender_test.h generator_test.c aes_cbc_crypter_test.c certificate_test.h prf_plus_test.c \ rsa_test.h generator_test.h aes_cbc_crypter_test.h send_queue_test.c -testing_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lgmp -lpthread \ +testing_LDADD = $(top_builddir)/src/libstrongswan/libstrongswan.la -lgmp -lpthread -lm \ $(top_srcdir)/src/charon/connection.o $(top_srcdir)/src/charon/local_connection_store.o $(top_srcdir)/src/charon/policy.o \ $(top_srcdir)/src/charon/local_policy_store.o $(top_srcdir)/src/charon/local_credential_store.o $(top_srcdir)/src/charon/traffic_selector.o \ $(top_srcdir)/src/charon/proposal.o $(top_srcdir)/src/charon/configuration.o $(top_srcdir)/src/charon/state.o $(top_srcdir)/src/charon/ike_sa_init_requested.o \ $(top_srcdir)/src/charon/ike_sa_init_responded.o $(top_srcdir)/src/charon/ike_sa_established.o $(top_srcdir)/src/charon/responder_init.o \ $(top_srcdir)/src/charon/initiator_init.o $(top_srcdir)/src/charon/ike_auth_requested.o $(top_srcdir)/src/charon/delete_ike_sa_requested.o \ -$(top_srcdir)/src/charon/delete_child_sa_requested.o \ +$(top_srcdir)/src/charon/delete_child_sa_requested.o $(top_srcdir)/src/charon/create_child_sa_requested.o \ $(top_srcdir)/src/charon/child_sa.o $(top_srcdir)/src/charon/ike_sa.o $(top_srcdir)/src/charon/ike_sa_manager.o $(top_srcdir)/src/charon/ike_sa_id.o \ $(top_srcdir)/src/charon/authenticator.o $(top_srcdir)/src/charon/encryption_payload.o $(top_srcdir)/src/charon/cert_payload.o \ $(top_srcdir)/src/charon/traffic_selector_substructure.o $(top_srcdir)/src/charon/transform_attribute.o $(top_srcdir)/src/charon/configuration_attribute.o \ @@ -30,7 +30,7 @@ $(top_srcdir)/src/charon/ke_payload.o $(top_srcdir)/src/charon/unknown_payload.o $(top_srcdir)/src/charon/delete_payload.o $(top_srcdir)/src/charon/sa_payload.o $(top_srcdir)/src/charon/certreq_payload.o $(top_srcdir)/src/charon/vendor_id_payload.o \ $(top_srcdir)/src/charon/proposal_substructure.o $(top_srcdir)/src/charon/payload.o $(top_srcdir)/src/charon/message.o $(top_srcdir)/src/charon/generator.o \ $(top_srcdir)/src/charon/parser.o $(top_srcdir)/src/charon/packet.o $(top_srcdir)/src/charon/socket.o $(top_srcdir)/src/charon/job.o \ -$(top_srcdir)/src/charon/delete_child_sa_job.o $(top_srcdir)/src/charon/rekey_child_sa_job.o $(top_srcdir)/src/charon/create_child_sa_requested.o \ +$(top_srcdir)/src/charon/delete_child_sa_job.o $(top_srcdir)/src/charon/rekey_child_sa_job.o $(top_srcdir)/src/charon/send_keepalive_job.o $(top_srcdir)/src/charon/send_dpd_job.o \ $(top_srcdir)/src/charon/delete_established_ike_sa_job.o $(top_srcdir)/src/charon/incoming_packet_job.o $(top_srcdir)/src/charon/delete_half_open_ike_sa_job.o \ $(top_srcdir)/src/charon/retransmit_request_job.o $(top_srcdir)/src/charon/initiate_ike_sa_job.o $(top_srcdir)/src/charon/job_queue.o $(top_srcdir)/src/charon/event_queue.o \ $(top_srcdir)/src/charon/send_queue.o $(top_srcdir)/src/charon/kernel_interface.o $(top_srcdir)/src/charon/thread_pool.o $(top_srcdir)/src/charon/scheduler.o \ diff --git a/src/charon/testing/child_sa_test.c b/src/charon/testing/child_sa_test.c index 2c318a624..e45e33e08 100644 --- a/src/charon/testing/child_sa_test.c +++ b/src/charon/testing/child_sa_test.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -34,6 +35,7 @@ void test_child_sa(protected_tester_t *tester) { proposal_t *proposal1, *proposal2; linked_list_t *list; + connection_t *connection; host_t *local_me, *remote_me; host_t *local_other, *remote_other; child_sa_t *local_sa, *remote_sa; @@ -49,8 +51,8 @@ void test_child_sa(protected_tester_t *tester) remote_me = host_create(AF_INET, "192.168.0.3", 0); remote_other = host_create(AF_INET, "192.168.0.4", 0); - local_sa = child_sa_create(0, local_me, local_other, 5, 10); - remote_sa = child_sa_create(0, remote_me, remote_other, 5, 10); + local_sa = child_sa_create(0, local_me, local_other, 5, 10, FALSE); + remote_sa = child_sa_create(0, remote_me, remote_other, 5, 10, FALSE); proposal1 = proposal_create(PROTO_ESP); proposal1->add_algorithm(proposal1, ENCRYPTION_ALGORITHM, ENCR_AES_CBC, 16); diff --git a/src/charon/testing/generator_test.c b/src/charon/testing/generator_test.c index 1b3798597..ac2c4cf32 100644 --- a/src/charon/testing/generator_test.c +++ b/src/charon/testing/generator_test.c @@ -710,7 +710,7 @@ void test_generator_with_notify_payload(protected_tester_t *tester) notify_payload->set_protocol_id(notify_payload,255); notify_payload->set_notify_message_type(notify_payload,63333); /* Hex F765 */ - notify_payload->set_spi(notify_payload, 0x3132333435ll); + notify_payload->set_spi(notify_payload, 0x31323334); notify_payload->set_notification_data(notify_payload,notification_data); generator->generate_payload(generator,(payload_t *)notify_payload); @@ -719,11 +719,10 @@ void test_generator_with_notify_payload(protected_tester_t *tester) u_int8_t expected_generation[] = { /* payload header */ - 0x00,0x00,0x00,0x12, - 0xFF,0x05,0xF7,0x65, + 0x00,0x00,0x00,0x11, + 0xFF,0x04,0xF7,0x65, /* spi */ 0x31,0x32,0x33,0x34, - 0x35, /* notification data */ 0x36,0x37,0x38,0x39, 0x30, diff --git a/src/charon/testing/kernel_interface_test.c b/src/charon/testing/kernel_interface_test.c index 04c0d40b7..705fe5543 100644 --- a/src/charon/testing/kernel_interface_test.c +++ b/src/charon/testing/kernel_interface_test.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -20,8 +21,7 @@ * for more details. */ -#include <unistd.h> - + #include "kernel_interface_test.h" #include <daemon.h> @@ -30,18 +30,91 @@ #include <utils/host.h> +/** + * @brief private method to test kernel_interface with optional NAT-T configuration data + */ + void private_test_kernel_interface(protected_tester_t *tester, natt_conf_t *natt) +{ + kernel_interface_t *kernel_interface; + u_int32_t spi; + host_t *me, *other, *left, *right; + status_t status; + prf_plus_t *prf_plus; + prf_t *prf; + u_int8_t key_bytes[] = { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08 + }; + chunk_t key = chunk_from_buf(key_bytes); + algorithm_t int_alg = {AUTH_HMAC_MD5_96, 0}; + algorithm_t enc_alg = {ENCR_AES_CBC, 128}; + + prf = prf_create(PRF_HMAC_MD5); + prf->set_key(prf, key); + prf_plus = prf_plus_create(prf, key); + + + kernel_interface = kernel_interface_create(); + + me = host_create(AF_INET, "192.168.0.2", 0); + other = host_create(AF_INET, "192.168.0.3", 0); + + status = kernel_interface->get_spi(kernel_interface, me, other, PROTO_ESP, 1234, &spi); + tester->assert_true(tester, status == SUCCESS, "spi get"); + + status = kernel_interface->add_sa(kernel_interface, me, other, spi, PROTO_ESP, 1234, 5, 10, &enc_alg, &int_alg, prf_plus, natt, TRUE); + tester->assert_true(tester, status == SUCCESS, "add sa"); + + left = host_create(AF_INET, "10.1.0.0", 0); + right = host_create(AF_INET, "10.2.0.0", 0); + + status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 16, 16, XFRM_POLICY_OUT, 0, PROTO_ESP, 1234); + tester->assert_true(tester, status == SUCCESS, "add policy"); + + status = kernel_interface->del_policy(kernel_interface, me, other, left, right, 16, 16, XFRM_POLICY_OUT, 0); + tester->assert_true(tester, status == SUCCESS, "del policy"); + + status = kernel_interface->del_sa(kernel_interface, other, spi, PROTO_ESP); + tester->assert_true(tester, status == SUCCESS, "del sa"); + + me->destroy(me); + other->destroy(other); + left->destroy(left); + right->destroy(right); + + kernel_interface->destroy(kernel_interface); +} + /* * described in Header-File */ void test_kernel_interface(protected_tester_t *tester) { + private_test_kernel_interface(tester, NULL); +} + +/* + * described in Header-File + */ +void test_kernel_interface_with_nat(protected_tester_t *tester) +{ + natt_conf_t natt; + natt.sport = 4500; + natt.dport = 9876; + + private_test_kernel_interface(tester, &natt); +} + +void test_kernel_interface_update_hosts(protected_tester_t *tester) +{ kernel_interface_t *kernel_interface; u_int32_t spi; - host_t *me, *other, *left, *right; + host_t *me, *other, *new_me, *new_other, *left, *right; status_t status; prf_plus_t *prf_plus; - prf_t *prf; + prf_t *prf; u_int8_t key_bytes[] = { + 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08 }; chunk_t key = chunk_from_buf(key_bytes); @@ -50,41 +123,53 @@ void test_kernel_interface(protected_tester_t *tester) prf = prf_create(PRF_HMAC_MD5); prf->set_key(prf, key); - prf_plus = prf_plus_create(prf, key); - + prf_plus = prf_plus_create(prf, key); + kernel_interface = kernel_interface_create(); me = host_create(AF_INET, "192.168.0.2", 0); other = host_create(AF_INET, "192.168.0.3", 0); - + + natt_conf_t natt; + natt.sport = 4500; + natt.dport = 9876; + status = kernel_interface->get_spi(kernel_interface, me, other, PROTO_ESP, 1234, &spi); tester->assert_true(tester, status == SUCCESS, "spi get"); - status = kernel_interface->add_sa(kernel_interface, me, other, spi, PROTO_ESP, 1234, 0, 0, &enc_alg, &int_alg, prf_plus, TRUE); + status = kernel_interface->add_sa(kernel_interface, me, other, spi, PROTO_ESP, 1234, 5, 10, &enc_alg, &int_alg, prf_plus, &natt, TRUE); tester->assert_true(tester, status == SUCCESS, "add sa"); - + left = host_create(AF_INET, "10.1.0.0", 0); right = host_create(AF_INET, "10.2.0.0", 0); - - status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 24, 24, XFRM_POLICY_OUT, 0, PROTO_ESP, 1234); + + status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 16, 16, XFRM_POLICY_OUT, 0, PROTO_ESP, 1234); tester->assert_true(tester, status == SUCCESS, "add policy OUT"); - status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 24, 24, XFRM_POLICY_IN, 0, PROTO_ESP, 1234); + status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 16, 16, XFRM_POLICY_IN, 0, PROTO_ESP, 1234); tester->assert_true(tester, status == SUCCESS, "add policy IN"); - status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 24, 24, XFRM_POLICY_FWD, 0, PROTO_ESP, 1234); + status = kernel_interface->add_policy(kernel_interface, me, other, left, right, 16, 16, XFRM_POLICY_FWD, 0, PROTO_ESP, 1234); tester->assert_true(tester, status == SUCCESS, "add policy FWD"); + + new_me = host_create(AF_INET, "192.168.1.12", 4500); + new_other = host_create(AF_INET, "192.168.1.13", 6543); + status = kernel_interface->update_sa_hosts(kernel_interface, me, other, new_me, new_other, me->get_differences(me, new_me), other->get_differences(other, new_other), spi, PROTO_ESP); + tester->assert_true(tester, status == SUCCESS, "update hosts on sa"); - kernel_interface->del_sa(kernel_interface, other, spi, PROTO_ESP); - - sleep(10); - + status = kernel_interface->del_policy(kernel_interface, me, other, left, right, 16, 16, XFRM_POLICY_OUT, 0); + tester->assert_true(tester, status == SUCCESS, "del policy"); + + status = kernel_interface->del_sa(kernel_interface, other, spi, PROTO_ESP); + tester->assert_true(tester, status == SUCCESS, "del sa"); + me->destroy(me); other->destroy(other); + new_me->destroy(new_me); + new_other->destroy(new_other); left->destroy(left); right->destroy(right); - - + + sleep(15); kernel_interface->destroy(kernel_interface); - } diff --git a/src/charon/testing/kernel_interface_test.h b/src/charon/testing/kernel_interface_test.h index fc8dab4b6..d0397a517 100644 --- a/src/charon/testing/kernel_interface_test.h +++ b/src/charon/testing/kernel_interface_test.h @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -34,5 +35,23 @@ */ void test_kernel_interface(protected_tester_t *tester); +/** + * @brief Test function used to test the kernel_interface functionality. Incldes NAT-T configuration. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_kernel_interface_with_nat(protected_tester_t *tester); + +/** + * @brief Test function used to test the hosts update functionality in kernel_interface_t. + * + * @param tester associated tester object + * + * @ingroup testcases + */ +void test_kernel_interface_update_hosts(protected_tester_t *tester); + #endif /*KERNEL_INTERFACE_TEST_H_*/ diff --git a/src/charon/testing/socket_test.c b/src/charon/testing/socket_test.c index 2b751935a..8bf4963c4 100644 --- a/src/charon/testing/socket_test.c +++ b/src/charon/testing/socket_test.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -35,7 +36,7 @@ void test_socket(protected_tester_t *tester) { int packet_count = 10; int current; - socket_t *skt = socket_create(500); + socket_t *skt = socket_create(500, 4500); packet_t *pkt = packet_create(); char test_data[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03, /* spi */ diff --git a/src/charon/testing/testcases.c b/src/charon/testing/testcases.c index 5e9bac535..828c24c2d 100644 --- a/src/charon/testing/testcases.c +++ b/src/charon/testing/testcases.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -126,7 +127,9 @@ test_t connection_test = {test_connection, "connection_t test"}; test_t policy_test = {test_policy, "policy_t test"}; test_t proposal_test = {test_proposal, "proposal_t test"}; test_t rsa_test = {test_rsa, "RSA private/public key test"}; -test_t kernel_interface_test = {test_kernel_interface, "Kernel Interface"}; +test_t kernel_interface_test1 = {test_kernel_interface, "Kernel Interface"}; +test_t kernel_interface_test2 = {test_kernel_interface_with_nat, "Kernel Interface: NAT"}; +test_t kernel_interface_test3 = {test_kernel_interface_update_hosts, "Kernel Interface: Hosts update"}; test_t child_sa_test = {test_child_sa, "Child SA"}; test_t certificate_test = {test_certificate, "X509 Certificate"}; test_t leak_detective_test = {test_leak_detective, "LEAK detective"}; @@ -159,7 +162,7 @@ daemon_t *daemon_create() /* assign methods */ charon->kill = daemon_kill; - //charon->socket = socket_create(500); + charon->socket = socket_create(500, 4500); charon->ike_sa_manager = ike_sa_manager_create(); charon->job_queue = job_queue_create(); charon->event_queue = event_queue_create(); diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c index 64160738a..5e4480313 100644 --- a/src/charon/threads/kernel_interface.c +++ b/src/charon/threads/kernel_interface.c @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * Copyright (C) 2003 Herbert Xu. @@ -26,6 +27,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <linux/netlink.h> +#include <linux/rtnetlink.h> #include <pthread.h> #include <unistd.h> #include <fcntl.h> @@ -45,69 +47,12 @@ #define SPD_PRIORITY 1024 -#define XFRM_DATA_LENGTH 1024 +#define BUFFER_SIZE 1024 - -typedef struct xfrm_data_t xfrm_data_t; - -/** - * Lenght/Type/data struct for userdata in xfrm - * We dont use the "I-don't-know-where-they-come-from"-structs - * used in the kernel. - */ -struct xfrm_data_t { - /** - * length of the data - */ - u_int16_t length; - - /** - * type of data - */ - u_int16_t type; - - /** - * and the data itself, for different purposes - */ - union { - /** algorithm */ - struct xfrm_algo algo; - /** policy tmpl */ - struct xfrm_user_tmpl tmpl; - }; -}; - - -typedef struct netlink_message_t netlink_message_t; - -/** - * Representation of ANY netlink message used - */ -struct netlink_message_t { - - /** - * header of the netlink message - */ - struct nlmsghdr hdr; - - union { - /** error message */ - struct nlmsgerr e; - /** message for spi allocation */ - struct xfrm_userspi_info spi; - /** message for SA manipulation */ - struct xfrm_usersa_id sa_id; - /** message for SA installation */ - struct xfrm_usersa_info sa; - /** message for policy manipulation */ - struct xfrm_userpolicy_id policy_id; - /** message for policy installation */ - struct xfrm_userpolicy_info policy; - /** expire message sent from kernel */ - struct xfrm_user_expire expire; - }; - u_int8_t data[XFRM_DATA_LENGTH]; -}; +/* returns a pointer to the first rtattr following the nlmsghdr *nlh and the 'usual' netlink data x like 'struct xfrm_usersa_info' */ +#define XFRM_RTA(nlh, x) ((struct rtattr*)(NLMSG_DATA(nlh) + NLMSG_ALIGN(sizeof(x)))) +/* returns the total size of attached rta data (after 'usual' netlink data x like 'struct xfrm_usersa_info') */ +#define XFRM_PAYLOAD(nlh, x) NLMSG_PAYLOAD(nlh, sizeof(x)) typedef struct kernel_algorithm_t kernel_algorithm_t; @@ -249,7 +194,7 @@ struct private_kernel_interface_t { /** * Sends a netlink_message_t down to the kernel and wait for reply. */ - status_t (*send_message) (private_kernel_interface_t *this, netlink_message_t *request, netlink_message_t **response); + status_t (*send_message) (private_kernel_interface_t *this, struct nlmsghdr *request, struct nlmsghdr **response); }; /** @@ -260,48 +205,53 @@ static status_t get_spi(private_kernel_interface_t *this, protocol_id_t protocol, u_int32_t reqid, u_int32_t *spi) { - netlink_message_t request, *response; + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *response; + + memset(&request, 0, sizeof(request)); status_t status = SUCCESS; - this->logger->log(this->logger, CONTROL|LEVEL1, "Getting SPI for reqid %d", reqid); + this->logger->log(this->logger, CONTROL|LEVEL2, "getting spi"); - memset(&request, 0, sizeof(request)); - request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.spi))); - request.hdr.nlmsg_flags = NLM_F_REQUEST; - request.hdr.nlmsg_type = XFRM_MSG_ALLOCSPI; - request.spi.info.saddr = src->get_xfrm_addr(src); - request.spi.info.id.daddr = dest->get_xfrm_addr(dest); - request.spi.info.mode = TRUE; /* tunnel mode */ - request.spi.info.reqid = reqid; - request.spi.info.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; - request.spi.info.family = PF_INET; - request.spi.min = 0xc0000000; - request.spi.max = 0xcFFFFFFF; - - if (this->send_message(this, &request, &response) != SUCCESS) + struct nlmsghdr *hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST; + hdr->nlmsg_type = XFRM_MSG_ALLOCSPI; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userspi_info)); + + struct xfrm_userspi_info *userspi = (struct xfrm_userspi_info*)NLMSG_DATA(hdr); + userspi->info.saddr = src->get_xfrm_addr(src); + userspi->info.id.daddr = dest->get_xfrm_addr(dest); + userspi->info.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + userspi->info.mode = TRUE; /* tunnel mode */ + userspi->info.reqid = reqid; + userspi->info.family = src->get_family(src); + userspi->min = 0xc0000000; + userspi->max = 0xcFFFFFFF; + + if (this->send_message(this, hdr, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); return FAILED; } - else if (response->hdr.nlmsg_type == NLMSG_ERROR) + else if (response->nlmsg_type == NLMSG_ERROR) { this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_ALLOCSPI got an error: %s", - strerror(-response->e.error)); + strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); status = FAILED; } - else if (response->hdr.nlmsg_type != XFRM_MSG_NEWSA) + else if (response->nlmsg_type != XFRM_MSG_NEWSA) { this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_ALLOCSPI got a unknown reply"); status = FAILED; } - else if (response->hdr.nlmsg_len < NLMSG_LENGTH(sizeof(response->sa))) + else if (response->nlmsg_len < NLMSG_LENGTH(sizeof(struct xfrm_usersa_info))) { this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_ALLOCSPI got an invalid reply"); status = FAILED; } else { - *spi = response->sa.id.spi; + *spi = ((struct xfrm_usersa_info*)NLMSG_DATA(response))->id.spi; this->logger->log(this->logger, CONTROL|LEVEL1, "SPI is 0x%x", *spi); } free(response); @@ -312,59 +262,56 @@ static status_t get_spi(private_kernel_interface_t *this, /** * Implementation of kernel_interface_t.add_sa. */ -static status_t add_sa( private_kernel_interface_t *this, - host_t *me, - host_t *other, - u_int32_t spi, - protocol_id_t protocol, - u_int32_t reqid, - u_int64_t expire_soft, - u_int64_t expire_hard, - algorithm_t *enc_alg, - algorithm_t *int_alg, - prf_plus_t *prf_plus, - bool replace) +static status_t add_sa(private_kernel_interface_t *this, + host_t *me, host_t *other, + u_int32_t spi, protocol_id_t protocol, + u_int32_t reqid, + u_int64_t expire_soft, u_int64_t expire_hard, + algorithm_t *enc_alg, algorithm_t *int_alg, + prf_plus_t *prf_plus, natt_conf_t *natt, + bool replace) { - netlink_message_t request, *response; - status_t status = SUCCESS; - int key_size; + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *response; char *alg_name; + size_t key_size; memset(&request, 0, sizeof(request)); + status_t status = SUCCESS; - this->logger->log(this->logger, CONTROL|LEVEL1, "Adding %s SA with SPI 0x%x, reqid %d to kernel", - mapping_find(protocol_id_m, protocol), htonl(spi), reqid); - - request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - request.hdr.nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; + this->logger->log(this->logger, CONTROL|LEVEL2, "adding SA"); + + struct nlmsghdr *hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); - request.sa.saddr = me->get_xfrm_addr(me); - request.sa.id.daddr = other->get_xfrm_addr(other); + struct xfrm_usersa_info *sa = (struct xfrm_usersa_info*)NLMSG_DATA(hdr); + sa->saddr = me->get_xfrm_addr(me); + sa->id.daddr = other->get_xfrm_addr(other); - request.sa.id.spi = spi; - request.sa.id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; - request.sa.family = me->get_family(me); - request.sa.mode = TRUE; /* tunnel mode */ - request.sa.replay_window = 32; - request.sa.reqid = reqid; + sa->id.spi = spi; + sa->id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa->family = me->get_family(me); + sa->mode = TRUE; /* tunnel mode */ + sa->replay_window = 32; + sa->reqid = reqid; /* we currently do not expire SAs by volume/packet count */ - request.sa.lft.soft_byte_limit = XFRM_INF; - request.sa.lft.hard_byte_limit = XFRM_INF; - request.sa.lft.soft_packet_limit = XFRM_INF; - request.sa.lft.hard_packet_limit = XFRM_INF; + sa->lft.soft_byte_limit = XFRM_INF; + sa->lft.hard_byte_limit = XFRM_INF; + sa->lft.soft_packet_limit = XFRM_INF; + sa->lft.hard_packet_limit = XFRM_INF; /* we use lifetimes since added, not since used */ - request.sa.lft.soft_add_expires_seconds = expire_soft; - request.sa.lft.hard_add_expires_seconds = expire_hard; - request.sa.lft.soft_use_expires_seconds = 0; - request.sa.lft.hard_use_expires_seconds = 0; - - request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.sa))); + sa->lft.soft_add_expires_seconds = expire_soft; + sa->lft.hard_add_expires_seconds = expire_hard; + sa->lft.soft_use_expires_seconds = 0; + sa->lft.hard_use_expires_seconds = 0; if (enc_alg->algorithm != ENCR_UNDEFINED) { - xfrm_data_t *data = (xfrm_data_t*)(((u_int8_t*)&request) + request.hdr.nlmsg_len); + struct rtattr *rthdr = (struct rtattr*)(request + hdr->nlmsg_len); - data->type = XFRMA_ALG_CRYPT; + rthdr->rta_type = XFRMA_ALG_CRYPT; alg_name = lookup_algorithm(encryption_algs, enc_alg, &key_size); if (alg_name == NULL) { @@ -374,22 +321,25 @@ static status_t add_sa( private_kernel_interface_t *this, } this->logger->log(this->logger, CONTROL|LEVEL2, " using encryption algorithm %s with key size %d", mapping_find(encryption_algorithm_m, enc_alg->algorithm), key_size); - data->length = 4 + sizeof(data->algo) + key_size; - data->algo.alg_key_len = key_size; - request.hdr.nlmsg_len += data->length; - if (request.hdr.nlmsg_len > sizeof(request)) + + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + key_size); + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) { return FAILED; } - strcpy(data->algo.alg_name, alg_name); - prf_plus->get_bytes(prf_plus, key_size / 8, data->algo.alg_key); + + struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); + algo->alg_key_len = key_size; + strcpy(algo->alg_name, alg_name); + prf_plus->get_bytes(prf_plus, key_size / 8, algo->alg_key); } if (int_alg->algorithm != AUTH_UNDEFINED) { - xfrm_data_t *data = (xfrm_data_t*)(((u_int8_t*)&request) + request.hdr.nlmsg_len); + struct rtattr *rthdr = (struct rtattr*)(request + hdr->nlmsg_len); - data->type = XFRMA_ALG_AUTH; + rthdr->rta_type = XFRMA_ALG_AUTH; alg_name = lookup_algorithm(integrity_algs, int_alg, &key_size); if (alg_name == NULL) { @@ -399,33 +349,68 @@ static status_t add_sa( private_kernel_interface_t *this, } this->logger->log(this->logger, CONTROL|LEVEL2, " using integrity algorithm %s with key size %d", mapping_find(integrity_algorithm_m, int_alg->algorithm), key_size); - data->length = 4 + sizeof(data->algo) + key_size; - data->algo.alg_key_len = key_size; - request.hdr.nlmsg_len += data->length; - if (request.hdr.nlmsg_len > sizeof(request)) + + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + key_size); + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) { return FAILED; } - strcpy(data->algo.alg_name, alg_name); - prf_plus->get_bytes(prf_plus, key_size / 8, data->algo.alg_key); + + struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); + algo->alg_key_len = key_size; + strcpy(algo->alg_name, alg_name); + prf_plus->get_bytes(prf_plus, key_size / 8, algo->alg_key); } - /* TODO: add IPComp here*/ + /* TODO: add IPComp here */ - if (this->send_message(this, &request, &response) != SUCCESS) + if (natt) + { + struct rtattr *rthdr = (struct rtattr*)(request + hdr->nlmsg_len); + + rthdr->rta_type = XFRMA_ENCAP; + rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl)); + + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_encap_tmpl* encap = (struct xfrm_encap_tmpl*)RTA_DATA(rthdr); + /* UDP_ENCAP_ESPINUDP, see /usr/src/linux/include/linux/udp.h + * we could probably use 3 here (as pluto does) although the + * result is eventually the same. */ + encap->encap_type = 2; + encap->encap_sport = ntohs(natt->sport); + encap->encap_dport = ntohs(natt->dport); + memset(&encap->encap_oa, 0, sizeof (xfrm_address_t)); + /* encap_oa could probably be derived from the + * traffic selectors [rfc4306, p39]. In the netlink kernel implementation + * pluto does the same as we do here but it uses encap_oa in the + * pfkey implementation. BUT as /usr/src/linux/net/key/af_key.c indicates + * the kernel ignores it anyway + * -> does that mean that NAT-T encap doesn't work in transport mode? + * No. The reason the kernel ignores NAT-OA is that it recomputes + * (or, rather, just ignores) the checksum. If packets pass + * the IPSec checks it marks them "checksum ok" so OA isn't needed. */ + } + + if (this->send_message(this, hdr, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); return FAILED; } - else if (response->hdr.nlmsg_type != NLMSG_ERROR) + else if (response->nlmsg_type != NLMSG_ERROR) { this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWSA not acknowledged"); status = FAILED; } - else if (response->e.error) + else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) { - this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWSA received error: %s", - strerror(-response->e.error)); + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWSA got an error: %s", + strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); status = FAILED; } @@ -433,43 +418,158 @@ static status_t add_sa( private_kernel_interface_t *this, return status; } -static status_t del_sa( private_kernel_interface_t *this, - host_t *dst, - u_int32_t spi, - protocol_id_t protocol) +static status_t update_sa_hosts( + private_kernel_interface_t *this, + host_t *src, host_t *dst, + host_t *new_src, host_t *new_dst, + int src_changes, int dst_changes, + u_int32_t spi, protocol_id_t protocol) { - netlink_message_t request, *response; + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *update, *response; + memset(&request, 0, sizeof(request)); status_t status = SUCCESS; - this->logger->log(this->logger, CONTROL|LEVEL1, "Deleting %s SA with SPI 0x%x from kernel", - mapping_find(protocol_id_m, protocol), htonl(spi)); + this->logger->log(this->logger, CONTROL|LEVEL2, "getting SA"); + + struct nlmsghdr *hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST ; + hdr->nlmsg_type = XFRM_MSG_GETSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); + + struct xfrm_usersa_id *sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); + sa_id->daddr = dst->get_xfrm_addr(dst); + sa_id->spi = spi; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->family = dst->get_family(dst); + + if (this->send_message(this, hdr, &update) != SUCCESS) + { + this->logger->log(this->logger, ERROR, "netlink communication failed"); + return FAILED; + } + else if (update->nlmsg_type == NLMSG_ERROR) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETSA got an error: %s", + strerror(-((struct nlmsgerr*)NLMSG_DATA(update))->error)); + free(update); + return FAILED; + } + else if (update->nlmsg_type != XFRM_MSG_NEWSA) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETSA got a unknown reply"); + free(update); + return FAILED; + } + else if (update->nlmsg_len < NLMSG_LENGTH(sizeof(struct xfrm_usersa_info))) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_GETSA got an invalid reply"); + free(update); + return FAILED; + } - request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - request.hdr.nlmsg_type = XFRM_MSG_DELSA; + this->logger->log(this->logger, CONTROL|LEVEL2, "updating SA"); + update->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + update->nlmsg_type = XFRM_MSG_UPDSA; - request.sa_id.daddr = dst->get_xfrm_addr(dst); + struct xfrm_usersa_info *sa = (struct xfrm_usersa_info*)NLMSG_DATA(update); + if (src_changes & HOST_DIFF_ADDR) + { + sa->saddr = new_src->get_xfrm_addr(new_src); + } + + if (dst_changes & HOST_DIFF_ADDR) + { + this->logger->log(this->logger, CONTROL|LEVEL2, "destination address changed! replacing SA"); + + update->nlmsg_type = XFRM_MSG_NEWSA; + sa->id.daddr = new_dst->get_xfrm_addr(new_dst); + } - request.sa_id.spi = spi; - request.sa_id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; - request.sa_id.family = dst->get_family(dst); + if (src_changes & HOST_DIFF_PORT || dst_changes & HOST_DIFF_PORT) + { + struct rtattr *rthdr = XFRM_RTA(update, struct xfrm_usersa_info); + size_t rtsize = XFRM_PAYLOAD(update, struct xfrm_usersa_info); + while (RTA_OK(rthdr, rtsize)) + { + if (rthdr->rta_type == XFRMA_ENCAP) + { + struct xfrm_encap_tmpl* encap = (struct xfrm_encap_tmpl*)RTA_DATA(rthdr); + encap->encap_sport = ntohs(new_src->get_port(new_src)); + encap->encap_dport = ntohs(new_dst->get_port(new_dst)); + break; + } + rthdr = RTA_NEXT(rthdr, rtsize); + } + } - request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.sa_id))); + if (this->send_message(this, update, &response) != SUCCESS) + { + this->logger->log(this->logger, ERROR, "netlink communication failed"); + free(update); + return FAILED; + } + else if (response->nlmsg_type != NLMSG_ERROR) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_XXXSA not acknowledged"); + status = FAILED; + } + else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) + { + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_XXXSA got an error: %s", + strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); + status = FAILED; + } + else if (dst_changes & HOST_DIFF_ADDR) + { + this->logger->log(this->logger, CONTROL|LEVEL2, "deleting old SA"); + status = this->public.del_sa(&this->public, dst, spi, protocol); + } + + free(update); + free(response); + return status; +} - if (this->send_message(this, &request, &response) != SUCCESS) +static status_t del_sa( private_kernel_interface_t *this, + host_t *dst, + u_int32_t spi, + protocol_id_t protocol) +{ + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *response; + + memset(&request, 0, sizeof(request)); + status_t status = SUCCESS; + + this->logger->log(this->logger, CONTROL|LEVEL2, "deleting SA"); + + struct nlmsghdr *hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_DELSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id)); + + struct xfrm_usersa_id *sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr); + sa_id->daddr = dst->get_xfrm_addr(dst); + sa_id->spi = spi; + sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + sa_id->family = dst->get_family(dst); + + if (this->send_message(this, hdr, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); return FAILED; } - else if (response->hdr.nlmsg_type != NLMSG_ERROR) + else if (response->nlmsg_type != NLMSG_ERROR) { this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_DELSA not acknowledged"); status = FAILED; } - else if (response->e.error) + else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) { - this->logger->log(this->logger, ERROR|LEVEL1, "netlink request XFRM_MSG_DELSA received error: %s", - strerror(-response->e.error)); + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_DELSA got an error: %s", + strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); status = FAILED; } @@ -488,71 +588,84 @@ static status_t add_policy(private_kernel_interface_t *this, protocol_id_t protocol, u_int32_t reqid) { - netlink_message_t request, *response; + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *response; + + memset(&request, 0, sizeof(request)); status_t status = SUCCESS; - xfrm_data_t *data; - this->logger->log(this->logger, CONTROL|LEVEL1, "Adding %s policy with reqid %d to kernel", - mapping_find(protocol_id_m, protocol), reqid); + this->logger->log(this->logger, CONTROL|LEVEL2, "adding policy"); + + struct nlmsghdr *hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_UPDPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_info)); + + struct xfrm_userpolicy_info *policy = (struct xfrm_userpolicy_info*)NLMSG_DATA(hdr); + + policy->sel.sport = htons(src->get_port(src)); + policy->sel.sport_mask = (policy->sel.sport) ? ~0 : 0; + policy->sel.saddr = src->get_xfrm_addr(src); + policy->sel.prefixlen_s = src_hostbits; - memset(&request, 0, sizeof(request)); - request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - - request.policy.sel.sport = htons(src->get_port(src)); - request.policy.sel.dport = htons(dst->get_port(dst)); - request.policy.sel.sport_mask = (request.policy.sel.sport) ? ~0 : 0; - request.policy.sel.dport_mask = (request.policy.sel.dport) ? ~0 : 0; - request.policy.sel.saddr = src->get_xfrm_addr(src); - request.policy.sel.daddr = dst->get_xfrm_addr(dst); - request.policy.sel.prefixlen_s = src_hostbits; - request.policy.sel.prefixlen_d = dst_hostbits; - request.policy.sel.proto = upper_proto; - request.policy.sel.family = src->get_family(src); - - request.hdr.nlmsg_type = XFRM_MSG_UPDPOLICY; - request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.policy))); - request.policy.dir = direction; - request.policy.priority = SPD_PRIORITY; - request.policy.action = XFRM_POLICY_ALLOW; - request.policy.share = XFRM_SHARE_ANY; + policy->sel.dport = htons(dst->get_port(dst)); + policy->sel.dport_mask = (policy->sel.dport) ? ~0 : 0; + policy->sel.daddr = dst->get_xfrm_addr(dst); + policy->sel.prefixlen_d = dst_hostbits; + + policy->sel.proto = upper_proto; + policy->sel.family = src->get_family(src); + + policy->dir = direction; + policy->priority = SPD_PRIORITY; + policy->action = XFRM_POLICY_ALLOW; + policy->share = XFRM_SHARE_ANY; /* policies currently don't expire */ - request.policy.lft.soft_byte_limit = XFRM_INF; - request.policy.lft.soft_packet_limit = XFRM_INF; - request.policy.lft.hard_byte_limit = XFRM_INF; - request.policy.lft.hard_packet_limit = XFRM_INF; - request.sa.lft.soft_add_expires_seconds = 0; - request.sa.lft.hard_add_expires_seconds = 0; - request.sa.lft.soft_use_expires_seconds = 0; - request.sa.lft.hard_use_expires_seconds = 0; - - data = (xfrm_data_t*)(((u_int8_t*)&request) + request.hdr.nlmsg_len); - data->type = XFRMA_TMPL; - - data->tmpl.reqid = reqid; - data->tmpl.id.proto = protocol == PROTO_AH ? KERNEL_AH : KERNEL_ESP; - data->tmpl.aalgos = data->tmpl.ealgos = data->tmpl.calgos = ~0; - data->tmpl.mode = TRUE; - data->tmpl.saddr = me->get_xfrm_addr(me); - data->tmpl.id.daddr = me->get_xfrm_addr(other); - - data->length = 4 + sizeof(struct xfrm_user_tmpl); - request.hdr.nlmsg_len += data->length; - - if (this->send_message(this, &request, &response) != SUCCESS) + policy->lft.soft_byte_limit = XFRM_INF; + policy->lft.soft_packet_limit = XFRM_INF; + policy->lft.hard_byte_limit = XFRM_INF; + policy->lft.hard_packet_limit = XFRM_INF; + policy->lft.soft_add_expires_seconds = 0; + policy->lft.hard_add_expires_seconds = 0; + policy->lft.soft_use_expires_seconds = 0; + policy->lft.hard_use_expires_seconds = 0; + + struct rtattr *rthdr = (struct rtattr*)(request + hdr->nlmsg_len); + rthdr->rta_type = XFRMA_TMPL; + + rthdr->rta_len = sizeof(struct xfrm_user_tmpl); + rthdr->rta_len = RTA_LENGTH(rthdr->rta_len); + + hdr->nlmsg_len += rthdr->rta_len; + if (hdr->nlmsg_len > sizeof(request)) + { + return FAILED; + } + + struct xfrm_user_tmpl *tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr); + tmpl->reqid = reqid; + tmpl->id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; + tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0; + tmpl->mode = TRUE; + + tmpl->saddr = me->get_xfrm_addr(me); + tmpl->id.daddr = other->get_xfrm_addr(other); + + if (this->send_message(this, hdr, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); return FAILED; } - else if (response->hdr.nlmsg_type != NLMSG_ERROR) + else if (response->nlmsg_type != NLMSG_ERROR) { this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWPOLICY not acknowledged"); status = FAILED; } - else if (response->e.error) + else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) { - this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWPOLICY received error: %s", - strerror(-response->e.error)); + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_NEWPOLICY got an error: %s", + strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); status = FAILED; } @@ -569,44 +682,49 @@ static status_t del_policy(private_kernel_interface_t *this, u_int8_t src_hostbits, u_int8_t dst_hostbits, int direction, int upper_proto) { - netlink_message_t request, *response; - status_t status = SUCCESS; - - this->logger->log(this->logger, CONTROL|LEVEL1, "Removing policy from kernel"); + unsigned char request[BUFFER_SIZE]; + struct nlmsghdr *response; memset(&request, 0, sizeof(request)); - request.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - - request.policy_id.sel.sport = htons(src->get_port(src)); - request.policy_id.sel.dport = htons(dst->get_port(dst)); - request.policy_id.sel.sport_mask = (request.policy.sel.sport) ? ~0 : 0; - request.policy_id.sel.dport_mask = (request.policy.sel.dport) ? ~0 : 0; - request.policy_id.sel.saddr = src->get_xfrm_addr(src); - request.policy_id.sel.daddr = dst->get_xfrm_addr(dst); - request.policy_id.sel.prefixlen_s = src_hostbits; - request.policy_id.sel.prefixlen_d = dst_hostbits; - request.policy_id.sel.proto = upper_proto; - request.policy_id.sel.family = src->get_family(src); + status_t status = SUCCESS; - request.policy_id.dir = direction; + this->logger->log(this->logger, CONTROL|LEVEL2, "deleting policy"); + + struct nlmsghdr *hdr = (struct nlmsghdr*)request; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + hdr->nlmsg_type = XFRM_MSG_DELPOLICY; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id)); - request.hdr.nlmsg_type = XFRM_MSG_DELPOLICY; - request.hdr.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(request.policy_id))); + struct xfrm_userpolicy_id *policy_id = (struct xfrm_userpolicy_id*)NLMSG_DATA(hdr); + policy_id->sel.sport = htons(src->get_port(src)); + policy_id->sel.sport_mask = (policy_id->sel.sport) ? ~0 : 0; + policy_id->sel.saddr = src->get_xfrm_addr(src); + policy_id->sel.prefixlen_s = src_hostbits; - if (this->send_message(this, &request, &response) != SUCCESS) + policy_id->sel.dport = htons(dst->get_port(dst)); + policy_id->sel.dport_mask = (policy_id->sel.dport) ? ~0 : 0; + policy_id->sel.daddr = dst->get_xfrm_addr(dst); + policy_id->sel.prefixlen_d = dst_hostbits; + + policy_id->sel.proto = upper_proto; + policy_id->sel.family = src->get_family(src); + + policy_id->dir = direction; + + if (this->send_message(this, hdr, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); return FAILED; } - else if (response->hdr.nlmsg_type != NLMSG_ERROR) + else if (response->nlmsg_type != NLMSG_ERROR) { this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_DELPOLICY not acknowledged"); status = FAILED; } - else if (response->e.error) + else if (((struct nlmsgerr*)NLMSG_DATA(response))->error) { - this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_DELPOLICY received error: %s", - strerror(-response->e.error)); + this->logger->log(this->logger, ERROR, "netlink request XFRM_MSG_DELPOLICY got an error: %s", + strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error)); status = FAILED; } @@ -617,26 +735,26 @@ static status_t del_policy(private_kernel_interface_t *this, /** * Implementation of private_kernel_interface_t.send_message. */ -static status_t send_message(private_kernel_interface_t *this, netlink_message_t *request, netlink_message_t **response) +static status_t send_message(private_kernel_interface_t *this, struct nlmsghdr *request, struct nlmsghdr **response) { size_t length; struct sockaddr_nl addr; - request->hdr.nlmsg_seq = ++this->seq; - request->hdr.nlmsg_pid = this->pid; + request->nlmsg_seq = ++this->seq; + request->nlmsg_pid = 0; memset(&addr, 0, sizeof(struct sockaddr_nl)); addr.nl_family = AF_NETLINK; addr.nl_pid = 0; addr.nl_groups = 0; - length = sendto(this->socket,(void *)request, request->hdr.nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); + length = sendto(this->socket,(void *)request, request->nlmsg_len, 0, (struct sockaddr *)&addr, sizeof(addr)); if (length < 0) { return FAILED; } - else if (length != request->hdr.nlmsg_len) + else if (length != request->nlmsg_len) { return FAILED; } @@ -651,12 +769,13 @@ static status_t send_message(private_kernel_interface_t *this, netlink_message_t iterator = this->responses->create_iterator(this->responses, TRUE); while (iterator->has_next(iterator)) { - netlink_message_t *listed_response; + struct nlmsghdr *listed_response; iterator->current(iterator, (void**)&listed_response); - if (listed_response->hdr.nlmsg_seq == request->hdr.nlmsg_seq) + if (listed_response->nlmsg_seq == request->nlmsg_seq) { /* matches our request, this is the reply */ *response = listed_response; + iterator->remove(iterator); found = TRUE; break; } @@ -683,7 +802,8 @@ static void receive_messages(private_kernel_interface_t *this) { while(TRUE) { - netlink_message_t response, *listed_response; + unsigned char response[BUFFER_SIZE]; + struct nlmsghdr *hdr, *listed_response; while (TRUE) { struct sockaddr_nl addr; @@ -692,7 +812,6 @@ static void receive_messages(private_kernel_interface_t *this) addr_length = sizeof(addr); - response.hdr.nlmsg_type = XFRM_MSG_NEWSA; length = recvfrom(this->socket, &response, sizeof(response), 0, (struct sockaddr*)&addr, &addr_length); if (length < 0) { @@ -703,7 +822,7 @@ static void receive_messages(private_kernel_interface_t *this) } charon->kill(charon, "receiving from netlink socket failed"); } - if (!NLMSG_OK(&response.hdr, length)) + if (!NLMSG_OK((struct nlmsghdr *)response, length)) { /* bad netlink message */ continue; @@ -719,44 +838,52 @@ static void receive_messages(private_kernel_interface_t *this) /* we handle ACQUIRE and EXPIRE messages directly */ - if (response.hdr.nlmsg_type == XFRM_MSG_ACQUIRE) + hdr = (struct nlmsghdr*)response; + if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE) { this->logger->log(this->logger, CONTROL, "Received a XFRM_MSG_ACQUIRE. Ignored"); } - else if (response.hdr.nlmsg_type == XFRM_MSG_EXPIRE) + else if (hdr->nlmsg_type == XFRM_MSG_EXPIRE) { job_t *job; + struct xfrm_user_expire *expire; this->logger->log(this->logger, CONTROL|LEVEL1, "Received a XFRM_MSG_EXPIRE"); - if (response.expire.hard) + expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr); + this->logger->log(this->logger, CONTROL|LEVEL0, + "creating %s job for CHILD_SA with reqid %d", + expire->hard ? "delete" : "rekey", + expire->state.reqid); + if (expire->hard) { this->logger->log(this->logger, CONTROL|LEVEL0, "creating delete job for CHILD_SA with reqid %d", - response.expire.state.reqid); + expire->state.reqid); job = (job_t*)delete_child_sa_job_create( - response.expire.state.reqid); + expire->state.reqid); } else { this->logger->log(this->logger, CONTROL|LEVEL0, "creating rekey job for CHILD_SA with reqid %d", - response.expire.state.reqid); + expire->state.reqid); job = (job_t*)rekey_child_sa_job_create( - response.expire.state.reqid); + expire->state.reqid); } charon->job_queue->add(charon->job_queue, job); } - /* NLMSG_ERROR is send back for acknowledge (or on error), an - * XFRM_MSG_NEWSA is returned when we alloc spis. + /* NLMSG_ERROR is sent back for acknowledge (or on error), an + * XFRM_MSG_NEWSA is returned when we alloc spis and when + * updating SAs. * list these responses for the sender */ - else if (response.hdr.nlmsg_type == NLMSG_ERROR || - response.hdr.nlmsg_type == XFRM_MSG_NEWSA) + else if (hdr->nlmsg_type == NLMSG_ERROR || + hdr->nlmsg_type == XFRM_MSG_NEWSA) { /* add response to queue */ - listed_response = malloc(sizeof(response)); - memcpy(listed_response, &response, sizeof(response)); + listed_response = malloc(hdr->nlmsg_len); + memcpy(listed_response, &response, hdr->nlmsg_len); pthread_mutex_lock(&(this->mutex)); this->responses->insert_last(this->responses, (void*)listed_response); @@ -792,8 +919,9 @@ kernel_interface_t *kernel_interface_create() /* public functions */ this->public.get_spi = (status_t(*)(kernel_interface_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi; - this->public.add_sa = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,algorithm_t*,algorithm_t*,prf_plus_t*,bool))add_sa; + this->public.add_sa = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,algorithm_t*,algorithm_t*,prf_plus_t*,natt_conf_t*,bool))add_sa; this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*, host_t*,host_t*,host_t*,u_int8_t,u_int8_t,int,int,protocol_id_t,u_int32_t))add_policy; + this->public.update_sa_hosts = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,int,int,u_int32_t,protocol_id_t))update_sa_hosts; this->public.del_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t))del_sa; this->public.del_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,host_t*,host_t*,u_int8_t,u_int8_t,int,int))del_policy; @@ -817,6 +945,7 @@ kernel_interface_t *kernel_interface_create() free(this); charon->kill(charon, "Unable to create netlink socket"); } + /* bind the socket and reqister for ACQUIRE & EXPIRE */ addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); diff --git a/src/charon/threads/kernel_interface.h b/src/charon/threads/kernel_interface.h index b70f9b6e8..9aa2d941a 100644 --- a/src/charon/threads/kernel_interface.h +++ b/src/charon/threads/kernel_interface.h @@ -6,6 +6,7 @@ */ /* + * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger * Copyright (C) 2005 Jan Hutter, Martin Willi * Hochschule fuer Technik Rapperswil * @@ -29,6 +30,15 @@ #include <crypto/prf_plus.h> #include <encoding/payloads/proposal_substructure.h> +typedef struct natt_conf_t natt_conf_t; + +/** + * @brief Configuration for NAT-T + */ +struct natt_conf_t { + u_int16_t sport, dport; +}; + typedef struct kernel_interface_t kernel_interface_t; /** @@ -47,6 +57,10 @@ struct kernel_interface_t { /** * @brief Get a SPI from the kernel. + * + * @warning get_spi() implicitely creates an SA with + * the allocated SPI, therefore the replace flag + * in add_sa() must be set when installing this SA. * * @param this calling object * @param src source address of SA @@ -86,6 +100,7 @@ struct kernel_interface_t { * @param enc_alg Algorithm to use for encryption (ESP only) * @param int_alg Algorithm to use for integrity protection * @param prf_plus PRF to derive keys + * @param natt NAT-T Configuration * @param replace Should an already installed SA be updated? * @return * - SUCCESS @@ -101,8 +116,35 @@ struct kernel_interface_t { algorithm_t *enc_alg, algorithm_t *int_alg, prf_plus_t *prf_plus, + natt_conf_t *natt, bool replace); /** + * @brief Update the hosts on an installed SA. Encapsulation ports are also updated. + * + * @note We cannot directly update the destination address as the kernel requires the spi, + * the protocol AND the destination address (and family) to identify SAs. Therefore if the + * destination address changed we create a new SA and delete the old one. + * + * @param this calling object + * @param src source address for this SA + * @param dst destination address for this SA + * @param new_src new source address for this SA + * @param new_dst new destination address for this SA + * @param src_changes changes in src + * @param dst_changes changes in dst + * @param spi SPI allocated by us or remote peer + * @param protocol protocol for this SA (ESP/AH) + * @return + * - SUCCESS + * - FAILED if kernel comm failed + */ + status_t (*update_sa_hosts)(kernel_interface_t *this, + host_t *src, host_t *dst, + host_t *new_src, host_t *new_dst, + int src_changes, int dst_changes, + u_int32_t spi, protocol_id_t protocol); + + /** * @brief Delete a previusly installed SA from the SAD. * * @param this calling object diff --git a/src/charon/threads/stroke_interface.c b/src/charon/threads/stroke_interface.c index 47acb242e..0bb97ca02 100755 --- a/src/charon/threads/stroke_interface.c +++ b/src/charon/threads/stroke_interface.c @@ -203,7 +203,7 @@ static void stroke_add_conn(private_stroke_t *this, stroke_msg_t *msg) return; } - if (charon->socket->is_listening_on(charon->socket, other_host)) + if (charon->interfaces->is_local_address(charon->interfaces, other_host)) { stroke_end_t tmp_end; host_t *tmp_host; @@ -218,7 +218,7 @@ static void stroke_add_conn(private_stroke_t *this, stroke_msg_t *msg) msg->add_conn.me = msg->add_conn.other; msg->add_conn.other = tmp_end; } - else if (!charon->socket->is_listening_on(charon->socket, my_host)) + else if (!charon->interfaces->is_local_address(charon->interfaces, my_host)) { this->stroke_logger->log(this->stroke_logger, ERROR, "left nor right host is our side, aborting"); diff --git a/src/charon/threads/thread_pool.c b/src/charon/threads/thread_pool.c index 83771ceb5..4e33e8894 100644 --- a/src/charon/threads/thread_pool.c +++ b/src/charon/threads/thread_pool.c @@ -29,12 +29,6 @@ #include <daemon.h> #include <queues/job_queue.h> -#include <queues/jobs/delete_half_open_ike_sa_job.h> -#include <queues/jobs/delete_established_ike_sa_job.h> -#include <queues/jobs/incoming_packet_job.h> -#include <queues/jobs/initiate_ike_sa_job.h> -#include <queues/jobs/retransmit_request_job.h> -#include <encoding/payloads/notify_payload.h> #include <utils/logger.h> @@ -145,13 +139,13 @@ thread_pool_t *thread_pool_create(size_t pool_size) this->public.destroy = (void(*)(thread_pool_t*))destroy; this->public.get_pool_size = (size_t(*)(thread_pool_t*))get_pool_size; - /* initialze memeber */ + /* initialize member */ this->pool_size = pool_size; this->threads = malloc(sizeof(pthread_t) * pool_size); this->pool_logger = logger_manager->get_logger(logger_manager, THREAD_POOL); this->worker_logger = logger_manager->get_logger(logger_manager, WORKER); - /* try to create as many threads as possible, up tu pool_size */ + /* try to create as many threads as possible, up to pool_size */ for (current = 0; current < pool_size; current++) { if (pthread_create(&(this->threads[current]), NULL, (void*(*)(void*))process_jobs, this) == 0) |