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:
Wen Liang
2021-11-10 21:13:38 -05:00
committed by Beniamino Galvani
parent b85a9cd9df
commit 8995d44a0b
5 changed files with 169 additions and 1 deletions

View File

@@ -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),

View File

@@ -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)
{

View File

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

View File

@@ -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 */

View File

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