Use a sync message cache to resynchronize IKE_SAs without rekeying

This commit is contained in:
Martin Willi
2010-07-26 13:53:49 +02:00
parent 2031002d42
commit aa334daa9b
16 changed files with 595 additions and 170 deletions
+1
View File
@@ -17,6 +17,7 @@ libstrongswan_ha_la_SOURCES = \
ha_tunnel.h ha_tunnel.c \ ha_tunnel.h ha_tunnel.c \
ha_dispatcher.h ha_dispatcher.c \ ha_dispatcher.h ha_dispatcher.c \
ha_segments.h ha_segments.c \ ha_segments.h ha_segments.c \
ha_cache.h ha_cache.c \
ha_kernel.h ha_kernel.c \ ha_kernel.h ha_kernel.c \
ha_ctl.h ha_ctl.c \ ha_ctl.h ha_ctl.c \
ha_ike.h ha_ike.c \ ha_ike.h ha_ike.c \
+362
View File
@@ -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;
}
+78
View File
@@ -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_ @}*/
+10 -1
View File
@@ -36,6 +36,11 @@ struct private_ha_child_t {
* tunnel securing sync messages * tunnel securing sync messages
*/ */
ha_tunnel_t *tunnel; ha_tunnel_t *tunnel;
/**
* message cache
*/
ha_cache_t *cache;
}; };
METHOD(listener_t, child_keys, bool, METHOD(listener_t, child_keys, bool,
@@ -103,6 +108,7 @@ METHOD(listener_t, child_keys, bool,
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
this->socket->push(this->socket, m); this->socket->push(this->socket, m);
m->destroy(m);
return TRUE; return TRUE;
} }
@@ -133,6 +139,7 @@ METHOD(listener_t, child_state_change, bool,
m->add_attribute(m, HA_INBOUND_SPI, m->add_attribute(m, HA_INBOUND_SPI,
child_sa->get_spi(child_sa, TRUE)); child_sa->get_spi(child_sa, TRUE));
this->socket->push(this->socket, m); this->socket->push(this->socket, m);
m->destroy(m);
} }
return TRUE; return TRUE;
} }
@@ -146,7 +153,8 @@ METHOD(ha_child_t, destroy, void,
/** /**
* See header * 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; 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, .socket = socket,
.tunnel = tunnel, .tunnel = tunnel,
.cache = cache,
); );
return &this->public; return &this->public;
+6 -3
View File
@@ -21,14 +21,15 @@
#ifndef HA_CHILD_H_ #ifndef HA_CHILD_H_
#define HA_CHILD_H_ #define HA_CHILD_H_
typedef struct ha_child_t ha_child_t;
#include "ha_socket.h" #include "ha_socket.h"
#include "ha_tunnel.h" #include "ha_tunnel.h"
#include "ha_segments.h" #include "ha_segments.h"
#include "ha_cache.h"
#include <daemon.h> #include <daemon.h>
typedef struct ha_child_t ha_child_t;
/** /**
* Listener to synchronize CHILD_SAs. * Listener to synchronize CHILD_SAs.
*/ */
@@ -50,8 +51,10 @@ struct ha_child_t {
* *
* @param socket socket to use for sending synchronization messages * @param socket socket to use for sending synchronization messages
* @param tunnel tunnel securing sync messages, if any * @param tunnel tunnel securing sync messages, if any
* @param cache message resync cache
* @return CHILD listener * @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_ @}*/ #endif /** HA_CHILD_ @}*/
+8 -2
View File
@@ -44,6 +44,11 @@ struct private_ha_ctl_t {
*/ */
ha_segments_t *segments; ha_segments_t *segments;
/**
* Resynchronization message cache
*/
ha_cache_t *cache;
/** /**
* FIFO reader thread * 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); this->segments->deactivate(this->segments, segment, TRUE);
break; break;
case '*': case '*':
this->segments->resync(this->segments, segment); this->cache->resync(this->cache, segment);
break; break;
default: default:
break; break;
@@ -106,7 +111,7 @@ METHOD(ha_ctl_t, destroy, void,
/** /**
* See header * 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; private_ha_ctl_t *this;
@@ -115,6 +120,7 @@ ha_ctl_t *ha_ctl_create(ha_segments_t *segments)
.destroy = _destroy, .destroy = _destroy,
}, },
.segments = segments, .segments = segments,
.cache = cache,
); );
if (access(HA_FIFO, R_OK|W_OK) != 0) if (access(HA_FIFO, R_OK|W_OK) != 0)
+3 -1
View File
@@ -22,6 +22,7 @@
#define HA_CTL_H_ #define HA_CTL_H_
#include "ha_segments.h" #include "ha_segments.h"
#include "ha_cache.h"
typedef struct ha_ctl_t ha_ctl_t; typedef struct ha_ctl_t ha_ctl_t;
@@ -40,8 +41,9 @@ struct ha_ctl_t {
* Create a ha_ctl instance. * Create a ha_ctl instance.
* *
* @param segments segments to control * @param segments segments to control
* @param cache message cache for resynchronization
* @return HA control interface * @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_ @}*/ #endif /** HA_CTL_ @}*/
+58 -15
View File
@@ -40,6 +40,11 @@ struct private_ha_dispatcher_t {
*/ */
ha_segments_t *segments; ha_segments_t *segments;
/**
* Cache for resync
*/
ha_cache_t *cache;
/** /**
* Dispatcher job * Dispatcher job
*/ */
@@ -153,6 +158,8 @@ static void process_ike_add(private_ha_dispatcher_t *this, ha_message_t *message
old_sa = NULL; old_sa = NULL;
} }
ike_sa->set_state(ike_sa, IKE_CONNECTING); 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); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
} }
else 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); 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 && if (ike_sa->get_state(ike_sa) == IKE_CONNECTING &&
ike_sa->get_peer_cfg(ike_sa)) 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); 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); 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); 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); 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_attribute_t attribute;
ha_message_value_t value; ha_message_value_t value;
enumerator_t *enumerator; enumerator_t *enumerator;
ike_sa_t *ike_sa; ike_sa_t *ike_sa = NULL;
enumerator = message->create_attribute_enumerator(message); enumerator = message->create_attribute_enumerator(message);
while (enumerator->enumerate(enumerator, &attribute, &value)) while (enumerator->enumerate(enumerator, &attribute, &value))
@@ -341,17 +364,22 @@ static void process_ike_delete(private_ha_dispatcher_t *this,
case HA_IKE_ID: case HA_IKE_ID:
ike_sa = charon->ike_sa_manager->checkout( ike_sa = charon->ike_sa_manager->checkout(
charon->ike_sa_manager, value.ike_sa_id); 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; break;
default: default:
break; break;
} }
} }
enumerator->destroy(enumerator); 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) if (!ike_sa)
{ {
DBG1(DBG_CHD, "IKE_SA for HA CHILD_SA not found"); DBG1(DBG_CHD, "IKE_SA for HA CHILD_SA not found");
message->destroy(message);
return; return;
} }
config = find_child_cfg(ike_sa, config_name); 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"); DBG1(DBG_CHD, "HA is missing nodes child configuration");
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
message->destroy(message);
return; 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)); local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy));
remote_ts->destroy_offset(remote_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); charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
message->destroy(message);
return; 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); child_sa->add_policies(child_sa, local_ts, remote_ts);
local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy)); local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy));
remote_ts->destroy_offset(remote_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); child_sa->set_state(child_sa, CHILD_INSTALLED);
ike_sa->add_child_sa(ike_sa, child_sa); ike_sa->add_child_sa(ike_sa, child_sa);
message->destroy(message);
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); 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; ha_message_value_t value;
enumerator_t *enumerator; enumerator_t *enumerator;
ike_sa_t *ike_sa = NULL; ike_sa_t *ike_sa = NULL;
child_sa_t *child_sa;
u_int32_t spi = 0;
enumerator = message->create_attribute_enumerator(message); enumerator = message->create_attribute_enumerator(message);
while (enumerator->enumerate(enumerator, &attribute, &value)) while (enumerator->enumerate(enumerator, &attribute, &value))
@@ -591,20 +627,24 @@ static void process_child_delete(private_ha_dispatcher_t *this,
value.ike_sa_id); value.ike_sa_id);
break; break;
case HA_INBOUND_SPI: case HA_INBOUND_SPI:
if (ike_sa) spi = value.u32;
{
ike_sa->destroy_child_sa(ike_sa, PROTO_ESP, value.u32);
}
break; break;
default: default:
break; break;
} }
} }
enumerator->destroy(enumerator);
if (ike_sa) 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); 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); enumerator->destroy(enumerator);
message->destroy(message);
} }
/** /**
@@ -667,6 +708,7 @@ static void process_status(private_ha_dispatcher_t *this,
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
this->segments->handle_status(this->segments, mask); 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) switch (attribute)
{ {
case HA_SEGMENT: case HA_SEGMENT:
this->segments->resync(this->segments, value.u16); this->cache->resync(this->cache, value.u16);
break; break;
default: default:
break; break;
} }
} }
enumerator->destroy(enumerator); enumerator->destroy(enumerator);
message->destroy(message);
} }
/** /**
@@ -746,10 +789,9 @@ static job_requeue_t dispatch(private_ha_dispatcher_t *this)
break; break;
default: default:
DBG1(DBG_CFG, "received unknown HA message type %d", type); DBG1(DBG_CFG, "received unknown HA message type %d", type);
message->destroy(message);
break; break;
} }
message->destroy(message);
return JOB_REQUEUE_DIRECT; return JOB_REQUEUE_DIRECT;
} }
@@ -764,7 +806,7 @@ METHOD(ha_dispatcher_t, destroy, void,
* See header * See header
*/ */
ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket, 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; private_ha_dispatcher_t *this;
@@ -775,6 +817,7 @@ ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket,
}, },
.socket = socket, .socket = socket,
.segments = segments, .segments = segments,
.cache = cache,
); );
this->job = callback_job_create((callback_job_cb_t)dispatch, this->job = callback_job_create((callback_job_cb_t)dispatch,
this, NULL, NULL); this, NULL, NULL);
+3 -1
View File
@@ -23,6 +23,7 @@
#include "ha_socket.h" #include "ha_socket.h"
#include "ha_segments.h" #include "ha_segments.h"
#include "ha_cache.h"
typedef struct ha_dispatcher_t ha_dispatcher_t; typedef struct ha_dispatcher_t ha_dispatcher_t;
@@ -42,9 +43,10 @@ struct ha_dispatcher_t {
* *
* @param socket socket to pull messages from * @param socket socket to pull messages from
* @param segments segments to control based on received messages * @param segments segments to control based on received messages
* @param cache message cache to use for resynchronization
* @return dispatcher object * @return dispatcher object
*/ */
ha_dispatcher_t *ha_dispatcher_create(ha_socket_t *socket, 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_ @}*/ #endif /** HA_DISPATCHER_ @}*/
+24 -1
View File
@@ -36,6 +36,11 @@ struct private_ha_ike_t {
* tunnel securing sync messages * tunnel securing sync messages
*/ */
ha_tunnel_t *tunnel; ha_tunnel_t *tunnel;
/**
* message cache
*/
ha_cache_t *cache;
}; };
/** /**
@@ -117,6 +122,7 @@ METHOD(listener_t, ike_keys, bool,
chunk_clear(&secret); chunk_clear(&secret);
this->socket->push(this->socket, m); this->socket->push(this->socket, m);
this->cache->cache(this->cache, ike_sa, m);
return TRUE; 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)); m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa));
} }
this->socket->push(this->socket, m); this->socket->push(this->socket, m);
this->cache->cache(this->cache, ike_sa, m);
return TRUE; return TRUE;
} }
@@ -192,6 +199,17 @@ METHOD(listener_t, ike_rekey, bool,
return TRUE; 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, METHOD(listener_t, message_hook, bool,
private_ha_ike_t *this, ike_sa_t *ike_sa, message_t *message, bool incoming) 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_IKE_ID, ike_sa->get_id(ike_sa));
m->add_attribute(m, HA_MID, message->get_message_id(message) + 1); m->add_attribute(m, HA_MID, message->get_message_id(message) + 1);
this->socket->push(this->socket, m); this->socket->push(this->socket, m);
this->cache->cache(this->cache, ike_sa, m);
} }
if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED && if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
message->get_exchange_type(message) == IKE_AUTH && 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_IKE_ID, ike_sa->get_id(ike_sa));
m->add_attribute(m, HA_REMOTE_VIP, vip); m->add_attribute(m, HA_REMOTE_VIP, vip);
this->socket->push(this->socket, m); this->socket->push(this->socket, m);
this->cache->cache(this->cache, ike_sa, m);
} }
} }
return TRUE; return TRUE;
@@ -247,7 +267,8 @@ METHOD(ha_ike_t, destroy, void,
/** /**
* See header * 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; 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_keys = _ike_keys,
.ike_updown = _ike_updown, .ike_updown = _ike_updown,
.ike_rekey = _ike_rekey, .ike_rekey = _ike_rekey,
.ike_state_change = _ike_state_change,
.message = _message_hook, .message = _message_hook,
}, },
.destroy = _destroy, .destroy = _destroy,
}, },
.socket = socket, .socket = socket,
.tunnel = tunnel, .tunnel = tunnel,
.cache = cache,
); );
return &this->public; return &this->public;
+6 -3
View File
@@ -21,14 +21,15 @@
#ifndef HA_IKE_H_ #ifndef HA_IKE_H_
#define HA_IKE_H_ #define HA_IKE_H_
typedef struct ha_ike_t ha_ike_t;
#include "ha_socket.h" #include "ha_socket.h"
#include "ha_tunnel.h" #include "ha_tunnel.h"
#include "ha_segments.h" #include "ha_segments.h"
#include "ha_cache.h"
#include <daemon.h> #include <daemon.h>
typedef struct ha_ike_t ha_ike_t;
/** /**
* Listener to synchronize IKE_SAs. * Listener to synchronize IKE_SAs.
*/ */
@@ -50,8 +51,10 @@ struct ha_ike_t {
* *
* @param socket socket to use for sending synchronization messages * @param socket socket to use for sending synchronization messages
* @param tunnel tunnel securing sync messages, if any * @param tunnel tunnel securing sync messages, if any
* @param cache message cache
* @return IKE listener * @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_ @}*/ #endif /** HA_IKE_ @}*/
+14 -5
View File
@@ -21,6 +21,7 @@
#include "ha_dispatcher.h" #include "ha_dispatcher.h"
#include "ha_segments.h" #include "ha_segments.h"
#include "ha_ctl.h" #include "ha_ctl.h"
#include "ha_cache.h"
#include <daemon.h> #include <daemon.h>
#include <config/child_cfg.h> #include <config/child_cfg.h>
@@ -76,6 +77,11 @@ struct private_ha_plugin_t {
* Segment control interface via FIFO * Segment control interface via FIFO
*/ */
ha_ctl_t *ctl; ha_ctl_t *ctl;
/**
* Message cache for resynchronization
*/
ha_cache_t *cache;
}; };
METHOD(plugin_t, destroy, void, METHOD(plugin_t, destroy, void,
@@ -88,6 +94,7 @@ METHOD(plugin_t, destroy, void,
this->ike->destroy(this->ike); this->ike->destroy(this->ike);
this->child->destroy(this->child); this->child->destroy(this->child);
this->dispatcher->destroy(this->dispatcher); this->dispatcher->destroy(this->dispatcher);
this->cache->destroy(this->cache);
this->segments->destroy(this->segments); this->segments->destroy(this->segments);
this->kernel->destroy(this->kernel); this->kernel->destroy(this->kernel);
this->socket->destroy(this->socket); this->socket->destroy(this->socket);
@@ -142,14 +149,16 @@ plugin_t *ha_plugin_create()
} }
this->kernel = ha_kernel_create(count); this->kernel = ha_kernel_create(count);
this->segments = ha_segments_create(this->socket, this->kernel, this->tunnel, 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) 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->dispatcher = ha_dispatcher_create(this->socket, this->segments,
this->ike = ha_ike_create(this->socket, this->tunnel); this->cache);
this->child = ha_child_create(this->socket, this->tunnel); 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->segments->listener);
charon->bus->add_listener(charon->bus, &this->ike->listener); charon->bus->add_listener(charon->bus, &this->ike->listener);
charon->bus->add_listener(charon->bus, &this->child->listener); charon->bus->add_listener(charon->bus, &this->child->listener);
+12 -110
View File
@@ -183,6 +183,7 @@ static void enable_disable(private_ha_segments_t *this, u_int segment,
message = ha_message_create(type); message = ha_message_create(type);
message->add_attribute(message, HA_SEGMENT, segment); message->add_attribute(message, HA_SEGMENT, segment);
this->socket->push(this->socket, message); 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); 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, METHOD(listener_t, alert_hook, bool,
private_ha_segments_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args) private_ha_segments_t *this, ike_sa_t *ike_sa, alert_t alert, va_list args)
{ {
if (alert == ALERT_SHUTDOWN_SIGNAL) 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; 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 * 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); this->socket->push(this->socket, message);
message->destroy(message);
/* schedule next invocation */ /* schedule next invocation */
charon->scheduler->schedule_job_ms(charon->scheduler, (job_t*) 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_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel,
ha_tunnel_t *tunnel, u_int count, u_int node, ha_tunnel_t *tunnel, u_int count, u_int node,
bool monitor, bool sync) bool monitor)
{ {
private_ha_segments_t *this; 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, .listener.alert = _alert_hook,
.activate = _activate, .activate = _activate,
.deactivate = _deactivate, .deactivate = _deactivate,
.resync = _resync,
.handle_status = _handle_status, .handle_status = _handle_status,
.destroy = _destroy, .destroy = _destroy,
}, },
@@ -477,14 +387,6 @@ ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel,
start_watchdog(this); 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; return &this->public;
} }
+1 -14
View File
@@ -67,18 +67,6 @@ struct ha_segments_t {
*/ */
void (*deactivate)(ha_segments_t *this, u_int segment, bool notify); 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. * 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 count number of segments the cluster uses
* @param node node, currently 1 or 0 * @param node node, currently 1 or 0
* @param monitor should we use monitoring functionality * @param monitor should we use monitoring functionality
* @param resync request a complete resync on startup
* @return segment object * @return segment object
*/ */
ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel, ha_segments_t *ha_segments_create(ha_socket_t *socket, ha_kernel_t *kernel,
ha_tunnel_t *tunnel, u_int count, u_int node, ha_tunnel_t *tunnel, u_int count, u_int node,
bool monitor, bool resync); bool monitor);
#endif /** HA_SEGMENTS_ @}*/ #endif /** HA_SEGMENTS_ @}*/
+8 -13
View File
@@ -58,8 +58,8 @@ struct private_ha_socket_t {
* Data to pass to the send_message() callback job * Data to pass to the send_message() callback job
*/ */
typedef struct { typedef struct {
ha_message_t *message; chunk_t chunk;
private_ha_socket_t *this; int fd;
} job_data_t; } job_data_t;
/** /**
@@ -67,7 +67,7 @@ typedef struct {
*/ */
static void job_data_destroy(job_data_t *this) static void job_data_destroy(job_data_t *this)
{ {
this->message->destroy(this->message); free(this->chunk.ptr);
free(this); 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) static job_requeue_t send_message(job_data_t *data)
{ {
private_ha_socket_t *this; if (send(data->fd, data->chunk.ptr, data->chunk.len, 0) < data->chunk.len)
chunk_t chunk;
this = data->this;
chunk = data->message->get_encoding(data->message);
if (send(this->fd, chunk.ptr, chunk.len, 0) < chunk.len)
{ {
DBG1(DBG_CFG, "pushing HA message failed: %s", strerror(errno)); 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() /* Fallback to asynchronous transmission. This is required, as sendto()
* is a blocking call if it acquires a policy. We could end up in a * is a blocking call if it acquires a policy. We could end up in a
* deadlock, as we own an IKE_SA. */ * deadlock, as we own an IKE_SA. */
data = malloc_thing(job_data_t); INIT(data,
data->message = message; .chunk = chunk_clone(chunk),
data->this = this; .fd = this->fd,
);
job = callback_job_create((callback_job_cb_t)send_message, job = callback_job_create((callback_job_cb_t)send_message,
data, (void*)job_data_destroy, NULL); 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)); DBG1(DBG_CFG, "pushing HA message failed: %s", strerror(errno));
} }
message->destroy(message);
} }
METHOD(ha_socket_t, pull, ha_message_t*, METHOD(ha_socket_t, pull, ha_message_t*,
+1 -1
View File
@@ -35,7 +35,7 @@ struct ha_socket_t {
/** /**
* Push synchronization information to the responsible node. * 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); void (*push)(ha_socket_t *this, ha_message_t *message);