]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: add an explicit way to configure whether a link is useful as default route
authorLennart Poettering <lennart@poettering.net>
Tue, 4 Dec 2018 11:40:07 +0000 (12:40 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 21 Dec 2018 11:10:07 +0000 (12:10 +0100)
Previously, we'd use a link as "default" route depending on whether
there are route-only domains defined on it or not. (If there are, it
would not be used as default route, if there aren't it would.)

Let's make this explicit and add a link variable controlling this. The
variable is not changeable from the outside yet, but subsequent commits
are supposed to add that.

Note that making this configurable adds a certain amount of redundancy,
as there are now two ways to ensure a link does not receive "default"
lookup (i.e. DNS queries matching no configured route):

1. By ensuring that at least one other link configures a route on it
   (for example by add "." to its search list)

2. By setting this new boolean to false.

But this is exactly what is intended with this patch: that there is an
explicit way to configure on the link itself whether it receives
'default' traffic, rather than require this to be configured on other
links.

The variable added is a tri-state: if true, the link is suitable for
recieving "default" traffic. If false, the link is not suitable for it.
If unset (i.e. negative) the original logic of "has this route-only
routes" is used, to ensure compatibility with the status quo ante.

src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-link.c
src/resolve/resolved-link.h
src/resolve/resolved-resolv-conf.c

index be45485998b6dd685da0fa976a996869d9b83428..972e661d720ce66a7c528bdbf6fb752acf222d44 100644 (file)
@@ -554,9 +554,8 @@ DnsScopeMatch dns_scope_good_domain(
                         return DNS_SCOPE_YES_BASE + n_best;
                 }
 
-                /* If the DNS server has route-only domains, don't send other requests to it. This would be a privacy
-                 * violation, will most probably fail anyway, and adds unnecessary load. */
-                if (dns_scope_has_route_only_domains(s))
+                /* See if this scope is suitable as default route. */
+                if (!dns_scope_is_default_route(s))
                         return DNS_SCOPE_NO;
 
                 /* Exclude link-local IP ranges */
@@ -1392,15 +1391,17 @@ int dns_scope_remove_dnssd_services(DnsScope *scope) {
         return 0;
 }
 
-bool dns_scope_has_route_only_domains(DnsScope *scope) {
+static bool dns_scope_has_route_only_domains(DnsScope *scope) {
         DnsSearchDomain *domain, *first;
         bool route_only = false;
 
-        /* Check if the scope has any route-only domains except for "~.", i. e. whether it should only be
-         * used for particular domains */
+        assert(scope);
+        assert(scope->protocol == DNS_PROTOCOL_DNS);
 
-        if (scope->protocol != DNS_PROTOCOL_DNS)
-                return false;
+        /* Returns 'true' if this scope is suitable for queries to specific domains only. For that we check
+         * if there are any route-only domains on this interface, as a heuristic to discern VPN-style links
+         * from non-VPN-style links. Returns 'false' for all other cases, i.e. if the scope is intended to
+         * take queries to arbitrary domains, i.e. has no routing domains set. */
 
         if (scope->link)
                 first = scope->link->search_domains;
@@ -1408,7 +1409,10 @@ bool dns_scope_has_route_only_domains(DnsScope *scope) {
                 first = scope->manager->search_domains;
 
         LIST_FOREACH(domains, domain, first) {
-                /* "." means "any domain", thus the interface takes any kind of traffic. */
+                /* "." means "any domain", thus the interface takes any kind of traffic. Thus, we exit early
+                 * here, as it doesn't really matter whether this link has any route-only domains or not,
+                 * "~."  really trumps everything and clearly indicates that this interface shall receive all
+                 * traffic it can get. */
                 if (dns_name_is_root(DNS_SEARCH_DOMAIN_NAME(domain)))
                         return false;
 
@@ -1418,3 +1422,24 @@ bool dns_scope_has_route_only_domains(DnsScope *scope) {
 
         return route_only;
 }
+
+bool dns_scope_is_default_route(DnsScope *scope) {
+        assert(scope);
+
+        /* Only use DNS scopes as default routes */
+        if (scope->protocol != DNS_PROTOCOL_DNS)
+                return false;
+
+        /* The global DNS scope is always suitable as default route */
+        if (!scope->link)
+                return true;
+
+        /* Honour whatever is explicitly configured. This is really the best approach, and trumps any
+         * automatic logic. */
+        if (scope->link->default_route >= 0)
+                return scope->link->default_route;
+
+        /* Otherwise check if we have any route-only domains, as a sensible heuristic: if so, let's not
+         * volunteer as default route. */
+        return !dns_scope_has_route_only_domains(scope);
+}
index 6f47a5593f07492543c21fe9bca9ea0cf6e4622d..f4b45c4f20822a51e60a36fc4d26c913166bd832 100644 (file)
@@ -109,4 +109,4 @@ int dns_scope_announce(DnsScope *scope, bool goodbye);
 int dns_scope_add_dnssd_services(DnsScope *scope);
 int dns_scope_remove_dnssd_services(DnsScope *scope);
 
-bool dns_scope_has_route_only_domains(DnsScope *scope);
+bool dns_scope_is_default_route(DnsScope *scope);
index 0f9a0e942137055ec221652b2409c9c00658e39d..351d51a30a10627c57fab73bea3547404af12c3b 100644 (file)
@@ -30,16 +30,19 @@ int link_new(Manager *m, Link **ret, int ifindex) {
         if (r < 0)
                 return r;
 
-        l = new0(Link, 1);
+        l = new(Link, 1);
         if (!l)
                 return -ENOMEM;
 
-        l->ifindex = ifindex;
-        l->llmnr_support = RESOLVE_SUPPORT_YES;
-        l->mdns_support = RESOLVE_SUPPORT_NO;
-        l->dnssec_mode = _DNSSEC_MODE_INVALID;
-        l->dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID;
-        l->operstate = IF_OPER_UNKNOWN;
+        *l = (Link) {
+                .ifindex = ifindex,
+                .default_route = -1,
+                .llmnr_support = RESOLVE_SUPPORT_YES,
+                .mdns_support = RESOLVE_SUPPORT_NO,
+                .dnssec_mode = _DNSSEC_MODE_INVALID,
+                .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID,
+                .operstate = IF_OPER_UNKNOWN,
+        };
 
         if (asprintf(&l->state_file, "/run/systemd/resolve/netif/%i", ifindex) < 0)
                 return -ENOMEM;
@@ -60,6 +63,7 @@ int link_new(Manager *m, Link **ret, int ifindex) {
 void link_flush_settings(Link *l) {
         assert(l);
 
+        l->default_route = -1;
         l->llmnr_support = RESOLVE_SUPPORT_YES;
         l->mdns_support = RESOLVE_SUPPORT_NO;
         l->dnssec_mode = _DNSSEC_MODE_INVALID;
@@ -1120,6 +1124,9 @@ static bool link_needs_save(Link *l) {
         if (!set_isempty(l->dnssec_negative_trust_anchors))
                 return true;
 
+        if (l->default_route >= 0)
+                return true;
+
         return false;
 }
 
@@ -1162,6 +1169,9 @@ int link_save_user(Link *l) {
         if (v)
                 fprintf(f, "DNSSEC=%s\n", v);
 
+        if (l->default_route >= 0)
+                fprintf(f, "DEFAULT_ROUTE=%s\n", yes_no(l->default_route));
+
         if (l->dns_servers) {
                 DnsServer *server;
 
@@ -1243,7 +1253,8 @@ int link_load_user(Link *l) {
                 *dnssec = NULL,
                 *servers = NULL,
                 *domains = NULL,
-                *ntas = NULL;
+                *ntas = NULL,
+                *default_route = NULL;
 
         ResolveSupport s;
         const char *p;
@@ -1266,7 +1277,8 @@ int link_load_user(Link *l) {
                            "DNSSEC", &dnssec,
                            "SERVERS", &servers,
                            "DOMAINS", &domains,
-                           "NTAS", &ntas);
+                           "NTAS", &ntas,
+                           "DEFAULT_ROUTE", &default_route);
         if (r == -ENOENT)
                 return 0;
         if (r < 0)
@@ -1283,6 +1295,10 @@ int link_load_user(Link *l) {
         if (s >= 0)
                 l->mdns_support = s;
 
+        r = parse_boolean(default_route);
+        if (r >= 0)
+                l->default_route = r;
+
         /* If we can't recognize the DNSSEC setting, then set it to invalid, so that the daemon default is used. */
         l->dnssec_mode = dnssec_mode_from_string(dnssec);
 
index 81ab2056ac65e4dbc5e81a17c573fc4ed1a1f3a1..f95ea37a4f1bec03052196e8f81a74b18c7933fe 100644 (file)
@@ -51,6 +51,8 @@ struct Link {
         LIST_HEAD(DnsSearchDomain, search_domains);
         unsigned n_search_domains;
 
+        int default_route;
+
         ResolveSupport llmnr_support;
         ResolveSupport mdns_support;
         DnsOverTlsMode dns_over_tls_mode;
index 832251b61097f0c8bd90f3d8b14715225132e628..5205071d3f0446609a77cd77296006c0353850e3 100644 (file)
@@ -228,11 +228,11 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) {
                 return;
         }
 
-        /* Check if the scope this DNS server belongs to is limited to particular domains; resolv.conf does not have a
-         * syntax to express that, so it must not appear as a global name server to avoid routing unrelated domains to
-         * it (which is a privacy violation, will most probably fail anyway, and adds unnecessary load) */
+        /* Check if the scope this DNS server belongs to is suitable as 'default' route for lookups; resolv.conf does
+         * not have a syntax to express that, so it must not appear as a global name server to avoid routing unrelated
+         * domains to it (which is a privacy violation, will most probably fail anyway, and adds unnecessary load) */
         scope = dns_server_scope(s);
-        if (scope && dns_scope_has_route_only_domains(scope)) {
+        if (scope && !dns_scope_is_default_route(scope)) {
                 log_debug("Scope of DNS server %s has only route-only domains, not using as global name server", dns_server_string(s));
                 return;
         }