Isakmp_dpd task added.
authorClavister OpenSource <opensource@clavister.com>
Tue, 10 Jan 2012 13:37:39 +0000 (14:37 +0100)
committerClavister OpenSource <opensource@clavister.com>
Tue, 20 Mar 2012 16:31:35 +0000 (17:31 +0100)
src/libcharon/Makefile.am [changed mode: 0644->0755]
src/libcharon/sa/ikev1/task_manager_v1.c [changed mode: 0644->0755]
src/libcharon/sa/ikev1/task_manager_v1.h [changed mode: 0644->0755]
src/libcharon/sa/ikev1/tasks/aggressive_mode.c [changed mode: 0644->0755]
src/libcharon/sa/ikev1/tasks/informational.c [changed mode: 0644->0755]
src/libcharon/sa/ikev1/tasks/informational.h [changed mode: 0644->0755]
src/libcharon/sa/ikev1/tasks/isakmp_dpd.c [new file with mode: 0755]
src/libcharon/sa/ikev1/tasks/isakmp_dpd.h [new file with mode: 0755]
src/libcharon/sa/ikev1/tasks/main_mode.c [changed mode: 0644->0755]
src/libcharon/sa/ikev1/tasks/quick_mode.c [changed mode: 0644->0755]
src/libcharon/sa/task.h [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
index c85c472..d5e1394
@@ -113,6 +113,7 @@ sa/ikev1/tasks/isakmp_cert_post.c sa/ikev1/tasks/isakmp_cert_post.h \
 sa/ikev1/tasks/isakmp_natd.c sa/ikev1/tasks/isakmp_natd.h \
 sa/ikev1/tasks/isakmp_vendor.c sa/ikev1/tasks/isakmp_vendor.h \
 sa/ikev1/tasks/isakmp_delete.c sa/ikev1/tasks/isakmp_delete.h \
+sa/ikev1/tasks/isakmp_dpd.c sa/ikev1/tasks/isakmp_dpd.h \
 sa/ikev1/tasks/xauth.c sa/ikev1/tasks/xauth.h \
 sa/ikev1/tasks/quick_mode.c sa/ikev1/tasks/quick_mode.h \
 sa/ikev1/tasks/quick_delete.c sa/ikev1/tasks/quick_delete.h \
old mode 100644 (file)
new mode 100755 (executable)
index f7415a5..7e1ff61
@@ -31,6 +31,8 @@
 #include <sa/ikev1/tasks/isakmp_cert_pre.h>
 #include <sa/ikev1/tasks/isakmp_cert_post.h>
 #include <sa/ikev1/tasks/isakmp_delete.h>
+#include <sa/ikev1/tasks/isakmp_dpd.h>
+
 #include <processing/jobs/retransmit_job.h>
 #include <processing/jobs/delete_ike_sa_job.h>
 
@@ -197,6 +199,16 @@ struct private_task_manager_t {
         * Base to calculate retransmission timeout
         */
        double retransmit_base;
+
+       /**
+        * Sequence number for sending DPD requests
+        */
+       u_int32_t dpd_send_seqnr;
+
+       /**
+        * Sequence number for received DPD requests
+        */
+       u_int32_t dpd_rec_seqnr;
 };
 
 /**
@@ -406,6 +418,13 @@ METHOD(task_manager_t, initiate, status_t,
                                        new_mid = TRUE;
                                        break;
                                }
+
+                               if (activate_task(this, TASK_ISAKMP_DPD))
+                               {
+                                       exchange = INFORMATIONAL_V1;
+                                       new_mid = TRUE;
+                                       break;
+                               }
                                break;
                        default:
                                break;
@@ -580,7 +599,20 @@ 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));
-       message->set_message_id(message, request->get_message_id(request));
+
+       /* 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_request(message, FALSE);
 
        this->responding.mid = request->get_message_id(request);
@@ -710,6 +742,7 @@ static status_t process_request(private_task_manager_t *this,
        enumerator_t *enumerator;
        task_t *task = NULL;
        bool send_response = FALSE;
+       bool informational = FALSE;
 
        if (message->get_exchange_type(message) == INFORMATIONAL_V1 ||
                this->passive_tasks->get_count(this->passive_tasks) == 0)
@@ -752,8 +785,9 @@ static status_t process_request(private_task_manager_t *this,
                                this->passive_tasks->insert_last(this->passive_tasks, task);
                                break;
                        case INFORMATIONAL_V1:
-                               task = (task_t *)informational_create(this->ike_sa, NULL);
+                               task = (task_t *)informational_create(this->ike_sa, NULL, this->dpd_rec_seqnr);
                                this->passive_tasks->insert_first(this->passive_tasks, task);
+                               informational = TRUE;
                                break;
                        case TRANSACTION:
                                if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED)
@@ -784,6 +818,15 @@ static status_t process_request(private_task_manager_t *this,
                        case NEED_MORE:
                                /* processed, but task needs at least another call to build() */
                                send_response = TRUE;
+                               if (informational && !this->dpd_rec_seqnr)
+                               {
+                                       /* Update the received DPD sequence number if it the first received one */
+                                       if (task->get_type(task) == TASK_ISAKMP_DPD)
+                                       {
+                                               isakmp_dpd_t *isakmp_dpd = (isakmp_dpd_t *)task;
+                                               this->dpd_rec_seqnr = isakmp_dpd->get_dpd_seqnr(isakmp_dpd);
+                                       }
+                               }
                                continue;
                        case ALREADY_DONE:
                                send_response = FALSE;
@@ -950,6 +993,7 @@ METHOD(task_manager_t, process_message, status_t,
        u_int32_t hash, mid, i;
        host_t *me, *other;
        status_t status;
+       bool dpd_response = FALSE;
 
        /* TODO-IKEv1: update hosts more selectively */
        me = msg->get_destination(msg);
@@ -977,7 +1021,27 @@ METHOD(task_manager_t, process_message, status_t,
                }
        }
 
-       if ((mid && mid == this->initiating.mid) ||
+       /* DPD Acks are not sent with a same message ID as the request.*/
+       if (msg->get_exchange_type(msg) == INFORMATIONAL_V1 &&
+               this->active_tasks->get_count(this->active_tasks))
+       {
+               enumerator_t *enumerator;
+               task_t *task;
+               /* In case of ongoing DPD request, let the DPD task handle all information exchanges. */
+               enumerator = this->active_tasks->create_enumerator(this->active_tasks);
+               while (enumerator->enumerate(enumerator, (void**)&task))
+               {
+                       if (task->get_type(task) == TASK_ISAKMP_DPD)
+                       {
+                               dpd_response = TRUE;
+                               break;
+                       }
+               }
+               enumerator->destroy(enumerator);
+       }
+
+
+       if ((mid && mid == this->initiating.mid) || dpd_response ||
                (this->initiating.mid == 0 &&
                 msg->get_exchange_type(msg) == this->initiating.type &&
                 this->active_tasks->get_count(this->active_tasks)))
@@ -1271,10 +1335,16 @@ METHOD(task_manager_t, queue_child_delete, void,
                                                                                                  spi, FALSE, expired));
 }
 
