ike-rekey: Return TEMPORARY_FAILURE when concurrently creating a CHILD_SA
authorTobias Brunner <tobias@strongswan.org>
Thu, 2 Jun 2016 13:13:24 +0000 (15:13 +0200)
committerTobias Brunner <tobias@strongswan.org>
Fri, 17 Jun 2016 16:48:07 +0000 (18:48 +0200)
src/libcharon/sa/ikev2/tasks/ike_rekey.c

index 44684bc..2f0552a 100644 (file)
@@ -182,38 +182,59 @@ METHOD(task_t, build_i, status_t,
        return NEED_MORE;
 }
 
-METHOD(task_t, process_r, status_t,
-       private_ike_rekey_t *this, message_t *message)
+/**
+ * Check if there are any half-open children
+ */
+static bool have_half_open_children(private_ike_rekey_t *this)
 {
        enumerator_t *enumerator;
        child_sa_t *child_sa;
-
-       if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
-       {
-               DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting");
-               this->failed_temporarily = TRUE;
-               return NEED_MORE;
-       }
+       task_t *task;
 
        enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
        while (enumerator->enumerate(enumerator, (void**)&child_sa))
        {
                switch (child_sa->get_state(child_sa))
                {
-                       case CHILD_CREATED:
                        case CHILD_REKEYING:
                        case CHILD_RETRYING:
                        case CHILD_DELETING:
-                               /* we do not allow rekeying while we have children in-progress */
-                               DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open");
                                enumerator->destroy(enumerator);
-                               this->failed_temporarily = TRUE;
-                               return NEED_MORE;
+                               return TRUE;
                        default:
                                break;
                }
        }
        enumerator->destroy(enumerator);
+       enumerator = this->ike_sa->create_task_enumerator(this->ike_sa,
+                                                                                                         TASK_QUEUE_ACTIVE);
+       while (enumerator->enumerate(enumerator, (void**)&task))
+       {
+               if (task->get_type(task) == TASK_CHILD_CREATE)
+               {
+                       enumerator->destroy(enumerator);
+                       return TRUE;
+               }
+       }
+       enumerator->destroy(enumerator);
+       return FALSE;
+}
+
+METHOD(task_t, process_r, status_t,
+       private_ike_rekey_t *this, message_t *message)
+{
+       if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
+       {
+               DBG1(DBG_IKE, "peer initiated rekeying, but we are deleting");
+               this->failed_temporarily = TRUE;
+               return NEED_MORE;
+       }
+       if (have_half_open_children(this))
+       {
+               DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open");
+               this->failed_temporarily = TRUE;
+               return NEED_MORE;
+       }
 
        this->new_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
                                                        this->ike_sa->get_version(this->ike_sa), FALSE);