added possibility to route CHILD_SAs, without to set them up
authorMartin Willi <martin@strongswan.org>
Fri, 21 Jul 2006 13:31:53 +0000 (13:31 -0000)
committerMartin Willi <martin@strongswan.org>
Fri, 21 Jul 2006 13:31:53 +0000 (13:31 -0000)
support for auto=route parameter
support for ipsec route and ipsec unroute
initiating of CHILD and/or IKE_SAs based on kernel acquires

26 files changed:
src/charon/Makefile.am
src/charon/config/traffic_selector.c
src/charon/config/traffic_selector.h
src/charon/queues/jobs/acquire_job.c [new file with mode: 0644]
src/charon/queues/jobs/acquire_job.h [new file with mode: 0644]
src/charon/queues/jobs/delete_child_sa_job.c
src/charon/queues/jobs/delete_child_sa_job.h
src/charon/queues/jobs/job.h
src/charon/queues/jobs/rekey_child_sa_job.c
src/charon/queues/jobs/rekey_child_sa_job.h
src/charon/queues/jobs/route_job.c [new file with mode: 0644]
src/charon/queues/jobs/route_job.h [new file with mode: 0644]
src/charon/sa/child_sa.c
src/charon/sa/child_sa.h
src/charon/sa/ike_sa.c
src/charon/sa/ike_sa.h
src/charon/sa/ike_sa_manager.c
src/charon/sa/ike_sa_manager.h
src/charon/threads/kernel_interface.c
src/charon/threads/stroke_interface.c
src/ipsec/ipsec.in
src/starter/starterstroke.c
src/stroke/stroke.c
src/stroke/stroke.h
src/stroke/stroke_keywords.h
src/stroke/stroke_keywords.txt

index 2a8735f..c8bd339 100644 (file)
@@ -46,6 +46,8 @@ queues/jobs/send_keepalive_job.c queues/jobs/send_keepalive_job.h \
 queues/jobs/rekey_child_sa_job.c queues/jobs/rekey_child_sa_job.h \
 queues/jobs/delete_child_sa_job.c queues/jobs/delete_child_sa_job.h \
 queues/jobs/send_dpd_job.c queues/jobs/send_dpd_job.h \
+queues/jobs/route_job.c queues/jobs/route_job.h \
+queues/jobs/acquire_job.c queues/jobs/acquire_job.h \
 queues/job_queue.c queues/event_queue.c queues/send_queue.h queues/job_queue.h queues/event_queue.h \
 queues/send_queue.c threads/kernel_interface.c threads/thread_pool.c threads/scheduler.c threads/sender.c \
 threads/sender.h threads/kernel_interface.h threads/scheduler.h threads/receiver.c threads/stroke_interface.c \
