Support for closing CHILD/IKE_SA if a CHILD_SA is inactive.
authorMartin Willi <martin@strongswan.org>
Tue, 12 Jan 2010 09:16:34 +0000 (10:16 +0100)
committerMartin Willi <martin@strongswan.org>
Tue, 12 Jan 2010 09:23:42 +0000 (10:23 +0100)
NEWS
src/charon/Makefile.am
src/charon/processing/jobs/inactivity_job.c [new file with mode: 0644]
src/charon/processing/jobs/inactivity_job.h [new file with mode: 0644]
src/charon/sa/tasks/child_create.c

diff --git a/NEWS b/NEWS
index 3fcb49c..e2b180a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,11 @@ strongswan-4.3.6
 
 - The IKEv1 and IKEV2 daemons now check certificate path length constraints.
 
+- The new strongswan.conf option "charon.inactivity_timeout" closes a CHILD_SA
+  if no traffic was sent or received within the given interval. To close the
+  complete IKE_SA if its only CHILD_SA was inactive, set
+  "charon.inactivity_close_ike" to yes.
+
 - More detailed IKEv2 EAP payload information in debug output
 
 - IKEv2 EAP-SIM and EAP-AKA share joint libsimaka library
index cb31b76..e20d45c 100644 (file)
@@ -60,6 +60,7 @@ processing/jobs/send_dpd_job.c processing/jobs/send_dpd_job.h \
 processing/jobs/send_keepalive_job.c processing/jobs/send_keepalive_job.h \
 processing/jobs/roam_job.c processing/jobs/roam_job.h \
 processing/jobs/update_sa_job.c processing/jobs/update_sa_job.h \
+processing/jobs/inactivity_job.c processing/jobs/inactivity_job.h \
 processing/scheduler.c processing/scheduler.h \
 processing/processor.c processing/processor.h  \
 sa/authenticators/authenticator.c sa/authenticators/authenticator.h \
