From 1cba0a3cca80d2dc8436d8ffdf58506d9eb95846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8D=C3=B1igo=20Huguet?= Date: Mon, 25 Aug 2025 08:58:21 +0200 Subject: [PATCH] dns: make [global-dns] to overwrite configs from connections According to the documentation, settings from [global-dns] (searches and options) are always merged with those from connections. However this was not happening if no [global-dns-domain-*] exists, in which case connections were ignored. This happened because in the past both global sections must de defined or undefined. When this was changed to allow defining only [global-dns], allowing it in the function that generates the resolv.conf file was forgotten. Fix that now. Anyway, merging these configs doesn't make much sense. The searches and options defined in connections probably make sense only for the nameservers defined in that same connection. Because of this, make the following change: if global nameservers are defined, use searches and options from [global-dns] only, because those defined in connections may not make sense for the global nameservers. If [global-dns] is missing, assume an empty [global-dns] section. Also, if no global nameservers are defined, but [global-dns] is, make that it overwrites the searches and options defined in connections. This is not ideal, but none of the alternatives is better and at least this is easy to remember. So, the resulting rules from above are: - If [global-dns] is defined, it always overwrite searches and options from connections. - If [global-dns-domain-*] is defined, it always overwrite nameservers from connections. It overwrites searches and options too. Fixes: 1f0d1d78d2a2 ('dns-manager: always apply options from [global-dns]') Fixes: f57a848da5aa ('man: update documentation about global DNS configuration') --- man/NetworkManager.conf.xml | 12 +++++- src/core/dns/nm-dns-manager.c | 78 +++++++++++++++++++++++------------ src/core/nm-config-data.c | 17 +++++++- src/core/nm-config-data.h | 1 + src/core/nm-l3-config-data.c | 23 ++++++----- src/core/nm-l3-config-data.h | 3 +- 6 files changed, 94 insertions(+), 40 deletions(-) diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 2b42755971..3f9aec790b 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -1540,8 +1540,12 @@ managed=1 <literal>global-dns</literal> section - This section specifies DNS settings that are applied - globally, in addition to connection-specific ones. + This section specifies DNS settings that are applied globally. They + override the equivalent options defined in individual connections, making + them to be ignored. If a [global-dns-domain-*] section is defined, but this + section isn't, an empty [global-dns] section is assumed, thus overwriting + connection specific configurations too. + @@ -1601,6 +1605,10 @@ managed=1 default domain "*". When the global DNS domains are valid, the name servers and domains defined globally override the ones from active connections. + + If any global DNS domain is defined but a [global-dns] section isn't, + an empty [global-dns] section is assumed, thus overwriting its + connection specific configurations too. diff --git a/src/core/dns/nm-dns-manager.c b/src/core/dns/nm-dns-manager.c index d47590dc24..57e732264c 100644 --- a/src/core/dns/nm-dns-manager.c +++ b/src/core/dns/nm-dns-manager.c @@ -586,7 +586,11 @@ add_dns_domains(GPtrArray *array, } static void -merge_one_l3cd(NMResolvConfData *rc, int addr_family, int ifindex, const NML3ConfigData *l3cd) +merge_one_l3cd(NMResolvConfData *rc, + int addr_family, + int ifindex, + const NML3ConfigData *l3cd, + gboolean ignore_searches_and_options) { char buf[NM_INET_ADDRSTRLEN + 50]; gboolean has_trust_ad; @@ -624,30 +628,32 @@ merge_one_l3cd(NMResolvConfData *rc, int addr_family, int ifindex, const NML3Con add_string_item(rc->nameservers, buf, TRUE); } - add_dns_domains(rc->searches, addr_family, l3cd, FALSE, TRUE); + if (!ignore_searches_and_options) { + add_dns_domains(rc->searches, addr_family, l3cd, FALSE, TRUE); - has_trust_ad = FALSE; - strarr = nm_l3_config_data_get_dns_options(l3cd, addr_family, &num); - for (i = 0; i < num; i++) { - const char *option = strarr[i]; + has_trust_ad = FALSE; + strarr = nm_l3_config_data_get_dns_options(l3cd, addr_family, &num); + for (i = 0; i < num; i++) { + const char *option = strarr[i]; - if (nm_streq(option, NM_SETTING_DNS_OPTION_TRUST_AD)) { - has_trust_ad = TRUE; - continue; + if (nm_streq(option, NM_SETTING_DNS_OPTION_TRUST_AD)) { + has_trust_ad = TRUE; + continue; + } + add_dns_option_item(rc->options, option); } - add_dns_option_item(rc->options, option); - } - if (num_nameservers == 0) { - /* If the @l3cd contributes no DNS servers, ignore whether trust-ad is set or unset - * for this @l3cd. */ - } else if (has_trust_ad) { - /* We only set has_trust_ad to TRUE, if all IP configs agree (or don't contribute). - * Once set to FALSE, it doesn't get reset. */ - if (rc->has_trust_ad == NM_TERNARY_DEFAULT) - rc->has_trust_ad = NM_TERNARY_TRUE; - } else - rc->has_trust_ad = NM_TERNARY_FALSE; + if (num_nameservers == 0) { + /* If the @l3cd contributes no DNS servers, ignore whether trust-ad is set or unset + * for this @l3cd. */ + } else if (has_trust_ad) { + /* We only set has_trust_ad to TRUE, if all IP configs agree (or don't contribute). + * Once set to FALSE, it doesn't get reset. */ + if (rc->has_trust_ad == NM_TERNARY_DEFAULT) + rc->has_trust_ad = NM_TERNARY_TRUE; + } else + rc->has_trust_ad = NM_TERNARY_FALSE; + } if (addr_family == AF_INET) { const in_addr_t *nis_servers; @@ -1231,12 +1237,15 @@ compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[ { nm_auto_free_checksum GChecksum *sum = NULL; NMDnsConfigIPData *ip_data; + gboolean has_global_dns_section = FALSE; sum = g_checksum_new(G_CHECKSUM_SHA1); nm_assert(HASH_LEN == g_checksum_type_get_length(G_CHECKSUM_SHA1)); - if (global) + if (global) { nm_global_dns_config_update_checksum(global, sum); + has_global_dns_section = nm_global_dns_has_global_dns_section(global); + } if (!global || !nm_global_dns_config_lookup_domain(global, "*")) { const CList *head; @@ -1248,7 +1257,8 @@ compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[ nm_l3_config_data_hash_dns(ip_data->l3cd, sum, ip_data->addr_family, - ip_data->ip_config_type); + ip_data->ip_config_type, + has_global_dns_section); } } @@ -1264,6 +1274,9 @@ merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf) const char *const *servers; guint i; + /* Global config must be processed before connections' config */ + nm_assert(rc->nameservers->len == 0); + if (!global_conf) return FALSE; @@ -1351,12 +1364,17 @@ _collect_resolv_conf_data(NMDnsManager *self, .nis_servers = g_ptr_array_new(), .has_trust_ad = NM_TERNARY_DEFAULT, }; + gboolean has_global_dns_section = FALSE; priv = NM_DNS_MANAGER_GET_PRIVATE(self); - if (global_config) + if (global_config) { merge_global_dns_config(&rc, global_config); + has_global_dns_section = nm_global_dns_has_global_dns_section(global_config); + } + /* If global nameservers are defined, no DNS configs are used from connections at all, + * including searches and options. */ if (!global_config || !nm_global_dns_config_lookup_domain(global_config, "*")) { nm_auto_str_buf NMStrBuf tmp_strbuf = NM_STR_BUF_INIT(0, FALSE); int first_prio = 0; @@ -1390,8 +1408,16 @@ _collect_resolv_conf_data(NMDnsManager *self, skip ? "" : "", get_nameserver_list(ip_data->addr_family, ip_data->l3cd, &tmp_strbuf)); - if (!skip) - merge_one_l3cd(&rc, ip_data->addr_family, ip_data->data->ifindex, ip_data->l3cd); + if (!skip) { + /* Merge the configs from connections. However, if there was a [global-dns] + * it overwrites searches and options from the connections, thus we only + * merge the nameservers. */ + merge_one_l3cd(&rc, + ip_data->addr_family, + ip_data->data->ifindex, + ip_data->l3cd, + has_global_dns_section); + } } } diff --git a/src/core/nm-config-data.c b/src/core/nm-config-data.c index a23d622fa3..24a2d8c922 100644 --- a/src/core/nm-config-data.c +++ b/src/core/nm-config-data.c @@ -50,9 +50,10 @@ struct _NMGlobalDnsConfig { char **options; GHashTable *domains; const char **domain_list; - gboolean internal; char *cert_authority; NMDnsResolveMode resolve_mode; + gboolean internal; + gboolean has_global_dns; }; /*****************************************************************************/ @@ -941,6 +942,14 @@ next: /*****************************************************************************/ +gboolean +nm_global_dns_has_global_dns_section(const NMGlobalDnsConfig *dns_config) +{ + g_return_val_if_fail(dns_config, FALSE); + + return dns_config->has_global_dns; +} + const char *const * nm_global_dns_config_get_searches(const NMGlobalDnsConfig *dns_config) { @@ -1386,6 +1395,12 @@ load_global_dns(GKeyFile *keyfile, gboolean internal) return NULL; } + /* Defining [global-dns-domain-*] implies defining [global-dns] too (maybe empty) */ + if (default_found) + dns_config->has_global_dns = TRUE; + else + dns_config->has_global_dns = g_key_file_has_group(keyfile, group); + dns_config->internal = internal; global_dns_config_seal_domains(dns_config); return dns_config; diff --git a/src/core/nm-config-data.h b/src/core/nm-config-data.h index d764a7c45f..6666ccf6e8 100644 --- a/src/core/nm-config-data.h +++ b/src/core/nm-config-data.h @@ -274,6 +274,7 @@ gboolean nm_config_data_is_intern_atomic_group(const NMConfigData *self, const c GKeyFile *nm_config_data_clone_keyfile_intern(const NMConfigData *self); +gboolean nm_global_dns_has_global_dns_section(const NMGlobalDnsConfig *dns_config); const char *const *nm_global_dns_config_get_searches(const NMGlobalDnsConfig *dns_config); const char *const *nm_global_dns_config_get_options(const NMGlobalDnsConfig *dns_config); const char *nm_global_dns_config_get_certification_authority(const NMGlobalDnsConfig *dns_config); diff --git a/src/core/nm-l3-config-data.c b/src/core/nm-l3-config-data.c index 2062314581..666aa8a38c 100644 --- a/src/core/nm-l3-config-data.c +++ b/src/core/nm-l3-config-data.c @@ -3139,7 +3139,8 @@ void nm_l3_config_data_hash_dns(const NML3ConfigData *l3cd, GChecksum *sum, int addr_family, - NMDnsIPConfigType dns_ip_config_type) + NMDnsIPConfigType dns_ip_config_type, + gboolean ignore_searches_and_options) { guint i; int val; @@ -3178,16 +3179,18 @@ nm_l3_config_data_hash_dns(const NML3ConfigData *l3cd, empty = FALSE; } - searches = nm_l3_config_data_get_searches(l3cd, addr_family, &num_searches); - for (i = 0; i < num_searches; i++) { - g_checksum_update(sum, (const guint8 *) searches[i], strlen(searches[i])); - empty = FALSE; - } + if (!ignore_searches_and_options) { + searches = nm_l3_config_data_get_searches(l3cd, addr_family, &num_searches); + for (i = 0; i < num_searches; i++) { + g_checksum_update(sum, (const guint8 *) searches[i], strlen(searches[i])); + empty = FALSE; + } - options = nm_l3_config_data_get_dns_options(l3cd, addr_family, &num_options); - for (i = 0; i < num_options; i++) { - g_checksum_update(sum, (const guint8 *) options[i], strlen(options[i])); - empty = FALSE; + options = nm_l3_config_data_get_dns_options(l3cd, addr_family, &num_options); + for (i = 0; i < num_options; i++) { + g_checksum_update(sum, (const guint8 *) options[i], strlen(options[i])); + empty = FALSE; + } } val = nm_l3_config_data_get_mdns(l3cd); diff --git a/src/core/nm-l3-config-data.h b/src/core/nm-l3-config-data.h index bcb6af6777..265e126d89 100644 --- a/src/core/nm-l3-config-data.h +++ b/src/core/nm-l3-config-data.h @@ -615,6 +615,7 @@ nmtst_l3_config_data_get_best_gateway(const NML3ConfigData *self, int addr_famil void nm_l3_config_data_hash_dns(const NML3ConfigData *l3cd, GChecksum *sum, int addr_family, - NMDnsIPConfigType dns_ip_config_type); + NMDnsIPConfigType dns_ip_config_type, + gboolean ignore_searches_and_options); #endif /* __NM_L3_CONFIG_DATA_H__ */