Besides being clearer, this fixes issues with GCC 15. The latter uses C23 by default, which changes the meaning of function declarations without parameters such as bool return false(); Instead of "this function takes an unknown number of arguments", this now equals (void), that is, "this function takes no arguments". So we run into incompatible pointer type warnings all over when using such functions. They could be cast to (void*) but this seems the cleaner solution for this use case.
744 lines
20 KiB
C
744 lines
20 KiB
C
/*
|
|
* Copyright (C) 2008-2012 Tobias Brunner
|
|
* Copyright (C) 2005-2006 Martin Willi
|
|
* Copyright (C) 2005 Jan Hutter
|
|
*
|
|
* Copyright (C) secunet Security Networks 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 <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "receiver.h"
|
|
|
|
#include <daemon.h>
|
|
#include <network/socket.h>
|
|
#include <processing/jobs/job.h>
|
|
#include <processing/jobs/process_message_job.h>
|
|
#include <processing/jobs/callback_job.h>
|
|
#include <crypto/hashers/hasher.h>
|
|
#include <threading/mutex.h>
|
|
#include <networking/packet.h>
|
|
|
|
/** lifetime of a cookie, in seconds */
|
|
#define COOKIE_LIFETIME 10
|
|
/** time we wait before disabling cookies */
|
|
#define COOKIE_CALMDOWN_DELAY 10
|
|
/** number of per-IP timestamps we keep track of (must be a power of 2) */
|
|
#define COOKIE_CALMDOWN_BUCKETS 32
|
|
/** mask applied to IP address hashes to determine the timestamp index */
|
|
#define COOKIE_CALMDOWN_MASK (COOKIE_CALMDOWN_BUCKETS-1)
|
|
/** time in seconds we use a secret at most (since we keep two secrets, must
|
|
* be at least COOKIE_LIFETIME to process all outstanding valid cookies) */
|
|
#define COOKIE_SECRET_SWITCH 120
|
|
/** default value for private_receiver_t.cookie_threshold */
|
|
#define COOKIE_THRESHOLD_DEFAULT 30
|
|
/** default value for private_receiver_t.cookie_threshold_ip (must be lower
|
|
* than BLOCK_THRESHOLD_DEFAULT) */
|
|
#define COOKIE_THRESHOLD_IP_DEFAULT 3
|
|
/** default value for private_receiver_t.block_threshold */
|
|
#define BLOCK_THRESHOLD_DEFAULT 5
|
|
/** length of the secret to use for cookie calculation */
|
|
#define SECRET_LENGTH 16
|
|
/** Length of a notify payload header */
|
|
#define NOTIFY_PAYLOAD_HEADER_LENGTH 8
|
|
|
|
typedef struct private_receiver_t private_receiver_t;
|
|
|
|
/**
|
|
* Private data of a receiver_t object.
|
|
*/
|
|
struct private_receiver_t {
|
|
/**
|
|
* Public part of a receiver_t object.
|
|
*/
|
|
receiver_t public;
|
|
|
|
/**
|
|
* Registered callback for ESP packets
|
|
*/
|
|
struct {
|
|
receiver_esp_cb_t cb;
|
|
void *data;
|
|
} esp_cb;
|
|
|
|
/**
|
|
* Mutex for ESP callback
|
|
*/
|
|
mutex_t *esp_cb_mutex;
|
|
|
|
/**
|
|
* current secret to use for cookie calculation
|
|
*/
|
|
char secret[SECRET_LENGTH];
|
|
|
|
/**
|
|
* previous secret used to verify older cookies
|
|
*/
|
|
char secret_old[SECRET_LENGTH];
|
|
|
|
/**
|
|
* time we used the current secret first
|
|
*/
|
|
uint32_t secret_first_use;
|
|
|
|
/**
|
|
* time offset to use, hides our system time
|
|
*/
|
|
uint32_t secret_offset;
|
|
|
|
/**
|
|
* the RNG to use for secret generation
|
|
*/
|
|
rng_t *rng;
|
|
|
|
/**
|
|
* hasher to use for cookie calculation
|
|
*/
|
|
hasher_t *hasher;
|
|
|
|
/**
|
|
* require cookies after this many half open IKE_SAs
|
|
*/
|
|
u_int cookie_threshold;
|
|
|
|
/**
|
|
* require cookies for a specific IP after this many half open IKE_SAs
|
|
*/
|
|
u_int cookie_threshold_ip;
|
|
|
|
/**
|
|
* global (0) and per-IP hash (1..n) timestamps of when the threshold was
|
|
* last reached
|
|
*/
|
|
time_t last_threshold[COOKIE_CALMDOWN_BUCKETS+1];
|
|
|
|
/**
|
|
* how many half open IKE_SAs per peer before blocking
|
|
*/
|
|
u_int 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;
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* Endpoint is allowed to act as an initiator only
|
|
*/
|
|
bool initiator_only;
|
|
|
|
};
|
|
|
|
/**
|
|
* send a notify back to the sender
|
|
*/
|
|
static void send_notify(message_t *request, int major, exchange_type_t exchange,
|
|
notify_type_t type, chunk_t data)
|
|
{
|
|
ike_sa_id_t *ike_sa_id;
|
|
message_t *response;
|
|
host_t *src, *dst;
|
|
packet_t *packet;
|
|
|
|
response = message_create(major, 0);
|
|
response->set_exchange_type(response, exchange);
|
|
response->add_notify(response, FALSE, type, data);
|
|
dst = request->get_source(request);
|
|
src = request->get_destination(request);
|
|
response->set_source(response, src->clone(src));
|
|
response->set_destination(response, dst->clone(dst));
|
|
if (major == IKEV2_MAJOR_VERSION)
|
|
{
|
|
response->set_request(response, FALSE);
|
|
response->set_message_id(response, request->get_message_id(request));
|
|
}
|
|
ike_sa_id = request->get_ike_sa_id(request);
|
|
ike_sa_id->switch_initiator(ike_sa_id);
|
|
response->set_ike_sa_id(response, ike_sa_id);
|
|
if (response->generate(response, NULL, &packet) == SUCCESS)
|
|
{
|
|
charon->sender->send(charon->sender, packet);
|
|
}
|
|
response->destroy(response);
|
|
}
|
|
|
|
/**
|
|
* build a cookie
|
|
*/
|
|
static bool cookie_build(private_receiver_t *this, message_t *message,
|
|
uint32_t t, chunk_t secret, chunk_t *cookie)
|
|
{
|
|
uint64_t spi = message->get_initiator_spi(message);
|
|
host_t *ip = message->get_source(message);
|
|
chunk_t input, hash;
|
|
|
|
/* COOKIE = t | sha1( IPi | SPIi | t | secret ) */
|
|
input = chunk_cata("cccc", ip->get_address(ip), chunk_from_thing(spi),
|
|
chunk_from_thing(t), secret);
|
|
hash = chunk_alloca(this->hasher->get_hash_size(this->hasher));
|
|
if (!this->hasher->get_hash(this->hasher, input, hash.ptr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
*cookie = chunk_cat("cc", chunk_from_thing(t), hash);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* verify a received cookie
|
|
*/
|
|
static bool cookie_verify(private_receiver_t *this, message_t *message,
|
|
chunk_t cookie)
|
|
{
|
|
uint32_t t, now;
|
|
chunk_t reference;
|
|
chunk_t secret;
|
|
|
|
now = time_monotonic(NULL);
|
|
t = *(uint32_t*)cookie.ptr;
|
|
|
|
if (cookie.len != sizeof(uint32_t) +
|
|
this->hasher->get_hash_size(this->hasher) ||
|
|
t < now - this->secret_offset - COOKIE_LIFETIME)
|
|
{
|
|
DBG2(DBG_NET, "received cookie lifetime expired, rejecting");
|
|
return FALSE;
|
|
}
|
|
|
|
/* check if cookie is derived from old_secret */
|
|
if (t + this->secret_offset >= this->secret_first_use)
|
|
{
|
|
secret = chunk_from_thing(this->secret);
|
|
}
|
|
else
|
|
{
|
|
secret = chunk_from_thing(this->secret_old);
|
|
}
|
|
|
|
/* compare own calculation against received */
|
|
if (!cookie_build(this, message, t, secret, &reference))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (chunk_equals_const(reference, cookie))
|
|
{
|
|
chunk_free(&reference);
|
|
return TRUE;
|
|
}
|
|
chunk_free(&reference);
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Check if a valid cookie found
|
|
*/
|
|
static bool check_cookie(private_receiver_t *this, message_t *message)
|
|
{
|
|
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. */
|
|
data = message->get_packet_data(message);
|
|
if (data.len <
|
|
IKE_HEADER_LENGTH + NOTIFY_PAYLOAD_HEADER_LENGTH +
|
|
sizeof(uint32_t) + this->hasher->get_hash_size(this->hasher) ||
|
|
*(data.ptr + 16) != PLV2_NOTIFY ||
|
|
*(uint16_t*)(data.ptr + IKE_HEADER_LENGTH + 6) != htons(COOKIE))
|
|
{
|
|
/* no cookie found */
|
|
return FALSE;
|
|
}
|
|
data.ptr += IKE_HEADER_LENGTH + NOTIFY_PAYLOAD_HEADER_LENGTH;
|
|
data.len = sizeof(uint32_t) + this->hasher->get_hash_size(this->hasher);
|
|
if (!cookie_verify(this, message, data))
|
|
{
|
|
DBG2(DBG_NET, "found cookie, but content invalid");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Struct to keep track of half-open SA counts
|
|
*/
|
|
typedef struct {
|
|
u_int count;
|
|
bool determined;
|
|
} half_open_count_t;
|
|
|
|
/**
|
|
* Get the number of half-open SAs, cached or from the manager, either global
|
|
* or for a single IP.
|
|
*/
|
|
static u_int get_half_open_count(half_open_count_t *this, host_t *src)
|
|
{
|
|
if (!this->determined)
|
|
{
|
|
this->determined = TRUE;
|
|
this->count = charon->ike_sa_manager->get_half_open_count(
|
|
charon->ike_sa_manager, src, TRUE);
|
|
}
|
|
return this->count;
|
|
}
|
|
|
|
/**
|
|
* Check if we currently require cookies either globally or for the given IP
|
|
*/
|
|
static bool cookie_required(private_receiver_t *this,
|
|
half_open_count_t *half_open, host_t *src,
|
|
uint32_t now)
|
|
{
|
|
u_int threshold = src ? this->cookie_threshold_ip : this->cookie_threshold;
|
|
u_int idx = 0;
|
|
|
|
if (!threshold)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (src)
|
|
{
|
|
/* keep track of IPs in segments so not all are affected if a single
|
|
* IP is targeted */
|
|
idx = 1 + (chunk_hash(src->get_address(src)) & COOKIE_CALMDOWN_MASK);
|
|
}
|
|
if (get_half_open_count(half_open, src) >= threshold)
|
|
{
|
|
this->last_threshold[idx] = now;
|
|
return TRUE;
|
|
}
|
|
if (this->last_threshold[idx] &&
|
|
now < this->last_threshold[idx] + COOKIE_CALMDOWN_DELAY)
|
|
{
|
|
/* We don't disable cookies unless the threshold was not reached for
|
|
* COOKIE_CALMDOWN_DELAY seconds. This avoids jittering between
|
|
* cookie on / cookie off states, which is problematic. Consider the
|
|
* following: A legitimate initiator sends an 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 the AUTH payload data.
|
|
*/
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Check if we should drop IKE_SA_INIT because of cookie/overload checking
|
|
*/
|
|
static bool drop_ike_sa_init(private_receiver_t *this, message_t *message)
|
|
{
|
|
half_open_count_t half_open = {}, half_open_ip = {};
|
|
host_t *src;
|
|
uint32_t now;
|
|
|
|
src = message->get_source(message);
|
|
now = time_monotonic(NULL);
|
|
|
|
/* check for cookies in IKEv2 */
|
|
if (message->get_major_version(message) == IKEV2_MAJOR_VERSION &&
|
|
(cookie_required(this, &half_open, NULL, now) ||
|
|
cookie_required(this, &half_open_ip, src, now)) &&
|
|
!check_cookie(this, message))
|
|
{
|
|
chunk_t cookie;
|
|
|
|
DBG2(DBG_NET, "received packet: from %#H to %#H (%zu bytes)",
|
|
src, message->get_destination(message),
|
|
message->get_packet_data(message).len);
|
|
|
|
if (!this->secret_first_use)
|
|
{
|
|
this->secret_first_use = now;
|
|
}
|
|
else if (now - this->secret_first_use > COOKIE_SECRET_SWITCH)
|
|
{
|
|
char secret[SECRET_LENGTH];
|
|
|
|
DBG1(DBG_NET, "generating new cookie secret after %ds since first "
|
|
"use", now - this->secret_first_use);
|
|
if (this->rng->get_bytes(this->rng, SECRET_LENGTH, secret))
|
|
{
|
|
memcpy(this->secret_old, this->secret, SECRET_LENGTH);
|
|
memcpy(this->secret, secret, SECRET_LENGTH);
|
|
memwipe(secret, SECRET_LENGTH);
|
|
this->secret_first_use = now;
|
|
}
|
|
else
|
|
{
|
|
DBG1(DBG_NET, "failed to allocated cookie secret, keeping old");
|
|
}
|
|
}
|
|
if (!cookie_build(this, message, now - this->secret_offset,
|
|
chunk_from_thing(this->secret), &cookie))
|
|
{
|
|
return TRUE;
|
|
}
|
|
DBG2(DBG_NET, "sending COOKIE notify to %H", src);
|
|
send_notify(message, IKEV2_MAJOR_VERSION, IKE_SA_INIT, COOKIE, cookie);
|
|
chunk_free(&cookie);
|
|
return TRUE;
|
|
}
|
|
|
|
/* check if peer has too many IKE_SAs half open */
|
|
if (this->block_threshold &&
|
|
get_half_open_count(&half_open_ip, src) >= this->block_threshold)
|
|
|
|
{
|
|
DBG1(DBG_NET, "ignoring IKE_SA setup from %H, per-IP half-open IKE_SA "
|
|
"limit of %d reached", src, this->block_threshold);
|
|
return TRUE;
|
|
}
|
|
|
|
/* check if global half open IKE_SA limit reached */
|
|
if (this->init_limit_half_open &&
|
|
get_half_open_count(&half_open, NULL) >= this->init_limit_half_open)
|
|
{
|
|
DBG1(DBG_NET, "ignoring IKE_SA setup from %H, half-open IKE_SA "
|
|
"limit of %d reached", src, 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", src, jobs, this->init_limit_job_load);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Job callback to receive packets
|
|
*/
|
|
static job_requeue_t receive_packets(private_receiver_t *this)
|
|
{
|
|
ike_sa_id_t *id;
|
|
packet_t *packet;
|
|
message_t *message;
|
|
host_t *src, *dst;
|
|
status_t status;
|
|
bool supported = TRUE;
|
|
chunk_t data, marker = chunk_from_chars(0x00, 0x00, 0x00, 0x00);
|
|
|
|
/* read in a packet */
|
|
status = charon->socket->receive(charon->socket, &packet);
|
|
if (status == NOT_SUPPORTED)
|
|
{
|
|
return JOB_REQUEUE_NONE;
|
|
}
|
|
else if (status != SUCCESS)
|
|
{
|
|
DBG2(DBG_NET, "receiving from socket failed!");
|
|
return JOB_REQUEUE_FAIR;
|
|
}
|
|
|
|
data = packet->get_data(packet);
|
|
if (data.len == 1 && data.ptr[0] == 0xFF)
|
|
{ /* silently drop NAT-T keepalives */
|
|
packet->destroy(packet);
|
|
return JOB_REQUEUE_DIRECT;
|
|
}
|
|
else if (data.len < marker.len)
|
|
{ /* drop packets that are too small */
|
|
DBG3(DBG_NET, "received packet is too short (%d bytes)", data.len);
|
|
packet->destroy(packet);
|
|
return JOB_REQUEUE_DIRECT;
|
|
}
|
|
|
|
dst = packet->get_destination(packet);
|
|
src = packet->get_source(packet);
|
|
if (!charon->kernel->all_interfaces_usable(charon->kernel)
|
|
&& !charon->kernel->get_interface(charon->kernel, dst, NULL))
|
|
{
|
|
DBG3(DBG_NET, "received packet from %#H to %#H on ignored interface",
|
|
src, dst);
|
|
packet->destroy(packet);
|
|
return JOB_REQUEUE_DIRECT;
|
|
}
|
|
|
|
/* if neither source nor destination port is 500 we assume an IKE packet
|
|
* with Non-ESP marker or an ESP packet */
|
|
if (dst->get_port(dst) != IKEV2_UDP_PORT &&
|
|
src->get_port(src) != IKEV2_UDP_PORT)
|
|
{
|
|
if (memeq(data.ptr, marker.ptr, marker.len))
|
|
{ /* remove Non-ESP marker */
|
|
packet->skip_bytes(packet, marker.len);
|
|
}
|
|
else
|
|
{ /* this seems to be an ESP packet */
|
|
this->esp_cb_mutex->lock(this->esp_cb_mutex);
|
|
if (this->esp_cb.cb)
|
|
{
|
|
this->esp_cb.cb(this->esp_cb.data, packet);
|
|
}
|
|
else
|
|
{
|
|
packet->destroy(packet);
|
|
}
|
|
this->esp_cb_mutex->unlock(this->esp_cb_mutex);
|
|
return JOB_REQUEUE_DIRECT;
|
|
}
|
|
}
|
|
|
|
/* parse message header */
|
|
message = message_create_from_packet(packet);
|
|
if (message->parse_header(message) != SUCCESS)
|
|
{
|
|
DBG1(DBG_NET, "received invalid IKE header from %H - ignored", src);
|
|
charon->bus->alert(charon->bus, ALERT_PARSE_ERROR_HEADER, message);
|
|
message->destroy(message);
|
|
return JOB_REQUEUE_DIRECT;
|
|
}
|
|
|
|
/* check IKE major version */
|
|
switch (message->get_major_version(message))
|
|
{
|
|
case IKEV2_MAJOR_VERSION:
|
|
#ifndef USE_IKEV2
|
|
if (message->get_exchange_type(message) == IKE_SA_INIT &&
|
|
message->get_request(message))
|
|
{
|
|
send_notify(message, IKEV1_MAJOR_VERSION, INFORMATIONAL_V1,
|
|
INVALID_MAJOR_VERSION, chunk_empty);
|
|
supported = FALSE;
|
|
}
|
|
#endif /* USE_IKEV2 */
|
|
break;
|
|
case IKEV1_MAJOR_VERSION:
|
|
#ifndef USE_IKEV1
|
|
if (message->get_exchange_type(message) == ID_PROT ||
|
|
message->get_exchange_type(message) == AGGRESSIVE)
|
|
{
|
|
send_notify(message, IKEV2_MAJOR_VERSION, INFORMATIONAL,
|
|
INVALID_MAJOR_VERSION, chunk_empty);
|
|
supported = FALSE;
|
|
}
|
|
#endif /* USE_IKEV1 */
|
|
break;
|
|
default:
|
|
#ifdef USE_IKEV2
|
|
send_notify(message, IKEV2_MAJOR_VERSION,
|
|
message->get_exchange_type(message),
|
|
INVALID_MAJOR_VERSION, chunk_empty);
|
|
#elif defined(USE_IKEV1)
|
|
send_notify(message, IKEV1_MAJOR_VERSION, INFORMATIONAL_V1,
|
|
INVALID_MAJOR_VERSION, chunk_empty);
|
|
#endif /* USE_IKEV1 */
|
|
supported = FALSE;
|
|
break;
|
|
}
|
|
if (!supported)
|
|
{
|
|
DBG1(DBG_NET, "received unsupported IKE version %d.%d from %H, sending "
|
|
"INVALID_MAJOR_VERSION", message->get_major_version(message),
|
|
message->get_minor_version(message), src);
|
|
message->destroy(message);
|
|
return JOB_REQUEUE_DIRECT;
|
|
}
|
|
if (message->get_request(message) &&
|
|
message->get_exchange_type(message) == IKE_SA_INIT)
|
|
{
|
|
id = message->get_ike_sa_id(message);
|
|
if (this->initiator_only || !id->is_initiator(id) ||
|
|
drop_ike_sa_init(this, message))
|
|
{
|
|
message->destroy(message);
|
|
return JOB_REQUEUE_DIRECT;
|
|
}
|
|
}
|
|
if (message->get_exchange_type(message) == ID_PROT ||
|
|
message->get_exchange_type(message) == AGGRESSIVE)
|
|
{
|
|
id = message->get_ike_sa_id(message);
|
|
if (id->get_responder_spi(id) == 0 &&
|
|
(this->initiator_only || drop_ike_sa_init(this, message)))
|
|
{
|
|
message->destroy(message);
|
|
return JOB_REQUEUE_DIRECT;
|
|
}
|
|
}
|
|
|
|
if (this->receive_delay)
|
|
{
|
|
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, add_esp_cb, void,
|
|
private_receiver_t *this, receiver_esp_cb_t callback, void *data)
|
|
{
|
|
this->esp_cb_mutex->lock(this->esp_cb_mutex);
|
|
this->esp_cb.cb = callback;
|
|
this->esp_cb.data = data;
|
|
this->esp_cb_mutex->unlock(this->esp_cb_mutex);
|
|
}
|
|
|
|
METHOD(receiver_t, del_esp_cb, void,
|
|
private_receiver_t *this, receiver_esp_cb_t callback)
|
|
{
|
|
this->esp_cb_mutex->lock(this->esp_cb_mutex);
|
|
if (this->esp_cb.cb == callback)
|
|
{
|
|
this->esp_cb.cb = NULL;
|
|
this->esp_cb.data = NULL;
|
|
}
|
|
this->esp_cb_mutex->unlock(this->esp_cb_mutex);
|
|
}
|
|
|
|
METHOD(receiver_t, destroy, void,
|
|
private_receiver_t *this)
|
|
{
|
|
DESTROY_IF(this->rng);
|
|
DESTROY_IF(this->hasher);
|
|
this->esp_cb_mutex->destroy(this->esp_cb_mutex);
|
|
free(this);
|
|
}
|
|
|
|
/*
|
|
* Described in header.
|
|
*/
|
|
receiver_t *receiver_create()
|
|
{
|
|
private_receiver_t *this;
|
|
uint32_t now = time_monotonic(NULL);
|
|
|
|
INIT(this,
|
|
.public = {
|
|
.add_esp_cb = _add_esp_cb,
|
|
.del_esp_cb = _del_esp_cb,
|
|
.destroy = _destroy,
|
|
},
|
|
.esp_cb_mutex = mutex_create(MUTEX_TYPE_DEFAULT),
|
|
.secret_offset = now ? random() % now : 0,
|
|
);
|
|
|
|
if (lib->settings->get_bool(lib->settings,
|
|
"%s.dos_protection", TRUE, lib->ns))
|
|
{
|
|
this->cookie_threshold = lib->settings->get_int(lib->settings,
|
|
"%s.cookie_threshold", COOKIE_THRESHOLD_DEFAULT, lib->ns);
|
|
this->cookie_threshold_ip = lib->settings->get_int(lib->settings,
|
|
"%s.cookie_threshold_ip", COOKIE_THRESHOLD_IP_DEFAULT, lib->ns);
|
|
this->block_threshold = lib->settings->get_int(lib->settings,
|
|
"%s.block_threshold", BLOCK_THRESHOLD_DEFAULT, lib->ns);
|
|
|
|
if (this->cookie_threshold_ip >= this->block_threshold)
|
|
{
|
|
this->block_threshold = this->cookie_threshold_ip + 1;
|
|
DBG1(DBG_NET, "increasing block threshold to %u due to per-IP "
|
|
"cookie threshold of %u", this->block_threshold,
|
|
this->cookie_threshold_ip);
|
|
}
|
|
}
|
|
this->init_limit_job_load = lib->settings->get_int(lib->settings,
|
|
"%s.init_limit_job_load", 0, lib->ns);
|
|
this->init_limit_half_open = lib->settings->get_int(lib->settings,
|
|
"%s.init_limit_half_open", 0, lib->ns);
|
|
this->receive_delay = lib->settings->get_int(lib->settings,
|
|
"%s.receive_delay", 0, lib->ns);
|
|
this->receive_delay_type = lib->settings->get_int(lib->settings,
|
|
"%s.receive_delay_type", 0, lib->ns);
|
|
this->receive_delay_request = lib->settings->get_bool(lib->settings,
|
|
"%s.receive_delay_request", TRUE, lib->ns);
|
|
this->receive_delay_response = lib->settings->get_bool(lib->settings,
|
|
"%s.receive_delay_response", TRUE, lib->ns);
|
|
this->initiator_only = lib->settings->get_bool(lib->settings,
|
|
"%s.initiator_only", FALSE, lib->ns);
|
|
|
|
this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
|
|
if (!this->hasher)
|
|
{
|
|
DBG1(DBG_NET, "creating cookie hasher failed, no hashers supported");
|
|
destroy(this);
|
|
return NULL;
|
|
}
|
|
this->rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG);
|
|
if (!this->rng)
|
|
{
|
|
DBG1(DBG_NET, "creating cookie RNG failed, no RNG supported");
|
|
destroy(this);
|
|
return NULL;
|
|
}
|
|
if (!this->rng->get_bytes(this->rng, SECRET_LENGTH, this->secret))
|
|
{
|
|
DBG1(DBG_NET, "creating cookie secret failed");
|
|
destroy(this);
|
|
return NULL;
|
|
}
|
|
memcpy(this->secret_old, this->secret, SECRET_LENGTH);
|
|
|
|
lib->processor->queue_job(lib->processor,
|
|
(job_t*)callback_job_create_with_prio((callback_job_cb_t)receive_packets,
|
|
this, NULL, callback_job_cancel_thread, JOB_PRIO_CRITICAL));
|
|
|
|
return &this->public;
|
|
}
|