Tor 0.4.9.0-alpha-dev
hs_dos.c
Go to the documentation of this file.
1/* Copyright (c) 2019-2021, The Tor Project, Inc. */
2/* See LICENSE for licensing information */
3
4/**
5 * \file hs_dos.c
6 * \brief Implement denial of service mitigation for the onion service
7 * subsystem.
8 *
9 * This module defenses:
10 *
11 * - Introduction Rate Limiting: If enabled by the consensus, an introduction
12 * point will rate limit client introduction towards the service (INTRODUCE2
13 * cells). It uses a token bucket model with a rate and burst per second.
14 *
15 * Proposal 305 will expand this module by allowing an operator to define
16 * these values into the ESTABLISH_INTRO cell. Not yet implemented.
17 **/
18
19#define HS_DOS_PRIVATE
20
21#include "core/or/or.h"
22#include "app/config/config.h"
23
24#include "core/or/circuitlist.h"
25
29
32
33#include "feature/hs/hs_dos.h"
34
35/** Default value of the allowed INTRODUCE2 cell rate per second. Above that
36 * value per second, the introduction is denied. */
37#define HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC 25
38
39/** Default value of the allowed INTRODUCE2 cell burst per second. This is the
40 * maximum value a token bucket has per second. We thus allow up to this value
41 * of INTRODUCE2 cell per second but the bucket is refilled by the rate value
42 * but never goes above that burst value. */
43#define HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC 200
44
45/** Default value of the consensus parameter enabling or disabling the
46 * introduction DoS defense. Disabled by default. */
47#define HS_DOS_INTRODUCE_ENABLED_DEFAULT 0
48
49/** INTRODUCE2 rejected request counter. */
50static uint64_t intro2_rejected_count = 0;
51
52/* Consensus parameters. The ESTABLISH_INTRO DoS cell extension have higher
53 * priority than these values. If no extension is sent, these are used only by
54 * the introduction point. */
55static uint32_t consensus_param_introduce_rate_per_sec =
57static uint32_t consensus_param_introduce_burst_per_sec =
59static uint32_t consensus_param_introduce_defense_enabled =
61
62STATIC uint32_t
63get_intro2_enable_consensus_param(const networkstatus_t *ns)
64{
65 return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSDefense",
67}
68
69/** Return the parameter for the introduction rate per sec. */
70STATIC uint32_t
72{
73 return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSRatePerSec",
75 0, INT32_MAX);
76}
77
78/** Return the parameter for the introduction burst per sec. */
79STATIC uint32_t
81{
82 return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSBurstPerSec",
84 0, INT32_MAX);
85}
86
87/** Go over all introduction circuit relay side and adjust their rate/burst
88 * values using the global parameters. This is called right after the
89 * consensus parameters might have changed. */
90static void
92{
93 /* Returns all HS version intro circuits. */
95
96 SMARTLIST_FOREACH_BEGIN(intro_circs, circuit_t *, circ) {
97 /* Ignore circuit if the defenses were set explicitly through the
98 * ESTABLISH_INTRO cell DoS extension. */
99 if (TO_OR_CIRCUIT(circ)->introduce2_dos_defense_explicit) {
100 continue;
101 }
102 /* Defenses might have been enabled or disabled. */
104 consensus_param_introduce_defense_enabled;
105 /* Adjust the rate/burst value that might have changed. */
106 token_bucket_ctr_adjust(&TO_OR_CIRCUIT(circ)->introduce2_bucket,
107 consensus_param_introduce_rate_per_sec,
108 consensus_param_introduce_burst_per_sec);
109 } SMARTLIST_FOREACH_END(circ);
110
111 smartlist_free(intro_circs);
112}
113
114/** Set consensus parameters. */
115static void
117{
118 consensus_param_introduce_rate_per_sec =
120 consensus_param_introduce_burst_per_sec =
122 consensus_param_introduce_defense_enabled =
123 get_intro2_enable_consensus_param(ns);
124
125 /* The above might have changed which means we need to go through all
126 * introduction circuits (relay side) and update the token buckets. */
128}
129
130/*
131 * Public API.
132 */
133
134/** Initialize the INTRODUCE2 token bucket for the DoS defenses using the
135 * consensus/default values. We might get a cell extension that changes those
136 * later but if we don't, the default or consensus parameters are used. */
137void
139{
140 tor_assert(circ);
141
143 consensus_param_introduce_defense_enabled;
145 consensus_param_introduce_rate_per_sec,
146 consensus_param_introduce_burst_per_sec,
147 (uint32_t) monotime_coarse_absolute_sec());
148}
149
150/** Called when the consensus has changed. We might have new consensus
151 * parameters to look at. */
152void
154{
155 /* No point on updating these values if we are not a public relay that can
156 * be picked to be an introduction point. */
158 return;
159 }
160
162}
163
164/** Return true iff an INTRODUCE2 cell can be sent on the given service
165 * introduction circuit. */
166bool
168{
169 tor_assert(s_intro_circ);
170
171 /* Allow to send the cell if the DoS defenses are disabled on the circuit.
172 * This can be set by the consensus, the ESTABLISH_INTRO cell extension or
173 * the hardcoded values in tor code. */
174 if (!s_intro_circ->introduce2_dos_defense_enabled) {
175 goto allow;
176 }
177
178 /* Should not happen but if so, scream loudly. */
179 if (BUG(TO_CIRCUIT(s_intro_circ)->purpose != CIRCUIT_PURPOSE_INTRO_POINT)) {
180 goto disallow;
181 }
182
183 /* This is called just after we got a valid and parsed INTRODUCE1 cell. The
184 * service has been found and we have its introduction circuit.
185 *
186 * First, the INTRODUCE2 bucket will be refilled (if any). Then, decremented
187 * because we are about to send or not the cell we just got. Finally,
188 * evaluate if we can send it based on our token bucket state. */
189
190 /* Refill INTRODUCE2 bucket. */
192 (uint32_t) monotime_coarse_absolute_sec());
193
194 /* Decrement the bucket for this valid INTRODUCE1 cell we just got. Don't
195 * underflow else we end up with a too big of a bucket. */
196 if (token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0) {
197 token_bucket_ctr_dec(&s_intro_circ->introduce2_bucket, 1);
198 }
199
200 /* Finally, we can send a new INTRODUCE2 if there are still tokens. */
201 if (token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0) {
202 goto allow;
203 }
204
205 /* If we reach this point, then it means the bucket has reached zero, and
206 we're going to disallow. */
207
208 disallow:
209 /* Increment stats counter, we are rejecting the INTRO2 cell. */
211 return false;
212
213 allow:
214 return true;
215}
216
217/** Return rolling count of rejected INTRO2. */
218uint64_t
220{
222}
223
224/** Initialize the onion service Denial of Service subsystem. */
225void
227{
229}
or_circuit_t * TO_OR_CIRCUIT(circuit_t *x)
Definition: circuitlist.c:173
Header file for circuitlist.c.
#define CIRCUIT_PURPOSE_INTRO_POINT
Definition: circuitlist.h:42
Functions and types for monotonic times.
const or_options_t * get_options(void)
Definition: config.c:944
Header file for config.c.
smartlist_t * hs_circuitmap_get_all_intro_circ_relay_side(void)
Header file for hs_circuitmap.c.
static void set_consensus_parameters(const networkstatus_t *ns)
Definition: hs_dos.c:116
bool hs_dos_can_send_intro2(or_circuit_t *s_intro_circ)
Definition: hs_dos.c:167
static uint64_t intro2_rejected_count
Definition: hs_dos.c:50
void hs_dos_init(void)
Definition: hs_dos.c:226
void hs_dos_setup_default_intro2_defenses(or_circuit_t *circ)
Definition: hs_dos.c:138
static void update_intro_circuits(void)
Definition: hs_dos.c:91
uint64_t hs_dos_get_intro2_rejected_count(void)
Definition: hs_dos.c:219
STATIC uint32_t get_intro2_rate_consensus_param(const networkstatus_t *ns)
Definition: hs_dos.c:71
#define HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC
Definition: hs_dos.c:43
void hs_dos_consensus_has_changed(const networkstatus_t *ns)
Definition: hs_dos.c:153
#define HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC
Definition: hs_dos.c:37
STATIC uint32_t get_intro2_burst_consensus_param(const networkstatus_t *ns)
Definition: hs_dos.c:80
#define HS_DOS_INTRODUCE_ENABLED_DEFAULT
Definition: hs_dos.c:47
Header file containing denial of service defenses for the HS subsystem for all versions.
int32_t networkstatus_get_param(const networkstatus_t *ns, const char *param_name, int32_t default_val, int32_t min_val, int32_t max_val)
Header file for networkstatus.c.
Master header file for Tor-specific functionality.
#define TO_CIRCUIT(x)
Definition: or.h:849
int public_server_mode(const or_options_t *options)
Definition: routermode.c:43
Header file for routermode.c.
#define SMARTLIST_FOREACH_BEGIN(sl, type, var)
unsigned int introduce2_dos_defense_enabled
Definition: or_circuit_st.h:95
token_bucket_ctr_t introduce2_bucket
#define STATIC
Definition: testsupport.h:32
void token_bucket_ctr_init(token_bucket_ctr_t *bucket, uint32_t rate, uint32_t burst, uint32_t now_ts_sec)
Definition: token_bucket.c:267
void token_bucket_ctr_adjust(token_bucket_ctr_t *bucket, uint32_t rate, uint32_t burst)
Definition: token_bucket.c:278
void token_bucket_ctr_refill(token_bucket_ctr_t *bucket, uint32_t now_ts_sec)
Definition: token_bucket.c:296
Headers for token_bucket.c.
#define tor_assert(expr)
Definition: util_bug.h:103