hs_dos.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /* Copyright (c) 2019, The Tor Project, Inc. */
  2. /* See LICENSE for licensing information */
  3. /**
  4. * \file hs_dos.c
  5. * \brief Implement denial of service mitigation for the onion service
  6. * subsystem.
  7. *
  8. * This module defenses:
  9. *
  10. * - Introduction Rate Limiting: If enabled by the consensus, an introduction
  11. * point will rate limit client introduction towards the service (INTRODUCE2
  12. * cells). It uses a token bucket model with a rate and burst per second.
  13. *
  14. * Proposal 305 will expand this module by allowing an operator to define
  15. * these values into the ESTABLISH_INTRO cell. Not yet implemented.
  16. **/
  17. #define HS_DOS_PRIVATE
  18. #include "core/or/or.h"
  19. #include "app/config/config.h"
  20. #include "core/or/circuitlist.h"
  21. #include "feature/hs/hs_circuitmap.h"
  22. #include "feature/nodelist/networkstatus.h"
  23. #include "feature/relay/routermode.h"
  24. #include "lib/evloop/token_bucket.h"
  25. #include "feature/hs/hs_dos.h"
  26. /* Default value of the allowed INTRODUCE2 cell rate per second. Above that
  27. * value per second, the introduction is denied. */
  28. #define HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC 25
  29. /* Default value of the allowed INTRODUCE2 cell burst per second. This is the
  30. * maximum value a token bucket has per second. We thus allow up to this value
  31. * of INTRODUCE2 cell per second but the bucket is refilled by the rate value
  32. * but never goes above that burst value. */
  33. #define HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC 200
  34. /* Default value of the consensus parameter enabling or disabling the
  35. * introduction DoS defense. Disabled by default. */
  36. #define HS_DOS_INTRODUCE_ENABLED_DEFAULT 0
  37. /* Consensus parameters. The ESTABLISH_INTRO DoS cell extension have higher
  38. * priority than these values. If no extension is sent, these are used only by
  39. * the introduction point. */
  40. static uint32_t consensus_param_introduce_rate_per_sec =
  41. HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC;
  42. static uint32_t consensus_param_introduce_burst_per_sec =
  43. HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC;
  44. static uint32_t consensus_param_introduce_defense_enabled =
  45. HS_DOS_INTRODUCE_ENABLED_DEFAULT;
  46. STATIC uint32_t
  47. get_intro2_enable_consensus_param(const networkstatus_t *ns)
  48. {
  49. return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSDefense",
  50. HS_DOS_INTRODUCE_ENABLED_DEFAULT, 0, 1);
  51. }
  52. /* Return the parameter for the introduction rate per sec. */
  53. STATIC uint32_t
  54. get_intro2_rate_consensus_param(const networkstatus_t *ns)
  55. {
  56. return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSRatePerSec",
  57. HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC,
  58. 0, INT32_MAX);
  59. }
  60. /* Return the parameter for the introduction burst per sec. */
  61. STATIC uint32_t
  62. get_intro2_burst_consensus_param(const networkstatus_t *ns)
  63. {
  64. return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSBurstPerSec",
  65. HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC,
  66. 0, INT32_MAX);
  67. }
  68. /* Go over all introduction circuit relay side and adjust their rate/burst
  69. * values using the global parameters. This is called right after the
  70. * consensus parameters might have changed. */
  71. static void
  72. update_intro_circuits(void)
  73. {
  74. /* Returns all HS version intro circuits. */
  75. smartlist_t *intro_circs = hs_circuitmap_get_all_intro_circ_relay_side();
  76. SMARTLIST_FOREACH_BEGIN(intro_circs, circuit_t *, circ) {
  77. /* Defenses might have been enabled or disabled. */
  78. TO_OR_CIRCUIT(circ)->introduce2_dos_defense_enabled =
  79. consensus_param_introduce_defense_enabled;
  80. /* Adjust the rate/burst value that might have changed. */
  81. token_bucket_ctr_adjust(&TO_OR_CIRCUIT(circ)->introduce2_bucket,
  82. consensus_param_introduce_rate_per_sec,
  83. consensus_param_introduce_burst_per_sec);
  84. } SMARTLIST_FOREACH_END(circ);
  85. smartlist_free(intro_circs);
  86. }
  87. /* Set consensus parameters. */
  88. static void
  89. set_consensus_parameters(const networkstatus_t *ns)
  90. {
  91. consensus_param_introduce_rate_per_sec =
  92. get_intro2_rate_consensus_param(ns);
  93. consensus_param_introduce_burst_per_sec =
  94. get_intro2_burst_consensus_param(ns);
  95. consensus_param_introduce_defense_enabled =
  96. get_intro2_enable_consensus_param(ns);
  97. /* The above might have changed which means we need to go through all
  98. * introduction circuits (relay side) and update the token buckets. */
  99. update_intro_circuits();
  100. }
  101. /*
  102. * Public API.
  103. */
  104. /* Initialize the INTRODUCE2 token bucket for the DoS defenses using the
  105. * consensus/default values. We might get a cell extension that changes those
  106. * later but if we don't, the default or consensus parameters are used. */
  107. void
  108. hs_dos_setup_default_intro2_defenses(or_circuit_t *circ)
  109. {
  110. tor_assert(circ);
  111. circ->introduce2_dos_defense_enabled =
  112. consensus_param_introduce_defense_enabled;
  113. token_bucket_ctr_init(&circ->introduce2_bucket,
  114. consensus_param_introduce_rate_per_sec,
  115. consensus_param_introduce_burst_per_sec,
  116. (uint32_t) approx_time());
  117. }
  118. /* Called when the consensus has changed. We might have new consensus
  119. * parameters to look at. */
  120. void
  121. hs_dos_consensus_has_changed(const networkstatus_t *ns)
  122. {
  123. /* No point on updating these values if we are not a public relay that can
  124. * be picked to be an introduction point. */
  125. if (!public_server_mode(get_options())) {
  126. return;
  127. }
  128. set_consensus_parameters(ns);
  129. }
  130. /* Return true iff an INTRODUCE2 cell can be sent on the given service
  131. * introduction circuit. */
  132. bool
  133. hs_dos_can_send_intro2(or_circuit_t *s_intro_circ)
  134. {
  135. tor_assert(s_intro_circ);
  136. /* Allow to send the cell if the DoS defenses are disabled on the circuit.
  137. * This can be set by the consensus, the ESTABLISH_INTRO cell extension or
  138. * the hardcoded values in tor code. */
  139. if (!s_intro_circ->introduce2_dos_defense_enabled) {
  140. return true;
  141. }
  142. /* Should not happen but if so, scream loudly. */
  143. if (BUG(TO_CIRCUIT(s_intro_circ)->purpose != CIRCUIT_PURPOSE_INTRO_POINT)) {
  144. return false;
  145. }
  146. /* This is called just after we got a valid and parsed INTRODUCE1 cell. The
  147. * service has been found and we have its introduction circuit.
  148. *
  149. * First, the INTRODUCE2 bucket will be refilled (if any). Then, decremented
  150. * because we are about to send or not the cell we just got. Finally,
  151. * evaluate if we can send it based on our token bucket state. */
  152. /* Refill INTRODUCE2 bucket. */
  153. token_bucket_ctr_refill(&s_intro_circ->introduce2_bucket,
  154. (uint32_t) approx_time());
  155. /* Decrement the bucket for this valid INTRODUCE1 cell we just got. Don't
  156. * underflow else we end up with a too big of a bucket. */
  157. if (token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0) {
  158. token_bucket_ctr_dec(&s_intro_circ->introduce2_bucket, 1);
  159. }
  160. /* Finally, we can send a new INTRODUCE2 if there are still tokens. */
  161. return token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0;
  162. }
  163. /* Initialize the onion service Denial of Service subsystem. */
  164. void
  165. hs_dos_init(void)
  166. {
  167. set_consensus_parameters(NULL);
  168. }