summary refs log tree commit diff
path: root/src/devices/nm-device.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-06-17 14:54:10 +0200
committerMichael Biebl <biebl@debian.org>2018-06-17 14:54:10 +0200
commit069cb5c3a525ebcc19cc2927964258acaca87b13 (patch)
tree66ac6b21a44d630c0fc281c0df327239d491226a /src/devices/nm-device.c
parent04bc9e1cd3544445d883ad29ea108c1645c8e7b7 (diff)
New upstream version 1.11.90 upstream/1.11.90
Diffstat (limited to 'src/devices/nm-device.c')
-rw-r--r--src/devices/nm-device.c427
1 files changed, 369 insertions, 58 deletions
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 9a40a0dd..9df41410 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -37,8 +37,11 @@
 #include <linux/if_arp.h>
 #include <linux/rtnetlink.h>
 #include <linux/pkt_sched.h>
+#include <uuid/uuid.h>
 
 #include "nm-utils/nm-dedup-multi.h"
+#include "nm-utils/nm-random-utils.h"
+#include "nm-utils/unaligned.h"
 
 #include "nm-common-macros.h"
 #include "nm-device-private.h"
@@ -6830,11 +6833,12 @@ dhcp4_grace_period_expired (gpointer user_data)
 }
 
 static void
-dhcp4_fail (NMDevice *self, gboolean timeout)
+dhcp4_fail (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	_LOGD (LOGD_DHCP4, "DHCPv4 failed%s", timeout ? " (timeout)" : "");
+	_LOGD (LOGD_DHCP4, "DHCPv4 failed (ip_state %s)",
+	       _ip_state_to_string (priv->ip4_state));
 
 	/* Keep client running if there are static addresses configured
 	 * on the interface.
@@ -6848,7 +6852,7 @@ dhcp4_fail (NMDevice *self, gboolean timeout)
 	 * configuration.
 	 */
 	if (   !priv->dhcp4.was_active
-	    && (timeout || priv->ip4_state == IP_CONF)) {
+	    && priv->ip4_state == IP_CONF) {
 		dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE);
 		nm_device_activate_schedule_ip4_config_timeout (self);
 		return;
@@ -6911,7 +6915,7 @@ dhcp4_state_changed (NMDhcpClient *client,
 	case NM_DHCP_STATE_BOUND:
 		if (!ip4_config) {
 			_LOGW (LOGD_DHCP4, "failed to get IPv4 config in response to DHCP event.");
-			dhcp4_fail (self, FALSE);
+			dhcp4_fail (self);
 			break;
 		}
 
@@ -6950,11 +6954,11 @@ dhcp4_state_changed (NMDhcpClient *client,
 			if (dhcp4_lease_change (self, ip4_config))
 				nm_device_update_metered (self);
 			else
-				dhcp4_fail (self, FALSE);
+				dhcp4_fail (self);
 		}
 		break;
 	case NM_DHCP_STATE_TIMEOUT:
-		dhcp4_fail (self, TRUE);
+		dhcp4_fail (self);
 		break;
 	case NM_DHCP_STATE_EXPIRE:
 		/* Ignore expiry before we even have a lease (NAK, old lease, etc) */
@@ -6963,7 +6967,7 @@ dhcp4_state_changed (NMDhcpClient *client,
 		/* fall through */
 	case NM_DHCP_STATE_DONE:
 	case NM_DHCP_STATE_FAIL:
-		dhcp4_fail (self, FALSE);
+		dhcp4_fail (self);
 		break;
 	default:
 		break;
@@ -7014,13 +7018,32 @@ get_dhcp_timeout (NMDevice *self, int addr_family)
 }
 
 static GBytes *
-dhcp4_get_client_id (NMDevice *self, NMConnection *connection)
+dhcp4_get_client_id_mac (const guint8 *hwaddr /* ETH_ALEN bytes */)
+{
+	guint8 *client_id_buf;
+	guint8 hwaddr_type = ARPHRD_ETHER;
+
+	client_id_buf = g_malloc (ETH_ALEN + 1);
+	client_id_buf[0] = hwaddr_type;
+	memcpy (&client_id_buf[1], hwaddr, ETH_ALEN);
+	return g_bytes_new_take (client_id_buf, ETH_ALEN + 1);
+}
+
+static GBytes *
+dhcp4_get_client_id (NMDevice *self,
+                     NMConnection *connection,
+                     GBytes *hwaddr)
 {
 	NMSettingIPConfig *s_ip4;
 	const char *client_id;
 	gs_free char *client_id_default = NULL;
 	guint8 *client_id_buf;
-	gboolean is_mac;
+	const char *fail_reason;
+	guint8 hwaddr_bin_buf[NM_UTILS_HWADDR_LEN_MAX];
+	const guint8 *hwaddr_bin;
+	gsize hwaddr_len;
+	GBytes *result;
+	gs_free char *logstr1 = NULL;
 
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	client_id = nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4));
@@ -7028,42 +7051,54 @@ dhcp4_get_client_id (NMDevice *self, NMConnection *connection)
 	if (!client_id) {
 		client_id_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
 		                                                           "ipv4.dhcp-client-id", self);
-		if (client_id_default && client_id_default[0])
+		if (client_id_default && client_id_default[0]) {
+			/* a non-empty client-id is always valid, see nm_dhcp_utils_client_id_string_to_bytes().  */
 			client_id = client_id_default;
+		}
 	}
 
