mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
Merge remote-tracking branch 'tor-github/pr/1208'
This commit is contained in:
@@ -45,9 +45,9 @@ problem function-size /src/app/config/config.c:parse_dir_fallback_line() 101
|
||||
problem function-size /src/app/config/config.c:parse_port_config() 446
|
||||
problem function-size /src/app/config/config.c:parse_ports() 168
|
||||
problem function-size /src/app/config/config.c:getinfo_helper_config() 113
|
||||
problem include-count /src/app/main/main.c 67
|
||||
problem include-count /src/app/main/main.c 68
|
||||
problem function-size /src/app/main/main.c:dumpstats() 102
|
||||
problem function-size /src/app/main/main.c:tor_init() 133
|
||||
problem function-size /src/app/main/main.c:tor_init() 137
|
||||
problem function-size /src/app/main/main.c:sandbox_init_filter() 291
|
||||
problem function-size /src/app/main/main.c:run_tor_main_loop() 105
|
||||
problem function-size /src/app/main/ntmain.c:nt_service_install() 126
|
||||
@@ -206,7 +206,7 @@ problem function-size /src/feature/nodelist/authcert.c:trusted_dirs_load_certs_f
|
||||
problem function-size /src/feature/nodelist/authcert.c:authority_certs_fetch_missing() 295
|
||||
problem function-size /src/feature/nodelist/fmt_routerstatus.c:routerstatus_format_entry() 162
|
||||
problem function-size /src/feature/nodelist/microdesc.c:microdesc_cache_rebuild() 134
|
||||
problem include-count /src/feature/nodelist/networkstatus.c 62
|
||||
problem include-count /src/feature/nodelist/networkstatus.c 63
|
||||
problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_check_consensus_signature() 175
|
||||
problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_set_current_consensus() 289
|
||||
problem function-size /src/feature/nodelist/node_select.c:router_pick_directory_server_impl() 122
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "feature/dircache/consdiffmgr.h"
|
||||
#include "feature/dirparse/routerparse.h"
|
||||
#include "feature/hibernate/hibernate.h"
|
||||
#include "feature/hs/hs_dos.h"
|
||||
#include "feature/nodelist/authcert.h"
|
||||
#include "feature/nodelist/networkstatus.h"
|
||||
#include "feature/nodelist/routerlist.h"
|
||||
@@ -637,6 +638,10 @@ tor_init(int argc, char *argv[])
|
||||
/* Initialize circuit padding to defaults+torrc until we get a consensus */
|
||||
circpad_machines_init();
|
||||
|
||||
/* Initialize hidden service DoS subsystem. We need to do this once the
|
||||
* configuration object has been set because it can be accessed. */
|
||||
hs_dos_init();
|
||||
|
||||
/* Initialize predicted ports list after loading options */
|
||||
predicted_ports_init();
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ LIBTOR_APP_A_SOURCES = \
|
||||
src/feature/hs/hs_config.c \
|
||||
src/feature/hs/hs_control.c \
|
||||
src/feature/hs/hs_descriptor.c \
|
||||
src/feature/hs/hs_dos.c \
|
||||
src/feature/hs/hs_ident.c \
|
||||
src/feature/hs/hs_intropoint.c \
|
||||
src/feature/hs/hs_service.c \
|
||||
@@ -374,6 +375,7 @@ noinst_HEADERS += \
|
||||
src/feature/hs/hs_config.h \
|
||||
src/feature/hs/hs_control.h \
|
||||
src/feature/hs/hs_descriptor.h \
|
||||
src/feature/hs/hs_dos.h \
|
||||
src/feature/hs/hs_ident.h \
|
||||
src/feature/hs/hs_intropoint.h \
|
||||
src/feature/hs/hs_service.h \
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "core/or/circuit_st.h"
|
||||
#include "core/or/crypt_path_st.h"
|
||||
|
||||
#include "lib/evloop/token_bucket.h"
|
||||
|
||||
struct onion_queue_t;
|
||||
|
||||
/** An or_circuit_t holds information needed to implement a circuit at an
|
||||
@@ -69,6 +71,11 @@ struct or_circuit_t {
|
||||
* exit-ward queues of this circuit; reset every time when writing
|
||||
* buffer stats to disk. */
|
||||
uint64_t total_cell_waiting_time;
|
||||
|
||||
/** INTRODUCE2 cell bucket controlling how much can go on this circuit. Only
|
||||
* used if this is a service introduction circuit at the intro point
|
||||
* (purpose = CIRCUIT_PURPOSE_INTRO_POINT). */
|
||||
token_bucket_ctr_t introduce2_bucket;
|
||||
};
|
||||
|
||||
#endif /* !defined(OR_CIRCUIT_ST_H) */
|
||||
|
||||
@@ -272,6 +272,33 @@ hs_circuitmap_get_or_circuit(hs_token_type_t type,
|
||||
|
||||
/**** Public relay-side getters: */
|
||||
|
||||
/* Public function: Return v2 and v3 introduction circuit to this relay.
|
||||
* Always return a newly allocated list for which it is the caller's
|
||||
* responsability to free it. */
|
||||
smartlist_t *
|
||||
hs_circuitmap_get_all_intro_circ_relay_side(void)
|
||||
{
|
||||
circuit_t **iter;
|
||||
smartlist_t *circuit_list = smartlist_new();
|
||||
|
||||
HT_FOREACH(iter, hs_circuitmap_ht, the_hs_circuitmap) {
|
||||
circuit_t *circ = *iter;
|
||||
|
||||
/* An origin circuit or purpose is wrong or the hs token is not set to be
|
||||
* a v2 or v3 intro relay side type, we ignore the circuit. Else, we have
|
||||
* a match so add it to our list. */
|
||||
if (CIRCUIT_IS_ORIGIN(circ) ||
|
||||
circ->purpose != CIRCUIT_PURPOSE_INTRO_POINT ||
|
||||
(circ->hs_token->type != HS_TOKEN_INTRO_V3_RELAY_SIDE &&
|
||||
circ->hs_token->type != HS_TOKEN_INTRO_V2_RELAY_SIDE)) {
|
||||
continue;
|
||||
}
|
||||
smartlist_add(circuit_list, circ);
|
||||
}
|
||||
|
||||
return circuit_list;
|
||||
}
|
||||
|
||||
/* Public function: Return a v3 introduction circuit to this relay with
|
||||
* <b>auth_key</b>. Return NULL if no such circuit is found in the
|
||||
* circuitmap. */
|
||||
|
||||
@@ -34,6 +34,8 @@ void hs_circuitmap_register_intro_circ_v2_relay_side(struct or_circuit_t *circ,
|
||||
void hs_circuitmap_register_intro_circ_v3_relay_side(struct or_circuit_t *circ,
|
||||
const ed25519_public_key_t *auth_key);
|
||||
|
||||
smartlist_t *hs_circuitmap_get_all_intro_circ_relay_side(void);
|
||||
|
||||
/** Public service-side API: */
|
||||
|
||||
struct origin_circuit_t *
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "feature/hs/hs_circuitmap.h"
|
||||
#include "feature/hs/hs_client.h"
|
||||
#include "feature/hs/hs_common.h"
|
||||
#include "feature/hs/hs_dos.h"
|
||||
#include "feature/hs/hs_ident.h"
|
||||
#include "feature/hs/hs_service.h"
|
||||
#include "feature/hs_common/shared_random_client.h"
|
||||
@@ -30,6 +31,7 @@
|
||||
#include "feature/nodelist/routerset.h"
|
||||
#include "feature/rend/rendcommon.h"
|
||||
#include "feature/rend/rendservice.h"
|
||||
#include "feature/relay/routermode.h"
|
||||
#include "lib/crypt_ops/crypto_rand.h"
|
||||
#include "lib/crypt_ops/crypto_util.h"
|
||||
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/* 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
|
||||
|
||||
#include "core/or/or.h"
|
||||
#include "app/config/config.h"
|
||||
|
||||
#include "core/or/circuitlist.h"
|
||||
|
||||
#include "feature/hs/hs_circuitmap.h"
|
||||
#include "feature/nodelist/networkstatus.h"
|
||||
#include "feature/relay/routermode.h"
|
||||
|
||||
#include "lib/evloop/token_bucket.h"
|
||||
|
||||
#include "hs_dos.h"
|
||||
|
||||
/* Default value of the allowed INTRODUCE2 cell rate per second. Above that
|
||||
* value per second, the introduction is denied. */
|
||||
#define HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC 25
|
||||
|
||||
/* 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. */
|
||||
#define HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC 200
|
||||
|
||||
/* Default value of the consensus parameter enabling or disabling the
|
||||
* introduction DoS defense. Disabled by default. */
|
||||
#define HS_DOS_INTRODUCE_ENABLED_DEFAULT 0
|
||||
|
||||
/* Consensus parameters. */
|
||||
static uint32_t hs_dos_introduce_rate_per_sec =
|
||||
HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC;
|
||||
static uint32_t hs_dos_introduce_burst_per_sec =
|
||||
HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC;
|
||||
static uint32_t hs_dos_introduce_enabled =
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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",
|
||||
HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC,
|
||||
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",
|
||||
HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC,
|
||||
0, INT32_MAX);
|
||||
}
|
||||
|
||||
/* 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,
|
||||
hs_dos_get_intro2_rate(),
|
||||
hs_dos_get_intro2_burst());
|
||||
} SMARTLIST_FOREACH_END(circ);
|
||||
|
||||
smartlist_free(intro_circs);
|
||||
}
|
||||
|
||||
/* Set consensus parameters. */
|
||||
static void
|
||||
set_consensus_parameters(const networkstatus_t *ns)
|
||||
{
|
||||
hs_dos_introduce_rate_per_sec = get_param_rate_per_sec(ns);
|
||||
hs_dos_introduce_burst_per_sec = get_param_burst_per_sec(ns);
|
||||
hs_dos_introduce_enabled = get_param_intro_dos_enabled(ns);
|
||||
|
||||
/* 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();
|
||||
}
|
||||
|
||||
/*
|
||||
* Public API.
|
||||
*/
|
||||
|
||||
/* Return the INTRODUCE2 cell rate per second. */
|
||||
uint32_t
|
||||
hs_dos_get_intro2_rate(void)
|
||||
{
|
||||
return hs_dos_introduce_rate_per_sec;
|
||||
}
|
||||
|
||||
/* Return the INTRODUCE2 cell burst per second. */
|
||||
uint32_t
|
||||
hs_dos_get_intro2_burst(void)
|
||||
{
|
||||
return hs_dos_introduce_burst_per_sec;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Always allowed if the defense is disabled. */
|
||||
if (!hs_dos_introduce_enabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Initialize the onion service Denial of Service subsystem. */
|
||||
void
|
||||
hs_dos_init(void)
|
||||
{
|
||||
set_consensus_parameters(NULL);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Copyright (c) 2019, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file hs_dos.h
|
||||
* \brief Header file containing denial of service defenses for the HS
|
||||
* subsystem for all versions.
|
||||
**/
|
||||
|
||||
#ifndef TOR_HS_DOS_H
|
||||
#define TOR_HS_DOS_H
|
||||
|
||||
#include "core/or/or_circuit_st.h"
|
||||
|
||||
#include "feature/nodelist/networkstatus_st.h"
|
||||
|
||||
/* Init */
|
||||
void hs_dos_init(void);
|
||||
|
||||
/* Consensus. */
|
||||
void hs_dos_consensus_has_changed(const networkstatus_t *ns);
|
||||
|
||||
bool hs_dos_can_send_intro2(or_circuit_t *s_intro_circ);
|
||||
|
||||
/* Getters. */
|
||||
uint32_t hs_dos_get_intro2_rate(void);
|
||||
uint32_t hs_dos_get_intro2_burst(void);
|
||||
|
||||
#ifdef HS_DOS_PRIVATE
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
|
||||
#endif /* define(TOR_UNIT_TESTS) */
|
||||
|
||||
#endif /* defined(HS_DOS_PRIVATE) */
|
||||
|
||||
#endif /* !defined(TOR_HS_DOS_H) */
|
||||
@@ -25,9 +25,10 @@
|
||||
#include "trunnel/hs/cell_introduce1.h"
|
||||
|
||||
#include "feature/hs/hs_circuitmap.h"
|
||||
#include "feature/hs/hs_descriptor.h"
|
||||
#include "feature/hs/hs_intropoint.h"
|
||||
#include "feature/hs/hs_common.h"
|
||||
#include "feature/hs/hs_descriptor.h"
|
||||
#include "feature/hs/hs_dos.h"
|
||||
#include "feature/hs/hs_intropoint.h"
|
||||
|
||||
#include "core/or/or_circuit_st.h"
|
||||
|
||||
@@ -203,6 +204,9 @@ handle_verified_establish_intro_cell(or_circuit_t *circ,
|
||||
hs_circuitmap_register_intro_circ_v3_relay_side(circ, &auth_key);
|
||||
/* Repurpose this circuit into an intro circuit. */
|
||||
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
|
||||
/* Initialize the INTRODUCE2 token bucket for the rate limiting. */
|
||||
token_bucket_ctr_init(&circ->introduce2_bucket, hs_dos_get_intro2_rate(),
|
||||
hs_dos_get_intro2_burst(), (uint32_t) approx_time());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -481,6 +485,20 @@ handle_introduce1(or_circuit_t *client_circ, const uint8_t *request,
|
||||
}
|
||||
}
|
||||
|
||||
/* Before sending, lets make sure this cell can be sent on the service
|
||||
* circuit asking the DoS defenses. */
|
||||
if (!hs_dos_can_send_intro2(service_circ)) {
|
||||
char *msg;
|
||||
static ratelim_t rlimit = RATELIM_INIT(5 * 60);
|
||||
if ((msg = rate_limit_log(&rlimit, approx_time()))) {
|
||||
log_info(LD_PROTOCOL, "Can't relay INTRODUCE1 v3 cell due to DoS "
|
||||
"limitations. Sending NACK to client.");
|
||||
tor_free(msg);
|
||||
}
|
||||
status = TRUNNEL_HS_INTRO_ACK_STATUS_UNKNOWN_ID;
|
||||
goto send_ack;
|
||||
}
|
||||
|
||||
/* Relay the cell to the service on its intro circuit with an INTRODUCE2
|
||||
* cell which is the same exact payload. */
|
||||
if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(service_circ),
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
#include "feature/dircommon/voting_schedule.h"
|
||||
#include "feature/dirparse/ns_parse.h"
|
||||
#include "feature/hibernate/hibernate.h"
|
||||
#include "feature/hs/hs_dos.h"
|
||||
#include "feature/nodelist/authcert.h"
|
||||
#include "feature/nodelist/dirlist.h"
|
||||
#include "feature/nodelist/fmt_routerstatus.h"
|
||||
@@ -1674,6 +1675,7 @@ notify_before_networkstatus_changes(const networkstatus_t *old_c,
|
||||
notify_control_networkstatus_changed(old_c, new_c);
|
||||
dos_consensus_has_changed(new_c);
|
||||
relay_consensus_has_changed(new_c);
|
||||
hs_dos_consensus_has_changed(new_c);
|
||||
}
|
||||
|
||||
/* Called after a new consensus has been put in the global state. It is safe
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "feature/rend/rendmid.h"
|
||||
#include "feature/stats/rephist.h"
|
||||
#include "feature/hs/hs_circuitmap.h"
|
||||
#include "feature/hs/hs_dos.h"
|
||||
#include "feature/hs/hs_intropoint.h"
|
||||
|
||||
#include "core/or/or_circuit_st.h"
|
||||
@@ -180,6 +181,14 @@ rend_mid_introduce_legacy(or_circuit_t *circ, const uint8_t *request,
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Before sending, lets make sure this cell can be sent on the service
|
||||
* circuit asking the DoS defenses. */
|
||||
if (!hs_dos_can_send_intro2(intro_circ)) {
|
||||
log_info(LD_PROTOCOL, "Can't relay INTRODUCE1 v2 cell due to DoS "
|
||||
"limitations. Sending NACK to client.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
log_info(LD_REND,
|
||||
"Sending introduction request for service %s "
|
||||
"from circ %u to circ %u",
|
||||
|
||||
@@ -158,6 +158,7 @@ src_test_test_SOURCES += \
|
||||
src/test/test_handles.c \
|
||||
src/test/test_hs_cache.c \
|
||||
src/test/test_hs_descriptor.c \
|
||||
src/test/test_hs_dos.c \
|
||||
src/test/test_introduce.c \
|
||||
src/test/test_keypin.c \
|
||||
src/test/test_link_handshake.c \
|
||||
|
||||
@@ -877,6 +877,7 @@ struct testgroup_t testgroups[] = {
|
||||
{ "hs_config/", hs_config_tests },
|
||||
{ "hs_control/", hs_control_tests },
|
||||
{ "hs_descriptor/", hs_descriptor },
|
||||
{ "hs_dos/", hs_dos_tests },
|
||||
{ "hs_intropoint/", hs_intropoint_tests },
|
||||
{ "hs_ntor/", hs_ntor_tests },
|
||||
{ "hs_service/", hs_service_tests },
|
||||
|
||||
@@ -227,6 +227,7 @@ extern struct testcase_t hs_common_tests[];
|
||||
extern struct testcase_t hs_config_tests[];
|
||||
extern struct testcase_t hs_control_tests[];
|
||||
extern struct testcase_t hs_descriptor[];
|
||||
extern struct testcase_t hs_dos_tests[];
|
||||
extern struct testcase_t hs_intropoint_tests[];
|
||||
extern struct testcase_t hs_ntor_tests[];
|
||||
extern struct testcase_t hs_service_tests[];
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/* Copyright (c) 2017-2019, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file test_hs_cell.c
|
||||
* \brief Test hidden service cell functionality.
|
||||
*/
|
||||
|
||||
#define CIRCUITLIST_PRIVATE
|
||||
#define NETWORKSTATUS_PRIVATE
|
||||
|
||||
#include "test/test.h"
|
||||
#include "test/test_helpers.h"
|
||||
#include "test/log_test_helpers.h"
|
||||
|
||||
#include "app/config/config.h"
|
||||
|
||||
#include "core/or/circuitlist.h"
|
||||
#include "core/or/circuituse.h"
|
||||
#include "core/or/or_circuit_st.h"
|
||||
|
||||
#include "feature/hs/hs_dos.h"
|
||||
#include "feature/nodelist/networkstatus.h"
|
||||
|
||||
static void
|
||||
setup_mock_consensus(void)
|
||||
{
|
||||
current_ns_consensus = tor_malloc_zero(sizeof(networkstatus_t));
|
||||
current_ns_consensus->net_params = smartlist_new();
|
||||
smartlist_add(current_ns_consensus->net_params,
|
||||
(void *) "HiddenServiceEnableIntroDoSDefense=1");
|
||||
hs_dos_consensus_has_changed(current_ns_consensus);
|
||||
}
|
||||
|
||||
static void
|
||||
free_mock_consensus(void)
|
||||
{
|
||||
smartlist_free(current_ns_consensus->net_params);
|
||||
tor_free(current_ns_consensus);
|
||||
}
|
||||
|
||||
static void
|
||||
test_can_send_intro2(void *arg)
|
||||
{
|
||||
uint32_t now = (uint32_t) approx_time();
|
||||
or_circuit_t *or_circ = NULL;
|
||||
|
||||
(void) arg;
|
||||
|
||||
hs_init();
|
||||
hs_dos_init();
|
||||
|
||||
get_options_mutable()->ORPort_set = 1;
|
||||
setup_mock_consensus();
|
||||
|
||||
or_circ = or_circuit_new(1, NULL);
|
||||
|
||||
/* Make that circuit a service intro point. */
|
||||
circuit_change_purpose(TO_CIRCUIT(or_circ), CIRCUIT_PURPOSE_INTRO_POINT);
|
||||
/* Initialize the INTRODUCE2 token bucket for the rate limiting. */
|
||||
token_bucket_ctr_init(&or_circ->introduce2_bucket, hs_dos_get_intro2_rate(),
|
||||
hs_dos_get_intro2_burst(), now);
|
||||
|
||||
/* Brand new circuit, we should be able to send INTRODUCE2 cells. */
|
||||
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
|
||||
|
||||
/* Simulate that 10 cells have arrived in 1 second. There should be no
|
||||
* refill since the bucket is already at maximum on the first cell. */
|
||||
update_approx_time(++now);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
|
||||
}
|
||||
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
|
||||
hs_dos_get_intro2_burst() - 10);
|
||||
|
||||
/* Fully refill the bucket minus 1 cell. */
|
||||
update_approx_time(++now);
|
||||
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
|
||||
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
|
||||
hs_dos_get_intro2_burst() - 1);
|
||||
|
||||
/* Receive an INTRODUCE2 at each second. We should have the bucket full
|
||||
* since at every second it gets refilled. */
|
||||
for (int i = 0; i < 10; i++) {
|
||||
update_approx_time(++now);
|
||||
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
|
||||
}
|
||||
/* Last check if we can send the cell decrements the bucket so minus 1. */
|
||||
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
|
||||
hs_dos_get_intro2_burst() - 1);
|
||||
|
||||
/* Manually reset bucket for next test. */
|
||||
token_bucket_ctr_reset(&or_circ->introduce2_bucket, now);
|
||||
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
|
||||
hs_dos_get_intro2_burst());
|
||||
|
||||
/* Do a full burst in the current second which should empty the bucket and
|
||||
* we shouldn't be allowed to send one more cell after that. We go minus 1
|
||||
* cell else the very last check if we can send the INTRO2 cell returns
|
||||
* false because the bucket goes down to 0. */
|
||||
for (uint32_t i = 0; i < hs_dos_get_intro2_burst() - 1; i++) {
|
||||
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
|
||||
}
|
||||
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 1);
|
||||
/* Get the last remaining cell, we shouldn't be allowed to send it. */
|
||||
tt_int_op(false, OP_EQ, hs_dos_can_send_intro2(or_circ));
|
||||
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 0);
|
||||
|
||||
/* Make sure the next 100 cells aren't allowed and bucket stays at 0. */
|
||||
for (int i = 0; i < 100; i++) {
|
||||
tt_int_op(false, OP_EQ, hs_dos_can_send_intro2(or_circ));
|
||||
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ, 0);
|
||||
}
|
||||
|
||||
/* One second has passed, we should have the rate minus 1 cell added. */
|
||||
update_approx_time(++now);
|
||||
tt_int_op(true, OP_EQ, hs_dos_can_send_intro2(or_circ));
|
||||
tt_uint_op(token_bucket_ctr_get(&or_circ->introduce2_bucket), OP_EQ,
|
||||
hs_dos_get_intro2_rate() - 1);
|
||||
|
||||
done:
|
||||
circuit_free_(TO_CIRCUIT(or_circ));
|
||||
|
||||
hs_free_all();
|
||||
free_mock_consensus();
|
||||
}
|
||||
|
||||
struct testcase_t hs_dos_tests[] = {
|
||||
{ "can_send_intro2", test_can_send_intro2, TT_FORK,
|
||||
NULL, NULL },
|
||||
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "feature/hs/hs_cell.h"
|
||||
#include "feature/hs/hs_circuitmap.h"
|
||||
#include "feature/hs/hs_common.h"
|
||||
#include "feature/hs/hs_dos.h"
|
||||
#include "feature/hs/hs_intropoint.h"
|
||||
#include "feature/hs/hs_service.h"
|
||||
|
||||
@@ -119,6 +120,8 @@ helper_create_intro_circuit(void)
|
||||
or_circuit_t *circ = or_circuit_new(0, NULL);
|
||||
tt_assert(circ);
|
||||
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR);
|
||||
token_bucket_ctr_init(&circ->introduce2_bucket, 100, 100,
|
||||
(uint32_t) approx_time());
|
||||
done:
|
||||
return circ;
|
||||
}
|
||||
@@ -900,42 +903,63 @@ test_received_introduce1_handling(void *arg)
|
||||
UNMOCK(relay_send_command_from_edge_);
|
||||
}
|
||||
|
||||
static void *
|
||||
hs_subsystem_setup_fn(const struct testcase_t *tc)
|
||||
{
|
||||
(void) tc;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
hs_subsystem_cleanup_fn(const struct testcase_t *tc, void *arg)
|
||||
{
|
||||
(void) tc;
|
||||
(void) arg;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct testcase_setup_t test_setup = {
|
||||
hs_subsystem_setup_fn, hs_subsystem_cleanup_fn
|
||||
};
|
||||
|
||||
struct testcase_t hs_intropoint_tests[] = {
|
||||
{ "intro_point_registration",
|
||||
test_intro_point_registration, TT_FORK, NULL, NULL },
|
||||
test_intro_point_registration, TT_FORK, NULL, &test_setup},
|
||||
|
||||
{ "receive_establish_intro_wrong_keytype",
|
||||
test_establish_intro_wrong_keytype, TT_FORK, NULL, NULL },
|
||||
test_establish_intro_wrong_keytype, TT_FORK, NULL, &test_setup},
|
||||
|
||||
{ "receive_establish_intro_wrong_keytype2",
|
||||
test_establish_intro_wrong_keytype2, TT_FORK, NULL, NULL },
|
||||
test_establish_intro_wrong_keytype2, TT_FORK, NULL, &test_setup},
|
||||
|
||||
{ "receive_establish_intro_wrong_purpose",
|
||||
test_establish_intro_wrong_purpose, TT_FORK, NULL, NULL },
|
||||
test_establish_intro_wrong_purpose, TT_FORK, NULL, &test_setup},
|
||||
|
||||
{ "receive_establish_intro_wrong_sig",
|
||||
test_establish_intro_wrong_sig, TT_FORK, NULL, NULL },
|
||||
test_establish_intro_wrong_sig, TT_FORK, NULL, &test_setup},
|
||||
|
||||
{ "receive_establish_intro_wrong_sig_len",
|
||||
test_establish_intro_wrong_sig_len, TT_FORK, NULL, NULL },
|
||||
test_establish_intro_wrong_sig_len, TT_FORK, NULL, &test_setup},
|
||||
|
||||
{ "receive_establish_intro_wrong_auth_key_len",
|
||||
test_establish_intro_wrong_auth_key_len, TT_FORK, NULL, NULL },
|
||||
test_establish_intro_wrong_auth_key_len, TT_FORK, NULL, &test_setup},
|
||||
|
||||
{ "receive_establish_intro_wrong_mac",
|
||||
test_establish_intro_wrong_mac, TT_FORK, NULL, NULL },
|
||||
test_establish_intro_wrong_mac, TT_FORK, NULL, &test_setup},
|
||||
|
||||
{ "introduce1_suitable_circuit",
|
||||
test_introduce1_suitable_circuit, TT_FORK, NULL, NULL },
|
||||
test_introduce1_suitable_circuit, TT_FORK, NULL, &test_setup},
|
||||
|
||||
{ "introduce1_is_legacy",
|
||||
test_introduce1_is_legacy, TT_FORK, NULL, NULL },
|
||||
test_introduce1_is_legacy, TT_FORK, NULL, &test_setup},
|
||||
|
||||
{ "introduce1_validation",
|
||||
test_introduce1_validation, TT_FORK, NULL, NULL },
|
||||
test_introduce1_validation, TT_FORK, NULL, &test_setup},
|
||||
|
||||
{ "received_introduce1_handling",
|
||||
test_received_introduce1_handling, TT_FORK, NULL, NULL },
|
||||
test_received_introduce1_handling, TT_FORK, NULL, &test_setup},
|
||||
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user