diff --git a/src/libcharon/plugins/ha/Makefile.am b/src/libcharon/plugins/ha/Makefile.am index 74fe1f4c7..165f8c9dc 100644 --- a/src/libcharon/plugins/ha/Makefile.am +++ b/src/libcharon/plugins/ha/Makefile.am @@ -17,6 +17,7 @@ libstrongswan_ha_la_SOURCES = \ ha_tunnel.h ha_tunnel.c \ ha_dispatcher.h ha_dispatcher.c \ ha_segments.h ha_segments.c \ + ha_cache.h ha_cache.c \ ha_kernel.h ha_kernel.c \ ha_ctl.h ha_ctl.c \ ha_ike.h ha_ike.c \ diff --git a/src/libcharon/plugins/ha/ha_cache.c b/src/libcharon/plugins/ha/ha_cache.c new file mode 100644 index 000000000..1ebc33ca4 --- /dev/null +++ b/src/libcharon/plugins/ha/ha_cache.c @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * 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 . + * + * 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 "ha_cache.h" + +#include +#include +#include +#include + +typedef struct private_ha_cache_t private_ha_cache_t; + +/** + * Private data of an ha_cache_t object. + */ +struct private_ha_cache_t { + + /** + * Public ha_cache_t interface. + */ + ha_cache_t public; + + /** + * Kernel helper functions + */ + ha_kernel_t *kernel; + + /** + * Socket to send sync messages over + */ + ha_socket_t *socket; + + /** + * Total number of segments + */ + u_int count; + + /** + * cached entries (ike_sa_t, entry_t) + */ + hashtable_t *cache; + + /** + * Mutex to lock cache + */ + mutex_t *mutex; +}; + +/** + * Hashtable hash function + */ +static u_int hash(void *key) +{ + return (uintptr_t)key; +} + +/** + * Hashtable equals function + */ +static bool equals(void *a, void *b) +{ + return a == b; +} + +/** + * Cache entry for an IKE_SA + */ +typedef struct { + /* segment this entry is associate to */ + u_int segment; + /* ADD message */ + ha_message_t *add; + /* list of updates UPDATE message */ + linked_list_t *updates; + /* last initiator mid */ + ha_message_t *midi; + /* last responder mid */ + ha_message_t *midr; +} entry_t; + +/** + * Create a entry with an add message + */ +static entry_t *entry_create(ha_message_t *add) +{ + entry_t *entry; + + INIT(entry, + .add = add, + .updates = linked_list_create(), + ); + return entry; +} + +/** + * clean up a entry + */ +static void entry_destroy(entry_t *entry) +{ + entry->updates->destroy_offset(entry->updates, + offsetof(ha_message_t, destroy)); + entry->add->destroy(entry->add); + DESTROY_IF(entry->midi); + DESTROY_IF(entry->midr); + free(entry); +} + +METHOD(ha_cache_t, cache, void, + private_ha_cache_t *this, ike_sa_t *ike_sa, ha_message_t *message) +{ + entry_t *entry; + + this->mutex->lock(this->mutex); + switch (message->get_type(message)) + { + case HA_IKE_ADD: + entry = entry_create(message); + entry = this->cache->put(this->cache, ike_sa, entry); + if (entry) + { + entry_destroy(entry); + } + break; + case HA_IKE_UPDATE: + entry = this->cache->get(this->cache, ike_sa); + if (entry) + { + entry->segment = this->kernel->get_segment(this->kernel, + ike_sa->get_other_host(ike_sa)); + entry->updates->insert_last(entry->updates, message); + break; + } + message->destroy(message); + break; + case HA_IKE_MID_INITIATOR: + entry = this->cache->get(this->cache, ike_sa); + if (entry) + { + DESTROY_IF(entry->midi); + entry->midi = message; + break; + } + message->destroy(message); + break; + case HA_IKE_MID_RESPONDER: + entry = this->cache->get(this->cache, ike_sa); + if (entry) + { + DESTROY_IF(entry->midr); + entry->midr = message; + break; + } + message->destroy(message); + break; + case HA_IKE_DELETE: + entry = this->cache->remove(this->cache, ike_sa); + if (entry) + { + entry_destroy(entry); + } + message->destroy(message); + break; + default: + message->destroy(message); + break; + } + this->mutex->unlock(this->mutex); +} + +METHOD(ha_cache_t, delete_, void, + private_ha_cache_t *this, ike_sa_t *ike_sa) +{ + entry_t *entry; + + entry = this->cache->remove(this->cache, ike_sa); + if (entry) + { + entry_destroy(entry); + } +} + +/** + * Rekey all children of an IKE_SA + */ +static status_t rekey_children(ike_sa_t *ike_sa) +{ + iterator_t *iterator; + child_sa_t *child_sa; + status_t status = SUCCESS; + + iterator = ike_sa->create_child_sa_iterator(ike_sa); + while (iterator->iterate(iterator, (void**)&child_sa)) + { + DBG1(DBG_CFG, "resyncing CHILD_SA"); + status = ike_sa->rekey_child_sa(ike_sa, child_sa->get_protocol(child_sa), + child_sa->get_spi(child_sa, TRUE)); + if (status == DESTROY_ME) + { + break; + } + } + iterator->destroy(iterator); + return status; +} + +/** + * Trigger rekeying of CHILD_SA in segment + */ +static void rekey_segment(private_ha_cache_t *this, u_int segment) +{ + ike_sa_t *ike_sa; + enumerator_t *enumerator; + linked_list_t *list; + ike_sa_id_t *id; + + list = linked_list_create(); + + enumerator = charon->ike_sa_manager->create_enumerator( + charon->ike_sa_manager); + while (enumerator->enumerate(enumerator, &ike_sa)) + { + if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && + this->kernel->get_segment(this->kernel, + ike_sa->get_other_host(ike_sa)) == segment) + { + id = ike_sa->get_id(ike_sa); + list->insert_last(list, id->clone(id)); + } + } + enumerator->destroy(enumerator); + + while (list->remove_last(list, (void**)&id) == SUCCESS) + { + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id); + if (ike_sa) + { + if (rekey_children(ike_sa) != DESTROY_ME) + { + charon->ike_sa_manager->checkin( + charon->ike_sa_manager, ike_sa); + } + else + { + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + } + } + id->destroy(id); + } + list->destroy(list); +} + +METHOD(ha_cache_t, resync, void, + private_ha_cache_t *this, u_int segment) +{ + enumerator_t *enumerator, *updates; + ike_sa_t *ike_sa; + entry_t *entry; + ha_message_t *message; + + DBG1(DBG_CFG, "resyncing HA segment %d", segment); + + this->mutex->lock(this->mutex); + enumerator = this->cache->create_enumerator(this->cache); + while (enumerator->enumerate(enumerator, &ike_sa, &entry)) + { + if (entry->segment == segment) + { + this->socket->push(this->socket, entry->add); + updates = entry->updates->create_enumerator(entry->updates); + while (updates->enumerate(updates, &message)) + { + this->socket->push(this->socket, message); + } + updates->destroy(updates); + if (entry->midi) + { + this->socket->push(this->socket, entry->midi); + } + if (entry->midr) + { + this->socket->push(this->socket, entry->midr); + } + } + } + enumerator->destroy(enumerator); + this->mutex->unlock(this->mutex); + + rekey_segment(this, segment); +} + +/** + * Request a resync of all segments + */ +static job_requeue_t request_resync(private_ha_cache_t *this) +{ + ha_message_t *message; + int i; + + DBG1(DBG_CFG, "requesting HA resynchronization"); + + message = ha_message_create(HA_RESYNC); + for (i = 1; i <= this->count; i++) + { + message->add_attribute(message, HA_SEGMENT, i); + } + this->socket->push(this->socket, message); + message->destroy(message); + return JOB_REQUEUE_NONE; +} + +METHOD(ha_cache_t, destroy, void, + private_ha_cache_t *this) +{ + this->cache->destroy(this->cache); + this->mutex->destroy(this->mutex); + free(this); +} + +/** + * See header + */ +ha_cache_t *ha_cache_create(ha_kernel_t *kernel, ha_socket_t *socket, + bool sync, u_int count) +{ + private_ha_cache_t *this; + + INIT(this, + .public = { + .cache = _cache, + .delete = _delete_, + .resync = _resync, + .destroy = _destroy, + }, + .count = count, + .kernel = kernel, + .socket = socket, + .cache = hashtable_create(hash, equals, 8), + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + ); + + if (sync) + { + /* request a resync as soon as we are up */ + charon->scheduler->schedule_job(charon->scheduler, (job_t*) + callback_job_create((callback_job_cb_t)request_resync, + this, NULL, NULL), 1); + } + return &this->public; +} diff --git a/src/libcharon/plugins/ha/ha_cache.h b/src/libcharon/plugins/ha/ha_cache.h new file mode 100644 index 000000000..39f1947a8 --- /dev/null +++ b/src/libcharon/plugins/ha/ha_cache.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * 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 . + * + * 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. + */ + +/** + * @defgroup ha_cache ha_cache + * @{ @ingroup ha + */ + +#ifndef HA_CACHE_H_ +#define HA_CACHE_H_ + +typedef struct ha_cache_t ha_cache_t; + +#include "ha_message.h" +#include "ha_kernel.h" +#include "ha_socket.h" + +#include + +#include + +/** + * HA message caching facility, allows reintegration of new nodes. + */ +struct ha_cache_t { + + /** + * Cache an IKE specific message. + * + * @param ike_sa associated IKE_SA + * @param message message to cache + */ + void (*cache)(ha_cache_t *this, ike_sa_t *ike_sa, ha_message_t *message); + + /** + * Delete a cache entry for an IKE_SA. + * + * @param ike_sa cache entry to delete + */ + void (*delete)(ha_cache_t *this, ike_sa_t *ike_sa); + + /** + * Resync a segment to the node using the cached messages. + * + * @param segment segment to resync + */ + void (*resync)(ha_cache_t *this, u_int segment); + + /** + * Destroy a ha_cache_t. + */ + void (*destroy)(ha_cache_t *this); +}; + +/** + * Create a ha_cache instance. + * + * @param kernel kernel helper + * @param socket socket to send resync messages + * @param resync request a resync during startup? + * @param count total number of segments + */ +ha_cache_t *ha_cache_create(ha_kernel_t *kernel, ha_socket_t *socket, + bool resync, u_int count); + +#endif /** HA_CACHE_H_ @}*/ diff --git a/src/libcharon/plugins/ha/ha_child.c b/src/libcharon/plugins/ha/ha_child.c index 808a42098..5a437c5ac 100644 --- a/src/libcharon/plugins/ha/ha_child.c +++ b/src/libcharon/plugins/ha/ha_child.c @@ -36,6 +36,11 @@ struct private_ha_child_t { * tunnel securing sync messages */ ha_tunnel_t *tunnel; + + /** + * message cache + */ + ha_cache_t *cache; }; METHOD(listener_t, child_keys, bool, @@ -103,6 +108,7 @@ METHOD(listener_t, child_keys, bool, enumerator->destroy(enumerator); this->socket->push(this->socket, m); + m->destroy(m); return TRUE; } @@ -133,6 +139,7 @@ METHOD(listener_t, child_state_change, bool, m->add_attribute(m, HA_INBOUND_SPI, child_sa->get_spi(child_sa, TRUE)); this->socket->push(this->socket, m); + m->destroy(m); } return TRUE; } @@ -146,7 +153,8 @@ METHOD(ha_child_t, destroy, void, /** * See header */ -ha_child_t *ha_child_create(ha_socket_t *socket, ha_tunnel_t *tunnel) +ha_child_t *ha_child_create(ha_socket_t *socket, ha_tunnel_t *tunnel, + ha_cache_t *cache) { private_ha_child_t *this; @@ -160,6 +168,7 @@ ha_child_t *ha_child_create(ha_socket_t *socket, ha_tunnel_t *tunnel) }, .socket = socket, .tunnel = tunnel, + .cache = cache, ); return &this->public; diff --git a/src/libcharon/plugins/ha/ha_child.h b/src/libcharon/plugins/ha/ha_child.h index 9b4a57510..9a28e5123 100644 --- a/src/libcharon/plugins/ha/ha_child.h +++ b/src/libcharon/plugins/ha/ha_child.h @@ -21,14 +21,15 @@ #ifndef HA_CHILD_H_ #define HA_CHILD_H_ +typedef struct ha_child_t ha_child_t; + #include "ha_socket.h" #include "ha_tunnel.h" #include "ha_segments.h" +#include "ha_cache.h" #include -typedef struct ha_child_t ha_child_t; - /** * Listener to synchronize CHILD_SAs. */ @@ -50,8 +51,10 @@ struct ha_child_t { * * @param socket socket to use for sending synchronization messages * @param tunnel tunnel securing sync messages, if any + * @param cache message resync cache * @return CHILD listener */ -ha_child_t *ha_child_create(ha_socket_t *socket, ha_tunnel_t *tunnel); +ha_child_t *ha_child_create(ha_socket_t *socket, ha_tunnel_t *tunnel, + ha_cache_t *cache); #endif /** HA_CHILD_ @}*/ diff --git a/src/libcharon/plugins/ha/ha_ctl.c b/src/libcharon/plugins/ha/ha_ctl.c index 011b350e0..e188a8484 100644 --- a/src/libcharon/plugins/ha/ha_ctl.c +++ b/src/libcharon/plugins/ha/ha_ctl.c @@ -44,6 +44,11 @@ struct private_ha_ctl_t { */ ha_segments_t *segments; + /** + * Resynchronization message cache + */ + ha_cache_t *cache; + /** * FIFO reader thread */ @@ -84,7 +89,7 @@ static job_requeue_t dispatch_fifo(private_ha_ctl_t *this) this->segments->deactivate(this->segments, segment, TRUE); break; case '*': - this->segments->resync(this->segments, segment); + this->cache->resync(this->cache, segment); break; default: break; @@ -106,7 +111,7 @@ METHOD(ha_ctl_t, destroy, void, /** * See header */ -ha_ctl_t *ha_ctl_create(ha_segments_t *segments) +ha_ctl_t *ha_ctl_create(ha_segments_t *segments, ha_cache_t *cache) { private_ha_ctl_t *this; @@ -115,6 +120,7 @@ ha_ctl_t *ha_ctl_create(ha_segments_t *segments) .destroy = _destroy, }, .segments = segments, + .cache = cache, ); if (access(HA_FIFO, R_OK|W_OK) != 0) diff --git a/src/libcharon/plugins/ha/ha_ctl.h b/src/libcharon/plugins/ha/ha_ctl.h index 3aae132d8..1e717832a 100644 --- a/src/libcharon/plugins/ha/ha_ctl.h +++ b/src/libcharon/plugins/ha/ha_ctl.h @@ -22,6 +22,7 @@ #define HA_CTL_H_ #include "ha_segments.h" +#include "ha_cache.h" typedef struct ha_ctl_t ha_ctl_t; @@ -40,8 +41,9 @@ struct ha_ctl_t { * Create a ha_ctl instance. * * @param segments segments to control + * @param cache message cache for resynchronization * @return HA control interface */ -ha_ctl_t *ha_ctl_create(ha_segments_t *segments); +ha_ctl_t *ha_ctl_create(ha_segments_t *segments, ha_cache_t *cache); #endif /** HA_CTL_ @}*/ diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index 575d8eeb2..d9563047b 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -40,6 +40,11 @@ struct private_ha_dispatcher_t { */ ha_segments_t *segments; + /** + * Cache for resync + */ + ha_cache_t *cache; + /** * Dispatcher job */ @@ -153,6 +158,8 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message old_sa = NULL; } ike_sa->set_state(ike_sa, IKE_CONNECTING); + this->cache->cache(this->cache, ike_sa, message); + message = NULL; charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } else @@ -167,6 +174,7 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message { charon->ike_sa_manager->checkin(charon->ike_sa_manager, old_sa); } + DESTROY_IF(message); } /** @@ -276,10 +284,20 @@ static void process_ike_update(private_ha_dispatcher_t *this, if (ike_sa->get_state(ike_sa) == IKE_CONNECTING && ike_sa->get_peer_cfg(ike_sa)) { + DBG1(DBG_CFG, "installed HA passive IKE_SA '%s' %H[%Y]...%H[%Y]", + ike_sa->get_name(ike_sa), + ike_sa->get_my_host(ike_sa), ike_sa->get_my_id(ike_sa), + ike_sa->get_other_host(ike_sa), ike_sa->get_other_id(ike_sa)); ike_sa->set_state(ike_sa, IKE_PASSIVE); } + this->cache->cache(this->cache, ike_sa, message); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } + else + { + DBG1(DBG_CFG, "passive HA IKE_SA to update not found"); + message->destroy(message); + } } /** @@ -318,8 +336,13 @@ static void process_ike_mid(private_ha_dispatcher_t *this, { ike_sa->set_message_id(ike_sa, initiator, mid); } + this->cache->cache(this->cache, ike_sa, message); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } + else + { + message->destroy(message); + } } /** @@ -331,7 +354,7 @@ static void process_ike_delete(private_ha_dispatcher_t *this, ha_message_attribute_t attribute; ha_message_value_t value; enumerator_t *enumerator; - ike_sa_t *ike_sa; + ike_sa_t *ike_sa = NULL; enumerator = message->create_attribute_enumerator(message); while (enumerator->enumerate(enumerator, &attribute, &value)) @@ -341,17 +364,22 @@ static void process_ike_delete(private_ha_dispatcher_t *this, case HA_IKE_ID: ike_sa = charon->ike_sa_manager->checkout( charon->ike_sa_manager, value.ike_sa_id); - if (ike_sa) - { - charon->ike_sa_manager->checkin_and_destroy( - charon->ike_sa_manager, ike_sa); - } break; default: break; } } enumerator->destroy(enumerator); + if (ike_sa) + { + this->cache->cache(this->cache, ike_sa, message); + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + } + else + { + message->destroy(message); + } } /** @@ -465,6 +493,7 @@ static void process_child_add(private_ha_dispatcher_t *this, if (!ike_sa) { DBG1(DBG_CHD, "IKE_SA for HA CHILD_SA not found"); + message->destroy(message); return; } config = find_child_cfg(ike_sa, config_name); @@ -472,6 +501,7 @@ static void process_child_add(private_ha_dispatcher_t *this, { DBG1(DBG_CHD, "HA is missing nodes child configuration"); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + message->destroy(message); return; } @@ -558,15 +588,19 @@ static void process_child_add(private_ha_dispatcher_t *this, local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy)); remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy)); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + message->destroy(message); return; } + DBG1(DBG_CFG, "installed HA CHILD_SA '%s' %#R=== %#R", + child_sa->get_name(child_sa), local_ts, remote_ts); child_sa->add_policies(child_sa, local_ts, remote_ts); local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy)); remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy)); child_sa->set_state(child_sa, CHILD_INSTALLED); ike_sa->add_child_sa(ike_sa, child_sa); + message->destroy(message); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } @@ -580,6 +614,8 @@ static void process_child_delete(private_ha_dispatcher_t *this, ha_message_value_t value; enumerator_t *enumerator; ike_sa_t *ike_sa = NULL; + child_sa_t *child_sa; + u_int32_t spi = 0; enumerator = message->create_attribute_enumerator(message); while (enumerator->enumerate(enumerator, &attribute, &value)) @@ -591,20 +627,24 @@ static void process_child_delete(private_ha_dispatcher_t *this, value.ike_sa_id); break; case HA_INBOUND_SPI: - if (ike_sa) - { - ike_sa->destroy_child_sa(ike_sa, PROTO_ESP, value.u32); - } + spi = value.u32; break; default: break; } } + enumerator->destroy(enumerator); + if (ike_sa) { + child_sa = ike_sa->get_child_sa(ike_sa, PROTO_ESP, spi, TRUE); + if (child_sa) + { + ike_sa->destroy_child_sa(ike_sa, PROTO_ESP, spi); + } charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); } - enumerator->destroy(enumerator); + message->destroy(message); } /** @@ -639,6 +679,7 @@ static void process_segment(private_ha_dispatcher_t *this, } } enumerator->destroy(enumerator); + message->destroy(message); } /** @@ -667,6 +708,7 @@ static void process_status(private_ha_dispatcher_t *this, enumerator->destroy(enumerator); this->segments->handle_status(this->segments, mask); + message->destroy(message); } /** @@ -685,13 +727,14 @@ static void process_resync(private_ha_dispatcher_t *this, switch (attribute) { case HA_SEGMENT: - this->segments->resync(this->segments, value.u16); + this->cache->resync(this->cache, value.u16); break; default: break; } } enumerator->destroy(enumerator); + message->destroy(message); } /** @@ -746,10 +789,9 @@ static job_requeue_t dispatch(private_ha_dispatcher_t *this) break; default: DBG1(DBG_CFG, "received unknown HA message type %d", type); + message->destroy(message); break; } - message->destroy(message); - return JOB_REQUEUE_DIRECT; } @@ -764,7 +806,7 @@ METHOD(ha_dispatcher_t, destroy, void, * See header */ ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket, - ha_segments_t *segments) + ha_segments_t *segments, ha_cache_t *cache) { private_ha_dispatcher_t *this; @@ -775,6 +817,7 @@ ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket, }, .socket = socket, .segments = segments, + .cache = cache, ); this->job = callback_job_create((callback_job_cb_t)dispatch, this, NULL, NULL); diff --git a/src/libcharon/plugins/ha/ha_dispatcher.h b/src/libcharon/plugins/ha/ha_dispatcher.h index 55c08580a..3190458fc 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.h +++ b/src/libcharon/plugins/ha/ha_dispatcher.h @@ -23,6 +23,7 @@ #include "ha_socket.h" #include "ha_segments.h" +#include "ha_cache.h" typedef struct ha_dispatcher_t ha_dispatcher_t; @@ -42,9 +43,10 @@ struct ha_dispatcher_t { * * @param socket socket to pull messages from * @param segments segments to control based on received messages + * @param cache message cache to use for resynchronization * @return dispatcher object */ ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket, - ha_segments_t *segments); + ha_segments_t *segments, ha_cache_t *cache); #endif /** HA_DISPATCHER_ @}*/ diff --git a/src/libcharon/plugins/ha/ha_ike.c b/src/libcharon/plugins/ha/ha_ike.c index 9c0919ac7..c7a7c5826 100644 --- a/src/libcharon/plugins/ha/ha_ike.c +++ b/src/libcharon/plugins/ha/ha_ike.c @@ -36,6 +36,11 @@ struct private_ha_ike_t { * tunnel securing sync messages */ ha_tunnel_t *tunnel; + + /** + * message cache + */ + ha_cache_t *cache; }; /** @@ -117,6 +122,7 @@ METHOD(listener_t, ike_keys, bool, chunk_clear(&secret); this->socket->push(this->socket, m); + this->cache->cache(this->cache, ike_sa, m); return TRUE; } @@ -181,6 +187,7 @@ METHOD(listener_t, ike_updown, bool, m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); } this->socket->push(this->socket, m); + this->cache->cache(this->cache, ike_sa, m); return TRUE; } @@ -192,6 +199,17 @@ METHOD(listener_t, ike_rekey, bool, return TRUE; } +METHOD(listener_t, ike_state_change, bool, + private_ha_ike_t *this, ike_sa_t *ike_sa, ike_sa_state_t new) +{ + /* clean up cache if a passive IKE_SA goes away */ + if (ike_sa->get_state(ike_sa) == IKE_PASSIVE && new == IKE_DESTROYING) + { + this->cache->delete(this->cache, ike_sa); + } + return TRUE; +} + METHOD(listener_t, message_hook, bool, private_ha_ike_t *this, ike_sa_t *ike_sa, message_t *message, bool incoming) { @@ -216,6 +234,7 @@ METHOD(listener_t, message_hook, bool, m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); m->add_attribute(m, HA_MID, message->get_message_id(message) + 1); this->socket->push(this->socket, m); + this->cache->cache(this->cache, ike_sa, m); } if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && message->get_exchange_type(message) == IKE_AUTH && @@ -233,6 +252,7 @@ METHOD(listener_t, message_hook, bool, m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); m->add_attribute(m, HA_REMOTE_VIP, vip); this->socket->push(this->socket, m); + this->cache->cache(this->cache, ike_sa, m); } } return TRUE; @@ -247,7 +267,8 @@ METHOD(ha_ike_t, destroy, void, /** * See header */ -ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel) +ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel, + ha_cache_t *cache) { private_ha_ike_t *this; @@ -257,12 +278,14 @@ ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel) .ike_keys = _ike_keys, .ike_updown = _ike_updown, .ike_rekey = _ike_rekey, + .ike_state_change = _ike_state_change, .message = _message_hook, }, .destroy = _destroy, }, .socket = socket, .tunnel = tunnel, + .cache = cache, ); return &this->public; diff --git a/src/libcharon/plugins/ha/ha_ike.h b/src/libcharon/plugins/ha/ha_ike.h index 9ba8f5574..b22cd6250 100644 --- a/src/libcharon/plugins/ha/ha_ike.h +++ b/src/libcharon/plugins/ha/ha_ike.h @@ -21,14 +21,15 @@ #ifndef HA_IKE_H_ #define HA_IKE_H_ +typedef struct ha_ike_t ha_ike_t; + #include "ha_socket.h" #include "ha_tunnel.h" #include "ha_segments.h" +#include "ha_cache.h" #include -typedef struct ha_ike_t ha_ike_t; - /** * Listener to synchronize IKE_SAs. */ @@ -50,8 +51,10 @@ struct ha_ike_t { * * @param socket socket to use for sending synchronization messages * @param tunnel tunnel securing sync messages, if any + * @param cache message cache * @return IKE listener */ -ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel); +ha_ike_t *ha_ike_create(ha_socket_t *socket, ha_tunnel_t *tunnel, + ha_cache_t *cache); #endif /** HA_IKE_ @}*/ diff --git a/src/libcharon/plugins/ha/ha_plugin.c b/src/libcharon/plugins/ha/ha_plugin.c index 70daca0bf..cfce45e47 100644 --- a/src/libcharon/plugins/ha/ha_plugin.c +++ b/src/libcharon/plugins/ha/ha_plugin.c @@ -21,6 +21,7 @@ #include "ha_dispatcher.h" #include "ha_segments.h" #include "ha_ctl.h" +#include "ha_cache.h" #include #include @@ -76,6 +77,11 @@ struct private_ha_plugin_t { * Segment control interface via FIFO */ ha_ctl_t *ctl; + + /** + * Message cache for resynchronization + */ + ha_cache_t *cache; }; METHOD(plugin_t, destroy, void, @@ -88,6 +94,7 @@ METHOD(plugin_t, destroy, void, this->ike->destroy(this->ike); this->child->destroy(this->child); this->dispatcher->destroy(this->dispatcher); + this->cache->destroy(this->cache); this->segments->destroy(this->segments); this->kernel->destroy(this->kernel); this->socket->destroy(this->socket); @@ -142,14 +149,16 @@ plugin_t *ha_plugin_create() } this->kernel = ha_kernel_create(count); this->segments = ha_segments_create(this->socket, this->kernel, this->tunnel, - count, strcmp(local, remote) > 0, monitor, resync); + count, strcmp(local, remote) > 0, monitor); + this->cache = ha_cache_create(this->kernel, this->socket, resync, count); if (fifo) { - this->ctl = ha_ctl_create(this->segments); + this->ctl = ha_ctl_create(this->segments, this->cache); } - this->dispatcher = ha_dispatcher_create(this->socket, this->segments); - this->ike = ha_ike_create(this->socket, this->tunnel); - this->child = ha_child_create(this->socket, this->tunnel); + this->dispatcher = ha_dispatcher_create(this->socket, this->segments, + this->cache); + this->ike = ha_ike_create(this->socket, this->tunnel, this->cache); + this->child = ha_child_create(this->socket, this->tunnel, this->cache); charon->bus->add_listener(charon->bus, &this->segments->listener); charon->bus->add_listener(charon->bus, &this->ike->listener); charon->bus->add_listener(charon->bus, &this->child->listener); diff --git a/src/libcharon/plugins/ha/ha_segments.c b/src/libcharon/plugins/ha/ha_segments.c index 5cec3c5b0..bdd850fc7 100644 --- a/src/libcharon/plugins/ha/ha_segments.c +++ b/src/libcharon/plugins/ha/ha_segments.c @@ -183,6 +183,7 @@ static void enable_disable(private_ha_segments_t *this, u_int segment, message = ha_message_create(type); message->add_attribute(message, HA_SEGMENT, segment); this->socket->push(this->socket, message); + message->destroy(message); } } @@ -221,115 +222,24 @@ METHOD(ha_segments_t, deactivate, void, enable_disable_all(this, segment, FALSE, notify); } -/** - * Rekey all children of an IKE_SA - */ -static status_t rekey_children(ike_sa_t *ike_sa) -{ - iterator_t *iterator; - child_sa_t *child_sa; - status_t status = SUCCESS; - - iterator = ike_sa->create_child_sa_iterator(ike_sa); - while (iterator->iterate(iterator, (void**)&child_sa)) - { - DBG1(DBG_CFG, "resyncing CHILD_SA"); - status = ike_sa->rekey_child_sa(ike_sa, child_sa->get_protocol(child_sa), - child_sa->get_spi(child_sa, TRUE)); - if (status == DESTROY_ME) - { - break; - } - } - iterator->destroy(iterator); - return status; -} - -METHOD(ha_segments_t, resync, void, - private_ha_segments_t *this, u_int segment) -{ - ike_sa_t *ike_sa; - enumerator_t *enumerator; - linked_list_t *list; - ike_sa_id_t *id; - - list = linked_list_create(); - this->mutex->lock(this->mutex); - - if (segment > 0 && segment <= this->count) - { - DBG1(DBG_CFG, "resyncing HA segment %d", segment); - - /* we do the actual rekeying in a seperate loop to avoid rekeying - * an SA twice. */ - enumerator = charon->ike_sa_manager->create_enumerator( - charon->ike_sa_manager); - while (enumerator->enumerate(enumerator, &ike_sa)) - { - if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && - this->kernel->get_segment(this->kernel, - ike_sa->get_other_host(ike_sa)) == segment) - { - id = ike_sa->get_id(ike_sa); - list->insert_last(list, id->clone(id)); - } - } - enumerator->destroy(enumerator); - } - this->mutex->unlock(this->mutex); - - while (list->remove_last(list, (void**)&id) == SUCCESS) - { - ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id); - id->destroy(id); - if (ike_sa) - { - DBG1(DBG_CFG, "resyncing IKE_SA"); - if (ike_sa->rekey(ike_sa) != DESTROY_ME) - { - if (rekey_children(ike_sa) != DESTROY_ME) - { - charon->ike_sa_manager->checkin( - charon->ike_sa_manager, ike_sa); - continue; - } - } - charon->ike_sa_manager->checkin_and_destroy( - charon->ike_sa_manager, ike_sa); - } - } - list->destroy(list); -} - METHOD(listener_t, alert_hook, bool, private_ha_segments_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args) { if (alert == ALERT_SHUTDOWN_SIGNAL) { - deactivate(this, 0, TRUE); + if (this->job) + { + DBG1(DBG_CFG, "HA heartbeat active, dropping all segments"); + deactivate(this, 0, TRUE); + } + else + { + DBG1(DBG_CFG, "no HA heartbeat active, closing IKE_SAs"); + } } return TRUE; } -/** - * Request a resync of all segments - */ -static job_requeue_t request_resync(private_ha_segments_t *this) -{ - ha_message_t *message; - int i; - - DBG1(DBG_CFG, "requesting HA resynchronization"); - - message = ha_message_create(HA_RESYNC); - for (i = 1; i <= this->count; i++) - { - message->add_attribute(message, HA_SEGMENT, i); - } - this->socket->push(this->socket, message); - return JOB_REQUEUE_NONE; -} - /** * Monitor heartbeat activity of remote node */ @@ -422,6 +332,7 @@ static job_requeue_t send_status(private_ha_segments_t *this) } this->socket->push(this->socket, message); + message->destroy(message); /* schedule next invocation */ charon->scheduler->schedule_job_ms(charon->scheduler, (job_t*) @@ -449,7 +360,7 @@ METHOD(ha_segments_t, destroy, void, */ ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel, ha_tunnel_t *tunnel, u_int count, u_int node, - bool monitor, bool sync) + bool monitor) { private_ha_segments_t *this; @@ -458,7 +369,6 @@ ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel, .listener.alert = _alert_hook, .activate = _activate, .deactivate = _deactivate, - .resync = _resync, .handle_status = _handle_status, .destroy = _destroy, }, @@ -477,14 +387,6 @@ ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel, start_watchdog(this); } - if (sync) - { - /* request a resync as soon as we are up */ - charon->scheduler->schedule_job(charon->scheduler, (job_t*) - callback_job_create((callback_job_cb_t)request_resync, - this, NULL, NULL), 2); - } - return &this->public; } diff --git a/src/libcharon/plugins/ha/ha_segments.h b/src/libcharon/plugins/ha/ha_segments.h index f6ce738ec..1699f7b0d 100644 --- a/src/libcharon/plugins/ha/ha_segments.h +++ b/src/libcharon/plugins/ha/ha_segments.h @@ -67,18 +67,6 @@ struct ha_segments_t { */ void (*deactivate)(ha_segments_t *this, u_int segment, bool notify); - /** - * Resync an active segment. - * - * To reintegrade a node into the cluster, resynchronization is reqired. - * IKE_SAs and CHILD_SAs are synced automatically during rekeying. A call - * to this method enforces a rekeying immediately sync all state of a - * segment. - * - * @param segment segment to resync - */ - void (*resync)(ha_segments_t *this, u_int segment); - /** * Handle a status message from the remote node. * @@ -101,11 +89,10 @@ struct ha_segments_t { * @param count number of segments the cluster uses * @param node node, currently 1 or 0 * @param monitor should we use monitoring functionality - * @param resync request a complete resync on startup * @return segment object */ ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel, ha_tunnel_t *tunnel, u_int count, u_int node, - bool monitor, bool resync); + bool monitor); #endif /** HA_SEGMENTS_ @}*/ diff --git a/src/libcharon/plugins/ha/ha_socket.c b/src/libcharon/plugins/ha/ha_socket.c index b30f3a37f..21e6eb6d5 100644 --- a/src/libcharon/plugins/ha/ha_socket.c +++ b/src/libcharon/plugins/ha/ha_socket.c @@ -58,8 +58,8 @@ struct private_ha_socket_t { * Data to pass to the send_message() callback job */ typedef struct { - ha_message_t *message; - private_ha_socket_t *this; + chunk_t chunk; + int fd; } job_data_t; /** @@ -67,7 +67,7 @@ typedef struct { */ static void job_data_destroy(job_data_t *this) { - this->message->destroy(this->message); + free(this->chunk.ptr); free(this); } @@ -76,12 +76,7 @@ static void job_data_destroy(job_data_t *this) */ static job_requeue_t send_message(job_data_t *data) { - private_ha_socket_t *this; - chunk_t chunk; - - this = data->this; - chunk = data->message->get_encoding(data->message); - if (send(this->fd, chunk.ptr, chunk.len, 0) < chunk.len) + if (send(data->fd, data->chunk.ptr, data->chunk.len, 0) < data->chunk.len) { DBG1(DBG_CFG, "pushing HA message failed: %s", strerror(errno)); } @@ -105,9 +100,10 @@ METHOD(ha_socket_t, push, void, /* Fallback to asynchronous transmission. This is required, as sendto() * is a blocking call if it acquires a policy. We could end up in a * deadlock, as we own an IKE_SA. */ - data = malloc_thing(job_data_t); - data->message = message; - data->this = this; + INIT(data, + .chunk = chunk_clone(chunk), + .fd = this->fd, + ); job = callback_job_create((callback_job_cb_t)send_message, data, (void*)job_data_destroy, NULL); @@ -116,7 +112,6 @@ METHOD(ha_socket_t, push, void, } DBG1(DBG_CFG, "pushing HA message failed: %s", strerror(errno)); } - message->destroy(message); } METHOD(ha_socket_t, pull, ha_message_t*, diff --git a/src/libcharon/plugins/ha/ha_socket.h b/src/libcharon/plugins/ha/ha_socket.h index 4155e26eb..a4789a51d 100644 --- a/src/libcharon/plugins/ha/ha_socket.h +++ b/src/libcharon/plugins/ha/ha_socket.h @@ -35,7 +35,7 @@ struct ha_socket_t { /** * Push synchronization information to the responsible node. * - * @param message message to send, gets destroyed by push() + * @param message message to send */ void (*push)(ha_socket_t *this, ha_message_t *message);