Handle retransmission of DPD exchange, both as initiator and responder
authorMartin Willi <martin@revosec.ch>
Tue, 10 Jan 2012 18:13:58 +0000 (19:13 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 20 Mar 2012 16:31:36 +0000 (17:31 +0100)
src/libcharon/sa/ikev1/task_manager_v1.c

index 562a9eb..a7c088b 100755 (executable)
@@ -332,7 +332,7 @@ METHOD(task_manager_t, initiate, status_t,
        host_t *me, *other;
        status_t status;
        exchange_type_t exchange = EXCHANGE_TYPE_UNDEFINED;
-       bool new_mid = FALSE, expect_response = FALSE, flushed = FALSE;
+       bool new_mid = FALSE, expect_response = FALSE, flushed = FALSE, keep = FALSE;
 
        if (this->initiating.type != EXCHANGE_TYPE_UNDEFINED)
        {
@@ -412,7 +412,6 @@ METHOD(task_manager_t, initiate, status_t,
                                        new_mid = TRUE;
                                        break;
                                }
-
                                if (activate_task(this, TASK_ISAKMP_DPD))
                                {
                                        exchange = INFORMATIONAL_V1;
@@ -485,6 +484,11 @@ METHOD(task_manager_t, initiate, status_t,
                        case SUCCESS:
                                /* task completed, remove it */
                                this->active_tasks->remove_at(this->active_tasks, enumerator);
+                               if (task->get_type(task) == AGGRESSIVE ||
+                                       task->get_type(task) == QUICK_MODE)
+                               {       /* last message of three message exchange */
+                                       keep = TRUE;
+                               }
                                task->destroy(task);
                                continue;
                        case NEED_MORE:
@@ -513,10 +517,22 @@ METHOD(task_manager_t, initiate, status_t,
        }
        enumerator->destroy(enumerator);
 
-       if (this->active_tasks->get_count(this->active_tasks) == 0)
+       if (this->active_tasks->get_count(this->active_tasks) == 0 &&
+               (exchange == QUICK_MODE || exchange == AGGRESSIVE))
        {       /* tasks completed, no exchange active anymore */
                this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
        }
+       if (exchange == INFORMATIONAL_V1)
+       {
+               if (message->get_notify(message, DPD_R_U_THERE))
+               {
+                       expect_response = TRUE;
+               }
+               if (message->get_notify(message, DPD_R_U_THERE_ACK))
+               {
+                       keep = TRUE;
+               }
+       }
        if (flushed)
        {
                message->destroy(message);
@@ -537,15 +553,13 @@ METHOD(task_manager_t, initiate, status_t,
        }
 
        this->initiating.seqnr++;
-       if (expect_response)
+       if (expect_response )
        {
                message->destroy(message);
                return retransmit(this, this->initiating.seqnr);
        }
-       if (message->get_exchange_type(message) == QUICK_MODE ||
-               message->get_exchange_type(message) == AGGRESSIVE)
-       {       /* keep the packet for retransmission in quick/aggressive mode.
-                * The responder might request a retransmission */
+       if (keep)
+       {       /* keep the packet for retransmission, the responder might request it */
                charon->sender->send(charon->sender,
                                        this->initiating.packet->clone(this->initiating.packet));
        }
@@ -593,20 +607,7 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
        /* send response along the path the request came in */
        message->set_source(message, me->clone(me));
        message->set_destination(message, other->clone(other));
-
-       /* Create new message id for informational exchanges*/
-       if (request->get_exchange_type(request) == INFORMATIONAL_V1)
-       {
-               u_int32_t message_id;
-
-               this->rng->get_bytes(this->rng, sizeof(message_id),
-                                                        (void*)&message_id);
-               message->set_message_id(message, message_id);
-       }
-       else
-       {
-               message->set_message_id(message, request->get_message_id(request));
-       }
+       message->set_message_id(message, request->get_message_id(request));
        message->set_request(message, FALSE);
 
        this->responding.mid = request->get_message_id(request);
@@ -858,6 +859,12 @@ static status_t process_request(private_task_manager_t *this,
        }
        enumerator->destroy(enumerator);
 
+       if (dpd && this->initiating.type == INFORMATIONAL_V1)
+       {       /* got a DPD reply, cancel any retransmission */
+               this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+               DESTROY_IF(this->initiating.packet);
+               this->initiating.packet = NULL;
+       }
        if (send_response)
        {
                if (build_response(this, message) != SUCCESS)
@@ -1065,6 +1072,14 @@ METHOD(task_manager_t, process_message, status_t,
                                charon->sender->send(charon->sender,
                                                this->responding.packet->clone(this->responding.packet));
                        }
+                       else if (this->initiating.packet &&
+                                        this->initiating.type == INFORMATIONAL_V1)
+                       {
+                               DBG1(DBG_IKE, "received retransmit of DPD request, "
+                                        "retransmitting response");
+                               charon->sender->send(charon->sender,
+                                               this->initiating.packet->clone(this->initiating.packet));
+                       }
                        else
                        {
                                DBG1(DBG_IKE, "received retransmit of request with ID %u, "