2019-05-29 14:05:16 -04:00
|
|
|
/* Copyright (c) 2019, The Tor Project, Inc. */
|
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \file hs_dos.c
|
|
|
|
* \brief Implement denial of service mitigation for the onion service
|
|
|
|
* subsystem.
|
|
|
|
*
|
|
|
|
* This module defenses:
|
|
|
|
*
|
|
|
|
* - Introduction Rate Limiting: If enabled by the consensus, an introduction
|
|
|
|
* point will rate limit client introduction towards the service (INTRODUCE2
|
|
|
|
* cells). It uses a token bucket model with a rate and burst per second.
|
|
|
|
*
|
|
|
|
* Proposal 305 will expand this module by allowing an operator to define
|
|
|
|
* these values into the ESTABLISH_INTRO cell. Not yet implemented.
|
|
|
|
**/
|
|
|
|
|
|
|
|
#define HS_DOS_PRIVATE
|
|
|
|
|
2019-06-11 08:28:13 -04:00
|
|
|
#include "core/or/or.h"
|
|
|
|
#include "app/config/config.h"
|
|
|
|
|
2019-05-29 14:05:16 -04:00
|
|
|
#include "core/or/circuitlist.h"
|
|
|
|
|
2019-06-27 13:32:58 -04:00
|
|
|
#include "feature/hs/hs_circuitmap.h"
|
2019-06-11 08:28:13 -04:00
|
|
|
#include "feature/nodelist/networkstatus.h"
|
|
|
|
#include "feature/relay/routermode.h"
|
|
|
|
|
|
|
|
#include "lib/evloop/token_bucket.h"
|
|
|
|
|
2019-05-29 14:05:16 -04:00
|
|
|
#include "hs_dos.h"
|
|
|
|
|
2019-06-11 08:28:13 -04:00
|
|
|
/* Default value of the allowed INTRODUCE2 cell rate per second. Above that
|
|
|
|
* value per second, the introduction is denied. */
|
2019-07-02 07:36:36 -04:00
|
|
|
#define HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC 25
|
2019-06-11 08:28:13 -04:00
|
|
|
|
|
|
|
/* Default value of the allowed INTRODUCE2 cell burst per second. This is the
|
|
|
|
* maximum value a token bucket has per second. We thus allow up to this value
|
|
|
|
* of INTRODUCE2 cell per second but the bucket is refilled by the rate value
|
|
|
|
* but never goes above that burst value. */
|
2019-07-02 07:36:36 -04:00
|
|
|
#define HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC 200
|
2019-06-11 08:28:13 -04:00
|
|
|
|
2019-06-27 12:58:43 -04:00
|
|
|
/* Default value of the consensus parameter enabling or disabling the
|
|
|
|
* introduction DoS defense. Disabled by default. */
|
|
|
|
#define HS_DOS_INTRODUCE_ENABLED_DEFAULT 0
|
|
|
|
|
2019-06-11 08:28:13 -04:00
|
|
|
/* Consensus parameters. */
|
2019-08-12 12:32:38 -04:00
|
|
|
static uint32_t param_introduce_rate_per_sec =
|
2019-07-02 07:36:36 -04:00
|
|
|
HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC;
|
2019-08-12 12:32:38 -04:00
|
|
|
static uint32_t param_introduce_burst_per_sec =
|
2019-07-02 07:36:36 -04:00
|
|
|
HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC;
|
2019-08-12 12:32:38 -04:00
|
|
|
static uint32_t param_introduce_defense_enabled =
|
2019-06-27 12:58:43 -04:00
|
|
|
HS_DOS_INTRODUCE_ENABLED_DEFAULT;
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
get_param_intro_dos_enabled(const networkstatus_t *ns)
|
|
|
|
{
|
|
|
|
return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSDefense",
|
|
|
|
HS_DOS_INTRODUCE_ENABLED_DEFAULT, 0, 1);
|
|
|
|
}
|
2019-06-11 08:28:13 -04:00
|
|
|
|
|
|
|
/* Return the parameter for the introduction rate per sec. */
|
|
|
|
static uint32_t
|
|
|
|
get_param_rate_per_sec(const networkstatus_t *ns)
|
|
|
|
{
|
|
|
|
return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSRatePerSec",
|
2019-07-02 07:36:36 -04:00
|
|
|
HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC,
|
2019-06-11 08:28:13 -04:00
|
|
|
0, INT32_MAX);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the parameter for the introduction burst per sec. */
|
|
|
|
static uint32_t
|
|
|
|
get_param_burst_per_sec(const networkstatus_t *ns)
|
|
|
|
{
|
|
|
|
return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSBurstPerSec",
|
2019-07-02 07:36:36 -04:00
|
|
|
HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC,
|
2019-06-11 08:28:13 -04:00
|
|
|
0, INT32_MAX);
|
|
|
|
}
|
|
|
|
|
2019-06-27 13:32:58 -04:00
|
|
|
/* Go over all introduction circuit relay side and adjust their rate/burst
|
|
|
|
* values using the global parameters. This is called right after the
|
|
|
|
* consensus parameters might have changed. */
|
|
|
|
static void
|
|
|
|
update_intro_circuits(void)
|
|
|
|
{
|
|
|
|
/* Returns all HS version intro circuits. */
|
|
|
|
smartlist_t *intro_circs = hs_circuitmap_get_all_intro_circ_relay_side();
|
|
|
|
|
|
|
|
SMARTLIST_FOREACH_BEGIN(intro_circs, circuit_t *, circ) {
|
|
|
|
/* Adjust the rate/burst value that might have changed. */
|
|
|
|
token_bucket_ctr_adjust(&TO_OR_CIRCUIT(circ)->introduce2_bucket,
|
2019-08-12 12:32:38 -04:00
|
|
|
hs_dos_get_intro2_rate_param(),
|
|
|
|
hs_dos_get_intro2_burst_param());
|
2019-06-27 13:32:58 -04:00
|
|
|
} SMARTLIST_FOREACH_END(circ);
|
|
|
|
|
|
|
|
smartlist_free(intro_circs);
|
|
|
|
}
|
|
|
|
|
2019-06-11 08:28:13 -04:00
|
|
|
/* Set consensus parameters. */
|
|
|
|
static void
|
|
|
|
set_consensus_parameters(const networkstatus_t *ns)
|
|
|
|
{
|
2019-08-12 12:32:38 -04:00
|
|
|
param_introduce_rate_per_sec = get_param_rate_per_sec(ns);
|
|
|
|
param_introduce_burst_per_sec = get_param_burst_per_sec(ns);
|
|
|
|
param_introduce_defense_enabled = get_param_intro_dos_enabled(ns);
|
2019-06-27 13:32:58 -04:00
|
|
|
|
|
|
|
/* The above might have changed which means we need to go through all
|
|
|
|
* introduction circuits (relay side) and update the token buckets. */
|
|
|
|
update_intro_circuits();
|
2019-06-11 08:28:13 -04:00
|
|
|
}
|
|
|
|
|
2019-05-29 14:05:16 -04:00
|
|
|
/*
|
|
|
|
* Public API.
|
|
|
|
*/
|
|
|
|
|
2019-08-12 12:32:38 -04:00
|
|
|
/* Return the INTRODUCE2 cell rate per second (param or default). */
|
2019-06-11 08:28:13 -04:00
|
|
|
uint32_t
|
2019-08-12 12:32:38 -04:00
|
|
|
hs_dos_get_intro2_rate_param(void)
|
2019-06-11 08:28:13 -04:00
|
|
|
{
|
2019-08-12 12:32:38 -04:00
|
|
|
return param_introduce_rate_per_sec;
|
2019-06-11 08:28:13 -04:00
|
|
|
}
|
|
|
|
|
2019-08-12 12:32:38 -04:00
|
|
|
/* Return the INTRODUCE2 cell burst per second (param or default). */
|
2019-06-11 08:28:13 -04:00
|
|
|
uint32_t
|
2019-08-12 12:32:38 -04:00
|
|
|
hs_dos_get_intro2_burst_param(void)
|
2019-06-11 08:28:13 -04:00
|
|
|
{
|
2019-08-12 12:32:38 -04:00
|
|
|
return param_introduce_burst_per_sec;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the INTRODUCE2 DoS defense enabled flag (param or default). */
|
|
|
|
unsigned int
|
|
|
|
hs_dos_get_intro2_enabled_param(void)
|
|
|
|
{
|
|
|
|
return (unsigned int) param_introduce_defense_enabled;
|
2019-06-11 08:28:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Called when the consensus has changed. We might have new consensus
|
|
|
|
* parameters to look at. */
|
|
|
|
void
|
|
|
|
hs_dos_consensus_has_changed(const networkstatus_t *ns)
|
|
|
|
{
|
|
|
|
/* No point on updating these values if we are not a public relay that can
|
|
|
|
* be picked to be an introduction point. */
|
|
|
|
if (!public_server_mode(get_options())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_consensus_parameters(ns);
|
|
|
|
}
|
|
|
|
|
2019-05-29 14:05:16 -04:00
|
|
|
/* Return true iff an INTRODUCE2 cell can be sent on the given service
|
|
|
|
* introduction circuit. */
|
|
|
|
bool
|
|
|
|
hs_dos_can_send_intro2(or_circuit_t *s_intro_circ)
|
|
|
|
{
|
|
|
|
tor_assert(s_intro_circ);
|
|
|
|
|
2019-06-27 12:58:43 -04:00
|
|
|
/* Always allowed if the defense is disabled. */
|
2019-08-12 12:32:38 -04:00
|
|
|
if (!param_introduce_defense_enabled) {
|
2019-06-27 12:58:43 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-05-29 14:05:16 -04:00
|
|
|
/* Should not happen but if so, scream loudly. */
|
|
|
|
if (BUG(TO_CIRCUIT(s_intro_circ)->purpose != CIRCUIT_PURPOSE_INTRO_POINT)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is called just after we got a valid and parsed INTRODUCE1 cell. The
|
|
|
|
* service has been found and we have its introduction circuit.
|
|
|
|
*
|
|
|
|
* First, the INTRODUCE2 bucket will be refilled (if any). Then, decremented
|
|
|
|
* because we are about to send or not the cell we just got. Finally,
|
|
|
|
* evaluate if we can send it based on our token bucket state. */
|
|
|
|
|
|
|
|
/* Refill INTRODUCE2 bucket. */
|
|
|
|
token_bucket_ctr_refill(&s_intro_circ->introduce2_bucket,
|
|
|
|
(uint32_t) approx_time());
|
|
|
|
|
|
|
|
/* Decrement the bucket for this valid INTRODUCE1 cell we just got. Don't
|
|
|
|
* underflow else we end up with a too big of a bucket. */
|
|
|
|
if (token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0) {
|
|
|
|
token_bucket_ctr_dec(&s_intro_circ->introduce2_bucket, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finally, we can send a new INTRODUCE2 if there are still tokens. */
|
|
|
|
return token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0;
|
|
|
|
}
|
2019-06-11 08:28:13 -04:00
|
|
|
|
|
|
|
/* Initialize the onion service Denial of Service subsystem. */
|
|
|
|
void
|
|
|
|
hs_dos_init(void)
|
|
|
|
{
|
|
|
|
set_consensus_parameters(NULL);
|
|
|
|
}
|