diff --git a/src/charon/processing/jobs/inactivity_job.c b/src/charon/processing/jobs/inactivity_job.c
new file mode 100644 (file)
index 0000000..13fc5e3
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * 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 "inactivity_job.h"
+
+#include <daemon.h>
+
+typedef struct private_inactivity_job_t private_inactivity_job_t;
+
+/**
+ * Private data of an inactivity_job_t object.
+ */
+struct private_inactivity_job_t {
+
+       /**
+        * Public inactivity_job_t interface.
+        */
+       inactivity_job_t public;
+
+       /**
+        * Reqid of CHILD_SA to check
+        */
+       u_int32_t reqid;
+
+       /**
+        * Inactivity timeout
+        */
+       u_int32_t timeout;
+
+       /**
+        * Close IKE_SA if last remaining CHILD inactive?
+        */
+       bool close_ike;
+};
+
+METHOD(job_t, destroy, void,
+       private_inactivity_job_t *this)
+{
+       free(this);
+}
+
+METHOD(job_t, execute, void,
+       private_inactivity_job_t *this)
+{
+       ike_sa_t *ike_sa;
+       bool rescheduled = FALSE;
+
+       ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
+                                                                                                       this->reqid, TRUE);
+       if (ike_sa)
+       {
+               iterator_t *iterator;
+               child_sa_t *child_sa;
+               u_int32_t delete = 0;
+               protocol_id_t proto = 0;
+               int children = 0;
+               status_t status = SUCCESS;
+
+               iterator = ike_sa->create_child_sa_iterator(ike_sa);
+               while (iterator->iterate(iterator, (void**)&child_sa))
+               {
+                       if (child_sa->get_reqid(child_sa) == this->reqid)
+                       {
+                               time_t in, out, diff;
+
+                               child_sa->get_usestats(child_sa, TRUE, &in, NULL);
+                               child_sa->get_usestats(child_sa, FALSE, &out, NULL);
+
+                               diff = time_monotonic(NULL) - max(in, out);
+
+                               if (diff >= this->timeout)
+                               {
+                                       delete = child_sa->get_spi(child_sa, TRUE);
+                                       proto = child_sa->get_protocol(child_sa);
+                               }
+                               else
+                               {
+                                       charon->scheduler->schedule_job(charon->scheduler,
+                                                       &this->public.job_interface, this->timeout - diff);
+                                       rescheduled = TRUE;
+                               }
+                       }
+                       children++;
+               }
+               iterator->destroy(iterator);
+
+               if (delete)
+               {
+                       if (children == 1 && this->close_ike)
+                       {
+                               DBG1(DBG_JOB, "deleting IKE_SA after %d seconds "
+                                        "of CHILD_SA inactivity", this->timeout);
+                               status = ike_sa->delete(ike_sa);
+                       }
+                       else
+                       {
+                               DBG1(DBG_JOB, "deleting CHILD_SA after %d seconds "
+                                        "of inactivity", this->timeout);
+                               status = ike_sa->delete_child_sa(ike_sa, proto, delete);
+                       }
+               }
+               if (status == 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);
+               }
+       }
+       if (!rescheduled)
+       {
+               destroy(this);
+       }
+}
+
+/**
+ * See header
+ */
+inactivity_job_t *inactivity_job_create(u_int32_t reqid, u_int32_t timeout,
+                                                                               bool close_ike)
+{
+       private_inactivity_job_t *this;
+
+       INIT(this,
+               .public.job_interface = {
+                       .execute = _execute,
+                       .destroy = _destroy,
+               },
+               .reqid = reqid,
+               .timeout = timeout,
+               .close_ike = close_ike,
+       );
+
+       return &this->public;
+}
+
diff --git a/src/charon/processing/jobs/inactivity_job.h b/src/charon/processing/jobs/inactivity_job.h
new file mode 100644 (file)
index 0000000..9c9dace
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * 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 inactivity_job inactivity_job
+ * @{ @ingroup jobs
+ */
+
+#ifndef INACTIVITY_JOB_H_
+#define INACTIVITY_JOB_H_
+
+#include <library.h>
+#include <processing/jobs/job.h>
+
+typedef struct inactivity_job_t inactivity_job_t;
+
+/**
+ * Job checking for inactivity of CHILD_SA to close them.
+ *
+ * The inactivity job reschedules itself to check CHILD_SAs prediodically.
+ */
+struct inactivity_job_t {
+
+       /**
+        * Implements job_t.
+        */
+       job_t job_interface;
+};
+
+/**
+ * Create a inactivity_job instance.
+ *
+ * @param reqid                reqid of CHILD_SA to check for inactivity
+ * @param timeout      inactivity timeout in s
+ * @param close_ike    close IKE_SA if the last remaining CHILD_SA is inactive?
+ * @return                     inactivity checking job
+ */
+inactivity_job_t *inactivity_job_create(u_int32_t reqid, u_int32_t timeout,
+                                                                               bool close_ike);
+
+#endif /** INACTIVITY_JOB_H_ @}*/
index 6255776..04f8cc8 100644 (file)
@@ -26,6 +26,7 @@
 #include <encoding/payloads/nonce_payload.h>
 #include <encoding/payloads/notify_payload.h>
 #include <processing/jobs/delete_ike_sa_job.h>
+#include <processing/jobs/inactivity_job.h>
 
 
 typedef struct private_child_create_t private_child_create_t;
@@ -248,6 +249,25 @@ static bool allocate_spi(private_child_create_t *this)
 }
 
 /**
+ * Schedule inactivity timeout for CHILD_SA with reqid, if enabled
+ */
+static void schedule_inactivity_timeout(u_int32_t reqid)
+{
+       time_t timeout;
+       bool close_ike;
+
+       timeout = lib->settings->get_time(lib->settings,
+                                                                         "charon.inactivity_timeout", 0);
+       if (timeout)
+       {
+               close_ike = lib->settings->get_bool(lib->settings,
+                                                                               "charon.inactivity_close_ike", FALSE);
+               charon->scheduler->schedule_job(charon->scheduler,
+                       (job_t*)inactivity_job_create(reqid, timeout, close_ike), timeout);
+       }
+}
+
+/**
  * Install a CHILD_SA for usage, return value:
  * - FAILED: no acceptable proposal
  * - INVALID_ARG: diffie hellman group inacceptable
@@ -516,6 +536,11 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
        this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
        this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
        this->established = TRUE;
+
+       if (!this->rekey)
+       {       /* a rekeyed SA uses the same reqid, no need for a new job */
+               schedule_inactivity_timeout(this->child_sa->get_reqid(this->child_sa));
+       }
        return SUCCESS;
 }