+METHOD(task_manager_v1_t, get_dpd_seqnr, u_int32_t,
+       private_task_manager_t *this)
+{
+       return this->dpd_send_seqnr++;
+}
+
 METHOD(task_manager_t, queue_dpd, void,
        private_task_manager_t *this)
 {
-       /* TODO-IKEv1: DPD checking */
+       queue_task(this, (task_t*)isakmp_dpd_create(this->ike_sa, NULL, _get_dpd_seqnr(&this->public)));
 }
 
 METHOD(task_manager_t, adopt_tasks, void,
@@ -1401,6 +1471,7 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
                                .create_task_enumerator = _create_task_enumerator,
                                .destroy = _destroy,
                        },
+                       .get_dpd_seqnr = _get_dpd_seqnr,
                },
                .ike_sa = ike_sa,
                .initiating.type = EXCHANGE_TYPE_UNDEFINED,
@@ -1417,5 +1488,11 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
                                                                "charon.retransmit_base", RETRANSMIT_BASE),
        );
 
+       this->rng->get_bytes(this->rng, sizeof(this->dpd_send_seqnr),
+                                                        (void*)&this->dpd_send_seqnr);
+
+       this->dpd_send_seqnr &= 0x7FFFFFFF;
+
        return &this->public;
 }
+
old mode 100644 (file)
new mode 100755 (executable)
index 99cd35e..58b20d0
@@ -34,6 +34,11 @@ struct task_manager_v1_t {
         * Implements task_manager_t.
         */
        task_manager_t task_manager;
+
+       /**
+        * Get new sequence number to use for sending DPD request.
+        */
+       u_int32_t (*get_dpd_seqnr) (task_manager_v1_t *this);
 };
 
 /**
old mode 100644 (file)
new mode 100755 (executable)
index 1fe36a9..df0c94b
@@ -164,7 +164,7 @@ static status_t send_notify(private_aggressive_mode_t *this, notify_type_t type)
        notify->set_spi_data(notify, spi);
 
        this->ike_sa->queue_task(this->ike_sa,
-                                               (task_t*)informational_create(this->ike_sa, notify));
+                                               (task_t*)informational_create(this->ike_sa, notify, 0));
        /* cancel all active/passive tasks in favour of informational */
        return ALREADY_DONE;
 }
