summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/devices/nm-device-bond.c12
-rw-r--r--src/devices/nm-device-ethernet.c4
-rw-r--r--src/devices/nm-device-factory.c18
-rw-r--r--src/devices/nm-device-infiniband.c26
-rw-r--r--src/devices/nm-device-ip-tunnel.c28
-rw-r--r--src/devices/nm-device-private.h6
-rw-r--r--src/devices/nm-device-vlan.c10
-rw-r--r--src/devices/nm-device.c263
-rw-r--r--src/devices/nm-device.h9
-rw-r--r--src/devices/ovs/nm-ovs-factory.c6
-rw-r--r--src/devices/wifi/nm-device-iwd.c290
-rw-r--r--src/devices/wifi/nm-device-iwd.h4
-rw-r--r--src/devices/wifi/nm-device-wifi.c26
-rw-r--r--src/devices/wifi/nm-iwd-manager.c144
-rw-r--r--src/devices/wwan/nm-modem.c10
-rw-r--r--src/devices/wwan/nm-modem.h2
-rw-r--r--src/dhcp/nm-dhcp-client.c11
-rw-r--r--src/dhcp/nm-dhcp-client.h6
-rw-r--r--src/dhcp/nm-dhcp-dhclient.c4
-rw-r--r--src/dhcp/nm-dhcp-manager.c4
-rw-r--r--src/dhcp/nm-dhcp-manager.h2
-rw-r--r--src/dhcp/nm-dhcp-utils.h6
-rw-r--r--src/main.c33
-rw-r--r--src/ndisc/nm-ndisc.c18
-rw-r--r--src/ndisc/nm-ndisc.h7
-rw-r--r--src/nm-iface-helper.c3
-rw-r--r--src/nm-logging.c58
-rw-r--r--src/nm-logging.h4
-rw-r--r--src/nm-manager.c10
-rw-r--r--src/platform/nm-fake-platform.c2
-rw-r--r--src/platform/tests/test-cleanup.c16
-rw-r--r--src/settings/nm-settings-connection.c22
32 files changed, 619 insertions, 445 deletions
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index 22f7cdde..f59ec9ff 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -338,8 +338,6 @@ apply_bonding_config (NMDevice *device)
 	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC);
 	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_LACP_RATE);
 	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_LP_INTERVAL);
-	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP);
-	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_NUM_UNSOL_NA);
 	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_MIN_LINKS);
 	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE);
 	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT);
@@ -348,6 +346,16 @@ apply_bonding_config (NMDevice *device)
 	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER);
 	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY);
 
+	/* num_grat_arp and num_unsol_na are actually the same attribute
+	 * on kernel side and their value in the bond setting is guaranteed
+	 * to be equal. Write only one of the two.
+	 */
+	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP);
+	if (value)
+		set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP, value);
+	else
+		set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_NUM_UNSOL_NA);
+
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index 87d16dbe..a9a9f968 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -1317,13 +1317,13 @@ act_stage3_ip4_config_start (NMDevice *device,
 }
 
 static guint32
-get_configured_mtu (NMDevice *device, gboolean *out_is_user_config)
+get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
 {
 	/* MTU only set for plain ethernet */
 	if (NM_DEVICE_ETHERNET_GET_PRIVATE ((NMDeviceEthernet *) device)->ppp_manager)
 		return 0;
 
-	return nm_device_get_configured_mtu_for_wired (device, out_is_user_config);
+	return nm_device_get_configured_mtu_for_wired (device, out_source);
 }
 
 static void
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index d8e30346..bc717bb3 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -35,8 +35,6 @@
 
 #define PLUGIN_PREFIX "libnm-device-plugin-"
 
-static NM_CACHED_QUARK_FCN ("NMManager-plugin-path", plugin_path_quark)
-
 /*****************************************************************************/
 
 enum {
@@ -303,7 +301,10 @@ _add_factory (NMDeviceFactory *factory,
 
 	nm_device_factory_get_supported_types (factory, &link_types, &setting_types);
 
-	g_object_set_qdata_full (G_OBJECT (factory), plugin_path_quark (), g_strdup (path), g_free);
+	g_return_val_if_fail (   (link_types && link_types[0] > NM_LINK_TYPE_UNKNOWN)
+	                      || (setting_types && setting_types[0]),
+	                      FALSE);
+
 	for (i = 0; link_types && link_types[i] > NM_LINK_TYPE_UNKNOWN; i++)
 		g_hash_table_insert (factories_by_link, GUINT_TO_POINTER (link_types[i]), g_object_ref (factory));
 	for (i = 0; setting_types && setting_types[i]; i++) {
@@ -319,7 +320,12 @@ _add_factory (NMDeviceFactory *factory,
 
 	callback (factory, user_data);
 
-	nm_log_info (LOGD_PLATFORM, "Loaded device plugin: %s (%s)", G_OBJECT_TYPE_NAME (factory), path);
+	nm_log (path ? LOGL_INFO : LOGL_DEBUG,
+	        LOGD_PLATFORM,
+	        NULL, NULL,
+	        "Loaded device plugin: %s (%s)",
+	        G_OBJECT_TYPE_NAME (factory),
+	        path ?: "internal");
 	return TRUE;
 }
 
@@ -328,10 +334,10 @@ _load_internal_factory (GType factory_gtype,
                         NMDeviceFactoryManagerFactoryFunc callback,
                         gpointer user_data)
 {
-	NMDeviceFactory *factory;
+	gs_unref_object NMDeviceFactory *factory = NULL;
 
 	factory = (NMDeviceFactory *) g_object_new (factory_gtype, NULL);
-	_add_factory (factory, "internal", callback, user_data);
+	_add_factory (factory, NULL, callback, user_data);
 }
 
 static void
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index 781bbd69..99bcef00 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -115,29 +115,11 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 }
 
 static guint32
-get_configured_mtu (NMDevice *device, gboolean *out_is_user_config)
+get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
 {
-	NMSettingInfiniband *setting;
-	gint64 mtu_default;
-	guint32 mtu;
-
-	nm_assert (NM_IS_DEVICE (device));
-	nm_assert (out_is_user_config);
-
-	setting = NM_SETTING_INFINIBAND (nm_device_get_applied_setting (device, NM_TYPE_SETTING_INFINIBAND));
-	if (!setting)
-		g_return_val_if_reached (0);
-
-	mtu = nm_setting_infiniband_get_mtu (setting);
-	if (mtu == 0) {
-		mtu_default = nm_device_get_configured_mtu_from_connection_default (device, "infiniband.mtu");
-		if (mtu_default >= 0) {
-			*out_is_user_config = TRUE;
-			return (guint32) mtu_default;
-		}
-	}
-	*out_is_user_config = (mtu != 0);
-	return mtu;
+	return nm_device_get_configured_mtu_from_connection (device,
+	                                                     NM_TYPE_SETTING_INFINIBAND,
+	                                                     out_source);
 }
 
 static gboolean
diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c
index 59ca9ed5..2e266bb9 100644
--- a/src/devices/nm-device-ip-tunnel.c
+++ b/src/devices/nm-device-ip-tunnel.c
@@ -741,33 +741,15 @@ create_and_realize (NMDevice *device,
 }
 
 static guint32
-get_configured_mtu (NMDevice *self, gboolean *out_is_user_config)
+get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
 {
-	NMSettingIPTunnel *setting;
-	gint64 mtu_default;
-	guint32 mtu;
-
-	nm_assert (NM_IS_DEVICE (self));
-	nm_assert (out_is_user_config);
-
-	setting = NM_SETTING_IP_TUNNEL (nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP_TUNNEL));
-	if (!setting)
-		g_return_val_if_reached (0);
-
-	mtu = nm_setting_ip_tunnel_get_mtu (setting);
-	if (mtu == 0) {
-		mtu_default = nm_device_get_configured_mtu_from_connection_default (self, "ip-tunnel.mtu");
-		if (mtu_default >= 0) {
-			*out_is_user_config = TRUE;
-			return (guint32) mtu_default;
-		}
-	}
-	*out_is_user_config = (mtu != 0);
-	return mtu;
+	return nm_device_get_configured_mtu_from_connection (device,
+	                                                     NM_TYPE_SETTING_IP_TUNNEL,
+	                                                     out_source);
 }
 
 static NMDeviceCapabilities
-get_generic_capabilities (NMDevice *dev)
+get_generic_capabilities (NMDevice *device)
 {
 	return NM_DEVICE_CAP_IS_SOFTWARE;
 }
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index c76debff..ba28e9e4 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -120,7 +120,11 @@ gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const
 gint64 nm_device_get_configured_mtu_from_connection_default (NMDevice *self,
                                                              const char *property_name);
 
-guint32 nm_device_get_configured_mtu_for_wired (NMDevice *self, gboolean *out_is_user_config);
+guint32 nm_device_get_configured_mtu_from_connection (NMDevice *device,
+                                                      GType setting_type,
+                                                      NMDeviceMtuSource *out_source);
+
+guint32 nm_device_get_configured_mtu_for_wired (NMDevice *self, NMDeviceMtuSource *out_source);
 
 void nm_device_commit_mtu (NMDevice *self);
 
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index a7f4c4bc..b0e746a8 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -534,19 +534,21 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 }
 
 static guint32
-get_configured_mtu (NMDevice *self, gboolean *out_is_user_config)
+get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source)
 {
 	guint32 mtu = 0;
 	int ifindex;
 
-	mtu = nm_device_get_configured_mtu_for_wired (self, out_is_user_config);
-	if (*out_is_user_config)
+	mtu = nm_device_get_configured_mtu_for_wired (self, out_source);
+	if (*out_source != NM_DEVICE_MTU_SOURCE_NONE)
 		return mtu;
 
 	/* Inherit the MTU from parent device, if any */
 	ifindex = nm_device_parent_get_ifindex (self);
-	if (ifindex > 0)
+	if (ifindex > 0) {
 		mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)), ifindex);
