diff options
26 files changed, 1011 insertions, 142 deletions
diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am index 2a8735f53..c8bd339fc 100644 --- a/src/charon/Makefile.am +++ b/src/charon/Makefile.am @@ -46,6 +46,8 @@ queues/jobs/send_keepalive_job.c queues/jobs/send_keepalive_job.h \ queues/jobs/rekey_child_sa_job.c queues/jobs/rekey_child_sa_job.h \ queues/jobs/delete_child_sa_job.c queues/jobs/delete_child_sa_job.h \ queues/jobs/send_dpd_job.c queues/jobs/send_dpd_job.h \ +queues/jobs/route_job.c queues/jobs/route_job.h \ +queues/jobs/acquire_job.c queues/jobs/acquire_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 \ diff --git a/src/charon/config/traffic_selector.c b/src/charon/config/traffic_selector.c index 3a9480f77..3d53d9072 100644 --- a/src/charon/config/traffic_selector.c +++ b/src/charon/config/traffic_selector.c @@ -248,6 +248,29 @@ static traffic_selector_t *get_subset(private_traffic_selector_t *this, private_ } /** + * implements traffic_selector_t.equals + */ +static bool equals(private_traffic_selector_t *this, private_traffic_selector_t *other) +{ + if (this->type != other->type) + { + return FALSE; + } + if (this->type == TS_IPV4_ADDR_RANGE) + { + if (this->from_addr_ipv4 == other->from_addr_ipv4 && + this->to_addr_ipv4 == other->to_addr_ipv4 && + this->from_port == other->from_port && + this->to_port == other->to_port && + this->protocol == other->protocol) + { + return TRUE; + } + } + return FALSE; +} + +/** * Implements traffic_selector_t.get_from_address. */ static chunk_t get_from_address(private_traffic_selector_t *this) @@ -518,6 +541,7 @@ static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, ts /* public functions */ this->public.get_subset = (traffic_selector_t*(*)(traffic_selector_t*,traffic_selector_t*))get_subset; + this->public.equals = (bool(*)(traffic_selector_t*,traffic_selector_t*))equals; this->public.get_string = (char*(*)(traffic_selector_t*))get_string; this->public.get_from_address = (chunk_t(*)(traffic_selector_t*))get_from_address; this->public.get_to_address = (chunk_t(*)(traffic_selector_t*))get_to_address; diff --git a/src/charon/config/traffic_selector.h b/src/charon/config/traffic_selector.h index 420f97d0f..90437f92f 100644 --- a/src/charon/config/traffic_selector.h +++ b/src/charon/config/traffic_selector.h @@ -184,6 +184,15 @@ struct traffic_selector_t { char* (*get_string) (traffic_selector_t *this); /** + * @brief Compare two traffic selectors for equality. + * + * @param this first to compare + * @param other second to compare with first + * @return pointer to a string. + */ + bool (*equals) (traffic_selector_t *this, traffic_selector_t *other); + + /** * @brief Destroys the ts object * * @param this calling object diff --git a/src/charon/queues/jobs/acquire_job.c b/src/charon/queues/jobs/acquire_job.c new file mode 100644 index 000000000..89eccef4a --- /dev/null +++ b/src/charon/queues/jobs/acquire_job.c @@ -0,0 +1,104 @@ +/** + * @file acquire_job.c + * + * @brief Implementation of acquire_job_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * 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 "acquire_job.h" + +#include <daemon.h> + + +typedef struct private_acquire_job_t private_acquire_job_t; + +/** + * Private data of an acquire_job_t object. + */ +struct private_acquire_job_t { + /** + * Public acquire_job_t interface. + */ + acquire_job_t public; + + /** + * reqid of the child to rekey + */ + u_int32_t reqid; + + /** + * Logger ref + */ + logger_t *logger; +}; + +/** + * Implementation of job_t.get_type. + */ +static job_type_t get_type(private_acquire_job_t *this) +{ + return ACQUIRE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_acquire_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, + this->reqid); + if (ike_sa == NULL) + { + this->logger->log(this->logger, ERROR|LEVEL1, + "CHILD_SA not found for acquiring"); + return DESTROY_ME; + } + ike_sa->acquire(ike_sa, this->reqid); + + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implementation of job_t.destroy. + */ +static void destroy(private_acquire_job_t *this) +{ + free(this); +} + +/* + * Described in header + */ +acquire_job_t *acquire_job_create(u_int32_t reqid) +{ + private_acquire_job_t *this = malloc_thing(private_acquire_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*)(job_t*)) destroy; + + /* private variables */ + this->reqid = reqid; + this->logger = logger_manager->get_logger(logger_manager, WORKER); + + return &(this->public); +} diff --git a/src/charon/queues/jobs/acquire_job.h b/src/charon/queues/jobs/acquire_job.h new file mode 100644 index 000000000..d607c91f8 --- /dev/null +++ b/src/charon/queues/jobs/acquire_job.h @@ -0,0 +1,61 @@ +/** + * @file acquire_job.h + * + * @brief Interface of acquire_job_t. + * + */ + +/* + * Copyright (C) 2006 Martin Willi + * 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 ACQUIRE_JOB_H_ +#define ACQUIRE_JOB_H_ + +#include <types.h> +#include <queues/jobs/job.h> + + +typedef struct acquire_job_t acquire_job_t; + +/** + * @brief Class representing an ACQUIRE Job. + * + * This job initiates a CHILD SA on kernel request. + * + * @b Constructors: + * - acquire_job_create() + * + * @ingroup jobs + */ +struct acquire_job_t { + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type ACQUIRE. + * + * We use the reqid to find the routed CHILD_SA. + * + * @param reqid reqid of the CHILD_SA to acquire + * @return acquire_job_t object + * + * @ingroup jobs + */ +acquire_job_t *acquire_job_create(u_int32_t reqid); + +#endif /* REKEY_CHILD_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/delete_child_sa_job.c b/src/charon/queues/jobs/delete_child_sa_job.c index 3cf7060c9..dd39572de 100644 --- a/src/charon/queues/jobs/delete_child_sa_job.c +++ b/src/charon/queues/jobs/delete_child_sa_job.c @@ -37,6 +37,11 @@ struct private_delete_child_sa_job_t { delete_child_sa_job_t public; /** + * reqid of the CHILD_SA + */ + u_int32_t reqid; + + /** * protocol of the CHILD_SA (ESP/AH) */ protocol_id_t protocol; @@ -66,12 +71,10 @@ static job_type_t get_type(private_delete_child_sa_job_t *this) static status_t execute(private_delete_child_sa_job_t *this) { ike_sa_t *ike_sa; - status_t status; - status = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, - this->protocol, this->spi, - &ike_sa); - if (status != SUCCESS) + ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, + this->reqid); + if (ike_sa == NULL) { this->logger->log(this->logger, ERROR|LEVEL1, "CHILD_SA not found for delete"); @@ -94,7 +97,9 @@ static void destroy(private_delete_child_sa_job_t *this) /* * Described in header */ -delete_child_sa_job_t *delete_child_sa_job_create(protocol_id_t protocol, u_int32_t spi) +delete_child_sa_job_t *delete_child_sa_job_create(u_int32_t reqid, + protocol_id_t protocol, + u_int32_t spi) { private_delete_child_sa_job_t *this = malloc_thing(private_delete_child_sa_job_t); @@ -104,6 +109,7 @@ delete_child_sa_job_t *delete_child_sa_job_create(protocol_id_t protocol, u_int3 this->public.job_interface.destroy = (void (*)(job_t*)) destroy; /* private variables */ + this->reqid = reqid; this->protocol = protocol; this->spi = spi; this->logger = logger_manager->get_logger(logger_manager, WORKER); diff --git a/src/charon/queues/jobs/delete_child_sa_job.h b/src/charon/queues/jobs/delete_child_sa_job.h index 2e16ef832..fb87440c4 100644 --- a/src/charon/queues/jobs/delete_child_sa_job.h +++ b/src/charon/queues/jobs/delete_child_sa_job.h @@ -51,15 +51,18 @@ struct delete_child_sa_job_t { /** * @brief Creates a job of type DELETE_CHILD_SA. * - * The CHILD_SA is identified by its protocol (AH/ESP) and its + * The CHILD_SA is identified by its reqid, protocol (AH/ESP) and its * inbound SPI. * + * @param reqid reqid of the CHILD_SA, as used in kernel * @param protocol protocol of the CHILD_SA * @param spi security parameter index of the CHILD_SA * @return delete_child_sa_job_t object * * @ingroup jobs */ -delete_child_sa_job_t *delete_child_sa_job_create(protocol_id_t protocol, u_int32_t spi); +delete_child_sa_job_t *delete_child_sa_job_create(u_int32_t reqid, + protocol_id_t protocol, + u_int32_t spi); #endif /* DELETE_CHILD_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/job.h b/src/charon/queues/jobs/job.h index 6063a2108..86af1a318 100644 --- a/src/charon/queues/jobs/job.h +++ b/src/charon/queues/jobs/job.h @@ -58,6 +58,20 @@ enum job_type_t { INITIATE, /** + * Install SPD entries. + * + * Job is implemented in class route_job_t + */ + ROUTE, + + /** + * React on a acquire message from the kernel (e.g. setup CHILD_SA) + * + * Job is implemented in class acquire_job_t + */ + ACQUIRE, + + /** * Delete an ike sa which is still not established. * * Job is implemented in class delete_half_open_ike_sa_job_t diff --git a/src/charon/queues/jobs/rekey_child_sa_job.c b/src/charon/queues/jobs/rekey_child_sa_job.c index a2b5b095f..e75d1911c 100644 --- a/src/charon/queues/jobs/rekey_child_sa_job.c +++ b/src/charon/queues/jobs/rekey_child_sa_job.c @@ -37,6 +37,11 @@ struct private_rekey_child_sa_job_t { rekey_child_sa_job_t public; /** + * reqid of the child to rekey + */ + u_int32_t reqid; + + /** * protocol of the CHILD_SA (ESP/AH) */ protocol_id_t protocol; @@ -66,12 +71,10 @@ static job_type_t get_type(private_rekey_child_sa_job_t *this) static status_t execute(private_rekey_child_sa_job_t *this) { ike_sa_t *ike_sa; - status_t status; - status = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, - this->protocol, this->spi, - &ike_sa); - if (status != SUCCESS) + ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, + this->reqid); + if (ike_sa == NULL) { this->logger->log(this->logger, ERROR|LEVEL1, "CHILD_SA not found for rekeying"); @@ -94,7 +97,9 @@ static void destroy(private_rekey_child_sa_job_t *this) /* * Described in header */ -rekey_child_sa_job_t *rekey_child_sa_job_create(protocol_id_t protocol, u_int32_t spi) +rekey_child_sa_job_t *rekey_child_sa_job_create(u_int32_t reqid, + protocol_id_t protocol, + u_int32_t spi) { private_rekey_child_sa_job_t *this = malloc_thing(private_rekey_child_sa_job_t); @@ -104,6 +109,7 @@ rekey_child_sa_job_t *rekey_child_sa_job_create(protocol_id_t protocol, u_int32_ this->public.job_interface.destroy = (void (*)(job_t*)) destroy; /* private variables */ + this->reqid = reqid; this->protocol = protocol; this->spi = spi; this->logger = logger_manager->get_logger(logger_manager, WORKER); diff --git a/src/charon/queues/jobs/rekey_child_sa_job.h b/src/charon/queues/jobs/rekey_child_sa_job.h index 87be00c0f..72d75d1e7 100644 --- a/src/charon/queues/jobs/rekey_child_sa_job.h +++ b/src/charon/queues/jobs/rekey_child_sa_job.h @@ -54,12 +54,13 @@ struct rekey_child_sa_job_t { * The CHILD_SA is identified by its protocol (AH/ESP) and its * inbound SPI. * + * @param reqid reqid of the CHILD_SA to rekey * @param protocol protocol of the CHILD_SA * @param spi security parameter index of the CHILD_SA * @return rekey_child_sa_job_t object * * @ingroup jobs */ -rekey_child_sa_job_t *rekey_child_sa_job_create(protocol_id_t protocol, u_int32_t spi); +rekey_child_sa_job_t *rekey_child_sa_job_create(u_int32_t reqid, protocol_id_t protocol, u_int32_t spi); #endif /* REKEY_CHILD_SA_JOB_H_ */ diff --git a/src/charon/queues/jobs/route_job.c b/src/charon/queues/jobs/route_job.c new file mode 100644 index 000000000..b6a862691 --- /dev/null +++ b/src/charon/queues/jobs/route_job.c @@ -0,0 +1,133 @@ +/** + * @file route_job.c + * + * @brief Implementation of route_job_t. + * + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * 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 "route_job.h" + +#include <daemon.h> + +typedef struct private_route_job_t private_route_job_t; + +/** + * Private data of an route_job_t Object + */ +struct private_route_job_t { + /** + * public route_job_t interface + */ + route_job_t public; + + /** + * associated connection to route + */ + connection_t *connection; + + /** + * associated policy to route + */ + policy_t *policy; + + /** + * route or unroute? + */ + bool route; + + /** + * logger + */ + logger_t *logger; +}; + +/** + * Implements route_job_t.get_type. + */ +static job_type_t get_type(private_route_job_t *this) +{ + return ROUTE; +} + +/** + * Implementation of job_t.execute. + */ +static status_t execute(private_route_job_t *this) +{ + ike_sa_t *ike_sa; + + this->logger->log(this->logger, CONTROL|LEVEL2, "getting an IKE SA"); + ike_sa = charon->ike_sa_manager->checkout_by_ids(charon->ike_sa_manager, + this->policy->get_my_id(this->policy), + this->policy->get_other_id(this->policy)); + + if (this->route) + { + if (ike_sa->route(ike_sa, this->connection, this->policy) != SUCCESS) + { + this->logger->log(this->logger, ERROR, + "routing failed"); + } + } + else + { + if (ike_sa->unroute(ike_sa, this->policy) == DESTROY_ME) + { + this->logger->log(this->logger, ERROR, + "removing IKE_SA, as last routed CHILD_SA unrouted"); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; + } + } + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return DESTROY_ME; +} + +/** + * Implements job_t.destroy. + */ +static void destroy(private_route_job_t *this) +{ + this->connection->destroy(this->connection); + this->policy->destroy(this->policy); + free(this); +} + +/* + * Described in header + */ +route_job_t *route_job_create(connection_t *connection, policy_t *policy, bool route) +{ + private_route_job_t *this = malloc_thing(private_route_job_t); + + /* interface functions */ + this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type; + this->public.job_interface.execute = (status_t (*) (job_t *)) execute; + this->public.job_interface.destroy = (void (*) (job_t *)) destroy; + + /* private variables */ + this->connection = connection; + this->policy = policy; + this->route = route; + this->logger = logger_manager->get_logger(logger_manager, WORKER); + + return &this->public; +} diff --git a/src/charon/queues/jobs/route_job.h b/src/charon/queues/jobs/route_job.h new file mode 100644 index 000000000..df2648ae2 --- /dev/null +++ b/src/charon/queues/jobs/route_job.h @@ -0,0 +1,60 @@ +/** + * @file route_job.h + * + * @brief Interface of route_job_t. + */ + +/* + * Copyright (C) 2005-2006 Martin Willi + * 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 ROUTE_JOB_H_ +#define ROUTE_JOB_H_ + +#include <types.h> +#include <queues/jobs/job.h> +#include <config/policies/policy.h> +#include <config/connections/connection.h> + + +typedef struct route_job_t route_job_t; + +/** + * @brief Class representing an ROUTE Job. + * + * @b Constructors: + * - route_job_create() + * + * @ingroup jobs + */ +struct route_job_t { + /** + * implements job_t interface + */ + job_t job_interface; +}; + +/** + * @brief Creates a job of type ROUTE. + * + * @param connection connection used for routing + * @param policy policy to set up + * @param route TRUE to route, FALSE to unroute + * @return route_job_t object + * + * @ingroup jobs + */ +route_job_t *route_job_create(connection_t *connection, policy_t *policy, bool route); + +#endif /*ROUTE_JOB_H_*/ diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c index 580599362..d73542163 100644 --- a/src/charon/sa/child_sa.c +++ b/src/charon/sa/child_sa.c @@ -33,6 +33,7 @@ mapping_t child_sa_state_m[] = { {CHILD_CREATED, "CREATED"}, {CHILD_INSTALLED, "INSTALLED"}, + {CHILD_ROUTED, "ROUTED"}, {CHILD_REKEYING, "REKEYING"}, {CHILD_DELETING, "DELETING"}, {MAPPING_END, NULL} @@ -502,6 +503,13 @@ static status_t add_policies(private_child_sa_t *this, linked_list_t *my_ts_list } my_iter->destroy(my_iter); other_iter->destroy(other_iter); + + /* switch to routed state if no SAD entry set up */ + if (this->state == CHILD_CREATED) + { + this->state = CHILD_ROUTED; + } + return SUCCESS; } @@ -597,65 +605,74 @@ static void log_status(private_child_sa_t *this, logger_t *logger, char* name) } now = (u_int32_t)time(NULL); - /* query SA times */ - status = charon->kernel_interface->query_sa(charon->kernel_interface, - this->me.addr, this->me.spi, this->protocol, &use_in); - if (status == SUCCESS && use_in) - { - snprintf(use_in_str, sizeof(use_in_str), "%ds", now - use_in); - } - status = charon->kernel_interface->query_sa(charon->kernel_interface, - this->other.addr, this->other.spi, this->protocol, &use_out); - if (status == SUCCESS && use_out) - { - snprintf(use_out_str, sizeof(use_out_str), "%ds", now - use_out); - } - - /* calculate rekey times */ - if (this->soft_lifetime) + if (this->state == CHILD_INSTALLED) { - rekeying = this->soft_lifetime - (now - this->install_time); - snprintf(rekey_str, sizeof(rekey_str), "%ds", (int)rekeying); - } - - /* algorithms used */ - if (this->protocol == PROTO_ESP) - { - if (this->encryption.key_size) + /* query SA times */ + status = charon->kernel_interface->query_sa(charon->kernel_interface, + this->me.addr, this->me.spi, this->protocol, &use_in); + if (status == SUCCESS && use_in) { - snprintf(enc_str, sizeof(enc_str), "%s-%d,", - mapping_find(encryption_algorithm_m, this->encryption.algorithm), - this->encryption.key_size); + snprintf(use_in_str, sizeof(use_in_str), "%ds", now - use_in); + } + status = charon->kernel_interface->query_sa(charon->kernel_interface, + this->other.addr, this->other.spi, this->protocol, &use_out); + if (status == SUCCESS && use_out) + { + snprintf(use_out_str, sizeof(use_out_str), "%ds", now - use_out); + } + + /* calculate rekey times */ + if (this->soft_lifetime) + { + rekeying = this->soft_lifetime - (now - this->install_time); + snprintf(rekey_str, sizeof(rekey_str), "%ds", (int)rekeying); + } + + /* algorithms used */ + if (this->protocol == PROTO_ESP) + { + if (this->encryption.key_size) + { + snprintf(enc_str, sizeof(enc_str), "%s-%d,", + mapping_find(encryption_algorithm_m, this->encryption.algorithm), + this->encryption.key_size); + } + else + { + snprintf(enc_str, sizeof(enc_str), "%s,", + mapping_find(encryption_algorithm_m, this->encryption.algorithm)); + } + } + if (this->integrity.key_size) + { + snprintf(int_str, sizeof(int_str), "%s-%d", + mapping_find(integrity_algorithm_m, this->integrity.algorithm), + this->integrity.key_size); } else { - snprintf(enc_str, sizeof(enc_str), "%s,", - mapping_find(encryption_algorithm_m, this->encryption.algorithm)); + snprintf(int_str, sizeof(int_str), "%s", + mapping_find(integrity_algorithm_m, this->integrity.algorithm)); } - } - if (this->integrity.key_size) - { - snprintf(int_str, sizeof(int_str), "%s-%d", - mapping_find(integrity_algorithm_m, this->integrity.algorithm), - this->integrity.key_size); + + logger->log(logger, CONTROL|LEVEL1, + " \"%s\": state: %s, reqid: %d, ", + name, mapping_find(child_sa_state_m, this->state), this->reqid); + logger->log(logger, CONTROL|LEVEL1, + " \"%s\": %s (%s%s), SPIs (in/out): 0x%x/0x%x", + name, this->protocol == PROTO_ESP ? "ESP" : "AH", + enc_str, int_str, + htonl(this->me.spi), htonl(this->other.spi)); + logger->log(logger, CONTROL|LEVEL1, + " \"%s\": rekeying: %s, key age (in/out): %s/%s", + name, rekey_str, use_in_str, use_out_str); } else { - snprintf(int_str, sizeof(int_str), "%s", - mapping_find(integrity_algorithm_m, this->integrity.algorithm)); - } - - logger->log(logger, CONTROL|LEVEL1, - " \"%s\": %s (%s%s), SPIs (in/out): 0x%x/0x%x, reqid: %d", - name, - this->protocol == PROTO_ESP ? "ESP" : "AH", - enc_str, int_str, - htonl(this->me.spi), htonl(this->other.spi), - this->reqid); - logger->log(logger, CONTROL|LEVEL1, - " \"%s\": state: %s, rekeying: %s, key age (in/out): %s/%s", - name, mapping_find(child_sa_state_m, this->state), - rekey_str, use_in_str, use_out_str); + logger->log(logger, CONTROL|LEVEL1, " \"%s\": state: %s, reqid: %d", + name, mapping_find(child_sa_state_m, this->state), + this->reqid); + } iterator = this->policies->create_iterator(this->policies, TRUE); while (iterator->has_next(iterator)) diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h index 2a74a2a44..68015cf07 100644 --- a/src/charon/sa/child_sa.h +++ b/src/charon/sa/child_sa.h @@ -49,6 +49,11 @@ enum child_sa_state_t { CHILD_CREATED, /** + * Installed SPD, but no SAD entries + */ + CHILD_ROUTED, + + /** * Installed an in-use CHILD_SA */ CHILD_INSTALLED, diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c index bfffdad2f..c3011ecba 100644 --- a/src/charon/sa/ike_sa.c +++ b/src/charon/sa/ike_sa.c @@ -329,10 +329,13 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other) child_sa_t *child_sa = NULL; host_diff_t my_diff, other_diff; - if (this->my_host == NULL || this->other_host == NULL) + if (this->my_host->is_anyaddr(this->my_host) || + this->other_host->is_anyaddr(this->other_host)) { /* on first received message */ + this->my_host->destroy(this->my_host); this->my_host = me->clone(me); + this->other_host->destroy(this->other_host); this->other_host = other->clone(other); return; } @@ -792,9 +795,14 @@ static status_t initiate(private_ike_sa_t *this, */ ike_sa_init_t *ike_sa_init; + this->logger->log(this->logger, CONTROL, + "initiating IKE_SA"); + set_name(this, connection->get_name(connection)); + DESTROY_IF(this->my_host); this->my_host = connection->get_my_host(connection); this->my_host = this->my_host->clone(this->my_host); + DESTROY_IF(this->other_host); this->other_host = connection->get_other_host(connection); this->other_host = this->other_host->clone(this->other_host); @@ -806,6 +814,8 @@ static status_t initiate(private_ike_sa_t *this, case IKE_DELETING: { /* if we are in DELETING, we deny set up of a policy. */ + this->logger->log(this->logger, CONTROL, + "creating CHILD_SA discarded, as IKE_SA is deleting"); policy->destroy(policy); connection->destroy(connection); return FAILED; @@ -820,6 +830,9 @@ static status_t initiate(private_ike_sa_t *this, */ create_child_sa_t *create_child; + this->logger->log(this->logger, CONTROL, + "initiating CHILD_SA"); + connection->destroy(connection); create_child = create_child_sa_create(&this->public); create_child->set_policy(create_child, policy); @@ -834,33 +847,275 @@ static status_t initiate(private_ike_sa_t *this, */ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid) { - /* - get TS from child with reqid - * - get a policy from TS - * - get connection from policy - */ + connection_t *connection; + policy_t *policy; + iterator_t *iterator; + child_sa_t *current, *child_sa = NULL; + linked_list_t *my_ts, *other_ts; + + if (this->state == IKE_DELETING) + { + this->logger->log(this->logger, CONTROL, + "acquiring CHILD_SA with reqid %d discarded, as IKE_SA is deleting", + reqid); + return FAILED; + } + + + /* find CHILD_SA */ + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->get_reqid(current) == reqid) + { + iterator->remove(iterator); + child_sa = current; + break; + } + } + iterator->destroy(iterator); + if (!child_sa) + { + this->logger->log(this->logger, ERROR, + "CHILD_SA with reqid %d not found, unable to acquire", + reqid); + return FAILED; + } + my_ts = child_sa->get_my_traffic_selectors(child_sa); + other_ts = child_sa->get_other_traffic_selectors(child_sa); + + policy = charon->policies->get_policy(charon->policies, + this->my_id, this->other_id, + my_ts, other_ts, + this->my_host, this->other_host); + child_sa->destroy(child_sa); + if (policy == NULL) + { + this->logger->log(this->logger, ERROR, + "no policy found to acquire CHILD_SA with reqid %d", + reqid); + return FAILED; + } + switch (this->state) { case IKE_CREATED: - /* ike_sa_init */ + { + ike_sa_init_t *ike_sa_init; + + this->logger->log(this->logger, CONTROL, + "acquiring CHILD_SA with reqid %d, IKE_SA setup needed", + reqid); + + connection = charon->connections->get_connection_by_hosts( + charon->connections, this->my_host, this->other_host); + + if (connection == NULL) + { + this->logger->log(this->logger, ERROR, + "no connection found to acquire IKE_SA for CHILD_SA with reqid %d", + reqid); + policy->destroy(policy); + return FAILED; + } + + this->message_id_out = 1; + ike_sa_init = ike_sa_init_create(&this->public); + ike_sa_init->set_config(ike_sa_init, connection, policy); + return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE); + } + case IKE_CONNECTING: + case IKE_ESTABLISHED: + { + create_child_sa_t *create_child; + + this->logger->log(this->logger, CONTROL, + "acquiring CHILD_SA with reqid %d", + reqid); + + create_child = create_child_sa_create(&this->public); + create_child->set_policy(create_child, policy); + return queue_transaction(this, (transaction_t*)create_child, FALSE); + } + default: + break; + } + return FAILED; +} + +/** + * destroy a list of traffic selectors + */ +static void ts_list_destroy(linked_list_t *list) +{ + traffic_selector_t *ts; + while (list->remove_last(list, (void**)&ts) == SUCCESS) + { + ts->destroy(ts); + } + list->destroy(list); +} + +/** + * compare two lists of traffic selectors for equality + */ +static bool ts_list_equals(linked_list_t *l1, linked_list_t *l2) +{ + bool equals = TRUE; + iterator_t *i1, *i2; + traffic_selector_t *t1, *t2; + + i1 = l1->create_iterator(l1, TRUE); + i2 = l2->create_iterator(l2, TRUE); + while (i1->iterate(i1, (void**)&t1) && i2->iterate(i2, (void**)&t2)) + { + if (!t1->equals(t1, t2)) + { + equals = FALSE; break; + } + } + /* check if one iterator is not at the end */ + if (i1->has_next(i1) || i2->has_next(i2)) + { + equals = FALSE; + } + i1->destroy(i1); + i2->destroy(i2); + return equals; +} + +/** + * Implementation of ike_sa_t.route. + */ +static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t *policy) +{ + child_sa_t *child_sa = NULL; + iterator_t *iterator; + linked_list_t *my_ts, *other_ts; + status_t status; + + /* check if not already routed*/ + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + linked_list_t *my_ts_conf, *other_ts_conf; + + my_ts = child_sa->get_my_traffic_selectors(child_sa); + other_ts = child_sa->get_other_traffic_selectors(child_sa); + + my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host); + other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host); + + if (ts_list_equals(my_ts, my_ts_conf) && + ts_list_equals(other_ts, other_ts_conf)) + { + ts_list_destroy(my_ts_conf); + ts_list_destroy(other_ts_conf); + iterator->destroy(iterator); + this->logger->log(this->logger, CONTROL, + "a CHILD_SA with such a policy already routed"); + + return FAILED; + } + ts_list_destroy(my_ts_conf); + ts_list_destroy(other_ts_conf); + } + iterator->destroy(iterator); + + switch (this->state) + { + case IKE_CREATED: case IKE_CONNECTING: + /* we update IKE_SA information as good as possible, + * this allows us to set up the SA later when an acquire comes in. */ + if (this->my_id->get_type(this->my_id) == ID_ANY) + { + this->my_id->destroy(this->my_id); + this->my_id = policy->get_my_id(policy); + this->my_id = this->my_id->clone(this->my_id); + } + if (this->other_id->get_type(this->other_id) == ID_ANY) + { + this->other_id->destroy(this->other_id); + this->other_id = policy->get_other_id(policy); + this->other_id = this->other_id->clone(this->other_id); + } + if (this->my_host->is_anyaddr(this->my_host)) + { + this->my_host->destroy(this->my_host); + this->my_host = connection->get_my_host(connection); + this->my_host = this->my_host->clone(this->my_host); + } + if (this->other_host->is_anyaddr(this->other_host)) + { + this->other_host->destroy(this->other_host); + this->other_host = connection->get_other_host(connection); + this->other_host = this->other_host->clone(this->other_host); + } + set_name(this, connection->get_name(connection)); + break; case IKE_ESTABLISHED: - /* queue create_child_sa */ + /* nothing to do */ break; case IKE_DELETING: /* deny */ - break; + return FAILED; } - return FAILED; + + child_sa = child_sa_create(0, this->my_host, this->other_host, 0, 0, FALSE); + my_ts = policy->get_my_traffic_selectors(policy, this->my_host); + other_ts = policy->get_other_traffic_selectors(policy, this->other_host); + status = child_sa->add_policies(child_sa, my_ts, other_ts); + ts_list_destroy(my_ts); + ts_list_destroy(other_ts); + this->child_sas->insert_last(this->child_sas, child_sa); + + return status; } /** - * Implementation of ike_sa_t.route. + * Implementation of ike_sa_t.unroute. */ -static status_t route(private_ike_sa_t *this, policy_t *policy) +static status_t unroute(private_ike_sa_t *this, policy_t *policy) { - /* TODO: create CHILD_SA, add policy */ - return FAILED; + iterator_t *iterator; + child_sa_t *child_sa = NULL; + linked_list_t *my_ts, *other_ts, *my_ts_conf, *other_ts_conf; + + /* find CHILD_SA in ROUTED state */ + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + if (child_sa->get_state(child_sa) == CHILD_ROUTED) + { + my_ts = child_sa->get_my_traffic_selectors(child_sa); + other_ts = child_sa->get_other_traffic_selectors(child_sa); + + my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host); + other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host); + + if (ts_list_equals(my_ts, my_ts_conf) && + ts_list_equals(other_ts, other_ts_conf)) + { + iterator->remove(iterator); + child_sa->destroy(child_sa); + ts_list_destroy(my_ts_conf); + ts_list_destroy(other_ts_conf); + break; + } + ts_list_destroy(my_ts_conf); + ts_list_destroy(other_ts_conf); + } + } + iterator->destroy(iterator); + /* if we are not established, and we have no more routed childs, remove whole SA */ + if (this->state == IKE_CREATED && + this->child_sas->get_count(this->child_sas) == 0) + { + return DESTROY_ME; + } + return SUCCESS; } /** @@ -1218,6 +1473,28 @@ static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa) } /** + * Implementation of protected_ike_sa_t.has_child_sa. + */ +static bool has_child_sa(private_ike_sa_t *this, u_int32_t reqid) +{ + iterator_t *iterator; + child_sa_t *current; + bool found = FALSE; + + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); + while (iterator->iterate(iterator, (void**)¤t)) + { + if (current->get_reqid(current) == reqid) + { + found = TRUE; + break; + } + } + iterator->destroy(iterator); + return found; +} + +/** * Implementation of protected_ike_sa_t.get_child_sa. */ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol, @@ -1226,12 +1503,12 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol, iterator_t *iterator; child_sa_t *current, *found = NULL; - iterator = this->child_sas->create_iterator(this->child_sas, FALSE); + iterator = this->child_sas->create_iterator(this->child_sas, TRUE); while (iterator->has_next(iterator)) { iterator->current(iterator, (void**)¤t); if (current->get_spi(current, inbound) == spi && - current->get_protocol(current) == protocol) + current->get_protocol(current) == protocol) { found = current; } @@ -1353,17 +1630,28 @@ static void log_status(private_ike_sa_t *this, logger_t *logger, char *name) */ static status_t delete_(private_ike_sa_t *this) { - delete_ike_sa_t *delete_ike_sa; - delete_ike_sa = delete_ike_sa_create(&this->public); - - if (this->transaction_out) + switch (this->state) { - /* already a transaction in progress. As this may hang - * around a while, we don't inform the other peer. */ - return DESTROY_ME; + case IKE_CONNECTING: + case IKE_ESTABLISHED: + { + delete_ike_sa_t *delete_ike_sa; + delete_ike_sa = delete_ike_sa_create(&this->public); + if (this->transaction_out) + { + /* already a transaction in progress. As this may hang + * around a while, we don't inform the other peer. */ + return DESTROY_ME; + } + return queue_transaction(this, (transaction_t*)delete_ike_sa, FALSE); + } + case IKE_CREATED: + case IKE_DELETING: + default: + { + return DESTROY_ME; + } } - - return queue_transaction(this, (transaction_t*)delete_ike_sa, FALSE); } /** @@ -1483,7 +1771,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.set_name = (void(*)(ike_sa_t*,char*))set_name; this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message; this->public.initiate = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) initiate; - this->public.route = (status_t(*)(ike_sa_t*,policy_t*)) route; + this->public.route = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) route; + this->public.unroute = (status_t(*)(ike_sa_t*,policy_t*)) unroute; this->public.acquire = (status_t(*)(ike_sa_t*,u_int32_t)) acquire; this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id; this->public.get_my_host = (host_t*(*)(ike_sa_t*)) get_my_host; @@ -1505,6 +1794,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->public.get_prf_auth_r = (prf_t *(*) (ike_sa_t *)) get_prf_auth_r; this->public.build_transforms = (status_t (*) (ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t,bool)) build_transforms; this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa; + this->public.has_child_sa = (bool(*)(ike_sa_t*,u_int32_t)) has_child_sa; this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa; this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) rekey_child_sa; this->public.delete_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) delete_child_sa; @@ -1517,10 +1807,10 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id) this->ike_sa_id = ike_sa_id->clone(ike_sa_id); this->name = strdup("(uninitialized)"); this->child_sas = linked_list_create(); - this->my_host = NULL; - this->other_host = NULL; - this->my_id = NULL; - this->other_id = NULL; + this->my_host = host_create(AF_INET, "0.0.0.0", 0); + this->other_host = host_create(AF_INET, "0.0.0.0", 0); + this->my_id = identification_create_from_encoding(ID_ANY, CHUNK_INITIALIZER); + this->other_id = identification_create_from_encoding(ID_ANY, CHUNK_INITIALIZER); this->crypter_in = NULL; this->crypter_out = NULL; this->signer_in = NULL; diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h index f593e382a..dfd24b049 100644 --- a/src/charon/sa/ike_sa.h +++ b/src/charon/sa/ike_sa.h @@ -237,16 +237,27 @@ struct ike_sa_t { * * Installs the policies in the kernel. If traffic matches, * the kernel requests connection setup from the IKE_SA via acquire(). - * The policy is owned by the IKE_SA after the call, so - * do not modify or destroy it. * * @param this calling object + * @param connection connection definition used for routing * @param policy policy to route * @return - * - SUCCESS if initialization started - * - DESTROY_ME if initialization failed and IKE_SA MUST be deleted + * - SUCCESS if routed successfully + * - FAILED if routing failed */ - status_t (*route) (ike_sa_t *this, policy_t *policy); + status_t (*route) (ike_sa_t *this, connection_t *connection, policy_t *policy); + + /** + * @brief Unroute a policy in the kernel previously routed. + * + * @param this calling object + * @param policy policy to route + * @return + * - SUCCESS if route removed + * - DESTROY_ME if last route was removed from + * an IKE_SA which was not established + */ + status_t (*unroute) (ike_sa_t *this, policy_t *policy); /** * @brief Acquire connection setup for a policy. @@ -436,6 +447,15 @@ struct ike_sa_t { void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa); /** + * @brief Check if an IKE_SA has one or more CHILD_SAs with a given reqid. + * + * @param this calling object + * @param reqid reqid of the CHILD + * @return TRUE if it has such a CHILD, FALSE if not + */ + bool (*has_child_sa) (ike_sa_t *this, u_int32_t reqid); + + /** * @brief Get a CHILD_SA identified by protocol and SPI. * * @param this calling object diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c index c8e4d71da..abb9c0fef 100644 --- a/src/charon/sa/ike_sa_manager.c +++ b/src/charon/sa/ike_sa_manager.c @@ -554,12 +554,11 @@ static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id /** * Implementation of of ike_sa_manager.checkout_by_child. */ -static status_t checkout_by_child(private_ike_sa_manager_t *this, - protocol_id_t protocol, u_int32_t spi, - ike_sa_t **ike_sa) +static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this, + u_int32_t reqid) { iterator_t *iterator; - status_t status = NOT_FOUND; + ike_sa_t *ike_sa = NULL; pthread_mutex_lock(&(this->mutex)); @@ -572,12 +571,11 @@ static status_t checkout_by_child(private_ike_sa_manager_t *this, if (wait_for_entry(this, entry)) { /* ok, access is exclusive for us, check for child */ - if (entry->ike_sa->get_child_sa(entry->ike_sa, protocol, spi, TRUE) != NULL) + if (entry->ike_sa->has_child_sa(entry->ike_sa, reqid)) { /* match */ entry->checked_out = TRUE; - *ike_sa = entry->ike_sa; - status = SUCCESS; + ike_sa = entry->ike_sa; break; } } @@ -585,7 +583,7 @@ static status_t checkout_by_child(private_ike_sa_manager_t *this, iterator->destroy(iterator); pthread_mutex_unlock(&(this->mutex)); - return status; + return ike_sa; } /** @@ -915,7 +913,7 @@ ike_sa_manager_t *ike_sa_manager_create() this->public.destroy = (void(*)(ike_sa_manager_t*))destroy; this->public.checkout_by_ids = (ike_sa_t*(*)(ike_sa_manager_t*,identification_t*,identification_t*))checkout_by_ids; this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout; - this->public.checkout_by_child = (status_t(*)(ike_sa_manager_t*,protocol_id_t,u_int32_t,ike_sa_t**))checkout_by_child; + this->public.checkout_by_child = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t))checkout_by_child; this->public.get_ike_sa_list = (linked_list_t*(*)(ike_sa_manager_t*))get_ike_sa_list; this->public.get_ike_sa_list_by_name = (linked_list_t*(*)(ike_sa_manager_t*,const char*))get_ike_sa_list_by_name; this->public.log_status = (void(*)(ike_sa_manager_t*,logger_t*,char*))log_status; diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h index 1c8dbad16..f327be09c 100644 --- a/src/charon/sa/ike_sa_manager.h +++ b/src/charon/sa/ike_sa_manager.h @@ -92,15 +92,12 @@ struct ike_sa_manager_t { * CHILD_SA the kernel wants to modify. * * @param this the manager object - * @param protocol protocol of the CHILD_SA - * @param spi SPI of the CHILD_SA - * @param[out] ike_sa checked out SA + * @param reqid reqid of the CHILD_SA * @return - * - NOT_FOUND, if no IKE SA with such a child found - * - SUCCESS, if ike_sa set + * - checked out IKE_SA, if found + * - NULL, if not found */ - status_t (*checkout_by_child) (ike_sa_manager_t* this, protocol_id_t protocol, - u_int32_t spi, ike_sa_t **ike_sa); + ike_sa_t* (*checkout_by_child) (ike_sa_manager_t* this, u_int32_t reqid); /** * @brief Get a list of all IKE_SA SAs currently set up. diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c index 4bb9a6cea..9ca821fa2 100644 --- a/src/charon/threads/kernel_interface.c +++ b/src/charon/threads/kernel_interface.c @@ -43,6 +43,7 @@ #include <utils/linked_list.h> #include <queues/jobs/delete_child_sa_job.h> #include <queues/jobs/rekey_child_sa_job.h> +#include <queues/jobs/acquire_job.h> /** kernel level protocol identifiers */ #define KERNEL_ESP 50 @@ -342,44 +343,65 @@ static void receive_messages(private_kernel_interface_t *this) break; } - /* we handle ACQUIRE and EXPIRE messages directly - */ + /* we handle ACQUIRE and EXPIRE messages directly */ hdr = (struct nlmsghdr*)response; if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE) { - struct xfrm_user_acquire *acquire; - - acquire = (struct xfrm_user_acquire*)NLMSG_DATA(hdr); - this->logger->log(this->logger, CONTROL, - "Received a XFRM_MSG_ACQUIRE with index %d. Ignored", - acquire->policy.index); - + u_int32_t reqid = 0; + job_t *job; + struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_user_acquire); + size_t rtsize = XFRM_PAYLOAD(hdr, struct xfrm_user_tmpl); + if (RTA_OK(rthdr, rtsize)) + { + if (rthdr->rta_type == XFRMA_TMPL) + { + struct xfrm_user_tmpl* tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr); + reqid = tmpl->reqid; + } + } + if (reqid == 0) + { + this->logger->log(this->logger, ERROR, + "Received a XFRM_MSG_ACQUIRE, but no reqid found"); + } + else + { + this->logger->log(this->logger, CONTROL|LEVEL1, + "Received a XFRM_MSG_ACQUIRE"); + this->logger->log(this->logger, CONTROL, + "creating acquire job for CHILD_SA with reqid %d", + reqid); + job = (job_t*)acquire_job_create(reqid); + charon->job_queue->add(charon->job_queue, job); + } } else if (hdr->nlmsg_type == XFRM_MSG_EXPIRE) { job_t *job; protocol_id_t protocol; - u_int32_t spi; + u_int32_t spi, reqid; struct xfrm_user_expire *expire; expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr); protocol = expire->state.id.proto == KERNEL_ESP ? PROTO_ESP : PROTO_AH; spi = expire->state.id.spi; + reqid = expire->state.reqid; this->logger->log(this->logger, CONTROL|LEVEL1, "Received a XFRM_MSG_EXPIRE"); this->logger->log(this->logger, CONTROL, - "creating %s job for %s CHILD_SA 0x%x", + "creating %s job for %s CHILD_SA 0x%x (reqid %d)", expire->hard ? "delete" : "rekey", - mapping_find(protocol_id_m, protocol), ntohl(spi)); + mapping_find(protocol_id_m, protocol), ntohl(spi), + reqid); if (expire->hard) { - job = (job_t*)delete_child_sa_job_create(protocol, spi); + job = (job_t*)delete_child_sa_job_create(reqid, protocol, spi); } else { - job = (job_t*)rekey_child_sa_job_create(protocol, spi); + job = (job_t*)rekey_child_sa_job_create(reqid, protocol, spi); } charon->job_queue->add(charon->job_queue, job); } @@ -670,7 +692,7 @@ static status_t update_sa( sa_id->spi = spi; sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH; sa_id->family = dst->get_family(dst); - POS; + if (send_message(this, hdr, &update) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); @@ -695,7 +717,6 @@ static status_t update_sa( free(update); return FAILED; } - POS; this->logger->log(this->logger, CONTROL|LEVEL2, "updating SA"); update->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -731,7 +752,7 @@ static status_t update_sa( rthdr = RTA_NEXT(rthdr, rtsize); } } - POS; + if (send_message(this, update, &response) != SUCCESS) { this->logger->log(this->logger, ERROR, "netlink communication failed"); @@ -754,8 +775,7 @@ static status_t update_sa( this->logger->log(this->logger, CONTROL|LEVEL2, "deleting old SA"); status = this->public.del_sa(&this->public, dst, spi, protocol); } - POS; - + free(update); free(response); return status; @@ -1052,7 +1072,7 @@ static status_t add_policy(private_kernel_interface_t *this, 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->id.proto = (protocol == PROTO_AH) ? KERNEL_AH : KERNEL_ESP; tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0; tmpl->mode = TRUE; diff --git a/src/charon/threads/stroke_interface.c b/src/charon/threads/stroke_interface.c index 004e3ec1f..7188a532d 100755 --- a/src/charon/threads/stroke_interface.c +++ b/src/charon/threads/stroke_interface.c @@ -38,6 +38,7 @@ #include <daemon.h> #include <crypto/x509.h> #include <queues/jobs/initiate_job.h> +#include <queues/jobs/route_job.h> #define IKE_PORT 500 #define PATH_BUF 256 @@ -519,6 +520,56 @@ static void stroke_initiate(private_stroke_t *this, stroke_msg_t *msg) } /** + * route/unroute a policy (install SPD entries) + */ +static void stroke_route(private_stroke_t *this, stroke_msg_t *msg, bool route) +{ + route_job_t *job; + connection_t *connection; + policy_t *policy; + + pop_string(msg, &(msg->route.name)); + this->logger->log(this->logger, CONTROL, + "received stroke: %s \"%s\"", + route ? "route" : "unroute", + msg->route.name); + + /* we wouldn't need a connection, but we only want to route policies + * whose connections are keyexchange=ikev2. */ + connection = charon->connections->get_connection_by_name(charon->connections, + msg->route.name); + if (connection == NULL) + { + this->stroke_logger->log(this->stroke_logger, ERROR, + "no connection named \"%s\"", + msg->route.name); + return; + } + if (!connection->is_ikev2(connection)) + { + connection->destroy(connection); + return; + } + + policy = charon->policies->get_policy_by_name(charon->policies, + msg->route.name); + if (policy == NULL) + { + this->stroke_logger->log(this->stroke_logger, ERROR, + "no policy named \"%s\"", + msg->route.name); + connection->destroy(connection); + return; + } + this->stroke_logger->log(this->stroke_logger, CONTROL, + "%s policy \"%s\"", + route ? "routing" : "unrouting", + msg->route.name); + job = route_job_create(connection, policy, route); + charon->job_queue->add(charon->job_queue, (job_t*)job); +} + +/** * terminate a connection by name */ static void stroke_terminate(private_stroke_t *this, stroke_msg_t *msg) @@ -795,6 +846,12 @@ static void stroke_receive(private_stroke_t *this) case STR_INITIATE: stroke_initiate(this, msg); break; + case STR_ROUTE: + stroke_route(this, msg, TRUE); + break; + case STR_UNROUTE: + stroke_route(this, msg, FALSE); + break; case STR_TERMINATE: stroke_terminate(this, msg); break; diff --git a/src/ipsec/ipsec.in b/src/ipsec/ipsec.in index 5524ee627..477d345fb 100755 --- a/src/ipsec/ipsec.in +++ b/src/ipsec/ipsec.in @@ -174,6 +174,10 @@ route|unroute) then $IPSEC_WHACK --name "$1" "--$op" fi + if test -e $IPSEC_CHARON_PID + then + $IPSEC_STROKE "$op" "$1" + fi exit 0 ;; scencrypt|scdecrypt) diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c index d17a8508b..68a25df90 100644 --- a/src/starter/starterstroke.c +++ b/src/starter/starterstroke.c @@ -159,7 +159,7 @@ int starter_stroke_del_conn(starter_conn_t *conn) msg.type = STR_DEL_CONN; msg.length = offsetof(stroke_msg_t, buffer); - msg.install.name = push_string(&msg, connection_name(conn)); + msg.del_conn.name = push_string(&msg, connection_name(conn)); return send_stroke_msg(&msg); } @@ -167,9 +167,9 @@ int starter_stroke_route_conn(starter_conn_t *conn) { stroke_msg_t msg; - msg.type = STR_INSTALL; + msg.type = STR_ROUTE; msg.length = offsetof(stroke_msg_t, buffer); - msg.install.name = push_string(&msg, connection_name(conn)); + msg.route.name = push_string(&msg, connection_name(conn)); return send_stroke_msg(&msg); } diff --git a/src/stroke/stroke.c b/src/stroke/stroke.c index 15661a2ec..e29a58813 100644 --- a/src/stroke/stroke.c +++ b/src/stroke/stroke.c @@ -169,6 +169,26 @@ static int terminate_connection(char *name) return send_stroke_msg(&msg); } +static int route_connection(char *name) +{ + stroke_msg_t msg; + + msg.type = STR_ROUTE; + msg.length = offsetof(stroke_msg_t, buffer); + msg.route.name = push_string(&msg, name); + return send_stroke_msg(&msg); +} + +static int unroute_connection(char *name) +{ + stroke_msg_t msg; + + msg.type = STR_UNROUTE; + msg.length = offsetof(stroke_msg_t, buffer); + msg.unroute.name = push_string(&msg, name); + return send_stroke_msg(&msg); +} + static int show_status(stroke_keyword_t kw, char *connection) { stroke_msg_t msg; @@ -336,6 +356,20 @@ int main(int argc, char *argv[]) } res = terminate_connection(argv[2]); break; + case STROKE_ROUTE: + if (argc < 3) + { + exit_usage("\"route\" needs a connection name"); + } + res = route_connection(argv[2]); + break; + case STROKE_UNROUTE: + if (argc < 3) + { + exit_usage("\"unroute\" needs a connection name"); + } + res = unroute_connection(argv[2]); + break; case STROKE_LOGTYPE: if (argc < 5) { diff --git a/src/stroke/stroke.h b/src/stroke/stroke.h index 9aa4de35f..b71e19921 100644 --- a/src/stroke/stroke.h +++ b/src/stroke/stroke.h @@ -99,8 +99,10 @@ struct stroke_msg_t { enum { /* initiate a connection */ STR_INITIATE, - /* install SPD entries for a connection */ - STR_INSTALL, + /* install SPD entries for a policy */ + STR_ROUTE, + /* uninstall SPD entries for a policy */ + STR_UNROUTE, /* add a connection */ STR_ADD_CONN, /* delete a connection */ @@ -123,10 +125,10 @@ struct stroke_msg_t { } type; union { - /* data for STR_INITIATE, STR_INSTALL, STR_UP, STR_DOWN, ... */ + /* data for STR_INITIATE, STR_ROUTE, STR_UP, STR_DOWN, ... */ struct { char *name; - } initiate, install, terminate, status, del_conn; + } initiate, route, unroute, terminate, status, del_conn; /* data for STR_ADD_CONN */ struct { diff --git a/src/stroke/stroke_keywords.h b/src/stroke/stroke_keywords.h index c40bed3af..6a8dd5359 100644 --- a/src/stroke/stroke_keywords.h +++ b/src/stroke/stroke_keywords.h @@ -23,6 +23,7 @@ typedef enum { STROKE_DEL, STROKE_DELETE, STROKE_ROUTE, + STROKE_UNROUTE, STROKE_UP, STROKE_DOWN, STROKE_LOGTYPE, diff --git a/src/stroke/stroke_keywords.txt b/src/stroke/stroke_keywords.txt index 9b380ae66..d720a7d3a 100644 --- a/src/stroke/stroke_keywords.txt +++ b/src/stroke/stroke_keywords.txt @@ -30,6 +30,7 @@ add, STROKE_ADD del, STROKE_DEL delete, STROKE_DELETE route, STROKE_ROUTE +unroute, STROKE_UNROUTE up, STROKE_UP down, STROKE_DOWN logtype, STROKE_LOGTYPE |