aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-12-04 12:40:07 +0100
committerLennart Poettering <lennart@poettering.net>2018-12-21 12:10:07 +0100
commitca5394d26039f58ea47255be61c265fee4c9597c (patch)
treedb0fb40dbf4ffbd2b2a04009c7d829ecdd478784
parentresolved: rework dns_server_limited_domains(), replace by dns_scope_has_route... (diff)
downloadsystemd-ca5394d26039f58ea47255be61c265fee4c9597c.tar.gz
systemd-ca5394d26039f58ea47255be61c265fee4c9597c.tar.bz2
systemd-ca5394d26039f58ea47255be61c265fee4c9597c.zip
resolved: add an explicit way to configure whether a link is useful as default route
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.
-rw-r--r--src/resolve/resolved-dns-scope.c43
-rw-r--r--src/resolve/resolved-dns-scope.h2
-rw-r--r--src/resolve/resolved-link.c34
-rw-r--r--src/resolve/resolved-link.h2
-rw-r--r--src/resolve/resolved-resolv-conf.c8
5 files changed, 66 insertions, 23 deletions
diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c
index be4548599..972e661d7 100644
--- a/src/resolve/resolved-dns-scope.c
+++ b/src/resolve/resolved-dns-scope.c
@@ -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);
+}
diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h
index 6f47a5593..f4b45c4f2 100644
--- a/src/resolve/resolved-dns-scope.h
+++ b/src/resolve/resolved-dns-scope.h
@@ -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);
diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c
index 0f9a0e942..351d51a30 100644
--- a/src/resolve/resolved-link.c
+++ b/src/resolve/resolved-link.c
@@ -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);
diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h
index 81ab2056a..f95ea37a4 100644
--- a/src/resolve/resolved-link.h
+++ b/src/resolve/resolved-link.h
@@ -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;
diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c
index 832251b61..5205071d3 100644
--- a/src/resolve/resolved-resolv-conf.c
+++ b/src/resolve/resolved-resolv-conf.c
@@ -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;
}