+		*out_source = NM_DEVICE_MTU_SOURCE_PARENT;
+	}
 
 	return mtu;
 }
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 9df41410..5a5cb50e 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -280,11 +280,6 @@ typedef struct _NMDevicePrivate {
 
 	bool          real:1;
 
-	/* there was a IP config change, but no idle action was scheduled because device
-	 * is still not platform-init */
-	bool queued_ip4_config_pending:1;
-	bool queued_ip6_config_pending:1;
-
 	bool update_ip_config_completed_v4:1;
 	bool update_ip_config_completed_v6:1;
 
@@ -349,8 +344,9 @@ typedef struct _NMDevicePrivate {
 	gulong          config_changed_id;
 	guint32         mtu;
 	guint32         ip6_mtu;
-	guint32 mtu_initial;
-	guint32 ip6_mtu_initial;
+	guint32         mtu_initial;
+	guint32         ip6_mtu_initial;
+	NMDeviceMtuSource mtu_source;
 
 	guint32         v4_route_table;
 	guint32         v6_route_table;
@@ -366,8 +362,6 @@ typedef struct _NMDevicePrivate {
 	bool            carrier:1;
 	bool            ignore_carrier:1;
 
-	bool mtu_initialized:1;
-
 	bool            up:1;   /* IFF_UP */
 
 	bool            v4_commit_first_time:1;
@@ -735,6 +729,14 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_device_state_reason_to_str, NMDeviceStateReason,
 #define reason_to_string(reason) \
 	NM_UTILS_LOOKUP_STR (nm_device_state_reason_to_str, reason)
 
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (mtu_source_to_str, NMDeviceMtuSource,
+	NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_MTU_SOURCE_NONE,       "none"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_MTU_SOURCE_PARENT,     "parent"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_MTU_SOURCE_IP_CONFIG,  "ip-config"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_MTU_SOURCE_CONNECTION, "connection"),
+);
+
 /*****************************************************************************/
 
 NMSettings *
@@ -4001,7 +4003,7 @@ realize_start_setup (NMDevice *self,
 	/* Balanced by a thaw in nm_device_realize_finish() */
 	g_object_freeze_notify (G_OBJECT (self));
 
-	priv->mtu_initialized = FALSE;
+	priv->mtu_source = NM_DEVICE_MTU_SOURCE_NONE;
 	priv->mtu_initial = 0;
 	priv->ip6_mtu_initial = 0;
 	priv->ip6_mtu = 0;
@@ -4063,9 +4065,6 @@ realize_start_setup (NMDevice *self,
 		_notify (self, PROP_UDI);
 	}
 
-	priv->queued_ip4_config_pending = TRUE;
-	priv->queued_ip6_config_pending = TRUE;
-
 	nm_device_update_hw_address (self);
 	nm_device_update_initial_hw_address (self);
 	nm_device_update_permanent_hw_address (self, FALSE);
@@ -6822,7 +6821,9 @@ static gboolean
 dhcp4_grace_period_expired (gpointer user_data)
 {
 	NMDevice *self = user_data;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
+	priv->dhcp4.grace_id = 0;
 	_LOGI (LOGD_DHCP4, "DHCPv4: grace period expired");
 
 	nm_device_ip_method_failed (self, AF_INET,
@@ -7553,7 +7554,9 @@ static gboolean
 dhcp6_grace_period_expired (gpointer user_data)
 {
 	NMDevice *self = user_data;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
+	priv->dhcp6.grace_id = 0;
 	_LOGI (LOGD_DHCP6, "DHCPv6: grace period expired");
 
 	nm_device_ip_method_failed (self, AF_INET6,
@@ -7828,7 +7831,7 @@ generate_duid_from_machine_id (void)
 }
 
 static GBytes *
-dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, NMDhcpDuidEnforce *out_enforce)
+dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboolean *out_enforce)
 {
 	NMSettingIPConfig *s_ip6;
 	const char *duid;
@@ -7837,7 +7840,7 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, NMDhcp
 	GBytes *duid_out;
 	guint8 sha256_digest[32];
 	gsize len = sizeof (sha256_digest);
-	NMDhcpDuidEnforce duid_enforce = NM_DHCP_DUID_ENFORCE_ALWAYS;
+	gboolean duid_enforce = TRUE;
 	gs_free char *logstr1 = NULL;
 
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
@@ -7852,7 +7855,7 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, NMDhcp
 	}
 
 	if (nm_streq (duid, "lease")) {
-		duid_enforce = NM_DHCP_DUID_ENFORCE_NEVER;
+		duid_enforce = FALSE;
 		duid_out = generate_duid_from_machine_id ();
 		if (!duid_out) {
 			duid_error = "failure to read machine-id";
@@ -7972,7 +7975,7 @@ out_good:
 	       "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");
+	       duid_enforce ? "enforcing" : "prefer lease");
 
 	NM_SET_OUT (out_enforce, duid_enforce);
 	return duid_out;
@@ -7985,7 +7988,7 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	NMSettingIPConfig *s_ip6;
 	gs_unref_bytes GBytes *hwaddr = NULL;
 	gs_unref_bytes GBytes *duid = NULL;
-	NMDhcpDuidEnforce enforce_duid = NM_DHCP_DUID_ENFORCE_NEVER;
+	gboolean enforce_duid = FALSE;
 
 	const NMPlatformIP6Address *ll_addr = NULL;
 
@@ -8371,40 +8374,68 @@ nm_device_get_configured_mtu_from_connection_default (NMDevice *self,
 }
 
 guint32
-nm_device_get_configured_mtu_for_wired (NMDevice *self, gboolean *out_is_user_config)
+nm_device_get_configured_mtu_from_connection (NMDevice *self,
+                                              GType setting_type,
+                                              NMDeviceMtuSource *out_source)
 {
+	const char *global_property_name;
 	NMConnection *connection;
-	NMSettingWired *setting;
+	NMSetting *setting;
 	gint64 mtu_default;
-	guint32 mtu;
+	guint32 mtu = 0;
 
 	nm_assert (NM_IS_DEVICE (self));
-	nm_assert (out_is_user_config);
+	nm_assert (out_source);
 
 	connection = nm_device_get_applied_connection (self);
 	if (!connection)
 		g_return_val_if_reached (0);
 
-	setting = nm_connection_get_setting_wired (connection);
+	setting = nm_connection_get_setting (connection, setting_type);
+
+	if (setting_type == NM_TYPE_SETTING_WIRED) {
+		if (setting)
+			mtu = nm_setting_wired_get_mtu (NM_SETTING_WIRED (setting));
+		global_property_name = "ethernet.mtu";
+	} else if (setting_type == NM_TYPE_SETTING_WIRELESS) {
+		if (setting)
+			mtu = nm_setting_wireless_get_mtu (NM_SETTING_WIRELESS (setting));
+		global_property_name = "wifi.mtu";
+	} else if (setting_type == NM_TYPE_SETTING_INFINIBAND) {
+		if (setting)
+			mtu = nm_setting_infiniband_get_mtu (NM_SETTING_INFINIBAND (setting));
+		global_property_name = "infiniband.mtu";
+	} else if (setting_type == NM_TYPE_SETTING_IP_TUNNEL) {
+		if (setting)
+			mtu = nm_setting_ip_tunnel_get_mtu (NM_SETTING_IP_TUNNEL (setting));
+		global_property_name = "ip-tunnel.mtu";
+	} else
+		g_return_val_if_reached (0);
 
-	if (setting) {
-		mtu = nm_setting_wired_get_mtu (setting);
-		if (mtu) {
-			*out_is_user_config = TRUE;
-			return mtu;
-		}
+
+	if (mtu) {
+		*out_source = NM_DEVICE_MTU_SOURCE_CONNECTION;
+		return mtu;
 	}
 
-	mtu_default = nm_device_get_configured_mtu_from_connection_default (self, "ethernet.mtu");
+	mtu_default = nm_device_get_configured_mtu_from_connection_default (self, global_property_name);
 	if (mtu_default >= 0) {
-		*out_is_user_config = TRUE;
+		*out_source = NM_DEVICE_MTU_SOURCE_CONNECTION;
 		return (guint32) mtu_default;
 	}
 
-	*out_is_user_config = FALSE;
+	*out_source = NM_DEVICE_MTU_SOURCE_NONE;
 	return 0;
 }
 
+guint32
+nm_device_get_configured_mtu_for_wired (NMDevice *self, NMDeviceMtuSource *out_source)
+{
+	return nm_device_get_configured_mtu_from_connection (self,
+	                                                     NM_TYPE_SETTING_WIRED,
+	                                                     out_source);
+}
+
 /*****************************************************************************/
 
 static void
@@ -8431,6 +8462,7 @@ static void
 _commit_mtu (NMDevice *self, const NMIP4Config *config)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDeviceMtuSource source = NM_DEVICE_MTU_SOURCE_NONE;
 	guint32 ip6_mtu, ip6_mtu_orig;
 	guint32 mtu_desired, mtu_desired_orig;
 	guint32 mtu_plat;
@@ -8440,6 +8472,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 	} ip6_mtu_sysctl = { 0, };
 	int ifindex;
 	char sbuf[64], sbuf1[64], sbuf2[64];
+	gboolean success = TRUE;
 
 	ifindex = nm_device_get_ip_ifindex (self);
 	if (ifindex <= 0)
@@ -8453,7 +8486,6 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 	}
 
 	{
-		gboolean mtu_is_user_config = FALSE;
 		guint32 mtu = 0;
 
 		/* preferably, get the MTU from explict user-configuration.
@@ -8461,23 +8493,30 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 		 * MTUs from DHCP/PPP) or maybe fallback to a device-specific MTU. */
 
 		if (NM_DEVICE_GET_CLASS (self)->get_configured_mtu)
-			mtu = NM_DEVICE_GET_CLASS (self)->get_configured_mtu (self, &mtu_is_user_config);
+			mtu = NM_DEVICE_GET_CLASS (self)->get_configured_mtu (self, &source);
+
+		if (   config
+		    && source < NM_DEVICE_MTU_SOURCE_IP_CONFIG
+		    && nm_ip4_config_get_mtu (config)) {
+			mtu = nm_ip4_config_get_mtu (config);
+			source = NM_DEVICE_MTU_SOURCE_IP_CONFIG;
+		}
+
+		if (mtu != 0) {
+			_LOGT (LOGD_DEVICE,
+			       "mtu: value %u from source '%s' (%u), current source '%s' (%u)",
+			       (guint) mtu,
+			       mtu_source_to_str (source), (guint) source,
+			       mtu_source_to_str (priv->mtu_source), (guint) priv->mtu_source);
+		}
 
-		if (mtu_is_user_config)
+		if (   mtu != 0
+		    && (   source > priv->mtu_source
+		        || (priv->mtu_source == NM_DEVICE_MTU_SOURCE_PARENT && source == priv->mtu_source)))
 			mtu_desired = mtu;
 		else {
-			if (config)
-				mtu_desired = nm_ip4_config_get_mtu (config);
-			else
-				mtu_desired = 0;
-			if (!mtu_desired && !priv->mtu_initialized) {
-				/* there is no MTU specified, and this is the first commit of the MTU.
-				 * Reset a per-device MTU default, as returned from get_configured_mtu().
-				 *
-				 * The device might choose not to return a default MTU via get_configured_mtu()
-				 * to suppress this behavior. */
-				mtu_desired = mtu;
-			}
+			mtu_desired = 0;
+			source = NM_DEVICE_MTU_SOURCE_NONE;
 		}
 	}
 
@@ -8499,7 +8538,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 	}
 
 	ip6_mtu = priv->ip6_mtu;
-	if (!ip6_mtu && !priv->mtu_initialized) {
+	if (!ip6_mtu && priv->mtu_source == NM_DEVICE_MTU_SOURCE_NONE) {
 		/* initially, if the IPv6 MTU is not specified, grow it as large as the
 		 * link MTU @mtu_desired. Only exception is, if @mtu_desired is so small
 		 * to disable IPv6. */
@@ -8507,8 +8546,6 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 			ip6_mtu = mtu_desired;
 	}
 
-	priv->mtu_initialized = TRUE;
-
 	if (!ip6_mtu && !mtu_desired)
 		return;
 
@@ -8560,6 +8597,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 		if (mtu_desired && mtu_desired != mtu_plat) {
 			if (nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, mtu_desired) == NM_PLATFORM_ERROR_CANT_SET_MTU) {
 				anticipated_failure = TRUE;
+				success = FALSE;
 				_LOGW (LOGD_DEVICE, "mtu: failure to set MTU. %s",
 				       NM_IS_DEVICE_VLAN (self)
 				         ? "Is the parent's MTU size large enough?"
@@ -8581,10 +8619,15 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 				        anticipated_failure && errsv == EINVAL
 				           ? ": Is the underlying MTU value successfully set?"
 				           : "");
+				success = FALSE;
 			}
 			priv->carrier_wait_until_ms = nm_utils_get_monotonic_timestamp_ms () + CARRIER_WAIT_TIME_AFTER_MTU_MS;
 		}
 	}
+
+	if (success && source != NM_DEVICE_MTU_SOURCE_NONE)
+		priv->mtu_source = source;
+
 #undef _IP6_MTU_SYS
 }
 
