summaryrefslogtreecommitdiff
path: root/src/libcharon/plugins/ha/ha_cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/plugins/ha/ha_cache.c')
-rw-r--r--src/libcharon/plugins/ha/ha_cache.c362
1 files changed, 362 insertions, 0 deletions
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;
+}