diff options
author | Thomas Haller <thaller@redhat.com> | 2020-11-19 17:38:39 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2020-11-20 18:28:03 +0100 |
commit | ee9fab03613e30f818d56217a968d1fabd5ea8d7 (patch) | |
tree | cb979fe375a08efc0ec863f455633a0250d171b8 | |
parent | a875d154de3baac6a8b5601dcb30f0316de4c8cb (diff) | |
download | NetworkManager-ee9fab03613e30f818d56217a968d1fabd5ea8d7.tar.gz |
dns: fix handling default routing domains with systemd-resolved
We used to set "~." domains for all devices that should be used for
resolving unknown domains.
Systemd-resolved also supports setting "SetLinkDefaultRoute()".
We should only set the wildcard domain if we want that this
interface is used exclusively. Otherwise, we should only set
DefaultRoute. See ([1], [2], [3], [4]).
Otherwise the bad effect is if other components (wg-quick) want
to set exclusive DNS lookups on their link. That is achieved by
explicitly adding "~." and that is also what resolved's
`/usr/sbin/resolvconf -x` does. If NetworkManager sets "~." for
interfaces that are not important and should not be used exclusively,
then this steals the DNS requests from those external components.
In NetworkManager we know whether a link should get exclusive lookups
based on the "ipv[46].dns-priority" setting.
[1] https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html
[2] https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html
[3] https://github.com/systemd/systemd/issues/17529#issuecomment-730522444
[4] https://github.com/systemd/systemd/pull/17678
-rw-r--r-- | src/dns/nm-dns-dnsmasq.c | 15 | ||||
-rw-r--r-- | src/dns/nm-dns-manager.c | 125 | ||||
-rw-r--r-- | src/dns/nm-dns-manager.h | 22 | ||||
-rw-r--r-- | src/dns/nm-dns-systemd-resolved.c | 37 |
4 files changed, 154 insertions, 45 deletions
diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c index 451e48465a..97f1dfabf8 100644 --- a/src/dns/nm-dns-dnsmasq.c +++ b/src/dns/nm-dns-dnsmasq.c @@ -820,11 +820,18 @@ add_ip_config(NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsIPConfigD for (i = 0; i < num; i++) { addr = nm_ip_config_get_nameserver(ip_config, i); ip_addr_to_string(addr_family, addr, iface, ip_addr_to_string_buf); - for (j = 0; ip_data->domains.search[j]; j++) { - domain = nm_utils_parse_dns_domain(ip_data->domains.search[j], NULL); - add_dnsmasq_nameserver(self, servers, ip_addr_to_string_buf, domain[0] ? domain : NULL); - } + if (!ip_data->domains.has_default_route_explicit && ip_data->domains.has_default_route) + add_dnsmasq_nameserver(self, servers, ip_addr_to_string_buf, NULL); + if (ip_data->domains.search) { + for (j = 0; ip_data->domains.search[j]; j++) { + domain = nm_utils_parse_dns_domain(ip_data->domains.search[j], NULL); + add_dnsmasq_nameserver(self, + servers, + ip_addr_to_string_buf, + domain[0] ? domain : NULL); + } + } if (ip_data->domains.reverse) { for (j = 0; ip_data->domains.reverse[j]; j++) { add_dnsmasq_nameserver(self, diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c index de8e134204..caf99fc36e 100644 --- a/src/dns/nm-dns-manager.c +++ b/src/dns/nm-dns-manager.c @@ -222,6 +222,26 @@ _ASSERT_ip_config_data(const NMDnsIPConfigData *ip_data) nm_assert(NM_IS_IP_CONFIG(ip_data->ip_config)); nm_assert(c_list_contains(&ip_data->data->data_lst_head, &ip_data->data_lst)); nm_assert(ip_data->data->ifindex == nm_ip_config_get_ifindex(ip_data->ip_config)); +#if NM_MORE_ASSERTS > 5 + { + gboolean has_default = FALSE; + gsize i; + + for (i = 0; ip_data->domains.search && ip_data->domains.search; i++) { + const char *d = ip_data->domains.search[i]; + + d = nm_utils_parse_dns_domain(d, NULL); + nm_assert(d); + if (d[0] == '\0') + has_default = TRUE; + } + nm_assert(has_default == ip_data->domains.has_default_route_explicit); + if (ip_data->domains.has_default_route_explicit) + nm_assert(ip_data->domains.has_default_route_exclusive); + if (ip_data->domains.has_default_route_exclusive) + nm_assert(ip_data->domains.has_default_route); + } +#endif } static NMDnsIPConfigData * @@ -1358,6 +1378,9 @@ rebuild_domain_lists(NMDnsManager *self) c_list_for_each_entry (ip_data, head, ip_config_lst) { nm_assert(!ip_data->domains.search); nm_assert(!ip_data->domains.reverse); + nm_assert(!ip_data->domains.has_default_route_explicit); + nm_assert(!ip_data->domains.has_default_route_exclusive); + nm_assert(!ip_data->domains.has_default_route); } #endif @@ -1398,8 +1421,11 @@ rebuild_domain_lists(NMDnsManager *self) guint n_domains; guint num_dom1; guint num_dom2; - guint cap_dom; + guint n_domains_allocated; guint i; + gboolean has_default_route_maybe = FALSE; + gboolean has_default_route_explicit = FALSE; + gboolean has_default_route_auto = FALSE; if (!nm_ip_config_get_num_nameservers(ip_config)) continue; @@ -1413,12 +1439,6 @@ rebuild_domain_lists(NMDnsManager *self) nm_assert(prev_priority <= priority); prev_priority = priority; - cap_dom = 2u + NM_MAX(n_domains, n_searches); - - domains = g_new(const char *, cap_dom); - - num_dom1 = 0; - /* Add wildcard lookup domain to connections with the default route. * If there is no default route, add the wildcard domain to all non-VPN * connections */ @@ -1429,12 +1449,17 @@ rebuild_domain_lists(NMDnsManager *self) * whether it is suitable for certain operations (like having an automatically * added "~" domain). */ if (g_hash_table_contains(wildcard_entries, ip_data)) - domains[num_dom1++] = "~"; + has_default_route_maybe = TRUE; } else { if (ip_data->ip_config_type != NM_DNS_IP_CONFIG_TYPE_VPN) - domains[num_dom1++] = "~"; + has_default_route_maybe = TRUE; } + n_domains_allocated = (n_searches > 0 ? n_searches : n_domains) + 1u; + domains = g_new(const char *, n_domains_allocated); + + num_dom1 = 0; + /* searches are preferred over domains */ if (n_searches > 0) { for (i = 0; i < n_searches; i++) @@ -1444,32 +1469,55 @@ rebuild_domain_lists(NMDnsManager *self) domains[num_dom1++] = nm_ip_config_get_domain(ip_config, i); } - nm_assert(num_dom1 < cap_dom); + nm_assert(num_dom1 < n_domains_allocated); num_dom2 = 0; - for (i = 0; i < num_dom1; i++) { + for (i = 0; TRUE; i++) { + const char *domain_full; const char *domain_clean; const char *parent; int old_priority; int parent_priority; - - domain_clean = nm_utils_parse_dns_domain(domains[i], NULL); + gboolean check_default_route; + + if (i < num_dom1) { + check_default_route = FALSE; + domain_full = domains[i]; + domain_clean = nm_utils_parse_dns_domain(domains[i], NULL); + } else if (i == num_dom1) { + if (!has_default_route_maybe) + continue; + if (has_default_route_explicit) + continue; + check_default_route = TRUE; + domain_full = "~"; + domain_clean = ""; + } else + break; /* Remove domains with lower priority */ if (domain_ht_get_priority(ht, domain_clean, &old_priority)) { nm_assert(old_priority <= priority); if (old_priority < priority) { - _LOGT( - "plugin: drop domain '%s' (i=%d, p=%d) because it already exists with p=%d", - domains[i], - ip_data->data->ifindex, - priority, - old_priority); + _LOGT("plugin: drop domain %s%s%s (i=%d, p=%d) because it already exists " + "with p=%d", + NM_PRINT_FMT_QUOTED(!check_default_route, + "'", + domain_full, + "'", + "<auto-default>"), + ip_data->data->ifindex, + priority, + old_priority); continue; } } else if (domain_is_shadowed(ht, domain_clean, priority, &parent, &parent_priority)) { - _LOGT("plugin: drop domain '%s' (i=%d, p=%d) shadowed by '%s' (p=%d)", - domains[i], + _LOGT("plugin: drop domain %s%s%s (i=%d, p=%d) shadowed by '%s' (p=%d)", + NM_PRINT_FMT_QUOTED(!check_default_route, + "'", + domain_full, + "'", + "<auto-default>"), ip_data->data->ifindex, priority, parent, @@ -1477,22 +1525,38 @@ rebuild_domain_lists(NMDnsManager *self) continue; } - _LOGT("plugin: add domain '%s' (i=%d, p=%d)", - domains[i], - ip_data->data->ifindex, - priority); + _LOGT( + "plugin: add domain %s%s%s (i=%d, p=%d)", + NM_PRINT_FMT_QUOTED(!check_default_route, "'", domain_full, "'", "<auto-default>"), + ip_data->data->ifindex, + priority); + if (!ht) ht = g_hash_table_new(nm_str_hash, g_str_equal); g_hash_table_insert(ht, (gpointer) domain_clean, GINT_TO_POINTER(priority)); - domains[num_dom2++] = domains[i]; + + if (check_default_route) + has_default_route_auto = TRUE; + else { + nm_assert(num_dom2 <= num_dom1); + nm_assert(num_dom2 < n_domains_allocated); + domains[num_dom2++] = domain_full; + if (domain_clean[0] == '\0') + has_default_route_explicit = TRUE; + } } - nm_assert(num_dom2 < cap_dom); + nm_assert(num_dom2 < n_domains_allocated); domains[num_dom2] = NULL; nm_assert(!ip_data->domains.search); nm_assert(!ip_data->domains.reverse); - ip_data->domains.search = domains; - ip_data->domains.reverse = get_ip_rdns_domains(ip_config); + ip_data->domains.search = domains; + ip_data->domains.reverse = get_ip_rdns_domains(ip_config); + ip_data->domains.has_default_route_explicit = has_default_route_explicit; + ip_data->domains.has_default_route_exclusive = + has_default_route_explicit || (priority < 0 && has_default_route_auto); + ip_data->domains.has_default_route = + ip_data->domains.has_default_route_exclusive || has_default_route_auto; } } @@ -1506,6 +1570,9 @@ clear_domain_lists(NMDnsManager *self) c_list_for_each_entry (ip_data, head, ip_config_lst) { nm_clear_g_free(&ip_data->domains.search); nm_clear_pointer(&ip_data->domains.reverse, g_strfreev); + ip_data->domains.has_default_route_explicit = FALSE; + ip_data->domains.has_default_route_exclusive = FALSE; + ip_data->domains.has_default_route = FALSE; } } diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h index dc305a68ba..37d62a124e 100644 --- a/src/dns/nm-dns-manager.h +++ b/src/dns/nm-dns-manager.h @@ -37,6 +37,28 @@ typedef struct { struct { const char **search; char ** reverse; + + /* Whether "search" explicitly contains a default route "~" + * or "". It is redundant information, but for faster lookup. */ + bool has_default_route_explicit : 1; + + /* Whether an explicit "~" search domain should be added. + * For systemd-resolved, this configured an explicit wildcard + * search domain, and should be used for profiles with negative + * DNS priority. + * + * If "has_default_route_explicit", this is always TRUE and implied. + * + * With systemd-resolved, if TRUE we will set a "." search domain. + */ + bool has_default_route_exclusive : 1; + + /* Whether the device should be used for any domains "~". + * + * If "has_default_route_exclusive", this is always TRUE and implied. + * + * With systemd-resolved, this is the value for SetLinkDefaultRoute(). */ + bool has_default_route : 1; } domains; } NMDnsIPConfigData; diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c index f1d08f67be..26e26d202d 100644 --- a/src/dns/nm-dns-systemd-resolved.c +++ b/src/dns/nm-dns-systemd-resolved.c @@ -141,18 +141,18 @@ update_add_ip_config(NMDnsSystemdResolved *self, GVariantBuilder * domains, NMDnsIPConfigData * data) { - int addr_family; - gsize addr_size; - guint i, n; - gboolean is_routing; - const char **iter; - const char * domain; - gboolean has_config = FALSE; + int addr_family; + gsize addr_size; + guint i, n; + gboolean is_routing; + const char *domain; + gboolean has_config = FALSE; addr_family = nm_ip_config_get_addr_family(data->ip_config); addr_size = nm_utils_addr_family_to_size(addr_family); - if (!data->domains.search || !data->domains.search[0]) + if ((!data->domains.search || !data->domains.search[0]) + && !data->domains.has_default_route_exclusive) return FALSE; n = nm_ip_config_get_num_nameservers(data->ip_config); @@ -169,11 +169,17 @@ update_add_ip_config(NMDnsSystemdResolved *self, has_config = TRUE; } - for (iter = data->domains.search; *iter; iter++) { - domain = nm_utils_parse_dns_domain(*iter, &is_routing); - g_variant_builder_add(domains, "(sb)", domain[0] ? domain : ".", is_routing); + if (!data->domains.has_default_route_explicit && data->domains.has_default_route_exclusive) { + g_variant_builder_add(domains, "(sb)", ".", TRUE); has_config = TRUE; } + if (data->domains.search) { + for (i = 0; data->domains.search[i]; i++) { + domain = nm_utils_parse_dns_domain(data->domains.search[i], &is_routing); + g_variant_builder_add(domains, "(sb)", domain[0] ? domain : ".", is_routing); + has_config = TRUE; + } + } return has_config; } @@ -198,7 +204,8 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT; const char * mdns_arg = NULL, *llmnr_arg = NULL; - gboolean has_config = FALSE; + gboolean has_config = FALSE; + gboolean has_default_route = FALSE; g_variant_builder_init(&dns, G_VARIANT_TYPE("(ia(iay))")); g_variant_builder_add(&dns, "i", ic->ifindex); @@ -214,6 +221,9 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) has_config |= update_add_ip_config(self, &dns, &domains, data); + if (data->domains.has_default_route) + has_default_route = TRUE; + if (NM_IS_IP4_CONFIG(ip_config)) { mdns = NM_MAX(mdns, nm_ip4_config_mdns_get(NM_IP4_CONFIG(ip_config))); llmnr = NM_MAX(llmnr, nm_ip4_config_llmnr_get(NM_IP4_CONFIG(ip_config))); @@ -263,6 +273,9 @@ prepare_one_interface(NMDnsSystemdResolved *self, InterfaceConfig *ic) "SetLinkDomains", g_variant_builder_end(&domains)); _request_item_append(&priv->request_queue_lst_head, + "SetLinkDefaultRoute", + g_variant_new("(ib)", ic->ifindex, has_default_route)); + _request_item_append(&priv->request_queue_lst_head, "SetLinkMulticastDNS", g_variant_new("(is)", ic->ifindex, mdns_arg ?: "")); _request_item_append(&priv->request_queue_lst_head, |