@@ -10178,13 +10221,13 @@ _cleanup_ip_pre (NMDevice *self, int addr_family, CleanupType cleanup_type)
 	}
 
 	if (IS_IPv4) {
-		priv->queued_ip4_config_pending = FALSE;
 		dhcp4_cleanup (self, cleanup_type, FALSE);
 		arp_cleanup (self);
 		dnsmasq_cleanup (self);
 		ipv4ll_cleanup (self);
 	} else {
-		priv->queued_ip6_config_pending = FALSE;
+		g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref);
+		priv->dad6_failed_addrs = NULL;
 		g_clear_object (&priv->dad6_ip6_config);
 		dhcp6_cleanup (self, cleanup_type, FALSE);
 		nm_clear_g_source (&priv->linklocal6_timeout_id);
@@ -12185,22 +12228,16 @@ static gboolean
 queued_ip_config_change (NMDevice *self, int addr_family)
 {
 	NMDevicePrivate *priv;
-	gboolean need_ipv6ll = FALSE;
 	const gboolean IS_IPv4 = (addr_family == AF_INET);
-	NMPlatform *platform;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), G_SOURCE_REMOVE);
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	nm_assert (IS_IPv4 ? !priv->queued_ip4_config_pending : !priv->queued_ip6_config_pending);
-
 	/* Wait for any queued state changes */
 	if (priv->queued_state.id)
 		return G_SOURCE_CONTINUE;
 
-	priv->queued_ip_config_id_x[IS_IPv4] = 0;
-
 	/* If a commit is scheduled, this function would potentially interfere with
 	 * it changing IP configurations before they are applied. Postpone the
 	 * update in such case.
@@ -12209,39 +12246,39 @@ queued_ip_config_change (NMDevice *self, int addr_family)
 	                                    IS_IPv4
 	                                      ? activate_stage5_ip4_config_result
 	                                      : activate_stage5_ip6_config_commit,
-	                                    addr_family)) {
-		if (IS_IPv4) {
-			priv->queued_ip4_config_pending = FALSE;
-			priv->queued_ip_config_id_4 = g_idle_add (queued_ip4_config_change, self);
-		} else {
-			priv->queued_ip6_config_pending = FALSE;
-			priv->queued_ip_config_id_6 = g_idle_add (queued_ip6_config_change, self);
-		}
-		_LOGT (LOGD_DEVICE, "IP%c update was postponed",
-		       nm_utils_addr_family_to_char (addr_family));
-	} else {
-		update_ip_config (self, addr_family);
+	                                    addr_family))
+		return G_SOURCE_CONTINUE;
 
-		if (!IS_IPv4) {
-			/* Check whether we need to complete waiting for link-local.
-			 * We are also called from an idle handler, so no problem doing state transitions
-			 * now. */
-			linklocal6_check_complete (self);
-		}
+	priv->queued_ip_config_id_x[IS_IPv4] = 0;
+
+	update_ip_config (self, addr_family);
+
+	if (!IS_IPv4) {
+		/* Check whether we need to complete waiting for link-local.
+		 * We are also called from an idle handler, so no problem doing state transitions
+		 * now. */
+		linklocal6_check_complete (self);
 	}
 
 	if (!IS_IPv4) {
-		if (   priv->state < NM_DEVICE_STATE_DEACTIVATING
+		NMPlatform *platform;
+		GSList *dad6_failed_addrs, *iter;
+
+		dad6_failed_addrs = g_steal_pointer (&priv->dad6_failed_addrs);
+
+		if (   priv->state > NM_DEVICE_STATE_DISCONNECTED
+		    && priv->state < NM_DEVICE_STATE_DEACTIVATING
+		    && !nm_device_sys_iface_state_is_external (self)
 		    && (platform = nm_device_get_platform (self))
 		    && nm_platform_link_get (platform, priv->ifindex)) {
+			gboolean need_ipv6ll = FALSE;
+			NMNDiscConfigMap ndisc_config_changed = NM_NDISC_CONFIG_NONE;
+
 			/* Handle DAD failures */
-			while (priv->dad6_failed_addrs) {
-				nm_auto_nmpobj const NMPObject *obj = NULL;
+			for (iter = dad6_failed_addrs; iter; iter = iter->next) {
+				const NMPObject *obj = iter->data;
 				const NMPlatformIP6Address *addr;
 
-				obj = priv->dad6_failed_addrs->data;
-				priv->dad6_failed_addrs = g_slist_delete_link (priv->dad6_failed_addrs, priv->dad6_failed_addrs);
-
 				if (!nm_ndisc_dad_addr_is_fail_candidate (platform, obj))
 					continue;
 
@@ -12253,9 +12290,12 @@ queued_ip_config_change (NMDevice *self, int addr_family)
 				if (IN6_IS_ADDR_LINKLOCAL (&addr->address))
 					need_ipv6ll = TRUE;
 				else if (priv->ndisc)
-					nm_ndisc_dad_failed (priv->ndisc, &addr->address);
+					ndisc_config_changed |= nm_ndisc_dad_failed (priv->ndisc, &addr->address, FALSE);
 			}
 
+			if (ndisc_config_changed != NM_NDISC_CONFIG_NONE)
+				nm_ndisc_emit_config_change (priv->ndisc, ndisc_config_changed);
+
 			/* If no IPv6 link-local address exists but other addresses do then we
 			 * must add the LL address to remain conformant with RFC 3513 chapter 2.1
 			 * ("Addressing Model"): "All interfaces are required to have at least
@@ -12264,14 +12304,14 @@ queued_ip_config_change (NMDevice *self, int addr_family)
 			if (   priv->ip_config_6
 			    && nm_ip6_config_get_num_addresses (priv->ip_config_6))
 				need_ipv6ll = TRUE;
-
 			if (need_ipv6ll)
 				check_and_add_ipv6ll_addr (self);
-		} else {
-			g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref);
-			priv->dad6_failed_addrs = NULL;
 		}
 
+		g_slist_free_full (dad6_failed_addrs, (GDestroyNotify) nmp_object_unref);
+	}
+
+	if (!IS_IPv4) {
 		/* Check if DAD is still pending */
 		if (   priv->ip6_state == IP_CONF
 		    && priv->dad6_ip6_config
@@ -12327,16 +12367,20 @@ device_ipx_changed (NMPlatform *platform,
 	if (nm_device_get_ip_ifindex (self) != ifindex)
 		return;
 
+	if (!nm_device_is_real (self))
+		return;
+
+	if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) {
+		/* ignore all platform signals until the link is initialized in platform. */
+		return;
+	}
+
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	switch (obj_type) {
 	case NMP_OBJECT_TYPE_IP4_ADDRESS:
 	case NMP_OBJECT_TYPE_IP4_ROUTE:
-		if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) {
-			priv->queued_ip4_config_pending = TRUE;
-			nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_4));
-		} else if (!priv->queued_ip_config_id_4) {
-			priv->queued_ip4_config_pending = FALSE;
+		if (!priv->queued_ip_config_id_4) {
 			priv->queued_ip_config_id_4 = g_idle_add (queued_ip4_config_change, self);
 			_LOGD (LOGD_DEVICE, "queued IP4 config change");
 		}
@@ -12350,13 +12394,10 @@ device_ipx_changed (NMPlatform *platform,
 			priv->dad6_failed_addrs = g_slist_prepend (priv->dad6_failed_addrs,
 			                                           (gpointer) nmp_object_ref (NMP_OBJECT_UP_CAST (addr)));
 		}
+
 		/* fall through */
 	case NMP_OBJECT_TYPE_IP6_ROUTE:
-		if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) {
-			priv->queued_ip6_config_pending = TRUE;
-			nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_6));
-		} else if (!priv->queued_ip_config_id_6) {
-			priv->queued_ip6_config_pending = FALSE;
+		if (!priv->queued_ip_config_id_6) {
 			priv->queued_ip_config_id_6 = g_idle_add (queued_ip6_config_change, self);
 			_LOGD (LOGD_DEVICE, "queued IP6 config change");
 		}
@@ -12623,17 +12664,11 @@ _set_unmanaged_flags (NMDevice *self,
 			                               !!unmanaged);
 		}
 
-		if (priv->queued_ip4_config_pending) {
-			priv->queued_ip4_config_pending = FALSE;
-			nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_4));
-			priv->queued_ip_config_id_4 = g_idle_add (queued_ip4_config_change, self);
-		}
-
-		if (priv->queued_ip6_config_pending) {
-			priv->queued_ip6_config_pending = FALSE;
-			nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_6));
-			priv->queued_ip_config_id_6 = g_idle_add (queued_ip6_config_change, self);
-		}
+		/* trigger an initial update of IP configuration. */
+		nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_4));
+		nm_assert_se (!nm_clear_g_source (&priv->queued_ip_config_id_6));
+		priv->queued_ip_config_id_4 = g_idle_add (queued_ip4_config_change, self);
+		priv->queued_ip_config_id_6 = g_idle_add (queued_ip6_config_change, self);
 
 		if (!priv->pending_actions) {
 			do_notify_has_pending_actions = TRUE;
@@ -13623,7 +13658,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 		NM_DEVICE_GET_CLASS (self)->deactivate_reset_hw_addr (self);
 	}
 
-	priv->mtu_initialized = FALSE;
+	priv->mtu_source = NM_DEVICE_MTU_SOURCE_NONE;
 	if (priv->mtu_initial || priv->ip6_mtu_initial) {
 		ifindex = nm_device_get_ip_ifindex (self);
 
@@ -13709,12 +13744,10 @@ nm_device_spawn_iface_helper (NMDevice *self)
 		g_ptr_array_add (argv, g_strdup_printf ("%d %s", (int) stable_type, stable_id));
 	}
 
