resolved: switch cache option to a tri-state option (systemd#5552).

Change the resolved.conf Cache option to a tri-state "no, no-negative, yes" values.

If a lookup returns SERVFAIL systemd-resolved will cache the result for 30s (See 201d995),
however, there are several use cases on which this condition is not acceptable (See systemd#5552 comments)
and the only workaround would be to disable cache entirely or flush it , which isn't optimal.

This change adds the 'no-negative' option when set it avoids putting in cache
negative answers but still works the same heuristics for positive answers.

Signed-off-by: Jorge Niedbalski <jnr@metaklass.org>
This commit is contained in:
Jorge Niedbalski 2019-07-12 15:34:24 -04:00
parent 81c07a9555
commit 37d7a7d984
11 changed files with 49 additions and 8 deletions

7
NEWS
View File

@ -178,6 +178,13 @@ CHANGES WITH 243 in spe:
* systemd-resolved gained support for a new 'strict' DNS-over-TLS mode. * systemd-resolved gained support for a new 'strict' DNS-over-TLS mode.
* systemd-resolved "Cache=" configuration option in resolved.conf has been extended
to also accept the 'no-negative' value. Previously,
only a boolean option was allowed (yes/no), having yes as the default.
If this option is set to 'no-negative', negative answers are skipped
from being cached while keeping the same cache heuristics for positive answers.
The default remains as "yes" (i. e. caching is enabled).
* The predictable naming scheme for network devices now supports * The predictable naming scheme for network devices now supports
generating predictable names for "netdevsim" devices. generating predictable names for "netdevsim" devices.

View File

@ -227,10 +227,11 @@
<varlistentry> <varlistentry>
<term><varname>Cache=</varname></term> <term><varname>Cache=</varname></term>
<listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default), resolving a domain name <listitem><para>Takes a boolean or <literal>no-negative</literal> as argument. If <literal>yes</literal> (the default), resolving a domain name
which already got queried earlier will return the previous result as long as it is still valid, and thus does which already got queried earlier will return the previous result as long as it is still valid, and thus does
not result in a new network request. Be aware that turning off caching comes at a performance penalty, which not result in a new network request. Be aware that turning off caching comes at a performance penalty, which
is particularly high when DNSSEC is used.</para> is particularly high when DNSSEC is used.</para>
If <literal>no-negative</literal>, only positive answers are cached.
<para>Note that caching is turned off implicitly if the configured DNS server is on a host-local IP address <para>Note that caching is turned off implicitly if the configured DNS server is on a host-local IP address
(such as 127.0.0.1 or ::1), in order to avoid duplicate local caching.</para></listitem> (such as 127.0.0.1 or ::1), in order to avoid duplicate local caching.</para></listitem>

View File

@ -621,6 +621,7 @@ static bool rr_eligible(DnsResourceRecord *rr) {
int dns_cache_put( int dns_cache_put(
DnsCache *c, DnsCache *c,
DnsCacheMode cache_mode,
DnsResourceKey *key, DnsResourceKey *key,
int rcode, int rcode,
DnsAnswer *answer, DnsAnswer *answer,
@ -728,6 +729,13 @@ int dns_cache_put(
return 0; return 0;
} }
if (cache_mode == DNS_CACHE_MODE_NO_NEGATIVE) {
char key_str[DNS_RESOURCE_KEY_STRING_MAX];
log_debug("Not caching negative entry for: %s, cache mode set to no-negative",
dns_resource_key_to_string(key, key_str, sizeof key_str));
return 0;
}
r = dns_cache_put_negative( r = dns_cache_put_negative(
c, c,
key, key,

View File

@ -4,6 +4,7 @@
#include "hashmap.h" #include "hashmap.h"
#include "list.h" #include "list.h"
#include "prioq.h" #include "prioq.h"
#include "resolve-util.h"
#include "time-util.h" #include "time-util.h"
typedef struct DnsCache { typedef struct DnsCache {
@ -21,7 +22,7 @@ typedef struct DnsCache {
void dns_cache_flush(DnsCache *c); void dns_cache_flush(DnsCache *c);
void dns_cache_prune(DnsCache *c); void dns_cache_prune(DnsCache *c);
int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, bool authenticated, uint32_t nsec_ttl, usec_t timestamp, int owner_family, const union in_addr_union *owner_address); int dns_cache_put(DnsCache *c, DnsCacheMode cache_mode, DnsResourceKey *key, int rcode, DnsAnswer *answer, bool authenticated, uint32_t nsec_ttl, usec_t timestamp, int owner_family, const union in_addr_union *owner_address);
int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **answer, bool *authenticated); int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcode, DnsAnswer **answer, bool *authenticated);
int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address); int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address);

View File

@ -672,7 +672,7 @@ static void dns_transaction_cache_answer(DnsTransaction *t) {
return; return;
/* Caching disabled? */ /* Caching disabled? */
if (!t->scope->manager->enable_cache) if (t->scope->manager->enable_cache == DNS_CACHE_MODE_NO)
return; return;
/* We never cache if this packet is from the local host, under /* We never cache if this packet is from the local host, under
@ -683,6 +683,7 @@ static void dns_transaction_cache_answer(DnsTransaction *t) {
return; return;
dns_cache_put(&t->scope->cache, dns_cache_put(&t->scope->cache,
t->scope->manager->enable_cache,
t->key, t->key,
t->answer_rcode, t->answer_rcode,
t->answer, t->answer,

View File

@ -24,6 +24,6 @@ Resolve.LLMNR, config_parse_resolve_support, 0,
Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support) Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support)
Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode) Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Manager, dns_over_tls_mode) Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Manager, dns_over_tls_mode)
Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache) Resolve.Cache, config_parse_dns_cache_mode, DNS_CACHE_MODE_YES, offsetof(Manager, enable_cache)
Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode) Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode)
Resolve.ReadEtcHosts, config_parse_bool, 0, offsetof(Manager, read_etc_hosts) Resolve.ReadEtcHosts, config_parse_bool, 0, offsetof(Manager, read_etc_hosts)

View File

@ -581,7 +581,7 @@ int manager_new(Manager **ret) {
.mdns_support = RESOLVE_SUPPORT_YES, .mdns_support = RESOLVE_SUPPORT_YES,
.dnssec_mode = DEFAULT_DNSSEC_MODE, .dnssec_mode = DEFAULT_DNSSEC_MODE,
.dns_over_tls_mode = DEFAULT_DNS_OVER_TLS_MODE, .dns_over_tls_mode = DEFAULT_DNS_OVER_TLS_MODE,
.enable_cache = true, .enable_cache = DNS_CACHE_MODE_YES,
.dns_stub_listener_mode = DNS_STUB_LISTENER_YES, .dns_stub_listener_mode = DNS_STUB_LISTENER_YES,
.read_resolv_conf = true, .read_resolv_conf = true,
.need_builtin_fallbacks = true, .need_builtin_fallbacks = true,

View File

@ -37,7 +37,7 @@ struct Manager {
ResolveSupport mdns_support; ResolveSupport mdns_support;
DnssecMode dnssec_mode; DnssecMode dnssec_mode;
DnsOverTlsMode dns_over_tls_mode; DnsOverTlsMode dns_over_tls_mode;
bool enable_cache; DnsCacheMode enable_cache;
DnsStubListenerMode dns_stub_listener_mode; DnsStubListenerMode dns_stub_listener_mode;
#if ENABLE_DNS_OVER_TLS #if ENABLE_DNS_OVER_TLS

View File

@ -319,7 +319,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us
dns_transaction_process_reply(t, p); dns_transaction_process_reply(t, p);
} }
dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender); dns_cache_put(&scope->cache, scope->manager->enable_cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender);
} else if (dns_packet_validate_query(p) > 0) { } else if (dns_packet_validate_query(p) > 0) {
log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p)); log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p));

View File

@ -41,3 +41,12 @@ bool dns_server_address_valid(int family, const union in_addr_union *sa) {
return true; return true;
} }
DEFINE_CONFIG_PARSE_ENUM(config_parse_dns_cache_mode, dns_cache_mode, DnsCacheMode, "Failed to parse DNS cache mode setting")
static const char* const dns_cache_mode_table[_DNS_CACHE_MODE_MAX] = {
[DNS_CACHE_MODE_YES] = "yes",
[DNS_CACHE_MODE_NO] = "no",
[DNS_CACHE_MODE_NO_NEGATIVE] = "no-negative",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_cache_mode, DnsCacheMode, DNS_CACHE_MODE_YES);

View File

@ -8,6 +8,16 @@
/* 127.0.0.53 in native endian */ /* 127.0.0.53 in native endian */
#define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U) #define INADDR_DNS_STUB ((in_addr_t) 0x7f000035U)
typedef enum DnsCacheMode DnsCacheMode;
enum DnsCacheMode {
DNS_CACHE_MODE_NO,
DNS_CACHE_MODE_YES,
DNS_CACHE_MODE_NO_NEGATIVE,
_DNS_CACHE_MODE_MAX,
_DNS_CACHE_MODE_INVALID = 1
};
typedef enum ResolveSupport ResolveSupport; typedef enum ResolveSupport ResolveSupport;
typedef enum DnssecMode DnssecMode; typedef enum DnssecMode DnssecMode;
typedef enum DnsOverTlsMode DnsOverTlsMode; typedef enum DnsOverTlsMode DnsOverTlsMode;
@ -56,6 +66,7 @@ enum DnsOverTlsMode {
CONFIG_PARSER_PROTOTYPE(config_parse_resolve_support); CONFIG_PARSER_PROTOTYPE(config_parse_resolve_support);
CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_mode); CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_dns_over_tls_mode); CONFIG_PARSER_PROTOTYPE(config_parse_dns_over_tls_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_dns_cache_mode);
const char* resolve_support_to_string(ResolveSupport p) _const_; const char* resolve_support_to_string(ResolveSupport p) _const_;
ResolveSupport resolve_support_from_string(const char *s) _pure_; ResolveSupport resolve_support_from_string(const char *s) _pure_;
@ -67,3 +78,6 @@ const char* dns_over_tls_mode_to_string(DnsOverTlsMode p) _const_;
DnsOverTlsMode dns_over_tls_mode_from_string(const char *s) _pure_; DnsOverTlsMode dns_over_tls_mode_from_string(const char *s) _pure_;
bool dns_server_address_valid(int family, const union in_addr_union *sa); bool dns_server_address_valid(int family, const union in_addr_union *sa);
const char* dns_cache_mode_to_string(DnsCacheMode p) _const_;
DnsCacheMode dns_cache_mode_from_string(const char *s) _pure_;