-	if (!client_id)
+	if (!client_id) {
+		_LOGD (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4,
+		       "ipv4.dhcp-client-id: no explicity client-id configured");
 		return NULL;
+	}
 
-	if (   (is_mac = nm_streq (client_id, "mac"))
-	    || nm_streq (client_id, "perm-mac")) {
-		const char *hwaddr;
-		char addr_buf[NM_UTILS_HWADDR_LEN_MAX];
-		gsize addr_len;
-		guint8 addr_type;
-
-		hwaddr = is_mac
-		         ? nm_device_get_hw_address (self)
-		         : nm_device_get_permanent_hw_address (self);
-		if (!hwaddr)
-			return NULL;
+	if (nm_streq (client_id, "mac")) {
+		if (!hwaddr) {
+			fail_reason = "failed to get current MAC address";
+			goto out_fail;
+		}
+
+		hwaddr_bin = g_bytes_get_data (hwaddr, &hwaddr_len);
+		if (hwaddr_len != ETH_ALEN) {
+			fail_reason = "MAC address is not ethernet";
+			goto out_fail;
+		}
+
+		result = dhcp4_get_client_id_mac (hwaddr_bin);
+		goto out_good;
+	}
+
+	if (nm_streq (client_id, "perm-mac")) {
+		const char *hwaddr_str;
+
+		hwaddr_str = nm_device_get_permanent_hw_address (self);
+		if (!hwaddr_str) {
+			fail_reason = "failed to get permanent MAC address";
+			goto out_fail;
+		}
 
-		if (!_nm_utils_hwaddr_aton (hwaddr, addr_buf, sizeof (addr_buf), &addr_len))
+		if (!_nm_utils_hwaddr_aton (hwaddr_str, hwaddr_bin_buf, sizeof (hwaddr_bin_buf), &hwaddr_len))
 			g_return_val_if_reached (NULL);
 
-		switch (addr_len) {
-		case ETH_ALEN:
-			addr_type = ARPHRD_ETHER;
-			break;
-		default:
+		if (hwaddr_len != ETH_ALEN) {
 			/* unsupported type. */
-			return NULL;
+			fail_reason = "MAC address is not ethernet";
+			goto out_fail;
 		}
 
-		client_id_buf = g_malloc (addr_len + 1);
-		client_id_buf[0] = addr_type;
-		memcpy (&client_id_buf[1], addr_buf, addr_len);
-		return g_bytes_new_take (client_id_buf, addr_len + 1);
+		result = dhcp4_get_client_id_mac (hwaddr_bin_buf);
+		goto out_good;
 	}
 
 	if (nm_streq (client_id, "stable")) {
@@ -7099,10 +7134,30 @@ dhcp4_get_client_id (NMDevice *self, NMConnection *connection)
 		client_id_buf = g_malloc (1 + 15);
 		client_id_buf[0] = 0;
 		memcpy (&client_id_buf[1], buf, 15);
-		return g_bytes_new_take (client_id_buf, 1 + 15);
+		result = g_bytes_new_take (client_id_buf, 1 + 15);
+		goto out_good;
 	}
 
-	return nm_dhcp_utils_client_id_string_to_bytes (client_id);
+	result = nm_dhcp_utils_client_id_string_to_bytes (client_id);
+	goto out_good;
+
+out_fail:
+	nm_assert (fail_reason);
+	_LOGW (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4,
+	       "ipv4.dhcp-client-id: failure to generate client id (%s). Use random client id",
+	       fail_reason);
+	client_id_buf = g_malloc (1 + 15);
+	client_id_buf[0] = 0;
+	nm_utils_random_bytes (&client_id_buf[1], 15);
+	result = g_bytes_new_take (client_id_buf, 1 + 15);
+
+out_good:
+	nm_assert (result);
+	_LOGD (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4,
+	       "ipv4.dhcp-client-id: use \"%s\" client ID: %s",
+	       client_id,
+	       (logstr1 = nm_dhcp_utils_duid_to_string (result)));
+	return result;
 }
 
 static NMActStageReturn
@@ -7126,7 +7181,7 @@ dhcp4_start (NMDevice *self)
 	hwaddr = nm_platform_link_get_address_as_bytes (nm_device_get_platform (self),
 	                                                nm_device_get_ip_ifindex (self));
 
-	client_id = dhcp4_get_client_id (self, connection);
+	client_id = dhcp4_get_client_id (self, connection, hwaddr);
 
 	g_warn_if_fail (priv->dhcp4.client == NULL);
 	priv->dhcp4.client = nm_dhcp_manager_start_ip4 (nm_dhcp_manager_get (),
@@ -7678,12 +7733,260 @@ dhcp6_prefix_delegated (NMDhcpClient *client,
 	g_signal_emit (self, signals[IP6_PREFIX_DELEGATED], 0, prefix);
 }
 
+/* RFC 3315 defines the epoch for the DUID-LLT time field on Jan 1st 2000. */
+#define EPOCH_DATETIME_200001010000  946684800
+
+static GBytes *
+generate_duid_llt (const guint8 *hwaddr /* ETH_ALEN bytes */,
+                   gint64 time)
+{
+	GByteArray *duid_arr;
+	const guint16 duid_type = htons (1);
+	const guint16 hw_type = htons (ARPHRD_ETHER);
+	const guint32 duid_time = htonl (NM_MAX (0, time - EPOCH_DATETIME_200001010000));
+
+	duid_arr = g_byte_array_sized_new (2 + 4 + 2 + ETH_ALEN);
+
+	g_byte_array_append (duid_arr, (const guint8 *) &duid_type, 2);
+	g_byte_array_append (duid_arr, (const guint8 *) &hw_type, 2);
+	g_byte_array_append (duid_arr, (const guint8 *) &duid_time, 4);
+	g_byte_array_append (duid_arr, hwaddr, ETH_ALEN);
+
+	return g_byte_array_free_to_bytes (duid_arr);
+}
+
+static GBytes *
+generate_duid_ll (const guint8 *hwaddr /* ETH_ALEN bytes */)
+{
+	GByteArray *duid_arr;
+	const guint16 duid_type = htons (3);
+	const guint16 hw_type = htons (ARPHRD_ETHER);
+
+	duid_arr = g_byte_array_sized_new (2 + 2 + ETH_ALEN);
+
+	g_byte_array_append (duid_arr, (const guint8 *) &duid_type, 2);
+	g_byte_array_append (duid_arr, (const guint8 *) &hw_type, 2);
+	g_byte_array_append (duid_arr, hwaddr, ETH_ALEN);
+
+	return g_byte_array_free_to_bytes (duid_arr);
+}
+
+static GBytes *
+generate_duid_uuid (guint8 *data, gsize data_len)
+{
+	const guint16 duid_type = g_htons (4);
+	const int DUID_SIZE = 18;
+	guint8 *duid_buffer;
+
+	nm_assert (data);
+	nm_assert (data_len >= 16);
+
+	/* Generate a DHCP Unique Identifier for DHCPv6 using the
+	 * DUID-UUID method (see RFC 6355 section 4).  Format is:
+	 *
+	 * u16: type (DUID-UUID = 4)
+	 * u8[16]: UUID bytes
+	 */
+	duid_buffer = g_malloc (DUID_SIZE);
+
+	G_STATIC_ASSERT_EXPR (sizeof (duid_type) == 2);
+	memcpy (&duid_buffer[0], &duid_type, 2);
+
+	/* UUID is 128 bits, we just take the first 128 bits
+	 * (regardless of data size) as the DUID-UUID.
+	 */
+	memcpy (&duid_buffer[2], data, 16);
+
+	return g_bytes_new_take (duid_buffer, DUID_SIZE);
+}
+
+static GBytes *
+generate_duid_from_machine_id (void)
+{
+	gs_free const char *machine_id_s = NULL;
+	uuid_t uuid;
+	GChecksum *sum;
+	guint8 sha256_digest[32];
+	gsize len = sizeof (sha256_digest);
+	static GBytes *global_duid = NULL;
+
+	if (global_duid)
+		return g_bytes_ref (global_duid);
+
+	machine_id_s = nm_utils_machine_id_read ();
+	if (!nm_utils_machine_id_parse (machine_id_s, uuid))
+		return NULL;
+
+	/* Hash the machine ID so it's not leaked to the network */
+	sum = g_checksum_new (G_CHECKSUM_SHA256);
+	g_checksum_update (sum, (const guchar *) &uuid, sizeof (uuid));
+	g_checksum_get_digest (sum, sha256_digest, &len);
+	g_checksum_free (sum);
+
+	global_duid = generate_duid_uuid (sha256_digest, len);
+	return g_bytes_ref (global_duid);
+}
+
+static GBytes *
+dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, NMDhcpDuidEnforce *out_enforce)
+{
+	NMSettingIPConfig *s_ip6;
+	const char *duid;
+	gs_free char *duid_default = NULL;
+	const char *duid_error;
+	GBytes *duid_out;
+	guint8 sha256_digest[32];
+	gsize len = sizeof (sha256_digest);
+	NMDhcpDuidEnforce duid_enforce = NM_DHCP_DUID_ENFORCE_ALWAYS;
+	gs_free char *logstr1 = NULL;
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	duid = nm_setting_ip6_config_get_dhcp_duid (NM_SETTING_IP6_CONFIG (s_ip6));
+
+	if (!duid) {
+		duid_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+		                                                      "ipv6.dhcp-duid", self);
+		duid = duid_default;
+		if (!duid)
+			duid = "lease";
+	}
+
+	if (nm_streq (duid, "lease")) {
+		duid_enforce = NM_DHCP_DUID_ENFORCE_NEVER;
+		duid_out = generate_duid_from_machine_id ();
+		if (!duid_out) {
+			duid_error = "failure to read machine-id";
+			goto out_fail;
+		}
+		goto out_good;
+	}
+
+	if (!_nm_utils_dhcp_duid_valid (duid, &duid_out)) {
+		duid_error = "invalid duid";
+		goto out_fail;
+	}
+
+	if (duid_out)
+		goto out_good;
+
+	if (NM_IN_STRSET (duid, "ll", "llt")) {
+		if (!hwaddr) {
+			duid_error = "missing link-layer address";
+			goto out_fail;
+		}
+		if (g_bytes_get_size (hwaddr) != ETH_ALEN) {
+			duid_error = "unsupported link-layer address";
+			goto out_fail;
+		}
+
+		if (nm_streq (duid, "ll")) {
+			duid_out = generate_duid_ll (g_bytes_get_data (hwaddr, NULL));
+		} else {
+			gint64 time;
+
+			time = nm_utils_secret_key_get_timestamp ();
+			if (!time) {
+				duid_error = "cannot retrieve the secret key timestamp";
+				goto out_fail;
+			}
+
+			duid_out = generate_duid_llt (g_bytes_get_data (hwaddr, NULL), time);
+		}
+
+		goto out_good;
+	}
+
+	if (NM_IN_STRSET (duid, "stable-ll", "stable-llt", "stable-uuid")) {
+		NMUtilsStableType stable_type;
+		const char *stable_id = NULL;
+		guint32 salted_header;
+		GChecksum *sum;
+		const guint8 *secret_key;
+		gsize secret_key_len;
+
+		stable_id = _get_stable_id (self, connection, &stable_type);
+		if (!stable_id)
+			g_return_val_if_reached (NULL);
+
+		salted_header = htonl (670531087 + stable_type);
+
+		nm_utils_secret_key_get (&secret_key, &secret_key_len);
+
+		sum = g_checksum_new (G_CHECKSUM_SHA256);
+
+		g_checksum_update (sum, (const guchar *) &salted_header, sizeof (salted_header));
+		g_checksum_update (sum, (const guchar *) stable_id, -1);
+		g_checksum_update (sum, (const guchar *) secret_key, secret_key_len);
+
+		g_checksum_get_digest (sum, sha256_digest, &len);
+		g_checksum_free (sum);
+
+		if (nm_streq (duid, "stable-ll")) {
+			duid_out = generate_duid_ll (sha256_digest);
+		} else if (nm_streq (duid, "stable-llt")) {
+			gint64 time;
+
+#define EPOCH_DATETIME_THREE_YEARS  (356 * 24 * 3600 * 3)
+
+			/* We want a variable time between the secret_key timestamp and three years
+			 * before. Let's compute the time (in seconds) from 0 to 3 years; then we'll
+			 * subtract it from the secret_key timestamp.
+			 */
+			time = nm_utils_secret_key_get_timestamp ();
+			if (!time) {
+				duid_error = "cannot retrieve the secret key timestamp";
+				goto out_fail;
+			}
+			/* don't use too old timestamps. They cannot be expressed in DUID-LLT and
+			 * would all be truncated to zero. */
+			time = NM_MAX (time, EPOCH_DATETIME_200001010000 + EPOCH_DATETIME_THREE_YEARS);
+			time -= (unaligned_read_be32 (&sha256_digest[ETH_ALEN]) % EPOCH_DATETIME_THREE_YEARS);
+
+			duid_out = generate_duid_llt (sha256_digest, time);
+		} else {
+			nm_assert (nm_streq (duid, "stable-uuid"));
+			duid_out = generate_duid_uuid (sha256_digest, len);
+		}
+
+		goto out_good;
+	}
+
+	g_return_val_if_reached (NULL);
+
+out_fail:
+	nm_assert (!duid_out && duid_error);
+	{
+		guint8 uuid[16];
+
+		_LOGW (LOGD_IP6 | LOGD_DHCP6,
+		       "ipv6.dhcp-duid: failure to generate %s DUID: %s. Fallback to random DUID-UUID.",
+		       duid, duid_error);
+
+		nm_utils_random_bytes (uuid, sizeof (uuid));
+		duid_out = generate_duid_uuid (uuid, sizeof (uuid));
+	}
+
+out_good:
+	nm_assert (duid_out);
+	_LOGD (LOGD_IP6 | LOGD_DHCP6,
+	       "ipv6.dhcp-duid: generate %s DUID '%s' (%s)",
+	       duid,
+	       (logstr1 = nm_dhcp_utils_duid_to_string (duid_out)),
+	       (duid_enforce == NM_DHCP_DUID_ENFORCE_ALWAYS) ? "enforcing" : "fallback");
+
+	NM_SET_OUT (out_enforce, duid_enforce);
+	return duid_out;
+}
+
 static gboolean
 dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMSettingIPConfig *s_ip6;
 	gs_unref_bytes GBytes *hwaddr = NULL;