-	logging_backend = nm_config_get_is_debug (nm_config_get ())
-	                  ? g_strdup ("debug")
-	                  : nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG,
-	                                              NM_CONFIG_KEYFILE_GROUP_LOGGING,
-	                                              NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND,
-	                                              NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
+	logging_backend = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG,
+	                                            NM_CONFIG_KEYFILE_GROUP_LOGGING,
+	                                            NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND,
+	                                            NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
 	if (logging_backend) {
 		g_ptr_array_add (argv, g_strdup ("--logging-backend"));
 		g_ptr_array_add (argv, logging_backend);
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 0d4d5cf8..010a0521 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -44,6 +44,13 @@ typedef enum {
 	NM_DEVICE_SYS_IFACE_STATE_REMOVED,
 } NMDeviceSysIfaceState;
 
+typedef enum {
+	NM_DEVICE_MTU_SOURCE_NONE,
+	NM_DEVICE_MTU_SOURCE_PARENT,
+	NM_DEVICE_MTU_SOURCE_IP_CONFIG,
+	NM_DEVICE_MTU_SOURCE_CONNECTION,
+} NMDeviceMtuSource;
+
 static inline NMDeviceStateReason
 nm_device_state_reason_check (NMDeviceStateReason reason)
 {
@@ -287,7 +294,7 @@ typedef struct _NMDeviceClass {
 	                                  NMConnection *connection,
 	                                  char **specific_object);
 
-	guint32     (*get_configured_mtu) (NMDevice *self, gboolean *out_is_user_config);
+	guint32     (*get_configured_mtu) (NMDevice *self, NMDeviceMtuSource *out_source);
 
 	/* Checks whether the connection is compatible with the device using
 	 * only the devices type and characteristics.  Does not use any live
diff --git a/src/devices/ovs/nm-ovs-factory.c b/src/devices/ovs/nm-ovs-factory.c
index 830f94fc..766c650b 100644
--- a/src/devices/ovs/nm-ovs-factory.c
+++ b/src/devices/ovs/nm-ovs-factory.c
@@ -80,14 +80,14 @@ new_device_from_type (const char *name, NMDeviceType device_type)
 
 	if (device_type == NM_DEVICE_TYPE_OVS_INTERFACE) {
 		type = NM_TYPE_DEVICE_OVS_INTERFACE;
-		type_desc = "OpenVSwitch Interface";
+		type_desc = "Open vSwitch Interface";
 		link_type = NM_LINK_TYPE_OPENVSWITCH;
 	} else if (device_type == NM_DEVICE_TYPE_OVS_PORT) {
 		type = NM_TYPE_DEVICE_OVS_PORT;
-		type_desc = "OpenVSwitch Port";
+		type_desc = "Open vSwitch Port";
 	} else if (device_type == NM_DEVICE_TYPE_OVS_BRIDGE) {
 		type = NM_TYPE_DEVICE_OVS_BRIDGE;
-		type_desc = "OpenVSwitch Bridge";
+		type_desc = "Open vSwitch Bridge";
 	} else {
 		return NULL;
 	}
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c
index d28fcdbd..7667816a 100644
--- a/src/devices/wifi/nm-device-iwd.c
+++ b/src/devices/wifi/nm-device-iwd.c
@@ -73,7 +73,6 @@ typedef struct {
 	GCancellable *  cancellable;
 	NMDeviceWifiCapabilities capabilities;
 	NMActRequestGetSecretsCallId *wifi_secrets_id;
-	GDBusMethodInvocation *secrets_request;
 	guint           periodic_scan_id;
 	bool            enabled:1;
 	bool            can_scan:1;
@@ -379,14 +378,6 @@ wifi_secrets_cancel (NMDeviceIwd *self)
 	if (priv->wifi_secrets_id)
 		nm_act_request_cancel_secrets (NULL, priv->wifi_secrets_id);
 	nm_assert (!priv->wifi_secrets_id);
-
-	if (priv->secrets_request) {
-		g_dbus_method_invocation_return_error_literal (priv->secrets_request, NM_DEVICE_ERROR,
-		                                               NM_DEVICE_ERROR_INVALID_CONNECTION,
-		                                               "NM secrets request cancelled");
-		priv->secrets_request = NULL;
-	}
-
 }
 
 static void
@@ -545,7 +536,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 		return FALSE;
 
 	mode = nm_setting_wireless_get_mode (s_wireless);
-	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
+	if (mode && g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
 		return FALSE;
 
 	/* 8021x networks can only be used if they've been provisioned on the IWD side and
@@ -575,7 +566,7 @@ check_connection_available (NMDevice *device,
 
 	/* Only Infrastrusture mode at this time */
 	mode = nm_setting_wireless_get_mode (s_wifi);
-	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
+	if (mode && g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
 		return FALSE;
 
 	/* Hidden SSIDs not supported yet */
@@ -630,7 +621,7 @@ complete_connection (NMDevice *device,
 
 	mode = s_wifi ? nm_setting_wireless_get_mode (s_wifi) : NULL;
 
-	if (s_wifi && !nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) {
+	if (mode && !nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) {
 		g_set_error_literal (error,
 		                     NM_DEVICE_ERROR,
 		                     NM_DEVICE_ERROR_INVALID_CONNECTION,
@@ -806,7 +797,7 @@ can_auto_connect (NMDevice *device,
 
 	/* Only Infrastrusture mode */
 	mode = nm_setting_wireless_get_mode (s_wifi);
-	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
+	if (mode && g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0)
 		return FALSE;
 
 	/* Don't autoconnect to networks that have been tried at least once
@@ -1001,6 +992,127 @@ scanning_prohibited (NMDeviceIwd *self, gboolean periodic)
 	return !priv->can_scan;
 }
 
+/*
+ * try_reply_agent_request
+ *
+ * Check if the connection settings already have the secrets corresponding
+ * to the IWD agent method that was invoked.  If they do, send the method reply
+ * with the appropriate secrets.  Otherwise return the missing secret's setting
+ * name and key so the caller can send a NM secrets request with this data.
+ * Return TRUE in either case, return FALSE if an error is detected.
+ */
+static gboolean
+try_reply_agent_request (NMDeviceIwd *self,
+                         NMConnection *connection,
+                         GDBusMethodInvocation *invocation,
+                         const gchar **setting_name,
+                         const gchar **setting_key,
+                         gboolean *replied)
+{
+	const gchar *method_name = g_dbus_method_invocation_get_method_name (invocation);
+	NMSettingWirelessSecurity *s_wireless_sec;
+	NMSetting8021x *s_8021x;
+
+	s_wireless_sec = nm_connection_get_setting_wireless_security (connection);
+	s_8021x = nm_connection_get_setting_802_1x (connection);
+
+	*replied = FALSE;
+
+	if (!strcmp (method_name, "RequestPassphrase")) {
+		const gchar *psk;
+
+		if (!s_wireless_sec)
+			return FALSE;
+
+		psk = nm_setting_wireless_security_get_psk (s_wireless_sec);
+		if (psk) {
+			_LOGD (LOGD_DEVICE | LOGD_WIFI,
+			       "Returning the PSK to the IWD Agent");
+
+			g_dbus_method_invocation_return_value (invocation,
+			                                       g_variant_new ("(s)", psk));
+			*replied = TRUE;
+			return TRUE;
+		}
+
+		*setting_name = NM_SETTING_WIRELESS_SECURITY_SETTING_NAME;
+		*setting_key = NM_SETTING_WIRELESS_SECURITY_PSK;
+		return TRUE;
+	} else if (!strcmp (method_name, "RequestPrivateKeyPassphrase")) {
+		const gchar *password;
+
+		if (!s_8021x)
+			return FALSE;
+
+		password = nm_setting_802_1x_get_private_key_password (s_8021x);
+		if (password) {
+			_LOGD (LOGD_DEVICE | LOGD_WIFI,
+			       "Returning the private key password to the IWD Agent");
+
+			g_dbus_method_invocation_return_value (invocation,
+			                                       g_variant_new ("(s)", password));
+			*replied = TRUE;
+			return TRUE;
+		}
+
+		*setting_name = NM_SETTING_802_1X_SETTING_NAME;
+		*setting_key = NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD;
+		return TRUE;
+	} else if (!strcmp (method_name, "RequestUserNameAndPassword")) {
+		const gchar *identity, *password;
+
+		if (!s_8021x)
+			return FALSE;
+
+		identity = nm_setting_802_1x_get_identity (s_8021x);
+		password = nm_setting_802_1x_get_password (s_8021x);
+		if (identity && password) {
+			_LOGD (LOGD_DEVICE | LOGD_WIFI,
+			       "Returning the username and password to the IWD Agent");
+
+			g_dbus_method_invocation_return_value (invocation,
+			                                       g_variant_new ("(ss)", identity, password));
+			*replied = TRUE;
+			return TRUE;
+		}
+
+		*setting_name = NM_SETTING_802_1X_SETTING_NAME;
+		if (!identity)
+			*setting_key = NM_SETTING_802_1X_IDENTITY;
+		else
+			*setting_key = NM_SETTING_802_1X_PASSWORD;
+		return TRUE;
+	} else if (!strcmp (method_name, "RequestUserPassword")) {
+		const gchar *password;
+
+		if (!s_8021x)
+			return FALSE;
+
+		password = nm_setting_802_1x_get_password (s_8021x);
+		if (password) {
+			_LOGD (LOGD_DEVICE | LOGD_WIFI,
+			       "Returning the user password to the IWD Agent");
+
+			g_dbus_method_invocation_return_value (invocation,
+			                                       g_variant_new ("(s)", password));
+			*replied = TRUE;
+			return TRUE;
+		}
+
+		*setting_name = NM_SETTING_802_1X_SETTING_NAME;
+		*setting_key = NM_SETTING_802_1X_PASSWORD;
+		return TRUE;
+	} else
+		return FALSE;
+}
+
+static void
+wifi_secrets_get_one (NMDeviceIwd *self,
+                      const char *setting_name,
+                      NMSecretAgentGetSecretsFlags flags,
+                      const char *setting_key,
+                      GDBusMethodInvocation *invocation);
+
 static void
 wifi_secrets_cb (NMActRequest *req,
                  NMActRequestGetSecretsCallId *call_id,
@@ -1008,25 +1120,33 @@ wifi_secrets_cb (NMActRequest *req,
                  GError *error,
                  gpointer user_data)
 {
-	NMDevice *device = user_data;
-	NMDeviceIwd *self = user_data;
+	NMDeviceIwd *self;
 	NMDeviceIwdPrivate *priv;
-	NMSettingWirelessSecurity *s_wireless_sec;
-	const gchar *psk;
+	NMDevice *device;
+	GDBusMethodInvocation *invocation;
+	const gchar *setting_name;
+	const gchar *setting_key;
+	gboolean replied;
+	NMSecretAgentGetSecretsFlags get_secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
+
+	nm_utils_user_data_unpack (user_data, &self, &invocation);
 
 	g_return_if_fail (NM_IS_DEVICE_IWD (self));
-	g_return_if_fail (NM_IS_ACT_REQUEST (req));
 
 	priv = NM_DEVICE_IWD_GET_PRIVATE (self);
+	device = NM_DEVICE (self);
 
 	g_return_if_fail (priv->wifi_secrets_id == call_id);
 
 	priv->wifi_secrets_id = NULL;
 
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR,
+		                                               NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                                               "NM secrets request cancelled");
 		return;
+	}
 
-	g_return_if_fail (priv->secrets_request);
 	g_return_if_fail (req == nm_device_get_act_request (device));
 	g_return_if_fail (nm_act_request_get_settings_connection (req) == s_connection);
 
@@ -1038,32 +1158,30 @@ wifi_secrets_cb (NMActRequest *req,
 		goto secrets_error;
 	}
 
-	s_wireless_sec = nm_connection_get_setting_wireless_security (nm_act_request_get_applied_connection (req));
-	if (!s_wireless_sec)
-		goto secrets_error;
-
-	psk = nm_setting_wireless_security_get_psk (s_wireless_sec);
-	if (!psk)
+	if (!try_reply_agent_request (self, nm_act_request_get_applied_connection (req),
+	                              invocation, &setting_name, &setting_key,
+	                              &replied))
 		goto secrets_error;
 
-	_LOGD (LOGD_DEVICE | LOGD_WIFI,
-	       "Returning a new PSK to the IWD Agent");
+	if (replied) {
+		/* Change state back to what it was before NEED_AUTH */
+		nm_device_state_changed (device, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
+		return;
+	}
 
-	g_dbus_method_invocation_return_value (priv->secrets_request,
-	                                       g_variant_new ("(s)", psk));
-	priv->secrets_request = NULL;
+	if (nm_settings_connection_get_timestamp (nm_act_request_get_settings_connection (req),
+	                                          NULL))
+		get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
 
-	/* Change state back to what it was before NEED_AUTH */
-	nm_device_state_changed (device, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
+	/* Request further secrets if we still need something */
+	wifi_secrets_get_one (self, setting_name, get_secret_flags,
+	                      setting_key, invocation);
 	return;
 
 secrets_error:
-	if (priv->secrets_request) {
-		g_dbus_method_invocation_return_error_literal (priv->secrets_request, NM_DEVICE_ERROR,
-		                                               NM_DEVICE_ERROR_INVALID_CONNECTION,
-		                                               "NM secrets request failed");
-		priv->secrets_request = NULL;
-	}
+	g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR,
+	                                               NM_DEVICE_ERROR_INVALID_CONNECTION,
+	                                               "NM secrets request failed");
 
 	nm_device_state_changed (device,
 	                         NM_DEVICE_STATE_FAILED,
@@ -1073,9 +1191,11 @@ secrets_error:
 }
 
 static void
-wifi_secrets_get_secrets (NMDeviceIwd *self,
-                          const char *setting_name,
-                          NMSecretAgentGetSecretsFlags flags)
+wifi_secrets_get_one (NMDeviceIwd *self,
+                      const char *setting_name,
+                      NMSecretAgentGetSecretsFlags flags,
+                      const char *setting_key,
+                      GDBusMethodInvocation *invocation)
 {
 	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 	NMActRequest *req;
@@ -1089,9 +1209,9 @@ wifi_secrets_get_secrets (NMDeviceIwd *self,
 	                                                    TRUE,
 	                                                    setting_name,
 	                                                    flags,
-	                                                    NULL,
+	                                                    setting_key,
 	                                                    wifi_secrets_cb,
-	                                                    self);
+	                                                    nm_utils_user_data_pack (self, invocation));
 }
 
 static void
@@ -1219,15 +1339,13 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	ap = ap_path ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path) : NULL;
 	if (!ap) {
 		ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection);
-
-		/* TODO: assuming hidden networks aren't supported do we need
-		 * to consider the case of APs that are not in the scan list
-		 * yet, for which nm-device-wifi.c creates the temporary fake
-		 * AP object?
-		 */
+		if (!ap) {
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+			return NM_ACT_STAGE_RETURN_FAILURE;
+		}
 
 		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
-	                                              nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)));
+		                                          nm_dbus_object_get_path (NM_DBUS_OBJECT (ap)));
 	}
 
 	set_current_ap (self, ap, FALSE);
@@ -1316,29 +1434,11 @@ out:
 }
 
 static guint32
-get_configured_mtu (NMDevice *device, gboolean *out_is_user_config)
+get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
 {
-	NMSettingWireless *setting;
-	gint64 mtu_default;
-	guint32 mtu;
-
-	nm_assert (NM_IS_DEVICE (device));
-	nm_assert (out_is_user_config);
-
-	setting = NM_SETTING_WIRELESS (nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS));
-	if (!setting)
-		g_return_val_if_reached (0);
-
-	mtu = nm_setting_wireless_get_mtu (setting);
-	if (mtu == 0) {
-		mtu_default = nm_device_get_configured_mtu_from_connection_default (device, "wifi.mtu");
-		if (mtu_default >= 0) {
-			*out_is_user_config = TRUE;
-			return (guint32) mtu_default;
-		}
-	}
-	*out_is_user_config = (mtu != 0);
-	return mtu;
+	return nm_device_get_configured_mtu_from_connection (device,
+	                                                     NM_TYPE_SETTING_WIRELESS,
+	                                                     out_source);
 }
 
 static gboolean
