aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Willi <martin@revosec.ch>2012-09-18 17:22:30 +0200
committerMartin Willi <martin@revosec.ch>2012-09-18 17:22:47 +0200
commita69bc12a3ac16835f28b333dcf0e5b43aab77f45 (patch)
treef39db3ce8fd2293a7f662cc111b2fba39643a56b
parent56ea95195acbb432ee7ab2f03883725408789e97 (diff)
parent995a9c8a0a00d52b2e5008433f22485b2f1bcf01 (diff)
downloadstrongswan-a69bc12a3ac16835f28b333dcf0e5b43aab77f45.tar.bz2
strongswan-a69bc12a3ac16835f28b333dcf0e5b43aab77f45.tar.xz
Merge branch 'unity'
Add Cisco Unity extension support implemented in a dedicated plugin.
-rw-r--r--NEWS7
-rw-r--r--configure.in4
-rw-r--r--man/ipsec.conf.5.in5
-rw-r--r--src/libcharon/Makefile.am7
-rw-r--r--src/libcharon/plugins/unity/Makefile.am19
-rw-r--r--src/libcharon/plugins/unity/unity_handler.c433
-rw-r--r--src/libcharon/plugins/unity/unity_handler.h58
-rw-r--r--src/libcharon/plugins/unity/unity_narrow.c171
-rw-r--r--src/libcharon/plugins/unity/unity_narrow.h51
-rw-r--r--src/libcharon/plugins/unity/unity_plugin.c96
-rw-r--r--src/libcharon/plugins/unity/unity_plugin.h42
-rw-r--r--src/libcharon/plugins/unity/unity_provider.c175
-rw-r--r--src/libcharon/plugins/unity/unity_provider.h49
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c5
-rwxr-xr-xtesting/scripts/build-umlrootfs5
-rwxr-xr-xtesting/testing.conf1
-rw-r--r--testing/tests/ikev1/rw-cert-unity/description.txt6
-rw-r--r--testing/tests/ikev1/rw-cert-unity/evaltest.dat8
-rw-r--r--testing/tests/ikev1/rw-cert-unity/hosts/carol/etc/ipsec.conf20
-rw-r--r--testing/tests/ikev1/rw-cert-unity/hosts/carol/etc/strongswan.conf14
-rw-r--r--testing/tests/ikev1/rw-cert-unity/hosts/moon/etc/ipsec.conf19
-rw-r--r--testing/tests/ikev1/rw-cert-unity/hosts/moon/etc/strongswan.conf19
-rw-r--r--testing/tests/ikev1/rw-cert-unity/posttest.dat2
-rw-r--r--testing/tests/ikev1/rw-cert-unity/pretest.dat4
-rw-r--r--testing/tests/ikev1/rw-cert-unity/test.conf21
25 files changed, 1234 insertions, 7 deletions
diff --git a/NEWS b/NEWS
index 6d11762dc..7b2b6f3e3 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,13 @@ strongswan-5.0.1
PAM directly anymore, but can use any XAuth backend to verify credentials,
including xauth-pam.
+- The new unity plugin brings support for some parts of the IKEv1 Cisco Unity
+ Extension. As client, charon narrows traffic selectors to the received
+ Split-Include attributes and automatically installs IPsec bypass policies
+ for received Local-LAN attributes. As server, charon sends Split-Include
+ attributes for leftsubnet definitions containing multiple subnets to Unity-
+ aware clients.
+
- An EAP-Nak payload is returned by clients if the gateway requests an EAP
method that the client does not support. Clients can also request a specific
EAP method by configuring that method with leftauth.
diff --git a/configure.in b/configure.in
index 7a76efa9c..03dc5ac65 100644
--- a/configure.in
+++ b/configure.in
@@ -206,6 +206,7 @@ ARG_ENABL_SET([ctr], [enables the Counter Mode wrapper crypto plugin.
ARG_ENABL_SET([ccm], [enables the CCM AEAD wrapper crypto plugin.])
ARG_ENABL_SET([gcm], [enables the GCM AEAD wrapper crypto plugin.])
ARG_ENABL_SET([addrblock], [enables RFC 3779 address block constraint support.])
+ARG_ENABL_SET([unity], [enables Cisco Unity extension plugin.])
ARG_ENABL_SET([uci], [enable OpenWRT UCI configuration plugin.])
ARG_ENABL_SET([android], [enable Android specific plugin.])
ARG_ENABL_SET([android-log], [enable Android specific logger plugin.])
@@ -953,6 +954,7 @@ ADD_PLUGIN([radattr], [c charon])
ADD_PLUGIN([maemo], [c charon])
ADD_PLUGIN([uci], [c charon])
ADD_PLUGIN([addrblock], [c charon])
+ADD_PLUGIN([unity], [c charon])
ADD_PLUGIN([unit-tester], [c charon])
AC_SUBST(charon_plugins)
@@ -1078,6 +1080,7 @@ AM_CONDITIONAL(USE_SOCKET_DEFAULT, test x$socket_default = xtrue)
AM_CONDITIONAL(USE_SOCKET_DYNAMIC, test x$socket_dynamic = xtrue)
AM_CONDITIONAL(USE_FARP, test x$farp = xtrue)
AM_CONDITIONAL(USE_ADDRBLOCK, test x$addrblock = xtrue)
+AM_CONDITIONAL(USE_UNITY, test x$unity = xtrue)
dnl hydra plugins
dnl =============
@@ -1257,6 +1260,7 @@ AC_OUTPUT(
src/libcharon/plugins/medsrv/Makefile
src/libcharon/plugins/medcli/Makefile
src/libcharon/plugins/addrblock/Makefile
+ src/libcharon/plugins/unity/Makefile
src/libcharon/plugins/uci/Makefile
src/libcharon/plugins/ha/Makefile
src/libcharon/plugins/whitelist/Makefile
diff --git a/man/ipsec.conf.5.in b/man/ipsec.conf.5.in
index ea935b6c3..f4d7ed1d6 100644
--- a/man/ipsec.conf.5.in
+++ b/man/ipsec.conf.5.in
@@ -756,8 +756,9 @@ signifying that the left end of the connection goes to the left participant
only. Configured subnets of the peers may differ, the protocol narrows it to
the greatest common subnet. In IKEv1, this may lead to problems with other
implementations, make sure to configure identical subnets in such
-configurations. IKEv2 supports multiple subnets separated by commas, IKEv1 only
-interprets the first subnet of such a definition.
+configurations. IKEv2 supports multiple subnets separated by commas. IKEv1 only
+interprets the first subnet of such a definition, unless the Cisco Unity
+extension plugin is enabled.
.TP
.BR leftupdown " = <path>"
what ``updown'' script to run to adjust routing and/or firewalling
diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am
index bc9a6f28c..56192bf0e 100644
--- a/src/libcharon/Makefile.am
+++ b/src/libcharon/Makefile.am
@@ -533,6 +533,13 @@ if MONOLITHIC
endif
endif
+if USE_UNITY
+ SUBDIRS += plugins/unity
+if MONOLITHIC
+ libcharon_la_LIBADD += plugins/unity/libstrongswan-unity.la
+endif
+endif
+
if USE_UNIT_TESTS
SUBDIRS += plugins/unit_tester
if MONOLITHIC
diff --git a/src/libcharon/plugins/unity/Makefile.am b/src/libcharon/plugins/unity/Makefile.am
new file mode 100644
index 000000000..b23143fd6
--- /dev/null
+++ b/src/libcharon/plugins/unity/Makefile.am
@@ -0,0 +1,19 @@
+
+INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/libhydra \
+ -I$(top_srcdir)/src/libcharon
+
+AM_CFLAGS = -rdynamic
+
+if MONOLITHIC
+noinst_LTLIBRARIES = libstrongswan-unity.la
+else
+plugin_LTLIBRARIES = libstrongswan-unity.la
+endif
+
+libstrongswan_unity_la_SOURCES = \
+ unity_plugin.h unity_plugin.c \
+ unity_handler.h unity_handler.c \
+ unity_narrow.h unity_narrow.c \
+ unity_provider.h unity_provider.c
+
+libstrongswan_unity_la_LDFLAGS = -module -avoid-version
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;
+}
diff --git a/src/libcharon/plugins/unity/unity_handler.h b/src/libcharon/plugins/unity/unity_handler.h
new file mode 100644
index 000000000..8656fd372
--- /dev/null
+++ b/src/libcharon/plugins/unity/unity_handler.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup unity_handler unity_handler
+ * @{ @ingroup unity
+ */
+
+#ifndef UNITY_HANDLER_H_
+#define UNITY_HANDLER_H_
+
+#include <attributes/attribute_handler.h>
+
+typedef struct unity_handler_t unity_handler_t;
+
+/**
+ * Cisco Unity attribute handling.
+ */
+struct unity_handler_t {
+
+ /**
+ * Implements attribute_handler_t.
+ */
+ attribute_handler_t handler;
+
+ /**
+ * Create an enumerator over Split-Include attributes received for an IKE_SA.
+ *
+ * @param id IKE_SA unique ID to get Split-Includes for
+ * @return enumerator over traffic_selector_t*
+ */
+ enumerator_t* (*create_include_enumerator)(unity_handler_t *this,
+ u_int32_t id);
+
+ /**
+ * Destroy a unity_handler_t.
+ */
+ void (*destroy)(unity_handler_t *this);
+};
+
+/**
+ * Create a unity_handler instance.
+ */
+unity_handler_t *unity_handler_create();
+
+#endif /** UNITY_HANDLER_H_ @}*/
diff --git a/src/libcharon/plugins/unity/unity_narrow.c b/src/libcharon/plugins/unity/unity_narrow.c
new file mode 100644
index 000000000..56de0028f
--- /dev/null
+++ b/src/libcharon/plugins/unity/unity_narrow.c
@@ -0,0 +1,171 @@
+/*
+ * 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_narrow.h"
+
+#include <daemon.h>
+
+typedef struct private_unity_narrow_t private_unity_narrow_t;
+
+/**
+ * Private data of an unity_narrow_t object.
+ */
+struct private_unity_narrow_t {
+
+ /**
+ * Public unity_narrow_t interface.
+ */
+ unity_narrow_t public;
+
+ /**
+ * Unity attribute handler
+ */
+ unity_handler_t *handler;
+};
+
+/**
+ * Narrow TS as initiator to Unity Split-Include/Local-LAN
+ */
+static void narrow_initiator(private_unity_narrow_t *this, ike_sa_t *ike_sa,
+ child_cfg_t *cfg, linked_list_t *remote)
+{
+ traffic_selector_t *current, *orig = NULL;
+ linked_list_t *received, *selected;
+ enumerator_t *enumerator;
+
+ enumerator = this->handler->create_include_enumerator(this->handler,
+ ike_sa->get_unique_id(ike_sa));
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ if (orig == NULL)
+ { /* got one, replace original TS */
+ if (remote->remove_first(remote, (void**)&orig) != SUCCESS)
+ {
+ break;
+ }
+ }
+ /* narrow received Unity TS with the child configuration */
+ received = linked_list_create();
+ received->insert_last(received, current);
+ selected = cfg->get_traffic_selectors(cfg, FALSE, received, NULL);
+ while (selected->remove_first(selected, (void**)&current) == SUCCESS)
+ {
+ remote->insert_last(remote, current);
+ }
+ selected->destroy(selected);
+ received->destroy(received);
+ }
+ enumerator->destroy(enumerator);
+ if (orig)
+ {
+ DBG1(DBG_CFG, "narrowed CHILD_SA to %N %#R",
+ configuration_attribute_type_names,
+ UNITY_SPLIT_INCLUDE, remote);
+ orig->destroy(orig);
+ }
+}
+
+/**
+ * As initiator, bump up TS to 0.0.0.0/0 for on-the-wire bits
+ */
+static void narrow_initiator_pre(linked_list_t *list)
+{
+ traffic_selector_t *ts;
+
+ while (list->remove_first(list, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE,
+ "0.0.0.0", 0,
+ "255.255.255.255", 65535);
+ if (ts)
+ {
+ list->insert_last(list, ts);
+ }
+}
+
+/**
+ * As responder, narrow down TS to configuration for installation
+ */
+static void narrow_responder_post(child_cfg_t *child_cfg, linked_list_t *local)
+{
+ traffic_selector_t *ts;
+ linked_list_t *configured;
+
+ while (local->remove_first(local, (void**)&ts) == SUCCESS)
+ {
+ ts->destroy(ts);
+ }
+ configured = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL);
+
+ while (configured->remove_first(configured, (void**)&ts) == SUCCESS)
+ {
+ local->insert_last(local, ts);
+ }
+ configured->destroy(configured);
+}
+
+METHOD(listener_t, narrow, bool,
+ private_unity_narrow_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
+ narrow_hook_t type, linked_list_t *local, linked_list_t *remote)
+{
+ if (ike_sa->get_version(ike_sa) == IKEV1 &&
+ ike_sa->supports_extension(ike_sa, EXT_CISCO_UNITY))
+ {
+ switch (type)
+ {
+ case NARROW_INITIATOR_PRE_AUTH:
+ narrow_initiator_pre(remote);
+ break;
+ case NARROW_INITIATOR_POST_AUTH:
+ narrow_initiator(this, ike_sa,
+ child_sa->get_config(child_sa), remote);
+ break;
+ case NARROW_RESPONDER_POST:
+ narrow_responder_post(child_sa->get_config(child_sa), local);
+ break;
+ default:
+ break;
+ }
+ }
+ return TRUE;
+}
+
+METHOD(unity_narrow_t, destroy, void,
+ private_unity_narrow_t *this)
+{
+ free(this);
+}
+
+/**
+ * See header
+ */
+unity_narrow_t *unity_narrow_create(unity_handler_t *handler)
+{
+ private_unity_narrow_t *this;
+
+ INIT(this,
+ .public = {
+ .listener = {
+ .narrow = _narrow,
+ },
+ .destroy = _destroy,
+ },
+ .handler = handler,
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/plugins/unity/unity_narrow.h b/src/libcharon/plugins/unity/unity_narrow.h
new file mode 100644
index 000000000..5e0968518
--- /dev/null
+++ b/src/libcharon/plugins/unity/unity_narrow.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup unity_narrow unity_narrow
+ * @{ @ingroup unity
+ */
+
+#ifndef UNITY_NARROW_H_
+#define UNITY_NARROW_H_
+
+#include <bus/listeners/listener.h>
+
+#include "unity_handler.h"
+
+typedef struct unity_narrow_t unity_narrow_t;
+
+/**
+ * Listener that narrows Quick Modes to the Unity Split-Include subnets.
+ */
+struct unity_narrow_t {
+
+ /**
+ * Implements listener_t.
+ */
+ listener_t listener;
+
+ /**
+ * Destroy a unity_narrow_t.
+ */
+ void (*destroy)(unity_narrow_t *this);
+};
+
+/**
+ * Create a unity_narrow instance.
+ */
+unity_narrow_t *unity_narrow_create(unity_handler_t *handler);
+
+#endif /** UNITY_NARROW_H_ @}*/
diff --git a/src/libcharon/plugins/unity/unity_plugin.c b/src/libcharon/plugins/unity/unity_plugin.c
new file mode 100644
index 000000000..9e21bd9ed
--- /dev/null
+++ b/src/libcharon/plugins/unity/unity_plugin.c
@@ -0,0 +1,96 @@
+/*
+ * 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_plugin.h"
+#include "unity_handler.h"
+#include "unity_narrow.h"
+#include "unity_provider.h"
+
+#include <daemon.h>
+#include <hydra.h>
+
+typedef struct private_unity_plugin_t private_unity_plugin_t;
+
+/**
+ * private data of unity_plugin
+ */
+struct private_unity_plugin_t {
+
+ /**
+ * public functions
+ */
+ unity_plugin_t public;
+
+ /**
+ * Handler for UNITY configuration attributes
+ */
+ unity_handler_t *handler;
+
+ /**
+ * Responder Unity configuration attribute provider
+ */
+ unity_provider_t *provider;
+
+ /**
+ * Traffic selector narrower, for Unity Split-Includes
+ */
+ unity_narrow_t *narrower;
+};
+
+METHOD(plugin_t, get_name, char*,
+ private_unity_plugin_t *this)
+{
+ return "unity";
+}
+
+METHOD(plugin_t, destroy, void,
+ private_unity_plugin_t *this)
+{
+ charon->bus->remove_listener(charon->bus, &this->narrower->listener);
+ this->narrower->destroy(this->narrower);
+ hydra->attributes->remove_handler(hydra->attributes, &this->handler->handler);
+ hydra->attributes->remove_provider(hydra->attributes,
+ &this->provider->provider);
+ this->handler->destroy(this->handler);
+ this->provider->destroy(this->provider);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *unity_plugin_create()
+{
+ private_unity_plugin_t *this;
+
+ INIT(this,
+ .public = {
+ .plugin = {
+ .get_name = _get_name,
+ .reload = (void*)return_false,
+ .destroy = _destroy,
+ },
+ },
+ .handler = unity_handler_create(),
+ .provider = unity_provider_create(),
+ );
+ hydra->attributes->add_handler(hydra->attributes, &this->handler->handler);
+ hydra->attributes->add_provider(hydra->attributes, &this->provider->provider);
+
+ this->narrower = unity_narrow_create(this->handler),
+ charon->bus->add_listener(charon->bus, &this->narrower->listener);
+
+ return &this->public.plugin;
+}
diff --git a/src/libcharon/plugins/unity/unity_plugin.h b/src/libcharon/plugins/unity/unity_plugin.h
new file mode 100644
index 000000000..0d407b561
--- /dev/null
+++ b/src/libcharon/plugins/unity/unity_plugin.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup unity unity
+ * @ingroup cplugins
+ *
+ * @defgroup unity_plugin unity_plugin
+ * @{ @ingroup unity
+ */
+
+#ifndef UNITY_PLUGIN_H_
+#define UNITY_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct unity_plugin_t unity_plugin_t;
+
+/**
+ * IKEv1 Cisco Unity extension support.
+ */
+struct unity_plugin_t {
+
+ /**
+ * Implements plugin_t. interface.
+ */
+ plugin_t plugin;
+};
+
+#endif /** UNITY_PLUGIN_H_ @}*/
diff --git a/src/libcharon/plugins/unity/unity_provider.c b/src/libcharon/plugins/unity/unity_provider.c
new file mode 100644
index 000000000..c7feb090c
--- /dev/null
+++ b/src/libcharon/plugins/unity/unity_provider.c
@@ -0,0 +1,175 @@
+/*
+ * 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_provider.h"
+
+#include <daemon.h>
+
+typedef struct private_unity_provider_t private_unity_provider_t;
+
+/**
+ * Private data of an unity_provider_t object.
+ */
+struct private_unity_provider_t {
+
+ /**
+ * Public unity_provider_t interface.
+ */
+ unity_provider_t public;
+};
+
+/**
+ * Attribute enumerator for traffic selector list
+ */
+typedef struct {
+ /** Implements enumerator_t */
+ enumerator_t public;
+ /** list of traffic selectors to enumerate */
+ linked_list_t *list;
+ /** currently enumerating subnet */
+ u_char subnet[4];
+ /** currently enumerating subnet mask */
+ u_char mask[4];
+} attribute_enumerator_t;
+
+METHOD(enumerator_t, attribute_enumerate, bool,
+ attribute_enumerator_t *this, configuration_attribute_type_t *type,
+ chunk_t *attr)
+{
+ traffic_selector_t *ts;
+ u_int8_t i, mask;
+ host_t *net;
+
+ while (TRUE)
+ {
+ if (this->list->remove_first(this->list, (void**)&ts) != SUCCESS)
+ {
+ return FALSE;
+ }
+ if (ts->get_type(ts) == TS_IPV4_ADDR_RANGE &&
+ ts->to_subnet(ts, &net, &mask))
+ {
+ ts->destroy(ts);
+ break;
+ }
+ ts->destroy(ts);
+ }
+
+ memset(this->mask, 0, sizeof(this->mask));
+ for (i = 0; i < sizeof(this->mask); i++)
+ {
+ if (mask < 8)
+ {
+ this->mask[i] = 0xFF << (8 - mask);
+ break;
+ }
+ this->mask[i] = 0xFF;
+ mask -= 8;
+ }
+ memcpy(this->subnet, net->get_address(net).ptr, sizeof(this->subnet));
+ net->destroy(net);
+
+ *type = UNITY_SPLIT_INCLUDE;
+ *attr = chunk_create(this->subnet, sizeof(this->subnet) + sizeof(this->mask));
+
+ return TRUE;
+}
+
+METHOD(enumerator_t, attribute_destroy, void,
+ attribute_enumerator_t *this)
+{
+ this->list->destroy_offset(this->list, offsetof(traffic_selector_t, destroy));
+ free(this);
+}
+
+METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
+ private_unity_provider_t *this, linked_list_t *pools, identification_t *id,
+ linked_list_t *vips)
+{
+ attribute_enumerator_t *attr_enum;
+ enumerator_t *enumerator;
+ linked_list_t *list, *current;
+ traffic_selector_t *ts;
+ ike_sa_t *ike_sa;
+ peer_cfg_t *peer_cfg;
+ child_cfg_t *child_cfg;
+
+ 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) ||
+ !vips->get_count(vips))
+ {
+ return NULL;
+ }
+
+ list = linked_list_create();
+ peer_cfg = ike_sa->get_peer_cfg(ike_sa);
+ enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
+ while (enumerator->enumerate(enumerator, &child_cfg))
+ {
+ current = child_cfg->get_traffic_selectors(child_cfg, TRUE, NULL, NULL);
+ while (current->remove_first(current, (void**)&ts) == SUCCESS)
+ {
+ list->insert_last(list, ts);
+ }
+ current->destroy(current);
+ }
+ enumerator->destroy(enumerator);
+
+ if (list->get_count(list) == 0)
+ {
+ list->destroy(list);
+ return NULL;
+ }
+ DBG1(DBG_CFG, "sending %N: %#R",
+ configuration_attribute_type_names, UNITY_SPLIT_INCLUDE, list);
+
+ INIT(attr_enum,
+ .public = {
+ .enumerate = (void*)_attribute_enumerate,
+ .destroy = _attribute_destroy,
+ },
+ .list = list,
+ );
+
+ return &attr_enum->public;
+}
+
+METHOD(unity_provider_t, destroy, void,
+ private_unity_provider_t *this)
+{
+ free(this);
+}
+
+/**
+ * See header
+ */
+unity_provider_t *unity_provider_create()
+{
+ private_unity_provider_t *this;
+
+ INIT(this,
+ .public = {
+ .provider = {
+ .acquire_address = (void*)return_null,
+ .release_address = (void*)return_false,
+ .create_attribute_enumerator = _create_attribute_enumerator,
+ },
+ .destroy = _destroy,
+ },
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/plugins/unity/unity_provider.h b/src/libcharon/plugins/unity/unity_provider.h
new file mode 100644
index 000000000..a25df5df0
--- /dev/null
+++ b/src/libcharon/plugins/unity/unity_provider.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+/**
+ * @defgroup unity_provider unity_provider
+ * @{ @ingroup unity
+ */
+
+#ifndef UNITY_PROVIDER_H_
+#define UNITY_PROVIDER_H_
+
+typedef struct unity_provider_t unity_provider_t;
+
+#include <attributes/attribute_provider.h>
+
+/**
+ * Cisco Unity extension attribute provider.
+ */
+struct unity_provider_t {
+
+ /**
+ * Implements attribute_provier_t interface.
+ */
+ attribute_provider_t provider;
+
+ /**
+ * Destroy a unity_provider_t.
+ */
+ void (*destroy)(unity_provider_t *this);
+};
+
+/**
+ * Create a unity_provider instance.
+ */
+unity_provider_t *unity_provider_create();
+
+#endif /** UNITY_PROVIDER_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
index b0f9fa1a0..82a7238c3 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
@@ -460,11 +460,6 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local,
hosts->destroy(hosts);
if (list->get_first(list, (void**)&ts) == SUCCESS)
{
- if (this->initiator && list->get_count(list) > 1)
- {
- DBG1(DBG_IKE, "configuration has more than one %s traffic selector,"
- " using first only", local ? "local" : "remote");
- }
ts = ts->clone(ts);
}
else
diff --git a/testing/scripts/build-umlrootfs b/testing/scripts/build-umlrootfs
index 60def11ef..92595222c 100755
--- a/testing/scripts/build-umlrootfs
+++ b/testing/scripts/build-umlrootfs
@@ -392,6 +392,11 @@ then
echo -n " --enable-cisco-quirks" >> $INSTALLSHELL
fi
+if [ "$USE_UNITY" = "yes" ]
+then
+ echo -n " --enable-unity" >> $INSTALLSHELL
+fi
+
echo "" >> $INSTALLSHELL
echo "make -j" >> $INSTALLSHELL
echo "make install" >> $INSTALLSHELL
diff --git a/testing/testing.conf b/testing/testing.conf
index 0401cc8d7..d0c918860 100755
--- a/testing/testing.conf
+++ b/testing/testing.conf
@@ -87,6 +87,7 @@ USE_XAUTH_EAP="yes"
USE_PKCS8="yes"
USE_IFMAP="no"
USE_CISCO_QUIRKS="no"
+USE_UNITY="yes"
# Gentoo linux root filesystem
ROOTFS=$UMLTESTDIR/gentoo-fs-20111212.tar.bz2
diff --git a/testing/tests/ikev1/rw-cert-unity/description.txt b/testing/tests/ikev1/rw-cert-unity/description.txt
new file mode 100644
index 000000000..5e887c7f4
--- /dev/null
+++ b/testing/tests/ikev1/rw-cert-unity/description.txt
@@ -0,0 +1,6 @@
+The roadwarrior <b>carol</b> sets up a connection to gateway <b>moon</b>.
+The authentication is based on <b>X.509 certificates</b>. <b>carol</b>
+requests a virtual IP using <b>leftsourceip=%config</b> and indicates
+support for the Cisco Unity extension. Gateway <b>moon</b> responds with
+two Split-Include subnets configured in the <b>leftsubnet</b> definition and a
+global Local-LAN exclude option defined in strongswan.conf.
diff --git a/testing/tests/ikev1/rw-cert-unity/evaltest.dat b/testing/tests/ikev1/rw-cert-unity/evaltest.dat
new file mode 100644
index 000000000..b6a860b86
--- /dev/null
+++ b/testing/tests/ikev1/rw-cert-unity/evaltest.dat
@@ -0,0 +1,8 @@
+carol::ipsec status 2> /dev/null::home.*ESTABLISHED.*carol@strongswan.org.*moon.strongswan.org::YES
+moon:: ipsec status 2> /dev/null::rw\[1]: ESTABLISHED.*moon.strongswan.org.*carol@strongswan.org::YES
+carol::ipsec status 2> /dev/null::10.2.1.1/32 === 192.168.0.0/24 PASS::YES
+carol::ipsec status 2> /dev/null::home.*10.2.1.1/32 === 10.1.0.0/16 10.2.1.0/24::YES
+moon:: ipsec status 2> /dev/null::rw[{]1}.*10.1.0.0/16 10.2.1.0/24 === 10.2.1.1/32::YES
+carol::ping -c 1 PH_IP_ALICE::64 bytes from PH_IP_ALICE: icmp_seq=1::YES
+moon::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES
+moon::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES
diff --git a/testing/tests/ikev1/rw-cert-unity/hosts/carol/etc/ipsec.conf b/testing/tests/ikev1/rw-cert-unity/hosts/carol/etc/ipsec.conf
new file mode 100644
index 000000000..bad62811b
--- /dev/null
+++ b/testing/tests/ikev1/rw-cert-unity/hosts/carol/etc/ipsec.conf
@@ -0,0 +1,20 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+
+conn %default
+ ikelifetime=60m
+ keylife=20m
+ rekeymargin=3m
+ keyingtries=1
+ keyexchange=ikev1
+
+conn home
+ left=PH_IP_CAROL
+ leftcert=carolCert.pem
+ leftid=carol@strongswan.org
+ leftsourceip=%config
+ right=PH_IP_MOON
+ rightid=@moon.strongswan.org
+ rightsubnet=0.0.0.0/0
+ auto=add
diff --git a/testing/tests/ikev1/rw-cert-unity/hosts/carol/etc/strongswan.conf b/testing/tests/ikev1/rw-cert-unity/hosts/carol/etc/strongswan.conf
new file mode 100644
index 000000000..68e244025
--- /dev/null
+++ b/testing/tests/ikev1/rw-cert-unity/hosts/carol/etc/strongswan.conf
@@ -0,0 +1,14 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+ load = curl test-vectors aes des sha1 sha2 md5 pem pkcs1 pkcs8 gmp random nonce x509 revocation hmac xcbc ctr ccm gcm stroke kernel-netlink socket-default unity
+ cisco_unity = yes
+}
+
+libstrongswan {
+ dh_exponent_ansi_x9_42 = no
+ integrity_test = yes
+ crypto_test {
+ on_add = yes
+ }
+}
diff --git a/testing/tests/ikev1/rw-cert-unity/hosts/moon/etc/ipsec.conf b/testing/tests/ikev1/rw-cert-unity/hosts/moon/etc/ipsec.conf
new file mode 100644
index 000000000..ee8ee9093
--- /dev/null
+++ b/testing/tests/ikev1/rw-cert-unity/hosts/moon/etc/ipsec.conf
@@ -0,0 +1,19 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+
+conn %default
+ ikelifetime=60m
+ keylife=20m
+ rekeymargin=3m
+ keyingtries=1
+ keyexchange=ikev1
+
+conn rw
+ left=PH_IP_MOON
+ leftcert=moonCert.pem
+ leftid=@moon.strongswan.org
+ leftsubnet=10.1.0.0/16,10.2.1.0/24
+ right=%any
+ rightsourceip=10.2.1.0/24
+ auto=add
diff --git a/testing/tests/ikev1/rw-cert-unity/hosts/moon/etc/strongswan.conf b/testing/tests/ikev1/rw-cert-unity/hosts/moon/etc/strongswan.conf
new file mode 100644
index 000000000..3164176b2
--- /dev/null
+++ b/testing/tests/ikev1/rw-cert-unity/hosts/moon/etc/strongswan.conf
@@ -0,0 +1,19 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+ load = curl test-vectors aes des sha1 sha2 md5 pem pkcs1 pkcs8 gmp random nonce x509 revocation hmac xcbc ctr ccm gcm stroke kernel-netlink socket-default attr unity
+ cisco_unity = yes
+ plugins {
+ attr {
+ split-exclude = 192.168.0.0/24
+ }
+ }
+}
+
+libstrongswan {
+ dh_exponent_ansi_x9_42 = no
+ integrity_test = yes
+ crypto_test {
+ on_add = yes
+ }
+}
diff --git a/testing/tests/ikev1/rw-cert-unity/posttest.dat b/testing/tests/ikev1/rw-cert-unity/posttest.dat
new file mode 100644
index 000000000..c6d6235f9
--- /dev/null
+++ b/testing/tests/ikev1/rw-cert-unity/posttest.dat
@@ -0,0 +1,2 @@
+moon::ipsec stop
+carol::ipsec stop
diff --git a/testing/tests/ikev1/rw-cert-unity/pretest.dat b/testing/tests/ikev1/rw-cert-unity/pretest.dat
new file mode 100644
index 000000000..4fbe475bf
--- /dev/null
+++ b/testing/tests/ikev1/rw-cert-unity/pretest.dat
@@ -0,0 +1,4 @@
+moon::ipsec start
+carol::ipsec start
+carol::sleep 1
+carol::ipsec up home
diff --git a/testing/tests/ikev1/rw-cert-unity/test.conf b/testing/tests/ikev1/rw-cert-unity/test.conf
new file mode 100644
index 000000000..25cff970f
--- /dev/null
+++ b/testing/tests/ikev1/rw-cert-unity/test.conf
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# This configuration file provides information on the
+# UML instances used for this test
+
+# All UML instances that are required for this test
+#
+UMLHOSTS="alice moon carol winnetou"
+
+# Corresponding block diagram
+#
+DIAGRAM="a-m-c.png"
+
+# UML instances on which tcpdump is to be started
+#
+TCPDUMPHOSTS="moon"
+
+# UML instances on which IPsec is started
+# Used for IPsec logging purposes
+#
+IPSECHOSTS="moon carol"