summaryrefslogtreecommitdiff
path: root/src/libcharon/plugins/unity/unity_handler.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/plugins/unity/unity_handler.c')
-rw-r--r--src/libcharon/plugins/unity/unity_handler.c433
1 files changed, 433 insertions, 0 deletions
diff --git a/src/libcharon/plugins/unity/unity_handler.c b/src/libcharon/plugins/unity/unity_handler.c
new file mode 100644
index 000000000..b2aeba605
--- /dev/null
+++ b/src/libcharon/plugins/unity/unity_handler.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2012 Martin Willi
+ * Copyright (C) 2012 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 "unity_handler.h"
+
+#include <daemon.h>
+#include <threading/mutex.h>
+#include <utils/linked_list.h>
+#include <processing/jobs/callback_job.h>
+
+typedef struct private_unity_handler_t private_unity_handler_t;
+
+/**
+ * Private data of an unity_handler_t object.
+ */
+struct private_unity_handler_t {
+
+ /**
+ * Public unity_handler_t interface.
+ */
+ unity_handler_t public;
+
+ /**
+ * List of subnets to include, as entry_t
+ */
+ linked_list_t *include;
+
+ /**
+ * Mutex for concurrent access to lists
+ */
+ mutex_t *mutex;
+};
+
+/**
+ * Traffic selector entry for networks to include under a given IKE_SA
+ */
+typedef struct {
+ /** associated IKE_SA, unique ID */
+ u_int32_t sa;
+ /** traffic selector to include/exclude */
+ traffic_selector_t *ts;
+} entry_t;
+
+/**
+ * Clean up an entry
+ */
+static void entry_destroy(entry_t *this)
+{
+ this->ts->destroy(this->ts);
+ free(this);
+}
+
+/**
+ * Create a traffic selector from a unity subnet definition
+ */
+static traffic_selector_t *create_ts(chunk_t subnet)
+{
+ chunk_t net, mask;
+ int i;
+
+ if (subnet.len != 8)
+ {
+ return NULL;
+ }
+ net = chunk_create(subnet.ptr, 4);
+ mask = chunk_clonea(chunk_skip(subnet, 4));
+ for (i = 0; i < net.len; i++)
+ {
+ mask.ptr[i] = (mask.ptr[i] ^ 0xFF) | net.ptr[i];
+ }
+ return traffic_selector_create_from_bytes(0, TS_IPV4_ADDR_RANGE,
+ net, 0, mask, 65535);
+}
+
+/**
+ * Store a subnet to include in tunnels under this IKE_SA
+ */
+static bool add_include(private_unity_handler_t *this, chunk_t subnet)
+{
+ traffic_selector_t *ts;
+ ike_sa_t *ike_sa;
+ entry_t *entry;
+
+ ike_sa = charon->bus->get_sa(charon->bus);
+ if (!ike_sa)
+ {
+ return FALSE;
+ }
+ ts = create_ts(subnet);
+ if (!ts)
+ {
+ return FALSE;
+ }
+ INIT(entry,
+ .sa = ike_sa->get_unique_id(ike_sa),
+ .ts = ts,
+ );
+
+ this->mutex->lock(this->mutex);
+ this->include->insert_last(this->include, entry);
+ this->mutex->unlock(this->mutex);
+ return TRUE;
+}
+
+/**
+ * Rempve a subnet from the inclusion list for this IKE_SA
+ */
+static bool remove_include(private_unity_handler_t *this, chunk_t subnet)
+{
+ enumerator_t *enumerator;
+ traffic_selector_t *ts;
+ ike_sa_t *ike_sa;
+ entry_t *entry;
+
+ ike_sa = charon->bus->get_sa(charon->bus);
+ if (!ike_sa)
+ {
+ return FALSE;
+ }
+ ts = create_ts(subnet);
+ if (!ts)
+ {
+ return FALSE;
+ }
+
+ this->mutex->lock(this->mutex);
+ enumerator = this->include->create_enumerator(this->include);
+ while (enumerator->enumerate(enumerator, &entry))
+ {
+ if (entry->sa == ike_sa->get_unique_id(ike_sa) &&
+ ts->equals(ts, entry->ts))
+ {
+ this->include->remove_at(this->include, enumerator);
+ entry_destroy(entry);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->mutex->unlock(this->mutex);
+ ts->destroy(ts);
+ return TRUE;
+}
+
+/**
+ * Create a unique shunt name for a bypass policy
+ */
+static void create_shunt_name(ike_sa_t *ike_sa, traffic_selector_t *ts,
+ char *buf, size_t len)
+{
+ snprintf(buf, len, "Unity (%s[%u]: %R)", ike_sa->get_name(ike_sa),
+ ike_sa->get_unique_id(ike_sa), ts);
+}
+
+/**
+ * Install entry as a shunt policy
+ */
+static job_requeue_t add_exclude_async(entry_t *entry)
+{
+ enumerator_t *enumerator;
+ child_cfg_t *child_cfg;
+ lifetime_cfg_t lft = {};
+ ike_sa_t *ike_sa;
+ char name[128];
+ host_t *host;
+ bool has_vip = FALSE;
+
+ ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
+ entry->sa, FALSE);
+ if (ike_sa)
+ {
+ create_shunt_name(ike_sa, entry->ts, name, sizeof(name));
+
+ child_cfg = child_cfg_create(name, &lft, NULL, TRUE, MODE_PASS,
+ ACTION_NONE, ACTION_NONE, ACTION_NONE,
+ FALSE, 0, 0, NULL, NULL, FALSE);
+ child_cfg->add_traffic_selector(child_cfg, FALSE,
+ entry->ts->clone(entry->ts));
+ enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ has_vip = TRUE;
+ child_cfg->add_traffic_selector(child_cfg, TRUE,
+ traffic_selector_create_from_subnet(host->clone(host), 32, 0, 0));
+ }
+ enumerator->destroy(enumerator);
+
+ if (!has_vip)
+ {
+ host = ike_sa->get_my_host(ike_sa);
+ child_cfg->add_traffic_selector(child_cfg, TRUE,
+ traffic_selector_create_from_subnet(host->clone(host), 32, 0, 0));
+ }
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+
+ charon->shunts->install(charon->shunts, child_cfg);
+ child_cfg->destroy(child_cfg);
+
+ DBG1(DBG_IKE, "installed %N bypass policy for %R",
+ configuration_attribute_type_names, UNITY_LOCAL_LAN, entry->ts);
+ }
+ return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Add a bypass policy for a given subnet
+ */
+static bool add_exclude(private_unity_handler_t *this, chunk_t subnet)
+{
+ traffic_selector_t *ts;
+ ike_sa_t *ike_sa;
+ entry_t *entry;
+
+ ike_sa = charon->bus->get_sa(charon->bus);
+ if (!ike_sa)
+ {
+ return FALSE;
+ }
+ ts = create_ts(subnet);
+ if (!ts)
+ {
+ return FALSE;
+ }
+ INIT(entry,
+ .sa = ike_sa->get_unique_id(ike_sa),
+ .ts = ts,
+ );
+
+ /* we can't install the shunt policy yet, as we don't know the virtual IP.
+ * Defer installation using an async callback. */
+ lib->processor->queue_job(lib->processor, (job_t*)
+ callback_job_create((void*)add_exclude_async, entry,
+ (void*)entry_destroy, NULL));
+ return TRUE;
+}
+
+/**
+ * Remove a bypass policy for a given subnet
+ */
+static bool remove_exclude(private_unity_handler_t *this, chunk_t subnet)
+{
+ traffic_selector_t *ts;
+ ike_sa_t *ike_sa;
+ char name[128];
+
+ ike_sa = charon->bus->get_sa(charon->bus);
+ if (!ike_sa)
+ {
+ return FALSE;
+ }
+ ts = create_ts(subnet);
+ if (!ts)
+ {
+ return FALSE;
+ }
+ create_shunt_name(ike_sa, ts, name, sizeof(name));
+ DBG1(DBG_IKE, "uninstalling %N bypass policy for %R",
+ configuration_attribute_type_names, UNITY_LOCAL_LAN, ts);
+ ts->destroy(ts);
+ return charon->shunts->uninstall(charon->shunts, name);
+}
+
+METHOD(attribute_handler_t, handle, bool,
+ private_unity_handler_t *this, identification_t *id,
+ configuration_attribute_type_t type, chunk_t data)
+{
+ switch (type)
+ {
+ case UNITY_SPLIT_INCLUDE:
+ return add_include(this, data);
+ case UNITY_LOCAL_LAN:
+ return add_exclude(this, data);
+ default:
+ return FALSE;
+ }
+}
+
+METHOD(attribute_handler_t, release, void,
+ private_unity_handler_t *this, identification_t *server,
+ configuration_attribute_type_t type, chunk_t data)
+{
+ switch (type)
+ {
+ case UNITY_SPLIT_INCLUDE:
+ remove_include(this, data);
+ break;
+ case UNITY_LOCAL_LAN:
+ remove_exclude(this, data);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Configuration attributes to request
+ */
+static configuration_attribute_type_t attributes[] = {
+ UNITY_SPLIT_INCLUDE,
+ UNITY_LOCAL_LAN,
+};
+
+/**
+ * Attribute enumerator implementation
+ */
+typedef struct {
+ /** implements enumerator_t */
+ enumerator_t public;
+ /** position in attributes[] */
+ int i;
+} attribute_enumerator_t;
+
+METHOD(enumerator_t, enumerate_attributes, bool,
+ attribute_enumerator_t *this, configuration_attribute_type_t *type,
+ chunk_t *data)
+{
+ if (this->i < countof(attributes))
+ {
+ *type = attributes[this->i++];
+ *data = chunk_empty;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *,
+ unity_handler_t *this, identification_t *id, linked_list_t *vips)
+{
+ attribute_enumerator_t *enumerator;
+ ike_sa_t *ike_sa;
+
+ ike_sa = charon->bus->get_sa(charon->bus);
+ if (!ike_sa || ike_sa->get_version(ike_sa) != IKEV1 ||
+ !ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY))
+ {
+ return enumerator_create_empty();
+ }
+ INIT(enumerator,
+ .public = {
+ .enumerate = (void*)_enumerate_attributes,
+ .destroy = (void*)free,
+ },
+ );
+ return &enumerator->public;
+}
+
+typedef struct {
+ /** mutex to unlock */
+ mutex_t *mutex;
+ /** IKE_SA ID to filter for */
+ u_int32_t id;
+} include_filter_t;
+
+/**
+ * Include enumerator filter function
+ */
+static bool include_filter(include_filter_t *data,
+ entry_t **entry, traffic_selector_t **ts)
+{
+ if ((*entry)->sa == data->id)
+ {
+ *ts = (*entry)->ts;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Destroy include filter data, unlock mutex
+ */
+static void destroy_filter(include_filter_t *data)
+{
+ data->mutex->unlock(data->mutex);
+ free(data);
+}
+
+METHOD(unity_handler_t, create_include_enumerator, enumerator_t*,
+ private_unity_handler_t *this, u_int32_t id)
+{
+ include_filter_t *data;
+
+ INIT(data,
+ .mutex = this->mutex,
+ .id = id,
+ );
+ data->mutex->lock(data->mutex);
+ return enumerator_create_filter(
+ this->include->create_enumerator(this->include),
+ (void*)include_filter, data, (void*)destroy_filter);
+}
+
+METHOD(unity_handler_t, destroy, void,
+ private_unity_handler_t *this)
+{
+ this->include->destroy(this->include);
+ this->mutex->destroy(this->mutex);
+ free(this);
+}
+
+/**
+ * See header
+ */
+unity_handler_t *unity_handler_create()
+{
+ private_unity_handler_t *this;
+
+ INIT(this,
+ .public = {
+ .handler = {
+ .handle = _handle,
+ .release = _release,
+ .create_attribute_enumerator = _create_attribute_enumerator,
+ },
+ .create_include_enumerator = _create_include_enumerator,
+ .destroy = _destroy,
+ },
+ .include = linked_list_create(),
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ );
+
+ return &this->public;
+}