@@ -1748,40 +1848,44 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object)
 }
 
 gboolean
-nm_device_iwd_agent_psk_query (NMDeviceIwd *self,
-                               GDBusMethodInvocation *invocation)
+nm_device_iwd_agent_query (NMDeviceIwd *self,
+                           GDBusMethodInvocation *invocation)
 {
-	NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self);
 	NMActRequest *req;
-	NMSettingWirelessSecurity *s_wireless_sec;
-	const gchar *psk;
+	const gchar *setting_name;
+	const gchar *setting_key;
+	gboolean replied;
+	NMSecretAgentGetSecretsFlags get_secret_flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION;
 
 	req = nm_device_get_act_request (NM_DEVICE (self));
 	if (!req)
 		return FALSE;
 
-	s_wireless_sec = nm_connection_get_setting_wireless_security (nm_act_request_get_applied_connection (req));
-	if (!s_wireless_sec)
+	if (!try_reply_agent_request (self, nm_act_request_get_applied_connection (req),
+	                              invocation, &setting_name, &setting_key,
+	                              &replied))
 		return FALSE;
 
-	psk = nm_setting_wireless_security_get_psk (s_wireless_sec);
-	if (psk) {
-		_LOGD (LOGD_DEVICE | LOGD_WIFI,
-		       "Returning the PSK to the IWD Agent");
-
-		g_dbus_method_invocation_return_value (invocation,
-		                                       g_variant_new ("(s)", psk));
+	if (replied)
 		return TRUE;
-	}
+
+	/* Normally require new secrets every time IWD asks for them.
+	 * IWD only queries us if it has not saved the secrets (e.g. by policy)
+	 * or a previous attempt has failed with current secrets so it wants
+	 * a fresh set.  However if this is a new connection it may include
+	 * all of the needed settings already so allow using these, too.
+	 * Connection timestamp is set after activation or after first
+	 * activation failure (to 0).
+	 */
+	if (nm_settings_connection_get_timestamp (nm_act_request_get_settings_connection (req),
+	                                          NULL))
+		get_secret_flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
 
 	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH,
 	                         NM_DEVICE_STATE_REASON_NO_SECRETS);
-	wifi_secrets_get_secrets (self,
-	                          NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
-	                          NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
-	                            | NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW);
+	wifi_secrets_get_one (self, setting_name, get_secret_flags,
+	                      setting_key, invocation);
 
-	priv->secrets_request = invocation;
 	return TRUE;
 }
 
diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h
index a4253f52..825123b1 100644
--- a/src/devices/wifi/nm-device-iwd.h
+++ b/src/devices/wifi/nm-device-iwd.h
@@ -51,8 +51,8 @@ NMDevice *nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabil
 
 void nm_device_iwd_set_dbus_object (NMDeviceIwd *device, GDBusObject *object);
 
-gboolean nm_device_iwd_agent_psk_query (NMDeviceIwd *device,
-                                        GDBusMethodInvocation *invocation);
+gboolean nm_device_iwd_agent_query (NMDeviceIwd *device,
+                                    GDBusMethodInvocation *invocation);
 
 const CList *_nm_device_iwd_get_aps (NMDeviceIwd *self);
 
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 1a86e13b..6bd06524 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -2786,29 +2786,11 @@ act_stage3_ip6_config_start (NMDevice *device,
 }
 
 static guint32
-get_configured_mtu (NMDevice *device, gboolean *out_is_user_config)
+get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
 {
-	NMSettingWireless *setting;
-	gint64 mtu_default;
-	guint32 mtu;
-
-	nm_assert (NM_IS_DEVICE (device));
-	nm_assert (out_is_user_config);
-
-	setting = NM_SETTING_WIRELESS (nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS));
-	if (!setting)
-		g_return_val_if_reached (0);
-
-	mtu = nm_setting_wireless_get_mtu (setting);
-	if (mtu == 0) {
-		mtu_default = nm_device_get_configured_mtu_from_connection_default (device, "wifi.mtu");
-		if (mtu_default >= 0) {
-			*out_is_user_config = TRUE;
-			return (guint32) mtu_default;
-		}
-	}
-	*out_is_user_config = (mtu != 0);
-	return mtu;
+	return nm_device_get_configured_mtu_from_connection (device,
+	                                                     NM_TYPE_SETTING_WIRELESS,
+	                                                     out_source);
 }
 
 static gboolean
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
index 39db3a04..0f95fa08 100644
--- a/src/devices/wifi/nm-iwd-manager.c
+++ b/src/devices/wifi/nm-iwd-manager.c
@@ -84,27 +84,31 @@ G_DEFINE_TYPE (NMIwdManager, nm_iwd_manager, G_TYPE_OBJECT)
 /*****************************************************************************/
 
 static void
-psk_agent_dbus_method_cb (GDBusConnection *connection,
-                          const gchar *sender, const gchar *object_path,
-                          const gchar *interface_name, const gchar *method_name,
-                          GVariant *parameters,
-                          GDBusMethodInvocation *invocation,
-                          gpointer user_data)
+agent_dbus_method_cb (GDBusConnection *connection,
+                      const gchar *sender, const gchar *object_path,
+                      const gchar *interface_name, const gchar *method_name,
+                      GVariant *parameters,
+                      GDBusMethodInvocation *invocation,
+                      gpointer user_data)
 {
 	NMIwdManager *self = user_data;
 	NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self);
-	GDBusObjectManagerClient *omc = G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager);
 	const gchar *network_path, *device_path, *ifname;
 	gs_unref_object GDBusInterface *network = NULL, *device_obj = NULL;
 	gs_unref_variant GVariant *value = NULL;
 	gint ifindex;
 	NMDevice *device;
+	gs_free char *name_owner = NULL;
 
 	/* Be paranoid and check the sender address */
-	if (!nm_streq0 (g_dbus_object_manager_client_get_name_owner (omc), sender))
+	name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager));
+	if (!nm_streq0 (name_owner, sender))
 		goto return_error;
 
