LCOV - code coverage report
Current view: top level - feature/hs - hs_dos.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 48 54 88.9 %
Date: 2021-11-24 03:28:48 Functions: 10 10 100.0 %

          Line data    Source code
       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             : 
      32             : #include "feature/hs/hs_dos.h"
      33             : 
      34             : /** Default value of the allowed INTRODUCE2 cell rate per second. Above that
      35             :  * value per second, the introduction is denied. */
      36             : #define HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC 25
      37             : 
      38             : /** Default value of the allowed INTRODUCE2 cell burst per second. This is the
      39             :  * maximum value a token bucket has per second. We thus allow up to this value
      40             :  * of INTRODUCE2 cell per second but the bucket is refilled by the rate value
      41             :  * but never goes above that burst value. */
      42             : #define HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC 200
      43             : 
      44             : /** Default value of the consensus parameter enabling or disabling the
      45             :  * introduction DoS defense. Disabled by default. */
      46             : #define HS_DOS_INTRODUCE_ENABLED_DEFAULT 0
      47             : 
      48             : /** INTRODUCE2 rejected request counter. */
      49             : static uint64_t intro2_rejected_count = 0;
      50             : 
      51             : /* Consensus parameters. The ESTABLISH_INTRO DoS cell extension have higher
      52             :  * priority than these values. If no extension is sent, these are used only by
      53             :  * the introduction point. */
      54             : static uint32_t consensus_param_introduce_rate_per_sec =
      55             :   HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC;
      56             : static uint32_t consensus_param_introduce_burst_per_sec =
      57             :   HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC;
      58             : static uint32_t consensus_param_introduce_defense_enabled =
      59             :   HS_DOS_INTRODUCE_ENABLED_DEFAULT;
      60             : 
      61             : STATIC uint32_t
      62         196 : get_intro2_enable_consensus_param(const networkstatus_t *ns)
      63             : {
      64         196 :   return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSDefense",
      65             :                                  HS_DOS_INTRODUCE_ENABLED_DEFAULT, 0, 1);
      66             : }
      67             : 
      68             : /** Return the parameter for the introduction rate per sec. */
      69             : STATIC uint32_t
      70         198 : get_intro2_rate_consensus_param(const networkstatus_t *ns)
      71             : {
      72         198 :   return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSRatePerSec",
      73             :                                  HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC,
      74             :                                  0, INT32_MAX);
      75             : }
      76             : 
      77             : /** Return the parameter for the introduction burst per sec. */
      78             : STATIC uint32_t
      79         401 : get_intro2_burst_consensus_param(const networkstatus_t *ns)
      80             : {
      81         401 :   return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSBurstPerSec",
      82             :                                  HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC,
      83             :                                  0, INT32_MAX);
      84             : }
      85             : 
      86             : /** Go over all introduction circuit relay side and adjust their rate/burst
      87             :  * values using the global parameters. This is called right after the
      88             :  * consensus parameters might have changed. */
      89             : static void
      90         196 : update_intro_circuits(void)
      91             : {
      92             :   /* Returns all HS version intro circuits. */
      93         196 :   smartlist_t *intro_circs = hs_circuitmap_get_all_intro_circ_relay_side();
      94             : 
      95         196 :   SMARTLIST_FOREACH_BEGIN(intro_circs, circuit_t *, circ) {
      96             :     /* Ignore circuit if the defenses were set explicitly through the
      97             :      * ESTABLISH_INTRO cell DoS extension. */
      98           0 :     if (TO_OR_CIRCUIT(circ)->introduce2_dos_defense_explicit) {
      99           0 :       continue;
     100             :     }
     101             :     /* Defenses might have been enabled or disabled. */
     102           0 :     TO_OR_CIRCUIT(circ)->introduce2_dos_defense_enabled =
     103             :       consensus_param_introduce_defense_enabled;
     104             :     /* Adjust the rate/burst value that might have changed. */
     105           0 :     token_bucket_ctr_adjust(&TO_OR_CIRCUIT(circ)->introduce2_bucket,
     106             :                             consensus_param_introduce_rate_per_sec,
     107             :                             consensus_param_introduce_burst_per_sec);
     108           0 :   } SMARTLIST_FOREACH_END(circ);
     109             : 
     110         196 :   smartlist_free(intro_circs);
     111         196 : }
     112             : 
     113             : /** Set consensus parameters. */
     114             : static void
     115         196 : set_consensus_parameters(const networkstatus_t *ns)
     116             : {
     117         392 :   consensus_param_introduce_rate_per_sec =
     118         196 :     get_intro2_rate_consensus_param(ns);
     119         392 :   consensus_param_introduce_burst_per_sec =
     120         196 :     get_intro2_burst_consensus_param(ns);
     121         392 :   consensus_param_introduce_defense_enabled =
     122         196 :     get_intro2_enable_consensus_param(ns);
     123             : 
     124             :   /* The above might have changed which means we need to go through all
     125             :    * introduction circuits (relay side) and update the token buckets. */
     126         196 :   update_intro_circuits();
     127         196 : }
     128             : 
     129             : /*
     130             :  * Public API.
     131             :  */
     132             : 
     133             : /** Initialize the INTRODUCE2 token bucket for the DoS defenses using the
     134             :  * consensus/default values. We might get a cell extension that changes those
     135             :  * later but if we don't, the default or consensus parameters are used. */
     136             : void
     137           7 : hs_dos_setup_default_intro2_defenses(or_circuit_t *circ)
     138             : {
     139           7 :   tor_assert(circ);
     140             : 
     141           7 :   circ->introduce2_dos_defense_enabled =
     142             :     consensus_param_introduce_defense_enabled;
     143           7 :   token_bucket_ctr_init(&circ->introduce2_bucket,
     144             :                         consensus_param_introduce_rate_per_sec,
     145             :                         consensus_param_introduce_burst_per_sec,
     146           7 :                         (uint32_t) approx_time());
     147           7 : }
     148             : 
     149             : /** Called when the consensus has changed. We might have new consensus
     150             :  * parameters to look at. */
     151             : void
     152           6 : hs_dos_consensus_has_changed(const networkstatus_t *ns)
     153             : {
     154             :   /* No point on updating these values if we are not a public relay that can
     155             :    * be picked to be an introduction point. */
     156           6 :   if (!public_server_mode(get_options())) {
     157             :     return;
     158             :   }
     159             : 
     160           1 :   set_consensus_parameters(ns);
     161             : }
     162             : 
     163             : /** Return true iff an INTRODUCE2 cell can be sent on the given service
     164             :  * introduction circuit. */
     165             : bool
     166         324 : hs_dos_can_send_intro2(or_circuit_t *s_intro_circ)
     167             : {
     168         324 :   tor_assert(s_intro_circ);
     169             : 
     170             :   /* Allow to send the cell if the DoS defenses are disabled on the circuit.
     171             :    * This can be set by the consensus, the ESTABLISH_INTRO cell extension or
     172             :    * the hardcoded values in tor code. */
     173         324 :   if (!s_intro_circ->introduce2_dos_defense_enabled) {
     174           1 :     goto allow;
     175             :   }
     176             : 
     177             :   /* Should not happen but if so, scream loudly. */
     178         323 :   if (BUG(TO_CIRCUIT(s_intro_circ)->purpose != CIRCUIT_PURPOSE_INTRO_POINT)) {
     179           0 :     goto disallow;
     180             :   }
     181             : 
     182             :   /* This is called just after we got a valid and parsed INTRODUCE1 cell. The
     183             :    * service has been found and we have its introduction circuit.
     184             :    *
     185             :    * First, the INTRODUCE2 bucket will be refilled (if any). Then, decremented
     186             :    * because we are about to send or not the cell we just got. Finally,
     187             :    * evaluate if we can send it based on our token bucket state. */
     188             : 
     189             :   /* Refill INTRODUCE2 bucket. */
     190         323 :   token_bucket_ctr_refill(&s_intro_circ->introduce2_bucket,
     191         323 :                           (uint32_t) approx_time());
     192             : 
     193             :   /* Decrement the bucket for this valid INTRODUCE1 cell we just got. Don't
     194             :    * underflow else we end up with a too big of a bucket. */
     195         323 :   if (token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0) {
     196         223 :     token_bucket_ctr_dec(&s_intro_circ->introduce2_bucket, 1);
     197             :   }
     198             : 
     199             :   /* Finally, we can send a new INTRODUCE2 if there are still tokens. */
     200         323 :   if (token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0) {
     201         222 :     goto allow;
     202             :   }
     203             : 
     204             :   /* If we reach this point, then it means the bucket has reached zero, and
     205             :      we're going to disallow. */
     206             : 
     207         101 :  disallow:
     208             :   /* Increment stats counter, we are rejecting the INTRO2 cell. */
     209         101 :   intro2_rejected_count++;
     210         101 :   return false;
     211             : 
     212             :  allow:
     213             :   return true;
     214             : }
     215             : 
     216             : /** Return rolling count of rejected INTRO2. */
     217             : uint64_t
     218           1 : hs_dos_get_intro2_rejected_count(void)
     219             : {
     220           1 :   return intro2_rejected_count;
     221             : }
     222             : 
     223             : /** Initialize the onion service Denial of Service subsystem. */
     224             : void
     225         195 : hs_dos_init(void)
     226             : {
     227         195 :   set_consensus_parameters(NULL);
     228         195 : }

Generated by: LCOV version 1.14