Check if colliding task has actually a CHILD, i.e. after a migrate
[strongswan.git] / src / libcharon / sa / tasks / child_rekey.c
index b5e4e84..05db057 100644 (file)
@@ -75,6 +75,15 @@ struct private_child_rekey_t {
         * colliding task, may be delete or rekey
         */
        task_t *collision;
         * colliding task, may be delete or rekey
         */
        task_t *collision;
+
+       /**
+        * Indicate that peer destroyed the redundant child from collision.
+        * This happens if a peer's delete notification for the redundant
+        * child gets processed before the rekey job. If so, we must not
+        * touch the child created in the collision since it points to
+        * memory already freed.
+        */
+       bool other_child_destroyed;
 };
 
 /**
 };
 
 /**
@@ -215,6 +224,70 @@ static status_t build_r(private_child_rekey_t *this, message_t *message)
 }
 
 /**
 }
 
 /**
+ * Handle a rekey collision
+ */
+static child_sa_t *handle_collision(private_child_rekey_t *this)
+{
+       child_sa_t *to_delete;
+
+       if (this->collision->get_type(this->collision) == CHILD_REKEY)
+       {
+               chunk_t this_nonce, other_nonce;
+               private_child_rekey_t *other = (private_child_rekey_t*)this->collision;
+
+               this_nonce = this->child_create->get_lower_nonce(this->child_create);
+               other_nonce = other->child_create->get_lower_nonce(other->child_create);
+
+               /* if we have the lower nonce, delete rekeyed SA. If not, delete
+                * the redundant. */
+               if (memcmp(this_nonce.ptr, other_nonce.ptr,
+                                  min(this_nonce.len, other_nonce.len)) > 0)
+               {
+                       child_sa_t *child_sa;
+
+                       DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting old child");
+                       to_delete = this->child_sa;
+                       /* don't touch child other created, it has already been deleted */
+                       if (!this->other_child_destroyed)
+                       {
+                               /* disable close action for the redundand child */
+                               child_sa = other->child_create->get_child(other->child_create);
+                               if (child_sa)
+                               {
+                                       child_sa->set_close_action(child_sa, ACTION_NONE);
+                               }
+                       }
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "CHILD_SA rekey collision lost, "
+                                "deleting rekeyed child");
+                       to_delete = this->child_create->get_child(this->child_create);
+               }
+       }
+       else
+       {       /* CHILD_DELETE */
+               child_delete_t *del = (child_delete_t*)this->collision;
+
+               /* we didn't had a chance to compare the nonces, so we delete
+                * the CHILD_SA the other is not deleting. */
+               if (del->get_child(del) != this->child_sa)
+               {
+                       DBG1(DBG_IKE, "CHILD_SA rekey/delete collision, "
+                                "deleting rekeyed child");
+                       to_delete = this->child_sa;
+               }
+               else
+               {
+                       DBG1(DBG_IKE, "CHILD_SA rekey/delete collision, "
+                                "deleting redundant child");
+                       to_delete = this->child_create->get_child(this->child_create);
+               }
+       }
+       return to_delete;
+}
+
+/**
  * Implementation of task_t.process for initiator
  */
 static status_t process_i(private_child_rekey_t *this, message_t *message)
  * Implementation of task_t.process for initiator
  */
 static status_t process_i(private_child_rekey_t *this, message_t *message)
@@ -228,7 +301,7 @@ static status_t process_i(private_child_rekey_t *this, message_t *message)
                DBG1(DBG_IKE, "peer seems to not support CHILD_SA rekeying, "
                         "starting reauthentication");
                this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
                DBG1(DBG_IKE, "peer seems to not support CHILD_SA rekeying, "
                         "starting reauthentication");
                this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
-               charon->processor->queue_job(charon->processor,
+               lib->processor->queue_job(lib->processor,
                                (job_t*)rekey_ike_sa_job_create(
                                                        this->ike_sa->get_id(this->ike_sa), TRUE));
                return SUCCESS;
                                (job_t*)rekey_ike_sa_job_create(
                                                        this->ike_sa->get_id(this->ike_sa), TRUE));
                return SUCCESS;
@@ -258,40 +331,19 @@ static status_t process_i(private_child_rekey_t *this, message_t *message)
                        DBG1(DBG_IKE, "CHILD_SA rekeying failed, "
                                                                "trying again in %d seconds", retry);
                        this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
                        DBG1(DBG_IKE, "CHILD_SA rekeying failed, "
                                                                "trying again in %d seconds", retry);
                        this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