-	g_variant_get (parameters, "(&o)", &network_path);
+	if (!strcmp (method_name, "RequestUserPassword"))
+		g_variant_get (parameters, "(&os)", &network_path, NULL);
+	else
+		g_variant_get (parameters, "(&o)", &network_path);
 
 	network = g_dbus_object_manager_get_interface (priv->object_manager,
 	                                               network_path,
@@ -113,7 +117,7 @@ psk_agent_dbus_method_cb (GDBusConnection *connection,
 	device_path = g_variant_get_string (value, NULL);
 
 	if (!device_path) {
-		_LOGE ("Device not cached for network %s in IWD Agent request",
+		_LOGD ("agent-request: device not cached for network %s in IWD Agent request",
 		       network_path);
 		goto return_error;
 	}
@@ -126,103 +130,99 @@ psk_agent_dbus_method_cb (GDBusConnection *connection,
 	ifname = g_variant_get_string (value, NULL);
 
 	if (!ifname) {
-		_LOGE ("Name not cached for device %s in IWD Agent request",
+		_LOGD ("agent-request: name not cached for device %s in IWD Agent request",
 		       device_path);
 		goto return_error;
 	}
 
 	ifindex = if_nametoindex (ifname);
 	if (!ifindex) {
-		_LOGE ("if_nametoindex failed for Name %s for Device at %s: %i",
+		_LOGD ("agent-request: if_nametoindex failed for Name %s for Device at %s: %i",
 		       ifname, device_path, errno);
 		goto return_error;
 	}
 
 	device = nm_manager_get_device_by_ifindex (priv->manager, ifindex);
 	if (!NM_IS_DEVICE_IWD (device)) {
-		_LOGE ("IWD device named %s is not a Wifi device in IWD Agent request",
-                       ifname);
+		_LOGD ("agent-request: IWD device named %s is not a Wifi device in IWD Agent request",
+		       ifname);
 		goto return_error;
 	}
 
-	if (nm_device_iwd_agent_psk_query (NM_DEVICE_IWD (device), invocation))
+	if (nm_device_iwd_agent_query (NM_DEVICE_IWD (device), invocation))
 		return;
 
-	_LOGE ("Device %s did not handle the IWD Agent request", ifname);
+	_LOGD ("agent-request: device %s did not handle the IWD Agent request", ifname);
 
 return_error:
 	/* IWD doesn't look at the specific error */
 	g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR,
 	                                               NM_DEVICE_ERROR_INVALID_CONNECTION,
-	                                               "No PSK available for this connection");
+	                                               "Secrets not available for this connection");
 }
 
+static const GDBusInterfaceInfo iwd_agent_iface_info = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
+	"net.connman.iwd.Agent",
+	.methods = NM_DEFINE_GDBUS_METHOD_INFOS (
+		NM_DEFINE_GDBUS_METHOD_INFO (
+			"RequestPassphrase",
+			.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+				NM_DEFINE_GDBUS_ARG_INFO ("network", "o"),
+			),
+			.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+				NM_DEFINE_GDBUS_ARG_INFO ("passphrase", "s"),
+			),
+		),
+		NM_DEFINE_GDBUS_METHOD_INFO (
+			"RequestPrivateKeyPassphrase",
+			.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+				NM_DEFINE_GDBUS_ARG_INFO ("network", "o"),
+			),
+			.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+				NM_DEFINE_GDBUS_ARG_INFO ("passphrase", "s"),
+			),
+		),
+		NM_DEFINE_GDBUS_METHOD_INFO (
+			"RequestUserNameAndPassword",
+			.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+				NM_DEFINE_GDBUS_ARG_INFO ("network", "o"),
+			),
+			.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+				NM_DEFINE_GDBUS_ARG_INFO ("user", "s"),
+				NM_DEFINE_GDBUS_ARG_INFO ("password", "s"),
+			),
+		),
+		NM_DEFINE_GDBUS_METHOD_INFO (
+			"RequestUserPassword",
+			.in_args = NM_DEFINE_GDBUS_ARG_INFOS (
+				NM_DEFINE_GDBUS_ARG_INFO ("network", "o"),
+				NM_DEFINE_GDBUS_ARG_INFO ("user", "s"),
+			),
+			.out_args = NM_DEFINE_GDBUS_ARG_INFOS (
+				NM_DEFINE_GDBUS_ARG_INFO ("password", "s"),
+			),
+		),
+	),
+);
+
 static guint
