Queue Mode Config tasks when required
[strongswan.git] / src / libcharon / sa / task_manager_v1.c
index 41cd8a7..e899b06 100755 (executable)
@@ -22,7 +22,8 @@
 #include <sa/tasks/child_delete.h>
 #include <sa/tasks/main_mode.h>
 #include <sa/tasks/quick_mode.h>
-#include <sa/tasks/xauth_request.h>
+#include <sa/tasks/xauth.h>
+#include <sa/tasks/mode_config.h>
 #include <sa/tasks/ike_delete.h>
 #include <sa/tasks/ike_natd_v1.h>
 #include <sa/tasks/ike_vendor_v1.h>
@@ -280,13 +281,19 @@ METHOD(task_manager_t, initiate, status_t,
                                }
                                break;
                        case IKE_CONNECTING:
-                               if (activate_task(this, TASK_XAUTH_REQUEST))
+                               if (activate_task(this, TASK_XAUTH))
                                {
                                        exchange = TRANSACTION;
                                        new_mid = TRUE;
                                }
                                break;
                        case IKE_ESTABLISHED:
+                               if (activate_task(this, TASK_MODE_CONFIG))
+                               {
+                                       exchange = TRANSACTION;
+                                       new_mid = TRUE;
+                                       break;
+                               }
                                if (activate_task(this, TASK_QUICK_MODE))
                                {
                                        exchange = QUICK_MODE;
@@ -313,7 +320,7 @@ METHOD(task_manager_t, initiate, status_t,
                                case TASK_QUICK_MODE:
                                        exchange = QUICK_MODE;
                                        break;
-                               case TASK_XAUTH_REQUEST:
+                               case TASK_XAUTH:
                                        exchange = TRANSACTION;
                                        new_mid = TRUE;
                                        break;
@@ -400,15 +407,11 @@ METHOD(task_manager_t, initiate, status_t,
        {
                return retransmit(this, this->initiating.seqnr);
        }
-       else
-       {
-               charon->sender->send(charon->sender,
-                                       this->initiating.packet->clone(this->initiating.packet));
-
-               this->initiating.packet->destroy(this->initiating.packet);
-               this->initiating.packet = NULL;
-               return SUCCESS;
-       }
+       charon->sender->send(charon->sender,
+                               this->initiating.packet->clone(this->initiating.packet));
+       this->initiating.packet->destroy(this->initiating.packet);
+       this->initiating.packet = NULL;
+       return SUCCESS;
 }
 
 /**
@@ -501,6 +504,66 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
 }
 
 /**
+ * Send a notify in a separate INFORMATIONAL exchange back to the sender.
+ */
+static void send_notify_response(private_task_manager_t *this,
+                                                                message_t *request, notify_type_t type,
+                                                                chunk_t data, task_t *task)
+{
+       message_t *response;
+       packet_t *packet;
+       host_t *me, *other;
+       u_int32_t mid;
+
+       if (request && request->get_exchange_type(request) == INFORMATIONAL_V1)
+       {       /* don't respond to INFORMATIONAL requests to avoid a notify war */
+               DBG1(DBG_IKE, "ignore malformed INFORMATIONAL request");
+               return;
+       }
+
+       response = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION);
+       response->set_exchange_type(response, INFORMATIONAL_V1);
+       response->set_request(response, TRUE);
+       this->rng->get_bytes(this->rng, sizeof(mid), (void*)&mid);
+       response->set_message_id(response, mid);
+
+       if (task)
+       {
+               /* Let the task build the response */
+               if (task->build(task,response) != SUCCESS)
+               {
+                       response->destroy(response);
+                       return;
+               }
+       }
+       else
+       {
+               response->add_notify(response, FALSE, type, data);
+       }
+
+       me = this->ike_sa->get_my_host(this->ike_sa);
+       if (me->is_anyaddr(me))
+       {
+               me = request->get_destination(request);
+               this->ike_sa->set_my_host(this->ike_sa, me->clone(me));
+       }
+       other = this->ike_sa->get_other_host(this->ike_sa);
+       if (other->is_anyaddr(other))
+       {
+               other = request->get_source(request);
+               this->ike_sa->set_other_host(this->ike_sa, other->clone(other));
+       }
+       response->set_source(response, me->clone(me));
+       response->set_destination(response, other->clone(other));
+       if (this->ike_sa->generate_message(this->ike_sa, response,
+                                                                          &packet) == SUCCESS)
+       {
+               charon->sender->send(charon->sender, packet);
+       }
+       response->destroy(response);
+}
+
+/**
  * handle an incoming request message
  */
 static status_t process_request(private_task_manager_t *this,
@@ -532,6 +595,12 @@ static status_t process_request(private_task_manager_t *this,
                                /* TODO-IKEv1: agressive mode */
                                return FAILED;
                        case QUICK_MODE:
+                               if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED)
+                               {
+                                       DBG1(DBG_IKE, "received quick mode request for "
+                                                "unestablished IKE_SA, ignored");
+                                       return FAILED;
+                               }
                                task = (task_t *)quick_mode_create(this->ike_sa, NULL,
                                                                                                   NULL, NULL);
                                this->passive_tasks->insert_last(this->passive_tasks, task);
@@ -588,7 +657,14 @@ static status_t process_request(private_task_manager_t *this,
                                enumerator->destroy(enumerator);
                                break;
                        case TRANSACTION:
-                               task = (task_t *)xauth_request_create(this->ike_sa, FALSE);
+                               if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED)
+                               {
+                                       task = (task_t *)mode_config_create(this->ike_sa, FALSE);
+                               }
+                               else
+                               {
+                                       task = (task_t *)xauth_create(this->ike_sa, FALSE);
+                               }
                                this->passive_tasks->insert_last(this->passive_tasks, task);
                                break;
                        default:
@@ -610,6 +686,8 @@ static status_t process_request(private_task_manager_t *this,
                                /* processed, but task needs at least another call to build() */
                                send_response = TRUE;
                                break;
+                       case FAILED_SEND_ERROR:
+                               send_notify_response(this, NULL, 0, chunk_empty, task);
                        case FAILED:
                        default:
                                charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
@@ -626,7 +704,17 @@ static status_t process_request(private_task_manager_t *this,
 
        if (send_response)
        {
-               return build_response(this, message);
+               if (build_response(this, message) != SUCCESS)
+               {
+                       return DESTROY_ME;
+               }
+       }
+       if (this->passive_tasks->get_count(this->passive_tasks) == 0 &&
+               this->queued_tasks->get_count(this->queued_tasks) > 0)
+       {
+               /* passive tasks completed, check if an active task has been queued,
+                * such as XAUTH or modeconfig push */
+               return initiate(this);
        }
        return SUCCESS;
 }
@@ -684,52 +772,6 @@ static status_t process_response(private_task_manager_t *this,
 }
 
 /**
- * Send a notify in a separate INFORMATIONAL exchange back to the sender.
- */
-static void send_notify_response(private_task_manager_t *this,
-                                                                message_t *request, notify_type_t type,
-                                                                chunk_t data)
-{
-       message_t *response;
-       packet_t *packet;
-       host_t *me, *other;
-       u_int32_t mid;
-
-       if (request->get_exchange_type(request) == INFORMATIONAL_V1)
-       {       /* don't respond to INFORMATIONAL requests to avoid a notify war */
-               DBG1(DBG_IKE, "ignore malformed INFORMATIONAL request");
-               return;
-       }
-
-       response = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION);
-       response->set_exchange_type(response, INFORMATIONAL_V1);
-       response->set_request(response, TRUE);
-       this->rng->get_bytes(this->rng, sizeof(mid), (void*)&mid);
-       response->set_message_id(response, mid);
-       response->add_notify(response, FALSE, type, data);
-       me = this->ike_sa->get_my_host(this->ike_sa);
-       if (me->is_anyaddr(me))
-       {
-               me = request->get_destination(request);
-               this->ike_sa->set_my_host(this->ike_sa, me->clone(me));
-       }
-       other = this->ike_sa->get_other_host(this->ike_sa);
-       if (other->is_anyaddr(other))
-       {
-               other = request->get_source(request);
-               this->ike_sa->set_other_host(this->ike_sa, other->clone(other));
-       }
-       response->set_source(response, me->clone(me));
-       response->set_destination(response, other->clone(other));
-       if (this->ike_sa->generate_message(this->ike_sa, response,
-                                                                          &packet) == SUCCESS)
-       {
-               charon->sender->send(charon->sender, packet);
-       }
-       response->destroy(response);
-}
-
-/**
  * Parse the given message and verify that it is valid.
  */
 static status_t parse_message(private_task_manager_t *this, message_t *msg)
@@ -745,27 +787,27 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg)
                        case NOT_SUPPORTED:
                                DBG1(DBG_IKE, "unsupported exchange type");
                                send_notify_response(this, msg,
-                                                                        INVALID_EXCHANGE_TYPE, chunk_empty);
+                                                                        INVALID_EXCHANGE_TYPE, chunk_empty, NULL);
                                break;
                        case PARSE_ERROR:
                                DBG1(DBG_IKE, "message parsing failed");
                                send_notify_response(this, msg,
-                                                                        PAYLOAD_MALFORMED, chunk_empty);
+                                                                        PAYLOAD_MALFORMED, chunk_empty, NULL);
                                break;
                        case VERIFY_ERROR:
                                DBG1(DBG_IKE, "message verification failed");
                                send_notify_response(this, msg,
-                                                                        PAYLOAD_MALFORMED, chunk_empty);
+                                                                        PAYLOAD_MALFORMED, chunk_empty, NULL);
                                break;
                        case FAILED:
                                DBG1(DBG_IKE, "integrity check failed");
                                send_notify_response(this, msg,
-                                                                        INVALID_HASH_INFORMATION, chunk_empty);
+                                                                        INVALID_HASH_INFORMATION, chunk_empty, NULL);
                                break;
                        case INVALID_STATE:
                                DBG1(DBG_IKE, "found encrypted message, but no keys available");
                                send_notify_response(this, msg,
-                                                                        PAYLOAD_MALFORMED, chunk_empty);
+                                                                        PAYLOAD_MALFORMED, chunk_empty, NULL);
                        default:
                                break;
                }
@@ -844,7 +886,7 @@ METHOD(task_manager_t, process_message, status_t,
                                DBG1(DBG_IKE, "no IKE config found for %H...%H, sending %N",
                                         me, other, notify_type_names, NO_PROPOSAL_CHOSEN);
                                send_notify_response(this, msg,
-                                                                        NO_PROPOSAL_CHOSEN, chunk_empty);
+                                                                        NO_PROPOSAL_CHOSEN, chunk_empty, NULL);
                                return DESTROY_ME;
                        }
                        this->ike_sa->set_ike_cfg(this->ike_sa, ike_cfg);