+	gs_unref_bytes GBytes *duid = NULL;
+	NMDhcpDuidEnforce enforce_duid = NM_DHCP_DUID_ENFORCE_NEVER;
+
 	const NMPlatformIP6Address *ll_addr = NULL;
 
 	g_assert (connection);
@@ -7704,6 +8007,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	hwaddr = nm_platform_link_get_address_as_bytes (nm_device_get_platform (self),
 	                                                nm_device_get_ip_ifindex (self));
 
+	duid = dhcp6_get_duid (self, connection, hwaddr, &enforce_duid);
 	priv->dhcp6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (),
 	                                                nm_device_get_multi_index (self),
 	                                                nm_device_get_ip_iface (self),
@@ -7715,6 +8019,8 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	                                                nm_device_get_route_metric (self, AF_INET6),
 	                                                nm_setting_ip_config_get_dhcp_send_hostname (s_ip6),
 	                                                nm_setting_ip_config_get_dhcp_hostname (s_ip6),
+	                                                duid,
+	                                                enforce_duid,
 	                                                get_dhcp_timeout (self, AF_INET6),
 	                                                priv->dhcp_anycast_address,
 	                                                (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) ? TRUE : FALSE,
@@ -8139,8 +8445,10 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 	if (ifindex <= 0)
 		return;
 
