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
*/
- u_int receive_delay;
+ int receive_delay;
+
+ /**
+ * Specific message type to delay, 0 for any
+ */
+ int receive_delay_type;
+
+ /**
+ * Delay request messages?
+ */
+ bool receive_delay_request;
+
+ /**
+ * Delay response messages?
+ */
+ bool receive_delay_response;
};
/**
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);
}
/**
- * 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_to_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;
}
{
packet_t *packet;
message_t *message;
- job_t *job;
+ 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;
if (message->get_request(message) &&
message->get_exchange_type(message) == IKE_SA_INIT)
{
- /* check for cookies */
- if (this->cookie_threshold && cookie_required(this, message))
+ if (drop_ike_sa_init(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_to_aggressive(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;
}
}
- job = (job_t*)process_message_job_create(message);
if (this->receive_delay)
{
- charon->scheduler->schedule_job_ms(charon->scheduler,
- job, this->receive_delay);
- }
- else
- {
- charon->processor->queue_job(charon->processor, job);
+ if (this->receive_delay_type == 0 ||
+ this->receive_delay_type == message->get_exchange_type(message))
+ {
+ if ((message->get_request(message) && this->receive_delay_request) ||
+ (!message->get_request(message) && this->receive_delay_response))
+ {
+ DBG1(DBG_NET, "using receive delay: %dms",
+ this->receive_delay);
+ lib->scheduler->schedule_job_ms(lib->scheduler,
+ (job_t*)process_message_job_create(message),
+ this->receive_delay);
+ return JOB_REQUEUE_DIRECT;
+ }
+ }
}
+ 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);
u_int32_t now = time_monotonic(NULL);
INIT(this,
- .public.destroy = _destroy,
+ .public = {
+ .destroy = _destroy,
+ },
.secret_switch = now,
.secret_offset = random() % now,
);
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,
+ "charon.receive_delay_type", 0),
+ this->receive_delay_request = lib->settings->get_bool(lib->settings,
+ "charon.receive_delay_request", TRUE),
+ this->receive_delay_response = lib->settings->get_int(lib->settings,
+ "charon.receive_delay_response", TRUE),
this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_PREFERRED);
if (this->hasher == NULL)
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;
}