123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- /* Copyright (c) 2018, The Tor Project, Inc. */
- /* See LICENSE for licensing information */
- /*
- * \file dos.c
- * \brief Implement Denial of Service mitigation subsystem.
- */
- #define DOS_PRIVATE
- #include "or.h"
- #include "channel.h"
- #include "config.h"
- #include "geoip.h"
- #include "main.h"
- #include "networkstatus.h"
- #include "dos.h"
- /*
- * Circuit creation denial of service mitigation.
- *
- * Namespace used for this mitigation framework is "dos_cc_" where "cc" is for
- * Circuit Creation.
- */
- /* Is the circuit creation DoS mitigation enabled? */
- static unsigned int dos_cc_enabled = 0;
- /* Consensus parameters. They can be changed when a new consensus arrives.
- * They are initialized with the hardcoded default values. */
- static uint32_t dos_cc_min_concurrent_conn;
- static uint32_t dos_cc_circuit_rate_tenths;
- static uint32_t dos_cc_circuit_burst;
- static dos_cc_defense_type_t dos_cc_defense_type;
- static int32_t dos_cc_defense_time_period;
- /*
- * Concurrent connection denial of service mitigation.
- *
- * Namespace used for this mitigation framework is "dos_conn_".
- */
- /* Is the connection DoS mitigation enabled? */
- static unsigned int dos_conn_enabled = 0;
- /* Consensus parameters. They can be changed when a new consensus arrives.
- * They are initialized with the hardcoded default values. */
- static uint32_t dos_conn_max_concurrent_count;
- static dos_conn_defense_type_t dos_conn_defense_type;
- /*
- * General interface of the denial of service mitigation subsystem.
- */
- /* Return true iff the circuit creation mitigation is enabled. We look at the
- * consensus for this else a default value is returned. */
- MOCK_IMPL(STATIC unsigned int,
- get_param_cc_enabled, (const networkstatus_t *ns))
- {
- if (get_options()->DoSCircuitCreationEnabled != -1) {
- return get_options()->DoSCircuitCreationEnabled;
- }
- return !!networkstatus_get_param(ns, "DoSCircuitCreationEnabled",
- DOS_CC_ENABLED_DEFAULT, 0, 1);
- }
- /* Return the parameter for the minimum concurrent connection at which we'll
- * start counting circuit for a specific client address. */
- STATIC uint32_t
- get_param_cc_min_concurrent_connection(const networkstatus_t *ns)
- {
- if (get_options()->DoSCircuitCreationMinConnections) {
- return get_options()->DoSCircuitCreationMinConnections;
- }
- return networkstatus_get_param(ns, "DoSCircuitCreationMinConnections",
- DOS_CC_MIN_CONCURRENT_CONN_DEFAULT,
- 1, INT32_MAX);
- }
- /* Return the parameter for the time rate that is how many circuits over this
- * time span. */
- static uint32_t
- get_param_cc_circuit_rate_tenths(const networkstatus_t *ns)
- {
- /* This is in seconds. */
- if (get_options()->DoSCircuitCreationRateTenths) {
- return get_options()->DoSCircuitCreationRateTenths;
- }
- return networkstatus_get_param(ns, "DoSCircuitCreationRateTenths",
- DOS_CC_CIRCUIT_RATE_TENTHS_DEFAULT,
- 1, INT32_MAX);
- }
- /* Return the parameter for the maximum circuit count for the circuit time
- * rate. */
- STATIC uint32_t
- get_param_cc_circuit_burst(const networkstatus_t *ns)
- {
- if (get_options()->DoSCircuitCreationBurst) {
- return get_options()->DoSCircuitCreationBurst;
- }
- return networkstatus_get_param(ns, "DoSCircuitCreationBurst",
- DOS_CC_CIRCUIT_BURST_DEFAULT,
- 1, INT32_MAX);
- }
- /* Return the consensus parameter of the circuit creation defense type. */
- static uint32_t
- get_param_cc_defense_type(const networkstatus_t *ns)
- {
- if (get_options()->DoSCircuitCreationDefenseType) {
- return get_options()->DoSCircuitCreationDefenseType;
- }
- return networkstatus_get_param(ns, "DoSCircuitCreationDefenseType",
- DOS_CC_DEFENSE_TYPE_DEFAULT,
- DOS_CC_DEFENSE_NONE, DOS_CC_DEFENSE_MAX);
- }
- /* Return the consensus parameter of the defense time period which is how much
- * time should we defend against a malicious client address. */
- static int32_t
- get_param_cc_defense_time_period(const networkstatus_t *ns)
- {
- /* Time in seconds. */
- if (get_options()->DoSCircuitCreationDefenseTimePeriod) {
- return get_options()->DoSCircuitCreationDefenseTimePeriod;
- }
- return networkstatus_get_param(ns, "DoSCircuitCreationDefenseTimePeriod",
- DOS_CC_DEFENSE_TIME_PERIOD_DEFAULT,
- 0, INT32_MAX);
- }
- /* Return true iff connection mitigation is enabled. We look at the consensus
- * for this else a default value is returned. */
- MOCK_IMPL(STATIC unsigned int,
- get_param_conn_enabled, (const networkstatus_t *ns))
- {
- if (get_options()->DoSConnectionEnabled != -1) {
- return get_options()->DoSConnectionEnabled;
- }
- return !!networkstatus_get_param(ns, "DoSConnectionEnabled",
- DOS_CONN_ENABLED_DEFAULT, 0, 1);
- }
- /* Return the consensus parameter for the maximum concurrent connection
- * allowed. */
- STATIC uint32_t
- get_param_conn_max_concurrent_count(const networkstatus_t *ns)
- {
- if (get_options()->DoSConnectionMaxConcurrentCount) {
- return get_options()->DoSConnectionMaxConcurrentCount;
- }
- return networkstatus_get_param(ns, "DoSConnectionMaxConcurrentCount",
- DOS_CONN_MAX_CONCURRENT_COUNT_DEFAULT,
- 1, INT32_MAX);
- }
- /* Return the consensus parameter of the connection defense type. */
- static uint32_t
- get_param_conn_defense_type(const networkstatus_t *ns)
- {
- if (get_options()->DoSConnectionDefenseType) {
- return get_options()->DoSConnectionDefenseType;
- }
- return networkstatus_get_param(ns, "DoSConnectionDefenseType",
- DOS_CONN_DEFENSE_TYPE_DEFAULT,
- DOS_CONN_DEFENSE_NONE, DOS_CONN_DEFENSE_MAX);
- }
- /* Set circuit creation parameters located in the consensus or their default
- * if none are present. Called at initialization or when the consensus
- * changes. */
- static void
- set_dos_parameters(const networkstatus_t *ns)
- {
- /* Get the default consensus param values. */
- dos_cc_enabled = get_param_cc_enabled(ns);
- dos_cc_min_concurrent_conn = get_param_cc_min_concurrent_connection(ns);
- dos_cc_circuit_rate_tenths = get_param_cc_circuit_rate_tenths(ns);
- dos_cc_circuit_burst = get_param_cc_circuit_burst(ns);
- dos_cc_defense_time_period = get_param_cc_defense_time_period(ns);
- dos_cc_defense_type = get_param_cc_defense_type(ns);
- /* Connection detection. */
- dos_conn_enabled = get_param_conn_enabled(ns);
- dos_conn_max_concurrent_count = get_param_conn_max_concurrent_count(ns);
- dos_conn_defense_type = get_param_conn_defense_type(ns);
- }
- /* Free everything for the circuit creation DoS mitigation subsystem. */
- static void
- cc_free_all(void)
- {
- /* If everything is freed, the circuit creation subsystem is not enabled. */
- dos_cc_enabled = 0;
- }
- /* Called when the consensus has changed. Do appropriate actions for the
- * circuit creation subsystem. */
- static void
- cc_consensus_has_changed(const networkstatus_t *ns)
- {
- /* Looking at the consensus, is the circuit creation subsystem enabled? If
- * not and it was enabled before, clean it up. */
- if (dos_cc_enabled && !get_param_cc_enabled(ns)) {
- cc_free_all();
- }
- }
- /* Concurrent connection private API. */
- /* Free everything for the connection DoS mitigation subsystem. */
- static void
- conn_free_all(void)
- {
- dos_conn_enabled = 0;
- }
- /* Called when the consensus has changed. Do appropriate actions for the
- * connection mitigation subsystem. */
- static void
- conn_consensus_has_changed(const networkstatus_t *ns)
- {
- /* Looking at the consensus, is the connection mitigation subsystem enabled?
- * If not and it was enabled before, clean it up. */
- if (dos_conn_enabled && !get_param_conn_enabled(ns)) {
- conn_free_all();
- }
- }
- /* General private API */
- /* Return true iff we have at least one DoS detection enabled. This is used to
- * decide if we need to allocate any kind of high level DoS object. */
- static inline int
- dos_is_enabled(void)
- {
- return (dos_cc_enabled || dos_conn_enabled);
- }
- /* Circuit creation public API. */
- /* Concurrent connection detection public API. */
- /* General API */
- /* Called when a new client connection has been established on the given
- * address. */
- void
- dos_new_client_conn(or_connection_t *or_conn)
- {
- clientmap_entry_t *entry;
- tor_assert(or_conn);
- /* Past that point, we know we have at least one DoS detection subsystem
- * enabled so we'll start allocating stuff. */
- if (!dos_is_enabled()) {
- goto end;
- }
- /* We are only interested in client connection from the geoip cache. */
- entry = geoip_lookup_client(&or_conn->real_addr, NULL,
- GEOIP_CLIENT_CONNECT);
- if (BUG(entry == NULL)) {
- /* Should never happen because we note down the address in the geoip
- * cache before this is called. */
- goto end;
- }
- entry->dos_stats.concurrent_count++;
- or_conn->tracked_for_dos_mitigation = 1;
- log_debug(LD_DOS, "Client address %s has now %u concurrent connections.",
- fmt_addr(&or_conn->real_addr),
- entry->dos_stats.concurrent_count);
- end:
- return;
- }
- /* Called when a client connection for the given IP address has been closed. */
- void
- dos_close_client_conn(const or_connection_t *or_conn)
- {
- clientmap_entry_t *entry;
- tor_assert(or_conn);
- /* We have to decrement the count on tracked connection only even if the
- * subsystem has been disabled at runtime because it might be re-enabled
- * after and we need to keep a synchronized counter at all time. */
- if (!or_conn->tracked_for_dos_mitigation) {
- goto end;
- }
- /* We are only interested in client connection from the geoip cache. */
- entry = geoip_lookup_client(&or_conn->real_addr, NULL,
- GEOIP_CLIENT_CONNECT);
- if (entry == NULL) {
- /* This can happen because we can close a connection before the channel
- * got to be noted down in the geoip cache. */
- goto end;
- }
- /* Extra super duper safety. Going below 0 means an underflow which could
- * lead to most likely a false positive. In theory, this should never happen
- * but lets be extra safe. */
- if (BUG(entry->dos_stats.concurrent_count == 0)) {
- goto end;
- }
- entry->dos_stats.concurrent_count--;
- log_debug(LD_DOS, "Client address %s has lost a connection. Concurrent "
- "connections are now at %u",
- fmt_addr(&or_conn->real_addr),
- entry->dos_stats.concurrent_count);
- end:
- return;
- }
- /* Called when the consensus has changed. We might have new consensus
- * parameters to look at. */
- void
- dos_consensus_has_changed(const networkstatus_t *ns)
- {
- cc_consensus_has_changed(ns);
- conn_consensus_has_changed(ns);
- /* We were already enabled or we just became enabled but either way, set the
- * consensus parameters for all subsystems. */
- set_dos_parameters(ns);
- }
- /* Return true iff the DoS mitigation subsystem is enabled. */
- int
- dos_enabled(void)
- {
- return dos_is_enabled();
- }
- /* Free everything from the Denial of Service subsystem. */
- void
- dos_free_all(void)
- {
- /* Free the circuit creation mitigation subsystem. It is safe to do this
- * even if it wasn't initialized. */
- cc_free_all();
- /* Free the connection mitigation subsystem. It is safe to do this even if
- * it wasn't initialized. */
- conn_free_all();
- }
- /* Initialize the Denial of Service subsystem. */
- void
- dos_init(void)
- {
- /* To initialize, we only need to get the parameters. */
- set_dos_parameters(NULL);
- }
|