diff --git a/changes/ticket40194 b/changes/ticket40194 new file mode 100644 index 0000000000..b4fcae9cdc --- /dev/null +++ b/changes/ticket40194 @@ -0,0 +1,3 @@ + o Minor feature (relay, metrics): + - Add counters to the MetricsPort how many connections, per type, are + currently opened and how many were created. Part of ticket 40194. diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index 883610d7e1..7c121196a3 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -657,6 +657,9 @@ listener_connection_new(int type, int socket_family) listener_connection_t *listener_conn = tor_malloc_zero(sizeof(listener_connection_t)); connection_init(time(NULL), TO_CONN(listener_conn), type, socket_family); + /* Listener connections aren't accounted for with note_connection() so do + * this explicitly so to count them. */ + rep_hist_note_conn_opened(false, type); return listener_conn; } @@ -1158,6 +1161,9 @@ connection_mark_for_close_internal_, (connection_t *conn, * the number of seconds since last successful write, so * we get our whole 15 seconds */ conn->timestamp_last_write_allowed = time(NULL); + + /* Note the connection close. */ + rep_hist_note_conn_closed(conn->from_listener, conn->type); } /** Find each connection that has hold_open_until_flushed set to @@ -2005,6 +2011,7 @@ connection_handle_listener_read(connection_t *conn, int new_type) log_notice(LD_APP, "Denying socks connection from untrusted address %s.", fmt_and_decorate_addr(&addr)); + rep_hist_note_conn_rejected(new_type); tor_close_socket(news); return 0; } @@ -2014,6 +2021,7 @@ connection_handle_listener_read(connection_t *conn, int new_type) if (dir_policy_permits_address(&addr) == 0) { log_notice(LD_DIRSERV,"Denying dir connection from address %s.", fmt_and_decorate_addr(&addr)); + rep_hist_note_conn_rejected(new_type); tor_close_socket(news); return 0; } @@ -2022,6 +2030,7 @@ connection_handle_listener_read(connection_t *conn, int new_type) /* Assess with the connection DoS mitigation subsystem if this address * can open a new connection. */ if (dos_conn_addr_get_defense_type(&addr) == DOS_CONN_DEFENSE_CLOSE) { + rep_hist_note_conn_rejected(new_type); tor_close_socket(news); return 0; } @@ -2072,6 +2081,9 @@ connection_handle_listener_read(connection_t *conn, int new_type) tor_assert(0); }; + /* We are receiving this connection. */ + newconn->from_listener = 1; + if (connection_add(newconn) < 0) { /* no space, forget it */ connection_free(newconn); return 0; /* no need to tear down the parent */ @@ -2083,7 +2095,7 @@ connection_handle_listener_read(connection_t *conn, int new_type) return 0; } - note_connection(true /* inbound */, conn->socket_family); + note_connection(true /* inbound */, newconn); return 0; } @@ -2262,7 +2274,7 @@ connection_connect_sockaddr,(connection_t *conn, } } - note_connection(false /* outbound */, conn->socket_family); + note_connection(false /* outbound */, conn); /* it succeeded. we're connected. */ log_fn(inprogress ? LOG_DEBUG : LOG_INFO, LD_NET, diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index ea4bf00735..7ba7ecc4c5 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -4206,6 +4206,7 @@ connection_exit_connect(edge_connection_t *edge_conn) log_info(LD_EXIT,"%s failed exit policy%s. Closing.", connection_describe(conn), why_failed_exit_policy); + rep_hist_note_conn_rejected(conn->type); connection_edge_end(edge_conn, END_STREAM_REASON_EXITPOLICY); circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn); connection_free(conn); @@ -4233,6 +4234,7 @@ connection_exit_connect(edge_connection_t *edge_conn) nodelist_reentry_contains(&conn->addr, conn->port)) { log_info(LD_EXIT, "%s tried to connect back to a known relay address. " "Closing.", connection_describe(conn)); + rep_hist_note_conn_rejected(conn->type); connection_edge_end(edge_conn, END_STREAM_REASON_CONNECTREFUSED); circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn); connection_free(conn); diff --git a/src/core/or/connection_st.h b/src/core/or/connection_st.h index 6326afbc6e..d3a230daa0 100644 --- a/src/core/or/connection_st.h +++ b/src/core/or/connection_st.h @@ -87,6 +87,10 @@ struct connection_t { /** True iff we've called connection_close_immediate() on this linked * connection. */ unsigned int linked_conn_is_closed:1; + /** True iff this connection was opened from a listener and thus we've + * recevied this connection. Else, it means we've initiated an outbound + * connection. */ + unsigned int from_listener:1; /** CONNECT/SOCKS proxy client handshake state (for outgoing connections). */ unsigned int proxy_state:4; diff --git a/src/core/or/status.c b/src/core/or/status.c index 1e599aafb3..601c03becc 100644 --- a/src/core/or/status.c +++ b/src/core/or/status.c @@ -30,6 +30,7 @@ #include "app/config/statefile.h" #include "feature/hs/hs_stats.h" #include "feature/hs/hs_service.h" +#include "core/or/connection_st.h" #include "core/or/dos.h" #include "feature/stats/geoip_stats.h" @@ -130,21 +131,23 @@ static unsigned n_outgoing_ipv6; * heartbeat message. **/ void -note_connection(bool inbound, int family) +note_connection(bool inbound, const connection_t *conn) { - if (family == AF_INET) { + if (conn->socket_family == AF_INET) { if (inbound) { ++n_incoming_ipv4; } else { ++n_outgoing_ipv4; } - } else if (family == AF_INET6) { + } else if (conn->socket_family == AF_INET6) { if (inbound) { ++n_incoming_ipv6; } else { ++n_outgoing_ipv6; } } + + rep_hist_note_conn_opened(inbound, conn->type); } /** diff --git a/src/core/or/status.h b/src/core/or/status.h index 57e28002fc..55492d24da 100644 --- a/src/core/or/status.h +++ b/src/core/or/status.h @@ -11,7 +11,7 @@ #include "lib/testsupport/testsupport.h" -void note_connection(bool inbound, int family); +void note_connection(bool inbound, const connection_t *conn); void note_circ_closed_for_unrecognized_cells(time_t n_seconds, uint32_t n_cells); diff --git a/src/feature/relay/relay_metrics.c b/src/feature/relay/relay_metrics.c index 908cfdb0d9..8d0fef86b3 100644 --- a/src/feature/relay/relay_metrics.c +++ b/src/feature/relay/relay_metrics.c @@ -11,6 +11,7 @@ #include "orconfig.h" #include "core/or/or.h" +#include "core/mainloop/connection.h" #include "core/or/relay.h" #include "lib/malloc/malloc.h" @@ -24,6 +25,7 @@ #include /** Declarations of each fill function for metrics defined in base_metrics. */ +static void fill_connections_values(void); static void fill_dns_error_values(void); static void fill_dns_query_values(void); static void fill_global_bw_limit_values(void); @@ -87,6 +89,13 @@ static const relay_metrics_entry_t base_metrics[] = .help = "Total number of times we ran out of TCP ports", .fill_fn = fill_tcp_exhaustion_values, }, + { + .key = RELAY_METRICS_NUM_CONNECTIONS, + .type = METRICS_TYPE_COUNTER, + .name = METRICS_NAME(relay_connections), + .help = "Connections metrics of this relay", + .fill_fn = fill_connections_values, + }, }; static const size_t num_base_metrics = ARRAY_LENGTH(base_metrics); @@ -113,6 +122,62 @@ handshake_type_to_str(const uint16_t type) } } +/** 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, + 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_update(sentry, value); +} + +/** Fill function for the RELAY_METRICS_NUM_CONNECTIONS metric. */ +static void +fill_connections_values(void) +{ + const relay_metrics_entry_t *rentry = + &base_metrics[RELAY_METRICS_NUM_CONNECTIONS]; + + 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", + rep_hist_get_conn_created(false, i)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "received", "created", + rep_hist_get_conn_created(true, i)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "initiated", "opened", + rep_hist_get_conn_opened(false, i)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "received", "opened", + rep_hist_get_conn_opened(true, i)); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help); + fill_single_connection_value(sentry, i, "received", "rejected", + rep_hist_get_conn_rejected(i)); + } +} + /** Fill function for the RELAY_METRICS_NUM_DNS metrics. */ static void fill_tcp_exhaustion_values(void) diff --git a/src/feature/relay/relay_metrics.h b/src/feature/relay/relay_metrics.h index 00dfeaa624..02b92cd043 100644 --- a/src/feature/relay/relay_metrics.h +++ b/src/feature/relay/relay_metrics.h @@ -29,6 +29,8 @@ typedef enum { RELAY_METRICS_NUM_DNS_ERRORS = 5, /** Number of TCP exhaustion reached. */ RELAY_METRICS_NUM_TCP_EXHAUSTION = 6, + /** Number of connections. */ + RELAY_METRICS_NUM_CONNECTIONS = 7, } relay_metrics_key_t; /** The metadata of a relay metric. */ diff --git a/src/feature/stats/rephist.c b/src/feature/stats/rephist.c index 5738259262..553a7f953e 100644 --- a/src/feature/stats/rephist.c +++ b/src/feature/stats/rephist.c @@ -69,6 +69,7 @@ #define REPHIST_PRIVATE #include "core/or/or.h" #include "app/config/config.h" +#include "core/mainloop/connection.h" #include "core/or/circuitlist.h" #include "core/or/connection_or.h" #include "feature/dirauth/authmode.h" @@ -1638,6 +1639,81 @@ rep_hist_note_exit_stream_opened(uint16_t port) log_debug(LD_HIST, "Opened exit stream to port %d", port); } +/******* Connections statistics *******/ + +#define CONN_DIRECTION_INITIATED 0 +#define CONN_DIRECTION_RECEIVED 1 + +#define CONN_DIRECTION(from_listener) \ + (from_listener) ? CONN_DIRECTION_RECEIVED : CONN_DIRECTION_INITIATED + +/** Number of connections created as in seen per direction per type. */ +static uint64_t conn_num_created[2][CONN_TYPE_MAX_]; +/** Number of connections opened per direction per type. */ +static uint64_t conn_num_opened[2][CONN_TYPE_MAX_]; +/** Number of connections rejected per type. Always inbound. */ +static uint64_t conn_num_rejected[CONN_TYPE_MAX_]; + +/** Note that a connection has opened of the given type. */ +void +rep_hist_note_conn_opened(bool from_listener, unsigned int type) +{ + tor_assert(type <= CONN_TYPE_MAX_); + + unsigned int dir = CONN_DIRECTION(from_listener); + + conn_num_created[dir][type]++; + conn_num_opened[dir][type]++; +} + +/** Note that a connection has closed of the given type. */ +void +rep_hist_note_conn_closed(bool from_listener, unsigned int type) +{ + tor_assert(type <= CONN_TYPE_MAX_); + + unsigned int dir = CONN_DIRECTION(from_listener); + + if (conn_num_opened[dir][type] > 0) { + conn_num_opened[dir][type]--; + } +} + +/** Note that a connection has rejected of the given type. */ +void +rep_hist_note_conn_rejected(unsigned int type) +{ + tor_assert(type <= CONN_TYPE_MAX_); + + conn_num_rejected[type]++; +} + +/** Return number of created connections of the given type. */ +uint64_t +rep_hist_get_conn_created(bool from_listener, unsigned int type) +{ + tor_assert(type <= CONN_TYPE_MAX_); + unsigned int dir = CONN_DIRECTION(from_listener); + return conn_num_created[dir][type]; +} + +/** Return number of opened connections of the given type. */ +uint64_t +rep_hist_get_conn_opened(bool from_listener, unsigned int type) +{ + tor_assert(type <= CONN_TYPE_MAX_); + unsigned int dir = CONN_DIRECTION(from_listener); + return conn_num_opened[dir][type]; +} + +/** Return number of opened connections of the given type. */ +uint64_t +rep_hist_get_conn_rejected(unsigned int type) +{ + tor_assert(type <= CONN_TYPE_MAX_); + return conn_num_rejected[type]; +} + /*** cell statistics ***/ /** Start of the current buffer stats interval or 0 if we're not diff --git a/src/feature/stats/rephist.h b/src/feature/stats/rephist.h index e8f43fbb1d..2a83dd185e 100644 --- a/src/feature/stats/rephist.h +++ b/src/feature/stats/rephist.h @@ -41,6 +41,13 @@ void rep_hist_note_exit_bytes(uint16_t port, size_t num_written, size_t num_read); void rep_hist_note_exit_stream_opened(uint16_t port); +void rep_hist_note_conn_opened(bool initiated, unsigned int type); +void rep_hist_note_conn_closed(bool initiated, unsigned int type); +void rep_hist_note_conn_rejected(unsigned int type); +uint64_t rep_hist_get_conn_created(bool initiated, unsigned int type); +uint64_t rep_hist_get_conn_opened(bool initiated, unsigned int type); +uint64_t rep_hist_get_conn_rejected(unsigned int type); + void rep_hist_buffer_stats_init(time_t now); void rep_hist_buffer_stats_add_circ(circuit_t *circ, time_t end_of_interval);