Skip to content

Commit

Permalink
dns: Gather DNS request statistics
Browse files Browse the repository at this point in the history
We now keep track of all errors and total number of request seen. This
is so we can expose those values to the MetricsPort to help Exit
operators monitor the DNS requests and failures.

Related to #40367.

Signed-off-by: David Goulet <[email protected]>
  • Loading branch information
dgoulet-tor committed May 12, 2021
1 parent 897344f commit 423910e
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/feature/relay/dns.c
Original file line number Diff line number Diff line change
Expand Up @@ -1650,6 +1650,10 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
dns_found_answer(string_address, orig_query_type,
result, &addr, hostname, 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_);
}

Expand All @@ -1668,6 +1672,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,
Expand Down
170 changes: 170 additions & 0 deletions src/feature/stats/rephist.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
#include "feature/nodelist/networkstatus_st.h"
#include "core/or/or_circuit_st.h"

#include <event2/dns.h>

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
Expand Down Expand Up @@ -212,6 +214,174 @@ static overload_stats_t overload_stats;
static uint64_t stats_n_read_limit_reached = 0;
static uint64_t stats_n_write_limit_reached = 0;

/***** DNS statistics *****/

/** Represents the statistics of DNS queries seen if it is an Exit. */
typedef struct {
/* Total number of DNS errors found in RFC 1035 (from 0 to 5 code). */
uint64_t stats_n_error_none; /* 0 */
uint64_t stats_n_error_format; /* 1 */
uint64_t stats_n_error_serverfailed; /* 2 */
uint64_t stats_n_error_notexist; /* 3 */
uint64_t stats_n_error_notimpl; /* 4 */
uint64_t stats_n_error_refused; /* 5 */

/* Total number of DNS errors specific to libevent. */
uint64_t stats_n_error_truncated; /* 65 */
uint64_t stats_n_error_unknown; /* 66 */
uint64_t stats_n_error_timeout; /* 67 */
uint64_t stats_n_error_shutdown; /* 68 */
uint64_t stats_n_error_cancel; /* 69 */
uint64_t stats_n_error_nodata; /* 70 */

/* Total number of DNS request seen at an Exit. They might not all end
* successfully or might even be lost by tor. This counter is incremented
* right before the DNS request is initiated. */
uint64_t stats_n_request;
} dns_stats_t;

/** DNS statistics store for each DNS record type for which tor supports only
* three at the moment: A, PTR and AAAA. */
static dns_stats_t dns_A_stats;
static dns_stats_t dns_PTR_stats;
static dns_stats_t dns_AAAA_stats;

/** From a libevent record type, return a pointer to the corresponding DNS
* statistics store. NULL is returned if the type is unhandled. */
static inline dns_stats_t *
get_dns_stats_by_type(const int type)
{
switch (type) {
case DNS_IPv4_A:
return &dns_A_stats;
case DNS_PTR:
return &dns_PTR_stats;
case DNS_IPv6_AAAA:
return &dns_AAAA_stats;
default:
return NULL;
}
}

/** Return the DNS error count for the given libevent DNS type and error code.
* The possible types are: DNS_IPv4_A, DNS_PTR, DNS_IPv6_AAAA. */
uint64_t
rep_hist_get_n_dns_error(int type, uint8_t error)
{
dns_stats_t *dns_stats = get_dns_stats_by_type(type);
if (BUG(!dns_stats)) {
return 0;
}

switch (error) {
case DNS_ERR_NONE:
return dns_stats->stats_n_error_none;
case DNS_ERR_FORMAT:
return dns_stats->stats_n_error_format;
case DNS_ERR_SERVERFAILED:
return dns_stats->stats_n_error_serverfailed;
case DNS_ERR_NOTEXIST:
return dns_stats->stats_n_error_notexist;
case DNS_ERR_NOTIMPL:
return dns_stats->stats_n_error_notimpl;
case DNS_ERR_REFUSED:
return dns_stats->stats_n_error_refused;
case DNS_ERR_TRUNCATED:
return dns_stats->stats_n_error_truncated;
case DNS_ERR_UNKNOWN:
return dns_stats->stats_n_error_unknown;
case DNS_ERR_TIMEOUT:
return dns_stats->stats_n_error_timeout;
case DNS_ERR_SHUTDOWN:
return dns_stats->stats_n_error_shutdown;
case DNS_ERR_CANCEL:
return dns_stats->stats_n_error_cancel;
case DNS_ERR_NODATA:
return dns_stats->stats_n_error_nodata;
default:
/* Unhandled code sent back by libevent. */
return 0;
}
}

