Use CRITICAL job priority class for long running dispatcher jobs
[strongswan.git] / src / libcharon / network / receiver.c
index 63a8cab..2887595 100644 (file)
@@ -101,6 +101,16 @@ struct private_receiver_t {
        u_int32_t block_threshold;
 
        /**
+        * Drop IKE_SA_INIT requests if processor job load exceeds this limit
+        */
+       u_int init_limit_job_load;
+
+       /**
+        * Drop IKE_SA_INIT requests if half open IKE_SA count exceeds this limit
+        */
+       u_int init_limit_half_open;
+
+       /**
         * Delay for receiving incoming packets, to simulate larger RTT
         */
        int receive_delay;
@@ -146,7 +156,7 @@ static void send_notify(message_t *request, notify_type_t type, chunk_t data)
                ike_sa_id->switch_initiator(ike_sa_id);
                response->set_ike_sa_id(response, ike_sa_id);
                response->add_notify(response, FALSE, type, data);
-               if (response->generate(response, NULL, NULL, &packet) == SUCCESS)
+               if (response->generate(response, NULL, &packet) == SUCCESS)
                {
                        charon->sender->send(charon->sender, packet);
                        response->destroy(response);
@@ -215,55 +225,115 @@ static bool cookie_verify(private_receiver_t *this, message_t *message,
 }
 
 /**
- * check if cookies are required, and if so, a valid cookie is included
+ * Check if a valid cookie found
  */
-static bool cookie_required(private_receiver_t *this, message_t *message)
+static bool check_cookie(private_receiver_t *this, message_t *message)
 {
-       bool failed = FALSE;
-
-       if (charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager,
-                                                                                               NULL) >= this->cookie_threshold)
+       packet_t *packet;
+       chunk_t data;
+
+       /* check for a cookie. We don't use our parser here and do it
+        * quick and dirty for performance reasons.
+        * we assume the cookie is the first payload (which is a MUST), and
+        * the cookie's SPI length is zero. */
+       packet = message->get_packet(message);
+       data = packet->get_data(packet);
+       if (data.len <
+                IKE_HEADER_LENGTH + NOTIFY_PAYLOAD_HEADER_LENGTH +
+                sizeof(u_int32_t) + this->hasher->get_hash_size(this->hasher) ||
+               *(data.ptr + 16) != NOTIFY ||
+               *(u_int16_t*)(data.ptr + IKE_HEADER_LENGTH + 6) != htons(COOKIE))
        {
-               /* check for a cookie. We don't use our parser here and do it
-                * quick and dirty for performance reasons.
-                * we assume the cookie is the first payload (which is a MUST), and
-                * the cookie's SPI length is zero. */
-               packet_t *packet = message->get_packet(message);
-               chunk_t data = packet->get_data(packet);
-               if (data.len <
-                        IKE_HEADER_LENGTH + NOTIFY_PAYLOAD_HEADER_LENGTH +
-                        sizeof(u_int32_t) + this->hasher->get_hash_size(this->hasher) ||
-                       *(data.ptr + 16) != NOTIFY ||
-                       *(u_int16_t*)(data.ptr + IKE_HEADER_LENGTH + 6) != htons(COOKIE))
-               {
-                       /* no cookie found */
-                       failed = TRUE;
-               }
-               else
-               {
-                       data.ptr += IKE_HEADER_LENGTH + NOTIFY_PAYLOAD_HEADER_LENGTH;
-                       data.len = sizeof(u_int32_t) + this->hasher->get_hash_size(this->hasher);
-                       if (!cookie_verify(this, message, data))
-                       {
-                               DBG2(DBG_NET, "found cookie, but content invalid");
-                               failed = TRUE;
-                       }
-               }
+               /* no cookie found */
+               packet->destroy(packet);
+               return FALSE;
+       }
+       data.ptr += IKE_HEADER_LENGTH + NOTIFY_PAYLOAD_HEADER_LENGTH;
+       data.len = sizeof(u_int32_t) + this->hasher->get_hash_size(this->hasher);
+       if (!cookie_verify(this, message, data))
+       {
+               DBG2(DBG_NET, "found cookie, but content invalid");
                packet->destroy(packet);
+               return FALSE;
        }
-       return failed;
+       return TRUE;
 }
 
 /**
- * check if peer has to many half open IKE_SAs
+ * Check if we should drop IKE_SA_INIT because of cookie/overload checking
  */
-static bool peer_too_aggressive(private_receiver_t *this, message_t *message)
+static bool drop_ike_sa_init(private_receiver_t *this, message_t *message)
 {
-       if (charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager,
-                                               message->get_source(message)) >= this->block_threshold)
+       u_int half_open;
+
+       half_open = charon->ike_sa_manager->get_half_open_count(
+                                                                               charon->ike_sa_manager, NULL);
+
+       /* check for cookies */
+       if (this->cookie_threshold && half_open >= this->cookie_threshold &&
+               !check_cookie(this, message))
+       {
+               u_int32_t now = time_monotonic(NULL);
+               chunk_t cookie = cookie_build(this, message, now - this->secret_offset,
+                                                                         chunk_from_thing(this->secret));
+
+               DBG2(DBG_NET, "received packet from: %#H to %#H",
+                        message->get_source(message),
+                        message->get_destination(message));
+               DBG2(DBG_NET, "sending COOKIE notify to %H",
+                        message->get_source(message));
+               send_notify(message, COOKIE, cookie);
+               chunk_free(&cookie);
+               if (++this->secret_used > COOKIE_REUSE)
+               {
+                       /* create new cookie */
+                       DBG1(DBG_NET, "generating new cookie secret after %d uses",
+                                this->secret_used);
+                       memcpy(this->secret_old, this->secret, SECRET_LENGTH);
+                       this->rng->get_bytes(this->rng, SECRET_LENGTH, this->secret);
+                       this->secret_switch = now;
+                       this->secret_used = 0;
+               }
+               return TRUE;
+       }
+
+       /* check if peer has too many IKE_SAs half open */
+       if (this->block_threshold &&
+               charon->ike_sa_manager->get_half_open_count(charon->ike_sa_manager,
+                               message->get_source(message)) >= this->block_threshold)
        {
+               DBG1(DBG_NET, "ignoring IKE_SA setup from %H, "
+                        "peer too aggressive", message->get_source(message));
                return TRUE;
        }
+
+       /* check if global half open IKE_SA limit reached */
+       if (this->init_limit_half_open &&
+               half_open >= this->init_limit_half_open)
+       {
+               DBG1(DBG_NET, "ignoring IKE_SA setup from %H, half open IKE_SA "
+                        "count of %d exceeds limit of %d", message->get_source(message),
+                        half_open, this->init_limit_half_open);
+               return TRUE;
+       }
+
+       /* check if job load acceptable */
+       if (this->init_limit_job_load)
+       {
+               u_int jobs = 0, i;
+
+               for (i = 0; i < JOB_PRIO_MAX; i++)
+               {
+                       jobs += lib->processor->get_job_load(lib->processor, i);
+               }
+               if (jobs > this->init_limit_job_load)
+               {
+                       DBG1(DBG_NET, "ignoring IKE_SA setup from %H, job load of %d "
+                                "exceeds limit of %d", message->get_source(message),
+                                jobs, this->init_limit_job_load);
+                       return TRUE;
+               }
+       }
        return FALSE;
 }
 