-                       charon->scheduler->schedule_job(charon->scheduler, job, retry);
+                       lib->scheduler->schedule_job(lib->scheduler, job, retry);
                }
                return SUCCESS;
        }
 
                }
                return SUCCESS;
        }
 
-       to_delete = this->child_sa;
-
        /* check for rekey collisions */
        /* check for rekey collisions */
-       if (this->collision &&
-               this->collision->get_type(this->collision) == CHILD_REKEY)
+       if (this->collision)
        {
        {
-               chunk_t this_nonce, other_nonce;
-               private_child_rekey_t *other = (private_child_rekey_t*)this->collision;
-
-               this_nonce = this->child_create->get_lower_nonce(this->child_create);
-               other_nonce = other->child_create->get_lower_nonce(other->child_create);
-
-               /* if we have the lower nonce, delete rekeyed SA. If not, delete
-                * the redundant. */
-               if (memcmp(this_nonce.ptr, other_nonce.ptr,
-                                  min(this_nonce.len, other_nonce.len)) < 0)
-               {
-                       DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting rekeyed child");
-               }
-               else
-               {
-                       DBG1(DBG_IKE, "CHILD_SA rekey collision lost, deleting redundant child");
-                       to_delete = this->child_create->get_child(this->child_create);
-                       if (to_delete == NULL)
-                       {
-                               /* ooops, should not happen, fallback */
-                               to_delete = this->child_sa;
-                       }
-               }
+               to_delete = handle_collision(this);
+       }
+       else
+       {
+               to_delete = this->child_sa;
        }
 
        if (to_delete != this->child_create->get_child(this->child_create))
        }
 
        if (to_delete != this->child_create->get_child(this->child_create))
@@ -300,6 +352,10 @@ static status_t process_i(private_child_rekey_t *this, message_t *message)
                                                        this->child_create->get_child(this->child_create));
        }
 
                                                        this->child_create->get_child(this->child_create));
        }
 
+       if (to_delete == NULL)
+       {
+               return SUCCESS;
+       }
        spi = to_delete->get_spi(to_delete, TRUE);
        protocol = to_delete->get_protocol(to_delete);
 
        spi = to_delete->get_spi(to_delete, TRUE);
        protocol = to_delete->get_protocol(to_delete);
 
@@ -329,7 +385,7 @@ static void collide(private_child_rekey_t *this, task_t *other)
        if (other->get_type(other) == CHILD_REKEY)
        {
                private_child_rekey_t *rekey = (private_child_rekey_t*)other;
        if (other->get_type(other) == CHILD_REKEY)
        {
                private_child_rekey_t *rekey = (private_child_rekey_t*)other;
-               if (rekey == NULL || rekey->child_sa != this->child_sa)
+               if (rekey->child_sa != this->child_sa)
                {
                        /* not the same child => no collision */
                        other->destroy(other);
                {
                        /* not the same child => no collision */
                        other->destroy(other);
@@ -339,7 +395,14 @@ static void collide(private_child_rekey_t *this, task_t *other)
        else if (other->get_type(other) == CHILD_DELETE)
        {
                child_delete_t *del = (child_delete_t*)other;
        else if (other->get_type(other) == CHILD_DELETE)
        {
                child_delete_t *del = (child_delete_t*)other;
-               if (del == NULL || del->get_child(del) != this->child_sa)
+               if (del->get_child(del) == this->child_create->get_child(this->child_create))
+               {
+                       /* peer deletes redundant child created in collision */
+                       this->other_child_destroyed = TRUE;
+                       other->destroy(other);
+                       return;
+               }
+               if (del->get_child(del) != this->child_sa)
                {
                        /* not the same child => no collision */
                        other->destroy(other);
                {
                        /* not the same child => no collision */
                        other->destroy(other);
@@ -352,6 +415,8 @@ static void collide(private_child_rekey_t *this, task_t *other)
                other->destroy(other);
                return;
        }
                other->destroy(other);
                return;
        }
+       DBG1(DBG_IKE, "detected %N collision with %N", task_type_names, CHILD_REKEY,
+                task_type_names, other->get_type(other));
        DESTROY_IF(this->collision);
        this->collision = other;
 }
        DESTROY_IF(this->collision);
        this->collision = other;
 }
@@ -425,6 +490,7 @@ child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, protocol_id_t protocol,
        this->spi = spi;
        this->collision = NULL;
        this->child_delete = NULL;
        this->spi = spi;
        this->collision = NULL;
        this->child_delete = NULL;
+       this->other_child_destroyed = FALSE;
 
        return &this->public;
 }
 
        return &this->public;
 }