/*
- * Copyright (C) 2008 Martin Willi
+ * Copyright (C) 2008-2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
#include <daemon.h>
#include <utils/host.h>
+#include <processing/jobs/callback_job.h>
typedef struct private_ha_sync_socket_t private_ha_sync_socket_t;
+typedef struct ha_backend_t ha_backend_t;
+typedef struct ha_creds_t ha_creds_t;
+
+/**
+ * Serves credentials for the HA sync SA
+ */
+struct ha_creds_t {
+
+ /**
+ * Implements credential_set_t
+ */
+ credential_set_t public;
+
+ /**
+ * own identity
+ */
+ identification_t *local;
+
+ /**
+ * peer identity
+ */
+ identification_t *remote;
+
+ /**
+ * Shared key to serve
+ */
+ shared_key_t *key;
+};
+
+/**
+ * Serves configurations for the HA sync SA
+ */
+struct ha_backend_t {
+
+ /**
+ * Implements backend_t
+ */
+ backend_t public;
+
+ /**
+ * peer config we serve
+ */
+ peer_cfg_t *cfg;
+};
/**
* Private data of an ha_sync_socket_t object.
* UDP communication socket fd
*/
int fd;
+
+ /**
+ * remote host to receive/send to
+ */
+ host_t *remote;
+
+ /**
+ * Reqid of installed trap
+ */
+ u_int32_t trap;
+
+ /**
+ * backend for sync SA
+ */
+ ha_backend_t backend;
+
+ /**
+ * credential set for sync SA
+ */
+ ha_creds_t creds;
};
+/**
+ * Data to pass to the send_message() callback job
+ */
+typedef struct {
+ ha_sync_message_t *message;
+ private_ha_sync_socket_t *this;
+} job_data_t;
+
+/**
+ * Cleanup job data
+ */
+static void job_data_destroy(job_data_t *this)
+{
+ this->message->destroy(this->message);
+ free(this);
+}
+
+/**
+ * Callback to asynchronously send messages
+ */
+static job_requeue_t send_message(job_data_t *data)
+{
+ private_ha_sync_socket_t *this;
+ chunk_t chunk;
+
+ this = data->this;
+ chunk = data->message->get_encoding(data->message);
+ if (sendto(this->fd, chunk.ptr, chunk.len, 0,
+ this->remote->get_sockaddr(this->remote),
+ *this->remote->get_sockaddr_len(this->remote)) < chunk.len)
+ {
+ DBG1(DBG_CFG, "pushing HA sync message failed: %s", strerror(errno));
+ }
+ return JOB_REQUEUE_NONE;
+}
+
/**
* Implementation of ha_sync_socket_t.push
*/
static void push(private_ha_sync_socket_t *this, ha_sync_message_t *message)
{
- chunk_t data;
+ if (this->trap)
+ {
+ callback_job_t *job;
+ job_data_t *data;
- data = message->get_encoding(message);
- if (send(this->fd, data.ptr, data.len, 0) < data.len)
+ data = malloc_thing(job_data_t);
+ data->message = message;
+ data->this = this;
+
+ /* we send sync message asynchronously. This is required, as sendto()
+ * is a blocking call if it acquires a policy. Otherwise we could
+ * end up in a deadlock, as we own an IKE_SA. */
+ job = callback_job_create((callback_job_cb_t)send_message,
+ data, (void*)job_data_destroy, NULL);
+ charon->processor->queue_job(charon->processor, (job_t*)job);
+ }
+ else
{
- DBG1(DBG_CFG, "pushing HA sync message failed: %s", strerror(errno));
+ job_data_t data;
+
+ data.message = message;
+ data.this = this;
+ send_message(&data);
+ message->destroy(message);
}
}
ssize_t len;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
- len = recv(this->fd, buf, sizeof(buf), 0);
+ len = recvfrom(this->fd, buf, sizeof(buf), 0,
+ this->remote->get_sockaddr(this->remote),
+ this->remote->get_sockaddr_len(this->remote));
pthread_setcancelstate(oldstate, NULL);
if (len <= 0)
{
}
}
+/**
+ * Implementation of ha_sync_socket_t.is_sync_sa
+ */
+static bool is_sync_sa(private_ha_sync_socket_t *this, ike_sa_t *ike_sa)
+{
+ peer_cfg_t *cfg = this->backend.cfg;
+
+ return cfg && ike_sa->get_ike_cfg(ike_sa) == cfg->get_ike_cfg(cfg);
+}
+
+/**
+ * Enumerator over HA shared_key
+ */
+typedef struct {
+ /** Implements enumerator_t */
+ enumerator_t public;
+ /** a single secret we serve */
+ shared_key_t *key;
+} shared_enum_t;
+
+/**
+ * Implementation of shared_enum_t.enumerate
+ */
+static bool shared_enumerate(shared_enum_t *this, shared_key_t **key,
+ id_match_t *me, id_match_t *other)
+{
+ if (this->key)
+ {
+ if (me)
+ {
+ *me = ID_MATCH_PERFECT;
+ }
+ if (other)
+ {
+ *other = ID_MATCH_PERFECT;
+ }
+ *key = this->key;
+ this->key = NULL;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Implements ha_creds_t.create_shared_enumerator
+ */
+static enumerator_t* create_shared_enumerator(ha_creds_t *this,
+ shared_key_type_t type, identification_t *me,
+ identification_t *other)
+{
+ shared_enum_t *enumerator;
+
+ if (type != SHARED_IKE && type != SHARED_ANY)
+ {
+ return NULL;
+ }
+ if (me && !me->equals(me, this->local))
+ {
+ return NULL;
+ }
+ if (other && !other->equals(other, this->remote))
+ {
+ return NULL;
+ }
+
+ enumerator = malloc_thing(shared_enum_t);
+ enumerator->public.enumerate = (void*)shared_enumerate;
+ enumerator->public.destroy = (void*)free;
+ enumerator->key = this->key;
+
+ return &enumerator->public;
+}
+
+/**
+ * Implementation of backend_t.create_peer_cfg_enumerator.
+ */
+static enumerator_t* create_peer_cfg_enumerator(ha_backend_t *this,
+ identification_t *me, identification_t *other)
+{
+ return enumerator_create_single(this->cfg, NULL);
+}
+
+/**
+ * Implementation of backend_t.create_ike_cfg_enumerator.
+ */
+static enumerator_t* create_ike_cfg_enumerator(ha_backend_t *this,
+ host_t *me, host_t *other)
+{
+ return enumerator_create_single(this->cfg->get_ike_cfg(this->cfg), NULL);
+}
+
+/**
+ * Install configs and a a trap for secured sync
+ */
+static void setup_sync_tunnel(private_ha_sync_socket_t *this)
+{
+ char *local, *remote, *secret;
+ peer_cfg_t *peer_cfg;
+ ike_cfg_t *ike_cfg;
+ auth_cfg_t *auth_cfg;
+ child_cfg_t *child_cfg;
+ traffic_selector_t *ts;
+
+ secret = lib->settings->get_str(lib->settings,
+ "charon.plugins.ha_sync.secret", NULL);
+ if (!secret)
+ {
+ DBG1(DBG_CFG, "no HA sync secret defined, using unencrypted sync");
+ return;
+ }
+ local = lib->settings->get_str(lib->settings,
+ "charon.plugins.ha_sync.local", NULL);
+ remote = lib->settings->get_str(lib->settings,
+ "charon.plugins.ha_sync.remote", NULL);
+
+ /* setup credentials */
+ this->creds.key = shared_key_create(SHARED_IKE,
+ chunk_clone(chunk_create(secret, strlen(secret))));
+ this->creds.local = identification_create_from_string(local);
+ this->creds.remote = identification_create_from_string(remote);
+ this->creds.public.create_private_enumerator = (void*)return_null;
+ this->creds.public.create_cert_enumerator = (void*)return_null;
+ this->creds.public.create_shared_enumerator = (void*)create_shared_enumerator;
+ this->creds.public.create_cdp_enumerator = (void*)return_null;
+ this->creds.public.cache_cert = (void*)nop;
+
+ charon->credentials->add_set(charon->credentials, &this->creds.public);
+
+ /* create config and backend */
+ ike_cfg = ike_cfg_create(FALSE, FALSE, local, remote);
+ ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
+ peer_cfg = peer_cfg_create("ha-sync", 2, ike_cfg, CERT_NEVER_SEND,
+ UNIQUE_KEEP, 0, 86400, 0, 7200, 3600, FALSE, 30,
+ NULL, NULL, FALSE, NULL, NULL);
+
+ auth_cfg = auth_cfg_create();
+ auth_cfg->add(auth_cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK);
+ auth_cfg->add(auth_cfg, AUTH_RULE_IDENTITY,
+ identification_create_from_string(local));
+ peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, TRUE);
+
+ auth_cfg = auth_cfg_create();
+ auth_cfg->add(auth_cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK);
+ auth_cfg->add(auth_cfg, AUTH_RULE_IDENTITY,
+ identification_create_from_string(remote));
+ peer_cfg->add_auth_cfg(peer_cfg, auth_cfg, FALSE);
+
+ child_cfg = child_cfg_create("ha-sync", 0, 21600, 1200, FALSE, TRUE,
+ MODE_TRANSPORT, ACTION_NONE, ACTION_NONE, FALSE);
+ ts = traffic_selector_create_dynamic(0, HA_SYNC_PORT, HA_SYNC_PORT);
+ child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
+ ts = traffic_selector_create_dynamic(0, HA_SYNC_PORT, HA_SYNC_PORT);
+ child_cfg->add_traffic_selector(child_cfg, FALSE, ts);
+ child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
+ peer_cfg->add_child_cfg(peer_cfg, child_cfg);
+
+ this->backend.cfg = peer_cfg;
+ this->backend.public.create_peer_cfg_enumerator = (void*)create_peer_cfg_enumerator;
+ this->backend.public.create_ike_cfg_enumerator = (void*)create_ike_cfg_enumerator;
+ this->backend.public.get_peer_cfg_by_name = (void*)return_null;
+
+ charon->backends->add_backend(charon->backends, &this->backend.public);
+
+ /* install an acquiring trap */
+ this->trap = charon->traps->install(charon->traps, peer_cfg, child_cfg);
+}
+
/**
* read local/remote node address from config
*/
*/
static bool open_socket(private_ha_sync_socket_t *this)
{
- host_t *local, *remote;
- bool success = TRUE;
+ host_t *local;
local = get_host_config("local");
- remote = get_host_config("remote");
- if (!local || !remote)
+ if (!local)
{
- DESTROY_IF(local);
- DESTROY_IF(remote);
return FALSE;
}
this->fd = socket(local->get_family(local), SOCK_DGRAM, 0);
if (!this->fd)
{
- DESTROY_IF(local);
- DESTROY_IF(remote);
+ local->destroy(local);
DBG1(DBG_CFG, "opening HA sync socket failed: %s", strerror(errno));
return FALSE;
}
{
DBG1(DBG_CFG, "binding HA sync socket failed: %s", strerror(errno));
close(this->fd);
- success = FALSE;
- }
- if (connect(this->fd, remote->get_sockaddr(remote),
- *remote->get_sockaddr_len(remote)) == -1)
- {
- DBG1(DBG_CFG, "connecting HA sync socket failed: %s", strerror(errno));
- close(this->fd);
- success = FALSE;
+ local->destroy(local);
+ return FALSE;
}
local->destroy(local);
- remote->destroy(remote);
- return success;
+ return TRUE;
}
/**
static void destroy(private_ha_sync_socket_t *this)
{
close(this->fd);
+ if (this->backend.cfg)
+ {
+ charon->backends->remove_backend(charon->backends, &this->backend.public);
+ this->backend.cfg->destroy(this->backend.cfg);
+ }
+ if (this->creds.key)
+ {
+ charon->credentials->remove_set(charon->credentials, &this->creds.public);
+ this->creds.key->destroy(this->creds.key);
+ }
+ DESTROY_IF(this->creds.local);
+ DESTROY_IF(this->creds.remote);
+ DESTROY_IF(this->remote);
+ if (this->trap)
+ {
+ charon->traps->uninstall(charon->traps, this->trap);
+ }
free(this);
}
this->public.push = (void(*)(ha_sync_socket_t*, ha_sync_message_t*))push;
this->public.pull = (ha_sync_message_t*(*)(ha_sync_socket_t*))pull;
+ this->public.is_sync_sa = (bool(*)(ha_sync_socket_t*, ike_sa_t *ike_sa))is_sync_sa;
this->public.destroy = (void(*)(ha_sync_socket_t*))destroy;
+ this->remote = get_host_config("remote");
+ if (!this->remote)
+ {
+ free(this);
+ return NULL;
+ }
+ this->trap = 0;
+ this->creds.key = NULL;
+ this->creds.local = NULL;
+ this->creds.remote = NULL;
+ this->backend.cfg = NULL;
+
+ setup_sync_tunnel(this);
+
if (!open_socket(this))
{
free(this);
return NULL;
}
+
return &this->public;
}