index 3a9480f..3d53d90 100644 (file)
@@ -248,6 +248,29 @@ static traffic_selector_t *get_subset(private_traffic_selector_t *this, private_
 }
 
 /**
+ * implements traffic_selector_t.equals
+ */
+static bool equals(private_traffic_selector_t *this, private_traffic_selector_t *other)
+{
+       if (this->type != other->type)
+       {
+               return FALSE;
+       }
+       if (this->type == TS_IPV4_ADDR_RANGE)
+       {
+               if (this->from_addr_ipv4 == other->from_addr_ipv4 &&
+                       this->to_addr_ipv4 == other->to_addr_ipv4 &&
+                       this->from_port == other->from_port &&
+                       this->to_port == other->to_port &&
+                       this->protocol == other->protocol)
+               {
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+/**
  * Implements traffic_selector_t.get_from_address.
  */
 static chunk_t get_from_address(private_traffic_selector_t *this)
@@ -518,6 +541,7 @@ static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, ts
 
        /* public functions */
        this->public.get_subset = (traffic_selector_t*(*)(traffic_selector_t*,traffic_selector_t*))get_subset;
+       this->public.equals = (bool(*)(traffic_selector_t*,traffic_selector_t*))equals;
        this->public.get_string = (char*(*)(traffic_selector_t*))get_string;
        this->public.get_from_address = (chunk_t(*)(traffic_selector_t*))get_from_address;
        this->public.get_to_address = (chunk_t(*)(traffic_selector_t*))get_to_address;
index 420f97d..90437f9 100644 (file)
@@ -184,6 +184,15 @@ struct traffic_selector_t {
        char* (*get_string) (traffic_selector_t *this);
        
        /**
+        * @brief Compare two traffic selectors for equality.
+        * 
+        * @param this          first to compare
+        * @param other         second to compare with first
+        * @return                      pointer to a string.
+        */
+       bool (*equals) (traffic_selector_t *this, traffic_selector_t *other);
+       
+       /**
         * @brief Destroys the ts object
         *
         * @param this          calling object
diff --git a/src/charon/queues/jobs/acquire_job.c b/src/charon/queues/jobs/acquire_job.c
new file mode 100644 (file)
index 0000000..89eccef
--- /dev/null
@@ -0,0 +1,104 @@
+/**
+ * @file acquire_job.c
+ * 
+ * @brief Implementation of acquire_job_t.
+ * 
+ */
+
+/*
+ * Copyright (C) 2006 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 "acquire_job.h"
+
+#include <daemon.h>
+
+
+typedef struct private_acquire_job_t private_acquire_job_t;
+
+/**
+ * Private data of an acquire_job_t object.
+ */
+struct private_acquire_job_t {
+       /**
+        * Public acquire_job_t interface.
+        */
+       acquire_job_t public;
+       
+       /**
+        * reqid of the child to rekey
+        */
+       u_int32_t reqid;
+       
+       /**
+        * Logger ref
+        */
+       logger_t *logger;
+};
+
+/**
+ * Implementation of job_t.get_type.
+ */
+static job_type_t get_type(private_acquire_job_t *this)
+{
+       return ACQUIRE;
+}
+
+/**
+ * Implementation of job_t.execute.
+ */
+static status_t execute(private_acquire_job_t *this)
+{
+       ike_sa_t *ike_sa;
+       
+       ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager,
+                                                                                                          this->reqid);
+       if (ike_sa == NULL)
+       {
+               this->logger->log(this->logger, ERROR|LEVEL1, 
+                                                 "CHILD_SA not found for acquiring");
+               return DESTROY_ME;
+       }
+       ike_sa->acquire(ike_sa, this->reqid);
+       
+       charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+       return DESTROY_ME;
+}
+
+/**
+ * Implementation of job_t.destroy.
+ */
+static void destroy(private_acquire_job_t *this)
+{
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+acquire_job_t *acquire_job_create(u_int32_t reqid)
+{
+       private_acquire_job_t *this = malloc_thing(private_acquire_job_t);
+       
+       /* interface functions */
+       this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type;
+       this->public.job_interface.execute = (status_t (*) (job_t *)) execute;
+       this->public.job_interface.destroy = (void (*)(job_t*)) destroy;
+       
+       /* private variables */
+       this->reqid = reqid;
+       this->logger = logger_manager->get_logger(logger_manager, WORKER);
+       
+       return &(this->public);
+}
diff --git a/src/charon/queues/jobs/acquire_job.h b/src/charon/queues/jobs/acquire_job.h
new file mode 100644 (file)
index 0000000..d607c91
--- /dev/null
@@ -0,0 +1,61 @@
+/**
+ * @file acquire_job.h
+ * 
+ * @brief Interface of acquire_job_t.
+ * 
+ */
+
+/*
+ * Copyright (C) 2006 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.
+ */
+
+#ifndef ACQUIRE_JOB_H_
+#define ACQUIRE_JOB_H_
+
+#include <types.h>
+#include <queues/jobs/job.h>
+
+
+typedef struct acquire_job_t acquire_job_t;
+
+/**
+ * @brief Class representing an ACQUIRE Job.
+ * 
+ * This job initiates a CHILD SA on kernel request.
+ * 
+ * @b Constructors:
+ *  - acquire_job_create()
+ * 
+ * @ingroup jobs
+ */
+struct acquire_job_t {
+       /**
+        * The job_t interface.
+        */
+       job_t job_interface;
+};
+
+/**
+ * @brief Creates a job of type ACQUIRE.
+ *
+ * We use the reqid to find the routed CHILD_SA.
+ *
+ * @param reqid                reqid of the CHILD_SA to acquire
+ * @return                     acquire_job_t object
+ * 
+ * @ingroup jobs
+ */
+acquire_job_t *acquire_job_create(u_int32_t reqid);
+
+#endif /* REKEY_CHILD_SA_JOB_H_ */
index 3cf7060..dd39572 100644 (file)
@@ -37,6 +37,11 @@ struct private_delete_child_sa_job_t {
        delete_child_sa_job_t public;
        
        /**
+        * reqid of the CHILD_SA
+        */
+       u_int32_t reqid;
+       
+       /**
         * protocol of the CHILD_SA (ESP/AH)
         */
        protocol_id_t protocol;
@@ -66,12 +71,10 @@ static job_type_t get_type(private_delete_child_sa_job_t *this)
 static status_t execute(private_delete_child_sa_job_t *this)
 {
        ike_sa_t *ike_sa;
-       status_t status;
        
-       status = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, 
-                                                                                                          this->protocol, this->spi,
-                                                                                                          &ike_sa);
-       if (status != SUCCESS)
+       ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager,
+                                                                                                          this->reqid);
+       if (ike_sa == NULL)
        {
                this->logger->log(this->logger, ERROR|LEVEL1, 
                                                  "CHILD_SA not found for delete");
@@ -94,7 +97,9 @@ static void destroy(private_delete_child_sa_job_t *this)
 /*
  * Described in header
  */
-delete_child_sa_job_t *delete_child_sa_job_create(protocol_id_t protocol, u_int32_t spi)
+delete_child_sa_job_t *delete_child_sa_job_create(u_int32_t reqid, 
+                                                                                                 protocol_id_t protocol, 
+                                                                                                 u_int32_t spi)
 {
        private_delete_child_sa_job_t *this = malloc_thing(private_delete_child_sa_job_t);
        
@@ -104,6 +109,7 @@ delete_child_sa_job_t *delete_child_sa_job_create(protocol_id_t protocol, u_int3
        this->public.job_interface.destroy = (void (*)(job_t*)) destroy;
        
        /* private variables */
+       this->reqid = reqid;
        this->protocol = protocol;
        this->spi = spi;
        this->logger = logger_manager->get_logger(logger_manager, WORKER);
index 2e16ef8..fb87440 100644 (file)
@@ -51,15 +51,18 @@ struct delete_child_sa_job_t {
 /**
  * @brief Creates a job of type DELETE_CHILD_SA.
  *
- * The CHILD_SA is identified by its protocol (AH/ESP) and its
+ * The CHILD_SA is identified by its reqid, protocol (AH/ESP) and its
  * inbound SPI.
  *
+ * @param reqid                reqid of the CHILD_SA, as used in kernel
  * @param protocol     protocol of the CHILD_SA
  * @param spi          security parameter index of the CHILD_SA
  * @return                     delete_child_sa_job_t object
  * 
  * @ingroup jobs
  */
-delete_child_sa_job_t *delete_child_sa_job_create(protocol_id_t protocol, u_int32_t spi);
+delete_child_sa_job_t *delete_child_sa_job_create(u_int32_t reqid, 
+                                                                                                 protocol_id_t protocol, 
+                                                                                                 u_int32_t spi);
 
 #endif /* DELETE_CHILD_SA_JOB_H_ */
index 6063a21..86af1a3 100644 (file)
@@ -58,6 +58,20 @@ enum job_type_t {
        INITIATE,
        
        /** 
+        * Install SPD entries.
+        * 
+        * Job is implemented in class route_job_t
+        */
+       ROUTE,
+       
+       /** 
+        * React on a acquire message from the kernel (e.g. setup CHILD_SA)
+        * 
+        * Job is implemented in class acquire_job_t
+        */
+       ACQUIRE,
+       
+       /** 
         * Delete an ike sa which is still not established.
         * 
         * Job is implemented in class delete_half_open_ike_sa_job_t
index a2b5b09..e75d191 100644 (file)
@@ -37,6 +37,11 @@ struct private_rekey_child_sa_job_t {
        rekey_child_sa_job_t public;
        
        /**
+        * reqid of the child to rekey
+        */
+       u_int32_t reqid;
+       
+       /**
         * protocol of the CHILD_SA (ESP/AH)
         */
        protocol_id_t protocol;
@@ -66,12 +71,10 @@ static job_type_t get_type(private_rekey_child_sa_job_t *this)
 static status_t execute(private_rekey_child_sa_job_t *this)
 {
        ike_sa_t *ike_sa;
-       status_t status;
        
-       status = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager, 
-                                                                                                          this->protocol, this->spi,
-                                                                                                          &ike_sa);
-       if (status != SUCCESS)
+       ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager,
+                                                                                                          this->reqid);
+       if (ike_sa == NULL)
        {
                this->logger->log(this->logger, ERROR|LEVEL1, 
                                                  "CHILD_SA not found for rekeying");
@@ -94,7 +97,9 @@ static void destroy(private_rekey_child_sa_job_t *this)
 /*
  * Described in header
  */
-rekey_child_sa_job_t *rekey_child_sa_job_create(protocol_id_t protocol, u_int32_t spi)
+rekey_child_sa_job_t *rekey_child_sa_job_create(u_int32_t reqid, 
+                                                                                               protocol_id_t protocol, 
+                                                                                               u_int32_t spi)
 {
        private_rekey_child_sa_job_t *this = malloc_thing(private_rekey_child_sa_job_t);
        
@@ -104,6 +109,7 @@ rekey_child_sa_job_t *rekey_child_sa_job_create(protocol_id_t protocol, u_int32_
        this->public.job_interface.destroy = (void (*)(job_t*)) destroy;
                
        /* private variables */
+       this->reqid = reqid;
        this->protocol = protocol;
        this->spi = spi;
        this->logger = logger_manager->get_logger(logger_manager, WORKER);
index 87be00c..72d75d1 100644 (file)
@@ -54,12 +54,13 @@ struct rekey_child_sa_job_t {
  * The CHILD_SA is identified by its protocol (AH/ESP) and its
  * inbound SPI.
  *
+ * @param reqid                reqid of the CHILD_SA to rekey
  * @param protocol     protocol of the CHILD_SA
  * @param spi          security parameter index of the CHILD_SA
  * @return                     rekey_child_sa_job_t object
  * 
  * @ingroup jobs
  */
-rekey_child_sa_job_t *rekey_child_sa_job_create(protocol_id_t protocol, u_int32_t spi);
+rekey_child_sa_job_t *rekey_child_sa_job_create(u_int32_t reqid, protocol_id_t protocol, u_int32_t spi);
 
 #endif /* REKEY_CHILD_SA_JOB_H_ */
diff --git a/src/charon/queues/jobs/route_job.c b/src/charon/queues/jobs/route_job.c
new file mode 100644 (file)
index 0000000..b6a8626
--- /dev/null
@@ -0,0 +1,133 @@
+/**
+ * @file route_job.c
+ * 
+ * @brief Implementation of route_job_t.
+ * 
+ */
+
+/*
+ * Copyright (C) 2005-2006 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 <stdlib.h>
+
+#include "route_job.h"
+
+#include <daemon.h>
+
+typedef struct private_route_job_t private_route_job_t;
+
+/**
+ * Private data of an route_job_t Object
+ */
+struct private_route_job_t {
+       /**
+        * public route_job_t interface
+        */
+       route_job_t public;
+       
+       /**
+        * associated connection to route
+        */
+       connection_t *connection;
+       
+       /**
+        * associated policy to route
+        */
+       policy_t *policy;
+       
+       /**
+        * route or unroute?
+        */
+       bool route;
+       
+       /**
+        * logger
+        */
+       logger_t *logger;
+};
+
+/**
+ * Implements route_job_t.get_type.
+ */
+static job_type_t get_type(private_route_job_t *this)
+{
+       return ROUTE;
+}
+
+/**
+ * Implementation of job_t.execute.
+ */
+static status_t execute(private_route_job_t *this)
+{
+       ike_sa_t *ike_sa;
+       
+       this->logger->log(this->logger, CONTROL|LEVEL2, "getting an IKE SA");
+       ike_sa = charon->ike_sa_manager->checkout_by_ids(charon->ike_sa_manager,
+                                                                               this->policy->get_my_id(this->policy),
+                                                                               this->policy->get_other_id(this->policy));
+       
+       if (this->route)
+       {
+               if (ike_sa->route(ike_sa, this->connection, this->policy) != SUCCESS)
+               {
+                       this->logger->log(this->logger, ERROR,
+                                                         "routing failed");
+               }
+       }
+       else
+       {
+               if (ike_sa->unroute(ike_sa, this->policy) == DESTROY_ME)
+               {
+                       this->logger->log(this->logger, ERROR,
+                                                         "removing IKE_SA, as last routed CHILD_SA unrouted");
+                       charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
+                       return DESTROY_ME;
+               }
+       }
+       charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+       return DESTROY_ME;
+}
+
+/**
+ * Implements job_t.destroy.
+ */
+static void destroy(private_route_job_t *this)
+{
+       this->connection->destroy(this->connection);
+       this->policy->destroy(this->policy);
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+route_job_t *route_job_create(connection_t *connection, policy_t *policy, bool route)
+{
+       private_route_job_t *this = malloc_thing(private_route_job_t);
+       
+       /* interface functions */
+       this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type;
+       this->public.job_interface.execute = (status_t (*) (job_t *)) execute;
+       this->public.job_interface.destroy = (void (*) (job_t *)) destroy;
+       
+       /* private variables */
+       this->connection = connection;
+       this->policy = policy;
+       this->route = route;
+       this->logger = logger_manager->get_logger(logger_manager, WORKER);
+       
+       return &this->public;
+}
diff --git a/src/charon/queues/jobs/route_job.h b/src/charon/queues/jobs/route_job.h
new file mode 100644 (file)
index 0000000..df2648a
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ * @file route_job.h
+ * 
+ * @brief Interface of route_job_t.
+ */
+
+/*
+ * Copyright (C) 2005-2006 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.
+ */
+
+#ifndef ROUTE_JOB_H_
+#define ROUTE_JOB_H_
+
+#include <types.h>
+#include <queues/jobs/job.h>
+#include <config/policies/policy.h>
+#include <config/connections/connection.h>
+
+
+typedef struct route_job_t route_job_t;
+
+/**
+ * @brief Class representing an ROUTE Job.
+ * 
+ * @b Constructors:
+ * - route_job_create()
+ * 
+ * @ingroup jobs
+ */
+struct route_job_t {
+       /**
+        * implements job_t interface
+        */
+       job_t job_interface;
+};
+
+/**
+ * @brief Creates a job of type ROUTE.
+ * 
+ * @param connection   connection used for routing
+ * @param policy               policy to set up
+ * @param route                        TRUE to route, FALSE to unroute
+ * @return                             route_job_t object
+ * 
+ * @ingroup jobs
+ */
+route_job_t *route_job_create(connection_t *connection, policy_t *policy, bool route);
+
+#endif /*ROUTE_JOB_H_*/
index 5805993..d735421 100644 (file)
@@ -33,6 +33,7 @@
 mapping_t child_sa_state_m[] = {
        {CHILD_CREATED, "CREATED"},
        {CHILD_INSTALLED, "INSTALLED"},
+       {CHILD_ROUTED, "ROUTED"},
        {CHILD_REKEYING, "REKEYING"},
        {CHILD_DELETING, "DELETING"},
        {MAPPING_END, NULL}
@@ -502,6 +503,13 @@ static status_t add_policies(private_child_sa_t *this, linked_list_t *my_ts_list
        }
        my_iter->destroy(my_iter);
        other_iter->destroy(other_iter);
+       
+       /* switch to routed state if no SAD entry set up */
+       if (this->state == CHILD_CREATED)
+       {
+               this->state = CHILD_ROUTED;
+       }
+       
        return SUCCESS;
 }
 
@@ -597,65 +605,74 @@ static void log_status(private_child_sa_t *this, logger_t *logger, char* name)
        }
        now = (u_int32_t)time(NULL);
        
-       /* query SA times */
-       status = charon->kernel_interface->query_sa(charon->kernel_interface,
-                                       this->me.addr, this->me.spi, this->protocol, &use_in);
-       if (status == SUCCESS && use_in)
-       {
-               snprintf(use_in_str, sizeof(use_in_str), "%ds", now - use_in);
-       }
-       status = charon->kernel_interface->query_sa(charon->kernel_interface,
-                                       this->other.addr, this->other.spi, this->protocol, &use_out);
-       if (status == SUCCESS && use_out)
-       {
-               snprintf(use_out_str, sizeof(use_out_str), "%ds", now - use_out);
-       }
-       
-       /* calculate rekey times */
-       if (this->soft_lifetime)
+       if (this->state == CHILD_INSTALLED)
        {
-               rekeying = this->soft_lifetime - (now - this->install_time);
-               snprintf(rekey_str, sizeof(rekey_str), "%ds", (int)rekeying);
-       }
-       
-       /* algorithms used */
-       if (this->protocol == PROTO_ESP)
-       {
-               if (this->encryption.key_size)
+               /* query SA times */
+               status = charon->kernel_interface->query_sa(charon->kernel_interface,
+                                               this->me.addr, this->me.spi, this->protocol, &use_in);
+               if (status == SUCCESS && use_in)
                {
-                       snprintf(enc_str, sizeof(enc_str), "%s-%d,", 
-                                       mapping_find(encryption_algorithm_m, this->encryption.algorithm),
-                                       this->encryption.key_size);
+                       snprintf(use_in_str, sizeof(use_in_str), "%ds", now - use_in);
+               }
+               status = charon->kernel_interface->query_sa(charon->kernel_interface,
+                                               this->other.addr, this->other.spi, this->protocol, &use_out);
+               if (status == SUCCESS && use_out)
+               {
+                       snprintf(use_out_str, sizeof(use_out_str), "%ds", now - use_out);
+               }
+               
+               /* calculate rekey times */
+               if (this->soft_lifetime)
+               {
+                       rekeying = this->soft_lifetime - (now - this->install_time);
+                       snprintf(rekey_str, sizeof(rekey_str), "%ds", (int)rekeying);
+               }
+               
+               /* algorithms used */
+               if (this->protocol == PROTO_ESP)
+               {
+                       if (this->encryption.key_size)
+                       {
+                               snprintf(enc_str, sizeof(enc_str), "%s-%d,", 
+                                               mapping_find(encryption_algorithm_m, this->encryption.algorithm),
+                                               this->encryption.key_size);
+                       }
+                       else
+                       {
+                               snprintf(enc_str, sizeof(enc_str), "%s,", 
+                                               mapping_find(encryption_algorithm_m, this->encryption.algorithm));
+                       }
+               }
+               if (this->integrity.key_size)
+               {
+                       snprintf(int_str, sizeof(int_str), "%s-%d", 
+                                       mapping_find(integrity_algorithm_m, this->integrity.algorithm),
+                                       this->integrity.key_size);
                }
                else
                {
-                       snprintf(enc_str, sizeof(enc_str), "%s,", 
-                                       mapping_find(encryption_algorithm_m, this->encryption.algorithm));
+                       snprintf(int_str, sizeof(int_str), "%s", 
+                                       mapping_find(integrity_algorithm_m, this->integrity.algorithm));
                }
-       }
-       if (this->integrity.key_size)
-       {
-               snprintf(int_str, sizeof(int_str), "%s-%d", 
-                                mapping_find(integrity_algorithm_m, this->integrity.algorithm),
-                                this->integrity.key_size);
+               
+               logger->log(logger, CONTROL|LEVEL1,
+                                       "  \"%s\":   state: %s, reqid: %d, ",
+                                       name, mapping_find(child_sa_state_m, this->state), this->reqid);
+               logger->log(logger, CONTROL|LEVEL1,
+                                       "  \"%s\":    %s (%s%s), SPIs (in/out): 0x%x/0x%x",
+                                       name, this->protocol == PROTO_ESP ? "ESP" : "AH",
+                                       enc_str, int_str,
+                                       htonl(this->me.spi), htonl(this->other.spi));
+               logger->log(logger, CONTROL|LEVEL1,
+                                       "  \"%s\":    rekeying: %s, key age (in/out): %s/%s",
+                                       name, rekey_str, use_in_str, use_out_str);
        }
        else
        {
-               snprintf(int_str, sizeof(int_str), "%s", 
-                                mapping_find(integrity_algorithm_m, this->integrity.algorithm));
-       }
-       
-       logger->log(logger, CONTROL|LEVEL1,
-                               "  \"%s\":   %s (%s%s), SPIs (in/out): 0x%x/0x%x, reqid: %d",
-                               name,
-                               this->protocol == PROTO_ESP ? "ESP" : "AH",
-                               enc_str, int_str,
-                               htonl(this->me.spi), htonl(this->other.spi),
-                               this->reqid);
-       logger->log(logger, CONTROL|LEVEL1,
-                               "  \"%s\":   state: %s, rekeying: %s, key age (in/out): %s/%s",
-                               name, mapping_find(child_sa_state_m, this->state),
-                               rekey_str, use_in_str, use_out_str);
+               logger->log(logger, CONTROL|LEVEL1, "  \"%s\":   state: %s, reqid: %d",
+                                       name, mapping_find(child_sa_state_m, this->state), 
+                                       this->reqid);
+       }
        
        iterator = this->policies->create_iterator(this->policies, TRUE);
        while (iterator->has_next(iterator))
index 2a74a2a..68015cf 100644 (file)
@@ -49,6 +49,11 @@ enum child_sa_state_t {
        CHILD_CREATED,
        
        /**
+        * Installed SPD, but no SAD entries
+        */
+       CHILD_ROUTED,
+       
+       /**
         * Installed an in-use CHILD_SA
         */
        CHILD_INSTALLED,
index bfffdad..c3011ec 100644 (file)
@@ -329,10 +329,13 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other)
        child_sa_t *child_sa = NULL;
        host_diff_t my_diff, other_diff;
        
-       if (this->my_host == NULL || this->other_host == NULL)
+       if (this->my_host->is_anyaddr(this->my_host) ||
+               this->other_host->is_anyaddr(this->other_host))
        {
                /* on first received message */
+               this->my_host->destroy(this->my_host);
                this->my_host = me->clone(me);
+               this->other_host->destroy(this->other_host);
                this->other_host = other->clone(other);
                return;
        }
@@ -792,9 +795,14 @@ static status_t initiate(private_ike_sa_t *this,
                         */
                        ike_sa_init_t *ike_sa_init;
                        
+                       this->logger->log(this->logger, CONTROL, 
+                                                         "initiating IKE_SA");
+                       
                        set_name(this, connection->get_name(connection));
+                       DESTROY_IF(this->my_host);
                        this->my_host = connection->get_my_host(connection);
                        this->my_host = this->my_host->clone(this->my_host);
+                       DESTROY_IF(this->other_host);
                        this->other_host = connection->get_other_host(connection);
                        this->other_host = this->other_host->clone(this->other_host);
                        
@@ -806,6 +814,8 @@ static status_t initiate(private_ike_sa_t *this,
                case IKE_DELETING:
                {
                        /* if we are in DELETING, we deny set up of a policy. */
+                       this->logger->log(this->logger, CONTROL, 
+                                                         "creating CHILD_SA discarded, as IKE_SA is deleting");
                        policy->destroy(policy);
                        connection->destroy(connection);
                        return FAILED;
@@ -820,6 +830,9 @@ static status_t initiate(private_ike_sa_t *this,
                         */
                        create_child_sa_t *create_child;
                        
+                       this->logger->log(this->logger, CONTROL, 
+                                                         "initiating CHILD_SA");
+                       
                        connection->destroy(connection);
                        create_child = create_child_sa_create(&this->public);
                        create_child->set_policy(create_child, policy);
@@ -834,33 +847,275 @@ static status_t initiate(private_ike_sa_t *this,
  */
 static status_t acquire(private_ike_sa_t *this, u_int32_t reqid)
 {
-       /* - get TS from child with reqid
-        * - get a policy from TS
-        * - get connection from policy
-        */
+       connection_t *connection;
+       policy_t *policy;
+       iterator_t *iterator;
+       child_sa_t *current, *child_sa = NULL;
+       linked_list_t *my_ts, *other_ts;
+       
+       if (this->state == IKE_DELETING)
+       {
+               this->logger->log(this->logger, CONTROL, 
+                                                 "acquiring CHILD_SA with reqid %d discarded, as IKE_SA is deleting",
+                                                 reqid);
+               return FAILED;
+       }
+       
+       
+       /* find CHILD_SA */
+       iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+       while (iterator->iterate(iterator, (void**)&current))
+       {
+               if (current->get_reqid(current) == reqid)
+               {
+                       iterator->remove(iterator);
+                       child_sa = current;
+                       break;
+               }
+       }
+       iterator->destroy(iterator);
+       if (!child_sa)
+       {
+               this->logger->log(this->logger, ERROR, 
+                                                 "CHILD_SA with reqid %d not found, unable to acquire",
+                                                 reqid);
+               return FAILED;
+       }
+       my_ts = child_sa->get_my_traffic_selectors(child_sa);
+       other_ts = child_sa->get_other_traffic_selectors(child_sa);
+       
+       policy = charon->policies->get_policy(charon->policies, 
+                                                                                 this->my_id, this->other_id, 
+                                                                                 my_ts, other_ts, 
+                                                                                 this->my_host, this->other_host);
+       child_sa->destroy(child_sa);
+       if (policy == NULL)
+       {
+               this->logger->log(this->logger, ERROR, 
+                                                 "no policy found to acquire CHILD_SA with reqid %d", 
+                                                 reqid);
+               return FAILED;
+       }
+       
        switch (this->state)
        {
                case IKE_CREATED:
-                       /* ike_sa_init */
+               {
+                       ike_sa_init_t *ike_sa_init;
+                       
+                       this->logger->log(this->logger, CONTROL,
+                                                         "acquiring CHILD_SA with reqid %d, IKE_SA setup needed", 
+                                                         reqid);
+                       
+                       connection = charon->connections->get_connection_by_hosts(
+                                       charon->connections, this->my_host, this->other_host);
+                       
+                       if (connection == NULL)
+                       {
+                               this->logger->log(this->logger, ERROR, 
+                                                                 "no connection found to acquire IKE_SA for CHILD_SA with reqid %d", 
+                                                                 reqid);
+                               policy->destroy(policy);
+                               return FAILED;
+                       }
+                       
+                       this->message_id_out = 1;
+                       ike_sa_init = ike_sa_init_create(&this->public);
+                       ike_sa_init->set_config(ike_sa_init, connection, policy);
+                       return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE);
+               }
+               case IKE_CONNECTING:
+               case IKE_ESTABLISHED:
+               {
+                       create_child_sa_t *create_child;
+                       
+                       this->logger->log(this->logger, CONTROL, 
+                                                         "acquiring CHILD_SA with reqid %d",
+                                                         reqid);
+                       
+                       create_child = create_child_sa_create(&this->public);
+                       create_child->set_policy(create_child, policy);
+                       return queue_transaction(this, (transaction_t*)create_child, FALSE);
+               }
+               default:
+                       break;
+       }
+       return FAILED;
+}
+
+/**
+ * destroy a list of traffic selectors
+ */
+static void ts_list_destroy(linked_list_t *list)
+{
+       traffic_selector_t *ts;
+       while (list->remove_last(list, (void**)&ts) == SUCCESS)
+       {
+               ts->destroy(ts);
+       }
+       list->destroy(list);
+}
+
+/**
+ * compare two lists of traffic selectors for equality
+ */
+static bool ts_list_equals(linked_list_t *l1, linked_list_t *l2)
+{
+       bool equals = TRUE;
+       iterator_t *i1, *i2;
+       traffic_selector_t *t1, *t2;
+       
+       i1 = l1->create_iterator(l1, TRUE);
+       i2 = l2->create_iterator(l2, TRUE);
+       while (i1->iterate(i1, (void**)&t1) && i2->iterate(i2, (void**)&t2))
+       {
+               if (!t1->equals(t1, t2))
+               {
+                       equals = FALSE;
                        break;
+               }
+       }
+       /* check if one iterator is not at the end */
+       if (i1->has_next(i1) || i2->has_next(i2))
+       {
+               equals = FALSE;
+       }
+       i1->destroy(i1);
+       i2->destroy(i2);
+       return equals;
+}
+
+/**
+ * Implementation of ike_sa_t.route.
+ */
+static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t *policy)
+{
+       child_sa_t *child_sa = NULL;
+       iterator_t *iterator;
+       linked_list_t *my_ts, *other_ts;
+       status_t status;
+       
+       /* check if not already routed*/
+       iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+       while (iterator->iterate(iterator, (void**)&child_sa))
+       {
+               linked_list_t *my_ts_conf, *other_ts_conf;
+               
+               my_ts = child_sa->get_my_traffic_selectors(child_sa);
+               other_ts = child_sa->get_other_traffic_selectors(child_sa);
+               
+               my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host);
+               other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host);
+               
+               if (ts_list_equals(my_ts, my_ts_conf) &&
+                                 ts_list_equals(other_ts, other_ts_conf))
+               {
+                       ts_list_destroy(my_ts_conf);
+                       ts_list_destroy(other_ts_conf);
+                       iterator->destroy(iterator);
+                       this->logger->log(this->logger, CONTROL, 
+                                                         "a CHILD_SA with such a policy already routed");
+                       
+                       return FAILED;
+               }
+               ts_list_destroy(my_ts_conf);
+               ts_list_destroy(other_ts_conf);
+       }
+       iterator->destroy(iterator);
+       
+       switch (this->state)
+       {
+               case IKE_CREATED:
                case IKE_CONNECTING:
+                       /* we update IKE_SA information as good as possible, 
+                        * this allows us to set up the SA later when an acquire comes in. */
+                       if (this->my_id->get_type(this->my_id) == ID_ANY)
+                       {
+                               this->my_id->destroy(this->my_id);
+                               this->my_id = policy->get_my_id(policy);
+                               this->my_id = this->my_id->clone(this->my_id);
+                       }
+                       if (this->other_id->get_type(this->other_id) == ID_ANY)
+                       {
+                               this->other_id->destroy(this->other_id);
+                               this->other_id = policy->get_other_id(policy);
+                               this->other_id = this->other_id->clone(this->other_id);
+                       }
+                       if (this->my_host->is_anyaddr(this->my_host))
+                       {
+                               this->my_host->destroy(this->my_host);
+                               this->my_host = connection->get_my_host(connection);
+                               this->my_host = this->my_host->clone(this->my_host);
+                       }
+                       if (this->other_host->is_anyaddr(this->other_host))
+                       {
+                               this->other_host->destroy(this->other_host);
+                               this->other_host = connection->get_other_host(connection);
+                               this->other_host = this->other_host->clone(this->other_host);
+                       }
+                       set_name(this, connection->get_name(connection));
+                       break;
                case IKE_ESTABLISHED:
-                       /* queue create_child_sa */
+                       /* nothing to do */
                        break;
                case IKE_DELETING:
                        /* deny */
-                       break;
+                       return FAILED;
        }
-       return FAILED;
+       
+       child_sa = child_sa_create(0, this->my_host, this->other_host, 0, 0, FALSE);
+       my_ts = policy->get_my_traffic_selectors(policy, this->my_host);
+       other_ts = policy->get_other_traffic_selectors(policy, this->other_host);
+       status = child_sa->add_policies(child_sa, my_ts, other_ts);
+       ts_list_destroy(my_ts);
+       ts_list_destroy(other_ts);
+       this->child_sas->insert_last(this->child_sas, child_sa);
+       
+       return status;
 }
 
 /**
- * Implementation of ike_sa_t.route.
+ * Implementation of ike_sa_t.unroute.
  */
-static status_t route(private_ike_sa_t *this, policy_t *policy)
+static status_t unroute(private_ike_sa_t *this, policy_t *policy)
 {
-       /* TODO: create CHILD_SA, add policy */
-       return FAILED;
+       iterator_t *iterator;
+       child_sa_t *child_sa = NULL;
+       linked_list_t *my_ts, *other_ts, *my_ts_conf, *other_ts_conf;
+       
+       /* find CHILD_SA in ROUTED state */
+       iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+       while (iterator->iterate(iterator, (void**)&child_sa))
+       {
+               if (child_sa->get_state(child_sa) == CHILD_ROUTED)
+               {
+                       my_ts = child_sa->get_my_traffic_selectors(child_sa);
+                       other_ts = child_sa->get_other_traffic_selectors(child_sa);
+                       
+                       my_ts_conf = policy->get_my_traffic_selectors(policy, this->my_host);
+                       other_ts_conf = policy->get_other_traffic_selectors(policy, this->other_host);
+                       
+                       if (ts_list_equals(my_ts, my_ts_conf) &&
+                               ts_list_equals(other_ts, other_ts_conf))
+                       {
+                               iterator->remove(iterator);
+                               child_sa->destroy(child_sa);
+                               ts_list_destroy(my_ts_conf);
+                               ts_list_destroy(other_ts_conf);
+                               break;
+                       }
+                       ts_list_destroy(my_ts_conf);
+                       ts_list_destroy(other_ts_conf);
+               }
+       }
+       iterator->destroy(iterator);
+       /* if we are not established, and we have no more routed childs, remove whole SA */
+       if (this->state == IKE_CREATED &&
+               this->child_sas->get_count(this->child_sas) == 0)
+       {
+               return DESTROY_ME;
+       }
+       return SUCCESS;
 }
 
 /**
@@ -1218,6 +1473,28 @@ static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa)
 }
 
 /**
+ * Implementation of protected_ike_sa_t.has_child_sa.
+ */
+static bool has_child_sa(private_ike_sa_t *this, u_int32_t reqid)
+{
+       iterator_t *iterator;
+       child_sa_t *current;
+       bool found = FALSE;
+       
+       iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+       while (iterator->iterate(iterator, (void**)&current))
+       {
+               if (current->get_reqid(current) == reqid)
+               {
+                       found = TRUE;
+                       break;
+               }
+       }
+       iterator->destroy(iterator);
+       return found;
+}
+
+/**
  * Implementation of protected_ike_sa_t.get_child_sa.
  */
 static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
@@ -1226,12 +1503,12 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
        iterator_t *iterator;
        child_sa_t *current, *found = NULL;
        
-       iterator = this->child_sas->create_iterator(this->child_sas, FALSE);
+       iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
        while (iterator->has_next(iterator))
        {
                iterator->current(iterator, (void**)&current);
                if (current->get_spi(current, inbound) == spi &&
-                                 current->get_protocol(current) == protocol)
+                       current->get_protocol(current) == protocol)
                {
                        found = current;
                }
@@ -1353,17 +1630,28 @@ static void log_status(private_ike_sa_t *this, logger_t *logger, char *name)
  */
 static status_t delete_(private_ike_sa_t *this)
 {
-       delete_ike_sa_t *delete_ike_sa;
-       delete_ike_sa = delete_ike_sa_create(&this->public);
-       
-       if (this->transaction_out)
+       switch (this->state)
        {
-               /* already a transaction in progress. As this may hang
-                * around a while, we don't inform the other peer. */
-               return DESTROY_ME;
+               case IKE_CONNECTING:
+               case IKE_ESTABLISHED:
+               {
+                       delete_ike_sa_t *delete_ike_sa;
+                       delete_ike_sa = delete_ike_sa_create(&this->public);
+                       if (this->transaction_out)
+                       {
+                               /* already a transaction in progress. As this may hang
+                               * around a while, we don't inform the other peer. */
+                               return DESTROY_ME;
+                       }
+                       return queue_transaction(this, (transaction_t*)delete_ike_sa, FALSE);
+               }
+               case IKE_CREATED:
+               case IKE_DELETING:
+               default:
+               {
+                       return DESTROY_ME;
+               }
        }
-       
-       return queue_transaction(this, (transaction_t*)delete_ike_sa, FALSE);
 }
 
 /**
@@ -1483,7 +1771,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
        this->public.set_name = (void(*)(ike_sa_t*,char*))set_name;
        this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message;
        this->public.initiate = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) initiate;
-       this->public.route = (status_t(*)(ike_sa_t*,policy_t*)) route;
+       this->public.route = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) route;
+       this->public.unroute = (status_t(*)(ike_sa_t*,policy_t*)) unroute;
        this->public.acquire = (status_t(*)(ike_sa_t*,u_int32_t)) acquire;
        this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id;
        this->public.get_my_host = (host_t*(*)(ike_sa_t*)) get_my_host;
@@ -1505,6 +1794,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
        this->public.get_prf_auth_r = (prf_t *(*) (ike_sa_t *)) get_prf_auth_r;
        this->public.build_transforms = (status_t (*) (ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t,bool)) build_transforms;
        this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa;
+       this->public.has_child_sa = (bool(*)(ike_sa_t*,u_int32_t)) has_child_sa;
        this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa;
        this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) rekey_child_sa;
        this->public.delete_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) delete_child_sa;
@@ -1517,10 +1807,10 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
        this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
        this->name = strdup("(uninitialized)");
        this->child_sas = linked_list_create();
-       this->my_host = NULL;
-       this->other_host = NULL;
-       this->my_id = NULL;
-       this->other_id = NULL;
+       this->my_host = host_create(AF_INET, "0.0.0.0", 0);
+       this->other_host = host_create(AF_INET, "0.0.0.0", 0);
+       this->my_id = identification_create_from_encoding(ID_ANY, CHUNK_INITIALIZER);
+       this->other_id = identification_create_from_encoding(ID_ANY, CHUNK_INITIALIZER);
        this->crypter_in = NULL;
        this->crypter_out = NULL;
        this->signer_in = NULL;
index f593e38..dfd24b0 100644 (file)
@@ -237,16 +237,27 @@ struct ike_sa_t {
         *
         * Installs the policies in the kernel. If traffic matches,
         * the kernel requests connection setup from the IKE_SA via acquire().
-        * The policy is owned by the IKE_SA after the call, so
-        * do not modify or destroy it.
         * 
         * @param this                  calling object
+        * @param connection    connection definition used for routing
         * @param policy                policy to route
         * @return                              
-        *                                              - SUCCESS if initialization started
-        *                                              - DESTROY_ME if initialization failed and IKE_SA MUST be deleted
+        *                                              - SUCCESS if routed successfully
+        *                                              - FAILED if routing failed
         */
-       status_t (*route) (ike_sa_t *this, policy_t *policy);
+       status_t (*route) (ike_sa_t *this, connection_t *connection, policy_t *policy);
+
+       /**
+        * @brief Unroute a policy in the kernel previously routed.
+        *
+        * @param this                  calling object
+        * @param policy                policy to route
+        * @return                              
+        *                                              - SUCCESS if route removed
+        *                                              - DESTROY_ME if last route was removed from
+        *                                                an IKE_SA which was not established
+        */
+       status_t (*unroute) (ike_sa_t *this, policy_t *policy);
        
        /**
         * @brief Acquire connection setup for a policy.
@@ -436,6 +447,15 @@ struct ike_sa_t {
        void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa);
        
        /**
+        * @brief Check if an IKE_SA has one or more CHILD_SAs with a given reqid.
+        * 
+        * @param this                  calling object
+        * @param reqid                 reqid of the CHILD
+        * @return                              TRUE if it has such a CHILD, FALSE if not
+        */
+       bool (*has_child_sa) (ike_sa_t *this, u_int32_t reqid);
+       
+       /**
         * @brief Get a CHILD_SA identified by protocol and SPI.
         * 
         * @param this                  calling object
index c8e4d71..abb9c0f 100644 (file)
@@ -554,12 +554,11 @@ static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id
 /**
  * Implementation of of ike_sa_manager.checkout_by_child.
  */
-static status_t checkout_by_child(private_ike_sa_manager_t *this,
-                                                                 protocol_id_t protocol, u_int32_t spi,
-                                                                 ike_sa_t **ike_sa)
+static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this,
+                                                                  u_int32_t reqid)
 {
        iterator_t *iterator;
-       status_t status = NOT_FOUND;
+       ike_sa_t *ike_sa = NULL;
        
        pthread_mutex_lock(&(this->mutex));
        
@@ -572,12 +571,11 @@ static status_t checkout_by_child(private_ike_sa_manager_t *this,
                if (wait_for_entry(this, entry))
                {
                        /* ok, access is exclusive for us, check for child */
-                       if (entry->ike_sa->get_child_sa(entry->ike_sa, protocol, spi, TRUE) != NULL)
+                       if (entry->ike_sa->has_child_sa(entry->ike_sa, reqid))
                        {
                                /* match */
                                entry->checked_out = TRUE;
-                               *ike_sa = entry->ike_sa;
-                               status = SUCCESS;
+                               ike_sa = entry->ike_sa;
                                break;
                        }
                }
@@ -585,7 +583,7 @@ static status_t checkout_by_child(private_ike_sa_manager_t *this,
        iterator->destroy(iterator);
        pthread_mutex_unlock(&(this->mutex));
        
-       return status;
+       return ike_sa;
 }
 
 /**
@@ -915,7 +913,7 @@ ike_sa_manager_t *ike_sa_manager_create()
        this->public.destroy = (void(*)(ike_sa_manager_t*))destroy;
        this->public.checkout_by_ids = (ike_sa_t*(*)(ike_sa_manager_t*,identification_t*,identification_t*))checkout_by_ids;
        this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout;
-       this->public.checkout_by_child = (status_t(*)(ike_sa_manager_t*,protocol_id_t,u_int32_t,ike_sa_t**))checkout_by_child;
+       this->public.checkout_by_child = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t))checkout_by_child;
        this->public.get_ike_sa_list = (linked_list_t*(*)(ike_sa_manager_t*))get_ike_sa_list;
        this->public.get_ike_sa_list_by_name = (linked_list_t*(*)(ike_sa_manager_t*,const char*))get_ike_sa_list_by_name;
        this->public.log_status = (void(*)(ike_sa_manager_t*,logger_t*,char*))log_status;
index 1c8dbad..f327be0 100644 (file)
@@ -92,15 +92,12 @@ struct ike_sa_manager_t {
         * CHILD_SA the kernel wants to modify.
         *
         * @param this                          the manager object
-        * @param protocol                      protocol of the CHILD_SA
-        * @param spi                           SPI of the CHILD_SA
-        * @param[out] ike_sa           checked out SA
+        * @param reqid                         reqid of the CHILD_SA
         * @return
-        *                                                      - NOT_FOUND, if no IKE SA with such a child found
-        *                                                      - SUCCESS, if ike_sa set
+        *                                                      - checked out IKE_SA, if found
+        *                                                      - NULL, if not found
         */
-       status_t (*checkout_by_child) (ike_sa_manager_t* this, protocol_id_t protocol,
-                                                                       u_int32_t spi, ike_sa_t **ike_sa);
+       ike_sa_t* (*checkout_by_child) (ike_sa_manager_t* this, u_int32_t reqid);
        
        /**
         * @brief Get a list of all IKE_SA SAs currently set up.
index 4bb9a6c..9ca821f 100644 (file)
@@ -43,6 +43,7 @@
 #include <utils/linked_list.h>
 #include <queues/jobs/delete_child_sa_job.h>
 #include <queues/jobs/rekey_child_sa_job.h>
+#include <queues/jobs/acquire_job.h>
 
 /** kernel level protocol identifiers */
 #define KERNEL_ESP 50
@@ -342,44 +343,65 @@ static void receive_messages(private_kernel_interface_t *this)
                        break;
                }
                
-               /* we handle ACQUIRE and EXPIRE messages directly
-               */
+               /* we handle ACQUIRE and EXPIRE messages directly */
                hdr = (struct nlmsghdr*)response;
                if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE)
                {
-                       struct xfrm_user_acquire *acquire;
-                       
-                       acquire = (struct xfrm_user_acquire*)NLMSG_DATA(hdr);
-                       this->logger->log(this->logger, CONTROL,
-                                                         "Received a XFRM_MSG_ACQUIRE with index %d. Ignored",
-                                                         acquire->policy.index);
-                       
+                       u_int32_t reqid = 0;
+                       job_t *job;
+                       struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_user_acquire);
+                       size_t rtsize = XFRM_PAYLOAD(hdr, struct xfrm_user_tmpl);
+                       if (RTA_OK(rthdr, rtsize))
+                       {
+                               if (rthdr->rta_type == XFRMA_TMPL)
+                               {
+                                       struct xfrm_user_tmpl* tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr);
+                                       reqid = tmpl->reqid;
+                               }
+                       }
+                       if (reqid == 0)
+                       {
+                               this->logger->log(this->logger, ERROR,
+                                                                 "Received a XFRM_MSG_ACQUIRE, but no reqid found");
+                       }
+                       else
+                       {
+                               this->logger->log(this->logger, CONTROL|LEVEL1, 
+                                                               "Received a XFRM_MSG_ACQUIRE");
+                               this->logger->log(this->logger, CONTROL,
+                                                                 "creating acquire job for CHILD_SA with reqid %d",
+                                                                 reqid);
+                               job = (job_t*)acquire_job_create(reqid);
+                               charon->job_queue->add(charon->job_queue, job);
+                       }
                }
                else if (hdr->nlmsg_type == XFRM_MSG_EXPIRE)
                {
                        job_t *job;
                        protocol_id_t protocol;
-                       u_int32_t spi;
+                       u_int32_t spi, reqid;
                        struct xfrm_user_expire *expire;
                        
                        expire = (struct xfrm_user_expire*)NLMSG_DATA(hdr);
                        protocol = expire->state.id.proto == KERNEL_ESP ?
                                        PROTO_ESP : PROTO_AH;
                        spi = expire->state.id.spi;
+                       reqid = expire->state.reqid;
                        
                        this->logger->log(this->logger, CONTROL|LEVEL1,
                                                          "Received a XFRM_MSG_EXPIRE");
                        this->logger->log(this->logger, CONTROL,
-                                                         "creating %s job for %s CHILD_SA 0x%x",
+                                                         "creating %s job for %s CHILD_SA 0x%x (reqid %d)",
                                                          expire->hard ? "delete" : "rekey",
-                                                         mapping_find(protocol_id_m, protocol), ntohl(spi));
+                                                         mapping_find(protocol_id_m, protocol), ntohl(spi),
+                                                         reqid);
                        if (expire->hard)
                        {
-                               job = (job_t*)delete_child_sa_job_create(protocol, spi);
+                               job = (job_t*)delete_child_sa_job_create(reqid, protocol, spi);
                        }
                        else
                        {
-                               job = (job_t*)rekey_child_sa_job_create(protocol, spi);
+                               job = (job_t*)rekey_child_sa_job_create(reqid, protocol, spi);
                        }
                        charon->job_queue->add(charon->job_queue, job);
                }
@@ -670,7 +692,7 @@ static status_t update_sa(
        sa_id->spi = spi;
        sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
        sa_id->family = dst->get_family(dst);
-       POS;
+       
        if (send_message(this, hdr, &update) != SUCCESS)
        {
                this->logger->log(this->logger, ERROR, "netlink communication failed");
@@ -695,7 +717,6 @@ static status_t update_sa(
                free(update);
                return FAILED;
        }
-       POS;
        
        this->logger->log(this->logger, CONTROL|LEVEL2, "updating SA");
        update->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;        
@@ -731,7 +752,7 @@ static status_t update_sa(
                        rthdr = RTA_NEXT(rthdr, rtsize);
                }
        }
-       POS;
+       
        if (send_message(this, update, &response) != SUCCESS)
        {
                this->logger->log(this->logger, ERROR, "netlink communication failed");
@@ -754,8 +775,7 @@ static status_t update_sa(
                this->logger->log(this->logger, CONTROL|LEVEL2, "deleting old SA");
                status = this->public.del_sa(&this->public, dst, spi, protocol);
        }
-       POS;
-
+       
        free(update);
        free(response);
        return status;
@@ -1052,7 +1072,7 @@ static status_t add_policy(private_kernel_interface_t *this,
        
        struct xfrm_user_tmpl *tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr);
        tmpl->reqid = reqid;
-       tmpl->id.proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
+       tmpl->id.proto = (protocol == PROTO_AH) ? KERNEL_AH : KERNEL_ESP;
        tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0;
        tmpl->mode = TRUE;
        
index 004e3ec..7188a53 100755 (executable)
@@ -38,6 +38,7 @@
 #include <daemon.h>
 #include <crypto/x509.h>
 #include <queues/jobs/initiate_job.h>
+#include <queues/jobs/route_job.h>
 
 #define IKE_PORT       500
 #define PATH_BUF       256
@@ -519,6 +520,56 @@ static void stroke_initiate(private_stroke_t *this, stroke_msg_t *msg)
 }
 
 /**
+ * route/unroute a policy (install SPD entries)
+ */
+static void stroke_route(private_stroke_t *this, stroke_msg_t *msg, bool route)
+{
+       route_job_t *job;
+       connection_t *connection;
+       policy_t *policy;
+       
+       pop_string(msg, &(msg->route.name));
+       this->logger->log(this->logger, CONTROL,
+                                         "received stroke: %s \"%s\"",
+                                         route ? "route" : "unroute",
+                                         msg->route.name);
+       
+       /* we wouldn't need a connection, but we only want to route policies
+        * whose connections are keyexchange=ikev2. */
+       connection = charon->connections->get_connection_by_name(charon->connections,
+                                                                                                                        msg->route.name);
+       if (connection == NULL)
+       {
+               this->stroke_logger->log(this->stroke_logger, ERROR, 
+                                                                "no connection named \"%s\"", 
+                                                                msg->route.name);
+               return;
+       }
+       if (!connection->is_ikev2(connection))
+       {
+               connection->destroy(connection);
+               return;
+       }
+               
+       policy = charon->policies->get_policy_by_name(charon->policies, 
+                                                                                                 msg->route.name);
+       if (policy == NULL)
+       {
+               this->stroke_logger->log(this->stroke_logger, ERROR,
+                                                                "no policy named \"%s\"",
+                                                                msg->route.name);
+               connection->destroy(connection);
+               return;
+       }
+       this->stroke_logger->log(this->stroke_logger, CONTROL,
+                                                        "%s policy \"%s\"", 
+                                                        route ? "routing" : "unrouting",
+                                                        msg->route.name);
+       job = route_job_create(connection, policy, route);
+       charon->job_queue->add(charon->job_queue, (job_t*)job);
+}
+
+/**
  * terminate a connection by name
  */
 static void stroke_terminate(private_stroke_t *this, stroke_msg_t *msg)
@@ -795,6 +846,12 @@ static void stroke_receive(private_stroke_t *this)
                        case STR_INITIATE:
                                stroke_initiate(this, msg);
                                break;
+                       case STR_ROUTE:
+                               stroke_route(this, msg, TRUE);
+                               break;
+                       case STR_UNROUTE:
+                               stroke_route(this, msg, FALSE);
+                               break;
                        case STR_TERMINATE:
                                stroke_terminate(this, msg);
                                break;
index 5524ee6..477d345 100755 (executable)
@@ -174,6 +174,10 @@ route|unroute)
        then
                $IPSEC_WHACK --name "$1" "--$op"
        fi
+       if test -e $IPSEC_CHARON_PID
+       then
+               $IPSEC_STROKE "$op" "$1"
+       fi
        exit 0
        ;;
 scencrypt|scdecrypt)
index d17a850..68a25df 100644 (file)
@@ -159,7 +159,7 @@ int starter_stroke_del_conn(starter_conn_t *conn)
 
        msg.type = STR_DEL_CONN;
        msg.length = offsetof(stroke_msg_t, buffer);
-       msg.install.name = push_string(&msg, connection_name(conn));
+       msg.del_conn.name = push_string(&msg, connection_name(conn));
        return send_stroke_msg(&msg);
 }
 
@@ -167,9 +167,9 @@ int starter_stroke_route_conn(starter_conn_t *conn)
 {
        stroke_msg_t msg;
 
-       msg.type = STR_INSTALL;
+       msg.type = STR_ROUTE;
        msg.length = offsetof(stroke_msg_t, buffer);
-       msg.install.name = push_string(&msg, connection_name(conn));
+       msg.route.name = push_string(&msg, connection_name(conn));
        return send_stroke_msg(&msg);
 }
 
index 15661a2..e29a588 100644 (file)
@@ -169,6 +169,26 @@ static int terminate_connection(char *name)
        return send_stroke_msg(&msg);
 }
 
+static int route_connection(char *name)
+{
+       stroke_msg_t msg;
+       
+       msg.type = STR_ROUTE;
+       msg.length = offsetof(stroke_msg_t, buffer);
+       msg.route.name = push_string(&msg, name);
+       return send_stroke_msg(&msg);
+}
+
+static int unroute_connection(char *name)
+{
+       stroke_msg_t msg;
+       
+       msg.type = STR_UNROUTE;
+       msg.length = offsetof(stroke_msg_t, buffer);
+       msg.unroute.name = push_string(&msg, name);
+       return send_stroke_msg(&msg);
+}
+
 static int show_status(stroke_keyword_t kw, char *connection)
 {
        stroke_msg_t msg;
@@ -336,6 +356,20 @@ int main(int argc, char *argv[])
                        }
                        res = terminate_connection(argv[2]);
                        break;
+               case STROKE_ROUTE:
+                       if (argc < 3)
+                       {
+                               exit_usage("\"route\" needs a connection name");
+                       }
+                       res = route_connection(argv[2]);
+                       break;
+               case STROKE_UNROUTE:
+                       if (argc < 3)
+                       {
+                               exit_usage("\"unroute\" needs a connection name");
+                       }
+                       res = unroute_connection(argv[2]);
+                       break;
                case STROKE_LOGTYPE:
                        if (argc < 5)
                        {
index 9aa4de3..b71e199 100644 (file)
@@ -99,8 +99,10 @@ struct stroke_msg_t {
        enum {
                /* initiate a connection */
                STR_INITIATE,
-               /* install SPD entries for a connection */
-               STR_INSTALL,
+               /* install SPD entries for a policy */
+               STR_ROUTE,
+               /* uninstall SPD entries for a policy */
+               STR_UNROUTE,
                /* add a connection */
                STR_ADD_CONN,
                /* delete a connection */
@@ -123,10 +125,10 @@ struct stroke_msg_t {
        } type;
 
        union {
-               /* data for STR_INITIATE, STR_INSTALL, STR_UP, STR_DOWN, ... */
+               /* data for STR_INITIATE, STR_ROUTE, STR_UP, STR_DOWN, ... */
                struct {
                        char *name;
-               } initiate, install, terminate, status, del_conn;
+               } initiate, route, unroute, terminate, status, del_conn;
 
                /* data for STR_ADD_CONN */
                struct {
index c40bed3..6a8dd53 100644 (file)
@@ -23,6 +23,7 @@ typedef enum {
        STROKE_DEL,
        STROKE_DELETE,
        STROKE_ROUTE,
+       STROKE_UNROUTE,
        STROKE_UP,
        STROKE_DOWN,
        STROKE_LOGTYPE,
index 9b380ae..d720a7d 100644 (file)
@@ -30,6 +30,7 @@ add,           STROKE_ADD
 del,           STROKE_DEL
 delete,        STROKE_DELETE
 route,         STROKE_ROUTE
+unroute,       STROKE_UNROUTE
 up,            STROKE_UP
 down,          STROKE_DOWN
 logtype,       STROKE_LOGTYPE