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

(cherry picked from commit ee9fab0361)
(cherry picked from commit b8dab47705)
This commit is contained in:
Thomas Haller 2020-11-19 17:38:39 +01:00
parent 5239cb1e07
commit 4d8ba83dff
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
4 changed files with 147 additions and 34 deletions

View file

@ -788,14 +788,18 @@ add_ip_config (NMDnsDnsmasq *self, GVariantBuilder *servers, const NMDnsIPConfig
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, servers,

View file

@ -218,6 +218,26 @@ _ASSERT_ip_config_data (const NMDnsIPConfigData *ip_data)
nm_assert (NM_IS_IP_CONFIG (ip_data->ip_config, AF_UNSPEC));
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 *
@ -1330,6 +1350,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
@ -1370,8 +1393,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;
@ -1385,12 +1411,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 */
@ -1401,12 +1421,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++)
@ -1416,47 +1441,93 @@ 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;
gboolean check_default_route;
domain_clean = nm_utils_parse_dns_domain (domains[i], NULL);
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],
ip_data->data->ifindex, priority,
parent, parent_priority);
_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,
parent_priority);
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.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;
}
}
@ -1470,6 +1541,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;
}
}

View file

@ -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;

View file

@ -146,14 +146,14 @@ update_add_ip_config (NMDnsSystemdResolved *self,
gsize addr_size;
guint i, n;
gboolean is_routing;
const char **iter;
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;
}
@ -200,6 +206,7 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic)
NMSettingConnectionLlmnr llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
const char *mdns_arg = NULL, *llmnr_arg = NULL;
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);
@ -215,6 +222,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)));
@ -265,6 +275,9 @@ prepare_one_interface (NMDnsSystemdResolved *self, InterfaceConfig *ic)
_request_item_append (&priv->request_queue_lst_head,
"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 ?: ""));