aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMartin Willi <martin@revosec.ch>2010-07-22 18:54:35 +0200
committerMartin Willi <martin@revosec.ch>2010-07-26 13:53:49 +0200
commitaa334daa9bf3f87b4c8f0565a6f56d67da651316 (patch)
treed946c9eac831bc404fe66d94aade2f67de209a18 /src
parent2031002d426a94af3fc362053f36313f721c5489 (diff)
downloadstrongswan-aa334daa9bf3f87b4c8f0565a6f56d67da651316.tar.bz2
strongswan-aa334daa9bf3f87b4c8f0565a6f56d67da651316.tar.xz
Use a sync message cache to resynchronize IKE_SAs without rekeying
Diffstat (limited to 'src')
-rw-r--r--src/libcharon/plugins/ha/Makefile.am1
-rw-r--r--src/libcharon/plugins/ha/ha_cache.c362
-rw-r--r--src/libcharon/plugins/ha/ha_cache.h78
-rw-r--r--src/libcharon/plugins/ha/ha_child.c11
-rw-r--r--src/libcharon/plugins/ha/ha_child.h9
-rw-r--r--src/libcharon/plugins/ha/ha_ctl.c10
-rw-r--r--src/libcharon/plugins/ha/ha_ctl.h4
-rw-r--r--src/libcharon/plugins/ha/ha_dispatcher.c73
-rw-r--r--src/libcharon/plugins/ha/ha_dispatcher.h4
-rw-r--r--src/libcharon/plugins/ha/ha_ike.c25
-rw-r--r--src/libcharon/plugins/ha/ha_ike.h9
-rw-r--r--src/libcharon/plugins/ha/ha_plugin.c19
-rw-r--r--src/libcharon/plugins/ha/ha_segments.c120
-rw-r--r--src/libcharon/plugins/ha/ha_segments.h15
-rw-r--r--src/libcharon/plugins/ha/ha_socket.c21
-rw-r--r--src/libcharon/plugins/ha/ha_socket.h2
16 files changed, 594 insertions, 169 deletions
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 <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 "ha_cache.h"
+
+#include <utils/hashtable.h>
+#include <utils/linked_list.h>
+#include <threading/mutex.h>
+#include <processing/jobs/callback_job.h>
+
+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 <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.
+ */
+
+/**
+ * @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 <utils/enumerator.h>
+
+#include <sa/ike_sa.h>
+
+/**
+ * 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 <daemon.h>
-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
@@ -45,6 +45,11 @@ struct private_ha_ctl_t {
ha_segments_t *segments;
/**
+ * Resynchronization message cache
+ */
+ ha_cache_t *cache;
+
+ /**
* FIFO reader thread
*/
callback_job_t *job;
@@ -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
@@ -41,6 +41,11 @@ struct private_ha_dispatcher_t {
ha_segments_t *segments;
/**
+ * Cache for resync
+ */
+ ha_cache_t *cache;
+
+ /**
* Dispatcher job
*/
callback_job_t *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 <daemon.h>
-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 <daemon.h>
#include <config/child_cfg.h>
@@ -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,116 +222,25 @@ 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)
+METHOD(listener_t, alert_hook, bool,
+ private_ha_segments_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args)
{
- 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))
+ if (alert == ALERT_SHUTDOWN_SIGNAL)
{
- 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)
+ if (this->job)
{
- break;
+ DBG1(DBG_CFG, "HA heartbeat active, dropping all segments");
+ deactivate(this, 0, TRUE);
}
- }
- 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))
+ else
{
- 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));
- }
+ DBG1(DBG_CFG, "no HA heartbeat active, closing IKE_SAs");
}
- 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);
}
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
*/
static job_requeue_t watchdog(private_ha_segments_t *this)
@@ -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
@@ -68,18 +68,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.
*
* @param mask segments the remote node is serving actively
@@ -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);