-	if (nm_device_sys_iface_state_is_external_or_assume (self)) {
-		/* for assumed connections we don't tamper with the MTU. */
+	if (   !nm_device_get_applied_connection (self)
+	    || nm_device_sys_iface_state_is_external_or_assume (self)) {
+		/* we don't tamper with the MTU of disconnected and
+		 * external/assumed devices. */
 		return;
 	}
 
@@ -9886,43 +10194,45 @@ _cleanup_ip_pre (NMDevice *self, int addr_family, CleanupType cleanup_type)
 
 gboolean
 _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name,
-                                    GError **error, const char **argv)
+                                    GError **error, const char **whitelist)
 {
-	guint found_keys = 0;
+	guint found_whitelisted_keys = 0;
 	guint i;
 
 	nm_assert (hash && g_hash_table_size (hash) > 0);
-	nm_assert (argv && argv[0]);
+	nm_assert (whitelist && whitelist[0]);
 
 #if NM_MORE_ASSERTS > 10
-	/* Assert that the keys are unique. */
+	/* Require whitelist to only contain unique keys. */
 	{
 		gs_unref_hashtable GHashTable *check_dups = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, NULL);
 
-		for (i = 0; argv[i]; i++) {
-			if (!g_hash_table_add (check_dups, (char *) argv[i]))
+		for (i = 0; whitelist[i]; i++) {
+			if (!g_hash_table_add (check_dups, (char *) whitelist[i]))
 				nm_assert (FALSE);
 		}
 		nm_assert (g_hash_table_size (check_dups) > 0);
 	}
 #endif
 
-	for (i = 0; argv[i]; i++) {
-		if (g_hash_table_contains (hash, argv[i]))
-			found_keys++;
+	for (i = 0; whitelist[i]; i++) {
+		if (g_hash_table_contains (hash, whitelist[i]))
+			found_whitelisted_keys++;
 	}
 
-	if (found_keys != g_hash_table_size (hash)) {
+	if (found_whitelisted_keys == g_hash_table_size (hash)) {
+		/* Good, there are only whitelisted keys in the hash. */
+		return TRUE;
+	}
+
+	if (error) {
 		GHashTableIter iter;
 		const char *k = NULL;
 		const char *first_invalid_key = NULL;
 
-		if (!error)
-			return FALSE;
-
 		g_hash_table_iter_init (&iter, hash);
 		while (g_hash_table_iter_next (&iter, (gpointer *) &k, NULL)) {
-			if (nm_utils_strv_find_first ((char **) argv, -1, k) < 0) {
+			if (nm_utils_strv_find_first ((char **) whitelist, -1, k) < 0) {
 				first_invalid_key = k;
 				break;
 			}
@@ -9942,10 +10252,9 @@ _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name,
 			             first_invalid_key);
 		}
 		g_return_val_if_fail (first_invalid_key, FALSE);
-		return FALSE;
 	}
 
-	return TRUE;
+	return FALSE;
 }
 
 void
@@ -10088,9 +10397,11 @@ can_reapply_change (NMDevice *self, const char *setting_name,
 		                                          NM_SETTING_CONNECTION_METERED,
 		                                          NM_SETTING_CONNECTION_LLDP);
 	} else if (NM_IN_STRSET (setting_name,
-	                         NM_SETTING_IP4_CONFIG_SETTING_NAME,
-	                         NM_SETTING_IP6_CONFIG_SETTING_NAME,
 	                         NM_SETTING_PROXY_SETTING_NAME)) {
+		return TRUE;
+	} else if (NM_IN_STRSET (setting_name,
+	                         NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	                         NM_SETTING_IP6_CONFIG_SETTING_NAME)) {
 		if (g_hash_table_contains (diffs, NM_SETTING_IP_CONFIG_ROUTE_TABLE)) {
 			/* changing the route-table setting is complicated, because it affects
 			 * how we sync the routes. Don't support changing it without full
@@ -10221,7 +10532,7 @@ check_and_reapply_connection (NMDevice *self,
 
 	if (diffs) {
 		NMConnection *connection_clean = connection;
-		gs_free NMConnection *connection_clean_free = NULL;
+		gs_unref_object NMConnection *connection_clean_free = NULL;
 
 		{
 			NMSettingConnection *s_con_a, *s_con_n;