old mode 100644 (file)
new mode 100755 (executable)
index 999b497..2af641e
@@ -18,6 +18,8 @@
 #include <daemon.h>
 #include <sa/ikev1/tasks/isakmp_delete.h>
 #include <sa/ikev1/tasks/quick_delete.h>
+#include <sa/ikev1/tasks/isakmp_dpd.h>
+
 #include <encoding/payloads/delete_payload.h>
 
 typedef struct private_informational_t private_informational_t;
@@ -46,6 +48,16 @@ struct private_informational_t {
         * Delete subtask
         */
        task_t *del;
+
+       /**
+        * DPD subtask
+        */
+       task_t *dpd;
+
+       /**
+        * DPD sequence number
+        */
+       u_int32_t dpd_seqnr;
 };
 
 METHOD(task_t, build_i, status_t,
@@ -80,6 +92,15 @@ METHOD(task_t, process_r, status_t,
                                        this->ike_sa->set_condition(this->ike_sa,
                                                                                                COND_INIT_CONTACT_SEEN, TRUE);
                                }
+                               else if (type == DPD_R_U_THERE)
+                               {
+                                       DBG3(DBG_IKE, "received DPD request");
+                                       this->dpd = (task_t*)isakmp_dpd_create(this->ike_sa, notify, this->dpd_seqnr);
+                               }
+                               else if (type == DPD_R_U_THERE_ACK)
+                               {
+                                       DBG3(DBG_IKE, "received DPD request ack");
+                               }
                                else if (type < 16384)
                                {
                                        DBG1(DBG_IKE, "received %N error notify",
@@ -123,6 +144,11 @@ METHOD(task_t, process_r, status_t,
        {
                return this->del->process(this->del, message);
        }
+
+       if (this->dpd && status == SUCCESS)
+       {
+               return this->dpd->process(this->dpd, message);
+       }
        return status;
 }
 
@@ -133,6 +159,13 @@ METHOD(task_t, build_r, status_t,
        {
                return this->del->build(this->del, message);
        }
+
+       if (this->dpd)
+       {
+               status_t status = this->dpd->build(this->dpd, message);
+               this->dpd->destroy(this->dpd);
+               return status;
+       }
        return FAILED;
 }
 
@@ -165,7 +198,7 @@ METHOD(task_t, destroy, void,
 /*
  * Described in header.
  */
-informational_t *informational_create(ike_sa_t *ike_sa, notify_payload_t *notify)
+informational_t *informational_create(ike_sa_t *ike_sa, notify_payload_t *notify, u_int32_t dpd_seqnr)
 {
        private_informational_t *this;
 
@@ -179,6 +212,7 @@ informational_t *informational_create(ike_sa_t *ike_sa, notify_payload_t *notify
                },
                .ike_sa = ike_sa,
                .notify = notify,
+               .dpd_seqnr = dpd_seqnr,
        );
 
        if (notify)
old mode 100644 (file)
new mode 100755 (executable)
index f1543dc..59b8110
@@ -44,8 +44,9 @@ struct informational_t {
  *
  * @param ike_sa               IKE_SA this task works for
  * @param notify               notify to send as initiator, NULL if responder
+ * @param dpd_seqnr    DPD sequence number, incoming or outgoing
  * @return                             task to handle by the task_manager
  */
-informational_t *informational_create(ike_sa_t *ike_sa, notify_payload_t *notify);
+informational_t *informational_create(ike_sa_t *ike_sa, notify_payload_t *notify, u_int32_t dpd_seqnr);
 
 #endif /** INFORMATIONAL_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_dpd.c b/src/libcharon/sa/ikev1/tasks/isakmp_dpd.c
new file mode 100755 (executable)
index 0000000..ec0a1ed
--- /dev/null
@@ -0,0 +1,278 @@
+#include "isakmp_dpd.h"
+
+#include <encoding/payloads/notify_payload.h>
+#include <sa/ikev1/tasks/informational.h>
+
+#include <daemon.h>
+
+#ifdef SLIPSTREAM
+/* Should be the last include */
+#include <ikev2_mem.h>
+#endif /* SLIPSTREAM */
+
+typedef struct private_isakmp_dpd_t private_isakmp_dpd_t;
+
+/**
+ * Private members of a isakmp_dpd_t task.
+ */
+struct private_isakmp_dpd_t {
+
+       /**
+        * Public methods and task_t interface.
+        */
+       isakmp_dpd_t public;
+
+       /**
+        * Sequence number.
+        */
+       u_int32_t seqnr;
+
+       /**
+        * Notify payload, only provided for requests.
+        */
+       notify_payload_t *notify;
+
+       /**
+        * IKE SA we are serving.
+        */
+       ike_sa_t *ike_sa;
+};
+
+/**
+ * Get DPD sequence number from notify payload.
+ */
+static bool get_seqnr(notify_payload_t *notify, u_int32_t *seqnr)
+{
+       chunk_t chunk = notify->get_notification_data(notify);
+
+       if( chunk.ptr && chunk.len == 4)
+       {
+               u_int32_t seqnr_read = *((u_int32_t*)chunk.ptr);
+
+               *seqnr = ntohl(seqnr_read);
+
+               return TRUE;
+       }
+
+       DBG1(DBG_IKE, "no DPD seqnr received");
+
+       return FALSE;
+}
+
+/**
+ * Add notify payload to message.
+ */
+static void add_notify(private_isakmp_dpd_t *this, message_t *message, notify_type_t type)
+{
+       notify_payload_t *notify;
+
+       ike_sa_id_t *ike_sa_id;
+       u_int64_t spi_i, spi_r;
+       u_int32_t seqnr;
+       chunk_t spi;
+
+       notify = notify_payload_create_from_protocol_and_type(NOTIFY_V1,
+               PROTO_IKE, type);
+
+       seqnr = htonl(this->seqnr);
+       ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+       spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
+       spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
+       spi = chunk_cata("cc", chunk_from_thing(spi_i), chunk_from_thing(spi_r));
+
+       notify->set_spi_data(notify, spi);
+       notify->set_notification_data(notify, chunk_from_thing(seqnr));
+
+       message->add_payload(message, (payload_t*)notify);
+}
+
+METHOD(isakmp_dpd_t, get_dpd_seqnr, u_int32_t,
+       private_isakmp_dpd_t *this)
+{
+       return this->seqnr;
+}
+
+METHOD(task_t, build_i, status_t,
+       private_isakmp_dpd_t *this, message_t *message)
+{
+       add_notify(this, message, DPD_R_U_THERE);
+
+       return NEED_MORE;
+}
+
+METHOD(task_t, build_r, status_t,
+                        private_isakmp_dpd_t *this, message_t *message)
+{
+       add_notify(this, message, DPD_R_U_THERE_ACK);
+
+       return SUCCESS;
+}
+
+METHOD(task_t, process_i, status_t,
+       private_isakmp_dpd_t *this, message_t *message)
+{
+       enumerator_t *enumerator;
+       notify_payload_t *notify;
+       notify_type_t type;
+       payload_t *payload;
+       task_t *info_task = NULL;
+
+       enumerator = message->create_payload_enumerator(message);
+       while (enumerator->enumerate(enumerator, &payload))
+       {
+               switch (payload->get_type(payload))
+               {
+                       case NOTIFY_V1:
+                               notify = (notify_payload_t*)payload;
+                               type = notify->get_notify_type(notify);
+
+                               if (type == DPD_R_U_THERE_ACK)
+                               {
+                                       u_int32_t seqnr;
+
+                                       if (!get_seqnr(notify, &seqnr))
+                                       {
+                                               return FAILED;
+                                       }
+
+                                       if (this->seqnr != seqnr)
+                                       {
+                                               DBG1(DBG_IKE, "received DPD Ack with unexpected seqnr (%u) expect (%u)",seqnr,this->seqnr);
+                                               return SUCCESS;
+                                       }
+
+                                       DBG4(DBG_IKE, "received DPD Ack with seqnr (%u)",seqnr);
+
+                                       return SUCCESS;
+
+                               }
+                               else if (type == DPD_R_U_THERE)
+                               {
+                                       u_int32_t expected = this->seqnr + 1;
+
+                                       if (!get_seqnr(notify, &this->seqnr))
+                                       {
+                                               return FAILED;
+                                       }
+
+                                       if (expected != 1 && this->seqnr != expected)
+                                       {
+                                               DBG1(DBG_IKE, "received DPD request with unexpected seqnr (%u) expect (%u)",
+                                                       this->seqnr,expected);
+                                               return SUCCESS;
+                                       }
+
+                                       DBG4(DBG_IKE, "received DPD request with seqnr %u",this->seqnr);
+
+                                       this->public.task.build = _build_r;
+                                       return NEED_MORE;
+                               }
+                               else
+                               {
+                                       info_task = (task_t*)informational_create(this->ike_sa, NULL, 0);
+                               }
+                               continue;
+
+                       default:
+                               continue;
+               }
+               break;
+       }
+       enumerator->destroy(enumerator);
+
+       if (info_task)
+       {
+               status_t status = info_task->process(info_task, message);
+               /* Assuming that the informational task will not need to send other replies than dpd */
+               info_task->destroy(info_task);
+               return status;
+       }
+
+       return SUCCESS;
+}
+
+METHOD(task_t, process_r, status_t,
+                        private_isakmp_dpd_t *this, message_t *message)
+{
+       u_int32_t expected = this->seqnr + 1;
+
+       if (this->notify)
+       {
+               if (!get_seqnr(this->notify, &this->seqnr))
+               {
+                       return FAILED;
+               }
+
+               if (expected != 1 && this->seqnr != expected)
+               {
+                       DBG1(DBG_IKE, "received DPD request with unexpected seqnr (%u) expect (%u)",
+                               this->seqnr,expected);
+                       return SUCCESS;
+               }
+
+               DBG4(DBG_IKE, "DPD request received with seqnr %u",this->seqnr);
+       }
+       else
+       {
+               DBG1(DBG_IKE, "no notify provided");
+               return FAILED;
+       }
+       return NEED_MORE;
+}
+
+
+METHOD(task_t, get_type, task_type_t,
+       private_isakmp_dpd_t *this)
+{
+       return TASK_ISAKMP_DPD;
+}
+
+
+METHOD(task_t, migrate, void,
+       private_isakmp_dpd_t *this, ike_sa_t *ike_sa)
+{
+       this->ike_sa = ike_sa;
+       this->seqnr = 0;
+
+}
+
+METHOD(task_t, destroy, void,
+       private_isakmp_dpd_t *this)
+{
+       free(this);
+}
+
+/*
+ * Described in header.
+ */
+isakmp_dpd_t *isakmp_dpd_create(ike_sa_t *ike_sa, notify_payload_t *notify, u_int32_t seqnr)
+{
+       private_isakmp_dpd_t *this;
+
+       INIT(this,
+               .public = {
+                       .task = {
+                               .get_type = _get_type,
+                               .migrate = _migrate,
+                               .destroy = _destroy,
+                       },
+                       .get_dpd_seqnr = _get_dpd_seqnr,
+               },
+               .notify = notify,
+               .ike_sa = ike_sa,
+               .seqnr = seqnr,
+       );
+
+       if (!notify)
+       {
+               this->public.task.build = _build_i;
+               this->public.task.process = _process_i;
+       }
+       else
+       {
+               this->public.task.build = _build_r;
+               this->public.task.process = _process_r;
+       }
+
+       return &this->public;
+}
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_dpd.h b/src/libcharon/sa/ikev1/tasks/isakmp_dpd.h
new file mode 100755 (executable)
index 0000000..5d940b8
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef ISAKMP_DPD_H_
+#define ISAPMP_DPD_H_
+
+typedef struct isakmp_dpd_t isakmp_dpd_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * Task of type isakmp_dpd, detects dead peers.
+ *
+ *
+ */
+struct isakmp_dpd_t {
+
+       /**
+        * Implements the task_t interface
+        */
+       task_t task;
+
+       /**
+        * Get the received dpd seqnr.
+        *
+        * @return                              protocol ID
+        */
+       u_int32_t (*get_dpd_seqnr) (isakmp_dpd_t *dpd_task);
+};
+
+/**
+ * Create a new isakmp_dpd task.
+ *
+ * @param initiator            TRUE if task is the original initiator
+ * @return                             isakmp_dpd task to handle by the task_manager
+ */
+isakmp_dpd_t *isakmp_dpd_create(ike_sa_t *ike_sa, notify_payload_t *notify, u_int32_t seqnr);
+
+#endif /** ISAKMP_DPD_H_ @}*/
\ No newline at end of file
old mode 100644 (file)
new mode 100755 (executable)
index ba1a9ad..f1e3290
@@ -176,7 +176,7 @@ static status_t send_notify(private_main_mode_t *this, notify_type_t type)
        notify->set_spi_data(notify, spi);
 
        this->ike_sa->queue_task(this->ike_sa,
-                                               (task_t*)informational_create(this->ike_sa, notify));
+                                               (task_t*)informational_create(this->ike_sa, notify, 0));
        /* cancel all active/passive tasks in favour of informational */
        return ALREADY_DONE;
 }
old mode 100644 (file)
new mode 100755 (executable)
index dedeab1..b27b00d
@@ -539,7 +539,7 @@ static status_t send_notify(private_quick_mode_t *this, notify_type_t type)
        notify->set_spi(notify, this->spi_i);
 
        this->ike_sa->queue_task(this->ike_sa,
-                                               (task_t*)informational_create(this->ike_sa, notify));
+                                               (task_t*)informational_create(this->ike_sa, notify, 0));
        /* cancel all active/passive tasks in favour of informational */
        return ALREADY_DONE;
 }
old mode 100644 (file)
new mode 100755 (executable)
index 6b5c929..1b42bf1
@@ -93,6 +93,9 @@ enum task_type_t {
        TASK_ISAKMP_CERT_PRE,
        /** IKEv1 post-authentication certificate handling */
        TASK_ISAKMP_CERT_POST,
+       /** IKEv1 DPD */
+       TASK_ISAKMP_DPD,
+
 };
 
 /**