Properly compare CHILD_SAs during rekey collision
[strongswan.git] / src / libcharon / sa / ikev2 / tasks / child_rekey.c
index 50a8aad..d2003bb 100644 (file)
@@ -87,6 +87,24 @@ struct private_child_rekey_t {
 };
 
 /**
+ * Schedule a retry if rekeying temporary failed
+ */
+static void schedule_delayed_rekey(private_child_rekey_t *this)
+{
+       u_int32_t retry;
+       job_t *job;
+
+       retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
+       job = (job_t*)rekey_child_sa_job_create(
+                                               this->child_sa->get_reqid(this->child_sa),
+                                               this->child_sa->get_protocol(this->child_sa),
+                                               this->child_sa->get_spi(this->child_sa, TRUE));
+       DBG1(DBG_IKE, "CHILD_SA rekeying failed, trying again in %d seconds", retry);
+       this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+       lib->scheduler->schedule_job(lib->scheduler, job, retry);
+}
+
+/**
  * Implementation of task_t.build for initiator, after rekeying
  */
 static status_t build_i_delete(private_child_rekey_t *this, message_t *message)
@@ -161,13 +179,18 @@ METHOD(task_t, build_i, status_t,
        /* ... our CHILD_CREATE task does the hard work for us. */
        if (!this->child_create)
        {
-               this->child_create = child_create_create(this->ike_sa, config, TRUE,
-                                                                                                NULL, NULL);
+               this->child_create = child_create_create(this->ike_sa,
+                                                                       config->get_ref(config), TRUE, NULL, NULL);
        }
        reqid = this->child_sa->get_reqid(this->child_sa);
        this->child_create->use_reqid(this->child_create, reqid);
-       this->child_create->task.build(&this->child_create->task, message);
 
+       if (this->child_create->task.build(&this->child_create->task,
+                                                                          message) != NEED_MORE)
+       {
+               schedule_delayed_rekey(this);
+               return FAILED;
+       }
        this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
 
        return NEED_MORE;
@@ -187,6 +210,7 @@ METHOD(task_t, process_r, status_t,
 METHOD(task_t, build_r, status_t,
        private_child_rekey_t *this, message_t *message)
 {
+       child_cfg_t *config;
        u_int32_t reqid;
 
        if (this->child_sa == NULL ||
@@ -200,6 +224,8 @@ METHOD(task_t, build_r, status_t,
        /* let the CHILD_CREATE task build the response */
        reqid = this->child_sa->get_reqid(this->child_sa);
        this->child_create->use_reqid(this->child_create, reqid);
+       config = this->child_sa->get_config(this->child_sa);
+       this->child_create->set_config(this->child_create, config->get_ref(config));
        this->child_create->task.build(&this->child_create->task, message);
 
        if (message->get_payload(message, SECURITY_ASSOCIATION) == NULL)
@@ -313,17 +339,7 @@ METHOD(task_t, process_i, status_t,
                if (!(this->collision &&
                          this->collision->get_type(this->collision) == TASK_CHILD_DELETE))
                {
-                       job_t *job;
-                       u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
-
-                       job = (job_t*)rekey_child_sa_job_create(
-                                                               this->child_sa->get_reqid(this->child_sa),
-                                                               this->child_sa->get_protocol(this->child_sa),
-                                                               this->child_sa->get_spi(this->child_sa, TRUE));
-                       DBG1(DBG_IKE, "CHILD_SA rekeying failed, "
-                                                               "trying again in %d seconds", retry);
-                       this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
-                       lib->scheduler->schedule_job(lib->scheduler, job, retry);
+                       schedule_delayed_rekey(this);
                }
                return SUCCESS;
        }
@@ -352,7 +368,7 @@ METHOD(task_t, process_i, status_t,
        protocol = to_delete->get_protocol(to_delete);
 
        /* rekeying done, delete the obsolete CHILD_SA using a subtask */
-       this->child_delete = child_delete_create(this->ike_sa, protocol, spi);
+       this->child_delete = child_delete_create(this->ike_sa, protocol, spi, FALSE);
        this->public.task.build = (status_t(*)(task_t*,message_t*))build_i_delete;
        this->public.task.process = (status_t(*)(task_t*,message_t*))process_i_delete;
 
@@ -383,12 +399,19 @@ METHOD(child_rekey_t, collide, void,
        else if (other->get_type(other) == TASK_CHILD_DELETE)
        {
                child_delete_t *del = (child_delete_t*)other;
-               if (del->get_child(del) == this->child_create->get_child(this->child_create))
+               if (this->collision &&
+                       this->collision->get_type(this->collision) == TASK_CHILD_REKEY)
                {
-                       /* peer deletes redundant child created in collision */
-                       this->other_child_destroyed = TRUE;
-                       other->destroy(other);
-                       return;
+                       private_child_rekey_t *rekey;
+
+                       rekey = (private_child_rekey_t*)this->collision;
+                       if (del->get_child(del) == rekey->child_create->get_child(rekey->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)
                {