]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: don't redundantly switch DNS servers because of transaction failures
authorLennart Poettering <lennart@poettering.net>
Thu, 5 Nov 2020 16:00:20 +0000 (17:00 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 15 Feb 2021 20:59:05 +0000 (21:59 +0100)
When a transaction fails and we decide to switch DNS servers, don#t do
so unconditionally. Check if the current DNS server is still the same as
when the transaction was initiated. And if not, do not do anything.

That should reduce the number of redundant DNS server switches if many
parallel transactions fail simultaneously (which is pretty likely if
DNSSEC is on).

Fixes: #17040
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-link.c
src/resolve/resolved-link.h

index 245f9bfce86b36d14579033fa8d7607e07919d53..c962af9aba308e2fbb98b15dcc351ed97976e7ac 100644 (file)
@@ -153,16 +153,19 @@ unsigned dns_scope_get_n_dns_servers(DnsScope *s) {
         return n;
 }
 
-void dns_scope_next_dns_server(DnsScope *s) {
+void dns_scope_next_dns_server(DnsScope *s, DnsServer *if_current) {
         assert(s);
 
         if (s->protocol != DNS_PROTOCOL_DNS)
                 return;
 
+        /* Changes to the next DNS server in the list. If 'if_current' is passed will do so only if the
+         * current DNS server still matches it. */
+
         if (s->link)
-                link_next_dns_server(s->link);
+                link_next_dns_server(s->link, if_current);
         else
-                manager_next_dns_server(s->manager);
+                manager_next_dns_server(s->manager, if_current);
 }
 
 void dns_scope_packet_received(DnsScope *s, usec_t rtt) {
index 532f4d6b8006e11fa3a29525fe64b2387bbaf37f..7e863d3f665d9b1b43e2ea2b16e20b95fe0f2f82 100644 (file)
@@ -80,7 +80,7 @@ bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key);
 
 DnsServer *dns_scope_get_dns_server(DnsScope *s);
 unsigned dns_scope_get_n_dns_servers(DnsScope *s);
-void dns_scope_next_dns_server(DnsScope *s);
+void dns_scope_next_dns_server(DnsScope *s, DnsServer *if_current);
 
 int dns_scope_llmnr_membership(DnsScope *s, bool b);
 int dns_scope_mdns_membership(DnsScope *s, bool b);
index 541266ff9d6fdb929b709797545fe6e4aeeb94f7..585bfee2d101137318b8dc332e838fd847d72a74 100644 (file)
@@ -771,23 +771,25 @@ DnsServer *manager_get_dns_server(Manager *m) {
         return m->current_dns_server;
 }
 
-void manager_next_dns_server(Manager *m) {
+void manager_next_dns_server(Manager *m, DnsServer *if_current) {
         assert(m);
 
-        /* If there's currently no DNS server set, then the next
-         * manager_get_dns_server() will find one */
+        /* If the DNS server is already a different one than the one specified in 'if_current' don't do anything */
+        if (if_current && m->current_dns_server != if_current)
+                return;
+
+        /* If there's currently no DNS server set, then the next manager_get_dns_server() will find one */
         if (!m->current_dns_server)
                 return;
 
-        /* Change to the next one, but make sure to follow the linked
-         * list only if the server is still linked. */
+        /* Change to the next one, but make sure to follow the linked list only if the server is still
+         * linked. */
         if (m->current_dns_server->linked && m->current_dns_server->servers_next) {
                 manager_set_dns_server(m, m->current_dns_server->servers_next);
                 return;
         }
 
-        /* If there was no next one, then start from the beginning of
-         * the list */
+        /* If there was no next one, then start from the beginning of the list */
         if (m->current_dns_server->type == DNS_SERVER_FALLBACK)
                 manager_set_dns_server(m, m->fallback_dns_servers);
         else
index e4664c883a54113142284f236e86556158ab37dd..96e46226eedd81abb23743ce0405bd08392882e5 100644 (file)
@@ -143,7 +143,7 @@ DnsServer *manager_get_first_dns_server(Manager *m, DnsServerType t);
 
 DnsServer *manager_set_dns_server(Manager *m, DnsServer *s);
 DnsServer *manager_get_dns_server(Manager *m);
-void manager_next_dns_server(Manager *m);
+void manager_next_dns_server(Manager *m, DnsServer *if_current);
 
 DnssecMode dns_server_get_dnssec_mode(DnsServer *s);
 DnsOverTlsMode dns_server_get_dns_over_tls_mode(DnsServer *s);
index 1ef1900b7bf0ccd42565919531df24f68a26e8cf..bd73aa5451e899b0200308ce5e892cce1b5a9da8 100644 (file)
@@ -484,7 +484,7 @@ static void dns_transaction_retry(DnsTransaction *t, bool next_server) {
 
         /* Before we try again, switch to a new server. */
         if (next_server)
-                dns_scope_next_dns_server(t->scope);
+                dns_scope_next_dns_server(t->scope, t->server);
 
         r = dns_transaction_go(t);
         if (r < 0)
@@ -1859,7 +1859,7 @@ int dns_transaction_go(DnsTransaction *t) {
                 /* One of our own stub listeners */
                 log_debug_errno(r, "Detected that specified DNS server is our own extra listener, switching DNS servers.");
 
-                dns_scope_next_dns_server(t->scope);
+                dns_scope_next_dns_server(t->scope, t->server);
 
                 if (dns_scope_get_dns_server(t->scope) == t->server) {
                         log_debug_errno(r, "Still pointing to extra listener after switching DNS servers, refusing operation.");
@@ -1890,7 +1890,7 @@ int dns_transaction_go(DnsTransaction *t) {
                         return r;
 
                 /* Couldn't send? Try immediately again, with a new server */
-                dns_scope_next_dns_server(t->scope);
+                dns_scope_next_dns_server(t->scope, t->server);
 
                 return dns_transaction_go(t);
         }
index 3a549691c8dd1f77d3fd002f2be5c45db24e37b2..9778ebfff72b209c91d3b331a5b9022a1b047d04 100644 (file)
@@ -731,19 +731,27 @@ DnsServer *link_get_dns_server(Link *l) {
         return l->current_dns_server;
 }
 
-void link_next_dns_server(Link *l) {
+void link_next_dns_server(Link *l, DnsServer *if_current) {
         assert(l);
 
+        /* If the current server of the transaction is specified, and we already are at a different one,
+         * don't do anything */
+        if (if_current && l->current_dns_server != if_current)
+                return;
+
+        /* If currently have no DNS server, then don't do anything, we'll pick it lazily the next time a DNS
+         * server is needed. */
         if (!l->current_dns_server)
                 return;
 
-        /* Change to the next one, but make sure to follow the linked
-         * list only if this server is actually still linked. */
+        /* Change to the next one, but make sure to follow the linked list only if this server is actually
+         * still linked. */
         if (l->current_dns_server->linked && l->current_dns_server->servers_next) {
                 link_set_dns_server(l, l->current_dns_server->servers_next);
                 return;
         }
 
+        /* Pick the first one again, after we reached the end */
         link_set_dns_server(l, l->dns_servers);
 }
 
index 26b0d13127d5652e0e8f0dbe8dca35fcd7cdaf48..2be724376e08282de4e5f6602cef7c9539251c11 100644 (file)
@@ -91,7 +91,7 @@ void link_allocate_scopes(Link *l);
 
 DnsServer* link_set_dns_server(Link *l, DnsServer *s);
 DnsServer* link_get_dns_server(Link *l);
-void link_next_dns_server(Link *l);
+void link_next_dns_server(Link *l, DnsServer *if_current);
 
 DnssecMode link_get_dnssec_mode(Link *l);
 bool link_dnssec_supported(Link *l);