|
@@ -0,0 +1,737 @@
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * \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 "router.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.
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+static unsigned int dos_cc_enabled = 0;
|
|
|
+
|
|
|
+
|
|
|
+ * They are initialized with the hardcoded default values. */
|
|
|
+static uint32_t dos_cc_min_concurrent_conn;
|
|
|
+static uint32_t dos_cc_circuit_rate;
|
|
|
+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;
|
|
|
+
|
|
|
+
|
|
|
+static uint64_t cc_num_rejected_cells;
|
|
|
+static uint32_t cc_num_marked_addrs;
|
|
|
+
|
|
|
+
|
|
|
+ * Concurrent connection denial of service mitigation.
|
|
|
+ *
|
|
|
+ * Namespace used for this mitigation framework is "dos_conn_".
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+static unsigned int dos_conn_enabled = 0;
|
|
|
+
|
|
|
+
|
|
|
+ * 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;
|
|
|
+
|
|
|
+
|
|
|
+static uint64_t conn_num_addr_rejected;
|
|
|
+
|
|
|
+
|
|
|
+ * General interface of the denial of service mitigation subsystem.
|
|
|
+ */
|
|
|
+
|
|
|
+
|
|
|
+static uint64_t num_single_hop_client_refused;
|
|
|
+
|
|
|
+
|
|
|
+ * 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);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * 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);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * time span. */
|
|
|
+static uint32_t
|
|
|
+get_param_cc_circuit_rate(const networkstatus_t *ns)
|
|
|
+{
|
|
|
+
|
|
|
+ if (get_options()->DoSCircuitCreationRate) {
|
|
|
+ return get_options()->DoSCircuitCreationRate;
|
|
|
+ }
|
|
|
+ return networkstatus_get_param(ns, "DoSCircuitCreationRate",
|
|
|
+ DOS_CC_CIRCUIT_RATE_DEFAULT,
|
|
|
+ 1, INT32_MAX);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * 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);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+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);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * time should we defend against a malicious client address. */
|
|
|
+static int32_t
|
|
|
+get_param_cc_defense_time_period(const networkstatus_t *ns)
|
|
|
+{
|
|
|
+
|
|
|
+ if (get_options()->DoSCircuitCreationDefenseTimePeriod) {
|
|
|
+ return get_options()->DoSCircuitCreationDefenseTimePeriod;
|
|
|
+ }
|
|
|
+ return networkstatus_get_param(ns, "DoSCircuitCreationDefenseTimePeriod",
|
|
|
+ DOS_CC_DEFENSE_TIME_PERIOD_DEFAULT,
|
|
|
+ 0, INT32_MAX);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * 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);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * 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);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+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);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * if none are present. Called at initialization or when the consensus
|
|
|
+ * changes. */
|
|
|
+static void
|
|
|
+set_dos_parameters(const networkstatus_t *ns)
|
|
|
+{
|
|
|
+
|
|
|
+ dos_cc_enabled = get_param_cc_enabled(ns);
|
|
|
+ dos_cc_min_concurrent_conn = get_param_cc_min_concurrent_connection(ns);
|
|
|
+ dos_cc_circuit_rate = get_param_cc_circuit_rate(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);
|
|
|
+
|
|
|
+
|
|
|
+ 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);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static void
|
|
|
+cc_free_all(void)
|
|
|
+{
|
|
|
+
|
|
|
+ dos_cc_enabled = 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * circuit creation subsystem. */
|
|
|
+static void
|
|
|
+cc_consensus_has_changed(const networkstatus_t *ns)
|
|
|
+{
|
|
|
+
|
|
|
+ * not and it was enabled before, clean it up. */
|
|
|
+ if (dos_cc_enabled && !get_param_cc_enabled(ns)) {
|
|
|
+ cc_free_all();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * configuration. */
|
|
|
+STATIC uint32_t
|
|
|
+get_circuit_rate_per_second(void)
|
|
|
+{
|
|
|
+ return dos_cc_circuit_rate;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * bucket if needed. This also works if the bucket was never filled in the
|
|
|
+ * first place. The addr is only used for logging purposes. */
|
|
|
+STATIC void
|
|
|
+cc_stats_refill_bucket(cc_client_stats_t *stats, const tor_addr_t *addr)
|
|
|
+{
|
|
|
+ uint32_t new_circuit_bucket_count, circuit_rate = 0, num_token;
|
|
|
+ time_t now, elapsed_time_last_refill;
|
|
|
+
|
|
|
+ tor_assert(stats);
|
|
|
+ tor_assert(addr);
|
|
|
+
|
|
|
+ now = approx_time();
|
|
|
+
|
|
|
+
|
|
|
+ * and we are done. */
|
|
|
+ if (stats->last_circ_bucket_refill_ts == 0) {
|
|
|
+ num_token = dos_cc_circuit_burst;
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * first compute the circuit rate that is how many circuit are we allowed to
|
|
|
+ * do per second. */
|
|
|
+ circuit_rate = get_circuit_rate_per_second();
|
|
|
+
|
|
|
+
|
|
|
+ elapsed_time_last_refill = now - stats->last_circ_bucket_refill_ts;
|
|
|
+
|
|
|
+
|
|
|
+ * that case, lets be safe and fill it up to the maximum. Not filling it
|
|
|
+ * could trigger a detection for a valid client. Also, if the clock jumped
|
|
|
+ * negative but we didn't notice until the elapsed time became positive
|
|
|
+ * again, then we potentially spent many seconds not refilling the bucket
|
|
|
+ * when we should have been refilling it. But the fact that we didn't notice
|
|
|
+ * until now means that no circuit creation requests came in during that
|
|
|
+ * time, so the client doesn't end up punished that much from this hopefully
|
|
|
+ * rare situation.*/
|
|
|
+ if (elapsed_time_last_refill < 0) {
|
|
|
+
|
|
|
+ * give us the maximum allowed value of token. */
|
|
|
+ elapsed_time_last_refill = (dos_cc_circuit_burst / circuit_rate);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * add to the bucket. This can be big but it is cap to a maximum after. */
|
|
|
+ num_token = elapsed_time_last_refill * circuit_rate;
|
|
|
+
|
|
|
+ end:
|
|
|
+
|
|
|
+ * over time. */
|
|
|
+ new_circuit_bucket_count = MIN(stats->circuit_bucket + num_token,
|
|
|
+ dos_cc_circuit_burst);
|
|
|
+ log_debug(LD_DOS, "DoS address %s has its circuit bucket value: %" PRIu32
|
|
|
+ ". Filling it to %" PRIu32 ". Circuit rate is %" PRIu32,
|
|
|
+ fmt_addr(addr), stats->circuit_bucket, new_circuit_bucket_count,
|
|
|
+ circuit_rate);
|
|
|
+
|
|
|
+ stats->circuit_bucket = new_circuit_bucket_count;
|
|
|
+ stats->last_circ_bucket_refill_ts = now;
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * concurrent connections is greater or equal the minimum threshold set the
|
|
|
+ * consensus parameter. */
|
|
|
+static int
|
|
|
+cc_has_exhausted_circuits(const dos_client_stats_t *stats)
|
|
|
+{
|
|
|
+ tor_assert(stats);
|
|
|
+ return stats->cc_stats.circuit_bucket == 0 &&
|
|
|
+ stats->concurrent_count >= dos_cc_min_concurrent_conn;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * us until when it is marked as positively detected. */
|
|
|
+static void
|
|
|
+cc_mark_client(cc_client_stats_t *stats)
|
|
|
+{
|
|
|
+ tor_assert(stats);
|
|
|
+
|
|
|
+ * less predictable. */
|
|
|
+ stats->marked_until_ts =
|
|
|
+ approx_time() + dos_cc_defense_time_period +
|
|
|
+ crypto_rand_int_range(1, dos_cc_defense_time_period / 2);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * called a lot and part of the fast path of handling cells. It has to remain
|
|
|
+ * as fast as we can. */
|
|
|
+static int
|
|
|
+cc_channel_addr_is_marked(channel_t *chan)
|
|
|
+{
|
|
|
+ time_t now;
|
|
|
+ tor_addr_t addr;
|
|
|
+ clientmap_entry_t *entry;
|
|
|
+ cc_client_stats_t *stats = NULL;
|
|
|
+
|
|
|
+ if (chan == NULL) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!channel_is_client(chan)) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!channel_get_addr_if_possible(chan, &addr)) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ entry = geoip_lookup_client(&addr, NULL, GEOIP_CLIENT_CONNECT);
|
|
|
+ if (entry == NULL) {
|
|
|
+
|
|
|
+ * cache. Once this DoS subsystem is enabled, we can end up here with no
|
|
|
+ * entry for the channel. */
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+ now = approx_time();
|
|
|
+ stats = &entry->dos_stats.cc_stats;
|
|
|
+
|
|
|
+ end:
|
|
|
+ return stats && stats->marked_until_ts >= now;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+static void
|
|
|
+conn_free_all(void)
|
|
|
+{
|
|
|
+ dos_conn_enabled = 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * connection mitigation subsystem. */
|
|
|
+static void
|
|
|
+conn_consensus_has_changed(const networkstatus_t *ns)
|
|
|
+{
|
|
|
+
|
|
|
+ * If not and it was enabled before, clean it up. */
|
|
|
+ if (dos_conn_enabled && !get_param_conn_enabled(ns)) {
|
|
|
+ conn_free_all();
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * 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);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+dos_cc_new_create_cell(channel_t *chan)
|
|
|
+{
|
|
|
+ tor_addr_t addr;
|
|
|
+ clientmap_entry_t *entry;
|
|
|
+
|
|
|
+ tor_assert(chan);
|
|
|
+
|
|
|
+
|
|
|
+ if (!dos_cc_enabled) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (!channel_is_client(chan)) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!channel_get_addr_if_possible(chan, &addr)) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ entry = geoip_lookup_client(&addr, NULL, GEOIP_CLIENT_CONNECT);
|
|
|
+ if (entry == NULL) {
|
|
|
+
|
|
|
+ * cache. Once this DoS subsystem is enabled, we can end up here with no
|
|
|
+ * entry for the channel. */
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * malicious, we continue to track statistics. If it keeps going above
|
|
|
+ * threshold while marked, the defense period time will grow longer. There
|
|
|
+ * is really no point at unmarking a client that keeps DoSing us. */
|
|
|
+
|
|
|
+
|
|
|
+ * before we assess. */
|
|
|
+ cc_stats_refill_bucket(&entry->dos_stats.cc_stats, &addr);
|
|
|
+
|
|
|
+
|
|
|
+ * underflow the bucket. */
|
|
|
+ if (entry->dos_stats.cc_stats.circuit_bucket > 0) {
|
|
|
+ entry->dos_stats.cc_stats.circuit_bucket--;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * get marked as malicious. This should be kept as fast as possible. */
|
|
|
+ if (cc_has_exhausted_circuits(&entry->dos_stats)) {
|
|
|
+
|
|
|
+ * Under heavy DDoS, logging each time we mark would results in lots and
|
|
|
+ * lots of logs. */
|
|
|
+ if (entry->dos_stats.cc_stats.marked_until_ts == 0) {
|
|
|
+ log_debug(LD_DOS, "Detected circuit creation DoS by address: %s",
|
|
|
+ fmt_addr(&addr));
|
|
|
+ cc_num_marked_addrs++;
|
|
|
+ }
|
|
|
+ cc_mark_client(&entry->dos_stats.cc_stats);
|
|
|
+ }
|
|
|
+
|
|
|
+ end:
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ *
|
|
|
+ * This is part of the fast path and called a lot. */
|
|
|
+dos_cc_defense_type_t
|
|
|
+dos_cc_get_defense_type(channel_t *chan)
|
|
|
+{
|
|
|
+ tor_assert(chan);
|
|
|
+
|
|
|
+
|
|
|
+ if (!dos_cc_enabled) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * connection detected by our DoS circuit creation mitigation subsystem. */
|
|
|
+ if (cc_channel_addr_is_marked(chan)) {
|
|
|
+
|
|
|
+ * cell it just seen. Note it down. */
|
|
|
+ cc_num_rejected_cells++;
|
|
|
+ return dos_cc_defense_type;
|
|
|
+ }
|
|
|
+
|
|
|
+ end:
|
|
|
+ return DOS_CC_DEFENSE_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * A defense value is returned for the caller to take appropriate actions. */
|
|
|
+dos_conn_defense_type_t
|
|
|
+dos_conn_addr_get_defense_type(const tor_addr_t *addr)
|
|
|
+{
|
|
|
+ clientmap_entry_t *entry;
|
|
|
+
|
|
|
+ tor_assert(addr);
|
|
|
+
|
|
|
+
|
|
|
+ if (!dos_conn_enabled) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ entry = geoip_lookup_client(addr, NULL, GEOIP_CLIENT_CONNECT);
|
|
|
+ if (entry == NULL) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * defense. */
|
|
|
+ if (entry->dos_stats.concurrent_count > dos_conn_max_concurrent_count) {
|
|
|
+ conn_num_addr_rejected++;
|
|
|
+ return dos_conn_defense_type;
|
|
|
+ }
|
|
|
+
|
|
|
+ end:
|
|
|
+ return DOS_CONN_DEFENSE_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ * freed. This is called for every entry that is being freed.
|
|
|
+ *
|
|
|
+ * This function will clear out the connection tracked flag if the concurrent
|
|
|
+ * count of the entry is above 0 so if those connections end up being seen by
|
|
|
+ * this subsystem, we won't try to decrement the counter for a new geoip entry
|
|
|
+ * that might have been added after this call for the same address. */
|
|
|
+void
|
|
|
+dos_geoip_entry_about_to_free(const clientmap_entry_t *geoip_ent)
|
|
|
+{
|
|
|
+ tor_assert(geoip_ent);
|
|
|
+
|
|
|
+
|
|
|
+ * clear the geoip entry from the cache. */
|
|
|
+ if (geoip_ent->dos_stats.concurrent_count == 0) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * tracked flag because the entry is about to get removed from the geoip
|
|
|
+ * cache. We do not try to decrement if the flag is not set. */
|
|
|
+ SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
|
|
|
+ if (conn->type == CONN_TYPE_OR) {
|
|
|
+ or_connection_t *or_conn = TO_OR_CONN(conn);
|
|
|
+ if (!tor_addr_compare(&geoip_ent->addr, &or_conn->real_addr,
|
|
|
+ CMP_EXACT)) {
|
|
|
+ or_conn->tracked_for_dos_mitigation = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } SMARTLIST_FOREACH_END(conn);
|
|
|
+
|
|
|
+ end:
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * counter later used for the heartbeat. */
|
|
|
+void
|
|
|
+dos_note_refuse_single_hop_client(void)
|
|
|
+{
|
|
|
+ num_single_hop_client_refused++;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * be refused. */
|
|
|
+int
|
|
|
+dos_should_refuse_single_hop_client(void)
|
|
|
+{
|
|
|
+
|
|
|
+ if (!public_server_mode(get_options())) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (get_options()->DoSRefuseSingleHopClientRendezvous != -1) {
|
|
|
+ return get_options()->DoSRefuseSingleHopClientRendezvous;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (int) networkstatus_get_param(NULL,
|
|
|
+ "DoSRefuseSingleHopClientRendezvous",
|
|
|
+ 0 , 0, 1);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+dos_log_heartbeat(void)
|
|
|
+{
|
|
|
+ char *conn_msg = NULL;
|
|
|
+ char *cc_msg = NULL;
|
|
|
+ char *single_hop_client_msg = NULL;
|
|
|
+
|
|
|
+ if (!dos_is_enabled()) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dos_cc_enabled) {
|
|
|
+ tor_asprintf(&cc_msg,
|
|
|
+ " %" PRIu64 " circuits rejected,"
|
|
|
+ " %" PRIu32 " marked addresses.",
|
|
|
+ cc_num_rejected_cells, cc_num_marked_addrs);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dos_conn_enabled) {
|
|
|
+ tor_asprintf(&conn_msg,
|
|
|
+ " %" PRIu64 " connections closed.",
|
|
|
+ conn_num_addr_rejected);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dos_should_refuse_single_hop_client()) {
|
|
|
+ tor_asprintf(&single_hop_client_msg,
|
|
|
+ " %" PRIu64 " single hop clients refused.",
|
|
|
+ num_single_hop_client_refused);
|
|
|
+ }
|
|
|
+
|
|
|
+ log_notice(LD_HEARTBEAT,
|
|
|
+ "DoS mitigation since startup:%s%s%s",
|
|
|
+ (cc_msg != NULL) ? cc_msg : " [cc not enabled]",
|
|
|
+ (conn_msg != NULL) ? conn_msg : " [conn not enabled]",
|
|
|
+ (single_hop_client_msg != NULL) ? single_hop_client_msg : "");
|
|
|
+
|
|
|
+ tor_free(conn_msg);
|
|
|
+ tor_free(cc_msg);
|
|
|
+ tor_free(single_hop_client_msg);
|
|
|
+
|
|
|
+ end:
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * address. */
|
|
|
+void
|
|
|
+dos_new_client_conn(or_connection_t *or_conn)
|
|
|
+{
|
|
|
+ clientmap_entry_t *entry;
|
|
|
+
|
|
|
+ tor_assert(or_conn);
|
|
|
+
|
|
|
+
|
|
|
+ * enabled so we'll start allocating stuff. */
|
|
|
+ if (!dos_is_enabled()) {
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ entry = geoip_lookup_client(&or_conn->real_addr, NULL,
|
|
|
+ GEOIP_CLIENT_CONNECT);
|
|
|
+ if (BUG(entry == NULL)) {
|
|
|
+
|
|
|
+ * 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;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+dos_close_client_conn(const or_connection_t *or_conn)
|
|
|
+{
|
|
|
+ clientmap_entry_t *entry;
|
|
|
+
|
|
|
+ tor_assert(or_conn);
|
|
|
+
|
|
|
+
|
|
|
+ * 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;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ entry = geoip_lookup_client(&or_conn->real_addr, NULL,
|
|
|
+ GEOIP_CLIENT_CONNECT);
|
|
|
+ if (entry == NULL) {
|
|
|
+
|
|
|
+ * got to be noted down in the geoip cache. */
|
|
|
+ goto end;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * 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;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * parameters to look at. */
|
|
|
+void
|
|
|
+dos_consensus_has_changed(const networkstatus_t *ns)
|
|
|
+{
|
|
|
+ cc_consensus_has_changed(ns);
|
|
|
+ conn_consensus_has_changed(ns);
|
|
|
+
|
|
|
+
|
|
|
+ * consensus parameters for all subsystems. */
|
|
|
+ set_dos_parameters(ns);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int
|
|
|
+dos_enabled(void)
|
|
|
+{
|
|
|
+ return dos_is_enabled();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+dos_free_all(void)
|
|
|
+{
|
|
|
+
|
|
|
+ * even if it wasn't initialized. */
|
|
|
+ cc_free_all();
|
|
|
+
|
|
|
+
|
|
|
+ * it wasn't initialized. */
|
|
|
+ conn_free_all();
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+void
|
|
|
+dos_init(void)
|
|
|
+{
|
|
|
+
|
|
|
+ set_dos_parameters(NULL);
|
|
|
+}
|
|
|
+
|