core: compare the DNS configurations before updating DNS
DNS manager always sets `priv->config_changed = TRUE` and overwrites the "resolv.conf" file. To fix it, compare the new configuration with the old configuration, if there is no change, skipping the update. Fixes-test: @ipv4_ignore_resolveconf_with_ignore_auto_dns Fixes-test: @ipv4_ignore_resolveconf_with_ignore_auto_dns_var1 https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1023
This commit is contained in:

committed by
Beniamino Galvani

parent
b85a9cd9df
commit
8995d44a0b
@@ -35,6 +35,7 @@
|
||||
#include "nm-dns-plugin.h"
|
||||
#include "nm-dns-systemd-resolved.h"
|
||||
#include "nm-dns-unbound.h"
|
||||
#include "nm-ip-config.h"
|
||||
#include "nm-l3-config-data.h"
|
||||
#include "nm-manager.h"
|
||||
#include "nm-utils.h"
|
||||
@@ -99,6 +100,9 @@ typedef struct {
|
||||
char *hostname;
|
||||
guint updates_queue;
|
||||
|
||||
guint8 hash[HASH_LEN]; /* SHA1 hash of current DNS config */
|
||||
guint8 prev_hash[HASH_LEN]; /* Hash when begin_updates() was called */
|
||||
|
||||
NMDnsManagerResolvConfManager rc_manager;
|
||||
char * mode;
|
||||
NMDnsPlugin * sd_resolve_plugin;
|
||||
@@ -1097,6 +1101,30 @@ update_resolv_conf(NMDnsManager * self,
|
||||
return write_file_result;
|
||||
}
|
||||
|
||||
static void
|
||||
compute_hash(NMDnsManager *self, const NMGlobalDnsConfig *global, guint8 buffer[static HASH_LEN])
|
||||
{
|
||||
nm_auto_free_checksum GChecksum *sum = NULL;
|
||||
NMDnsConfigIPData * ip_data;
|
||||
|
||||
sum = g_checksum_new(G_CHECKSUM_SHA1);
|
||||
nm_assert(HASH_LEN == g_checksum_type_get_length(G_CHECKSUM_SHA1));
|
||||
|
||||
if (global)
|
||||
nm_global_dns_config_update_checksum(global, sum);
|
||||
else {
|
||||
const CList *head;
|
||||
|
||||
/* FIXME(ip-config-checksum): this relies on the fact that an IP
|
||||
* configuration without DNS parameters gives a zero checksum. */
|
||||
head = _mgr_get_ip_data_lst_head(self);
|
||||
c_list_for_each_entry (ip_data, head, ip_data_lst)
|
||||
nm_ip_config_dns_hash(ip_data->l3cd, sum, ip_data->addr_family);
|
||||
}
|
||||
|
||||
nm_utils_checksum_get_digest_len(sum, buffer, HASH_LEN);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
merge_global_dns_config(NMResolvConfData *rc, NMGlobalDnsConfig *global_conf)
|
||||
{
|
||||
@@ -1649,6 +1677,9 @@ update_dns(NMDnsManager *self, gboolean no_caching, gboolean force_emit, GError
|
||||
data = nm_config_get_data(priv->config);
|
||||
global_config = nm_config_data_get_global_dns_config(data);
|
||||
|
||||
/* Update hash with config we're applying */
|
||||
compute_hash(self, global_config, priv->hash);
|
||||
|
||||
_collect_resolv_conf_data(self,
|
||||
global_config,
|
||||
&searches,
|
||||
@@ -2006,6 +2037,10 @@ nm_dns_manager_begin_updates(NMDnsManager *self, const char *func)
|
||||
|
||||
priv = NM_DNS_MANAGER_GET_PRIVATE(self);
|
||||
|
||||
/* Save current hash when starting a new batch */
|
||||
if (priv->updates_queue == 0)
|
||||
memcpy(priv->prev_hash, priv->hash, sizeof(priv->hash));
|
||||
|
||||
priv->updates_queue++;
|
||||
|
||||
_LOGD("(%s): queueing DNS updates (%d)", func, priv->updates_queue);
|
||||
@@ -2016,12 +2051,15 @@ nm_dns_manager_end_updates(NMDnsManager *self, const char *func)
|
||||
{
|
||||
NMDnsManagerPrivate *priv;
|
||||
gs_free_error GError *error = NULL;
|
||||
guint8 new[HASH_LEN];
|
||||
|
||||
g_return_if_fail(self != NULL);
|
||||
|
||||
priv = NM_DNS_MANAGER_GET_PRIVATE(self);
|
||||
g_return_if_fail(priv->updates_queue > 0);
|
||||
|
||||
compute_hash(self, nm_config_data_get_global_dns_config(nm_config_get_data(priv->config)), new);
|
||||
priv->config_changed = (memcmp(new, priv->prev_hash, sizeof(new)) != 0) ? TRUE : FALSE;
|
||||
_LOGD("(%s): DNS configuration %s", func, priv->config_changed ? "changed" : "did not change");
|
||||
|
||||
priv->updates_queue--;
|
||||
@@ -2574,6 +2612,7 @@ nm_dns_manager_init(NMDnsManager *self)
|
||||
(GDestroyNotify) _dns_config_data_free,
|
||||
NULL);
|
||||
|
||||
compute_hash(self, NULL, NM_DNS_MANAGER_GET_PRIVATE(self)->hash);
|
||||
g_signal_connect(G_OBJECT(priv->config),
|
||||
NM_CONFIG_SIGNAL_CONFIG_CHANGED,
|
||||
G_CALLBACK(config_changed_cb),
|
||||
|
@@ -982,6 +982,61 @@ nm_global_dns_config_cmp(const NMGlobalDnsConfig *a,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nm_global_dns_config_update_checksum(const NMGlobalDnsConfig *dns_config, GChecksum *sum)
|
||||
{
|
||||
NMGlobalDnsDomain *domain;
|
||||
guint i, j;
|
||||
guint8 v8;
|
||||
|
||||
g_return_if_fail(dns_config);
|
||||
g_return_if_fail(sum);
|
||||
|
||||
v8 = NM_HASH_COMBINE_BOOLS(guint8,
|
||||
!dns_config->searches,
|
||||
!dns_config->options,
|
||||
!dns_config->domain_list);
|
||||
g_checksum_update(sum, (guchar *) &v8, 1);
|
||||
|
||||
if (dns_config->searches) {
|
||||
for (i = 0; dns_config->searches[i]; i++)
|
||||
g_checksum_update(sum,
|
||||
(guchar *) dns_config->searches[i],
|
||||
strlen(dns_config->searches[i]) + 1);
|
||||
}
|
||||
if (dns_config->options) {
|
||||
for (i = 0; dns_config->options[i]; i++)
|
||||
g_checksum_update(sum,
|
||||
(guchar *) dns_config->options[i],
|
||||
strlen(dns_config->options[i]) + 1);
|
||||
}
|
||||
|
||||
if (dns_config->domain_list) {
|
||||
for (i = 0; dns_config->domain_list[i]; i++) {
|
||||
domain = g_hash_table_lookup(dns_config->domains, dns_config->domain_list[i]);
|
||||
nm_assert(domain);
|
||||
|
||||
v8 = NM_HASH_COMBINE_BOOLS(guint8, !domain->servers, !domain->options);
|
||||
g_checksum_update(sum, (guchar *) &v8, 1);
|
||||
|
||||
g_checksum_update(sum, (guchar *) domain->name, strlen(domain->name) + 1);
|
||||
|
||||
if (domain->servers) {
|
||||
for (j = 0; domain->servers[j]; j++)
|
||||
g_checksum_update(sum,
|
||||
(guchar *) domain->servers[j],
|
||||
strlen(domain->servers[j]) + 1);
|
||||
}
|
||||
if (domain->options) {
|
||||
for (j = 0; domain->options[j]; j++)
|
||||
g_checksum_update(sum,
|
||||
(guchar *) domain->options[j],
|
||||
strlen(domain->options[j]) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
global_dns_domain_free(NMGlobalDnsDomain *domain)
|
||||
{
|
||||
|
@@ -267,7 +267,8 @@ gboolean nm_global_dns_config_is_empty(const NMGlobalDnsConfig *dns_co
|
||||
int nm_global_dns_config_cmp(const NMGlobalDnsConfig *a,
|
||||
const NMGlobalDnsConfig *b,
|
||||
gboolean check_internal);
|
||||
void nm_global_dns_config_free(NMGlobalDnsConfig *dns_config);
|
||||
void nm_global_dns_config_update_checksum(const NMGlobalDnsConfig *dns_config, GChecksum *sum);
|
||||
void nm_global_dns_config_free(NMGlobalDnsConfig *dns_config);
|
||||
|
||||
NMGlobalDnsConfig *nm_global_dns_config_from_dbus(const GValue *value, GError **error);
|
||||
void nm_global_dns_config_to_dbus(const NMGlobalDnsConfig *dns_config, GValue *value);
|
||||
|
@@ -498,6 +498,77 @@ nm_ip4_config_class_init(NMIP4ConfigClass *klass)
|
||||
g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST_ip4, obj_properties_ip4);
|
||||
}
|
||||
|
||||
void
|
||||
nm_ip_config_dns_hash(const NML3ConfigData *l3cd, GChecksum *sum, int addr_family)
|
||||
{
|
||||
guint i;
|
||||
int val;
|
||||
const char *const *nameservers;
|
||||
const in_addr_t * wins;
|
||||
const char *const *domains;
|
||||
const char *const *searches;
|
||||
const char *const *options;
|
||||
guint num_nameservers;
|
||||
guint num_wins;
|
||||
guint num_domains;
|
||||
guint num_searches;
|
||||
guint num_options;
|
||||
|
||||
g_return_if_fail(l3cd);
|
||||
g_return_if_fail(sum);
|
||||
|
||||
nameservers = nm_l3_config_data_get_nameservers(l3cd, addr_family, &num_nameservers);
|
||||
for (i = 0; i < num_nameservers; i++) {
|
||||
g_checksum_update(sum,
|
||||
nm_ip_addr_from_packed_array(addr_family, nameservers, i),
|
||||
nm_utils_addr_family_to_size(addr_family));
|
||||
}
|
||||
|
||||
if (addr_family == AF_INET) {
|
||||
wins = nm_l3_config_data_get_wins(l3cd, &num_wins);
|
||||
for (i = 0; i < num_wins; i++)
|
||||
g_checksum_update(sum, (guint8 *) &wins[i], 4);
|
||||
}
|
||||
|
||||
domains = nm_l3_config_data_get_domains(l3cd, addr_family, &num_domains);
|
||||
for (i = 0; i < num_domains; i++) {
|
||||
g_checksum_update(sum, (const guint8 *) domains[i], strlen(domains[i]));
|
||||
}
|
||||
|
||||
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]));
|
||||
}
|
||||
|
||||
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]));
|
||||
}
|
||||
|
||||
val = nm_l3_config_data_get_mdns(l3cd);
|
||||
if (val != NM_SETTING_CONNECTION_MDNS_DEFAULT)
|
||||
g_checksum_update(sum, (const guint8 *) &val, sizeof(val));
|
||||
|
||||
val = nm_l3_config_data_get_llmnr(l3cd);
|
||||
if (val != NM_SETTING_CONNECTION_LLMNR_DEFAULT)
|
||||
g_checksum_update(sum, (const guint8 *) &val, sizeof(val));
|
||||
|
||||
val = nm_l3_config_data_get_dns_over_tls(l3cd);
|
||||
if (val != NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT)
|
||||
g_checksum_update(sum, (const guint8 *) &val, sizeof(val));
|
||||
|
||||
/* FIXME(ip-config-checksum): the DNS priority should be considered relevant
|
||||
* and added into the checksum as well, but this can't be done right now
|
||||
* because in the DNS manager we rely on the fact that an empty
|
||||
* configuration (i.e. just created) has a zero checksum. This is needed to
|
||||
* avoid rewriting resolv.conf when there is no change.
|
||||
*
|
||||
* The DNS priority initial value depends on the connection type (VPN or
|
||||
* not), so it's a bit difficult to add it to checksum maintaining the
|
||||
* assumption of checksum(empty)=0
|
||||
*/
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* public */
|
||||
|
@@ -51,6 +51,8 @@ NMIPConfig *nm_ip_config_new(int addr_family, NML3Cfg *l3cfg);
|
||||
|
||||
void nm_ip_config_take_and_unexport_on_idle(NMIPConfig *self_take);
|
||||
|
||||
void nm_ip_config_dns_hash(const NML3ConfigData *l3cd, GChecksum *sum, int addr_family);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static inline NML3Cfg *
|
||||
|
Reference in New Issue
Block a user