From a150ff5e4e2481eb28d6ed6e0d3e176623e25f5a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 23 Dec 2015 19:06:36 +0100 Subject: [PATCH] resolved: gather statistics about resolved names This collects statistical data about transactions, dnssec verifications and the cache, and exposes it over the bus. The systemd-resolve-host tool learns new options to query these statistics and reset them. --- src/resolve-host/resolve-host.c | 251 +++++++++++++++++++++---- src/resolve/resolved-bus.c | 85 +++++++++ src/resolve/resolved-dns-cache.c | 27 ++- src/resolve/resolved-dns-cache.h | 4 + src/resolve/resolved-dns-transaction.c | 17 ++ src/resolve/resolved-manager.h | 3 + 6 files changed, 346 insertions(+), 41 deletions(-) diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 4752992cba..67c93c2618 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -33,6 +33,7 @@ #include "parse-util.h" #include "resolved-def.h" #include "resolved-dns-packet.h" +#include "terminal-util.h" #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC) @@ -42,7 +43,14 @@ static uint16_t arg_type = 0; static uint16_t arg_class = 0; static bool arg_legend = true; static uint64_t arg_flags = 0; -static bool arg_resolve_service = false; + +static enum { + MODE_RESOLVE_HOST, + MODE_RESOLVE_RECORD, + MODE_RESOLVE_SERVICE, + MODE_STATISTICS, + MODE_RESET_STATISTICS, +} arg_mode = MODE_RESOLVE_HOST; static void print_source(uint64_t flags, usec_t rtt) { char rtt_str[FORMAT_TIMESTAMP_MAX]; @@ -637,6 +645,121 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons return 0; } +static int show_statistics(sd_bus *bus) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + uint64_t n_current_transactions, n_total_transactions, + cache_size, n_cache_hit, n_cache_miss, + n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate; + int r; + + assert(bus); + + r = sd_bus_get_property(bus, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "TransactionStatistics", + &error, + &reply, + "(tt)"); + if (r < 0) + return log_error_errno(r, "Failed to get transaction statistics: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "(tt)", + &n_current_transactions, + &n_total_transactions); + if (r < 0) + return bus_log_parse_error(r); + + printf("%sTransactions%s\n" + "Current Transactions: %" PRIu64 "\n" + " Total Transactions: %" PRIu64 "\n", + ansi_highlight(), + ansi_normal(), + n_current_transactions, + n_total_transactions); + + reply = sd_bus_message_unref(reply); + + r = sd_bus_get_property(bus, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "CacheStatistics", + &error, + &reply, + "(ttt)"); + + r = sd_bus_message_read(reply, "(ttt)", + &cache_size, + &n_cache_hit, + &n_cache_miss); + if (r < 0) + return bus_log_parse_error(r); + + printf("\n%sCache%s\n" + " Current Cache Size: %" PRIu64 "\n" + " Cache Hits: %" PRIu64 "\n" + " Cache Misses: %" PRIu64 "\n", + ansi_highlight(), + ansi_normal(), + cache_size, + n_cache_hit, + n_cache_miss); + + reply = sd_bus_message_unref(reply); + + r = sd_bus_get_property(bus, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "DNSSECStatistics", + &error, + &reply, + "(tttt)"); + + r = sd_bus_message_read(reply, "(tttt)", + &n_dnssec_secure, + &n_dnssec_insecure, + &n_dnssec_bogus, + &n_dnssec_indeterminate); + if (r < 0) + return bus_log_parse_error(r); + + printf("\n%sDNSSEC%s\n" + " Secure RRsets: %" PRIu64 "\n" + " Insecure RRsets: %" PRIu64 "\n" + " Bogus RRsets: %" PRIu64 "\n" + "Indeterminate RRsets: %" PRIu64 "\n", + ansi_highlight(), + ansi_normal(), + n_dnssec_secure, + n_dnssec_insecure, + n_dnssec_bogus, + n_dnssec_indeterminate); + + return 0; +} + +static int reset_statistics(sd_bus *bus) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + r = sd_bus_call_method(bus, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "ResetStatistics", + &error, + NULL, + NULL); + if (r < 0) + return log_error_errno(r, "Failed to reset statistics: %s", bus_error_message(&error, r)); + + return 0; +} + static void help_dns_types(void) { int i; const char *t; @@ -681,6 +804,8 @@ static void help(void) { " --cname=BOOL Do [not] follow CNAME redirects\n" " --search=BOOL Do [not] use search domains\n" " --legend=BOOL Do [not] print column headers\n" + " --statistics Show resolver statistics\n" + " --reset-statistics Reset resolver statistics\n" , program_invocation_short_name, program_invocation_short_name); } @@ -693,20 +818,24 @@ static int parse_argv(int argc, char *argv[]) { ARG_SERVICE_ADDRESS, ARG_SERVICE_TXT, ARG_SEARCH, + ARG_STATISTICS, + ARG_RESET_STATISTICS, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "type", required_argument, NULL, 't' }, - { "class", required_argument, NULL, 'c' }, - { "legend", required_argument, NULL, ARG_LEGEND }, - { "protocol", required_argument, NULL, 'p' }, - { "cname", required_argument, NULL, ARG_CNAME }, - { "service", no_argument, NULL, ARG_SERVICE }, - { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS }, - { "service-txt", required_argument, NULL, ARG_SERVICE_TXT }, - { "search", required_argument, NULL, ARG_SEARCH }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "type", required_argument, NULL, 't' }, + { "class", required_argument, NULL, 'c' }, + { "legend", required_argument, NULL, ARG_LEGEND }, + { "protocol", required_argument, NULL, 'p' }, + { "cname", required_argument, NULL, ARG_CNAME }, + { "service", no_argument, NULL, ARG_SERVICE }, + { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS }, + { "service-txt", required_argument, NULL, ARG_SERVICE_TXT }, + { "search", required_argument, NULL, ARG_SEARCH }, + { "statistics", no_argument, NULL, ARG_STATISTICS, }, + { "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS }, {} }; @@ -763,6 +892,7 @@ static int parse_argv(int argc, char *argv[]) { arg_type = (uint16_t) r; assert((int) arg_type == r); + arg_mode = MODE_RESOLVE_RECORD; break; case 'c': @@ -806,7 +936,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_SERVICE: - arg_resolve_service = true; + arg_mode = MODE_RESOLVE_SERVICE; break; case ARG_CNAME: @@ -849,6 +979,14 @@ static int parse_argv(int argc, char *argv[]) { arg_flags &= ~SD_RESOLVED_NO_SEARCH; break; + case ARG_STATISTICS: + arg_mode = MODE_STATISTICS; + break; + + case ARG_RESET_STATISTICS: + arg_mode = MODE_RESET_STATISTICS; + break; + case '?': return -EINVAL; @@ -861,7 +999,7 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_type != 0 && arg_resolve_service) { + if (arg_type != 0 && arg_mode != MODE_RESOLVE_RECORD) { log_error("--service and --type= may not be combined."); return -EINVAL; } @@ -883,20 +1021,57 @@ int main(int argc, char **argv) { if (r <= 0) goto finish; - if (optind >= argc) { - log_error("No arguments passed"); - r = -EINVAL; - goto finish; - } - r = sd_bus_open_system(&bus); if (r < 0) { log_error_errno(r, "sd_bus_open_system: %m"); goto finish; } - if (arg_resolve_service) { + switch (arg_mode) { + case MODE_RESOLVE_HOST: + if (optind >= argc) { + log_error("No arguments passed"); + r = -EINVAL; + goto finish; + } + + while (argv[optind]) { + int family, ifindex, k; + union in_addr_union a; + + k = parse_address(argv[optind], &family, &a, &ifindex); + if (k >= 0) + k = resolve_address(bus, family, &a, ifindex); + else + k = resolve_host(bus, argv[optind]); + + if (r == 0) + r = k; + + optind++; + } + break; + + case MODE_RESOLVE_RECORD: + if (optind >= argc) { + log_error("No arguments passed"); + r = -EINVAL; + goto finish; + } + + while (argv[optind]) { + int k; + + k = resolve_record(bus, argv[optind]); + if (r == 0) + r = k; + + optind++; + } + break; + + case MODE_RESOLVE_SERVICE: if (argc < optind + 1) { log_error("Domain specification required."); r = -EINVAL; @@ -914,27 +1089,27 @@ int main(int argc, char **argv) { goto finish; } - goto finish; - } + break; - while (argv[optind]) { - int family, ifindex, k; - union in_addr_union a; - - if (arg_type != 0) - k = resolve_record(bus, argv[optind]); - else { - k = parse_address(argv[optind], &family, &a, &ifindex); - if (k >= 0) - k = resolve_address(bus, family, &a, ifindex); - else - k = resolve_host(bus, argv[optind]); + case MODE_STATISTICS: + if (argc > optind) { + log_error("Too many arguments."); + r = -EINVAL; + goto finish; } - if (r == 0) - r = k; + r = show_statistics(bus); + break; - optind++; + case MODE_RESET_STATISTICS: + if (argc > optind) { + log_error("Too many arguments."); + r = -EINVAL; + goto finish; + } + + r = reset_statistics(bus); + break; } finish: diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index af08a0555d..273bfbe949 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -1214,16 +1214,101 @@ static int bus_property_get_search_domains( return sd_bus_message_close_container(reply); } +static int bus_property_get_transaction_statistics( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "(tt)", + (uint64_t) hashmap_size(m->dns_transactions), + (uint64_t) m->n_transactions_total); +} + +static int bus_property_get_cache_statistics( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t size = 0, hit = 0, miss = 0; + Manager *m = userdata; + DnsScope *s; + + assert(reply); + assert(m); + + LIST_FOREACH(scopes, s, m->dns_scopes) { + size += dns_cache_size(&s->cache); + hit += s->cache.n_hit; + miss += s->cache.n_miss; + } + + return sd_bus_message_append(reply, "(ttt)", size, hit, miss); +} + +static int bus_property_get_dnssec_statistics( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "(tttt)", + (uint64_t) m->n_dnssec_secure, + (uint64_t) m->n_dnssec_insecure, + (uint64_t) m->n_dnssec_bogus, + (uint64_t) m->n_dnssec_indeterminate); +} + +static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + DnsScope *s; + + assert(message); + assert(m); + + LIST_FOREACH(scopes, s, m->dns_scopes) + s->cache.n_hit = s->cache.n_miss = 0; + + m->n_transactions_total = 0; + m->n_dnssec_secure = m->n_dnssec_insecure = m->n_dnssec_bogus = m->n_dnssec_indeterminate = 0; + + return sd_bus_reply_method_return(message, NULL); +} + static const sd_bus_vtable resolve_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0), SD_BUS_PROPERTY("DNSServers", "a(iiay)", bus_property_get_dns_servers, 0, 0), SD_BUS_PROPERTY("SearchDomains", "a(is)", bus_property_get_search_domains, 0, 0), + SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0), + SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0), + SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0), SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0), SD_BUS_VTABLE_END, }; diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index f9fe2c2e30..49d5090d36 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -740,6 +740,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r log_debug("Ignoring cache for ANY lookup: %s", key_str); } + c->n_miss++; + *ret = NULL; *rcode = DNS_RCODE_SUCCESS; return 0; @@ -757,6 +759,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r log_debug("Cache miss for %s", key_str); } + c->n_miss++; + *ret = NULL; *rcode = DNS_RCODE_SUCCESS; return 0; @@ -794,9 +798,15 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r *rcode = DNS_RCODE_SUCCESS; *authenticated = nsec->authenticated; - return !bitmap_isset(nsec->rr->nsec.types, key->type) && - !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) && - !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME); + if (!bitmap_isset(nsec->rr->nsec.types, key->type) && + !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_CNAME) && + !bitmap_isset(nsec->rr->nsec.types, DNS_TYPE_DNAME)) { + c->n_hit++; + return 1; + } + + c->n_miss++; + return 0; } if (log_get_max_level() >= LOG_DEBUG) { @@ -811,6 +821,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r } if (n <= 0) { + c->n_hit++; + *ret = NULL; *rcode = nxdomain ? DNS_RCODE_NXDOMAIN : DNS_RCODE_SUCCESS; *authenticated = have_authenticated && !have_non_authenticated; @@ -830,6 +842,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r return r; } + c->n_hit++; + *ret = answer; *rcode = DNS_RCODE_SUCCESS; *authenticated = have_authenticated && !have_non_authenticated; @@ -974,3 +988,10 @@ bool dns_cache_is_empty(DnsCache *cache) { return hashmap_isempty(cache->by_key); } + +unsigned dns_cache_size(DnsCache *cache) { + if (!cache) + return 0; + + return hashmap_size(cache->by_key); +} diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index 856c975299..9c85ca4c58 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -29,6 +29,8 @@ typedef struct DnsCache { Hashmap *by_key; Prioq *by_expiry; + unsigned n_hit; + unsigned n_miss; } DnsCache; #include "resolved-dns-answer.h" @@ -47,4 +49,6 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_ void dns_cache_dump(DnsCache *cache, FILE *f); bool dns_cache_is_empty(DnsCache *cache); +unsigned dns_cache_size(DnsCache *cache); + int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 0f48f9bf81..af6f28ea0f 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -153,6 +153,8 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) LIST_PREPEND(transactions_by_scope, s->transactions, t); t->scope = s; + s->manager->n_transactions_total ++; + if (ret) *ret = t; @@ -1996,6 +1998,8 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { if (r < 0) return r; + t->scope->manager->n_dnssec_secure++; + /* Exit the loop, we dropped something from the answer, start from the beginning */ changed = true; break; @@ -2018,6 +2022,8 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { if (r < 0) return r; + t->scope->manager->n_dnssec_insecure++; + changed = true; break; } @@ -2039,11 +2045,22 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { if (r < 0) return r; + t->scope->manager->n_dnssec_insecure++; + changed = true; break; } } + if (IN_SET(result, + DNSSEC_INVALID, + DNSSEC_SIGNATURE_EXPIRED, + DNSSEC_NO_SIGNATURE, + DNSSEC_UNSUPPORTED_ALGORITHM)) + t->scope->manager->n_dnssec_bogus++; + else + t->scope->manager->n_dnssec_indeterminate++; + r = dns_transaction_is_primary_response(t, rr); if (r < 0) return r; diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index b52273403a..dd85d3ba47 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -128,6 +128,9 @@ struct Manager { sd_bus_slot *prepare_for_sleep_slot; sd_event_source *sigusr1_event_source; + + unsigned n_transactions_total; + unsigned n_dnssec_secure, n_dnssec_insecure, n_dnssec_bogus, n_dnssec_indeterminate; }; /* Manager */