@@ -274,9 +344,17 @@ static job_requeue_t receive_packets(private_receiver_t *this)
 {
        packet_t *packet;
        message_t *message;
+       status_t status;
 
        /* read in a packet */
-       if (charon->socket->receive(charon->socket, &packet) != SUCCESS)
+       status = charon->socket->receive(charon->socket, &packet);
+       if (status == NOT_SUPPORTED)
+       {
+               /* the processor destroys this job  */
+               this->job = NULL;
+               return JOB_REQUEUE_NONE;
+       }
+       else if (status != SUCCESS)
        {
                DBG2(DBG_NET, "receiving from socket failed!");
                return JOB_REQUEUE_FAIR;
@@ -306,39 +384,8 @@ static job_requeue_t receive_packets(private_receiver_t *this)
        if (message->get_request(message) &&
                message->get_exchange_type(message) == IKE_SA_INIT)
        {
-               /* check for cookies */
-               if (this->cookie_threshold && cookie_required(this, message))
-               {
-                       u_int32_t now = time_monotonic(NULL);
-                       chunk_t cookie = cookie_build(this, message, now - this->secret_offset,
-                                                                                 chunk_from_thing(this->secret));
-
-                       DBG2(DBG_NET, "received packet from: %#H to %#H",
-                                message->get_source(message),
-                                message->get_destination(message));
-                       DBG2(DBG_NET, "sending COOKIE notify to %H",
-                                message->get_source(message));
-                       send_notify(message, COOKIE, cookie);
-                       chunk_free(&cookie);
-                       if (++this->secret_used > COOKIE_REUSE)
-                       {
-                               /* create new cookie */
-                               DBG1(DBG_NET, "generating new cookie secret after %d uses",
-                                        this->secret_used);
-                               memcpy(this->secret_old, this->secret, SECRET_LENGTH);
-                               this->rng->get_bytes(this->rng, SECRET_LENGTH, this->secret);
-                               this->secret_switch = now;
-                               this->secret_used = 0;
-                       }
-                       message->destroy(message);
-                       return JOB_REQUEUE_DIRECT;
-               }
-
-               /* check if peer has not too many IKE_SAs half open */
-               if (this->block_threshold && peer_too_aggressive(this, message))
+               if (drop_ike_sa_init(this, message))
                {
-                       DBG1(DBG_NET, "ignoring IKE_SA setup from %H, "
-                                "peer too aggressive", message->get_source(message));
                        message->destroy(message);
                        return JOB_REQUEUE_DIRECT;
                }
@@ -353,22 +400,25 @@ static job_requeue_t receive_packets(private_receiver_t *this)
                        {
                                DBG1(DBG_NET, "using receive delay: %dms",
                                         this->receive_delay);
-                               charon->scheduler->schedule_job_ms(charon->scheduler,
+                               lib->scheduler->schedule_job_ms(lib->scheduler,
                                                                (job_t*)process_message_job_create(message),
                                                                this->receive_delay);
                                return JOB_REQUEUE_DIRECT;
                        }
                }
        }
-       charon->processor->queue_job(charon->processor,
-                                                                (job_t*)process_message_job_create(message));
+       lib->processor->queue_job(lib->processor,
+                                                         (job_t*)process_message_job_create(message));
        return JOB_REQUEUE_DIRECT;
 }
 
 METHOD(receiver_t, destroy, void,
        private_receiver_t *this)
 {
-       this->job->cancel(this->job);
+       if (this->job)
+       {
+               this->job->cancel(this->job);
+       }
        this->rng->destroy(this->rng);
        this->hasher->destroy(this->hasher);
        free(this);
@@ -383,7 +433,9 @@ receiver_t *receiver_create()
        u_int32_t now = time_monotonic(NULL);
 
        INIT(this,
-               .public.destroy = _destroy,
+               .public = {
+                       .destroy = _destroy,
+               },
                .secret_switch = now,
                .secret_offset = random() % now,
        );
@@ -395,6 +447,10 @@ receiver_t *receiver_create()
                this->block_threshold = lib->settings->get_int(lib->settings,
                                                "charon.block_threshold", BLOCK_THRESHOLD_DEFAULT);
        }
+       this->init_limit_job_load = lib->settings->get_int(lib->settings,
+                                               "charon.init_limit_job_load", 0);
+       this->init_limit_half_open = lib->settings->get_int(lib->settings,
+                                               "charon.init_limit_half_open", 0);
        this->receive_delay = lib->settings->get_int(lib->settings,
                                                "charon.receive_delay", 0);
        this->receive_delay_type = lib->settings->get_int(lib->settings,
@@ -422,9 +478,9 @@ receiver_t *receiver_create()
        this->rng->get_bytes(this->rng, SECRET_LENGTH, this->secret);
        memcpy(this->secret_old, this->secret, SECRET_LENGTH);
 
-       this->job = callback_job_create((callback_job_cb_t)receive_packets,
-                                                                       this, NULL, NULL);
-       charon->processor->queue_job(charon->processor, (job_t*)this->job);
+       this->job = callback_job_create_with_prio((callback_job_cb_t)receive_packets,
+                                                                               this, NULL, NULL, JOB_PRIO_CRITICAL);
+       lib->processor->queue_job(lib->processor, (job_t*)this->job);
 
        return &this->public;
 }