daemon: Flush shunts before unloading plugins
[strongswan.git] / src / libcharon / control / controller.c
index fa7993d..fd8349e 100644 (file)
 #include <sys/types.h>
 #include <dirent.h>
 #include <sys/stat.h>
-#include <dlfcn.h>
 
 #include <daemon.h>
 #include <library.h>
 #include <threading/thread.h>
+#include <threading/spinlock.h>
 #include <threading/semaphore.h>
 
 typedef struct private_controller_t private_controller_t;
@@ -111,6 +111,11 @@ struct interface_listener_t {
         * semaphore to implement wait_for_listener()
         */
        semaphore_t *done;
+
+       /**
+        * spinlock to update the IKE_SA handle properly
+        */
+       spinlock_t *lock;
 };
 
 
@@ -203,9 +208,15 @@ static bool wait_for_listener(interface_job_t *job, u_int timeout)
 
 METHOD(logger_t, listener_log, void,
        interface_logger_t *this, debug_t group, level_t level, int thread,
-       ike_sa_t *ike_sa, char* message)
+       ike_sa_t *ike_sa, const char *message)
 {
-       if (this->listener->ike_sa == ike_sa)
+       ike_sa_t *target;
+
+       this->listener->lock->lock(this->listener->lock);
+       target = this->listener->ike_sa;
+       this->listener->lock->unlock(this->listener->lock);
+
+       if (target == ike_sa)
        {
                if (!this->callback(this->param, group, level, ike_sa, message))
                {
@@ -232,7 +243,13 @@ METHOD(job_t, get_priority_medium, job_priority_t,
 METHOD(listener_t, ike_state_change, bool,
        interface_listener_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
 {
-       if (this->ike_sa == ike_sa)
+       ike_sa_t *target;
+
+       this->lock->lock(this->lock);
+       target = this->ike_sa;
+       this->lock->unlock(this->lock);
+
+       if (target == ike_sa)
        {
                switch (state)
                {
@@ -266,7 +283,13 @@ METHOD(listener_t, child_state_change, bool,
        interface_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
        child_sa_state_t state)
 {
-       if (this->ike_sa == ike_sa)
+       ike_sa_t *target;
+
+       this->lock->lock(this->lock);
+       target = this->ike_sa;
+       this->lock->unlock(this->lock);
+
+       if (target == ike_sa)
        {
                switch (state)
                {
@@ -280,6 +303,18 @@ METHOD(listener_t, child_state_change, bool,
                                                /* proper delete */
                                                this->status = SUCCESS;
                                                break;
+                                       case CHILD_RETRYING:
+                                               /* retrying with a different DH group; survive another
+                                                * initiation round */
+                                               this->status = NEED_MORE;
+                                               return TRUE;
+                                       case CHILD_CREATED:
+                                               if (this->status == NEED_MORE)
+                                               {
+                                                       this->status = FAILED;
+                                                       return TRUE;
+                                               }
+                                               break;
                                        default:
                                                break;
                                }
@@ -296,6 +331,7 @@ METHOD(job_t, destroy_job, void,
 {
        if (ref_put(&this->refcount))
        {
+               this->listener.lock->destroy(this->listener.lock);
                DESTROY_IF(this->listener.done);
                free(this);
        }
@@ -326,7 +362,9 @@ METHOD(job_t, initiate_execute, job_requeue_t,
                listener_done(listener);
                return JOB_REQUEUE_NONE;
        }
+       listener->lock->lock(listener->lock);
        listener->ike_sa = ike_sa;
+       listener->lock->unlock(listener->lock);
 
        if (ike_sa->get_peer_cfg(ike_sa) == NULL)
        {
@@ -336,7 +374,10 @@ METHOD(job_t, initiate_execute, job_requeue_t,
 
        if (ike_sa->initiate(ike_sa, listener->child_cfg, 0, NULL, NULL) == SUCCESS)
        {
-               listener->status = SUCCESS;
+               if (!listener->logger.callback)
+               {
+                       listener->status = SUCCESS;
+               }
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        }
        else
@@ -372,6 +413,7 @@ METHOD(controller_t, initiate, status_t,
                        .status = FAILED,
                        .child_cfg = child_cfg,
                        .peer_cfg = peer_cfg,
+                       .lock = spinlock_create(),
                },
                .public = {
                        .execute = _initiate_execute,
@@ -381,6 +423,7 @@ METHOD(controller_t, initiate, status_t,
                .refcount = 1,
        );
        job->listener.logger.listener = &job->listener;
+       thread_cleanup_push((void*)destroy_job, job);
 
        if (callback == NULL)
        {
@@ -394,7 +437,7 @@ METHOD(controller_t, initiate, status_t,
                }
        }
        status = job->listener.status;
-       destroy_job(job);
+       thread_cleanup_pop(TRUE);
        return status;
 }
 
@@ -406,7 +449,7 @@ METHOD(job_t, terminate_ike_execute, job_requeue_t,
        ike_sa_t *ike_sa;
 
        ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
-                                                                                                       unique_id, FALSE);
+                                                                                                       unique_id);
        if (!ike_sa)
        {
                DBG1(DBG_IKE, "unable to terminate IKE_SA: ID %d not found", unique_id);
@@ -415,7 +458,9 @@ METHOD(job_t, terminate_ike_execute, job_requeue_t,
                listener_done(listener);
                return JOB_REQUEUE_NONE;
        }
+       listener->lock->lock(listener->lock);
        listener->ike_sa = ike_sa;
+       listener->lock->unlock(listener->lock);
 
        if (ike_sa->delete(ike_sa) != DESTROY_ME)
        {       /* delete failed */
@@ -424,7 +469,10 @@ METHOD(job_t, terminate_ike_execute, job_requeue_t,
        }
        else
        {
-               listener->status = SUCCESS;
+               if (!listener->logger.callback)
+               {
+                       listener->status = SUCCESS;
+               }
                charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
                                                                                                        ike_sa);
        }
@@ -454,6 +502,7 @@ METHOD(controller_t, terminate_ike, status_t,
                        },
                        .status = FAILED,
                        .id = unique_id,
+                       .lock = spinlock_create(),
                },
                .public = {
                        .execute = _terminate_ike_execute,
@@ -463,6 +512,7 @@ METHOD(controller_t, terminate_ike, status_t,
                .refcount = 1,
        );
        job->listener.logger.listener = &job->listener;
+       thread_cleanup_push((void*)destroy_job, job);
 
        if (callback == NULL)
        {
@@ -476,7 +526,7 @@ METHOD(controller_t, terminate_ike, status_t,
                }
        }
        status = job->listener.status;
-       destroy_job(job);
+       thread_cleanup_pop(TRUE);
        return status;
 }
 
@@ -484,40 +534,28 @@ METHOD(job_t, terminate_child_execute, job_requeue_t,
        interface_job_t *job)
 {
        interface_listener_t *listener = &job->listener;
-       u_int32_t reqid = listener->id;
-       enumerator_t *enumerator;
+       u_int32_t id = listener->id;
        child_sa_t *child_sa;
        ike_sa_t *ike_sa;
 
-       ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
-                                                                                                       reqid, TRUE);
+       ike_sa = charon->child_sa_manager->checkout_by_id(charon->child_sa_manager,
+                                                                                                         id, &child_sa);
        if (!ike_sa)
        {
-               DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found",
-                        reqid);
+               DBG1(DBG_IKE, "unable to terminate, CHILD_SA with ID %d not found", id);
                listener->status = NOT_FOUND;
                /* release listener */
                listener_done(listener);
                return JOB_REQUEUE_NONE;
        }
-       job->listener.ike_sa = ike_sa;
-
-       enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
-       while (enumerator->enumerate(enumerator, (void**)&child_sa))
-       {
-               if (child_sa->get_state(child_sa) != CHILD_ROUTED &&
-                       child_sa->get_reqid(child_sa) == reqid)
-               {
-                       break;
-               }
-               child_sa = NULL;
-       }
-       enumerator->destroy(enumerator);
+       listener->lock->lock(listener->lock);
+       listener->ike_sa = ike_sa;
+       listener->lock->unlock(listener->lock);
 
-       if (!child_sa)
+       if (child_sa->get_state(child_sa) == CHILD_ROUTED)
        {
                DBG1(DBG_IKE, "unable to terminate, established "
-                        "CHILD_SA with ID %d not found", reqid);
+                        "CHILD_SA with ID %d not found", id);
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
                listener->status = NOT_FOUND;
                /* release listener */
@@ -528,7 +566,10 @@ METHOD(job_t, terminate_child_execute, job_requeue_t,
        if (ike_sa->delete_child_sa(ike_sa, child_sa->get_protocol(child_sa),
                                        child_sa->get_spi(child_sa, TRUE), FALSE) != DESTROY_ME)
        {
-               listener->status = SUCCESS;
+               if (!listener->logger.callback)
+               {
+                       listener->status = SUCCESS;
+               }
                charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
        }
        else
@@ -541,7 +582,7 @@ METHOD(job_t, terminate_child_execute, job_requeue_t,
 }
 
 METHOD(controller_t, terminate_child, status_t,
-       controller_t *this, u_int32_t reqid,
+       controller_t *this, u_int32_t unique_id,
        controller_cb_t callback, void *param, u_int timeout)
 {
        interface_job_t *job;
@@ -562,7 +603,8 @@ METHOD(controller_t, terminate_child, status_t,
                                .param = param,
                        },
                        .status = FAILED,
-                       .id = reqid,
+                       .id = unique_id,
+                       .lock = spinlock_create(),
                },
                .public = {
                        .execute = _terminate_child_execute,
@@ -572,6 +614,7 @@ METHOD(controller_t, terminate_child, status_t,
                .refcount = 1,
        );
        job->listener.logger.listener = &job->listener;
+       thread_cleanup_push((void*)destroy_job, job);
 
        if (callback == NULL)
        {
@@ -585,7 +628,7 @@ METHOD(controller_t, terminate_child, status_t,
                }
        }
        status = job->listener.status;
-       destroy_job(job);
+       thread_cleanup_pop(TRUE);
        return status;
 }
 
@@ -593,7 +636,7 @@ METHOD(controller_t, terminate_child, status_t,
  * See header
  */
 bool controller_cb_empty(void *param, debug_t group, level_t level,
-                                                ike_sa_t *ike_sa, char *message)
+                                                ike_sa_t *ike_sa, const char *message)
 {
        return TRUE;
 }
@@ -623,4 +666,3 @@ controller_t *controller_create(void)
 
        return &this->public;
 }
-