/** Return the total number of DNS request seen for the given libevent DNS
* record type. Possible types are: DNS_IPv4_A, DNS_PTR, DNS_IPv6_AAAA. */
uint64_t
rep_hist_get_n_dns_request(int type)
{
dns_stats_t *dns_stats = get_dns_stats_by_type(type);
if (BUG(!dns_stats)) {
return 0;
}
return dns_stats->stats_n_request;
}

/** Note a DNS error for the given given libevent DNS record type and error
* code. Possible types are: DNS_IPv4_A, DNS_PTR, DNS_IPv6_AAAA. */
void
rep_hist_note_dns_error(int type, uint8_t error)
{
dns_stats_t *dns_stats = get_dns_stats_by_type(type);
if (BUG(!dns_stats)) {
return;
}

switch (error) {
case DNS_ERR_NONE:
dns_stats->stats_n_error_none++;
break;
case DNS_ERR_FORMAT:
dns_stats->stats_n_error_format++;
break;
case DNS_ERR_SERVERFAILED:
dns_stats->stats_n_error_serverfailed++;
break;
case DNS_ERR_NOTEXIST:
dns_stats->stats_n_error_notexist++;
break;
case DNS_ERR_NOTIMPL:
dns_stats->stats_n_error_notimpl++;
break;
case DNS_ERR_REFUSED:
dns_stats->stats_n_error_refused++;
break;
case DNS_ERR_TRUNCATED:
dns_stats->stats_n_error_truncated++;
break;
case DNS_ERR_UNKNOWN:
dns_stats->stats_n_error_unknown++;
break;
case DNS_ERR_TIMEOUT:
dns_stats->stats_n_error_timeout++;
break;
case DNS_ERR_SHUTDOWN:
dns_stats->stats_n_error_shutdown++;
break;
case DNS_ERR_CANCEL:
dns_stats->stats_n_error_cancel++;
break;
case DNS_ERR_NODATA:
dns_stats->stats_n_error_nodata++;
break;
default:
/* Unhandled code sent back by libevent. */
break;
}
}

/** Note a DNS request for the given given libevent DNS record type. */
void
rep_hist_note_dns_request(int type)
{
dns_stats_t *dns_stats = get_dns_stats_by_type(type);
if (BUG(!dns_stats)) {
return;
}
dns_stats->stats_n_request++;
}

/***** END of DNS statistics *****/

/** Return true if this overload happened within the last `n_hours`. */
static bool
overload_happened_recently(time_t overload_time, int n_hours)
Expand Down
5 changes: 5 additions & 0 deletions src/feature/stats/rephist.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ void rep_hist_note_negotiated_link_proto(unsigned link_proto,
int started_here);
void rep_hist_log_link_protocol_counts(void);

uint64_t rep_hist_get_n_dns_error(int type, uint8_t error);
uint64_t rep_hist_get_n_dns_request(int type);
void rep_hist_note_dns_request(int type);
void rep_hist_note_dns_error(int type, uint8_t error);

extern uint64_t rephist_total_alloc;
extern uint32_t rephist_total_num;
#ifdef TOR_UNIT_TESTS
Expand Down

0 comments on commit 423910e

Please sign in to comment.