Use a sync message cache to resynchronize IKE_SAs without rekeying

This commit is contained in:
Martin Willi 2010-07-22 18:54:35 +02:00
parent 2031002d42
commit aa334daa9b
16 changed files with 595 additions and 170 deletions

View File

@ -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 \

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;
}

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_ @}*/

View File

@ -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;

View File

@ -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_ @}*/

View File

@ -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)

View File

@ -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_ @}*/

View File

@ -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);

View File

@ -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_ @}*/

View File

@ -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;

View File

@ -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_ @}*/

View File

@ -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);

View File

@ -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;
}

View File

@ -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_ @}*/

View File

@ -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*,

View File

@ -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);