Merge branch 'ike-redirect'
authorTobias Brunner <tobias@strongswan.org>
Fri, 4 Mar 2016 15:03:07 +0000 (16:03 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 4 Mar 2016 15:03:07 +0000 (16:03 +0100)
This adds support for IKEv2 redirection (RFC 5685).  There is currently
no default implementation of the redirect_provider_t interface provided.
Plugins may implement the interface to decide if and when to redirect
connecting clients.  It is also possible to redirect established IKE_SAs
via VICI/swanctl.

50 files changed:
conf/options/charon.opt
src/libcharon/Android.mk
src/libcharon/Makefile.am
src/libcharon/daemon.c
src/libcharon/daemon.h
src/libcharon/plugins/vici/README.md
src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Session.pm
src/libcharon/plugins/vici/python/vici/session.py
src/libcharon/plugins/vici/ruby/lib/vici.rb
src/libcharon/plugins/vici/vici_control.c
src/libcharon/processing/jobs/redirect_job.c [new file with mode: 0644]
src/libcharon/processing/jobs/redirect_job.h [new file with mode: 0644]
src/libcharon/sa/ike_sa.c
src/libcharon/sa/ike_sa.h
src/libcharon/sa/ikev2/task_manager_v2.c
src/libcharon/sa/ikev2/tasks/child_create.c
src/libcharon/sa/ikev2/tasks/ike_auth.c
src/libcharon/sa/ikev2/tasks/ike_config.c
src/libcharon/sa/ikev2/tasks/ike_init.c
src/libcharon/sa/ikev2/tasks/ike_redirect.c [new file with mode: 0644]
src/libcharon/sa/ikev2/tasks/ike_redirect.h [new file with mode: 0644]
src/libcharon/sa/redirect_manager.c [new file with mode: 0644]
src/libcharon/sa/redirect_manager.h [new file with mode: 0644]
src/libcharon/sa/redirect_provider.h [new file with mode: 0644]
src/libcharon/sa/task.c
src/libcharon/sa/task.h
src/swanctl/Makefile.am
src/swanctl/command.h
src/swanctl/commands/redirect.c [new file with mode: 0644]
src/swanctl/swanctl.8.in
testing/tests/ikev2/redirect-active/description.txt [new file with mode: 0644]
testing/tests/ikev2/redirect-active/evaltest.dat [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.conf [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/certs/marsCert.pem [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/private/marsKey.pem [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.secrets [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/alice/etc/iptables.rules [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/alice/etc/strongswan.conf [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/carol/etc/ipsec.conf [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/carol/etc/strongswan.conf [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/dave/etc/ipsec.conf [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/dave/etc/strongswan.conf [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.conf [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/certs/marsCert.pem [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/private/marsKey.pem [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.secrets [new file with mode: 0644]
testing/tests/ikev2/redirect-active/hosts/moon/etc/strongswan.conf [new file with mode: 0644]
testing/tests/ikev2/redirect-active/posttest.dat [new file with mode: 0644]
testing/tests/ikev2/redirect-active/pretest.dat [new file with mode: 0644]
testing/tests/ikev2/redirect-active/test.conf [new file with mode: 0644]

index 3820036..86279ec 100644 (file)
@@ -97,6 +97,9 @@ charon.flush_auth_cfg = no
        this might conflict with plugins that later need access to e.g. the used
        certificates.
 
+charon.follow_redirects = yes
+       Whether to follow IKEv2 redirects (RFC 5685).
+
 charon.fragment_size = 0
        Maximum size (complete IP datagram size in bytes) of a sent IKE fragment
        when using proprietary IKEv1 or standardized IKEv2 fragmentation (0 for
index 844bbfd..0b09d10 100644 (file)
@@ -59,6 +59,7 @@ processing/jobs/delete_child_sa_job.c processing/jobs/delete_child_sa_job.h \
 processing/jobs/delete_ike_sa_job.c processing/jobs/delete_ike_sa_job.h \
 processing/jobs/migrate_job.c processing/jobs/migrate_job.h \
 processing/jobs/process_message_job.c processing/jobs/process_message_job.h \
+processing/jobs/redirect_job.c processing/jobs/redirect_job.h \
 processing/jobs/rekey_child_sa_job.c processing/jobs/rekey_child_sa_job.h \
 processing/jobs/rekey_ike_sa_job.c processing/jobs/rekey_ike_sa_job.h \
 processing/jobs/retransmit_job.c processing/jobs/retransmit_job.h \
@@ -84,6 +85,7 @@ sa/child_sa_manager.c sa/child_sa_manager.h \
 sa/task_manager.h sa/task_manager.c \
 sa/shunt_manager.c sa/shunt_manager.h \
 sa/trap_manager.c sa/trap_manager.h \
+sa/redirect_provider.h sa/redirect_manager.c sa/redirect_manager.h \
 sa/task.c sa/task.h
 
 libcharon_la_SOURCES += \
@@ -107,6 +109,7 @@ sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \
 sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \
 sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \
 sa/ikev2/tasks/ike_reauth_complete.c sa/ikev2/tasks/ike_reauth_complete.h \
+sa/ikev2/tasks/ike_redirect.c sa/ikev2/tasks/ike_redirect.h \
 sa/ikev2/tasks/ike_auth_lifetime.c sa/ikev2/tasks/ike_auth_lifetime.h \
 sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h
 
index 4de8faa..062b96a 100644 (file)
@@ -58,6 +58,7 @@ processing/jobs/delete_child_sa_job.c processing/jobs/delete_child_sa_job.h \
 processing/jobs/delete_ike_sa_job.c processing/jobs/delete_ike_sa_job.h \
 processing/jobs/migrate_job.c processing/jobs/migrate_job.h \
 processing/jobs/process_message_job.c processing/jobs/process_message_job.h \
+processing/jobs/redirect_job.c processing/jobs/redirect_job.h \
 processing/jobs/rekey_child_sa_job.c processing/jobs/rekey_child_sa_job.h \
 processing/jobs/rekey_ike_sa_job.c processing/jobs/rekey_ike_sa_job.h \
 processing/jobs/retransmit_job.c processing/jobs/retransmit_job.h \
@@ -83,6 +84,7 @@ sa/child_sa_manager.c sa/child_sa_manager.h \
 sa/task_manager.h sa/task_manager.c \
 sa/shunt_manager.c sa/shunt_manager.h \
 sa/trap_manager.c sa/trap_manager.h \
+sa/redirect_provider.h sa/redirect_manager.c sa/redirect_manager.h \
 sa/task.c sa/task.h
 
 if USE_IKEV2
@@ -107,6 +109,7 @@ sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \
 sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \
 sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \
 sa/ikev2/tasks/ike_reauth_complete.c sa/ikev2/tasks/ike_reauth_complete.h \
+sa/ikev2/tasks/ike_redirect.c sa/ikev2/tasks/ike_redirect.h \
 sa/ikev2/tasks/ike_auth_lifetime.c sa/ikev2/tasks/ike_auth_lifetime.h \
 sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h
 endif
index 799c3f6..cef8b89 100644 (file)
@@ -680,6 +680,7 @@ static void destroy(private_daemon_t *this)
        DESTROY_IF(this->kernel_handler);
        DESTROY_IF(this->public.traps);
        DESTROY_IF(this->public.shunts);
+       DESTROY_IF(this->public.redirect);
        DESTROY_IF(this->public.controller);
        DESTROY_IF(this->public.eap);
        DESTROY_IF(this->public.xauth);
@@ -872,6 +873,7 @@ private_daemon_t *daemon_create()
        this->public.socket = socket_manager_create();
        this->public.traps = trap_manager_create();
        this->public.shunts = shunt_manager_create();
+       this->public.redirect = redirect_manager_create();
        this->kernel_handler = kernel_handler_create();
 
        return this;
index 654e22a..48b9c7e 100644 (file)
@@ -190,6 +190,7 @@ typedef struct daemon_t daemon_t;
 #include <sa/child_sa_manager.h>
 #include <sa/trap_manager.h>
 #include <sa/shunt_manager.h>
+#include <sa/redirect_manager.h>
 #include <config/backend_manager.h>
 #include <sa/eap/eap_manager.h>
 #include <sa/xauth/xauth_manager.h>
@@ -265,6 +266,11 @@ struct daemon_t {
        shunt_manager_t *shunts;
 
        /**
+        * Manager for IKE redirect providers
+        */
+       redirect_manager_t *redirect;
+
+       /**
         * Manager for the different configuration backends.
         */
        backend_manager_t *backends;
index 773ef1a..52929bd 100644 (file)
@@ -289,6 +289,23 @@ Terminates an SA while streaming _control-log_ events.
 The default timeout of 0 waits indefinitely for a result, and a timeout value
 of -1 returns a result immediately.
 
+### redirect() ###
+
+Redirect a client-initiated IKE_SA to another gateway.  Only for IKEv2 and if
+supported by the peer.
+
+       {
+               ike = <redirect an IKE_SA by configuration name>
+               ike-id = <redirect an IKE_SA by its unique id>
+               peer-ip = <redirect an IKE_SA with matching peer IP, may also be a
+                                  subnet in CIDR notation or an IP range>
+               peer-id = <redirect an IKE_SA with matching peer identity, may contain
+                                  wildcards>
+       } => {
+               success = <yes or no>
+               errmsg = <error string on failure>
+       }
+
 ### install() ###
 
 Install a trap, drop or bypass policy defined by a CHILD_SA config.
index 5252296..7819713 100644 (file)
@@ -36,6 +36,10 @@ sub terminate {
     return request_vars_res('terminate', @_);
 }
 
+sub redirect {
+    return request_vars_res('redirect', @_);
+}
+
 sub install {
     return request_vars_res('install', @_);
 }
index 283e3d1..66de859 100644 (file)
@@ -53,6 +53,14 @@ class Session(object):
         """
         return self.handler.streamed_request("terminate", "control-log", sa)
 
+    def redirect(self, sa):
+        """Redirect an IKE_SA.
+
+        :param sa: the SA to redirect
+        :type sa: dict
+        """
+        self.handler.request("redirect", sa)
+
     def install(self, policy):
         """Install a trap, drop or bypass policy defined by a CHILD_SA config.
 
index f8169ad..018f507 100644 (file)
@@ -505,6 +505,12 @@ module Vici
     end
 
     ##
+    # Redirect an IKE_SA.
+    def redirect(options)
+      check_success(@transp.request("redirect", Message.new(options)))
+    end
+
+    ##
     # Install a shunt/route policy.
     def install(policy)
       check_success(@transp.request("install", Message.new(policy)))
index 87794d2..c526d2f 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
  * Copyright (C) 2014 Martin Willi
  * Copyright (C) 2014 revosec AG
  *
@@ -20,6 +23,7 @@
 
 #include <daemon.h>
 #include <collections/array.h>
+#include <processing/jobs/redirect_job.h>
 
 typedef struct private_vici_control_t private_vici_control_t;
 
@@ -357,6 +361,150 @@ CALLBACK(terminate, vici_message_t*,
 }
 
 /**
+ * Parse a peer-ip specified, which can be a subnet in CIDR notation, a range
+ * or a single IP address.
+ */
+static traffic_selector_t *parse_peer_ip(char *ip)
+{
+       traffic_selector_t *ts;
+       host_t *from, *to;
+       ts_type_t type;
+
+       if (host_create_from_range(ip, &from, &to))
+       {
+               if (to->get_family(to) == AF_INET)
+               {
+                       type = TS_IPV4_ADDR_RANGE;
+               }
+               else
+               {
+                       type = TS_IPV6_ADDR_RANGE;
+               }
+               ts = traffic_selector_create_from_bytes(0, type,
+                                                                                               from->get_address(from), 0,
+                                                                                               to->get_address(to), 0xFFFF);
+               from->destroy(from);
+               to->destroy(to);
+               return ts;
+       }
+       return traffic_selector_create_from_cidr(ip, 0, 0, 0xFFFF);
+}
+
+CALLBACK(redirect, vici_message_t*,
+       private_vici_control_t *this, char *name, u_int id, vici_message_t *request)
+{
+       enumerator_t *sas;
+       char *ike, *peer_ip, *peer_id, *gw, *errmsg = NULL;
+       u_int ike_id, current, found = 0;
+       identification_t *gateway, *identity = NULL, *other_id;
+       traffic_selector_t *ts = NULL;
+       ike_sa_t *ike_sa;
+       vici_builder_t *builder;
+
+       ike = request->get_str(request, NULL, "ike");
+       ike_id = request->get_int(request, 0, "ike-id");
+       peer_ip = request->get_str(request, NULL, "peer-ip");
+       peer_id = request->get_str(request, NULL, "peer-id");
+       gw = request->get_str(request, NULL, "gateway");
+
+       if (!gw || !(gateway = identification_create_from_string(gw)))
+       {
+               return send_reply(this, "missing target gateway");
+       }
+       switch (gateway->get_type(gateway))
+       {
+               case ID_IPV4_ADDR:
+               case ID_IPV6_ADDR:
+               case ID_FQDN:
+                       break;
+               default:
+                       return send_reply(this, "unsupported gateway identity");
+       }
+       if (peer_ip)
+       {
+               ts = parse_peer_ip(peer_ip);
+               if (!ts)
+               {
+                       return send_reply(this, "invalid peer IP selector");
+               }
+               DBG1(DBG_CFG, "vici redirect IKE_SAs with src %R to %Y", ts,
+                        gateway);
+       }
+       if (peer_id)
+       {
+               identity = identification_create_from_string(peer_id);
+               if (!identity)
+               {
+                       DESTROY_IF(ts);
+                       return send_reply(this, "invalid peer identity selector");
+               }
+               DBG1(DBG_CFG, "vici redirect IKE_SAs with ID '%Y' to %Y", identity,
+                        gateway);
+       }
+       if (ike_id)
+       {
+               DBG1(DBG_CFG, "vici redirect IKE_SA #%d to '%Y'", ike_id, gateway);
+       }
+       if (ike)
+       {
+               DBG1(DBG_CFG, "vici redirect IKE_SA '%s' to '%Y'", ike, gateway);
+       }
+       if (!peer_ip && !peer_id && !ike && !ike_id)
+       {
+               return send_reply(this, "missing redirect selector");
+       }
+
+       sas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE);
+       while (sas->enumerate(sas, &ike_sa))
+       {
+               if (ike_sa->get_version(ike_sa) != IKEV2)
+               {
+                       continue;
+               }
+               current = ike_sa->get_unique_id(ike_sa);
+               if (ike_id && ike_id != current)
+               {
+                       continue;
+               }
+               if (ike && !streq(ike, ike_sa->get_name(ike_sa)))
+               {
+                       continue;
+               }
+               if (ts && !ts->includes(ts, ike_sa->get_other_host(ike_sa)))
+               {
+                       continue;
+               }
+               if (identity)
+               {
+                       other_id = ike_sa->get_other_eap_id(ike_sa);
+                       if (!other_id->matches(other_id, identity))
+                       {
+                               continue;
+                       }
+               }
+               lib->processor->queue_job(lib->processor,
+                               (job_t*)redirect_job_create(ike_sa->get_id(ike_sa), gateway));
+               found++;
+       }
+       sas->destroy(sas);
+
+       builder = vici_builder_create();
+       if (!found)
+       {
+               errmsg = "no matching SAs to redirect found";
+       }
+       builder->add_kv(builder, "success", errmsg ? "no" : "yes");
+       if (errmsg)
+       {
+               builder->add_kv(builder, "errmsg", "%s", errmsg);
+       }
+       gateway->destroy(gateway);
+       DESTROY_IF(identity);
+       DESTROY_IF(ts);
+       return builder->finalize(builder);
+}
+
+/**
  * Find reqid of an existing CHILD_SA
  */
 static u_int32_t find_reqid(child_cfg_t *cfg)
@@ -498,6 +646,7 @@ static void manage_commands(private_vici_control_t *this, bool reg)
 {
        manage_command(this, "initiate", initiate, reg);
        manage_command(this, "terminate", terminate, reg);
+       manage_command(this, "redirect", redirect, reg);
        manage_command(this, "install", install, reg);
        manage_command(this, "uninstall", uninstall, reg);
        manage_command(this, "reload-settings", reload_settings, reg);
diff --git a/src/libcharon/processing/jobs/redirect_job.c b/src/libcharon/processing/jobs/redirect_job.c
new file mode 100644 (file)
index 0000000..e1af662
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 <daemon.h>
+
+#include "redirect_job.h"
+
+typedef struct private_redirect_job_t private_redirect_job_t;
+
+/**
+ * Private data
+ */
+struct private_redirect_job_t {
+
+       /**
+        * Public interface
+        */
+       redirect_job_t public;
+
+       /**
+        * ID of the IKE_SA to redirect
+        */
+       ike_sa_id_t *ike_sa_id;
+
+       /**
+        * Target gateway identity
+        */
+       identification_t *gateway;
+};
+
+
+METHOD(job_t, destroy, void,
+       private_redirect_job_t *this)
+{
+       this->ike_sa_id->destroy(this->ike_sa_id);
+       this->gateway->destroy(this->gateway);
+       free(this);
+}
+
+METHOD(job_t, execute, job_requeue_t,
+       private_redirect_job_t *this)
+{
+       ike_sa_t *ike_sa;
+
+       ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
+                                                                                         this->ike_sa_id);
+       if (ike_sa)
+       {
+               if (ike_sa->get_state(ike_sa) == IKE_PASSIVE)
+               {
+                       charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+                       return JOB_REQUEUE_NONE;
+               }
+               if (ike_sa->redirect(ike_sa, this->gateway) == DESTROY_ME)
+               {
+                       charon->ike_sa_manager->checkin_and_destroy(
+                                                                                       charon->ike_sa_manager, ike_sa);
+               }
+               else
+               {
+                       charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+               }
+       }
+       return JOB_REQUEUE_NONE;
+}
+
+METHOD(job_t, get_priority, job_priority_t,
+       private_redirect_job_t *this)
+{
+       return JOB_PRIO_MEDIUM;
+}
+
+/*
+ * Described in header
+ */
+redirect_job_t *redirect_job_create(ike_sa_id_t *ike_sa_id,
+                                                                       identification_t *gateway)
+{
+       private_redirect_job_t *this;
+
+       INIT(this,
+               .public = {
+                       .job_interface = {
+                               .execute = _execute,
+                               .get_priority = _get_priority,
+                               .destroy = _destroy,
+                       },
+               },
+               .ike_sa_id = ike_sa_id->clone(ike_sa_id),
+               .gateway = gateway->clone(gateway),
+       );
+
+       return &(this->public);
+}
diff --git a/src/libcharon/processing/jobs/redirect_job.h b/src/libcharon/processing/jobs/redirect_job.h
new file mode 100644 (file)
index 0000000..fe4b34e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 redirect_job redirect_job
+ * @{ @ingroup cjobs
+ */
+
+#ifndef REDIRECT_JOB_H_
+#define REDIRECT_JOB_H_
+
+typedef struct redirect_job_t redirect_job_t;
+
+#include <library.h>
+#include <sa/ike_sa_id.h>
+#include <processing/jobs/job.h>
+
+/**
+ * Job used to redirect an IKE_SA.
+ */
+struct redirect_job_t {
+
+       /**
+        * The job_t interface.
+        */
+       job_t job_interface;
+};
+
+/**
+ * Creates a job to redirect an IKE_SA.
+ *
+ * @param ike_sa_id                    id of the IKE_SA to redirect (cloned)
+ * @param gateway                      gateway identity (IP or FQDN) of target (cloned)
+ * @return                                     created redirect_job_t object
+ */
+redirect_job_t *redirect_job_create(ike_sa_id_t *ike_sa_id,
+                                                                       identification_t *gateway);
+
+#endif /** REDIRECT_JOB_H_ @}*/
index 48a4b27..b07ff0e 100644 (file)
@@ -56,6 +56,8 @@
 #include <processing/jobs/rekey_ike_sa_job.h>
 #include <processing/jobs/retry_initiate_job.h>
 #include <sa/ikev2/tasks/ike_auth_lifetime.h>
+#include <sa/ikev2/tasks/ike_reauth_complete.h>
+#include <sa/ikev2/tasks/ike_redirect.h>
 
 #ifdef ME
 #include <sa/ikev2/tasks/ike_me.h>
@@ -282,6 +284,21 @@ struct private_ike_sa_t {
         * Maximum length of a single fragment, 0 for address-specific defaults
         */
        size_t fragment_size;
+
+       /**
+        * Whether to follow IKEv2 redirects
+        */
+       bool follow_redirects;
+
+       /**
+        * Original gateway address from which we got redirected
+        */
+       host_t *redirected_from;
+
+       /**
+        * Timestamps of redirect attempts to handle loops
+        */
+       array_t *redirected_at;
 };
 
 /**
@@ -386,6 +403,12 @@ METHOD(ike_sa_t, set_other_host, void,
        this->other_host = other;
 }
 
+METHOD(ike_sa_t, get_redirected_from, host_t*,
+       private_ike_sa_t *this)
+{
+       return this->redirected_from;
+}
+
 METHOD(ike_sa_t, get_peer_cfg, peer_cfg_t*,
        private_ike_sa_t *this)
 {
@@ -743,6 +766,8 @@ METHOD(ike_sa_t, set_state, void,
                                {
                                        keepalives = TRUE;
                                }
+                               DESTROY_IF(this->redirected_from);
+                               this->redirected_from = NULL;
                        }
                        break;
                }
@@ -1750,6 +1775,86 @@ static bool is_child_queued(private_ike_sa_t *this, task_queue_t queue)
        return found;
 }
 
+/**
+ * Reestablish CHILD_SAs and migrate queued tasks.
+ *
+ * If force is true all SAs are restarted, otherwise their close/dpd_action
+ * is followed.
+ */
+static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new,
+                                                                        bool force)
+{
+       enumerator_t *enumerator;
+       child_sa_t *child_sa;
+       child_cfg_t *child_cfg;
+       action_t action;
+       status_t status = FAILED;
+
+       /* handle existing CHILD_SAs */
+       enumerator = create_child_sa_enumerator(this);
+       while (enumerator->enumerate(enumerator, (void**)&child_sa))
+       {
+               if (force)
+               {
+                       switch (child_sa->get_state(child_sa))
+                       {
+                               case CHILD_ROUTED:
+                               {       /* move routed child directly */
+                                       remove_child_sa(this, enumerator);
+                                       new->add_child_sa(new, child_sa);
+                                       action = ACTION_NONE;
+                                       break;
+                               }
+                               default:
+                               {       /* initiate/queue all other CHILD_SAs */
+                                       action = ACTION_RESTART;
+                                       break;
+                               }
+                       }
+               }
+               else
+               {       /* only restart CHILD_SAs that are configured accordingly */
+                       if (this->state == IKE_DELETING)
+                       {
+                               action = child_sa->get_close_action(child_sa);
+                       }
+                       else
+                       {
+                               action = child_sa->get_dpd_action(child_sa);
+                       }
+               }
+               switch (action)
+               {
+                       case ACTION_RESTART:
+                               child_cfg = child_sa->get_config(child_sa);
+                               DBG1(DBG_IKE, "restarting CHILD_SA %s",
+                                        child_cfg->get_name(child_cfg));
+                               child_cfg->get_ref(child_cfg);
+                               status = new->initiate(new, child_cfg,
+                                                               child_sa->get_reqid(child_sa), NULL, NULL);
+                               break;
+                       default:
+                               continue;
+               }
+               if (status == DESTROY_ME)
+               {
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       /* adopt any active or queued CHILD-creating tasks */
+       if (status != DESTROY_ME)
+       {
+               task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager;
+               other_tasks->adopt_child_tasks(other_tasks, this->task_manager);
+               if (new->get_state(new) == IKE_CREATED)
+               {
+                       status = new->initiate(new, NULL, 0, NULL, NULL);
+               }
+       }
+       return status;
+}
+
 METHOD(ike_sa_t, reestablish, status_t,
        private_ike_sa_t *this)
 {
@@ -1758,7 +1863,6 @@ METHOD(ike_sa_t, reestablish, status_t,
        action_t action;
        enumerator_t *enumerator;
        child_sa_t *child_sa;
-       child_cfg_t *child_cfg;
        bool restart = FALSE;
        status_t status = FAILED;
 
@@ -1851,8 +1955,11 @@ METHOD(ike_sa_t, reestablish, status_t,
        host = this->my_host;
        new->set_my_host(new, host->clone(host));
        charon->bus->ike_reestablish_pre(charon->bus, &this->public, new);
-       /* resolve hosts but use the old addresses above as fallback */
-       resolve_hosts((private_ike_sa_t*)new);
+       if (!has_condition(this, COND_REAUTHENTICATING))
+       {       /* reauthenticate to the same addresses, but resolve hosts if
+                * reestablishing (old addresses serve as fallback) */
+               resolve_hosts((private_ike_sa_t*)new);
+       }
        /* if we already have a virtual IP, we reuse it */
        enumerator = array_create_enumerator(this->my_vips);
        while (enumerator->enumerate(enumerator, &host))
@@ -1869,68 +1976,8 @@ METHOD(ike_sa_t, reestablish, status_t,
        else
 #endif /* ME */
        {
-               /* handle existing CHILD_SAs */
-               enumerator = create_child_sa_enumerator(this);
-               while (enumerator->enumerate(enumerator, (void**)&child_sa))
-               {
-                       if (has_condition(this, COND_REAUTHENTICATING))
-                       {
-                               switch (child_sa->get_state(child_sa))
-                               {
-                                       case CHILD_ROUTED:
-                                       {       /* move routed child directly */
-                                               remove_child_sa(this, enumerator);
-                                               new->add_child_sa(new, child_sa);
-                                               action = ACTION_NONE;
-                                               break;
-                                       }
-                                       default:
-                                       {       /* initiate/queue all other CHILD_SAs */
-                                               action = ACTION_RESTART;
-                                               break;
-                                       }
-                               }
-                       }
-                       else
-                       {       /* only restart CHILD_SAs that are configured accordingly */
-                               if (this->state == IKE_DELETING)
-                               {
-                                       action = child_sa->get_close_action(child_sa);
-                               }
-                               else
-                               {
-                                       action = child_sa->get_dpd_action(child_sa);
-                               }
-                       }
-                       switch (action)
-                       {
-                               case ACTION_RESTART:
-                                       child_cfg = child_sa->get_config(child_sa);
-                                       DBG1(DBG_IKE, "restarting CHILD_SA %s",
-                                                child_cfg->get_name(child_cfg));
-                                       child_cfg->get_ref(child_cfg);
-                                       status = new->initiate(new, child_cfg,
-                                                                       child_sa->get_reqid(child_sa), NULL, NULL);
-                                       break;
-                               default:
-                                       continue;
-                       }
-                       if (status == DESTROY_ME)
-                       {
-                               break;
-                       }
-               }
-               enumerator->destroy(enumerator);
-               /* adopt any active or queued CHILD-creating tasks */
-               if (status != DESTROY_ME)
-               {
-                       task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager;
-                       other_tasks->adopt_child_tasks(other_tasks, this->task_manager);
-                       if (new->get_state(new) == IKE_CREATED)
-                       {
-                               status = new->initiate(new, NULL, 0, NULL, NULL);
-                       }
-               }
+               status = reestablish_children(this, new,
+                                                                       has_condition(this, COND_REAUTHENTICATING));
        }
 
        if (status == DESTROY_ME)
@@ -1951,6 +1998,195 @@ METHOD(ike_sa_t, reestablish, status_t,
        return status;
 }
 
+/**
+ * Resolve the given gateway ID
+ */
+static host_t *resolve_gateway_id(identification_t *gateway)
+{
+       char gw[BUF_LEN];
+       host_t *addr;
+
+       snprintf(gw, sizeof(gw), "%Y", gateway);
+       gw[sizeof(gw)-1] = '\0';
+       addr = host_create_from_dns(gw, AF_UNSPEC, IKEV2_UDP_PORT);
+       if (!addr)
+       {
+               DBG1(DBG_IKE, "unable to resolve gateway ID '%Y', redirect failed",
+                        gateway);
+       }
+       return addr;
+}
+
+/**
+ * Redirect the current SA to the given target host
+ */
+static bool redirect_established(private_ike_sa_t *this, identification_t *to)
+{
+       private_ike_sa_t *new_priv;
+       ike_sa_t *new;
+       host_t *other;
+       time_t redirect;
+
+       new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+                                                                                          this->version, TRUE);
+       if (!new)
+       {
+               return FALSE;
+       }
+       new_priv = (private_ike_sa_t*)new;
+       new->set_peer_cfg(new, this->peer_cfg);
+       new_priv->redirected_from = this->other_host->clone(this->other_host);
+       charon->bus->ike_reestablish_pre(charon->bus, &this->public, new);
+       other = resolve_gateway_id(to);
+       if (other)
+       {
+               set_my_host(new_priv, this->my_host->clone(this->my_host));
+               /* this allows us to force the remote address while we still properly
+                * resolve the local address */
+               new_priv->remote_host = other;
+               resolve_hosts(new_priv);
+               new_priv->redirected_at = array_create(sizeof(time_t), MAX_REDIRECTS);
+               while (array_remove(this->redirected_at, ARRAY_HEAD, &redirect))
+               {
+                       array_insert(new_priv->redirected_at, ARRAY_TAIL, &redirect);
+               }
+               if (reestablish_children(this, new, TRUE) != DESTROY_ME)
+               {
+#ifdef USE_IKEV2
+                       new->queue_task(new, (task_t*)ike_reauth_complete_create(new,
+                                                                                                                        this->ike_sa_id));
+#endif
+                       charon->bus->ike_reestablish_post(charon->bus, &this->public, new,
+                                                                                         TRUE);
+                       charon->ike_sa_manager->checkin(charon->ike_sa_manager, new);
+                       charon->bus->set_sa(charon->bus, &this->public);
+                       return TRUE;
+               }
+       }
+       charon->bus->ike_reestablish_post(charon->bus, &this->public, new,
+                                                                         FALSE);
+       charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new);
+       charon->bus->set_sa(charon->bus, &this->public);
+       return FALSE;
+}
+
+/**
+ * Redirect the current connecting SA to the given target host
+ */
+static bool redirect_connecting(private_ike_sa_t *this, identification_t *to)
+{
+       host_t *other;
+
+       other = resolve_gateway_id(to);
+       if (!other)
+       {
+               return FALSE;
+       }
+       reset(this);
+       DESTROY_IF(this->redirected_from);
+       this->redirected_from = this->other_host->clone(this->other_host);
+       DESTROY_IF(this->remote_host);
+       /* this allows us to force the remote address while we still properly
+        * resolve the local address */
+       this->remote_host = other;
+       resolve_hosts(this);
+       return TRUE;
+}
+
+/**
+ * Check if the current redirect exceeds the limits for redirects
+ */
+static bool redirect_count_exceeded(private_ike_sa_t *this)
+{
+       time_t now, redirect;
+
+       now = time_monotonic(NULL);
+       /* remove entries outside the defined period */
+       while (array_get(this->redirected_at, ARRAY_HEAD, &redirect) &&
+                  now - redirect >= REDIRECT_LOOP_DETECT_PERIOD)
+       {
+               array_remove(this->redirected_at, ARRAY_HEAD, NULL);
+       }
+       if (array_count(this->redirected_at) < MAX_REDIRECTS)
+       {
+               if (!this->redirected_at)
+               {
+                       this->redirected_at = array_create(sizeof(time_t), MAX_REDIRECTS);
+               }
+               array_insert(this->redirected_at, ARRAY_TAIL, &now);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+METHOD(ike_sa_t, handle_redirect, bool,
+       private_ike_sa_t *this, identification_t *gateway)
+{
+       DBG1(DBG_IKE, "redirected to %Y", gateway);
+       if (!this->follow_redirects)
+       {
+               DBG1(DBG_IKE, "server sent REDIRECT even though we disabled it");
+               return FALSE;
+       }
+       if (redirect_count_exceeded(this))
+       {
+               DBG1(DBG_IKE, "only %d redirects are allowed within %d seconds",
+                        MAX_REDIRECTS, REDIRECT_LOOP_DETECT_PERIOD);
+               return FALSE;
+       }
+
+       switch (this->state)
+       {
+               case IKE_CONNECTING:
+                       return redirect_connecting(this, gateway);
+               case IKE_ESTABLISHED:
+                       return redirect_established(this, gateway);
+               default:
+                       DBG1(DBG_IKE, "unable to handle redirect for IKE_SA in state %N",
+                                ike_sa_state_names, this->state);
+                       return FALSE;
+       }
+}
+
+METHOD(ike_sa_t, redirect, status_t,
+       private_ike_sa_t *this, identification_t *gateway)
+{
+       switch (this->state)
+       {
+               case IKE_CONNECTING:
+               case IKE_ESTABLISHED:
+               case IKE_REKEYING:
+                       if (has_condition(this, COND_REDIRECTED))
+                       {       /* IKE_SA already got redirected */
+                               return SUCCESS;
+                       }
+                       if (has_condition(this, COND_ORIGINAL_INITIATOR))
+                       {
+                               DBG1(DBG_IKE, "unable to redirect IKE_SA as initiator");
+                               return FAILED;
+                       }
+                       if (this->version == IKEV1)
+                       {
+                               DBG1(DBG_IKE, "unable to redirect IKEv1 SA");
+                               return FAILED;
+                       }
+                       if (!supports_extension(this, EXT_IKE_REDIRECTION))
+                       {
+                               DBG1(DBG_IKE, "client does not support IKE redirection");
+                               return FAILED;
+                       }
+#ifdef USE_IKEV2
+                       this->task_manager->queue_task(this->task_manager,
+                                               (task_t*)ike_redirect_create(&this->public, gateway));
+#endif
+                       return this->task_manager->initiate(this->task_manager);
+               default:
+                       DBG1(DBG_IKE, "unable to redirect IKE_SA in state %N",
+                                ike_sa_state_names, this->state);
+                       return INVALID_STATE;
+       }
+}
+
 METHOD(ike_sa_t, retransmit, status_t,
        private_ike_sa_t *this, u_int32_t message_id)
 {
@@ -2464,6 +2700,8 @@ METHOD(ike_sa_t, destroy, void,
        DESTROY_IF(this->other_id);
        DESTROY_IF(this->local_host);
        DESTROY_IF(this->remote_host);
+       DESTROY_IF(this->redirected_from);
+       array_destroy(this->redirected_at);
 
        DESTROY_IF(this->ike_cfg);
        DESTROY_IF(this->peer_cfg);
@@ -2543,6 +2781,9 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
                        .destroy = _destroy,
                        .send_dpd = _send_dpd,
                        .send_keepalive = _send_keepalive,
+                       .redirect = _redirect,
+                       .handle_redirect = _handle_redirect,
+                       .get_redirected_from = _get_redirected_from,
                        .get_keymat = _get_keymat,
                        .add_child_sa = _add_child_sa,
                        .get_child_sa = _get_child_sa,
@@ -2608,6 +2849,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator,
                                                                "%s.flush_auth_cfg", FALSE, lib->ns),
                .fragment_size = lib->settings->get_int(lib->settings,
                                                                "%s.fragment_size", 0, lib->ns),
+               .follow_redirects = lib->settings->get_bool(lib->settings,
+                                                               "%s.follow_redirects", TRUE, lib->ns),
        );
 
        if (version == IKEV2)
index e15ac2e..158a690 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2014 Tobias Brunner
+ * Copyright (C) 2006-2015 Tobias Brunner
  * Copyright (C) 2006 Daniel Roethlisberger
  * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2005 Jan Hutter
@@ -66,6 +66,16 @@ typedef struct ike_sa_t ike_sa_t;
 #define RETRY_JITTER 20
 
 /**
+ * Number of redirects allowed within REDIRECT_LOOP_DETECT_PERIOD.
+ */
+#define MAX_REDIRECTS 5
+
+/**
+ * Time period in seconds in which at most MAX_REDIRECTS are allowed.
+ */
+#define REDIRECT_LOOP_DETECT_PERIOD 300
+
+/**
  * Extensions (or optional features) the peer supports
  */
 enum ike_extension_t {
@@ -136,6 +146,11 @@ enum ike_extension_t {
         * Signature Authentication, RFC 7427
         */
        EXT_SIGNATURE_AUTH = (1<<12),
+
+       /**
+        * IKEv2 Redirect Mechanism, RFC 5685
+        */
+       EXT_IKE_REDIRECTION = (1<<13),
 };
 
 /**
@@ -197,6 +212,11 @@ enum ike_condition_t {
         * This IKE_SA is currently being reauthenticated
         */
        COND_REAUTHENTICATING = (1<<10),
+
+       /**
+        * This IKE_SA has been redirected
+        */
+       COND_REDIRECTED = (1<<11),
 };
 
 /**
@@ -843,6 +863,32 @@ struct ike_sa_t {
        void (*send_keepalive) (ike_sa_t *this, bool scheduled);
 
        /**
+        * Redirect an active IKE_SA.
+        *
+        * @param gateway               gateway ID (IP or FQDN) of the target
+        * @return                              state, including DESTROY_ME, if this IKE_SA MUST be
+        *                                              destroyed
+        */
+       status_t (*redirect)(ike_sa_t *this, identification_t *gateway);
+
+       /**
+        * Handle a redirect request.
+        *
+        * The behavior is different depending on the state of the IKE_SA.
+        *
+        * @param gateway               gateway ID (IP or FQDN) of the target
+        * @return                              FALSE if redirect not possible, TRUE otherwise
+        */
+       bool (*handle_redirect)(ike_sa_t *this, identification_t *gateway);
+
+       /**
+        * Get the address of the gateway that redirected us.
+        *
+        * @return                              original gateway address
+        */
+       host_t *(*get_redirected_from)(ike_sa_t *this);
+
+       /**
         * Get the keying material of this IKE_SA.
         *
         * @return                              per IKE_SA keymat instance
index 4676867..8ed8630 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007-2014 Tobias Brunner
+ * Copyright (C) 2007-2015 Tobias Brunner
  * Copyright (C) 2007-2010 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -30,6 +30,7 @@
 #include <sa/ikev2/tasks/ike_rekey.h>
 #include <sa/ikev2/tasks/ike_reauth.h>
 #include <sa/ikev2/tasks/ike_reauth_complete.h>
+#include <sa/ikev2/tasks/ike_redirect.h>
 #include <sa/ikev2/tasks/ike_delete.h>
 #include <sa/ikev2/tasks/ike_config.h>
 #include <sa/ikev2/tasks/ike_dpd.h>
@@ -474,6 +475,11 @@ METHOD(task_manager_t, initiate, status_t,
                                        exchange = INFORMATIONAL;
                                        break;
                                }
+                               if (activate_task(this, TASK_IKE_REDIRECT))
+                               {
+                                       exchange = INFORMATIONAL;
+                                       break;
+                               }
                                if (activate_task(this, TASK_CHILD_DELETE))
                                {
                                        exchange = INFORMATIONAL;
@@ -656,6 +662,32 @@ static status_t process_response(private_task_manager_t *this,
                return DESTROY_ME;
        }
 
+       enumerator = array_create_enumerator(this->active_tasks);
+       while (enumerator->enumerate(enumerator, &task))
+       {
+               if (!task->pre_process)
+               {
+                       continue;
+               }
+               switch (task->pre_process(task, message))
+               {
+                       case SUCCESS:
+                               break;
+                       case FAILED:
+                       default:
+                               /* just ignore the message */
+                               DBG1(DBG_IKE, "ignore invalid %N response",
+                                        exchange_type_names, message->get_exchange_type(message));
+                               enumerator->destroy(enumerator);
+                               return SUCCESS;
+                       case DESTROY_ME:
+                               /* critical failure, destroy IKE_SA */
+                               enumerator->destroy(enumerator);
+                               return DESTROY_ME;
+               }
+       }
+       enumerator->destroy(enumerator);
+
        /* catch if we get resetted while processing */
        this->reset = FALSE;
        enumerator = array_create_enumerator(this->active_tasks);
@@ -992,6 +1024,11 @@ static status_t process_request(private_task_manager_t *this,
                                                                         * invokes all the required hooks. */
                                                                        task = (task_t*)ike_delete_create(
                                                                                                                this->ike_sa, FALSE);
+                                                                       break;
+                                                               case REDIRECT:
+                                                                       task = (task_t*)ike_redirect_create(
+                                                                                                                       this->ike_sa, NULL);
+                                                                       break;
                                                                default:
                                                                        break;
                                                        }
@@ -1041,6 +1078,44 @@ static status_t process_request(private_task_manager_t *this,
                }
        }
 
+       enumerator = array_create_enumerator(this->passive_tasks);
+       while (enumerator->enumerate(enumerator, &task))
+       {
+               if (!task->pre_process)
+               {
+                       continue;
+               }
+               switch (task->pre_process(task, message))
+               {
+                       case SUCCESS:
+                               break;
+                       case FAILED:
+                       default:
+                               /* just ignore the message */
+                               DBG1(DBG_IKE, "ignore invalid %N request",
+                                        exchange_type_names, message->get_exchange_type(message));
+                               enumerator->destroy(enumerator);
+                               switch (message->get_exchange_type(message))
+                               {
+                                       case IKE_SA_INIT:
+                                               /* no point in keeping the SA when it was created with
+                                                * an invalid IKE_SA_INIT message */
+                                               return DESTROY_ME;
+                                       default:
+                                               /* remove tasks we queued for this request */
+                                               flush_queue(this, TASK_QUEUE_PASSIVE);
+                                               /* fall-through */
+                                       case IKE_AUTH:
+                                               return NEED_MORE;
+                               }
+                       case DESTROY_ME:
+                               /* critical failure, destroy IKE_SA */
+                               enumerator->destroy(enumerator);
+                               return DESTROY_ME;
+               }
+       }
+       enumerator->destroy(enumerator);
+
        /* let the tasks process the message */
        enumerator = array_create_enumerator(this->passive_tasks);
        while (enumerator->enumerate(enumerator, (void*)&task))
@@ -1331,12 +1406,17 @@ METHOD(task_manager_t, process_message, status_t,
                        {       /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */
                                return SUCCESS;
                        }
-                       if (process_request(this, msg) != SUCCESS)
+                       switch (process_request(this, msg))
                        {
-                               flush(this);
-                               return DESTROY_ME;
+                               case SUCCESS:
+                                       this->responding.mid++;
+                                       break;
+                               case NEED_MORE:
+                                       break;
+                               default:
+                                       flush(this);
+                                       return DESTROY_ME;
                        }
-                       this->responding.mid++;
                }
                else if ((mid == this->responding.mid - 1) &&
                                 array_count(this->responding.packets))
index 740d097..3d4ded9 100644 (file)
@@ -1220,6 +1220,10 @@ METHOD(task_t, build_r, status_t,
                        {       /* wait until all authentication round completed */
                                return NEED_MORE;
                        }
+                       if (this->ike_sa->has_condition(this->ike_sa, COND_REDIRECTED))
+                       {       /* no CHILD_SA is created for redirected SAs */
+                               return SUCCESS;
+                       }
                        ike_auth = TRUE;
                default:
                        break;
index 2554496..79a436f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2015 Tobias Brunner
  * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
@@ -25,6 +25,7 @@
 #include <encoding/payloads/eap_payload.h>
 #include <encoding/payloads/nonce_payload.h>
 #include <sa/ikev2/authenticators/eap_authenticator.h>
+#include <processing/jobs/delete_ike_sa_job.h>
 
 typedef struct private_ike_auth_t private_ike_auth_t;
 
@@ -117,6 +118,11 @@ struct private_ike_auth_t {
         * Is EAP acceptable, did we strictly authenticate peer?
         */
        bool eap_acceptable;
+
+       /**
+        * Gateway ID if redirected
+        */
+       identification_t *redirect_to;
 };
 
 /**
@@ -685,6 +691,7 @@ METHOD(task_t, process_r, status_t,
 METHOD(task_t, build_r, status_t,
        private_ike_auth_t *this, message_t *message)
 {
+       identification_t *gateway;
        auth_cfg_t *cfg;
 
        if (message->get_exchange_type(message) == IKE_SA_INIT)
@@ -817,34 +824,56 @@ METHOD(task_t, build_r, status_t,
        {
                this->do_another_auth = FALSE;
        }
-       if (!this->do_another_auth && !this->expect_another_auth)
+       if (this->do_another_auth || this->expect_another_auth)
        {
-               if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
-                                                                                                        this->ike_sa, FALSE))
-               {
-                       DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy");
-                       charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP);
-                       message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
-                                                               chunk_empty);
-                       return FAILED;
-               }
-               if (!charon->bus->authorize(charon->bus, TRUE))
-               {
-                       DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling");
-                       goto peer_auth_failed;
-               }
-               DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
-                        this->ike_sa->get_name(this->ike_sa),
-                        this->ike_sa->get_unique_id(this->ike_sa),
-                        this->ike_sa->get_my_host(this->ike_sa),
-                        this->ike_sa->get_my_id(this->ike_sa),
-                        this->ike_sa->get_other_host(this->ike_sa),
-                        this->ike_sa->get_other_id(this->ike_sa));
-               this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-               charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
-               return SUCCESS;
+               return NEED_MORE;
        }
-       return NEED_MORE;
+
+       if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager,
+                                                                                                this->ike_sa, FALSE))
+       {
+               DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy");
+               charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP);
+               message->add_notify(message, TRUE, AUTHENTICATION_FAILED,
+                                                       chunk_empty);
+               return FAILED;
+       }
+       if (!charon->bus->authorize(charon->bus, TRUE))
+       {
+               DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling");
+               goto peer_auth_failed;
+       }
+       if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) &&
+               charon->redirect->redirect_on_auth(charon->redirect, this->ike_sa,
+                                                                                  &gateway))
+       {
+               delete_ike_sa_job_t *job;
+               chunk_t data;
+
+               DBG1(DBG_IKE, "redirecting peer to %Y", gateway);
+               data = redirect_data_create(gateway, chunk_empty);
+               message->add_notify(message, FALSE, REDIRECT, data);
+               gateway->destroy(gateway);
+               chunk_free(&data);
+               /* we use this condition to prevent the CHILD_SA from getting created */
+               this->ike_sa->set_condition(this->ike_sa, COND_REDIRECTED, TRUE);
+               /* if the peer does not delete the SA we do so after a while */
+               job = delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE);
+               lib->scheduler->schedule_job(lib->scheduler, (job_t*)job,
+                                               lib->settings->get_int(lib->settings,
+                                                       "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT,
+                                                       lib->ns));
+       }
+       DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
+                this->ike_sa->get_name(this->ike_sa),
+                this->ike_sa->get_unique_id(this->ike_sa),
+                this->ike_sa->get_my_host(this->ike_sa),
+                this->ike_sa->get_my_id(this->ike_sa),
+                this->ike_sa->get_other_host(this->ike_sa),
+                this->ike_sa->get_other_id(this->ike_sa));
+       this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+       charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
+       return SUCCESS;
 
 peer_auth_failed:
        message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
@@ -964,6 +993,15 @@ METHOD(task_t, process_i, status_t,
                                case ME_ENDPOINT:
                                        /* handled in ike_me task */
                                        break;
+                               case REDIRECT:
+                                       DESTROY_IF(this->redirect_to);
+                                       this->redirect_to = redirect_data_parse(
+                                                               notify->get_notification_data(notify), NULL);
+                                       if (!this->redirect_to)
+                                       {
+                                               DBG1(DBG_IKE, "received invalid REDIRECT notify");
+                                       }
+                                       break;
                                default:
                                {
                                        if (type <= 16383)
@@ -1094,30 +1132,35 @@ METHOD(task_t, process_i, status_t,
        {
                this->expect_another_auth = FALSE;
        }
-       if (!this->expect_another_auth && !this->do_another_auth && !this->my_auth)
+       if (this->expect_another_auth || this->do_another_auth || this->my_auth)
        {
-               if (!update_cfg_candidates(this, TRUE))
-               {
-                       goto peer_auth_failed;
-               }
-               if (!charon->bus->authorize(charon->bus, TRUE))
-               {
-                       DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, "
-                                             "cancelling");
-                       goto peer_auth_failed;
-               }
-               DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
-                        this->ike_sa->get_name(this->ike_sa),
-                        this->ike_sa->get_unique_id(this->ike_sa),
-                        this->ike_sa->get_my_host(this->ike_sa),
-                        this->ike_sa->get_my_id(this->ike_sa),
-                        this->ike_sa->get_other_host(this->ike_sa),
-                        this->ike_sa->get_other_id(this->ike_sa));
-               this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-               charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
-               return SUCCESS;
+               return NEED_MORE;
        }
-       return NEED_MORE;
+       if (!update_cfg_candidates(this, TRUE))
+       {
+               goto peer_auth_failed;
+       }
+       if (!charon->bus->authorize(charon->bus, TRUE))
+       {
+               DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, "
+                                     "cancelling");
+               goto peer_auth_failed;
+       }
+       DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]",
+                this->ike_sa->get_name(this->ike_sa),
+                this->ike_sa->get_unique_id(this->ike_sa),
+                this->ike_sa->get_my_host(this->ike_sa),
+                this->ike_sa->get_my_id(this->ike_sa),
+                this->ike_sa->get_other_host(this->ike_sa),
+                this->ike_sa->get_other_id(this->ike_sa));
+       this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+       charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE);
+
+       if (this->redirect_to)
+       {
+               this->ike_sa->handle_redirect(this->ike_sa, this->redirect_to);
+       }
+       return SUCCESS;
 
 peer_auth_failed:
        charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED);
@@ -1141,6 +1184,7 @@ METHOD(task_t, migrate, void,
        DESTROY_IF(this->peer_cfg);
        DESTROY_IF(this->my_auth);
        DESTROY_IF(this->other_auth);
+       DESTROY_IF(this->redirect_to);
        this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy));
 
        this->my_packet = NULL;
@@ -1149,6 +1193,7 @@ METHOD(task_t, migrate, void,
        this->peer_cfg = NULL;
        this->my_auth = NULL;
        this->other_auth = NULL;
+       this->redirect_to = NULL;
        this->do_another_auth = TRUE;
        this->expect_another_auth = TRUE;
        this->authentication_failed = FALSE;
@@ -1165,6 +1210,7 @@ METHOD(task_t, destroy, void,
        DESTROY_IF(this->my_auth);
        DESTROY_IF(this->other_auth);
        DESTROY_IF(this->peer_cfg);
+       DESTROY_IF(this->redirect_to);
        this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy));
        free(this);
 }
index 646f20c..6c42b81 100644 (file)
@@ -333,6 +333,11 @@ METHOD(task_t, build_r, status_t,
                linked_list_t *vips, *pools;
                host_t *requested;
 
+               if (this->ike_sa->has_condition(this->ike_sa, COND_REDIRECTED))
+               {       /* don't assign attributes for redirected SAs */
+                       return SUCCESS;
+               }
+
                id = this->ike_sa->get_other_eap_id(this->ike_sa);
                config = this->ike_sa->get_peer_cfg(this->ike_sa);
                vips = linked_list_create();
index 1ff643d..5cfb518 100644 (file)
@@ -118,6 +118,11 @@ struct private_ike_init_t {
         * Whether to use Signature Authentication as per RFC 7427
         */
        bool signature_authentication;
+
+       /**
+        * Whether to follow IKEv2 redirects as per RFC 5685
+        */
+       bool follow_redirects;
 };
 
 /**
@@ -324,6 +329,29 @@ static bool build_payloads(private_ike_init_t *this, message_t *message)
                        send_supported_hash_algorithms(this, message);
                }
        }
+       /* notify other peer if we support redirection */
+       if (!this->old_sa && this->initiator && this->follow_redirects)
+       {
+               identification_t *gateway;
+               host_t *from;
+               chunk_t data;
+
+               from = this->ike_sa->get_redirected_from(this->ike_sa);
+               if (from)
+               {
+                       gateway = identification_create_from_sockaddr(
+                                                                                                       from->get_sockaddr(from));
+                       data = redirect_data_create(gateway, chunk_empty);
+                       message->add_notify(message, FALSE, REDIRECTED_FROM, data);
+                       chunk_free(&data);
+                       gateway->destroy(gateway);
+               }
+               else
+               {
+                       message->add_notify(message, FALSE, REDIRECT_SUPPORTED,
+                                                               chunk_empty);
+               }
+       }
        return TRUE;
 }
 
@@ -391,6 +419,30 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
                                                        handle_supported_hash_algorithms(this, notify);
                                                }
                                                break;
+                                       case REDIRECTED_FROM:
+                                       {
+                                               identification_t *gateway;
+                                               chunk_t data;
+
+                                               data = notify->get_notification_data(notify);
+                                               gateway = redirect_data_parse(data, NULL);
+                                               if (!gateway)
+                                               {
+                                                       DBG1(DBG_IKE, "received invalid REDIRECTED_FROM "
+                                                                "notify, ignored");
+                                                       break;
+                                               }
+                                               DBG1(DBG_IKE, "client got redirected from %Y", gateway);
+                                               gateway->destroy(gateway);
+                                               /* fall-through */
+                                       }
+                                       case REDIRECT_SUPPORTED:
+                                               if (!this->old_sa)
+                                               {
+                                                       this->ike_sa->enable_extension(this->ike_sa,
+                                                                                                                  EXT_IKE_REDIRECTION);
+                                               }
+                                               break;
                                        default:
                                                /* other notifies are handled elsewhere */
                                                break;
@@ -550,6 +602,8 @@ static bool derive_keys(private_ike_init_t *this,
 METHOD(task_t, build_r, status_t,
        private_ike_init_t *this, message_t *message)
 {
+       identification_t *gateway;
+
        /* check if we have everything we need */
        if (this->proposal == NULL ||
                this->other_nonce.len == 0 || this->my_nonce.len == 0)
@@ -560,6 +614,22 @@ METHOD(task_t, build_r, status_t,
        }
        this->ike_sa->set_proposal(this->ike_sa, this->proposal);
 
+       /* check if we'd have to redirect the client */
+       if (!this->old_sa &&
+               this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) &&
+               charon->redirect->redirect_on_init(charon->redirect, this->ike_sa,
+                                                                                  &gateway))
+       {
+               chunk_t data;
+
+               DBG1(DBG_IKE, "redirecting peer to %Y", gateway);
+               data = redirect_data_create(gateway, this->other_nonce);
+               message->add_notify(message, TRUE, REDIRECT, data);
+               gateway->destroy(gateway);
+               chunk_free(&data);
+               return FAILED;
+       }
+
        if (this->dh == NULL ||
                !this->proposal->has_dh_group(this->proposal, this->dh_group))
        {
@@ -623,6 +693,54 @@ static void raise_alerts(private_ike_init_t *this, notify_type_t type)
        }
 }
 
+METHOD(task_t, pre_process_i, status_t,
+       private_ike_init_t *this, message_t *message)
+{
+       enumerator_t *enumerator;
+       payload_t *payload;
+
+       /* check for erroneous notifies */
+       enumerator = message->create_payload_enumerator(message);
+       while (enumerator->enumerate(enumerator, &payload))
+       {
+               if (payload->get_type(payload) == PLV2_NOTIFY)
+               {
+                       notify_payload_t *notify = (notify_payload_t*)payload;
+                       notify_type_t type = notify->get_notify_type(notify);
+
+                       switch (type)
+                       {
+                               case REDIRECT:
+                               {
+                                       identification_t *gateway;
+                                       chunk_t data, nonce = chunk_empty;
+                                       status_t status = SUCCESS;
+
+                                       if (this->old_sa)
+                                       {
+                                               break;
+                                       }
+                                       data = notify->get_notification_data(notify);
+                                       gateway = redirect_data_parse(data, &nonce);
+                                       if (!gateway || !chunk_equals(nonce, this->my_nonce))
+                                       {
+                                               DBG1(DBG_IKE, "received invalid REDIRECT notify");
+                                               status = FAILED;
+                                       }
+                                       DESTROY_IF(gateway);
+                                       chunk_free(&nonce);
+                                       enumerator->destroy(enumerator);
+                                       return status;
+                               }
+                               default:
+                                       break;
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+       return SUCCESS;
+}
+
 METHOD(task_t, process_i, status_t,
        private_ike_init_t *this, message_t *message)
 {
@@ -678,6 +796,29 @@ METHOD(task_t, process_i, status_t,
                                        this->retry++;
                                        return NEED_MORE;
                                }
+                               case REDIRECT:
+                               {
+                                       identification_t *gateway;
+                                       chunk_t data, nonce = chunk_empty;
+                                       status_t status = FAILED;
+
+                                       if (this->old_sa)
+                                       {
+                                               DBG1(DBG_IKE, "received REDIRECT notify during rekeying"
+                                                    ", ignored");
+                                               break;
+                                       }
+                                       data = notify->get_notification_data(notify);
+                                       gateway = redirect_data_parse(data, &nonce);
+                                       if (this->ike_sa->handle_redirect(this->ike_sa, gateway))
+                                       {
+                                               status = NEED_MORE;
+                                       }
+                                       DESTROY_IF(gateway);
+                                       chunk_free(&nonce);
+                                       enumerator->destroy(enumerator);
+                                       return status;
+                               }
                                default:
                                {
                                        if (type <= 16383)
@@ -802,6 +943,8 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
                .old_sa = old_sa,
                .signature_authentication = lib->settings->get_bool(lib->settings,
                                                                "%s.signature_authentication", TRUE, lib->ns),
+               .follow_redirects = lib->settings->get_bool(lib->settings,
+                                                               "%s.follow_redirects", TRUE, lib->ns),
        );
        this->nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat);
 
@@ -809,6 +952,7 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
        {
                this->public.task.build = _build_i;
                this->public.task.process = _process_i;
+               this->public.task.pre_process = _pre_process_i;
        }
        else
        {
diff --git a/src/libcharon/sa/ikev2/tasks/ike_redirect.c b/src/libcharon/sa/ikev2/tasks/ike_redirect.c
new file mode 100644 (file)
index 0000000..f82c80f
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 "ike_redirect.h"
+
+#include <daemon.h>
+#include <processing/jobs/delete_ike_sa_job.h>
+
+typedef struct private_ike_redirect_t private_ike_redirect_t;
+
+/**
+ * Private members
+ */
+struct private_ike_redirect_t {
+
+       /**
+        * Public interface
+        */
+       ike_redirect_t public;
+
+       /**
+        * Assigned IKE_SA
+        */
+       ike_sa_t *ike_sa;
+
+       /**
+        * Gateway ID to redirect to
+        */
+       identification_t *gateway;
+};
+
+METHOD(task_t, build_i, status_t,
+       private_ike_redirect_t *this, message_t *message)
+{
+       chunk_t data;
+
+       DBG1(DBG_IKE, "redirecting peer to %Y", this->gateway);
+       data = redirect_data_create(this->gateway, chunk_empty);
+       message->add_notify(message, FALSE, REDIRECT, data);
+       chunk_free(&data);
+       this->ike_sa->set_condition(this->ike_sa, COND_REDIRECTED, TRUE);
+       return NEED_MORE;
+}
+
+METHOD(task_t, process_r, status_t,
+       private_ike_redirect_t *this, message_t *message)
+{
+       notify_payload_t *notify;
+       identification_t *to;
+
+       notify = message->get_notify(message, REDIRECT);
+       if (!notify)
+       {
+               return SUCCESS;
+       }
+
+       to = redirect_data_parse(notify->get_notification_data(notify), NULL);
+       if (!to)
+       {
+               DBG1(DBG_IKE, "received invalid REDIRECT notify");
+       }
+       else
+       {
+               this->ike_sa->handle_redirect(this->ike_sa, to);
+               to->destroy(to);
+       }
+       return SUCCESS;
+}
+
+METHOD(task_t, build_r, status_t,
+       private_ike_redirect_t *this, message_t *message)
+{
+       /* not called because SUCCESS is returned above */
+       return SUCCESS;
+}
+
+METHOD(task_t, process_i, status_t,
+       private_ike_redirect_t *this, message_t *message)
+{
+       delete_ike_sa_job_t *job;
+
+       /* if the peer does not delete the SA we do so after a while */
+       job = delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE);
+       lib->scheduler->schedule_job(lib->scheduler, (job_t*)job,
+                                       lib->settings->get_int(lib->settings,
+                                                       "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT,
+                                                       lib->ns));
+       return SUCCESS;
+}
+
+METHOD(task_t, get_type, task_type_t,
+       private_ike_redirect_t *this)
+{
+       return TASK_IKE_REDIRECT;
+}
+
+METHOD(task_t, migrate, void,
+       private_ike_redirect_t *this, ike_sa_t *ike_sa)
+{
+       this->ike_sa = ike_sa;
+}
+
+METHOD(task_t, destroy, void,
+       private_ike_redirect_t *this)
+{
+       DESTROY_IF(this->gateway);
+       free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_redirect_t *ike_redirect_create(ike_sa_t *ike_sa, identification_t *to)
+{
+       private_ike_redirect_t *this;
+
+       INIT(this,
+               .public = {
+                       .task = {
+                               .get_type = _get_type,
+                               .build = _build_r,
+                               .process = _process_r,
+                               .migrate = _migrate,
+                               .destroy = _destroy,
+                       },
+               },
+               .ike_sa = ike_sa,
+       );
+
+       if (to)
+       {
+               this->gateway = to->clone(to);
+               this->public.task.build = _build_i;
+               this->public.task.process = _process_i;
+       }
+
+       return &this->public;
+}
diff --git a/src/libcharon/sa/ikev2/tasks/ike_redirect.h b/src/libcharon/sa/ikev2/tasks/ike_redirect.h
new file mode 100644 (file)
index 0000000..afa00ce
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 ike_redirect ike_redirect
+ * @{ @ingroup tasks_v2
+ */
+
+#ifndef IKE_REDIRECT_H_
+#define IKE_REDIRECT_H_
+
+typedef struct ike_redirect_t ike_redirect_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * Task that handles redirection requests for established SAs.
+ */
+struct ike_redirect_t {
+
+       /**
+        * Implements the task_t interface
+        */
+       task_t task;
+};
+
+/**
+ * Create a new ike_redirect_t task.
+ *
+ * As initiator (i.e. original responder) pass the ID of the target gateway,
+ * as responder (i.e. original initiator) this argument is NULL.
+ *
+ * @param ike_sa               IKE_SA this task works for
+ * @param to                   gateway ID (gets cloned), or NULL as responder
+ * @return                             task instance
+ */
+ike_redirect_t *ike_redirect_create(ike_sa_t *ike_sa,
+                                                                       identification_t *to);
+
+#endif /** IKE_REDIRECT_H_ @}*/
diff --git a/src/libcharon/sa/redirect_manager.c b/src/libcharon/sa/redirect_manager.c
new file mode 100644 (file)
index 0000000..ff92ac2
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 "redirect_manager.h"
+
+#include <collections/linked_list.h>
+#include <threading/rwlock.h>
+#include <bio/bio_reader.h>
+#include <bio/bio_writer.h>
+
+typedef struct private_redirect_manager_t private_redirect_manager_t;
+
+/**
+ * Private data
+ */
+struct private_redirect_manager_t {
+
+       /**
+        * Public interface
+        */
+       redirect_manager_t public;
+
+       /**
+        * Registered providers
+        */
+       linked_list_t *providers;
+
+       /**
+        * Lock to access list of providers
+        */
+       rwlock_t *lock;
+};
+
+
+/**
+ * Gateway identify types
+ *
+ * The encoding is the same as that for corresponding ID payloads.
+ */
+typedef enum {
+       /** IPv4 address of the VPN gateway */
+       GATEWAY_ID_TYPE_IPV4 = 1,
+       /** IPv6 address of the VPN gateway */
+       GATEWAY_ID_TYPE_IPV6 = 2,
+       /** FQDN of the VPN gateway */
+       GATEWAY_ID_TYPE_FQDN = 3,
+} gateway_id_type_t;
+
+/**
+ * Mapping of gateway identity types to identity types
+ */
+static id_type_t gateway_to_id_type(gateway_id_type_t type)
+{
+       switch (type)
+       {
+               case GATEWAY_ID_TYPE_IPV4:
+                       return ID_IPV4_ADDR;
+               case GATEWAY_ID_TYPE_IPV6:
+                       return ID_IPV6_ADDR;
+               case GATEWAY_ID_TYPE_FQDN:
+                       return ID_FQDN;
+               default:
+                       return 0;
+       }
+}
+
+/**
+ * Mapping of identity types to gateway identity types
+ */
+static gateway_id_type_t id_type_to_gateway(id_type_t type)
+{
+       switch (type)
+       {
+               case ID_IPV4_ADDR:
+                       return GATEWAY_ID_TYPE_IPV4;
+               case ID_IPV6_ADDR:
+                       return GATEWAY_ID_TYPE_IPV6;
+               case ID_FQDN:
+                       return GATEWAY_ID_TYPE_FQDN;
+               default:
+                       return 0;
+       }
+}
+
+METHOD(redirect_manager_t, add_provider, void,
+       private_redirect_manager_t *this, redirect_provider_t *provider)
+{
+       this->lock->write_lock(this->lock);
+       this->providers->insert_last(this->providers, provider);
+       this->lock->unlock(this->lock);
+}
+
+METHOD(redirect_manager_t, remove_provider, void,
+       private_redirect_manager_t *this, redirect_provider_t *provider)
+{
+       this->lock->write_lock(this->lock);
+       this->providers->remove(this->providers, provider, NULL);
+       this->lock->unlock(this->lock);
+}
+
+/**
+ * Determine whether a client should be redirected using the callback with the
+ * given offset into the redirect_provider_t interface.
+ */
+static bool should_redirect(private_redirect_manager_t *this, ike_sa_t *ike_sa,
+                                                       identification_t **gateway, size_t offset)
+{
+       enumerator_t *enumerator;
+       void *provider;
+       bool redirect = FALSE;
+
+       this->lock->read_lock(this->lock);
+       enumerator = this->providers->create_enumerator(this->providers);
+       while (enumerator->enumerate(enumerator, &provider))
+       {
+               bool (**method)(void*,ike_sa_t*,identification_t**) = provider + offset;
+               if (*method && (*method)(provider, ike_sa, gateway))
+               {
+                       if (*gateway && id_type_to_gateway((*gateway)->get_type(*gateway)))
+                       {
+                               redirect = TRUE;
+                               break;
+                       }
+                       else
+                       {
+                               DBG1(DBG_CFG, "redirect provider returned invalid gateway ID");
+                               DESTROY_IF(*gateway);
+                       }
+               }
+       }
+       enumerator->destroy(enumerator);
+       this->lock->unlock(this->lock);
+       return redirect;
+}
+
+METHOD(redirect_manager_t, redirect_on_init, bool,
+       private_redirect_manager_t *this, ike_sa_t *ike_sa,
+       identification_t **gateway)
+{
+       return should_redirect(this, ike_sa, gateway,
+                                                  offsetof(redirect_provider_t, redirect_on_init));
+}
+
+METHOD(redirect_manager_t, redirect_on_auth, bool,
+       private_redirect_manager_t *this, ike_sa_t *ike_sa,
+       identification_t **gateway)
+{
+       return should_redirect(this, ike_sa, gateway,
+                                                  offsetof(redirect_provider_t, redirect_on_auth));
+}
+
+METHOD(redirect_manager_t, destroy, void,
+       private_redirect_manager_t *this)
+{
+       this->providers->destroy(this->providers);
+       this->lock->destroy(this->lock);
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+redirect_manager_t *redirect_manager_create()
+{
+       private_redirect_manager_t *this;
+
+       INIT(this,
+               .public = {
+                       .add_provider = _add_provider,
+                       .remove_provider = _remove_provider,
+                       .redirect_on_init = _redirect_on_init,
+                       .redirect_on_auth = _redirect_on_auth,
+                       .destroy = _destroy,
+               },
+               .providers = linked_list_create(),
+               .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+       );
+
+       return &this->public;
+}
+
+/*
+ * Encoding of a REDIRECT or REDIRECTED_FROM notify
+ *
+                         1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    | Next Payload  |C|  RESERVED   |         Payload Length        |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |Protocol ID(=0)| SPI Size (=0) |      Notify Message Type      |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    | GW Ident Type |  GW Ident Len |                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               ~
+    ~                   New Responder GW Identity                   ~
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                                                               |
+    ~                        Nonce Data                             ~
+    |                                                               |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+
+/*
+ * Described in header
+ */
+chunk_t redirect_data_create(identification_t *gw, chunk_t nonce)
+{
+       gateway_id_type_t type;
+       bio_writer_t *writer;
+       chunk_t data;
+
+       type = id_type_to_gateway(gw->get_type(gw));
+       if (!type)
+       {
+               return chunk_empty;
+       }
+
+       writer = bio_writer_create(0);
+       writer->write_uint8(writer, type);
+       writer->write_data8(writer, gw->get_encoding(gw));
+       if (nonce.ptr)
+       {
+               writer->write_data(writer, nonce);
+       }
+
+       data = writer->extract_buf(writer);
+       writer->destroy(writer);
+       return data;
+}
+
+/*
+ * Described in header
+ */
+identification_t *redirect_data_parse(chunk_t data, chunk_t *nonce)
+{
+       bio_reader_t *reader;
+       id_type_t id_type;
+       chunk_t gateway;
+       u_int8_t type;
+
+       reader = bio_reader_create(data);
+       if (!reader->read_uint8(reader, &type) ||
+               !reader->read_data8(reader, &gateway))
+       {
+               DBG1(DBG_ENC, "invalid REDIRECT notify data");
+               reader->destroy(reader);
+               return NULL;
+       }
+       id_type = gateway_to_id_type(type);
+       if (!id_type)
+       {
+               DBG1(DBG_ENC, "invalid gateway ID type (%d) in REDIRECT notify", type);
+               reader->destroy(reader);
+               return NULL;
+       }
+       if (nonce)
+       {
+               *nonce = chunk_clone(reader->peek(reader));
+       }
+       reader->destroy(reader);
+       return identification_create_from_encoding(id_type, gateway);
+}
diff --git a/src/libcharon/sa/redirect_manager.h b/src/libcharon/sa/redirect_manager.h
new file mode 100644 (file)
index 0000000..45a727c
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 redirect_manager redirect_manager
+ * @{ @ingroup sa
+ */
+
+#ifndef REDIRECT_MANAGER_H_
+#define REDIRECT_MANAGER_H_
+
+typedef struct redirect_manager_t redirect_manager_t;
+
+#include <sa/redirect_provider.h>
+
+/**
+ * Manages redirect providers.
+ */
+struct redirect_manager_t {
+
+       /**
+        * Add a redirect provider.
+        *
+        * All registered providers are queried until one of them decides to
+        * redirect a client.
+        *
+        * A provider may be called concurrently for different IKE_SAs.
+        *
+        * @param provider      provider to register
+        */
+       void (*add_provider)(redirect_manager_t *this,
+                                                redirect_provider_t *provider);
+
+       /**
+        * Remove a redirect provider.
+        *
+        * @param provider      provider to unregister
+        */
+       void (*remove_provider)(redirect_manager_t *this,
+                                                       redirect_provider_t *provider);
+
+       /**
+        * Determine whether a client should be redirected upon receipt of the
+        * IKE_SA_INIT message.
+        *
+        * @param ike_sa                IKE_SA for which this is called
+        * @param gateway[out]  new IKE gateway (IP or FQDN)
+        * @return                              TRUE if client should be redirected, FALSE otherwise
+        */
+       bool (*redirect_on_init)(redirect_manager_t *this, ike_sa_t *ike_sa,
+                                                        identification_t **gateway);
+
+       /**
+        * Determine whether a client should be redirected after the IKE_AUTH has
+        * been handled.  Should be called after the client is authenticated and
+        * when the server authenticates itself.
+        *
+        * @param ike_sa                IKE_SA for which this is called
+        * @param gateway[out]  new IKE gateway (IP or FQDN)
+        * @return                              TRUE if client should be redirected, FALSE otherwise
+        */
+       bool (*redirect_on_auth)(redirect_manager_t *this, ike_sa_t *ike_sa,
+                                                        identification_t **gateway);
+
+       /**
+        * Destroy this instance.
+        */
+       void (*destroy)(redirect_manager_t *this);
+};
+
+/**
+ * Create a redirect manager instance.
+ *
+ * @return                                     manager instance
+ */
+redirect_manager_t *redirect_manager_create();
+
+/**
+ * Create notification data of a REDIRECT or REDIRECT_FROM payload using the
+ * given gateway identity and optional nonce (only used during IKE_SA_INIT).
+ *
+ * @param gw                           gateway identity (IP or FQDN), gets cloned
+ * @param nonce                                nonce value, or chunk_empty, gets cloned
+ * @return                                     notify data, chunk_empty if ID type is not supported
+ */
+chunk_t redirect_data_create(identification_t *gw, chunk_t nonce);
+
+/**
+ * Parse notification data of a REDIRECT or REDIRECTED_FROM notify payload.
+ *
+ * @param data                         notification data to parse
+ * @param nonce[out]           nonce data (allocated), if any was provided
+ * @return                                     gateway identity, NULL if data is invalid
+ */
+identification_t *redirect_data_parse(chunk_t data, chunk_t *nonce);
+
+#endif /** REDIRECT_MANAGER_H_ @}*/
diff --git a/src/libcharon/sa/redirect_provider.h b/src/libcharon/sa/redirect_provider.h
new file mode 100644 (file)
index 0000000..ef2288f
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 redirect_provider redirect_provider
+ * @{ @ingroup sa
+ */
+
+#ifndef REDIRECT_PROVIDER_H_
+#define REDIRECT_PROVIDER_H_
+
+typedef struct redirect_provider_t redirect_provider_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+
+/**
+ * Interface that allows implementations to decide whether a client is
+ * redirected during IKE_SA_INIT or IKE_AUTH using RFC 5685.
+ */
+struct redirect_provider_t {
+
+       /**
+        * Decide whether a client is redirect directly upon receipt of the
+        * IKE_SA_INIT message.
+        *
+        * @param ike_sa                IKE_SA for which this is called
+        * @param gateway[out]  new IKE gateway (IP or FQDN)
+        * @return                              TRUE if client should be redirected, FALSE otherwise
+        */
+       bool (*redirect_on_init)(redirect_provider_t *this, ike_sa_t *ike_sa,
+                                                        identification_t **gateway);
+
+       /**
+        * Decide whether a client is redirect after the IKE_AUTH has been
+        * handled.  This is called after the client is authenticated and when the
+        * server authenticates itself.
+        *
+        * @param ike_sa                IKE_SA for which this is called
+        * @param gateway[out]  new IKE gateway (IP or FQDN)
+        * @return                              TRUE if client should be redirected, FALSE otherwise
+        */
+       bool (*redirect_on_auth)(redirect_provider_t *this, ike_sa_t *ike_sa,
+                                                        identification_t **gateway);
+};
+
+#endif /** REDIRECT_PROVIDER_H_ @}*/
index b35b581..4bd2ba2 100644 (file)
@@ -28,6 +28,7 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_ISAKMP_CERT_POST,
        "IKE_REKEY",
        "IKE_REAUTH",
        "IKE_REAUTH_COMPLETE",
+       "IKE_REDIRECT",
        "IKE_DELETE",
        "IKE_DPD",
        "IKE_VENDOR",
index 7bd3da1..b2e9d88 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 Tobias Brunner
+ * Copyright (C) 2007-2015 Tobias Brunner
  * Copyright (C) 2006 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -57,6 +57,8 @@ enum task_type_t {
        TASK_IKE_REAUTH,
        /** completion task for make-before-break IKE_SA re-authentication */
        TASK_IKE_REAUTH_COMPLETE,
+       /** redirect an active IKE_SA */
+       TASK_IKE_REDIRECT,
        /** delete an IKE_SA */
        TASK_IKE_DELETE,
        /** liveness check */
@@ -154,6 +156,18 @@ struct task_t {
        status_t (*process) (task_t *this, message_t *message);
 
        /**
+        * Verify a message before processing it (optional to implement by tasks).
+        *
+        * @param message               message to verify
+        * @return
+        *                                              - FAILED if verification is not successful, the
+        *                                                message will be silently discarded
+        *                                              - DESTROY_ME if IKE_SA has to be destroyed
+        *                                              - SUCCESS if verification is successful
+        */
+       status_t (*pre_process) (task_t *this, message_t *message);
+
+       /**
         * Get the type of the task implementation.
         */
        task_type_t (*get_type) (task_t *this);
index 5b6b8e4..fb02714 100644 (file)
@@ -4,6 +4,7 @@ swanctl_SOURCES = \
        command.c command.h \
        commands/initiate.c \
        commands/terminate.c \
+       commands/redirect.c \
        commands/install.c \
        commands/list_sas.c \
        commands/list_pols.c \
index 7eb11a6..8d0a2e6 100644 (file)
@@ -27,7 +27,7 @@
 /**
  * Maximum number of commands (+1).
  */
-#define MAX_COMMANDS 22
+#define MAX_COMMANDS 23
 
 /**
  * Maximum number of options in a command (+3)
diff --git a/src/swanctl/commands/redirect.c b/src/swanctl/commands/redirect.c
new file mode 100644 (file)
index 0000000..6edb936
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 "command.h"
+
+#include <errno.h>
+
+static int redirect(vici_conn_t *conn)
+{
+       vici_req_t *req;
+       vici_res_t *res;
+       command_format_options_t format = COMMAND_FORMAT_NONE;
+       char *arg, *peer_ip = NULL, *peer_id = NULL, *ike = NULL, *gateway = NULL;
+       int ret = 0, ike_id = 0;
+
+       while (TRUE)
+       {
+               switch (command_getopt(&arg))
+               {
+                       case 'h':
+                               return command_usage(NULL);
+                       case 'P':
+                               format |= COMMAND_FORMAT_PRETTY;
+                               /* fall through to raw */
+                       case 'r':
+                               format |= COMMAND_FORMAT_RAW;
+                               continue;
+                       case 'i':
+                               ike = arg;
+                               continue;
+                       case 'I':
+                               ike_id = atoi(arg);
+                               continue;
+                       case 'p':
+                               peer_ip = arg;
+                               continue;
+                       case 'd':
+                               peer_id = arg;
+                               continue;
+                       case 'g':
+                               gateway = arg;
+                               continue;
+                       case EOF:
+                               break;
+                       default:
+                               return command_usage("invalid --redirect option");
+               }
+               break;
+       }
+       req = vici_begin("redirect");
+       if (ike)
+       {
+               vici_add_key_valuef(req, "ike", "%s", ike);
+       }
+       if (ike_id)
+       {
+               vici_add_key_valuef(req, "ike-id", "%d", ike_id);
+       }
+       if (peer_ip)
+       {
+               vici_add_key_valuef(req, "peer-ip", "%s", peer_ip);
+       }
+       if (peer_id)
+       {
+               vici_add_key_valuef(req, "peer-id", "%s", peer_id);
+       }
+       if (gateway)
+       {
+               vici_add_key_valuef(req, "gateway", "%s", gateway);
+       }
+       res = vici_submit(req, conn);
+       if (!res)
+       {
+               ret = errno;
+               fprintf(stderr, "redirect request failed: %s\n", strerror(errno));
+               return ret;
+       }
+       if (format & COMMAND_FORMAT_RAW)
+       {
+               vici_dump(res, "redirect reply", format & COMMAND_FORMAT_PRETTY,
+                                 stdout);
+       }
+       else
+       {
+               if (streq(vici_find_str(res, "no", "success"), "yes"))
+               {
+                       printf("redirect completed successfully\n");
+               }
+               else
+               {
+                       fprintf(stderr, "redirect failed: %s\n",
+                                       vici_find_str(res, "", "errmsg"));
+                       ret = 1;
+               }
+       }
+       vici_free_res(res);
+       return ret;
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+       command_register((command_t) {
+               redirect, 'd', "redirect", "redirect an IKE_SA",
+               {"--ike <name> | --ike-id <id> | --peer-ip <ip|subnet|range>",
+                "--peer-id <id|wildcards> | --gateway <ip|fqdn> [--raw|--pretty]"},
+               {
+                       {"help",                'h', 0, "show usage information"},
+                       {"ike",                 'i', 1, "redirect by IKE_SA name"},
+                       {"ike-id",              'I', 1, "redirect by IKE_SA unique identifier"},
+                       {"peer-ip",             'p', 1, "redirect by client IP"},
+                       {"peer-id",             'd', 1, "redirect by IKE_SA name"},
+                       {"gateway",             'g', 1, "target gateway (IP or FQDN)"},
+                       {"raw",                 'r', 0, "dump raw response message"},
+                       {"pretty",              'P', 0, "dump raw response message in pretty print"},
+               }
+       });
+}
index 4b49d30..a307460 100644 (file)
@@ -41,6 +41,10 @@ initiate a connection
 \-\-terminate\fR
 terminate a connection
 .TP
+.B "\-d, \-\-redirect"
+\-\-redirect\fR
+redirect an IKE_SA
+.TP
 .B "\-p, \-\-install"
 install a trap or shunt policy
 .TP
diff --git a/testing/tests/ikev2/redirect-active/description.txt b/testing/tests/ikev2/redirect-active/description.txt
new file mode 100644 (file)
index 0000000..942c66e
--- /dev/null
@@ -0,0 +1,8 @@
+The roadwarriors <b>carol</b> and <b>dave</b> set up a connection each
+to gateway <b>alice</b>, which identifies itself as <b>mars</b>.
+Host <b>venus</b> in the subnet behind this gateway uses gateway <b>moon</b> as
+default gateway, so it is not fully reachable by the two roadwarriors.
+Then both SAs are actively redirected with the <em>swanctl --redirect</em>
+command to gateway <b>moon</b> (also identified as <b>mars</b>) using the
+mechanism described in <b>RFC 5685</b> enabling the roadwarriors to
+ping <b>venus</b>.
diff --git a/testing/tests/ikev2/redirect-active/evaltest.dat b/testing/tests/ikev2/redirect-active/evaltest.dat
new file mode 100644 (file)
index 0000000..6d84173
--- /dev/null
@@ -0,0 +1,28 @@
+alice::ipsec status 2> /dev/null::rw\[1].*ESTABLISHED.*mars.strongswan.org.*carol@strongswan.org::YES
+alice::ipsec status 2> /dev/null::rw\[2].*ESTABLISHED.*mars.strongswan.org.*dave@strongswan.org::YES
+carol::ipsec status 2> /dev/null::home.*ESTABLISHED.*carol@strongswan.org.*mars.strongswan.org::YES
+dave:: ipsec status 2> /dev/null::home.*ESTABLISHED.*dave@strongswan.org.*mars.strongswan.org::YES
+carol::ping -c 1 -W 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_req=1::NO
+dave:: ping -c 1 -W 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_req=1::NO
+alice::swanctl --redirect --peer-ip PH_IP_CAROL --gateway 192.168.0.1::redirect completed successfully::YES
+alice::swanctl --redirect --peer-id dave@strongswan.org --gateway moon.strongswan.org::redirect completed successfully::YES
+carol::sleep 1::No output expected::NO
+carol::cat /var/log/daemon.log::redirected to 192.168.0.1::YES
+dave::cat /var/log/daemon.log::redirected to moon.strongswan.org::YES
+moon::cat /var/log/daemon.log::client got redirected from 192.168.0.5::YES
+moon::ipsec status 2> /dev/null::rw\[1].*ESTABLISHED.*mars.strongswan.org.*carol@strongswan.org::YES
+moon::ipsec status 2> /dev/null::rw\[2].*ESTABLISHED.*mars.strongswan.org.*dave@strongswan.org::YES
+carol::ping -c 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_req=1::YES
+dave:: ping -c 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_req=1::YES
+carol::tcpdump::IP carol.strongswan.org > mars.strongswan.org: ESP::YES
+carol::tcpdump::IP mars.strongswan.org > carol.strongswan.org: ESP::NO
+carol::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES
+carol::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES
+dave::tcpdump::IP dave.strongswan.org > mars.strongswan.org: ESP::YES
+dave::tcpdump::IP mars.strongswan.org > dave.strongswan.org: ESP::NO
+dave::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES
+dave::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES
+venus::tcpdump::IP carol.strongswan.org > venus.strongswan.org: ICMP echo request::YES
+venus::tcpdump::IP venus.strongswan.org > carol.strongswan.org: ICMP echo reply::YES
+venus::tcpdump::IP dave.strongswan.org > venus.strongswan.org: ICMP echo request::YES
+venus::tcpdump::IP venus.strongswan.org > dave.strongswan.org: ICMP echo reply::YES
diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.conf b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.conf
new file mode 100644 (file)
index 0000000..6a81055
--- /dev/null
@@ -0,0 +1,19 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+
+conn %default
+       ikelifetime=60m
+       keylife=20m
+       rekeymargin=3m
+       keyingtries=1
+
+conn rw
+       left=192.168.0.5
+       leftcert=marsCert.pem
+       leftid=mars.strongswan.org
+       leftsubnet=10.1.0.0/16
+       leftfirewall=yes
+       right=%any
+       keyexchange=ikev2
+       auto=add
diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/certs/marsCert.pem b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/certs/marsCert.pem
new file mode 100644 (file)
index 0000000..cf09774
--- /dev/null
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEQDCCAyigAwIBAgIBNDANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJDSDEZ
+MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS
+b290IENBMB4XDTE1MTEyNjA4MjQ1M1oXDTE5MDkwNjA4MjQ1M1owZDELMAkGA1UE
+BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xHDAaBgNVBAsTE1ZpcnR1
+YWwgVlBOIEdhdGV3YXkxHDAaBgNVBAMTE21hcnMuc3Ryb25nc3dhbi5vcmcwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxTvcinIErfzu8YZKl+a2MNTVY
++qjG83OkweWjWed/egAugxRjTyWeKjrSNf6DWpXC0pNhzja+Sj0njz4Kq8VczkFB
+/yYEkFKpdpFQHXvbQo5h7wn1i3Omhvou5x0rRfddRpLGl7Z/Xg8gnHsZMHHN60hI
+qiLZdRSu5QF25WNcy9tO58gz8Hvo5jsk8YDu6eGYolaygQo3cqxx/r8yWzehju0F
+oWm4sLOrq+OTegeCQYI8PsOALAwLWgWdGfMPMT0ntq9tD97AcUD0EilvcXVdmwUW
+44/hM9BZ0JS7N7Wv6fgIChC7xhZj1yo6vY+FDsmNMhz7vdeulKx2dqRJkn0XAgMB
+AAGjggEaMIIBFjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIDqDAdBgNVHQ4EFgQUvo44
+06FQwLI8P6VqNehTNHa2LNMwbQYDVR0jBGYwZIAUXafdcAZRMn7ntm2zteXgYOou
+Te+hSaRHMEUxCzAJBgNVBAYTAkNIMRkwFwYDVQQKExBMaW51eCBzdHJvbmdTd2Fu
+MRswGQYDVQQDExJzdHJvbmdTd2FuIFJvb3QgQ0GCAQAwHgYDVR0RBBcwFYITbWFy
+cy5zdHJvbmdzd2FuLm9yZzATBgNVHSUEDDAKBggrBgEFBQcDATA5BgNVHR8EMjAw
+MC6gLKAqhihodHRwOi8vY3JsLnN0cm9uZ3N3YW4ub3JnL3N0cm9uZ3N3YW4uY3Js
+MA0GCSqGSIb3DQEBCwUAA4IBAQCWvHc0v5BhxsLRxPvcOu1bVoeexzA/5nftfRly
+PumU2hYFQ8p7soJiT/jYPmnfNvl6halnmS0FrqLdTMNfXj/6WCZ3w4Xnokc5PYW4
+oL1VqdEgR7jyQjifZuzRkuPw+yAFoT+HbMttLdRMVTq9NXlPXtGaJTWbx5Z/gGP3
+1Ry47WhvzJyRSysPh/+9WJFOwS6nf6DFbNuyVtNXQRzXjpsGefZgN9ysntNRFMMZ
+V0j+zFIkLBLh2WBkJsuLQM9EFAlZrpQZ7iwHuz7gU0jATxN6Qxnn9AemNPl4k8T3
+igPXeVhDbZFeTc+u/Z7X5i1HXhks2QGz/zGee6no54XOnBWB
+-----END CERTIFICATE-----
diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/private/marsKey.pem b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/private/marsKey.pem
new file mode 100644 (file)
index 0000000..14f9440
--- /dev/null
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAsU73IpyBK387vGGSpfmtjDU1WPqoxvNzpMHlo1nnf3oALoMU
+Y08lnio60jX+g1qVwtKTYc42vko9J48+CqvFXM5BQf8mBJBSqXaRUB1720KOYe8J
+9Ytzpob6LucdK0X3XUaSxpe2f14PIJx7GTBxzetISKoi2XUUruUBduVjXMvbTufI
+M/B76OY7JPGA7unhmKJWsoEKN3Kscf6/Mls3oY7tBaFpuLCzq6vjk3oHgkGCPD7D
+gCwMC1oFnRnzDzE9J7avbQ/ewHFA9BIpb3F1XZsFFuOP4TPQWdCUuze1r+n4CAoQ
+u8YWY9cqOr2PhQ7JjTIc+73XrpSsdnakSZJ9FwIDAQABAoIBAQCe+TYMClamMUXV
++YYK2noIJAzY4qVA/DbLmi3S8GDnovNWwKAfCm+oCCUFJPQ1wZpVO4h5QPNEWjvK
+pFv0JV6nfyZzjdkAboYu+uf+9PB//oS6uiAr0AS5YnweYYl9qxXGw+gFtzEhsavO
+iX61bwIapsIApMrqPld1GE7f03vrTjyqYeJC+V5WEVZe1fkZtKFFJiHN1boPwoWt
+VRQ4UGlq9eto5pddmMcPl/uR9/TItdMC8AZ7fcRYP9RdYFEp8jS+tx3CDNs0XrlF
+qKlroDSdwBt475Ud5irtNwjGoohl3xCHvEne9l+fjlmQFynfRNeOb9rgVIsT3OoO
+MFSeW9HBAoGBAOiAjYCWoQHSlbi3Nkd5fsfTd4iZOkeeZi+2PJOhDByH1Hsq87CP
+KoYejjYUQ6QvJY2WDJCU8qXxPJkDuI6XZ6QbTDPad2xXDkA4pxuPi/c+Hb/eqLQ+
+24ebIzauC5iLdddp22oQXQOngKwUMihYggKs9cIpem8G99bcMfq1OeaDAoGBAMM6
+Z790eySPDIaKZZ/KVOu4hJe1s5vIJqN9xtV0o55PTR2wFCgwCogiXQchyhF8Qo6q
+BEpcKpY1bAivmL8T2PBsLUuKC4LAr8uzW8rVFbG3HfC62RoaVOZWmH+yBABi6eej
+Lh8KNhal+HzVOlMsw78FAkhq9aubWBywxfULFyrdAoGAPmnsEn3dIPkf+iMsrgk+
+cHQjHYdMrtclxV8daeAMuUf5UUsZeeck2jYvpPLRqayCXzzNiK6DaOqWgtkN3TFl
+wBcX4L+f2XSRPco3id2CDFAfeldSYHiTRBbwYAhe0od1jes73IAiCA4aK5eMU2pX
+FFLL8mBiHpX3SDV6MTwfG/MCgYEApgLXUC3LvVXLL+kl65ARUHUSgzNmrNSxTptY
+5hCORcNnbxRYv6fCfOrDspngC6hfrJMUnOrs59g7qtMvD2TgN+m1RqP3j9Msagp1
+xvAVx45QbnFkxdaYVVpTyVpmHrH99xSC1rBHOFEgEX78XtgVCC1JOK2DhM7PSW5k
+lzA8Hh0CgYBVXNnQsy4x+9D09zeFP2TKXsk7N154yUP6oxXu6OjZC+cfxHOGyi/g
+PVe8ukBIknZkFRz+ZXdfZzXXuJslENOVeFEInqVAu2W55KeCPc/3SU6hB2lj6MBl
+WIZ7yrUmtdalU3cPkFnHkQTL2glYAzjB2z22AxyXNzy37ZhtFpovtg==
+-----END RSA PRIVATE KEY-----
diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.secrets b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.secrets
new file mode 100644 (file)
index 0000000..d65b96e
--- /dev/null
@@ -0,0 +1,3 @@
+# /etc/ipsec.secrets - strongSwan IPsec secrets file
+
+: RSA marsKey.pem
diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/iptables.rules b/testing/tests/ikev2/redirect-active/hosts/alice/etc/iptables.rules
new file mode 100644 (file)
index 0000000..33c22b9
--- /dev/null
@@ -0,0 +1,34 @@
+*filter
+
+# default policy is DROP
+-P INPUT DROP
+-P OUTPUT DROP
+-P FORWARD DROP
+
+# forward ESP-tunneled traffic
+-A FORWARD -i eth1 -m policy --dir in  --pol ipsec --proto esp -s PH_IP_CAROL -j ACCEPT
+-A FORWARD -i eth1 -m policy --dir in  --pol ipsec --proto esp -s PH_IP_DAVE  -j ACCEPT
+-A FORWARD -o eth1 -m policy --dir out --pol ipsec --proto esp -j ACCEPT
+
+# allow esp
+-A INPUT  -p 50 -j ACCEPT
+-A OUTPUT -p 50 -d PH_IP_CAROL -j ACCEPT
+-A OUTPUT -p 50 -d PH_IP_DAVE -j ACCEPT
+
+# allow IKE
+-A INPUT  -i eth1 -p udp --sport 500 --dport 500 -j ACCEPT
+-A OUTPUT -o eth1 -p udp --dport 500 --sport 500 -j ACCEPT
+
+# allow MobIKE
+-A INPUT  -i eth1 -p udp --sport 4500 --dport 4500 -j ACCEPT
+-A OUTPUT -o eth1 -p udp --dport 4500 --sport 4500 -j ACCEPT
+
+# allow crl fetch from winnetou
+-A INPUT  -i eth1 -p tcp --sport 80 -s PH_IP_WINNETOU -j ACCEPT
+-A OUTPUT -o eth1 -p tcp --dport 80 -d PH_IP_WINNETOU -j ACCEPT
+
+# allow ssh
+-A INPUT  -p tcp --dport 22 -j ACCEPT
+-A OUTPUT -p tcp --sport 22 -j ACCEPT
+
+COMMIT
diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/strongswan.conf b/testing/tests/ikev2/redirect-active/hosts/alice/etc/strongswan.conf
new file mode 100644 (file)
index 0000000..6f7f4c4
--- /dev/null
@@ -0,0 +1,6 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+  load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 curl revocation hmac stroke kernel-netlink socket-default updown vici
+}
+
diff --git a/testing/tests/ikev2/redirect-active/hosts/carol/etc/ipsec.conf b/testing/tests/ikev2/redirect-active/hosts/carol/etc/ipsec.conf
new file mode 100644 (file)
index 0000000..a5c2cc5
--- /dev/null
@@ -0,0 +1,19 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+       charondebug="knl 2"
+conn %default
+       ikelifetime=60m
+       keylife=20m
+       rekeymargin=3m
+       keyingtries=1
+
+conn home
+       leftcert=carolCert.pem
+       leftid=carol@strongswan.org
+       leftfirewall=yes
+       right=192.168.0.5
+       rightid=mars.strongswan.org
+       rightsubnet=10.1.0.0/16
+       keyexchange=ikev2
+       auto=add
diff --git a/testing/tests/ikev2/redirect-active/hosts/carol/etc/strongswan.conf b/testing/tests/ikev2/redirect-active/hosts/carol/etc/strongswan.conf
new file mode 100644 (file)
index 0000000..e58af9e
--- /dev/null
@@ -0,0 +1,5 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+  load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 curl revocation hmac xcbc ctr ccm gcm stroke kernel-netlink socket-default updown
+}
diff --git a/testing/tests/ikev2/redirect-active/hosts/dave/etc/ipsec.conf b/testing/tests/ikev2/redirect-active/hosts/dave/etc/ipsec.conf
new file mode 100644 (file)
index 0000000..642811d
--- /dev/null
@@ -0,0 +1,19 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+
+conn %default
+       ikelifetime=60m
+       keylife=20m
+       rekeymargin=3m
+       keyingtries=1
+
+conn home
+       leftcert=daveCert.pem
+       leftid=dave@strongswan.org
+       leftfirewall=yes
+       right=192.168.0.5
+       rightid=mars.strongswan.org
+       rightsubnet=10.1.0.0/16
+       keyexchange=ikev2
+       auto=add
diff --git a/testing/tests/ikev2/redirect-active/hosts/dave/etc/strongswan.conf b/testing/tests/ikev2/redirect-active/hosts/dave/etc/strongswan.conf
new file mode 100644 (file)
index 0000000..ecbad66
--- /dev/null
@@ -0,0 +1,6 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+  load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 curl revocation hmac xcbc ctr ccm gcm stroke kernel-netlink socket-default updown
+}
+
diff --git a/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.conf b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.conf
new file mode 100644 (file)
index 0000000..fa901cb
--- /dev/null
@@ -0,0 +1,18 @@
+# /etc/ipsec.conf - strongSwan IPsec configuration file
+
+config setup
+
+conn %default
+       ikelifetime=60m
+       keylife=20m
+       rekeymargin=3m
+       keyingtries=1
+
+conn rw
+       leftcert=marsCert.pem
+       leftid=mars.strongswan.org
+       leftsubnet=10.1.0.0/16
+       leftfirewall=yes
+       right=%any
+       keyexchange=ikev2
+       auto=add
diff --git a/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/certs/marsCert.pem b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/certs/marsCert.pem
new file mode 100644 (file)
index 0000000..cf09774
--- /dev/null
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIEQDCCAyigAwIBAgIBNDANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJDSDEZ
+MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS
+b290IENBMB4XDTE1MTEyNjA4MjQ1M1oXDTE5MDkwNjA4MjQ1M1owZDELMAkGA1UE
+BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xHDAaBgNVBAsTE1ZpcnR1
+YWwgVlBOIEdhdGV3YXkxHDAaBgNVBAMTE21hcnMuc3Ryb25nc3dhbi5vcmcwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxTvcinIErfzu8YZKl+a2MNTVY
++qjG83OkweWjWed/egAugxRjTyWeKjrSNf6DWpXC0pNhzja+Sj0njz4Kq8VczkFB
+/yYEkFKpdpFQHXvbQo5h7wn1i3Omhvou5x0rRfddRpLGl7Z/Xg8gnHsZMHHN60hI
+qiLZdRSu5QF25WNcy9tO58gz8Hvo5jsk8YDu6eGYolaygQo3cqxx/r8yWzehju0F
+oWm4sLOrq+OTegeCQYI8PsOALAwLWgWdGfMPMT0ntq9tD97AcUD0EilvcXVdmwUW
+44/hM9BZ0JS7N7Wv6fgIChC7xhZj1yo6vY+FDsmNMhz7vdeulKx2dqRJkn0XAgMB
+AAGjggEaMIIBFjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIDqDAdBgNVHQ4EFgQUvo44
+06FQwLI8P6VqNehTNHa2LNMwbQYDVR0jBGYwZIAUXafdcAZRMn7ntm2zteXgYOou
+Te+hSaRHMEUxCzAJBgNVBAYTAkNIMRkwFwYDVQQKExBMaW51eCBzdHJvbmdTd2Fu
+MRswGQYDVQQDExJzdHJvbmdTd2FuIFJvb3QgQ0GCAQAwHgYDVR0RBBcwFYITbWFy
+cy5zdHJvbmdzd2FuLm9yZzATBgNVHSUEDDAKBggrBgEFBQcDATA5BgNVHR8EMjAw
+MC6gLKAqhihodHRwOi8vY3JsLnN0cm9uZ3N3YW4ub3JnL3N0cm9uZ3N3YW4uY3Js
+MA0GCSqGSIb3DQEBCwUAA4IBAQCWvHc0v5BhxsLRxPvcOu1bVoeexzA/5nftfRly
+PumU2hYFQ8p7soJiT/jYPmnfNvl6halnmS0FrqLdTMNfXj/6WCZ3w4Xnokc5PYW4
+oL1VqdEgR7jyQjifZuzRkuPw+yAFoT+HbMttLdRMVTq9NXlPXtGaJTWbx5Z/gGP3
+1Ry47WhvzJyRSysPh/+9WJFOwS6nf6DFbNuyVtNXQRzXjpsGefZgN9ysntNRFMMZ
+V0j+zFIkLBLh2WBkJsuLQM9EFAlZrpQZ7iwHuz7gU0jATxN6Qxnn9AemNPl4k8T3
+igPXeVhDbZFeTc+u/Z7X5i1HXhks2QGz/zGee6no54XOnBWB
+-----END CERTIFICATE-----
diff --git a/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/private/marsKey.pem b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/private/marsKey.pem
new file mode 100644 (file)
index 0000000..14f9440
--- /dev/null
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAsU73IpyBK387vGGSpfmtjDU1WPqoxvNzpMHlo1nnf3oALoMU
+Y08lnio60jX+g1qVwtKTYc42vko9J48+CqvFXM5BQf8mBJBSqXaRUB1720KOYe8J
+9Ytzpob6LucdK0X3XUaSxpe2f14PIJx7GTBxzetISKoi2XUUruUBduVjXMvbTufI
+M/B76OY7JPGA7unhmKJWsoEKN3Kscf6/Mls3oY7tBaFpuLCzq6vjk3oHgkGCPD7D
+gCwMC1oFnRnzDzE9J7avbQ/ewHFA9BIpb3F1XZsFFuOP4TPQWdCUuze1r+n4CAoQ
+u8YWY9cqOr2PhQ7JjTIc+73XrpSsdnakSZJ9FwIDAQABAoIBAQCe+TYMClamMUXV
++YYK2noIJAzY4qVA/DbLmi3S8GDnovNWwKAfCm+oCCUFJPQ1wZpVO4h5QPNEWjvK
+pFv0JV6nfyZzjdkAboYu+uf+9PB//oS6uiAr0AS5YnweYYl9qxXGw+gFtzEhsavO
+iX61bwIapsIApMrqPld1GE7f03vrTjyqYeJC+V5WEVZe1fkZtKFFJiHN1boPwoWt
+VRQ4UGlq9eto5pddmMcPl/uR9/TItdMC8AZ7fcRYP9RdYFEp8jS+tx3CDNs0XrlF
+qKlroDSdwBt475Ud5irtNwjGoohl3xCHvEne9l+fjlmQFynfRNeOb9rgVIsT3OoO
+MFSeW9HBAoGBAOiAjYCWoQHSlbi3Nkd5fsfTd4iZOkeeZi+2PJOhDByH1Hsq87CP
+KoYejjYUQ6QvJY2WDJCU8qXxPJkDuI6XZ6QbTDPad2xXDkA4pxuPi/c+Hb/eqLQ+
+24ebIzauC5iLdddp22oQXQOngKwUMihYggKs9cIpem8G99bcMfq1OeaDAoGBAMM6
+Z790eySPDIaKZZ/KVOu4hJe1s5vIJqN9xtV0o55PTR2wFCgwCogiXQchyhF8Qo6q
+BEpcKpY1bAivmL8T2PBsLUuKC4LAr8uzW8rVFbG3HfC62RoaVOZWmH+yBABi6eej
+Lh8KNhal+HzVOlMsw78FAkhq9aubWBywxfULFyrdAoGAPmnsEn3dIPkf+iMsrgk+
+cHQjHYdMrtclxV8daeAMuUf5UUsZeeck2jYvpPLRqayCXzzNiK6DaOqWgtkN3TFl
+wBcX4L+f2XSRPco3id2CDFAfeldSYHiTRBbwYAhe0od1jes73IAiCA4aK5eMU2pX
+FFLL8mBiHpX3SDV6MTwfG/MCgYEApgLXUC3LvVXLL+kl65ARUHUSgzNmrNSxTptY
+5hCORcNnbxRYv6fCfOrDspngC6hfrJMUnOrs59g7qtMvD2TgN+m1RqP3j9Msagp1
+xvAVx45QbnFkxdaYVVpTyVpmHrH99xSC1rBHOFEgEX78XtgVCC1JOK2DhM7PSW5k
+lzA8Hh0CgYBVXNnQsy4x+9D09zeFP2TKXsk7N154yUP6oxXu6OjZC+cfxHOGyi/g
+PVe8ukBIknZkFRz+ZXdfZzXXuJslENOVeFEInqVAu2W55KeCPc/3SU6hB2lj6MBl
+WIZ7yrUmtdalU3cPkFnHkQTL2glYAzjB2z22AxyXNzy37ZhtFpovtg==
+-----END RSA PRIVATE KEY-----
diff --git a/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.secrets b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.secrets
new file mode 100644 (file)
index 0000000..d65b96e
--- /dev/null
@@ -0,0 +1,3 @@
+# /etc/ipsec.secrets - strongSwan IPsec secrets file
+
+: RSA marsKey.pem
diff --git a/testing/tests/ikev2/redirect-active/hosts/moon/etc/strongswan.conf b/testing/tests/ikev2/redirect-active/hosts/moon/etc/strongswan.conf
new file mode 100644 (file)
index 0000000..7c415b1
--- /dev/null
@@ -0,0 +1,6 @@
+# /etc/strongswan.conf - strongSwan configuration file
+
+charon {
+  load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 curl revocation hmac stroke kernel-netlink socket-default updown
+}
+
diff --git a/testing/tests/ikev2/redirect-active/posttest.dat b/testing/tests/ikev2/redirect-active/posttest.dat
new file mode 100644 (file)
index 0000000..7e436a6
--- /dev/null
@@ -0,0 +1,10 @@
+carol::ipsec stop
+dave::ipsec stop
+moon::ipsec stop
+alice::ipsec stop
+moon::iptables-restore < /etc/iptables.flush
+alice::iptables-restore < /etc/iptables.flush
+carol::iptables-restore < /etc/iptables.flush
+dave::iptables-restore < /etc/iptables.flush
+alice::ip addr del 192.168.0.5/24 dev eth1
+alice::ifdown eth1
diff --git a/testing/tests/ikev2/redirect-active/pretest.dat b/testing/tests/ikev2/redirect-active/pretest.dat
new file mode 100644 (file)
index 0000000..3f7ac1d
--- /dev/null
@@ -0,0 +1,13 @@
+alice::ifup eth1
+alice::ip addr add 192.168.0.5/24 dev eth1
+moon::iptables-restore < /etc/iptables.rules
+alice::iptables-restore < /etc/iptables.rules
+carol::iptables-restore < /etc/iptables.rules
+dave::iptables-restore < /etc/iptables.rules
+moon::ipsec start
+alice::ipsec start
+carol::ipsec start
+dave::ipsec start
+carol::sleep 1
+carol::ipsec up home
+dave::ipsec up home
diff --git a/testing/tests/ikev2/redirect-active/test.conf b/testing/tests/ikev2/redirect-active/test.conf
new file mode 100644 (file)
index 0000000..8056d9c
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# This configuration file provides information on the
+# guest instances used for this test
+
+# All guest instances that are required for this test
+#
+VIRTHOSTS="alice venus moon carol winnetou dave"
+
+# Corresponding block diagram
+#
+DIAGRAM="a-v-m-c-w-d.png"
+
+# Guest instances on which tcpdump is to be started
+#
+TCPDUMPHOSTS="venus carol dave"
+
+# Guest instances on which IPsec is started
+# Used for IPsec logging purposes
+#
+IPSECHOSTS="alice moon carol dave"