diff options
Diffstat (limited to 'src/feature/relay')
33 files changed, 1535 insertions, 385 deletions
diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index 289a5be557..5b1609a1af 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -392,7 +392,9 @@ circuit_open_connection_for_extend(const struct extend_cell_t *ec, NULL, /*onion_key*/ NULL, /*curve25519_key*/ &chosen_ap->addr, - chosen_ap->port); + chosen_ap->port, + NULL /* protover summary */, + false); circ->n_chan_create_cell = tor_memdup(&ec->create_cell, sizeof(ec->create_cell)); diff --git a/src/feature/relay/circuitbuild_relay.h b/src/feature/relay/circuitbuild_relay.h index dc0b886a34..307825bb5c 100644 --- a/src/feature/relay/circuitbuild_relay.h +++ b/src/feature/relay/circuitbuild_relay.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/dns.c b/src/feature/relay/dns.c index 8b684fd9eb..a38bf5cf5a 100644 --- a/src/feature/relay/dns.c +++ b/src/feature/relay/dns.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -61,8 +61,10 @@ #include "core/or/relay.h" #include "feature/control/control_events.h" #include "feature/relay/dns.h" +#include "feature/nodelist/networkstatus.h" #include "feature/relay/router.h" #include "feature/relay/routermode.h" +#include "feature/stats/rephist.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/evloop/compat_libevent.h" #include "lib/sandbox/sandbox.h" @@ -109,6 +111,7 @@ static int answer_is_wildcarded(const char *ip); static int evdns_err_is_transient(int err); static void inform_pending_connections(cached_resolve_t *resolve); static void make_pending_resolve_cached(cached_resolve_t *cached); +static void configure_libevent_options(void); #ifdef DEBUG_DNS_CACHE static void assert_cache_ok_(void); @@ -211,6 +214,19 @@ evdns_log_cb(int warn, const char *msg) tor_log(severity, LD_EXIT, "eventdns: %s", msg); } +/** New consensus just appeared, take appropriate actions if need be. */ +void +dns_new_consensus_params(const networkstatus_t *ns) +{ + (void) ns; + + /* Consensus has parameters for the Exit relay DNS side and so we only reset + * the DNS nameservers if we are in server mode. */ + if (server_mode(get_options())) { + configure_libevent_options(); + } +} + /** Initialize the DNS subsystem; called by the OR process. */ int dns_init(void) @@ -1352,6 +1368,111 @@ configured_nameserver_address(const size_t idx) } #endif /* defined(HAVE_EVDNS_BASE_GET_NAMESERVER_ADDR) */ +/** Return a pointer to a stack allocated buffer containing the string + * representation of the exit_dns_timeout consensus parameter. */ +static const char * +get_consensus_param_exit_dns_timeout(void) +{ + static char str[4]; + + /* Get the Exit DNS timeout value from the consensus or default. This is in + * milliseconds. */ +#define EXIT_DNS_TIMEOUT_DEFAULT (1000) +#define EXIT_DNS_TIMEOUT_MIN (1) +#define EXIT_DNS_TIMEOUT_MAX (120000) + int32_t val = networkstatus_get_param(NULL, "exit_dns_timeout", + EXIT_DNS_TIMEOUT_DEFAULT, + EXIT_DNS_TIMEOUT_MIN, + EXIT_DNS_TIMEOUT_MAX); + /* NOTE: We convert it to seconds because libevent only supports that. In the + * future, if we support different resolver(s), we might want to specialize + * this call. */ + + /* NOTE: We also don't allow 0 and so we must cap the division to 1 second + * else all DNS request would fail if the consensus would ever tell us a + * value below 1000 (1 sec). */ + val = MAX(1, val / 1000); + + tor_snprintf(str, sizeof(str), "%d", val); + return str; +} + +/** Return a pointer to a stack allocated buffer containing the string + * representation of the exit_dns_num_attempts consensus parameter. */ +static const char * +get_consensus_param_exit_dns_attempts(void) +{ + static char str[4]; + + /* Get the Exit DNS number of attempt value from the consensus or default. */ +#define EXIT_DNS_NUM_ATTEMPTS_DEFAULT (2) +#define EXIT_DNS_NUM_ATTEMPTS_MIN (0) +#define EXIT_DNS_NUM_ATTEMPTS_MAX (255) + int32_t val = networkstatus_get_param(NULL, "exit_dns_num_attempts", + EXIT_DNS_NUM_ATTEMPTS_DEFAULT, + EXIT_DNS_NUM_ATTEMPTS_MIN, + EXIT_DNS_NUM_ATTEMPTS_MAX); + tor_snprintf(str, sizeof(str), "%d", val); + return str; +} + +/** Configure the libevent options. This can safely be called after + * initialization or even if the evdns base is not set. */ +static void +configure_libevent_options(void) +{ + /* This is possible because we can get called when a new consensus is set + * while the DNS subsystem is not initialized just yet. It should be + * harmless. */ + if (!the_evdns_base) { + return; + } + +#define SET(k,v) evdns_base_set_option(the_evdns_base, (k), (v)) + + // If we only have one nameserver, it does not make sense to back off + // from it for a timeout. Unfortunately, the value for max-timeouts is + // currently clamped by libevent to 255, but it does not hurt to set + // it higher in case libevent gets a patch for this. Higher-than- + // default maximum of 3 with multiple nameservers to avoid spuriously + // marking one down on bursts of timeouts resulting from scans/attacks + // against non-responding authoritative DNS servers. + if (evdns_base_count_nameservers(the_evdns_base) == 1) { + SET("max-timeouts:", "1000000"); + } else { + SET("max-timeouts:", "10"); + } + + // Elongate the queue of maximum inflight dns requests, so if a bunch + // remain pending at the resolver (happens commonly with Unbound) we won't + // stall every other DNS request. This potentially means some wasted + // CPU as there's a walk over a linear queue involved, but this is a + // much better tradeoff compared to just failing DNS requests because + // of a full queue. + SET("max-inflight:", "8192"); + + /* Set timeout to be 1 second. This tells libevent that it shouldn't wait + * more than N second to drop a DNS query and consider it "timed out". It is + * very important to differentiate here a libevent timeout and a DNS server + * timeout. And so, by setting this to N second, libevent sends back + * "DNS_ERR_TIMEOUT" if that N second is reached which does NOT indicate that + * the query itself timed out in transit. */ + SET("timeout:", get_consensus_param_exit_dns_timeout()); + + /* This tells libevent to attemps up to X times a DNS query if the previous + * one failed to complete within N second. We believe that this should be + * enough to catch temporary hiccups on the first query. But after that, it + * should signal us that it won't be able to resolve it. */ + SET("attempts:", get_consensus_param_exit_dns_attempts()); + + if (get_options()->ServerDNSRandomizeCase) + SET("randomize-case:", "1"); + else + SET("randomize-case:", "0"); + +#undef SET +} + /** Configure eventdns nameservers if force is true, or if the configuration * has changed since the last time we called this function, or if we failed on * our last attempt. On Unix, this reads from /etc/resolv.conf or @@ -1465,43 +1586,10 @@ configure_nameservers(int force) } #endif /* defined(_WIN32) */ -#define SET(k,v) evdns_base_set_option(the_evdns_base, (k), (v)) - - // If we only have one nameserver, it does not make sense to back off - // from it for a timeout. Unfortunately, the value for max-timeouts is - // currently clamped by libevent to 255, but it does not hurt to set - // it higher in case libevent gets a patch for this. Higher-than- - // default maximum of 3 with multiple nameservers to avoid spuriously - // marking one down on bursts of timeouts resulting from scans/attacks - // against non-responding authoritative DNS servers. - if (evdns_base_count_nameservers(the_evdns_base) == 1) { - SET("max-timeouts:", "1000000"); - } else { - SET("max-timeouts:", "10"); - } - - // Elongate the queue of maximum inflight dns requests, so if a bunch - // remain pending at the resolver (happens commonly with Unbound) we won't - // stall every other DNS request. This potentially means some wasted - // CPU as there's a walk over a linear queue involved, but this is a - // much better tradeoff compared to just failing DNS requests because - // of a full queue. - SET("max-inflight:", "8192"); - - // Two retries at 5 and 10 seconds for bind9/named which relies on - // clients to handle retries. Second retry for retried circuits with - // extended 15 second timeout. Superfluous with local-system Unbound - // instance--has its own elaborate retry scheme. - SET("timeout:", "5"); - SET("attempts:","3"); - - if (options->ServerDNSRandomizeCase) - SET("randomize-case:", "1"); - else - SET("randomize-case:", "0"); - -#undef SET + /* Setup libevent options. */ + configure_libevent_options(); + /* Relaunch periodical DNS check event. */ dns_servers_relaunch_checks(); nameservers_configured = 1; @@ -1639,6 +1727,10 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses, dns_found_answer(string_address, orig_query_type, result, &addr, hostname, clip_dns_fuzzy_ttl(ttl)); + /* The result can be changed within this function thus why we note the result + * at the end. */ + rep_hist_note_dns_error(type, result); + tor_free(arg_); } @@ -1657,6 +1749,9 @@ launch_one_resolve(const char *address, uint8_t query_type, addr[0] = (char) query_type; memcpy(addr+1, address, addr_len + 1); + /* Note the query for our statistics. */ + rep_hist_note_dns_request(query_type); + switch (query_type) { case DNS_IPv4_A: req = evdns_base_resolve_ipv4(the_evdns_base, diff --git a/src/feature/relay/dns.h b/src/feature/relay/dns.h index 120b75bf8d..3f8519bd97 100644 --- a/src/feature/relay/dns.h +++ b/src/feature/relay/dns.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -26,6 +26,7 @@ void dns_reset_correctness_checks(void); size_t dns_cache_total_allocation(void); void dump_dns_mem_usage(int severity); size_t dns_cache_handle_oom(time_t now, size_t min_remove_bytes); +void dns_new_consensus_params(const networkstatus_t *ns); /* These functions are only used within the feature/relay module, and don't * need stubs. */ @@ -47,6 +48,8 @@ void dns_launch_correctness_checks(void); ((void)(severity)) #define dns_cache_handle_oom(now, bytes) \ ((void)(now), (void)(bytes), 0) +#define dns_new_consensus_params(ns) \ + ((void) ns) #define connection_dns_remove(conn) \ STMT_BEGIN \ diff --git a/src/feature/relay/dns_structs.h b/src/feature/relay/dns_structs.h index 27a791b9b3..d153629bf8 100644 --- a/src/feature/relay/dns_structs.h +++ b/src/feature/relay/dns_structs.h @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/ext_orport.c b/src/feature/relay/ext_orport.c index 1bb8741e45..89b287b0b4 100644 --- a/src/feature/relay/ext_orport.c +++ b/src/feature/relay/ext_orport.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2020, The Tor Project, Inc. */ +/* Copyright (c) 2012-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -656,77 +656,6 @@ connection_ext_or_start_auth(or_connection_t *or_conn) return 0; } -/** Global map between Extended ORPort identifiers and OR - * connections. */ -static digestmap_t *orconn_ext_or_id_map = NULL; - -/** Remove the Extended ORPort identifier of <b>conn</b> from the - * global identifier list. Also, clear the identifier from the - * connection itself. */ -void -connection_or_remove_from_ext_or_id_map(or_connection_t *conn) -{ - or_connection_t *tmp; - if (!orconn_ext_or_id_map) - return; - if (!conn->ext_or_conn_id) - return; - - tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id); - if (!tor_digest_is_zero(conn->ext_or_conn_id)) - tor_assert(tmp == conn); - - memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN); -} - -#ifdef TOR_UNIT_TESTS -/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such - * connection is found. */ -or_connection_t * -connection_or_get_by_ext_or_id(const char *id) -{ - if (!orconn_ext_or_id_map) - return NULL; - return digestmap_get(orconn_ext_or_id_map, id); -} -#endif /* defined(TOR_UNIT_TESTS) */ - -/** Deallocate the global Extended ORPort identifier list */ -void -connection_or_clear_ext_or_id_map(void) -{ - digestmap_free(orconn_ext_or_id_map, NULL); - orconn_ext_or_id_map = NULL; -} - -/** Creates an Extended ORPort identifier for <b>conn</b> and deposits - * it into the global list of identifiers. */ -void -connection_or_set_ext_or_identifier(or_connection_t *conn) -{ - char random_id[EXT_OR_CONN_ID_LEN]; - or_connection_t *tmp; - - if (!orconn_ext_or_id_map) - orconn_ext_or_id_map = digestmap_new(); - - /* Remove any previous identifiers: */ - if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id)) - connection_or_remove_from_ext_or_id_map(conn); - - do { - crypto_rand(random_id, sizeof(random_id)); - } while (digestmap_get(orconn_ext_or_id_map, random_id)); - - if (!conn->ext_or_conn_id) - conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN); - - memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN); - - tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn); - tor_assert(!tmp); -} - /** Free any leftover allocated memory of the ext_orport.c subsystem. */ void ext_orport_free_all(void) diff --git a/src/feature/relay/ext_orport.h b/src/feature/relay/ext_orport.h index 416c358397..5a9063d005 100644 --- a/src/feature/relay/ext_orport.h +++ b/src/feature/relay/ext_orport.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -35,9 +35,6 @@ int connection_ext_or_start_auth(or_connection_t *or_conn); -void connection_or_set_ext_or_identifier(or_connection_t *conn); -void connection_or_remove_from_ext_or_id_map(or_connection_t *conn); -void connection_or_clear_ext_or_id_map(void); int connection_ext_or_finished_flushing(or_connection_t *conn); int connection_ext_or_process_inbuf(or_connection_t *or_conn); char *get_ext_or_auth_cookie_file_name(void); @@ -69,13 +66,6 @@ connection_ext_or_process_inbuf(or_connection_t *conn) tor_assert_nonfatal_unreached(); return -1; } -#define connection_or_set_ext_or_identifier(conn) \ - ((void)(conn)) -#define connection_or_remove_from_ext_or_id_map(conn) \ - ((void)(conn)) -#define connection_or_clear_ext_or_id_map() \ - STMT_NIL - #define get_ext_or_auth_cookie_file_name() \ (NULL) @@ -94,7 +84,6 @@ STATIC int handle_client_auth_nonce(const char *client_nonce, #ifdef TOR_UNIT_TESTS extern uint8_t *ext_or_auth_cookie; extern int ext_or_auth_cookie_is_set; -or_connection_t *connection_or_get_by_ext_or_id(const char *id); #endif #endif /* defined(EXT_ORPORT_PRIVATE) */ diff --git a/src/feature/relay/include.am b/src/feature/relay/include.am index 84bb1ff35e..8a121cef01 100644 --- a/src/feature/relay/include.am +++ b/src/feature/relay/include.am @@ -15,6 +15,7 @@ MODULE_RELAY_SOURCES = \ src/feature/relay/routermode.c \ src/feature/relay/relay_config.c \ src/feature/relay/relay_handshake.c \ + src/feature/relay/relay_metrics.c \ src/feature/relay/relay_periodic.c \ src/feature/relay/relay_sys.c \ src/feature/relay/routerkeys.c \ @@ -30,6 +31,7 @@ noinst_HEADERS += \ src/feature/relay/onion_queue.h \ src/feature/relay/relay_config.h \ src/feature/relay/relay_handshake.h \ + src/feature/relay/relay_metrics.h \ src/feature/relay/relay_periodic.h \ src/feature/relay/relay_sys.h \ src/feature/relay/relay_find_addr.h \ diff --git a/src/feature/relay/onion_queue.c b/src/feature/relay/onion_queue.c index 3cbaa65d28..b844aefcd1 100644 --- a/src/feature/relay/onion_queue.c +++ b/src/feature/relay/onion_queue.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -33,39 +33,104 @@ #include "core/or/circuitlist.h" #include "core/or/onion.h" #include "feature/nodelist/networkstatus.h" +#include "feature/stats/rephist.h" #include "core/or/or_circuit_st.h" +#include "core/or/channel.h" + +/** Onion queue default, max and min. */ + +/* In seconds. */ +#define ONION_QUEUE_WAIT_CUTOFF_DEFAULT 5 +#define ONION_QUEUE_WAIT_CUTOFF_MIN 0 +#define ONION_QUEUE_WAIT_CUTOFF_MAX INT32_MAX + +/* In msec. */ +#define ONION_QUEUE_MAX_DELAY_DEFAULT 1750 +#define ONION_QUEUE_MAX_DELAY_MIN 1 +#define ONION_QUEUE_MAX_DELAY_MAX INT32_MAX + +#define NUM_NTORS_PER_TAP_DEFAULT 10 +#define NUM_NTORS_PER_TAP_MIN 1 +#define NUM_NTORS_PER_TAP_MAX 100000 /** Type for a linked list of circuits that are waiting for a free CPU worker * to process a waiting onion handshake. */ typedef struct onion_queue_t { TOR_TAILQ_ENTRY(onion_queue_t) next; or_circuit_t *circ; - uint16_t handshake_type; + uint16_t queue_idx; create_cell_t *onionskin; time_t when_added; } onion_queue_t; -/** 5 seconds on the onion queue til we just send back a destroy */ -#define ONIONQUEUE_WAIT_CUTOFF 5 - TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t); typedef struct onion_queue_head_t onion_queue_head_t; +/** We have 3 queues: tap, fast, and ntor. (ntorv3 goes into ntor queue). */ +#define MAX_QUEUE_IDX ONION_HANDSHAKE_TYPE_NTOR + /** Array of queues of circuits waiting for CPU workers. An element is NULL * if that queue is empty.*/ -static onion_queue_head_t ol_list[MAX_ONION_HANDSHAKE_TYPE+1] = +static onion_queue_head_t ol_list[MAX_QUEUE_IDX+1] = { TOR_TAILQ_HEAD_INITIALIZER(ol_list[0]), /* tap */ TOR_TAILQ_HEAD_INITIALIZER(ol_list[1]), /* fast */ TOR_TAILQ_HEAD_INITIALIZER(ol_list[2]), /* ntor */ }; /** Number of entries of each type currently in each element of ol_list[]. */ -static int ol_entries[MAX_ONION_HANDSHAKE_TYPE+1]; +static int ol_entries[MAX_QUEUE_IDX+1]; -static int num_ntors_per_tap(void); static void onion_queue_entry_remove(onion_queue_t *victim); +/** Consensus parameters. */ +static int32_t ns_num_ntors_per_tap = NUM_NTORS_PER_TAP_DEFAULT; +static time_t ns_onion_queue_wait_cutoff = ONION_QUEUE_WAIT_CUTOFF_DEFAULT; +static uint32_t ns_onion_queue_max_delay = ONION_QUEUE_MAX_DELAY_DEFAULT; + +/** Return the number of ntors per tap from the cached parameter. */ +static inline int32_t +get_num_ntors_per_tap(void) +{ + return ns_num_ntors_per_tap; +} + +/** Return the onion queue wait cutoff value from the cached parameter. */ +static inline time_t +get_onion_queue_wait_cutoff(void) +{ + return ns_onion_queue_wait_cutoff; +} + +/** Return the max onion queue delay value either from the torrc options (if + * the user explicitly set it) else from the cached parameter. */ +static inline uint32_t +get_onion_queue_max_delay(const or_options_t *options) +{ + if (options && options->MaxOnionQueueDelay > 0) { + return options->MaxOnionQueueDelay; + } + return ns_onion_queue_max_delay; +} + +/** + * We combine ntorv3 and ntor into the same queue, so we must + * use this function to covert the cell type to a queue index. + */ +static inline uint16_t +onionskin_type_to_queue(uint16_t type) +{ + if (type == ONION_HANDSHAKE_TYPE_NTOR_V3) { + return ONION_HANDSHAKE_TYPE_NTOR; + } + + if (BUG(type > MAX_QUEUE_IDX)) { + return MAX_QUEUE_IDX; // use ntor if out of range + } + + return type; +} + /* XXXX Check lengths vs MAX_ONIONSKIN_{CHALLENGE,REPLY}_LEN. * * (By which I think I meant, "make sure that no @@ -80,13 +145,22 @@ have_room_for_onionskin(uint16_t type) { const or_options_t *options = get_options(); int num_cpus; + uint64_t max_onion_queue_delay; uint64_t tap_usec, ntor_usec; uint64_t ntor_during_tap_usec, tap_during_ntor_usec; /* If we've got fewer than 50 entries, we always have room for one more. */ if (ol_entries[type] < 50) return 1; - num_cpus = get_num_cpus(options); + + /* If zero, this means our thread pool was never initialized meaning we can't + * really get here but make sure we don't have such value because we are + * using as a divisor. */ + num_cpus = cpuworker_get_n_threads(); + tor_assert(num_cpus > 0); + + max_onion_queue_delay = get_onion_queue_max_delay(options); + /* Compute how many microseconds we'd expect to need to clear all * onionskins in various combinations of the queues. */ @@ -104,32 +178,30 @@ have_room_for_onionskin(uint16_t type) * process while draining the ntor queue? */ tap_during_ntor_usec = estimated_usec_for_onionskins( MIN(ol_entries[ONION_HANDSHAKE_TYPE_TAP], - ol_entries[ONION_HANDSHAKE_TYPE_NTOR] / num_ntors_per_tap()), + ol_entries[ONION_HANDSHAKE_TYPE_NTOR] / get_num_ntors_per_tap()), ONION_HANDSHAKE_TYPE_TAP) / num_cpus; /* How long would it take to process the ntor cells that we expect to * process while draining the tap queue? */ ntor_during_tap_usec = estimated_usec_for_onionskins( MIN(ol_entries[ONION_HANDSHAKE_TYPE_NTOR], - ol_entries[ONION_HANDSHAKE_TYPE_TAP] * num_ntors_per_tap()), + ol_entries[ONION_HANDSHAKE_TYPE_TAP] * get_num_ntors_per_tap()), ONION_HANDSHAKE_TYPE_NTOR) / num_cpus; /* See whether that exceeds MaxOnionQueueDelay. If so, we can't queue * this. */ if (type == ONION_HANDSHAKE_TYPE_NTOR && - (ntor_usec + tap_during_ntor_usec) / 1000 > - (uint64_t)options->MaxOnionQueueDelay) + (ntor_usec + tap_during_ntor_usec) / 1000 > max_onion_queue_delay) return 0; if (type == ONION_HANDSHAKE_TYPE_TAP && - (tap_usec + ntor_during_tap_usec) / 1000 > - (uint64_t)options->MaxOnionQueueDelay) + (tap_usec + ntor_during_tap_usec) / 1000 > max_onion_queue_delay) return 0; /* If we support the ntor handshake, then don't let TAP handshakes use * more than 2/3 of the space on the queue. */ if (type == ONION_HANDSHAKE_TYPE_TAP && - tap_usec / 1000 > (uint64_t)options->MaxOnionQueueDelay * 2 / 3) + tap_usec / 1000 > max_onion_queue_delay * 2 / 3) return 0; return 1; @@ -143,6 +215,7 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin) { onion_queue_t *tmp; time_t now = time(NULL); + uint16_t queue_idx = 0; if (onionskin->handshake_type > MAX_ONION_HANDSHAKE_TYPE) { /* LCOV_EXCL_START @@ -153,43 +226,52 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin) /* LCOV_EXCL_STOP */ } + queue_idx = onionskin_type_to_queue(onionskin->handshake_type); + tmp = tor_malloc_zero(sizeof(onion_queue_t)); tmp->circ = circ; - tmp->handshake_type = onionskin->handshake_type; + tmp->queue_idx = queue_idx; tmp->onionskin = onionskin; tmp->when_added = now; - if (!have_room_for_onionskin(onionskin->handshake_type)) { + if (!have_room_for_onionskin(queue_idx)) { #define WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL (60) static ratelim_t last_warned = RATELIM_INIT(WARN_TOO_MANY_CIRC_CREATIONS_INTERVAL); - char *m; - if (onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR && - (m = rate_limit_log(&last_warned, approx_time()))) { - log_warn(LD_GENERAL, - "Your computer is too slow to handle this many circuit " - "creation requests! Please consider using the " - "MaxAdvertisedBandwidth config option or choosing a more " - "restricted exit policy.%s",m); - tor_free(m); + if (!channel_is_client(circ->p_chan)) { + // Avoid counting create cells from clients, to go with the same + // check in command_process_create_cell(). + rep_hist_note_circuit_handshake_dropped(queue_idx); + } + if (queue_idx == ONION_HANDSHAKE_TYPE_NTOR) { + char *m; + if ((m = rate_limit_log(&last_warned, approx_time()))) { + log_warn(LD_GENERAL, + "Your computer is too slow to handle this many circuit " + "creation requests! Please consider using the " + "MaxAdvertisedBandwidth config option or choosing a more " + "restricted exit policy.%s", + m); + tor_free(m); + } } tor_free(tmp); return -1; } - ++ol_entries[onionskin->handshake_type]; + ++ol_entries[queue_idx]; log_info(LD_OR, "New create (%s). Queues now ntor=%d and tap=%d.", - onionskin->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap", + queue_idx == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap", ol_entries[ONION_HANDSHAKE_TYPE_NTOR], ol_entries[ONION_HANDSHAKE_TYPE_TAP]); circ->onionqueue_entry = tmp; - TOR_TAILQ_INSERT_TAIL(&ol_list[onionskin->handshake_type], tmp, next); + TOR_TAILQ_INSERT_TAIL(&ol_list[queue_idx], tmp, next); /* cull elderly requests. */ while (1) { - onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[onionskin->handshake_type]); - if (now - head->when_added < (time_t)ONIONQUEUE_WAIT_CUTOFF) + onion_queue_t *head = TOR_TAILQ_FIRST(&ol_list[queue_idx]); + if (now - head->when_added < get_onion_queue_wait_cutoff()) break; circ = head->circ; @@ -204,24 +286,6 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin) return 0; } -/** Return a fairness parameter, to prefer processing NTOR style - * handshakes but still slowly drain the TAP queue so we don't starve - * it entirely. */ -static int -num_ntors_per_tap(void) -{ -#define DEFAULT_NUM_NTORS_PER_TAP 10 -#define MIN_NUM_NTORS_PER_TAP 1 -#define MAX_NUM_NTORS_PER_TAP 100000 - - int result = networkstatus_get_param(NULL, "NumNTorsPerTAP", - DEFAULT_NUM_NTORS_PER_TAP, - MIN_NUM_NTORS_PER_TAP, - MAX_NUM_NTORS_PER_TAP); - tor_assert(result > 0); - return result; -} - /** Choose which onion queue we'll pull from next. If one is empty choose * the other; if they both have elements, load balance across them but * favoring NTOR. */ @@ -245,7 +309,7 @@ decide_next_handshake_type(void) * once tap is rare. We should reevaluate whether we like this decision * once tap gets more rare. */ if (ol_entries[ONION_HANDSHAKE_TYPE_NTOR] && - recently_chosen_ntors <= num_ntors_per_tap()) + recently_chosen_ntors <= get_num_ntors_per_tap()) ++recently_chosen_ntors; return ONION_HANDSHAKE_TYPE_NTOR; /* no taps? try ntor */ @@ -253,7 +317,7 @@ decide_next_handshake_type(void) /* They both have something queued. Pick ntor if we haven't done that * too much lately. */ - if (++recently_chosen_ntors <= num_ntors_per_tap()) { + if (++recently_chosen_ntors <= get_num_ntors_per_tap()) { return ONION_HANDSHAKE_TYPE_NTOR; } @@ -276,15 +340,15 @@ onion_next_task(create_cell_t **onionskin_out) return NULL; /* no onions pending, we're done */ tor_assert(head->circ); - tor_assert(head->handshake_type <= MAX_ONION_HANDSHAKE_TYPE); + tor_assert(head->queue_idx <= MAX_QUEUE_IDX); // tor_assert(head->circ->p_chan); /* make sure it's still valid */ /* XXX I only commented out the above line to make the unit tests * more manageable. That's probably not good long-term. -RD */ circ = head->circ; if (head->onionskin) - --ol_entries[head->handshake_type]; + --ol_entries[head->queue_idx]; log_info(LD_OR, "Processing create (%s). Queues now ntor=%d and tap=%d.", - head->handshake_type == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap", + head->queue_idx == ONION_HANDSHAKE_TYPE_NTOR ? "ntor" : "tap", ol_entries[ONION_HANDSHAKE_TYPE_NTOR], ol_entries[ONION_HANDSHAKE_TYPE_TAP]); @@ -300,7 +364,7 @@ onion_next_task(create_cell_t **onionskin_out) int onion_num_pending(uint16_t handshake_type) { - return ol_entries[handshake_type]; + return ol_entries[onionskin_type_to_queue(handshake_type)]; } /** Go through ol_list, find the onion_queue_t element which points to @@ -326,23 +390,23 @@ onion_pending_remove(or_circuit_t *circ) static void onion_queue_entry_remove(onion_queue_t *victim) { - if (victim->handshake_type > MAX_ONION_HANDSHAKE_TYPE) { + if (victim->queue_idx > MAX_QUEUE_IDX) { /* LCOV_EXCL_START * We should have rejected this far before this point */ log_warn(LD_BUG, "Handshake %d out of range! Dropping.", - victim->handshake_type); + victim->queue_idx); /* XXX leaks */ return; /* LCOV_EXCL_STOP */ } - TOR_TAILQ_REMOVE(&ol_list[victim->handshake_type], victim, next); + TOR_TAILQ_REMOVE(&ol_list[victim->queue_idx], victim, next); if (victim->circ) victim->circ->onionqueue_entry = NULL; if (victim->onionskin) - --ol_entries[victim->handshake_type]; + --ol_entries[victim->queue_idx]; tor_free(victim->onionskin); tor_free(victim); @@ -354,7 +418,7 @@ clear_pending_onions(void) { onion_queue_t *victim, *next; int i; - for (i=0; i<=MAX_ONION_HANDSHAKE_TYPE; i++) { + for (i=0; i<=MAX_QUEUE_IDX; i++) { for (victim = TOR_TAILQ_FIRST(&ol_list[i]); victim; victim = next) { next = TOR_TAILQ_NEXT(victim,next); onion_queue_entry_remove(victim); @@ -363,3 +427,28 @@ clear_pending_onions(void) } memset(ol_entries, 0, sizeof(ol_entries)); } + +/** Consensus has changed, update the cached parameters. */ +void +onion_consensus_has_changed(const networkstatus_t *ns) +{ + tor_assert(ns); + + ns_onion_queue_max_delay = + networkstatus_get_param(ns, "MaxOnionQueueDelay", + ONION_QUEUE_MAX_DELAY_DEFAULT, + ONION_QUEUE_MAX_DELAY_MIN, + ONION_QUEUE_MAX_DELAY_MAX); + + ns_onion_queue_wait_cutoff = + networkstatus_get_param(ns, "onion_queue_wait_cutoff", + ONION_QUEUE_WAIT_CUTOFF_DEFAULT, + ONION_QUEUE_WAIT_CUTOFF_MIN, + ONION_QUEUE_WAIT_CUTOFF_MAX); + + ns_num_ntors_per_tap = + networkstatus_get_param(ns, "NumNTorsPerTAP", + NUM_NTORS_PER_TAP_DEFAULT, + NUM_NTORS_PER_TAP_MIN, + NUM_NTORS_PER_TAP_MAX); +} diff --git a/src/feature/relay/onion_queue.h b/src/feature/relay/onion_queue.h index 08379b2c00..0c2b97c2b0 100644 --- a/src/feature/relay/onion_queue.h +++ b/src/feature/relay/onion_queue.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -14,6 +14,8 @@ struct create_cell_t; +void onion_consensus_has_changed(const networkstatus_t *ns); + int onion_pending_add(or_circuit_t *circ, struct create_cell_t *onionskin); or_circuit_t *onion_next_task(struct create_cell_t **onionskin_out); int onion_num_pending(uint16_t handshake_type); diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c index 8ea0ad8397..85ccfc18a7 100644 --- a/src/feature/relay/relay_config.c +++ b/src/feature/relay/relay_config.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -190,7 +190,7 @@ describe_relay_port(const port_cfg_t *port) /** Return true iff port p1 is equal to p2. * - * This does a field by field comparaison. */ + * This does a field by field comparison. */ static bool port_cfg_eq(const port_cfg_t *p1, const port_cfg_t *p2) { diff --git a/src/feature/relay/relay_config.h b/src/feature/relay/relay_config.h index d36863a1a1..cb08531782 100644 --- a/src/feature/relay/relay_config.h +++ b/src/feature/relay/relay_config.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -93,7 +93,7 @@ STATIC int have_enough_mem_for_dircache(const struct or_options_t *options, struct port_cfg_t; STATIC const char *describe_relay_port(const struct port_cfg_t *port); -#endif /* TOR_UNIT_TESTS */ +#endif /* defined(TOR_UNIT_TESTS) */ #endif /* defined(RELAY_CONFIG_PRIVATE) */ diff --git a/src/feature/relay/relay_find_addr.c b/src/feature/relay/relay_find_addr.c index c43885af51..f4f9d40823 100644 --- a/src/feature/relay/relay_find_addr.c +++ b/src/feature/relay/relay_find_addr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2001-2020, The Tor Project, Inc. */ +/* Copyright (c) 2001-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -221,7 +221,7 @@ relay_addr_learn_from_dirauth(void) "learn for now our address from them."); return; } - extend_info_t *ei = extend_info_from_node(node, 1); + extend_info_t *ei = extend_info_from_node(node, 1, false); if (BUG(!ei)) { return; } diff --git a/src/feature/relay/relay_find_addr.h b/src/feature/relay/relay_find_addr.h index f049d1bd20..5bb7f8736e 100644 --- a/src/feature/relay/relay_find_addr.h +++ b/src/feature/relay/relay_find_addr.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2020, The Tor Project, Inc. */ +/* Copyright (c) 2020-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -28,5 +28,5 @@ void relay_addr_learn_from_dirauth(void); #endif /* RELAY_FIND_ADDR_PRIVATE */ -#endif /* TOR_RELAY_FIND_ADDR_H */ +#endif /* !defined(TOR_RELAY_FIND_ADDR_H) */ diff --git a/src/feature/relay/relay_handshake.c b/src/feature/relay/relay_handshake.c index 030dc94956..be7dba721a 100644 --- a/src/feature/relay/relay_handshake.c +++ b/src/feature/relay/relay_handshake.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/relay_handshake.h b/src/feature/relay/relay_handshake.h index 99a658cbcc..87199c1c2d 100644 --- a/src/feature/relay/relay_handshake.h +++ b/src/feature/relay/relay_handshake.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/relay_metrics.c b/src/feature/relay/relay_metrics.c new file mode 100644 index 0000000000..cdf34a3404 --- /dev/null +++ b/src/feature/relay/relay_metrics.c @@ -0,0 +1,1058 @@ +/* Copyright (c) 2021, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file relay_metrics.c + * @brief Relay metrics exposed through the MetricsPort + **/ + +#define RELAY_METRICS_ENTRY_PRIVATE + +#include "orconfig.h" + +#include "core/or/or.h" +#include "core/mainloop/connection.h" +#include "core/mainloop/mainloop.h" +#include "core/or/congestion_control_common.h" +#include "core/or/congestion_control_vegas.h" +#include "core/or/congestion_control_flow.h" +#include "core/or/circuitlist.h" +#include "core/or/dos.h" +#include "core/or/relay.h" + +#include "app/config/config.h" + +#include "lib/container/smartlist.h" +#include "lib/log/util_bug.h" +#include "lib/malloc/malloc.h" +#include "lib/math/fp.h" +#include "lib/metrics/metrics_store.h" + +#include "feature/hs/hs_dos.h" +#include "feature/nodelist/nodelist.h" +#include "feature/nodelist/node_st.h" +#include "feature/nodelist/routerstatus_st.h" +#include "feature/relay/relay_metrics.h" +#include "feature/relay/router.h" +#include "feature/stats/rephist.h" + +#include <event2/dns.h> + +/** Declarations of each fill function for metrics defined in base_metrics. */ +static void fill_cc_counters_values(void); +static void fill_cc_gauges_values(void); +static void fill_circuits_values(void); +static void fill_conn_counter_values(void); +static void fill_conn_gauge_values(void); +static void fill_dns_error_values(void); +static void fill_dns_query_values(void); +static void fill_dos_values(void); +static void fill_global_bw_limit_values(void); +static void fill_socket_values(void); +static void fill_onionskins_values(void); +static void fill_oom_values(void); +static void fill_streams_values(void); +static void fill_relay_flags(void); +static void fill_tcp_exhaustion_values(void); +static void fill_traffic_values(void); + +/** The base metrics that is a static array of metrics added to the metrics + * store. + * + * The key member MUST be also the index of the entry in the array. */ +static const relay_metrics_entry_t base_metrics[] = +{ + { + .key = RELAY_METRICS_NUM_OOM_BYTES, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_load_oom_bytes_total), + .help = "Total number of bytes the OOM has freed by subsystem", + .fill_fn = fill_oom_values, + }, + { + .key = RELAY_METRICS_NUM_ONIONSKINS, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_load_onionskins_total), + .help = "Total number of onionskins handled", + .fill_fn = fill_onionskins_values, + }, + { + .key = RELAY_METRICS_NUM_SOCKETS, + .type = METRICS_TYPE_GAUGE, + .name = METRICS_NAME(relay_load_socket_total), + .help = "Total number of sockets", + .fill_fn = fill_socket_values, + }, + { + .key = RELAY_METRICS_NUM_GLOBAL_RW_LIMIT, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_load_global_rate_limit_reached_total), + .help = "Total number of global connection bucket limit reached", + .fill_fn = fill_global_bw_limit_values, + }, + { + .key = RELAY_METRICS_NUM_DNS, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_exit_dns_query_total), + .help = "Total number of DNS queries done by this relay", + .fill_fn = fill_dns_query_values, + }, + { + .key = RELAY_METRICS_NUM_DNS_ERRORS, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_exit_dns_error_total), + .help = "Total number of DNS errors encountered by this relay", + .fill_fn = fill_dns_error_values, + }, + { + .key = RELAY_METRICS_NUM_TCP_EXHAUSTION, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_load_tcp_exhaustion_total), + .help = "Total number of times we ran out of TCP ports", + .fill_fn = fill_tcp_exhaustion_values, + }, + { + .key = RELAY_METRICS_CONN_COUNTERS, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_connections_total), + .help = "Total number of created/rejected connections", + .fill_fn = fill_conn_counter_values, + }, + { + .key = RELAY_METRICS_CONN_GAUGES, + .type = METRICS_TYPE_GAUGE, + .name = METRICS_NAME(relay_connections), + .help = "Total number of opened connections", + .fill_fn = fill_conn_gauge_values, + }, + { + .key = RELAY_METRICS_NUM_STREAMS, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_streams_total), + .help = "Total number of streams", + .fill_fn = fill_streams_values, + }, + { + .key = RELAY_METRICS_CC_COUNTERS, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_congestion_control_total), + .help = "Congestion control related counters", + .fill_fn = fill_cc_counters_values, + }, + { + .key = RELAY_METRICS_CC_GAUGES, + .type = METRICS_TYPE_GAUGE, + .name = METRICS_NAME(relay_congestion_control), + .help = "Congestion control related gauges", + .fill_fn = fill_cc_gauges_values, + }, + { + .key = RELAY_METRICS_NUM_DOS, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_dos_total), + .help = "Denial of Service defenses related counters", + .fill_fn = fill_dos_values, + }, + { + .key = RELAY_METRICS_NUM_TRAFFIC, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_traffic_bytes), + .help = "Traffic related counters", + .fill_fn = fill_traffic_values, + }, + { + .key = RELAY_METRICS_RELAY_FLAGS, + .type = METRICS_TYPE_GAUGE, + .name = METRICS_NAME(relay_flag), + .help = "Relay flags from consensus", + .fill_fn = fill_relay_flags, + }, + { + .key = RELAY_METRICS_NUM_CIRCUITS, + .type = METRICS_TYPE_GAUGE, + .name = METRICS_NAME(relay_circuits_total), + .help = "Total number of circuits", + .fill_fn = fill_circuits_values, + }, +}; +static const size_t num_base_metrics = ARRAY_LENGTH(base_metrics); + +/** The only and single store of all the relay metrics. */ +static metrics_store_t *the_store; + +/** Helper function to convert an handshake type into a string. */ +static inline const char * +handshake_type_to_str(const uint16_t type) +{ + switch (type) { + case ONION_HANDSHAKE_TYPE_TAP: + return "tap"; + case ONION_HANDSHAKE_TYPE_FAST: + return "fast"; + case ONION_HANDSHAKE_TYPE_NTOR: + return "ntor"; + case ONION_HANDSHAKE_TYPE_NTOR_V3: + return "ntor_v3"; + default: + // LCOV_EXCL_START + tor_assert_unreached(); + // LCOV_EXCL_STOP + } +} + +/** Helper function to convert a socket family type into a string. */ +static inline const char * +af_to_string(const int af) +{ + switch (af) { + case AF_INET: + return "ipv4"; + case AF_INET6: + return "ipv6"; + case AF_UNIX: + return "unix"; + default: + return "<unknown>"; + } +} + +/** Fill function for the RELAY_METRICS_NUM_CIRCUITS metric. */ +static void +fill_circuits_values(void) +{ + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_NUM_CIRCUITS]; + metrics_store_entry_t *sentry = + metrics_store_add(the_store, rentry->type, rentry->name, rentry->help); + + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "opened")); + metrics_store_entry_update(sentry, + smartlist_len(circuit_get_global_list())); +} + +/** Fill function for the RELAY_METRICS_RELAY_FLAGS metric. */ +static void +fill_relay_flags(void) +{ + uint8_t is_fast = 0, is_exit = 0, is_authority = 0, is_stable = 0; + uint8_t is_running = 0, is_v2_dir = 0, is_guard = 0, is_sybil = 0; + uint8_t is_hs_dir = 0; + + const node_t *me = + node_get_by_id((const char *) router_get_my_id_digest()); + if (me && me->rs) { + is_fast = me->rs->is_fast; + is_exit = me->rs->is_exit; + is_authority = me->rs->is_authority; + is_stable = me->rs->is_stable; + is_running = me->rs->is_flagged_running; + is_v2_dir = me->rs->is_v2_dir; + is_guard = me->rs->is_possible_guard; + is_sybil = me->rs->is_sybil; + is_hs_dir = me->rs->is_hs_dir; + } + + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_RELAY_FLAGS]; + metrics_store_entry_t *sentry = + metrics_store_add(the_store, rentry->type, rentry->name, rentry->help); + + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "Fast")); + metrics_store_entry_update(sentry, is_fast); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "Exit")); + metrics_store_entry_update(sentry, is_exit); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "Authority")); + metrics_store_entry_update(sentry, is_authority); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "Stable")); + metrics_store_entry_update(sentry, is_stable); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "HSDir")); + metrics_store_entry_update(sentry, is_hs_dir); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "Running")); + metrics_store_entry_update(sentry, is_running); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "V2Dir")); + metrics_store_entry_update(sentry, is_v2_dir); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "Sybil")); + metrics_store_entry_update(sentry, is_sybil); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "Guard")); + metrics_store_entry_update(sentry, is_guard); +} + +/** Fill function for the RELAY_METRICS_NUM_TRAFFIC metric. */ +static void +fill_traffic_values(void) +{ + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_NUM_TRAFFIC]; + metrics_store_entry_t *sentry = + metrics_store_add(the_store, rentry->type, rentry->name, rentry->help); + + metrics_store_entry_add_label(sentry, + metrics_format_label("direction", "read")); + metrics_store_entry_update(sentry, get_bytes_read()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("direction", "written")); + metrics_store_entry_update(sentry, get_bytes_written()); +} + +/** Fill function for the RELAY_METRICS_NUM_DOS metric. */ +static void +fill_dos_values(void) +{ + const relay_metrics_entry_t *rentry = &base_metrics[RELAY_METRICS_NUM_DOS]; + metrics_store_entry_t *sentry = + metrics_store_add(the_store, rentry->type, rentry->name, rentry->help); + + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "circuit_rejected")); + metrics_store_entry_update(sentry, dos_get_num_cc_rejected()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "circuit_killed_max_cell")); + metrics_store_entry_update(sentry, stats_n_circ_max_cell_reached); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "circuit_killed_max_cell_outq")); + metrics_store_entry_update(sentry, stats_n_circ_max_cell_outq_reached); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "marked_address")); + metrics_store_entry_update(sentry, dos_get_num_cc_marked_addr()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "marked_address_maxq")); + metrics_store_entry_update(sentry, dos_get_num_cc_marked_addr_maxq()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "conn_rejected")); + metrics_store_entry_update(sentry, dos_get_num_conn_addr_connect_rejected()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "concurrent_conn_rejected")); + metrics_store_entry_update(sentry, dos_get_num_conn_addr_rejected()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "single_hop_refused")); + metrics_store_entry_update(sentry, dos_get_num_single_hop_refused()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "introduce2_rejected")); + metrics_store_entry_update(sentry, hs_dos_get_intro2_rejected_count()); +} + +/** Fill function for the RELAY_METRICS_CC_COUNTERS metric. */ +static void +fill_cc_counters_values(void) +{ + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_CC_COUNTERS]; + + metrics_store_entry_t *sentry = + metrics_store_add(the_store, rentry->type, rentry->name, rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "starvation")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "rtt_reset")); + metrics_store_entry_update(sentry, congestion_control_get_num_rtt_reset()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "clock_stalls")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "rtt_skipped")); + metrics_store_entry_update(sentry, + congestion_control_get_num_clock_stalls()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "flow_control")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "xoff_num_sent")); + metrics_store_entry_update(sentry, + cc_stats_flow_num_xoff_sent); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "flow_control")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "xon_num_sent")); + metrics_store_entry_update(sentry, + cc_stats_flow_num_xon_sent); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_limits")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "above_delta")); + metrics_store_entry_update(sentry, cc_stats_vegas_above_delta); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_limits")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "above_ss_cwnd_max")); + metrics_store_entry_update(sentry, cc_stats_vegas_above_ss_cwnd_max); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_limits")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "below_ss_inc_floor")); + metrics_store_entry_update(sentry, cc_stats_vegas_below_ss_inc_floor); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_circuits")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "circs_created")); + metrics_store_entry_update(sentry, cc_stats_circs_created); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_circuits")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "circs_closed")); + metrics_store_entry_update(sentry, cc_stats_circs_closed); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_circuits")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "circs_exited_ss")); + metrics_store_entry_update(sentry, cc_stats_vegas_circ_exited_ss); +} + +/** Fill function for the RELAY_METRICS_CC_GAUGES metric. */ +static void +fill_cc_gauges_values(void) +{ + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_CC_GAUGES]; + + metrics_store_entry_t *sentry = + metrics_store_add(the_store, rentry->type, rentry->name, rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "slow_start_exit")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "cwnd")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_exit_ss_cwnd_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "slow_start_exit")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "bdp")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_exit_ss_bdp_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "slow_start_exit")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "inc")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_exit_ss_inc_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "on_circ_close")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "cwnd")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_circ_close_cwnd_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "on_circ_close")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "ss_cwnd")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_circ_close_ss_cwnd_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "buffers")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "xon_outbuf")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_flow_xon_outbuf_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "buffers")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "xoff_outbuf")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_flow_xoff_outbuf_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_backoff")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "chan_blocked_pct")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_csig_blocked_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_backoff")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "gamma_drop")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_gamma_drop_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_backoff")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "delta_drop")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_delta_drop_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_backoff")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "ss_chan_blocked_pct")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_ss_csig_blocked_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_cwnd_update")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "alpha_pct")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_csig_alpha_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_cwnd_update")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "beta_pct")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_csig_beta_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_cwnd_update")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "delta_pct")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_csig_delta_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_estimates")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "ss_queue")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_ss_queue_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_estimates")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "queue")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_queue_ma)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "cc_estimates")); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "bdp")); + metrics_store_entry_update(sentry, + tor_llround(cc_stats_vegas_bdp_ma)); +} + +/** Helper: Fill in single stream metrics output. */ +static void +fill_single_stream_value(metrics_store_entry_t *sentry, uint8_t cmd) +{ + metrics_store_entry_add_label(sentry, + metrics_format_label("type", relay_command_to_string(cmd))); + metrics_store_entry_update(sentry, rep_hist_get_exit_stream_seen(cmd)); +} + +/** Fill function for the RELAY_METRICS_NUM_STREAMS metric. */ +static void +fill_streams_values(void) +{ + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_NUM_STREAMS]; + metrics_store_entry_t *sentry = + metrics_store_add(the_store, rentry->type, rentry->name, rentry->help); + fill_single_stream_value(sentry, RELAY_COMMAND_BEGIN); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_stream_value(sentry, RELAY_COMMAND_BEGIN_DIR); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_stream_value(sentry, RELAY_COMMAND_RESOLVE); +} + +/** Helper: Fill in single connection metrics output. */ +static void +fill_single_connection_value(metrics_store_entry_t *sentry, + unsigned int conn_type, + const char* direction, + const char* state, + int socket_family, + uint64_t value) +{ + metrics_store_entry_add_label(sentry, + metrics_format_label("type", conn_type_to_string(conn_type))); + metrics_store_entry_add_label(sentry, + metrics_format_label("direction", direction)); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", state)); + metrics_store_entry_add_label(sentry, + metrics_format_label("family", af_to_string(socket_family))); + metrics_store_entry_update(sentry, value); +} + +/** Fill function for the RELAY_METRICS_CONN_COUNTERS metric. */ +static void +fill_conn_counter_values(void) +{ + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_CONN_COUNTERS]; + + for (unsigned int i = CONN_TYPE_MIN_; i < CONN_TYPE_MAX_ ; i++) { + /* Type is unused. Ugly but else we clobber the output. */ + if (i == 10) { + continue; + } + metrics_store_entry_t *sentry = + metrics_store_add(the_store, rentry->type, rentry->name, rentry->help); + fill_single_connection_value(sentry, i, "initiated", "created", AF_INET, + rep_hist_get_conn_created(false, i, AF_INET)); + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "initiated", "created", AF_INET6, + rep_hist_get_conn_created(false, i, + AF_INET6)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "received", "created", AF_INET, + rep_hist_get_conn_created(true, i, AF_INET)); + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "received", "created", AF_INET6, + rep_hist_get_conn_created(true, i, AF_INET6)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "received", "rejected", AF_INET, + rep_hist_get_conn_rejected(i, AF_INET)); + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "received", "rejected", AF_INET6, + rep_hist_get_conn_rejected(i, AF_INET6)); + + /* No counter for "initiated" + "rejected" connections exists. */ + } +} + +/** Fill function for the RELAY_METRICS_CONN_GAUGES metric. */ +static void +fill_conn_gauge_values(void) +{ + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_CONN_GAUGES]; + + for (unsigned int i = CONN_TYPE_MIN_; i < CONN_TYPE_MAX_ ; i++) { + /* Type is unused. Ugly but else we clobber the output. */ + if (i == 10) { + continue; + } + metrics_store_entry_t *sentry = + metrics_store_add(the_store, rentry->type, rentry->name, rentry->help); + fill_single_connection_value(sentry, i, "initiated", "opened", AF_INET, + rep_hist_get_conn_opened(false, i, AF_INET)); + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "initiated", "opened", AF_INET6, + rep_hist_get_conn_opened(false, i, AF_INET6)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "received", "opened", AF_INET, + rep_hist_get_conn_opened(true, i, AF_INET)); + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "received", "opened", AF_INET6, + rep_hist_get_conn_opened(true, i, AF_INET6)); + } +} + +/** Fill function for the RELAY_METRICS_NUM_DNS metrics. */ +static void +fill_tcp_exhaustion_values(void) +{ + metrics_store_entry_t *sentry; + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_NUM_TCP_EXHAUSTION]; + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_update(sentry, rep_hist_get_n_tcp_exhaustion()); +} + +/* NOTE: Disable the record type label until libevent is fixed. */ +#if 0 +/** Helper array containing mapping for the name of the different DNS records + * and their corresponding libevent values. */ +static struct dns_type { + const char *name; + uint8_t type; +} dns_types[] = { + { .name = "A", .type = DNS_IPv4_A }, + { .name = "PTR", .type = DNS_PTR }, + { .name = "AAAA", .type = DNS_IPv6_AAAA }, +}; +static const size_t num_dns_types = ARRAY_LENGTH(dns_types); +#endif + +/** Fill function for the RELAY_METRICS_NUM_DNS_ERRORS metrics. */ +static void +fill_dns_error_values(void) +{ + metrics_store_entry_t *sentry; + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_NUM_DNS_ERRORS]; + + /* Helper array to map libeven DNS errors to their names and so we can + * iterate over this array to add all metrics. */ + static struct dns_error { + const char *name; + uint8_t key; + } errors[] = { + { .name = "success", .key = DNS_ERR_NONE }, + { .name = "format", .key = DNS_ERR_FORMAT }, + { .name = "serverfailed", .key = DNS_ERR_SERVERFAILED }, + { .name = "notexist", .key = DNS_ERR_NOTEXIST }, + { .name = "notimpl", .key = DNS_ERR_NOTIMPL }, + { .name = "refused", .key = DNS_ERR_REFUSED }, + { .name = "truncated", .key = DNS_ERR_TRUNCATED }, + { .name = "unknown", .key = DNS_ERR_UNKNOWN }, + { .name = "tor_timeout", .key = DNS_ERR_TIMEOUT }, + { .name = "shutdown", .key = DNS_ERR_SHUTDOWN }, + { .name = "cancel", .key = DNS_ERR_CANCEL }, + { .name = "nodata", .key = DNS_ERR_NODATA }, + }; + static const size_t num_errors = ARRAY_LENGTH(errors); + + /* NOTE: Disable the record type label until libevent is fixed. */ +#if 0 + for (size_t i = 0; i < num_dns_types; i++) { + /* Dup the label because metrics_format_label() returns a pointer to a + * string on the stack and we need that label for all metrics. */ + char *record_label = + tor_strdup(metrics_format_label("record", dns_types[i].name)); + + for (size_t j = 0; j < num_errors; j++) { + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, record_label); + metrics_store_entry_add_label(sentry, + metrics_format_label("reason", errors[j].name)); + metrics_store_entry_update(sentry, + rep_hist_get_n_dns_error(dns_types[i].type, errors[j].key)); + } + tor_free(record_label); + } +#endif + + /* Put in the DNS errors, unfortunately not per-type for now. */ + for (size_t j = 0; j < num_errors; j++) { + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("reason", errors[j].name)); + metrics_store_entry_update(sentry, + rep_hist_get_n_dns_error(0, errors[j].key)); + } +} + +/** Fill function for the RELAY_METRICS_NUM_DNS metrics. */ +static void +fill_dns_query_values(void) +{ + metrics_store_entry_t *sentry; + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_NUM_DNS]; + + /* NOTE: Disable the record type label until libevent is fixed (#40490). */ +#if 0 + for (size_t i = 0; i < num_dns_types; i++) { + /* Dup the label because metrics_format_label() returns a pointer to a + * string on the stack and we need that label for all metrics. */ + char *record_label = + tor_strdup(metrics_format_label("record", dns_types[i].name)); + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, record_label); + metrics_store_entry_update(sentry, + rep_hist_get_n_dns_request(dns_types[i].type)); + tor_free(record_label); + } +#endif + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_update(sentry, rep_hist_get_n_dns_request(0)); +} + +/** Fill function for the RELAY_METRICS_NUM_GLOBAL_RW_LIMIT metrics. */ +static void +fill_global_bw_limit_values(void) +{ + metrics_store_entry_t *sentry; + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_NUM_GLOBAL_RW_LIMIT]; + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("side", "read")); + metrics_store_entry_update(sentry, rep_hist_get_n_read_limit_reached()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("side", "write")); + metrics_store_entry_update(sentry, rep_hist_get_n_write_limit_reached()); +} + +/** Fill function for the RELAY_METRICS_NUM_SOCKETS metrics. */ +static void +fill_socket_values(void) +{ + metrics_store_entry_t *sentry; + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_NUM_SOCKETS]; + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("state", "opened")); + metrics_store_entry_update(sentry, get_n_open_sockets()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_update(sentry, get_max_sockets()); +} + +/** Fill function for the RELAY_METRICS_NUM_ONIONSKINS metrics. */ +static void +fill_onionskins_values(void) +{ + metrics_store_entry_t *sentry; + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_NUM_ONIONSKINS]; + + for (uint16_t t = 0; t <= MAX_ONION_HANDSHAKE_TYPE; t++) { + /* Dup the label because metrics_format_label() returns a pointer to a + * string on the stack and we need that label for all metrics. */ + char *type_label = + tor_strdup(metrics_format_label("type", handshake_type_to_str(t))); + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, type_label); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "processed")); + metrics_store_entry_update(sentry, + rep_hist_get_circuit_n_handshake_assigned(t)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, type_label); + metrics_store_entry_add_label(sentry, + metrics_format_label("action", "dropped")); + metrics_store_entry_update(sentry, + rep_hist_get_circuit_n_handshake_dropped(t)); + tor_free(type_label); + } +} + +/** Fill function for the RELAY_METRICS_NUM_OOM_BYTES metrics. */ +static void +fill_oom_values(void) +{ + metrics_store_entry_t *sentry; + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_NUM_OOM_BYTES]; + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("subsys", "cell")); + metrics_store_entry_update(sentry, oom_stats_n_bytes_removed_cell); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("subsys", "dns")); + metrics_store_entry_update(sentry, oom_stats_n_bytes_removed_dns); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("subsys", "geoip")); + metrics_store_entry_update(sentry, oom_stats_n_bytes_removed_geoip); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + metrics_store_entry_add_label(sentry, + metrics_format_label("subsys", "hsdir")); + metrics_store_entry_update(sentry, oom_stats_n_bytes_removed_hsdir); +} + +/** Reset the global store and fill it with all the metrics from base_metrics + * and their associated values. + * + * To pull this off, every metrics has a "fill" function that is called and in + * charge of adding the metrics to the store, appropriate labels and finally + * updating the value to report. */ +static void +fill_store(void) +{ + /* Reset the current store, we are about to fill it with all the things. */ + metrics_store_reset(the_store); + + /* Call the fill function for each metrics. */ + for (size_t i = 0; i < num_base_metrics; i++) { + if (BUG(!base_metrics[i].fill_fn)) { + continue; + } + base_metrics[i].fill_fn(); + } +} + +/** Return a list of all the relay metrics stores. This is the + * function attached to the .get_metrics() member of the subsys_t. */ +const smartlist_t * +relay_metrics_get_stores(void) +{ + /* We can't have the caller to free the returned list so keep it static, + * simply update it. */ + static smartlist_t *stores_list = NULL; + + /* We dynamically fill the store with all the metrics upon a request. The + * reason for this is because the exposed metrics of a relay are often + * internal counters in the fast path and thus we fetch the value when a + * metrics port request arrives instead of keeping a local metrics store of + * those values. */ + fill_store(); + + if (!stores_list) { + stores_list = smartlist_new(); + smartlist_add(stores_list, the_store); + } + + return stores_list; +} + +/** Initialize the relay metrics. */ +void +relay_metrics_init(void) +{ + if (BUG(the_store)) { + return; + } + the_store = metrics_store_new(); +} + +/** Free the relay metrics. */ +void +relay_metrics_free(void) +{ + if (!the_store) { + return; + } + /* NULL is set with this call. */ + metrics_store_free(the_store); +} diff --git a/src/feature/relay/relay_metrics.h b/src/feature/relay/relay_metrics.h new file mode 100644 index 0000000000..1d2d649d8a --- /dev/null +++ b/src/feature/relay/relay_metrics.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2021, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file relay_metrics.h + * @brief Header for feature/relay/relay_metrics.c + **/ + +#ifndef TOR_FEATURE_RELAY_RELAY_METRICS_H +#define TOR_FEATURE_RELAY_RELAY_METRICS_H + +#include "lib/container/smartlist.h" +#include "lib/metrics/metrics_common.h" + +/** Metrics key for each reported metrics. This key is also used as an index in + * the base_metrics array. */ +typedef enum { + /** Number of OOM invocation. */ + RELAY_METRICS_NUM_OOM_BYTES, + /** Number of onionskines handled. */ + RELAY_METRICS_NUM_ONIONSKINS, + /** Number of sockets. */ + RELAY_METRICS_NUM_SOCKETS, + /** Number of global connection rate limit. */ + RELAY_METRICS_NUM_GLOBAL_RW_LIMIT, + /** Number of DNS queries. */ + RELAY_METRICS_NUM_DNS, + /** Number of DNS query errors. */ + RELAY_METRICS_NUM_DNS_ERRORS, + /** Number of TCP exhaustion reached. */ + RELAY_METRICS_NUM_TCP_EXHAUSTION, + /** Connections counters (always going up). */ + RELAY_METRICS_CONN_COUNTERS, + /** Connections gauges. */ + RELAY_METRICS_CONN_GAUGES, + /** Number of streams. */ + RELAY_METRICS_NUM_STREAMS, + /** Congestion control counters. */ + RELAY_METRICS_CC_COUNTERS, + /** Congestion control gauges. */ + RELAY_METRICS_CC_GAUGES, + /** Denial of Service defenses subsystem. */ + RELAY_METRICS_NUM_DOS, + /** Denial of Service defenses subsystem. */ + RELAY_METRICS_NUM_TRAFFIC, + /** Relay flags. */ + RELAY_METRICS_RELAY_FLAGS, + /** Numer of circuits. */ + RELAY_METRICS_NUM_CIRCUITS, +} relay_metrics_key_t; + +/** The metadata of a relay metric. */ +typedef struct relay_metrics_entry_t { + /* Metric key used as a static array index. */ + relay_metrics_key_t key; + /* Metric type. */ + metrics_type_t type; + /* Metrics output name. */ + const char *name; + /* Metrics output help comment. */ + const char *help; + /* Update value function. */ + void (*fill_fn)(void); +} relay_metrics_entry_t; + +/* Init. */ +void relay_metrics_init(void); +void relay_metrics_free(void); + +/* Accessors. */ +const smartlist_t *relay_metrics_get_stores(void); + +#endif /* !defined(TOR_FEATURE_RELAY_RELAY_METRICS_H) */ diff --git a/src/feature/relay/relay_periodic.c b/src/feature/relay/relay_periodic.c index a917d90f1a..dd9be4e36f 100644 --- a/src/feature/relay/relay_periodic.c +++ b/src/feature/relay/relay_periodic.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -164,9 +164,7 @@ check_for_reachability_bw_callback(time_t now, const or_options_t *options) (have_completed_a_circuit() || !any_predicted_circuits(now)) && !net_is_disabled()) { if (get_uptime() < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { - router_do_reachability_checks(1, dirport_reachability_count==0); - if (++dirport_reachability_count > 5) - dirport_reachability_count = 0; + router_do_reachability_checks(); return EARLY_CHECK_REACHABILITY_INTERVAL; } else { /* If we haven't checked for 12 hours and our bandwidth estimate is @@ -221,7 +219,7 @@ reachability_warnings_callback(time_t now, const or_options_t *options) tor_asprintf(&where4, "%s:%d", address4, me->ipv4_orport); if (!v6_ok) tor_asprintf(&where6, "[%s]:%d", address6, me->ipv6_orport); - const char *opt_and = (!v4_ok && !v6_ok) ? "and" : ""; + const char *opt_and = (!v4_ok && !v6_ok) ? " and " : ""; /* IPv4 reachability test worked but not the IPv6. We will _not_ * publish the descriptor if our IPv6 was configured. We will if it @@ -264,20 +262,6 @@ reachability_warnings_callback(time_t now, const or_options_t *options) tor_free(address4); tor_free(address6); } - - if (me && !router_dirport_seems_reachable(options)) { - char *address4 = tor_addr_to_str_dup(&me->ipv4_addr); - log_warn(LD_CONFIG, - "Your server (%s:%d) has not managed to confirm that its " - "DirPort is reachable. Relays do not publish descriptors " - "until their ORPort and DirPort are reachable. Please check " - "your firewalls, ports, address, /etc/hosts file, etc.", - address4, me->ipv4_dirport); - control_event_server_status(LOG_WARN, - "REACHABILITY_FAILED DIRADDRESS=%s:%d", - address4, me->ipv4_dirport); - tor_free(address4); - } } return TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT; diff --git a/src/feature/relay/relay_periodic.h b/src/feature/relay/relay_periodic.h index ccda9a440b..d3a13ec835 100644 --- a/src/feature/relay/relay_periodic.h +++ b/src/feature/relay/relay_periodic.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/relay_stub.c b/src/feature/relay/relay_stub.c index 283aaf6e49..c7ac9093fa 100644 --- a/src/feature/relay/relay_stub.c +++ b/src/feature/relay/relay_stub.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/relay_sys.c b/src/feature/relay/relay_sys.c index 2e90740925..9c43734b84 100644 --- a/src/feature/relay/relay_sys.c +++ b/src/feature/relay/relay_sys.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -14,6 +14,7 @@ #include "feature/relay/dns.h" #include "feature/relay/ext_orport.h" +#include "feature/relay/relay_metrics.h" #include "feature/relay/onion_queue.h" #include "feature/relay/relay_periodic.h" #include "feature/relay/relay_sys.h" @@ -25,6 +26,7 @@ static int subsys_relay_initialize(void) { + relay_metrics_init(); relay_register_periodic_events(); return 0; } @@ -37,6 +39,7 @@ subsys_relay_shutdown(void) clear_pending_onions(); routerkeys_free_all(); router_free_all(); + relay_metrics_free(); } const struct subsys_fns_t sys_relay = { @@ -46,4 +49,6 @@ const struct subsys_fns_t sys_relay = { .level = RELAY_SUBSYS_LEVEL, .initialize = subsys_relay_initialize, .shutdown = subsys_relay_shutdown, + + .get_metrics = relay_metrics_get_stores, }; diff --git a/src/feature/relay/relay_sys.h b/src/feature/relay/relay_sys.h index 9bad93a6c9..2c5edb53dd 100644 --- a/src/feature/relay/relay_sys.h +++ b/src/feature/relay/relay_sys.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index fb26309dfa..bc98fd985c 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ROUTER_PRIVATE @@ -1357,8 +1357,8 @@ decide_to_advertise_dir_impl(const or_options_t *options, int router_should_advertise_dirport(const or_options_t *options, uint16_t dir_port) { - /* supports_tunnelled_dir_requests is not relevant, pass 0 */ - return decide_to_advertise_dir_impl(options, dir_port, 0) ? dir_port : 0; + /* Only authorities should advertise a DirPort now. */ + return authdir_mode(options) ? dir_port : 0; } /** Front-end to decide_to_advertise_dir_impl(): return 0 if we don't want to @@ -3062,6 +3062,15 @@ router_dump_router_to_string(routerinfo_t *router, smartlist_add_strdup(chunks, "tunnelled-dir-server\n"); } + /* Overload general information. */ + if (options->OverloadStatistics) { + char *overload_general = rep_hist_get_overload_general_line(); + + if (overload_general) { + smartlist_add(chunks, overload_general); + } + } + /* Sign the descriptor with Ed25519 */ if (emit_ed_sigs) { smartlist_add_strdup(chunks, "router-sig-ed25519 "); @@ -3345,6 +3354,11 @@ extrainfo_dump_to_string_stats_helper(smartlist_t *chunks, "hidserv-stats-end", now, &contents) > 0) { smartlist_add(chunks, contents); } + if (options->HiddenServiceStatistics && + load_stats_file("stats"PATH_SEPARATOR"hidserv-v3-stats", + "hidserv-v3-stats-end", now, &contents) > 0) { + smartlist_add(chunks, contents); + } if (options->EntryStatistics && load_stats_file("stats"PATH_SEPARATOR"entry-stats", "entry-stats-end", now, &contents) > 0) { @@ -3370,6 +3384,12 @@ extrainfo_dump_to_string_stats_helper(smartlist_t *chunks, if (contents) smartlist_add(chunks, contents); } + if (options->OverloadStatistics) { + contents = rep_hist_get_overload_stats_lines(); + if (contents) { + smartlist_add(chunks, contents); + } + } /* bridge statistics */ if (should_record_bridge_info(options)) { const char *bridge_stats = geoip_get_bridge_stats_extrainfo(now); diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h index 9556a66e68..b5b5a1fffa 100644 --- a/src/feature/relay/router.h +++ b/src/feature/relay/router.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/routerkeys.c b/src/feature/relay/routerkeys.c index 116f0b4e3d..64ec38ed19 100644 --- a/src/feature/relay/routerkeys.c +++ b/src/feature/relay/routerkeys.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2020, The Tor Project, Inc. */ +/* Copyright (c) 2014-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/routerkeys.h b/src/feature/relay/routerkeys.h index 1fb5d724e9..7b6d80773c 100644 --- a/src/feature/relay/routerkeys.h +++ b/src/feature/relay/routerkeys.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2020, The Tor Project, Inc. */ +/* Copyright (c) 2014-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/routermode.c b/src/feature/relay/routermode.c index c4d8792b5b..15f66de8ba 100644 --- a/src/feature/relay/routermode.c +++ b/src/feature/relay/routermode.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/routermode.h b/src/feature/relay/routermode.h index 6d7404968d..2c22c23c0f 100644 --- a/src/feature/relay/routermode.h +++ b/src/feature/relay/routermode.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/selftest.c b/src/feature/relay/selftest.c index 46b4b20ffc..399b6bca6e 100644 --- a/src/feature/relay/selftest.c +++ b/src/feature/relay/selftest.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -49,15 +49,12 @@ static bool have_orport_for_family(int family); static void inform_testing_reachability(const tor_addr_t *addr, - uint16_t port, - bool is_dirport); + uint16_t port); /** Whether we can reach our IPv4 ORPort from the outside. */ static bool can_reach_or_port_ipv4 = false; /** Whether we can reach our IPv6 ORPort from the outside. */ static bool can_reach_or_port_ipv6 = false; -/** Whether we can reach our DirPort from the outside. */ -static bool can_reach_dir_port = false; /** Has informed_testing_reachable logged a message about testing our IPv4 * ORPort? */ @@ -65,18 +62,14 @@ static bool have_informed_testing_or_port_ipv4 = false; /** Has informed_testing_reachable logged a message about testing our IPv6 * ORPort? */ static bool have_informed_testing_or_port_ipv6 = false; -/** Has informed_testing_reachable logged a message about testing our - * DirPort? */ -static bool have_informed_testing_dir_port = false; /** Forget what we have learned about our reachability status. */ void router_reset_reachability(void) { - can_reach_or_port_ipv4 = can_reach_or_port_ipv6 = can_reach_dir_port = false; + can_reach_or_port_ipv4 = can_reach_or_port_ipv6 = false; have_informed_testing_or_port_ipv4 = - have_informed_testing_or_port_ipv6 = - have_informed_testing_dir_port = false; + have_informed_testing_or_port_ipv6 = false; } /** Return 1 if we won't do reachability checks, because: @@ -137,31 +130,20 @@ router_orport_seems_reachable(const or_options_t *options, return true; } -/** Return 0 if we need to do a DirPort reachability check, because: - * - no reachability check has been done yet, or - * - we've initiated reachability checks, but none have succeeded. - * Return 1 if we don't need to do a DirPort reachability check, because: - * - we've seen a successful reachability check, or - * - there is no DirPort set, or - * - AssumeReachable is set, or - * - We're a dir auth (see ticket #40287), or - * - the network is disabled. - */ +/** Relay DirPorts are no longer used (though authorities are). In either case, + * reachability self test is done anymore, since network re-entry towards an + * authority DirPort is not allowed. Thus, consider it always reachable. */ int router_dirport_seems_reachable(const or_options_t *options) { - int reach_checks_disabled = router_reachability_checks_disabled(options) || - authdir_mode(options) || - !options->DirPort_set; - return reach_checks_disabled || - can_reach_dir_port; + (void) options; + return 1; } -/** See if we currently believe our ORPort or DirPort to be - * unreachable. If so, return 1 else return 0. - */ +/** See if we currently believe our ORPort to be unreachable. If so, return 1 + * else return 0. */ static int -router_should_check_reachability(int test_or, int test_dir) +router_should_check_reachability(void) { const routerinfo_t *me = router_get_my_routerinfo(); const or_options_t *options = get_options(); @@ -174,15 +156,13 @@ router_should_check_reachability(int test_or, int test_dir) options->StrictNodes) { /* If we've excluded ourself, and StrictNodes is set, we can't test * ourself. */ - if (test_or || test_dir) { #define SELF_EXCLUDED_WARN_INTERVAL 3600 - static ratelim_t warning_limit=RATELIM_INIT(SELF_EXCLUDED_WARN_INTERVAL); - log_fn_ratelim(&warning_limit, LOG_WARN, LD_CIRC, - "Can't perform self-tests for this relay: we have " - "listed ourself in ExcludeNodes, and StrictNodes is set. " - "We cannot learn whether we are usable, and will not " - "be able to advertise ourself."); - } + static ratelim_t warning_limit=RATELIM_INIT(SELF_EXCLUDED_WARN_INTERVAL); + log_fn_ratelim(&warning_limit, LOG_WARN, LD_CIRC, + "Can't perform self-tests for this relay: we have " + "listed ourself in ExcludeNodes, and StrictNodes is set. " + "We cannot learn whether we are usable, and will not " + "be able to advertise ourself."); return 0; } return 1; @@ -248,7 +228,10 @@ extend_info_from_router(const routerinfo_t *r, int family) info = extend_info_new(r->nickname, r->cache_info.identity_digest, ed_id_key, rsa_pubkey, r->onion_curve25519_pkey, - &ap.addr, ap.port); + &ap.addr, ap.port, + /* TODO-324: Should self-test circuits use + * congestion control? */ + NULL, false); crypto_pk_free(rsa_pubkey); return info; } @@ -274,6 +257,11 @@ router_do_orport_reachability_checks(const routerinfo_t *me, if (ei) { const char *family_name = fmt_af_family(family); const tor_addr_port_t *ap = extend_info_get_orport(ei, family); + if (BUG(!ap)) { + /* Not much we can do here to recover apart from screaming loudly. */ + extend_info_free(ei); + return; + } log_info(LD_CIRC, "Testing %s of my %s ORPort: %s.", !orport_reachable ? "reachability" : "bandwidth", family_name, fmt_addrport_ap(ap)); @@ -281,8 +269,8 @@ router_do_orport_reachability_checks(const routerinfo_t *me, if (!orport_reachable) { /* Only log if we are actually doing a reachability test to learn if our * ORPort is reachable. Else, this prints a log notice if we are simply - * opening a bandwidth testing circuit even do we are reachable. */ - inform_testing_reachability(&ap->addr, ap->port, false); + * opening a bandwidth testing circuit even though we are reachable. */ + inform_testing_reachability(&ap->addr, ap->port); } circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, @@ -293,53 +281,15 @@ router_do_orport_reachability_checks(const routerinfo_t *me, } } -/** Launch a self-testing circuit, and ask an exit to connect to our DirPort. - * <b>me</b> is our own routerinfo. +/** Some time has passed, or we just got new directory information. See if we + * currently believe our ORPort to be unreachable. If so, launch a new test + * for it. * - * Relays don't advertise IPv6 DirPorts, so this function only supports IPv4. - * - * See router_do_reachability_checks() for details. */ -static void -router_do_dirport_reachability_checks(const routerinfo_t *me) -{ - tor_addr_port_t my_dirport; - tor_addr_copy(&my_dirport.addr, &me->ipv4_addr); - my_dirport.port = me->ipv4_dirport; - - /* If there is already a pending connection, don't open another one. */ - if (!connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, - &my_dirport.addr, my_dirport.port, - DIR_PURPOSE_FETCH_SERVERDESC)) { - /* ask myself, via tor, for my server descriptor. */ - directory_request_t *req = - directory_request_new(DIR_PURPOSE_FETCH_SERVERDESC); - directory_request_set_dir_addr_port(req, &my_dirport); - directory_request_set_directory_id_digest(req, - me->cache_info.identity_digest); - /* ask via an anon circuit, connecting to our dirport. */ - directory_request_set_indirection(req, DIRIND_ANON_DIRPORT); - directory_request_set_resource(req, "authority.z"); - directory_initiate_request(req); - directory_request_free(req); - - inform_testing_reachability(&my_dirport.addr, my_dirport.port, true); - } -} - -/** Some time has passed, or we just got new directory information. - * See if we currently believe our ORPort or DirPort to be - * unreachable. If so, launch a new test for it. - * - * For ORPort, we simply try making a circuit that ends at ourselves. - * Success is noticed in onionskin_answer(). - * - * For DirPort, we make a connection via Tor to our DirPort and ask - * for our own server descriptor. - * Success is noticed in connection_dir_client_reached_eof(). + * For ORPort, we simply try making a circuit that ends at ourselves. Success + * is noticed in onionskin_answer(). */ void -router_do_reachability_checks(int test_or, int test_dir) +router_do_reachability_checks(void) { const routerinfo_t *me = router_get_my_routerinfo(); const or_options_t *options = get_options(); @@ -348,45 +298,34 @@ router_do_reachability_checks(int test_or, int test_dir) int orport_reachable_v6 = router_orport_seems_reachable(options, AF_INET6); - if (router_should_check_reachability(test_or, test_dir)) { + if (router_should_check_reachability()) { bool need_testing = !circuit_enough_testing_circs(); /* At the moment, tor relays believe that they are reachable when they * receive any create cell on an inbound connection, if the address * family is correct. */ - if (test_or && (!orport_reachable_v4 || need_testing)) { + if (!orport_reachable_v4 || need_testing) { router_do_orport_reachability_checks(me, AF_INET, orport_reachable_v4); } - if (test_or && (!orport_reachable_v6 || need_testing)) { + if (!orport_reachable_v6 || need_testing) { router_do_orport_reachability_checks(me, AF_INET6, orport_reachable_v6); } - - if (test_dir && !router_dirport_seems_reachable(options)) { - router_do_dirport_reachability_checks(me); - } } } /** Log a message informing the user that we are testing a port for * reachability, if we have not already logged such a message. * - * If @a is_dirport is true, then the port is a DirPort; otherwise it is an - * ORPort. - * * Calls to router_reset_reachability() will reset our view of whether we have * logged this message for a given port. */ static void -inform_testing_reachability(const tor_addr_t *addr, - uint16_t port, - bool is_dirport) +inform_testing_reachability(const tor_addr_t *addr, uint16_t port) { if (!router_get_my_routerinfo()) return; bool *have_informed_ptr; - if (is_dirport) { - have_informed_ptr = &have_informed_testing_dir_port; - } else if (tor_addr_family(addr) == AF_INET) { + if (tor_addr_family(addr) == AF_INET) { have_informed_ptr = &have_informed_testing_or_port_ipv4; } else { have_informed_ptr = &have_informed_testing_or_port_ipv6; @@ -401,18 +340,16 @@ inform_testing_reachability(const tor_addr_t *addr, char addr_buf[TOR_ADDRPORT_BUF_LEN]; strlcpy(addr_buf, fmt_addrport(addr, port), sizeof(addr_buf)); - const char *control_addr_type = is_dirport ? "DIRADDRESS" : "ORADDRESS"; - const char *port_type = is_dirport ? "DirPort" : "ORPort"; const char *afname = fmt_af_family(tor_addr_family(addr)); control_event_server_status(LOG_NOTICE, - "CHECKING_REACHABILITY %s=%s", - control_addr_type, addr_buf); + "CHECKING_REACHABILITY ORADDRESS=%s", + addr_buf); - log_notice(LD_OR, "Now checking whether %s %s %s is reachable... " + log_notice(LD_OR, "Now checking whether %s ORPort %s is reachable... " "(this may take up to %d minutes -- look for log " "messages indicating success)", - afname, port_type, addr_buf, + afname, addr_buf, TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT/60); *have_informed_ptr = true; @@ -426,8 +363,7 @@ static bool ready_to_publish(const or_options_t *options) { return options->PublishServerDescriptor_ != NO_DIRINFO && - router_dirport_seems_reachable(options) && - router_all_orports_seem_reachable(options); + router_all_orports_seem_reachable(options); } /** Annotate that we found our ORPort reachable with a given address @@ -481,40 +417,6 @@ router_orport_found_reachable(int family) } } -/** Annotate that we found our DirPort reachable. */ -void -router_dirport_found_reachable(void) -{ - const routerinfo_t *me = router_get_my_routerinfo(); - const or_options_t *options = get_options(); - - if (!can_reach_dir_port && me) { - char *address = tor_addr_to_str_dup(&me->ipv4_addr); - - if (!address) - return; - - can_reach_dir_port = true; - log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable " - "from the outside. Excellent.%s", - ready_to_publish(options) ? - " Publishing server descriptor." : ""); - - if (router_should_advertise_dirport(options, me->ipv4_dirport)) { - mark_my_descriptor_dirty("DirPort found reachable"); - /* This is a significant enough change to upload immediately, - * at least in a test network */ - if (options->TestingTorNetwork == 1) { - reschedule_descriptor_update_check(); - } - } - control_event_server_status(LOG_NOTICE, - "REACHABILITY_SUCCEEDED DIRADDRESS=%s:%d", - address, me->ipv4_dirport); - tor_free(address); - } -} - /** We have enough testing circuits open. Send a bunch of "drop" * cells down each of them, to exercise our bandwidth. * @@ -530,8 +432,8 @@ router_perform_bandwidth_test(int num_circs, time_t now) origin_circuit_t *circ = NULL; log_notice(LD_OR,"Performing bandwidth self-test...done."); - while ((circ = circuit_get_next_by_pk_and_purpose(circ, NULL, - CIRCUIT_PURPOSE_TESTING))) { + while ((circ = circuit_get_next_by_purpose(circ, + CIRCUIT_PURPOSE_TESTING))) { /* dump cells_per_circuit drop cells onto this circ */ int i = cells_per_circuit; if (circ->base_.state != CIRCUIT_STATE_OPEN) diff --git a/src/feature/relay/selftest.h b/src/feature/relay/selftest.h index e09c0e7898..b662fe0fb0 100644 --- a/src/feature/relay/selftest.h +++ b/src/feature/relay/selftest.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -23,11 +23,10 @@ int router_orport_seems_reachable( int router_dirport_seems_reachable( const struct or_options_t *options); -void router_do_reachability_checks(int test_or, int test_dir); +void router_do_reachability_checks(void); void router_perform_bandwidth_test(int num_circs, time_t now); void router_orport_found_reachable(int family); -void router_dirport_found_reachable(void); void router_reset_reachability(void); @@ -41,10 +40,8 @@ void router_reset_reachability(void); ((void)(opts), 0) static inline void -router_do_reachability_checks(int test_or, int test_dir) +router_do_reachability_checks(void) { - (void)test_or; - (void)test_dir; tor_assert_nonfatal_unreached(); } static inline void @@ -55,16 +52,16 @@ router_perform_bandwidth_test(int num_circs, time_t now) tor_assert_nonfatal_unreached(); } static inline int -inform_testing_reachability(void) +inform_testing_reachability(const tor_addr_t *addr, uint16_t port) { + (void) addr; + (void) port; tor_assert_nonfatal_unreached(); return 0; } #define router_orport_found_reachable() \ STMT_NIL -#define router_dirport_found_reachable() \ - STMT_NIL #define router_reset_reachability() \ STMT_NIL diff --git a/src/feature/relay/transport_config.c b/src/feature/relay/transport_config.c index 7dcce70e30..23e024fbee 100644 --- a/src/feature/relay/transport_config.c +++ b/src/feature/relay/transport_config.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/relay/transport_config.h b/src/feature/relay/transport_config.h index 6d956d9af1..6cf3142fb0 100644 --- a/src/feature/relay/transport_config.h +++ b/src/feature/relay/transport_config.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2020, The Tor Project, Inc. */ + * Copyright (c) 2007-2021, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** |