From bdd2e1c2aa0c8340743b307c3e78b38cda2a6934 Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Fri, 17 Feb 2017 15:33:27 +0100 Subject: [PATCH 1/9] policy: try to retrieve hostname from dbus first As we try to set the hostname through dbus, we should also try to retrieve current hostname value from dbus first: otherwise we may end retrieving the "old" hostname via gethostname while the dbus hostnamed updated is pending. --- src/nm-policy.c | 71 ++++++++++++++++++++++++++++---------- src/settings/nm-settings.c | 22 ++++++++++++ src/settings/nm-settings.h | 3 ++ 3 files changed, 78 insertions(+), 18 deletions(-) diff --git a/src/nm-policy.c b/src/nm-policy.c index d8e083572b..ff93c9853d 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -431,15 +431,54 @@ settings_set_hostname_cb (const char *hostname, nm_dispatcher_call_hostname (NULL, NULL, NULL); } +#define HOST_NAME_BUFSIZE (HOST_NAME_MAX + 2) + +static char * +_get_hostname (NMPolicy *self, char **hostname) +{ + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + char *buf; + + g_assert (hostname && *hostname == NULL); + + /* try to get the hostname via dbus... */ + if (nm_settings_get_transient_hostname (priv->settings, hostname)) { + _LOGT (LOGD_DNS, "gethostname: \"%s\" (from dbus)", *hostname); + return *hostname; + } + + /* ...or retrieve it by yourself */ + buf = g_malloc (HOST_NAME_BUFSIZE); + if (gethostname (buf, HOST_NAME_BUFSIZE -1) != 0) { + int errsv = errno; + + _LOGT (LOGD_DNS, "gethostname: couldn't get the system hostname: (%d) %s", + errsv, g_strerror (errsv)); + g_free (buf); + return NULL; + } + + /* the name may be truncated... */ + buf[HOST_NAME_BUFSIZE - 1] = '\0'; + if (strlen (buf) >= HOST_NAME_BUFSIZE -1) { + _LOGT (LOGD_DNS, "gethostname: system hostname too long: \"%s\"", buf); + g_free (buf); + return NULL; + } + + _LOGT (LOGD_DNS, "gethostname: \"%s\"", buf); + *hostname = buf; + return *hostname; +} + static void _set_hostname (NMPolicy *self, const char *new_hostname, const char *msg) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - char old_hostname[HOST_NAME_MAX + 1]; + gs_free char *old_hostname = NULL; const char *name; - int ret; /* The incoming hostname *can* be NULL, which will get translated to * 'localhost.localdomain' or such in the hostname policy code, but we @@ -484,19 +523,14 @@ _set_hostname (NMPolicy *self, } else name = new_hostname; - old_hostname[HOST_NAME_MAX] = '\0'; - errno = 0; - ret = gethostname (old_hostname, HOST_NAME_MAX); - if (ret != 0) { - _LOGW (LOGD_DNS, "couldn't get the system hostname: (%d) %s", - errno, strerror (errno)); - } else { - /* Don't set the hostname if it isn't actually changing */ - if (nm_streq (name, old_hostname)) - return; + /* Don't set the hostname if it isn't actually changing */ + if ( _get_hostname (self, &old_hostname) + && (nm_streq (name, old_hostname))) { + _LOGT (LOGD_DNS, "sethostname: already set to '%s' (%s)", name, msg); + return; } - _LOGI (LOGD_DNS, "setting system hostname to '%s' (%s)", name, msg); + _LOGI (LOGD_DNS, "sethostname: '%s' (%s)", name, msg); /* Ask NMSettings to update the transient hostname using its * systemd-hostnamed proxy */ @@ -2184,14 +2218,15 @@ constructed (GObject *object) { NMPolicy *self = NM_POLICY (object); NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); - char hostname[HOST_NAME_MAX + 2]; + char *hostname = NULL; /* Grab hostname on startup and use that if nothing provides one */ - memset (hostname, 0, sizeof (hostname)); - if (gethostname (&hostname[0], HOST_NAME_MAX) == 0) { + if (_get_hostname (self, &hostname)) { /* only cache it if it's a valid hostname */ - if (*hostname && nm_utils_is_specific_hostname (hostname)) - priv->orig_hostname = g_strdup (hostname); + if (nm_utils_is_specific_hostname (hostname)) + priv->orig_hostname = hostname; + else + g_free (hostname); } priv->firewall_manager = g_object_ref (nm_firewall_manager_get ()); diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c index f738b4377a..afd1b0849c 100644 --- a/src/settings/nm-settings.c +++ b/src/settings/nm-settings.c @@ -1685,6 +1685,28 @@ nm_settings_set_transient_hostname (NMSettings *self, info); } +gboolean +nm_settings_get_transient_hostname (NMSettings *self, char **hostname) +{ + NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self); + GVariant *v_hostname; + + if (!priv->hostname.hostnamed_proxy) + return FALSE; + + v_hostname = g_dbus_proxy_get_cached_property (priv->hostname.hostnamed_proxy, + "Hostname"); + if (!v_hostname) { + _LOGT ("transient hostname retrieval failed"); + return FALSE; + } + + *hostname = g_variant_dup_string (v_hostname, NULL); + g_variant_unref (v_hostname); + + return TRUE; +} + static gboolean write_hostname (NMSettingsPrivate *priv, const char *hostname) { diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h index 406e305767..7110a12ba8 100644 --- a/src/settings/nm-settings.h +++ b/src/settings/nm-settings.h @@ -132,4 +132,7 @@ void nm_settings_set_transient_hostname (NMSettings *self, NMSettingsSetHostnameCb cb, gpointer user_data); +gboolean nm_settings_get_transient_hostname (NMSettings *self, + char **hostname); + #endif /* __NM_SETTINGS_H__ */ From b7557fa723991f6f49505794e39f9bddd22555a6 Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Mon, 27 Feb 2017 15:47:43 +0100 Subject: [PATCH 2/9] policy: remove redundant check in _set_hostname This will allow also to remove the hostname_changed var from NMPolicyPrivate struct. --- src/nm-policy.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/nm-policy.c b/src/nm-policy.c index ff93c9853d..5b04e7f231 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -87,7 +87,6 @@ typedef struct { char *orig_hostname; /* hostname at NM start time */ char *cur_hostname; /* hostname we want to assign */ - gboolean hostname_changed; /* TRUE if NM ever set the hostname */ GArray *ip6_prefix_delegations; /* pool of ip6 prefixes delegated to all devices */ } NMPolicyPrivate; @@ -492,20 +491,12 @@ _set_hostname (NMPolicy *self, if (new_hostname) g_clear_object (&priv->lookup_addr); - if ( priv->orig_hostname - && (priv->hostname_changed == FALSE) - && g_strcmp0 (priv->orig_hostname, new_hostname) == 0) { - /* Don't change the hostname or update DNS this is the first time we're - * trying to change the hostname, and it's not actually changing. - */ - } else if (g_strcmp0 (priv->cur_hostname, new_hostname) == 0) { - /* Don't change the hostname or update DNS if the hostname isn't actually - * going to change. - */ - } else { + /* Update the DNS only if the hostname is actually + * going to change. + */ + if (!nm_streq0 (priv->cur_hostname, new_hostname)) { g_free (priv->cur_hostname); priv->cur_hostname = g_strdup (new_hostname); - priv->hostname_changed = TRUE; /* Notify the DNS manager of the hostname change so that the domain part, if * present, can be added to the search list. From d34add6f0074f58ad0a232038c8ea26c74970d03 Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Fri, 17 Feb 2017 16:22:30 +0100 Subject: [PATCH 3/9] policy: detect if the hostname was changed outside NetworkManager When updating the hostname we can now detect if someone else changed the hostname: if so, search for hostname candidates in the dhcp configuration but avoid to fallback to the hostname saved when NM started or querying dns for a reverse lookup of the current IP. --- src/nm-policy.c | 89 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 74 insertions(+), 15 deletions(-) diff --git a/src/nm-policy.c b/src/nm-policy.c index 5b04e7f231..1a489d4445 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -87,6 +87,8 @@ typedef struct { char *orig_hostname; /* hostname at NM start time */ char *cur_hostname; /* hostname we want to assign */ + char *last_hostname; /* last hostname NM set (to detect if someone else changed it in the meanwhile) */ + gboolean changing_hostname; /* hostname set operation still in progress */ GArray *ip6_prefix_delegations; /* pool of ip6 prefixes delegated to all devices */ } NMPolicyPrivate; @@ -412,6 +414,8 @@ settings_set_hostname_cb (const char *hostname, gboolean result, gpointer user_data) { + NMPolicy *self = NM_POLICY (user_data); + NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); int ret = 0; if (!result) { @@ -426,8 +430,10 @@ settings_set_hostname_cb (const char *hostname, } } + priv->changing_hostname = FALSE; if (!ret) nm_dispatcher_call_hostname (NULL, NULL, NULL); + g_object_unref (self); } #define HOST_NAME_BUFSIZE (HOST_NAME_MAX + 2) @@ -440,6 +446,15 @@ _get_hostname (NMPolicy *self, char **hostname) g_assert (hostname && *hostname == NULL); + /* If there is an in-progress hostname change, return + * the last hostname set as would be set soon... + */ + if (priv->changing_hostname) { + _LOGT (LOGD_DNS, "gethostname: \"%s\" (last on set)", priv->last_hostname); + *hostname = g_strdup (priv->last_hostname); + return *hostname; + } + /* try to get the hostname via dbus... */ if (nm_settings_get_transient_hostname (priv->settings, hostname)) { _LOGT (LOGD_DNS, "gethostname: \"%s\" (from dbus)", *hostname); @@ -521,6 +536,11 @@ _set_hostname (NMPolicy *self, return; } + /* Keep track of the last set hostname */ + g_free (priv->last_hostname); + priv->last_hostname = g_strdup (name); + priv->changing_hostname = TRUE; + _LOGI (LOGD_DNS, "sethostname: '%s' (%s)", name, msg); /* Ask NMSettings to update the transient hostname using its @@ -528,7 +548,7 @@ _set_hostname (NMPolicy *self, nm_settings_set_transient_hostname (priv->settings, name, settings_set_hostname_cb, - NULL); + g_object_ref (self)); } static void @@ -563,19 +583,43 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); char *configured_hostname = NULL; + gs_free char *temp_hostname = NULL; const char *dhcp_hostname, *p; NMIP4Config *ip4_config; NMIP6Config *ip6_config; + gboolean external_hostname = FALSE; g_return_if_fail (self != NULL); nm_clear_g_cancellable (&priv->lookup_cancellable); + /* Check if the hostname was set externally to NM, so that in that case + * we can avoid to fallback to the one we got when we started. + * Consider "not specific" hostnames as equal. */ + if ( _get_hostname (self, &temp_hostname) + && !nm_streq0 (temp_hostname, priv->last_hostname) + && ( nm_utils_is_specific_hostname (temp_hostname) + || nm_utils_is_specific_hostname (priv->last_hostname))) { + external_hostname = TRUE; + _LOGI (LOGD_DNS, "current hostname was changed outside NetworkManager: '%s'", + temp_hostname); + + if (!nm_streq0 (temp_hostname, priv->orig_hostname)) { + /* Update original (fallback) hostname */ + g_free (priv->orig_hostname); + if (nm_utils_is_specific_hostname (temp_hostname)) { + priv->orig_hostname = temp_hostname; + temp_hostname = NULL; + } else + priv->orig_hostname = NULL; + } + } + /* Hostname precedence order: * * 1) a configured hostname (from settings) * 2) automatic hostname from the default device's config (DHCP, VPN, etc) - * 3) the original hostname when NM started + * 3) the last hostname set outside NM * 4) reverse-DNS of the best device's IPv4 address * */ @@ -595,14 +639,6 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) if (!best6) best6 = get_best_ip6_device (self, TRUE); - if (!best4 && !best6) { - /* No best device; fall back to original hostname or if there wasn't - * one, 'localhost.localdomain' - */ - _set_hostname (self, priv->orig_hostname, "no default device"); - return; - } - if (best4) { NMDhcp4Config *dhcp4_config; @@ -643,8 +679,20 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) } } - /* If no automatically-configured hostname, try using the hostname from - * when NM started up. + /* If an hostname was set outside NetworkManager keep it */ + if (external_hostname) + return; + + if (!best4 && !best6) { + /* No best device; fall back to the last hostname set externally + * to NM or if there wasn't one, 'localhost.localdomain' + */ + _set_hostname (self, priv->orig_hostname, "no default device"); + return; + } + + /* If no automatically-configured hostname, try using the last hostname + * set externally to NM */ if (priv->orig_hostname) { _set_hostname (self, priv->orig_hostname, "from system startup"); @@ -2004,6 +2052,15 @@ dns_config_changed (NMDnsManager *dns_manager, gpointer user_data) /* Re-start the hostname lookup thread if we don't have hostname yet. */ if (priv->lookup_addr) { char *str = NULL; + gs_free char *hostname = NULL; + + /* Check if the hostname was externally set */ + if ( _get_hostname (self, &hostname) + && nm_utils_is_specific_hostname (hostname) + && !nm_streq0 (hostname, priv->last_hostname)) { + g_clear_object (&priv->lookup_addr); + return; + } _LOGD (LOGD_DNS, "restarting reverse-lookup thread for address %s", (str = g_inet_address_to_string (priv->lookup_addr))); @@ -2213,11 +2270,12 @@ constructed (GObject *object) /* Grab hostname on startup and use that if nothing provides one */ if (_get_hostname (self, &hostname)) { + /* init last_hostname */ + priv->last_hostname = hostname; + /* only cache it if it's a valid hostname */ if (nm_utils_is_specific_hostname (hostname)) - priv->orig_hostname = hostname; - else - g_free (hostname); + priv->orig_hostname = g_strdup (hostname); } priv->firewall_manager = g_object_ref (nm_firewall_manager_get ()); @@ -2310,6 +2368,7 @@ dispose (GObject *object) g_clear_pointer (&priv->orig_hostname, g_free); g_clear_pointer (&priv->cur_hostname, g_free); + g_clear_pointer (&priv->last_hostname, g_free); if (priv->settings) { g_signal_handlers_disconnect_by_data (priv->settings, priv); From 7c4bd15ec698e960593a18bf36c8288bb011ed93 Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Fri, 3 Mar 2017 18:03:47 +0100 Subject: [PATCH 4/9] policy: skip dns and route updates on ip6 config change signal if the device is not active This pairs with commit 30a1e17cc032676cdfb04e2abcfab9db0d0cf085. --- src/nm-policy.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/nm-policy.c b/src/nm-policy.c index 1a489d4445..2b71d13538 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -1693,8 +1693,9 @@ device_ip4_config_changed (NMDevice *device, nm_dns_manager_begin_updates (priv->dns_manager, __func__); - /* Ignore IP config changes while the device is activating, because we'll - * catch all the changes when the device moves to ACTIVATED state. + /* We catch already all the IP events registering on the device state changes but + * the ones where the IP changes but the device state keep stable (i.e., activated): + * ignore IP config changes but when the device is in activated state. * Prevents unecessary changes to DNS information. */ if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { @@ -1728,11 +1729,12 @@ device_ip6_config_changed (NMDevice *device, nm_dns_manager_begin_updates (priv->dns_manager, __func__); - /* Ignore IP config changes while the device is activating, because we'll - * catch all the changes when the device moves to ACTIVATED state. + /* We catch already all the IP events registering on the device state changes but + * the ones where the IP changes but the device state keep stable (i.e., activated): + * ignore IP config changes but when the device is in activated state. * Prevents unecessary changes to DNS information. */ - if (!nm_device_is_activating (device)) { + if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) { if (old_config != new_config) { if (old_config) nm_dns_manager_remove_ip6_config (priv->dns_manager, old_config); From e22af1aa9e361c6b7338ef64abfd28cd3bbafccd Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Thu, 2 Mar 2017 17:45:44 +0100 Subject: [PATCH 5/9] nm-config: fix comment typo --- src/nm-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nm-config.c b/src/nm-config.c index 32faace562..cb4e0853cf 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -659,7 +659,7 @@ read_config (GKeyFile *keyfile, gboolean is_base_config, const char *dirname, co } /* the config-group is internal to every configuration snippets. It doesn't make sense - * to merge the into the global configuration, and it doesn't make sense to preserve the + * to merge it into the global configuration, and it doesn't make sense to preserve the * group beyond this point. */ g_key_file_remove_group (kf, NM_CONFIG_KEYFILE_GROUP_CONFIG, NULL); From 2eba42b4ab46261f34d075d46f7a5e5347eaf889 Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Thu, 2 Mar 2017 19:18:49 +0100 Subject: [PATCH 6/9] policy: add support to configurable hostname mode --- src/nm-config.h | 1 + src/nm-policy.c | 20 ++++++++++++++++++++ src/nm-policy.h | 17 +++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/src/nm-config.h b/src/nm-config.h index 9930df33e2..933d0b40d4 100644 --- a/src/nm-config.h +++ b/src/nm-config.h @@ -62,6 +62,7 @@ #define NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT "auth-polkit" #define NM_CONFIG_KEYFILE_KEY_MAIN_DHCP "dhcp" #define NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG "debug" +#define NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE "hostname-mode" #define NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND "backend" #define NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE "enable" #define NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS ".was" diff --git a/src/nm-policy.c b/src/nm-policy.c index 2b71d13538..e88872308e 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -47,6 +47,7 @@ #include "settings/nm-settings-connection.h" #include "nm-dhcp4-config.h" #include "nm-dhcp6-config.h" +#include "nm-config.h" /*****************************************************************************/ @@ -85,6 +86,7 @@ typedef struct { guint schedule_activate_all_id; /* idle handler for schedule_activate_all(). */ + NMPolicyHostnameMode hostname_mode; char *orig_hostname; /* hostname at NM start time */ char *cur_hostname; /* hostname we want to assign */ char *last_hostname; /* last hostname NM set (to detect if someone else changed it in the meanwhile) */ @@ -591,6 +593,9 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) g_return_if_fail (self != NULL); + if (priv->hostname_mode == NM_POLICY_HOSTNAME_MODE_NONE) + return; + nm_clear_g_cancellable (&priv->lookup_cancellable); /* Check if the hostname was set externally to NM, so that in that case @@ -683,6 +688,9 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) if (external_hostname) return; + if (priv->hostname_mode == NM_POLICY_HOSTNAME_MODE_DHCP) + return; + if (!best4 && !best6) { /* No best device; fall back to the last hostname set externally * to NM or if there wasn't one, 'localhost.localdomain' @@ -2257,6 +2265,18 @@ static void nm_policy_init (NMPolicy *self) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); + const char *hostname_mode; + + hostname_mode = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG, + NM_CONFIG_KEYFILE_GROUP_MAIN, + NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE, + NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY); + if (nm_streq0 (hostname_mode, "none")) + priv->hostname_mode = NM_POLICY_HOSTNAME_MODE_NONE; + else if (nm_streq0 (hostname_mode, "dhcp")) + priv->hostname_mode = NM_POLICY_HOSTNAME_MODE_DHCP; + else /* default - full mode */ + priv->hostname_mode = NM_POLICY_HOSTNAME_MODE_FULL; priv->devices = g_hash_table_new (NULL, NULL); priv->ip6_prefix_delegations = g_array_new (FALSE, FALSE, sizeof (IP6PrefixDelegation)); diff --git a/src/nm-policy.h b/src/nm-policy.h index 64cac4fe70..2c96e6d090 100644 --- a/src/nm-policy.h +++ b/src/nm-policy.h @@ -47,4 +47,21 @@ NMDevice *nm_policy_get_default_ip6_device (NMPolicy *policy); NMDevice *nm_policy_get_activating_ip4_device (NMPolicy *policy); NMDevice *nm_policy_get_activating_ip6_device (NMPolicy *policy); +/** + * NMPolicyHostnameMode + * @NM_POLICY_HOSTNAME_MODE_NONE: never update the transient hostname. + * @NM_POLICY_HOSTNAME_MODE_DHCP: only hostname from DHCP hostname + * options are eligible to be set as transient hostname. + * @NM_POLICY_HOSTNAME_MODE_FULL: NM will try to update the hostname looking + * to current static hostname, DHCP options, reverse IP lookup and externally + * set hostnames. + * + * NMPolicy's hostname update policy + */ +typedef enum { + NM_POLICY_HOSTNAME_MODE_NONE, + NM_POLICY_HOSTNAME_MODE_DHCP, + NM_POLICY_HOSTNAME_MODE_FULL, +} NMPolicyHostnameMode; + #endif /* __NETWORKMANAGER_POLICY_H__ */ From 8ffc68cc0efc01305236df1a240125d1fd8a8276 Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Fri, 3 Mar 2017 18:32:08 +0100 Subject: [PATCH 7/9] policy: allow reset of dhcp hostname in "dhcp" hostname-mode config. When dhcp hostname-mode is selected, NetworkManager will just update the hostname with information available from DHCP (if any). So, when a connection providing a DHCP host-name option is brought up we update the transient hostname. When it is later teared down, this will trigger NetworkManager to update the hostname: this time no DHCP host-name option will be found and so the hostname will not be changed, keeping the obsoleted one from the disappeared DHCP option. In order to fix this we have to keep track if the last hostname set was retrieved from the DHCP host-name option: in this case NetworkManager will be able to reset it by applying back the previous hostname. --- src/nm-policy.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/nm-policy.c b/src/nm-policy.c index e88872308e..1945315727 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -91,6 +91,7 @@ typedef struct { char *cur_hostname; /* hostname we want to assign */ char *last_hostname; /* last hostname NM set (to detect if someone else changed it in the meanwhile) */ gboolean changing_hostname; /* hostname set operation still in progress */ + gboolean dhcp_hostname; /* current hostname was set from dhcp */ GArray *ip6_prefix_delegations; /* pool of ip6 prefixes delegated to all devices */ } NMPolicyPrivate; @@ -608,6 +609,7 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) external_hostname = TRUE; _LOGI (LOGD_DNS, "current hostname was changed outside NetworkManager: '%s'", temp_hostname); + priv->dhcp_hostname = FALSE; if (!nm_streq0 (temp_hostname, priv->orig_hostname)) { /* Update original (fallback) hostname */ @@ -633,6 +635,7 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) g_object_get (G_OBJECT (priv->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL); if (configured_hostname && nm_utils_is_specific_hostname (configured_hostname)) { _set_hostname (self, configured_hostname, "from system configuration"); + priv->dhcp_hostname = FALSE; g_free (configured_hostname); return; } @@ -656,6 +659,7 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) while (*p) { if (!g_ascii_isspace (*p++)) { _set_hostname (self, p-1, "from DHCPv4"); + priv->dhcp_hostname = TRUE; return; } } @@ -675,6 +679,7 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) while (*p) { if (!g_ascii_isspace (*p++)) { _set_hostname (self, p-1, "from DHCPv6"); + priv->dhcp_hostname = TRUE; return; } } @@ -688,8 +693,20 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) if (external_hostname) return; - if (priv->hostname_mode == NM_POLICY_HOSTNAME_MODE_DHCP) + if (priv->hostname_mode == NM_POLICY_HOSTNAME_MODE_DHCP) { + /* In dhcp hostname-mode, the hostname is updated only if it comes from + * a DHCP host-name option: if last set was from a host-name option and + * we are here than that connection is gone (with its host-name option), + * so reset the hostname to the previous value + */ + if (priv->dhcp_hostname) { + _set_hostname (self, priv->orig_hostname, "reset dhcp hostname"); + priv->dhcp_hostname = FALSE; + } return; + } + + priv->dhcp_hostname = FALSE; if (!best4 && !best6) { /* No best device; fall back to the last hostname set externally From 76b2e9542fd64d226f98c71972749babc5dd7391 Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Fri, 3 Mar 2017 12:11:57 +0100 Subject: [PATCH 8/9] policy: add some verbose logging for tracking hostname management --- src/nm-policy.c | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/nm-policy.c b/src/nm-policy.c index 1945315727..80f9caedfa 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -422,14 +422,15 @@ settings_set_hostname_cb (const char *hostname, int ret = 0; if (!result) { + _LOGT (LOGD_DNS, "set-hostname: hostname set via dbus failed, fallback to \"sethostname\""); ret = sethostname (hostname, strlen (hostname)); if (ret != 0) { int errsv = errno; - _LOGW (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s", + _LOGW (LOGD_DNS, "set-hostname: couldn't set the system hostname to '%s': (%d) %s", hostname, errsv, strerror (errsv)); if (errsv == EPERM) - _LOGW (LOGD_DNS, "you should use hostnamed when systemd hardening is in effect!"); + _LOGW (LOGD_DNS, "set-hostname: you should use hostnamed when systemd hardening is in effect!"); } } @@ -453,14 +454,14 @@ _get_hostname (NMPolicy *self, char **hostname) * the last hostname set as would be set soon... */ if (priv->changing_hostname) { - _LOGT (LOGD_DNS, "gethostname: \"%s\" (last on set)", priv->last_hostname); + _LOGT (LOGD_DNS, "get-hostname: \"%s\" (last on set)", priv->last_hostname); *hostname = g_strdup (priv->last_hostname); return *hostname; } /* try to get the hostname via dbus... */ if (nm_settings_get_transient_hostname (priv->settings, hostname)) { - _LOGT (LOGD_DNS, "gethostname: \"%s\" (from dbus)", *hostname); + _LOGT (LOGD_DNS, "get-hostname: \"%s\" (from dbus)", *hostname); return *hostname; } @@ -469,7 +470,7 @@ _get_hostname (NMPolicy *self, char **hostname) if (gethostname (buf, HOST_NAME_BUFSIZE -1) != 0) { int errsv = errno; - _LOGT (LOGD_DNS, "gethostname: couldn't get the system hostname: (%d) %s", + _LOGT (LOGD_DNS, "get-hostname: couldn't get the system hostname: (%d) %s", errsv, g_strerror (errsv)); g_free (buf); return NULL; @@ -478,12 +479,12 @@ _get_hostname (NMPolicy *self, char **hostname) /* the name may be truncated... */ buf[HOST_NAME_BUFSIZE - 1] = '\0'; if (strlen (buf) >= HOST_NAME_BUFSIZE -1) { - _LOGT (LOGD_DNS, "gethostname: system hostname too long: \"%s\"", buf); + _LOGT (LOGD_DNS, "get-hostname: system hostname too long: \"%s\"", buf); g_free (buf); return NULL; } - _LOGT (LOGD_DNS, "gethostname: \"%s\"", buf); + _LOGT (LOGD_DNS, "get-hostname: \"%s\"", buf); *hostname = buf; return *hostname; } @@ -535,7 +536,7 @@ _set_hostname (NMPolicy *self, /* Don't set the hostname if it isn't actually changing */ if ( _get_hostname (self, &old_hostname) && (nm_streq (name, old_hostname))) { - _LOGT (LOGD_DNS, "sethostname: already set to '%s' (%s)", name, msg); + _LOGT (LOGD_DNS, "set-hostname: hostname already set to '%s' (%s)", name, msg); return; } @@ -544,7 +545,7 @@ _set_hostname (NMPolicy *self, priv->last_hostname = g_strdup (name); priv->changing_hostname = TRUE; - _LOGI (LOGD_DNS, "sethostname: '%s' (%s)", name, msg); + _LOGI (LOGD_DNS, "set-hostname: set hostname to '%s' (%s)", name, msg); /* Ask NMSettings to update the transient hostname using its * systemd-hostnamed proxy */ @@ -582,7 +583,7 @@ lookup_callback (GObject *source, } static void -update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) +update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6, const char *msg) { NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self); char *configured_hostname = NULL; @@ -594,8 +595,12 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) g_return_if_fail (self != NULL); - if (priv->hostname_mode == NM_POLICY_HOSTNAME_MODE_NONE) + if (priv->hostname_mode == NM_POLICY_HOSTNAME_MODE_NONE) { + _LOGT (LOGD_DNS, "set-hostname: hostname is unmanaged"); return; + } + + _LOGT (LOGD_DNS, "set-hostname: updating hostname (%s)", msg); nm_clear_g_cancellable (&priv->lookup_cancellable); @@ -607,7 +612,7 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) && ( nm_utils_is_specific_hostname (temp_hostname) || nm_utils_is_specific_hostname (priv->last_hostname))) { external_hostname = TRUE; - _LOGI (LOGD_DNS, "current hostname was changed outside NetworkManager: '%s'", + _LOGI (LOGD_DNS, "set-hostname: current hostname was changed outside NetworkManager: '%s'", temp_hostname); priv->dhcp_hostname = FALSE; @@ -663,7 +668,7 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) return; } } - _LOGW (LOGD_DNS, "DHCPv4-provided hostname '%s' looks invalid; ignoring it", + _LOGW (LOGD_DNS, "set-hostname: DHCPv4-provided hostname '%s' looks invalid; ignoring it", dhcp_hostname); } } @@ -683,7 +688,7 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6) return; } } - _LOGW (LOGD_DNS, "DHCPv6-provided hostname '%s' looks invalid; ignoring it", + _LOGW (LOGD_DNS, "set-hostname: DHCPv6-provided hostname '%s' looks invalid; ignoring it", dhcp_hostname); } } @@ -1019,7 +1024,7 @@ update_routing_and_dns (NMPolicy *self, gboolean force_update) update_ip6_routing (self, force_update); /* Update the system hostname */ - update_system_hostname (self, priv->default_device4, priv->default_device6); + update_system_hostname (self, priv->default_device4, priv->default_device6, "routing and dns"); nm_dns_manager_end_updates (priv->dns_manager, __func__); } @@ -1250,7 +1255,7 @@ hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data) NMPolicyPrivate *priv = user_data; NMPolicy *self = _PRIV_TO_SELF (priv); - update_system_hostname (self, NULL, NULL); + update_system_hostname (self, NULL, NULL, "hostname changed"); } static void @@ -1732,7 +1737,7 @@ device_ip4_config_changed (NMDevice *device, } update_ip4_dns (self, priv->dns_manager); update_ip4_routing (self, TRUE); - update_system_hostname (self, priv->default_device4, priv->default_device6); + update_system_hostname (self, priv->default_device4, priv->default_device6, "ip4 conf"); } else { /* Old configs get removed immediately */ if (old_config) @@ -1768,7 +1773,7 @@ device_ip6_config_changed (NMDevice *device, } update_ip6_dns (self, priv->dns_manager); update_ip6_routing (self, TRUE); - update_system_hostname (self, priv->default_device4, priv->default_device6); + update_system_hostname (self, priv->default_device4, priv->default_device6, "ip6 conf"); } else { /* Old configs get removed immediately */ if (old_config) @@ -2295,6 +2300,7 @@ nm_policy_init (NMPolicy *self) else /* default - full mode */ priv->hostname_mode = NM_POLICY_HOSTNAME_MODE_FULL; + _LOGI (LOGD_DNS, "hostname management mode: %s", hostname_mode ? hostname_mode : "default"); priv->devices = g_hash_table_new (NULL, NULL); priv->ip6_prefix_delegations = g_array_new (FALSE, FALSE, sizeof (IP6PrefixDelegation)); g_array_set_clear_func (priv->ip6_prefix_delegations, clear_ip6_prefix_delegation); From 70c768901b66cd14188c4def5470cd16b91c40ae Mon Sep 17 00:00:00 2001 From: Francesco Giudici Date: Tue, 7 Mar 2017 10:27:58 +0100 Subject: [PATCH 9/9] man: add description for the 'hostname' config option in the main section --- man/NetworkManager.conf.xml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/man/NetworkManager.conf.xml b/man/NetworkManager.conf.xml index 251a2a36b6..1230347d83 100644 --- a/man/NetworkManager.conf.xml +++ b/man/NetworkManager.conf.xml @@ -276,6 +276,34 @@ no-auto-default=* + + hostname-mode + + + Set the management mode of the hostname. This parameter will + affect only the transient hostname. If a valid static hostname is set, + NetworkManager will skip the update of the hostname despite the value of + this option. An hostname empty or equal to 'localhost', 'localhost6', + 'localhost.localdomain' or 'localhost6.localdomain' is considered invalid. + + default: NetworkManager will update the hostname + with the one provided via DHCP on the main connection (the one with a default + route). If not present, the hostname will be updated to the last one set + outside NetworkManager. If it is not valid, NetworkManager will try to recover + the hostname from the reverse lookup of the IP address of the main connection. + If this fails too, the hostname will be set to 'localhost.localdomain'. + + dhcp: NetworkManager will update the transient hostname + only with information coming from DHCP. No fallback nor reverse lookup will be + performed, but when the dhcp connection providing the hostname is deactivated, + the hostname is reset to the last hostname set outside NetworkManager or + 'localhost' if none valid is there. + + none: NetworkManager will not manage the transient + hostname and will never set it. + + + dns Set the DNS (resolv.conf) processing mode.