-psk_agent_export (GDBusConnection *connection, gpointer user_data,
+iwd_agent_export (GDBusConnection *connection, gpointer user_data,
                   gchar **agent_path, GError **error)
 {
-	static const GDBusArgInfo request_passphrase_arg_network = {
-		-1,
-		(gchar *) "network",
-		(gchar *) "o",
-		NULL,
-	};
-	static const GDBusArgInfo *const request_passphrase_in_args[] = {
-		&request_passphrase_arg_network,
-		NULL,
-	};
-	static const GDBusArgInfo request_passphrase_arg_passphrase = {
-		-1,
-		(gchar *) "passphrase",
-		(gchar *) "s",
-		NULL,
-	};
-	static const GDBusArgInfo *const request_passphrase_out_args[] = {
-		&request_passphrase_arg_passphrase,
-		NULL,
+	static const GDBusInterfaceVTable vtable = {
+		.method_call = agent_dbus_method_cb,
 	};
-	static const GDBusMethodInfo request_passphrase_info = {
-		-1,
-		(gchar *) "RequestPassphrase",
-		(GDBusArgInfo **) &request_passphrase_in_args,
-		(GDBusArgInfo **) &request_passphrase_out_args,
-		NULL,
-	};
-	static const GDBusMethodInfo *const method_info[] = {
-		&request_passphrase_info,
-		NULL,
-	};
-	static GDBusInterfaceInfo interface_info = {
-		-1,
-		(gchar *) "net.connman.iwd.Agent",
-		(GDBusMethodInfo **) &method_info,
-		NULL,
-		NULL,
-		NULL,
-	};
-	static GDBusInterfaceVTable vtable = {
-		psk_agent_dbus_method_cb,
-		NULL,
-		NULL,
-	};
-
 	gchar path[50];
 	unsigned int rnd;
 	guint id;
 
-	if  (!nm_utils_random_bytes (&rnd, sizeof (rnd))) {
-		g_set_error_literal (error,
-		                     NM_DEVICE_ERROR,
-		                     NM_DEVICE_ERROR_FAILED,
-		                     "Can't read urandom.");
-		return 0;
-	}
+	nm_utils_random_bytes (&rnd, sizeof (rnd));
 
 	nm_sprintf_buf (path, "/agent/%u", rnd);
 
 	id = g_dbus_connection_register_object (connection, path,
-	                                        &interface_info, &vtable,
-	                                        user_data, NULL, error);
+	                                        NM_UNCONST_PTR (GDBusInterfaceInfo, &iwd_agent_iface_info),
+	                                        &vtable, user_data, NULL, error);
 
 	if (id)
 		*agent_path = g_strdup (path);
@@ -549,7 +549,7 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data)
 
 	connection = g_dbus_object_manager_client_get_connection (G_DBUS_OBJECT_MANAGER_CLIENT (object_manager));
 
-	priv->agent_id = psk_agent_export (connection, self,
+	priv->agent_id = iwd_agent_export (connection, self,
 	                                   &priv->agent_path, &error);
 	if (!priv->agent_id) {
 		_LOGE ("failed to export the IWD Agent: PSK/8021x WiFi networks will not work: %s",
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index ff1579d1..617d5c4a 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -859,7 +859,7 @@ nm_modem_stage3_ip6_config_start (NMModem *self,
 }
 
 guint32
-nm_modem_get_configured_mtu (NMDevice *self, gboolean *out_is_user_config)
+nm_modem_get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source)
 {
 	NMConnection *connection;
 	NMSetting *setting;
@@ -868,7 +868,7 @@ nm_modem_get_configured_mtu (NMDevice *self, gboolean *out_is_user_config)
 	const char *property_name;
 
 	nm_assert (NM_IS_DEVICE (self));
-	nm_assert (out_is_user_config);
+	nm_assert (out_source);
 
 	connection = nm_device_get_applied_connection (self);
 	if (!connection)
@@ -881,19 +881,19 @@ nm_modem_get_configured_mtu (NMDevice *self, gboolean *out_is_user_config)
 	if (setting) {
 		g_object_get (setting, "mtu", &mtu, NULL);
 		if (mtu) {
-			*out_is_user_config = TRUE;
+			*out_source = NM_DEVICE_MTU_SOURCE_CONNECTION;
 			return mtu;
 		}
 
 		property_name = NM_IS_SETTING_GSM (setting) ? "gsm.mtu" : "cdma.mtu";
 		mtu_default = nm_device_get_configured_mtu_from_connection_default (self, property_name);
 		if (mtu_default >= 0) {
-			*out_is_user_config = TRUE;
+			*out_source = NM_DEVICE_MTU_SOURCE_CONNECTION;
 			return (guint32) mtu_default;
 		}
 	}
 
-	*out_is_user_config = FALSE;
+	*out_source = NM_DEVICE_MTU_SOURCE_NONE;
 	return 0;
 }
 
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index 6e78d2d5..cad9ece4 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -275,6 +275,6 @@ void nm_modem_emit_ip6_config_result (NMModem *self,
 
 const gchar *nm_modem_ip_type_to_string (NMModemIPType ip_type);
 
-guint32 nm_modem_get_configured_mtu (NMDevice *self, gboolean *out_is_user_config);
+guint32 nm_modem_get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source);
 
 #endif /* __NETWORKMANAGER_MODEM_H__ */
diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c
index 390b7054..360bd367 100644
--- a/src/dhcp/nm-dhcp-client.c
+++ b/src/dhcp/nm-dhcp-client.c
@@ -513,7 +513,7 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self,
 }
 
 static GBytes *
-get_duid (NMDhcpClient *self, gboolean global)
+get_duid (NMDhcpClient *self)
 {
 	return NULL;
 }
@@ -521,7 +521,7 @@ get_duid (NMDhcpClient *self, gboolean global)
 gboolean
 nm_dhcp_client_start_ip6 (NMDhcpClient *self,
                           GBytes *client_id,
-                          NMDhcpDuidEnforce enforce_duid,
+                          gboolean enforce_duid,
                           const char *dhcp_anycast_addr,
                           const struct in6_addr *ll_addr,
                           const char *hostname,
@@ -541,12 +541,9 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
 	nm_assert (!priv->duid);
 	nm_assert (client_id);
 
-	if (enforce_duid == NM_DHCP_DUID_ENFORCE_NEVER)
-		priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self, TRUE);
-	else if (enforce_duid == NM_DHCP_DUID_ENFORCE_LEASE_FALLBACK)
-		priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self, FALSE);
+	if (!enforce_duid)
+		priv->duid = NM_DHCP_CLIENT_GET_CLASS (self)->get_duid (self);
 
-	/* NM_DHCP_DUID_ENFORCE_ALWAYS and fallback */
 	if (!priv->duid)
 		priv->duid = g_bytes_ref (client_id);
 
diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h
index 98c3ed26..f3d0b7d1 100644
--- a/src/dhcp/nm-dhcp-client.h
+++ b/src/dhcp/nm-dhcp-client.h
@@ -96,15 +96,13 @@ typedef struct {
 	/**
 	 * get_duid:
 	 * @self: the #NMDhcpClient
-	 * @global: if set to #true, the duid should be searched also in the
-	 *          DHCP client's system-wide persistent configuration.
 	 *
 	 * Attempts to find an existing DHCPv6 DUID for this client in the DHCP
 	 * client's persistent configuration.  Returned DUID should be the binary
 	 * representation of the DUID.  If no DUID is found, %NULL should be
 	 * returned.
 	 */
-	GBytes *(*get_duid) (NMDhcpClient *self, gboolean global);
+	GBytes *(*get_duid) (NMDhcpClient *self);
 
 	/* Signals */
 	void (*state_changed) (NMDhcpClient *self,
@@ -153,7 +151,7 @@ gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self,
 
 gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self,
                                    GBytes *client_id,
-                                   NMDhcpDuidEnforce enforce_duid,
+                                   gboolean enforce_duid,
                                    const char *dhcp_anycast_addr,
                                    const struct in6_addr *ll_addr,
                                    const char *hostname,
diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c
index 43746dd3..3bd14ebe 100644
--- a/src/dhcp/nm-dhcp-dhclient.c
+++ b/src/dhcp/nm-dhcp-dhclient.c
@@ -582,7 +582,7 @@ state_changed (NMDhcpClient *client,
 }
 
 static GBytes *
-get_duid (NMDhcpClient *client, gboolean global)
+get_duid (NMDhcpClient *client)
 {
 	NMDhcpDhclient *self = NM_DHCP_DHCLIENT (client);
 	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
@@ -607,7 +607,7 @@ get_duid (NMDhcpClient *client, gboolean global)
 		g_free (leasefile);
 	}
 
-	if (!duid && global) {
+	if (!duid) {
 		/* Otherwise read the default machine-wide DUID */
 		_LOGD ("looking for default DUID in '%s'", priv->def_leasefile);
 		duid = nm_dhcp_dhclient_read_duid (priv->def_leasefile, &error);
diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c
index 2d85c73a..67254323 100644
--- a/src/dhcp/nm-dhcp-manager.c
+++ b/src/dhcp/nm-dhcp-manager.c
@@ -164,7 +164,7 @@ client_start (NMDhcpManager *self,
               guint32 route_metric,
               const struct in6_addr *ipv6_ll_addr,
               GBytes *dhcp_client_id,
-              NMDhcpDuidEnforce enforce_duid,
+              gboolean enforce_duid,
               guint32 timeout,
               const char *dhcp_anycast_addr,
               const char *hostname,
@@ -299,7 +299,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
                            gboolean send_hostname,
                            const char *dhcp_hostname,
                            GBytes *duid,
-                           NMDhcpDuidEnforce enforce_duid,
+                           gboolean enforce_duid,
                            guint32 timeout,
                            const char *dhcp_anycast_addr,
                            gboolean info_only,
diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h
index ed8ee742..7eb32c37 100644
--- a/src/dhcp/nm-dhcp-manager.h
+++ b/src/dhcp/nm-dhcp-manager.h
@@ -73,7 +73,7 @@ NMDhcpClient * nm_dhcp_manager_start_ip6     (NMDhcpManager *manager,
                                               gboolean send_hostname,
                                               const char *dhcp_hostname,
                                               GBytes *duid,
-                                              NMDhcpDuidEnforce enforce_duid,
+                                              gboolean enforce_duid,
                                               guint32 timeout,
                                               const char *dhcp_anycast_addr,
                                               gboolean info_only,
diff --git a/src/dhcp/nm-dhcp-utils.h b/src/dhcp/nm-dhcp-utils.h
index afb87c1a..5c127bd1 100644
--- a/src/dhcp/nm-dhcp-utils.h
+++ b/src/dhcp/nm-dhcp-utils.h
@@ -24,12 +24,6 @@
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 
-typedef enum {
-	NM_DHCP_DUID_ENFORCE_NEVER = 0,
-	NM_DHCP_DUID_ENFORCE_LEASE_FALLBACK,
-	NM_DHCP_DUID_ENFORCE_ALWAYS,
-} NMDhcpDuidEnforce;
-
 NMIP4Config *nm_dhcp_utils_ip4_config_from_options (struct _NMDedupMultiIndex *multi_idx,
                                                     int ifindex,
                                                     const char *iface,
diff --git a/src/main.c b/src/main.c
index c1267b27..7da0c3c2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -227,11 +227,12 @@ main (int argc, char *argv[])
 	gboolean success = FALSE;
 	NMManager *manager = NULL;
 	NMConfig *config;
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
 	gboolean wrote_pidfile = FALSE;
 	char *bad_domains = NULL;
 	NMConfigCmdLineOptions *config_cli;
 	guint sd_id = 0;
+	GError *error_invalid_logging_config = NULL;
 
 	/* Known to cause a possible deadlock upon GDBus initialization:
 	 * https://bugzilla.gnome.org/show_bug.cgi?id=674885 */
@@ -304,11 +305,6 @@ main (int argc, char *argv[])
 		         _("%s.  Please use --help to see a list of valid options.\n"),
 		         error->message);
 		exit (1);
-	} else if (bad_domains) {
-		fprintf (stderr,
-		         _("Ignoring unrecognized log domain(s) '%s' passed on command line.\n"),
-		         bad_domains);
-		g_clear_pointer (&bad_domains, g_free);
 	}
 
 	/* Read the config file and CLI overrides */
@@ -330,15 +326,9 @@ main (int argc, char *argv[])
 		if (!nm_logging_setup (nm_config_get_log_level (config),
 		                       nm_config_get_log_domains (config),
 		                       &bad_domains,
-		                       &error)) {
-			fprintf (stderr, _("Error in configuration file: %s.\n"),
-			         error->message);
-			exit (1);
-		} else if (bad_domains) {
-			fprintf (stderr,
-			         _("Ignoring unrecognized log domain(s) '%s' from config files.\n"),
-			         bad_domains);
-			g_clear_pointer (&bad_domains, g_free);
+		                       &error_invalid_logging_config)) {
+			/* ignore error, and print the failure reason below.
+			 * Likewise, print about bad_domains below. */
 		}
 	}
 
@@ -374,6 +364,19 @@ main (int argc, char *argv[])
 	nm_log_info (LOGD_CORE, "Read config: %s", nm_config_data_get_config_description (nm_config_get_data (config)));
 	nm_config_data_log (nm_config_get_data (config), "CONFIG: ", "  ", NULL);
 
+	if (error_invalid_logging_config) {
+		nm_log_warn (LOGD_CORE, "config: invalid logging configuration: %s", error_invalid_logging_config->message);
+		g_clear_error (&error_invalid_logging_config);
+	}
+	if (bad_domains) {
+		nm_log_warn (LOGD_CORE, "config: invalid logging domains '%s' from %s",
+		             bad_domains,
+		             (global_opt.opt_log_level == NULL && global_opt.opt_log_domains == NULL)
+		               ? "config file"
+		               : "command line");
+		nm_clear_g_free (&bad_domains);
+	}
+
 	/* the first access to State causes the file to be read (and possibly print a warning) */
 	nm_config_state_get (config);
 
diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c
index ba61cb11..04f86312 100644
--- a/src/ndisc/nm-ndisc.c
+++ b/src/ndisc/nm-ndisc.c
@@ -231,8 +231,8 @@ _data_complete (NMNDiscDataInternal *data)
 	return &data->public;
 }
 
-static void
-_emit_config_change (NMNDisc *self, NMNDiscConfigMap changed)
+void
+nm_ndisc_emit_config_change (NMNDisc *self, NMNDiscConfigMap changed)
 {
 	_config_changed_log (self, changed);
 	g_signal_emit (self, signals[CONFIG_RECEIVED], 0,
@@ -743,7 +743,7 @@ nm_ndisc_set_iid (NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid)
 		if (rdata->addresses->len) {
 			_LOGD ("IPv6 interface identifier changed, flushing addresses");
 			g_array_remove_range (rdata->addresses, 0, rdata->addresses->len);
-			_emit_config_change (ndisc, NM_NDISC_CONFIG_ADDRESSES);
+			nm_ndisc_emit_config_change (ndisc, NM_NDISC_CONFIG_ADDRESSES);
 			solicit_routers (ndisc);
 		}
 		return TRUE;
@@ -796,8 +796,8 @@ nm_ndisc_start (NMNDisc *ndisc)
 	}
 }
 
-void
-nm_ndisc_dad_failed (NMNDisc *ndisc, const struct in6_addr *address)
+NMNDiscConfigMap
+nm_ndisc_dad_failed (NMNDisc *ndisc, const struct in6_addr *address, gboolean emit_changed_signal)
 {
 	NMNDiscDataInternal *rdata;
 	guint i;
@@ -819,8 +819,10 @@ nm_ndisc_dad_failed (NMNDisc *ndisc, const struct in6_addr *address)
 		i++;
 	}
 
-	if (changed)
-		_emit_config_change (ndisc, NM_NDISC_CONFIG_ADDRESSES);
+	if (emit_changed_signal && changed)
+		nm_ndisc_emit_config_change (ndisc, NM_NDISC_CONFIG_ADDRESSES);
+
+	return changed ? NM_NDISC_CONFIG_ADDRESSES : NM_NDISC_CONFIG_NONE;
 }
 
 #define CONFIG_MAP_MAX_STR 7
@@ -1131,7 +1133,7 @@ check_timestamps (NMNDisc *ndisc, gint32 now, NMNDiscConfigMap changed)
 	clean_dns_domains (ndisc, now, &changed, &nextevent);
 
 	if (changed)
-		_emit_config_change (ndisc, changed);
+		nm_ndisc_emit_config_change (ndisc, changed);
 
 	if (nextevent != G_MAXINT32) {
 		if (nextevent <= now)
diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h
index 9a8a27d7..fdc5615f 100644
--- a/src/ndisc/nm-ndisc.h
+++ b/src/ndisc/nm-ndisc.h
@@ -100,6 +100,7 @@ typedef struct {
 } NMNDiscDNSDomain;
 
 typedef enum {
+	NM_NDISC_CONFIG_NONE                                = 0,
 	NM_NDISC_CONFIG_DHCP_LEVEL                          = 1 << 0,
 	NM_NDISC_CONFIG_GATEWAYS                            = 1 << 1,
 	NM_NDISC_CONFIG_ADDRESSES                           = 1 << 2,
@@ -171,13 +172,17 @@ typedef struct {
 
 GType nm_ndisc_get_type (void);
 
+void nm_ndisc_emit_config_change (NMNDisc *self, NMNDiscConfigMap changed);
+
 int nm_ndisc_get_ifindex (NMNDisc *self);
 const char *nm_ndisc_get_ifname (NMNDisc *self);
 NMNDiscNodeType nm_ndisc_get_node_type (NMNDisc *self);
 
 gboolean nm_ndisc_set_iid (NMNDisc *ndisc, const NMUtilsIPv6IfaceId iid);
 void nm_ndisc_start (NMNDisc *ndisc);
-void nm_ndisc_dad_failed (NMNDisc *ndisc, const struct in6_addr *address);
+NMNDiscConfigMap nm_ndisc_dad_failed (NMNDisc *ndisc,
+                                      const struct in6_addr *address,
+                                      gboolean emit_changed_signal);
 void nm_ndisc_set_config (NMNDisc *ndisc,
                           const GArray *addresses,
                           const GArray *dns_servers,
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index 58d766c1..308c9e1f 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -339,7 +339,8 @@ dad_failed_handle_idle (gpointer user_data)
 
 		if (nm_ndisc_dad_addr_is_fail_candidate (data->platform, obj)) {
 			nm_ndisc_dad_failed (data->ndisc,
-			                     &NMP_OBJECT_CAST_IP6_ADDRESS (obj)->address);
+			                     &NMP_OBJECT_CAST_IP6_ADDRESS (obj)->address,
+			                     TRUE);
 		}
 	}
 
diff --git a/src/nm-logging.c b/src/nm-logging.c
index 11e05c31..c5553791 100644
--- a/src/nm-logging.c
+++ b/src/nm-logging.c
@@ -98,6 +98,12 @@ static struct Global {
 	const char *prefix;
 	const char *syslog_identifier;
 	enum {
+		/* before we setup syslog (during start), the backend defaults to GLIB, meaning:
+		 * we use g_log() for all logging. At that point, the application is not yet supposed
+		 * to do any logging and doing so indicates a bug.
+		 *
+		 * Afterwards, the backend is either SYSLOG or JOURNAL. From that point, also
+		 * g_log() is redirected to this backend via a logging handler. */
 		LOG_BACKEND_GLIB,
 		LOG_BACKEND_SYSLOG,
 		LOG_BACKEND_JOURNAL,
@@ -832,21 +838,36 @@ nm_logging_set_prefix (const char *format, ...)
 void
 nm_logging_syslog_openlog (const char *logging_backend, gboolean debug)
 {
+	gboolean fetch_monotonic_timestamp = FALSE;
+	gboolean obsolete_debug_backend = FALSE;
+
+	nm_assert (NM_IN_STRSET (""NM_CONFIG_DEFAULT_LOGGING_BACKEND,
+	                         NM_LOG_CONFIG_BACKEND_JOURNAL,
+	                         NM_LOG_CONFIG_BACKEND_SYSLOG));
+
 	if (global.log_backend != LOG_BACKEND_GLIB)
 		g_return_if_reached ();
 
 	if (!logging_backend)
 		logging_backend = ""NM_CONFIG_DEFAULT_LOGGING_BACKEND;
 
+	if (nm_streq (logging_backend, NM_LOG_CONFIG_BACKEND_DEBUG)) {
+		/* "debug" was wrongly documented as a valid logging backend. It makes no sense however,
+		 * because printing to stderr only makes sense when not demonizing. Whether to daemonize
+		 * is only controlled via command line arguments (--no-daemon, --debug) and not via the
+		 * logging backend from configuration.
+		 *
+		 * Fall back to the default. */
+		logging_backend = ""NM_CONFIG_DEFAULT_LOGGING_BACKEND;
+		obsolete_debug_backend = TRUE;
+	}
+
 #if SYSTEMD_JOURNAL
-	if (strcmp (logging_backend, "syslog") != 0) {
+	if (!nm_streq (logging_backend, NM_LOG_CONFIG_BACKEND_SYSLOG)) {
 		global.log_backend = LOG_BACKEND_JOURNAL;
 		global.uses_syslog = TRUE;
 		global.debug_stderr = debug;
-
-		/* ensure we read a monotonic timestamp. Reading the timestamp the first
-		 * time causes a logging message. We don't want to do that during _nm_log_impl. */
-		nm_utils_get_monotonic_timestamp_ns ();
+		fetch_monotonic_timestamp = TRUE;
 	} else
 #endif
 	{
@@ -860,5 +881,30 @@ nm_logging_syslog_openlog (const char *logging_backend, gboolean debug)
 	                   G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
 	                   nm_log_handler,
 	                   NULL);
-}
 
+	if (fetch_monotonic_timestamp) {
+		/* ensure we read a monotonic timestamp. Reading the timestamp the first
+		 * time causes a logging message. We don't want to do that during _nm_log_impl. */
+		nm_utils_get_monotonic_timestamp_ns ();
+	}
+
+	if (obsolete_debug_backend)
+		nm_log_dbg (LOGD_CORE, "config: ignore deprecated logging backend 'debug', fallback to '%s'", logging_backend);
+
+	if (nm_streq (logging_backend, NM_LOG_CONFIG_BACKEND_SYSLOG)) {
+		/* good */
+	} else if (nm_streq (logging_backend, NM_LOG_CONFIG_BACKEND_JOURNAL)) {
+#if !SYSTEMD_JOURNAL
+		nm_log_warn (LOGD_CORE, "config: logging backend 'journal' is not available, fallback to 'syslog'");
+#endif
+	} else {
+		nm_log_warn (LOGD_CORE, "config: invalid logging backend '%s', fallback to '%s'",
+		             logging_backend,
+#if SYSTEMD_JOURNAL
+		             NM_LOG_CONFIG_BACKEND_JOURNAL
+#else
+		             NM_LOG_CONFIG_BACKEND_SYSLOG
+#endif
+		             );
+	}
+}
diff --git a/src/nm-logging.h b/src/nm-logging.h
index 069d1d0d..70c14a5e 100644
--- a/src/nm-logging.h
+++ b/src/nm-logging.h
@@ -28,6 +28,10 @@
 #error nm-test-utils.h must be included as last header
 #endif
 
+#define NM_LOG_CONFIG_BACKEND_DEBUG   "debug"
+#define NM_LOG_CONFIG_BACKEND_SYSLOG  "syslog"
+#define NM_LOG_CONFIG_BACKEND_JOURNAL "journal"
+
 /* Log domains */
 typedef enum  { /*< skip >*/
 	LOGD_NONE       = 0LL,
diff --git a/src/nm-manager.c b/src/nm-manager.c
index c6d82c9d..0fea13de 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -1210,9 +1210,11 @@ nm_manager_get_device_by_ifindex (NMManager *self, int ifindex)
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDevice *device;
 
-	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
-		if (nm_device_get_ifindex (device) == ifindex)
-			return device;
+	if (ifindex > 0) {
+		c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
+			if (nm_device_get_ifindex (device) == ifindex)
+				return device;
+		}
 	}
 
 	return NULL;
@@ -7440,7 +7442,7 @@ static const NMDBusInterfaceInfoExtended interface_info_manager = {
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("WimaxHardwareEnabled",       "b",     NM_MANAGER_WIMAX_HARDWARE_ENABLED),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("ActiveConnections",          "ao",    NM_MANAGER_ACTIVE_CONNECTIONS),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("PrimaryConnection",          "o",     NM_MANAGER_PRIMARY_CONNECTION),
-			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("PrimartConnectionType",      "s",     NM_MANAGER_PRIMARY_CONNECTION_TYPE),
+			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("PrimaryConnectionType",      "s",     NM_MANAGER_PRIMARY_CONNECTION_TYPE),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Metered",                    "u",     NM_MANAGER_METERED),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("ActivatingConnection",       "o",     NM_MANAGER_ACTIVATING_CONNECTION),
 			NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L     ("Startup",                    "b",     NM_MANAGER_STARTUP),
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index 06dd7e13..f8f4627e 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.c
@@ -482,7 +482,7 @@ link_changed (NMPlatform *platform,
 
 	if (!IN6_IS_ADDR_UNSPECIFIED (&device->ip6_lladdr)) {
 		if (device->obj->link.connected)
-			ip6_address_add (platform, device->obj->link.ifindex, in6addr_any, 64, device->ip6_lladdr, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0);
+			ip6_address_add (platform, device->obj->link.ifindex, device->ip6_lladdr, 64, in6addr_any, NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0);
 		else
 			ip6_address_delete (platform, device->obj->link.ifindex, device->ip6_lladdr, 64);
 	}
diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c
index a213b31f..8b8c87d8 100644
--- a/src/platform/tests/test-cleanup.c
+++ b/src/platform/tests/test-cleanup.c
@@ -59,6 +59,22 @@ test_cleanup_internal (void)
 	ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME);
 	g_assert (ifindex > 0);
 
+	/* wait for kernel to add the IPv6 link local address... it takes a bit. */
+	NMTST_WAIT_ASSERT (100, {
+		gs_unref_array GArray *addrs = NULL;
+		const NMPlatformIP6Address *a;
+
+		if (nmtst_wait_iteration > 0) {
+			nmtstp_wait_for_signal (NM_PLATFORM_GET, nmtst_wait_remaining_us / 1000);
+			nm_platform_process_events (NM_PLATFORM_GET);
+		}
+		addrs = nmtstp_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex);
+		if (   addrs->len == 1
+		    && (a = &g_array_index (addrs, NMPlatformIP6Address, 0))
+		    && IN6_IS_ADDR_LINKLOCAL (&a->address))
+			break;
+	});
+
 	/* Add routes and addresses */
 	g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, plen4, addr4, lifetime, preferred, 0, NULL));
 	g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr6, plen6, in6addr_any, lifetime, preferred, flags));
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 50cc1427..c09f6804 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -1033,7 +1033,7 @@ get_secrets_done_cb (NMAgentManager *manager,
 	NMSettingsConnectionPrivate *priv;
 	NMConnection *applied_connection;
 	gs_free_error GError *local = NULL;
-	GVariant *dict;
+	GVariant *dict = NULL;
 	gboolean agent_had_system = FALSE;
 	ForEachSecretFlags cmp_flags = { NM_SETTING_SECRET_FLAG_NONE, NM_SETTING_SECRET_FLAG_NONE };
 
@@ -1096,7 +1096,8 @@ get_secrets_done_cb (NMAgentManager *manager,
 	       setting_name,
 	       call_id);
 
-	dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+	if (priv->system_secrets)
+		dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
 
 	/* Update the connection with our existing secrets from backing storage */
 	nm_connection_clear_secrets (NM_CONNECTION (self));
@@ -1240,7 +1241,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
                                     gpointer callback_data)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-	GVariant *existing_secrets;
+	GVariant *existing_secrets = NULL;
 	NMAgentManagerCallId call_id_a;
 	gs_free char *joined_hints = NULL;
 	NMSettingsConnectionCallId *call_id;
@@ -1262,15 +1263,6 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	call_id->callback_data = callback_data;
 	c_list_link_tail (&priv->call_ids_lst_head, &call_id->call_ids_lst);
 
-	/* Use priv->secrets to work around the fact that nm_connection_clear_secrets()
-	 * will clear secrets on this object's settings.
-	 */
-	if (!priv->system_secrets) {
-		g_set_error_literal (&local, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-		                     "secrets cache invalid");
-		goto schedule_dummy;
-	}
-
 	/* Make sure the request actually requests something we can return */
 	if (!nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name)) {
 		g_set_error (&local, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
@@ -1286,7 +1278,11 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 		goto schedule_dummy;
 	}
 
-	existing_secrets = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+	/* Use priv->system_secrets to work around the fact that nm_connection_clear_secrets()
+	 * will clear secrets on this object's settings.
+	 */
+	if (priv->system_secrets)
+		existing_secrets = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
 	if (existing_secrets)
 		g_variant_ref_sink (existing_secrets);