123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794 |
- #define DOS_PRIVATE
- #include "or.h"
- #include "channel.h"
- #include "config.h"
- #include "geoip.h"
- #include "main.h"
- #include "networkstatus.h"
- #include "nodelist.h"
- #include "router.h"
- #include "dos.h"
- static unsigned int dos_cc_enabled = 0;
- 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;
- static unsigned int dos_conn_enabled = 0;
- 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;
- static uint64_t num_single_hop_client_refused;
- 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);
- }
- 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);
- }
- 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);
- }
- 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);
- }
- 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);
- }
- 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);
- }
- 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);
- }
- 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;
- }
- static void
- cc_consensus_has_changed(const networkstatus_t *ns)
- {
-
- if (dos_cc_enabled && !get_param_cc_enabled(ns)) {
- cc_free_all();
- }
- }
- STATIC uint64_t
- get_circuit_rate_per_second(void)
- {
- return dos_cc_circuit_rate;
- }
- STATIC void
- cc_stats_refill_bucket(cc_client_stats_t *stats, const tor_addr_t *addr)
- {
- uint32_t new_circuit_bucket_count;
- uint64_t num_token, elapsed_time_last_refill = 0, circuit_rate = 0;
- time_t now;
- int64_t last_refill_ts;
- tor_assert(stats);
- tor_assert(addr);
- now = approx_time();
- last_refill_ts = (int64_t)stats->last_circ_bucket_refill_ts;
-
- if ((int64_t)now == last_refill_ts) {
- goto done;
- }
-
- circuit_rate = get_circuit_rate_per_second();
-
- if (last_refill_ts == 0) {
- num_token = dos_cc_circuit_burst;
- goto end;
- }
-
- if ((int64_t)now < last_refill_ts) {
-
- num_token = dos_cc_circuit_burst;
- goto end;
- }
-
- elapsed_time_last_refill = (uint64_t)now - last_refill_ts;
-
- if (elapsed_time_last_refill > UINT32_MAX) {
- num_token = dos_cc_circuit_burst;
- goto end;
- }
-
- num_token = elapsed_time_last_refill * circuit_rate;
- end:
-
- if (num_token > UINT32_MAX - stats->circuit_bucket) {
- new_circuit_bucket_count = dos_cc_circuit_burst;
- } else {
-
- new_circuit_bucket_count = MIN(stats->circuit_bucket + (uint32_t)num_token,
- dos_cc_circuit_burst);
- }
-
- tor_assert_nonfatal(new_circuit_bucket_count <= dos_cc_circuit_burst);
-
- tor_assert_nonfatal(new_circuit_bucket_count >= stats->circuit_bucket ||
- new_circuit_bucket_count == dos_cc_circuit_burst);
- log_debug(LD_DOS, "DoS address %s has its circuit bucket value: %" PRIu32
- ". Filling it to %" PRIu32 ". Circuit rate is %" PRIu64
- ". Elapsed time is %" PRIi64,
- fmt_addr(addr), stats->circuit_bucket, new_circuit_bucket_count,
- circuit_rate, (int64_t)elapsed_time_last_refill);
- stats->circuit_bucket = new_circuit_bucket_count;
- stats->last_circ_bucket_refill_ts = now;
- done:
- return;
- }
- 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;
- }
- static void
- cc_mark_client(cc_client_stats_t *stats)
- {
- tor_assert(stats);
-
- stats->marked_until_ts =
- approx_time() + dos_cc_defense_time_period +
- crypto_rand_int_range(1, dos_cc_defense_time_period / 2);
- }
- 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) {
-
- 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;
- }
- static void
- conn_consensus_has_changed(const networkstatus_t *ns)
- {
-
- if (dos_conn_enabled && !get_param_conn_enabled(ns)) {
- conn_free_all();
- }
- }
- 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) {
-
- goto end;
- }
-
-
- cc_stats_refill_bucket(&entry->dos_stats.cc_stats, &addr);
-
- if (entry->dos_stats.cc_stats.circuit_bucket > 0) {
- entry->dos_stats.cc_stats.circuit_bucket--;
- }
-
- if (cc_has_exhausted_circuits(&entry->dos_stats)) {
-
- 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;
- }
- dos_cc_defense_type_t
- dos_cc_get_defense_type(channel_t *chan)
- {
- tor_assert(chan);
-
- if (!dos_cc_enabled) {
- goto end;
- }
-
- if (cc_channel_addr_is_marked(chan)) {
-
- cc_num_rejected_cells++;
- return dos_cc_defense_type;
- }
- end:
- return DOS_CC_DEFENSE_NONE;
- }
- 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;
- }
-
- 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;
- }
- void
- dos_geoip_entry_about_to_free(const clientmap_entry_t *geoip_ent)
- {
- tor_assert(geoip_ent);
-
- if (geoip_ent->dos_stats.concurrent_count == 0) {
- goto end;
- }
-
- 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;
- }
- void
- dos_note_refuse_single_hop_client(void)
- {
- num_single_hop_client_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;
- }
- void
- dos_new_client_conn(or_connection_t *or_conn)
- {
- clientmap_entry_t *entry;
- tor_assert(or_conn);
-
- if (!dos_is_enabled()) {
- goto end;
- }
-
- if (nodelist_probably_contains_address(&or_conn->real_addr)) {
- goto end;
- }
-
- entry = geoip_lookup_client(&or_conn->real_addr, NULL,
- GEOIP_CLIENT_CONNECT);
- if (BUG(entry == NULL)) {
-
- 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);
-
- if (!or_conn->tracked_for_dos_mitigation) {
- goto end;
- }
-
- entry = geoip_lookup_client(&or_conn->real_addr, NULL,
- GEOIP_CLIENT_CONNECT);
- if (entry == NULL) {
-
- goto end;
- }
-
- 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;
- }
- void
- dos_consensus_has_changed(const networkstatus_t *ns)
- {
-
- if (!public_server_mode(get_options())) {
- return;
- }
- cc_consensus_has_changed(ns);
- conn_consensus_has_changed(ns);
-
- set_dos_parameters(ns);
- }
- int
- dos_enabled(void)
- {
- return dos_is_enabled();
- }
- void
- dos_free_all(void)
- {
-
- cc_free_all();
-
- conn_free_all();
- }
- void
- dos_init(void)
- {
-
- set_dos_parameters(NULL);
- }
|