diff options
author | Yves-Alexis Perez <corsac@corsac.net> | 2012-06-28 21:16:07 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@corsac.net> | 2012-06-28 21:16:07 +0200 |
commit | b34738ed08c2227300d554b139e2495ca5da97d6 (patch) | |
tree | 62f33b52820f2e49f0e53c0f8c636312037c8054 /src/libcharon/network | |
parent | 0a9d51a49042a68daa15b0c74a2b7f152f52606b (diff) | |
download | vyos-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.c | 221 | ||||
-rw-r--r-- | src/libcharon/network/receiver.h | 4 | ||||
-rw-r--r-- | src/libcharon/network/sender.c | 4 | ||||
-rw-r--r-- | src/libcharon/network/socket.c | 36 | ||||
-rw-r--r-- | src/libcharon/network/socket.h | 15 |
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_ @}*/ |