summaryrefslogtreecommitdiff
path: root/src/libcharon/network
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@corsac.net>2012-06-28 21:16:07 +0200
committerYves-Alexis Perez <corsac@corsac.net>2012-06-28 21:16:07 +0200
commitb34738ed08c2227300d554b139e2495ca5da97d6 (patch)
tree62f33b52820f2e49f0e53c0f8c636312037c8054 /src/libcharon/network
parent0a9d51a49042a68daa15b0c74a2b7f152f52606b (diff)
downloadvyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.tar.gz
vyos-strongswan-b34738ed08c2227300d554b139e2495ca5da97d6.zip
Imported Upstream version 4.6.4
Diffstat (limited to 'src/libcharon/network')
-rw-r--r--src/libcharon/network/receiver.c221
-rw-r--r--src/libcharon/network/receiver.h4
-rw-r--r--src/libcharon/network/sender.c4
-rw-r--r--src/libcharon/network/socket.c36
-rw-r--r--src/libcharon/network/socket.h15
5 files changed, 206 insertions, 74 deletions
diff --git a/src/libcharon/network/receiver.c b/src/libcharon/network/receiver.c
index d8cebe192..cfb1408ef 100644
--- a/src/libcharon/network/receiver.c
+++ b/src/libcharon/network/receiver.c
@@ -30,6 +30,8 @@
/** lifetime of a cookie, in seconds */
#define COOKIE_LIFETIME 10
+/** time we wait before disabling cookies */
+#define COOKIE_CALMDOWN_DELAY 10
/** how many times to reuse the secret */
#define COOKIE_REUSE 10000
/** default value for private_receiver_t.cookie_threshold */
@@ -96,11 +98,26 @@ struct private_receiver_t {
u_int32_t cookie_threshold;
/**
+ * timestamp of last cookie requested
+ */
+ time_t last_cookie;
+
+ /**
* how many half open IKE_SAs per peer before blocking
*/
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;
@@ -215,55 +232,146 @@ 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 TRUE;
+}
+
+/**
+ * Check if we currently require cookies
+ */
+static bool cookie_required(private_receiver_t *this,
+ u_int half_open, u_int32_t now)
+{
+ if (this->cookie_threshold && half_open >= this->cookie_threshold)
+ {
+ this->last_cookie = now;
+ return TRUE;
+ }
+ if (now < this->last_cookie + COOKIE_CALMDOWN_DELAY)
+ {
+ /* We don't disable cookies unless we haven't seen IKE_SA_INITs
+ * for COOKIE_CALMDOWN_DELAY seconds. This avoids jittering between
+ * cookie on / cookie off states, which is problematic. Consider the
+ * following: A legitimiate initiator sends a IKE_SA_INIT while we
+ * are under a DoS attack. If we toggle our cookie behavior,
+ * multiple retransmits of this IKE_SA_INIT might get answered with
+ * and without cookies. The initiator goes on and retries with
+ * a cookie, but it can't know if the completing IKE_SA_INIT response
+ * is to its IKE_SA_INIT request with or without cookies. This is
+ * problematic, as the cookie is part of AUTH payload data.
+ */
+ this->last_cookie = now;
+ return TRUE;
}
- return failed;
+ return FALSE;
}
/**
- * 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;
+ u_int32_t now;
+
+ now = time_monotonic(NULL);
+ half_open = charon->ike_sa_manager->get_half_open_count(
+ charon->ike_sa_manager, NULL);
+
+ /* check for cookies */
+ if (cookie_required(this, half_open, now) && !check_cookie(this, message))
+ {
+ chunk_t cookie;
+
+ 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;
}
@@ -314,39 +422,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;
}
@@ -408,6 +485,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,
@@ -435,8 +516,8 @@ 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);
+ 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;
diff --git a/src/libcharon/network/receiver.h b/src/libcharon/network/receiver.h
index 690d8dbab..1d9d4871e 100644
--- a/src/libcharon/network/receiver.h
+++ b/src/libcharon/network/receiver.h
@@ -30,7 +30,7 @@ typedef struct receiver_t receiver_t;
/**
* Receives packets from the socket and adds them to the job queue.
*
- * The receiver starts a thread, wich reads on the blocking socket. A received
+ * The receiver starts a thread, which reads on the blocking socket. A received
* packet is preparsed and a process_message_job is queued in the job queue.
*
* To endure DoS attacks, cookies are enabled when to many IKE_SAs are half
@@ -38,7 +38,7 @@ typedef struct receiver_t receiver_t;
* method in RFC4306. We do not include a nonce, because we think the advantage
* we gain does not justify the overhead to parse the whole message.
* Instead of VersionIdOfSecret, we include a timestamp. This allows us to
- * find out wich key was used for cookie creation. Further, we can set a
+ * find out which key was used for cookie creation. Further, we can set a
* lifetime for the cookie, which allows us to reuse the secret for a longer
* time.
* COOKIE = time | sha1( IPi | SPIi | time | secret )
diff --git a/src/libcharon/network/sender.c b/src/libcharon/network/sender.c
index 4177fb3e1..4df930b15 100644
--- a/src/libcharon/network/sender.c
+++ b/src/libcharon/network/sender.c
@@ -183,8 +183,8 @@ sender_t * sender_create()
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.got = condvar_create(CONDVAR_TYPE_DEFAULT),
.sent = condvar_create(CONDVAR_TYPE_DEFAULT),
- .job = callback_job_create((callback_job_cb_t)send_packets,
- this, NULL, NULL),
+ .job = callback_job_create_with_prio((callback_job_cb_t)send_packets,
+ this, NULL, NULL, JOB_PRIO_CRITICAL),
.send_delay = lib->settings->get_int(lib->settings,
"charon.send_delay", 0),
.send_delay_type = lib->settings->get_int(lib->settings,
diff --git a/src/libcharon/network/socket.c b/src/libcharon/network/socket.c
new file mode 100644
index 000000000..cdb14b4a0
--- /dev/null
+++ b/src/libcharon/network/socket.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 Martin Willi
+ * Copyright (C) 2011 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "socket.h"
+
+#include <daemon.h>
+
+/**
+ * See header
+ */
+bool socket_register(plugin_t *plugin, plugin_feature_t *feature,
+ bool reg, void *data)
+{
+ if (reg)
+ {
+ charon->socket->add_socket(charon->socket, (socket_constructor_t)data);
+ }
+ else
+ {
+ charon->socket->remove_socket(charon->socket,
+ (socket_constructor_t)data);
+ }
+ return TRUE;
+}
diff --git a/src/libcharon/network/socket.h b/src/libcharon/network/socket.h
index 51b26920f..be875035b 100644
--- a/src/libcharon/network/socket.h
+++ b/src/libcharon/network/socket.h
@@ -29,6 +29,7 @@ typedef struct socket_t socket_t;
#include <library.h>
#include <network/packet.h>
#include <utils/enumerator.h>
+#include <plugins/plugin.h>
/**
* Constructor prototype for sockets.
@@ -72,4 +73,18 @@ struct socket_t {
void (*destroy) (socket_t *this);
};
+/**
+ * Helper function to (un-)register socket interfaces from plugin features.
+ *
+ * This function is a plugin_feature_callback_t and can be used with the
+ * PLUGIN_CALLBACK macro to register an socket interface constructor.
+ *
+ * @param plugin plugin registering the socket interface
+ * @param feature associated plugin feature
+ * @param reg TRUE to register, FALSE to unregister
+ * @param data data passed to callback, a socket_constructor_t
+ */
+bool socket_register(plugin_t *plugin, plugin_feature_t *feature,
+ bool reg, void *data);
+
#endif /** SOCKET_H_ @}*/