hs_dos.c (7977B)
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 26 #include "feature/hs/hs_circuitmap.h" 27 #include "feature/nodelist/networkstatus.h" 28 #include "feature/relay/routermode.h" 29 30 #include "lib/evloop/token_bucket.h" 31 #include "lib/time/compat_time.h" 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. */ 50 static 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. */ 55 static uint32_t consensus_param_introduce_rate_per_sec = 56 HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC; 57 static uint32_t consensus_param_introduce_burst_per_sec = 58 HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC; 59 static uint32_t consensus_param_introduce_defense_enabled = 60 HS_DOS_INTRODUCE_ENABLED_DEFAULT; 61 62 STATIC uint32_t 63 get_intro2_enable_consensus_param(const networkstatus_t *ns) 64 { 65 return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSDefense", 66 HS_DOS_INTRODUCE_ENABLED_DEFAULT, 0, 1); 67 } 68 69 /** Return the parameter for the introduction rate per sec. */ 70 STATIC uint32_t 71 get_intro2_rate_consensus_param(const networkstatus_t *ns) 72 { 73 return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSRatePerSec", 74 HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC, 75 0, INT32_MAX); 76 } 77 78 /** Return the parameter for the introduction burst per sec. */ 79 STATIC uint32_t 80 get_intro2_burst_consensus_param(const networkstatus_t *ns) 81 { 82 return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSBurstPerSec", 83 HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC, 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. */ 90 static void 91 update_intro_circuits(void) 92 { 93 /* Returns all HS version intro circuits. */ 94 smartlist_t *intro_circs = hs_circuitmap_get_all_intro_circ_relay_side(); 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. */ 103 TO_OR_CIRCUIT(circ)->introduce2_dos_defense_enabled = 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. */ 115 static void 116 set_consensus_parameters(const networkstatus_t *ns) 117 { 118 consensus_param_introduce_rate_per_sec = 119 get_intro2_rate_consensus_param(ns); 120 consensus_param_introduce_burst_per_sec = 121 get_intro2_burst_consensus_param(ns); 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. */ 127 update_intro_circuits(); 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. */ 137 void 138 hs_dos_setup_default_intro2_defenses(or_circuit_t *circ) 139 { 140 tor_assert(circ); 141 142 circ->introduce2_dos_defense_enabled = 143 consensus_param_introduce_defense_enabled; 144 token_bucket_ctr_init(&circ->introduce2_bucket, 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. */ 152 void 153 hs_dos_consensus_has_changed(const networkstatus_t *ns) 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. */ 157 if (!public_server_mode(get_options())) { 158 return; 159 } 160 161 set_consensus_parameters(ns); 162 } 163 164 /** Return true iff an INTRODUCE2 cell can be sent on the given service 165 * introduction circuit. */ 166 bool 167 hs_dos_can_send_intro2(or_circuit_t *s_intro_circ) 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. */ 191 token_bucket_ctr_refill(&s_intro_circ->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. */ 210 intro2_rejected_count++; 211 return false; 212 213 allow: 214 return true; 215 } 216 217 /** Return rolling count of rejected INTRO2. */ 218 uint64_t 219 hs_dos_get_intro2_rejected_count(void) 220 { 221 return intro2_rejected_count; 222 } 223 224 /** Initialize the onion service Denial of Service subsystem. */ 225 void 226 hs_dos_init(void) 227 { 228 set_consensus_parameters(NULL); 229 }