about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorSebastien Bacher <seb128@ubuntu.com>2020-02-26 16:32:44 +0100
committerSebastien Bacher <seb128@ubuntu.com>2020-02-26 16:32:44 +0100
commit447eb1ee6c50e2cac4009e66add3c1d449822589 (patch)
tree7bc7db8f5d7e12137e758ea03ebe5c1eb8b07a23 /src
parent0b818c504ebbb9454b501c532be8603bf383e37a (diff)
parent3cbb3d6ecfef1076fc757632ee8327935250afee (diff)
Merge remote-tracking branch 'salsa/debian/master' into ubuntu/master
Diffstat (limited to 'src')
-rw-r--r--src/devices/nm-device-factory.c8
-rw-r--r--src/devices/nm-device.c230
-rw-r--r--src/devices/nm-device.h7
-rw-r--r--src/devices/ovs/nm-device-ovs-interface.c168
-rw-r--r--src/devices/team/nm-device-team.c19
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c4
-rw-r--r--src/devices/wwan/nm-device-modem.c4
-rw-r--r--src/dhcp/nm-dhcp-client.c34
-rw-r--r--src/dhcp/nm-dhcp-client.h7
-rw-r--r--src/dhcp/nm-dhcp-nettools.c24
-rw-r--r--src/dhcp/nm-dhcp-options.c2
-rw-r--r--src/dhcp/nm-dhcp-systemd.c8
-rw-r--r--src/initrd/nmi-cmdline-reader.c85
-rw-r--r--src/initrd/nmi-ibft-reader.c1
-rw-r--r--src/initrd/tests/test-cmdline-reader.c46
-rw-r--r--src/initrd/tests/test-ibft-reader.c3
-rw-r--r--src/ndisc/nm-lndp-ndisc.c2
-rw-r--r--src/ndisc/nm-lndp-ndisc.h1
-rw-r--r--src/ndisc/nm-ndisc.c77
-rw-r--r--src/ndisc/nm-ndisc.h8
-rw-r--r--src/ndisc/tests/test-ndisc-linux.c1
-rw-r--r--src/nm-config.c2
-rw-r--r--src/nm-connectivity.c1
-rw-r--r--src/nm-core-utils.c8
-rw-r--r--src/nm-iface-helper.c11
-rw-r--r--src/nm-manager.c40
-rw-r--r--src/platform/nm-platform-utils.c18
-rw-r--r--src/platform/tests/test-common.c22
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c12
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c56
-rw-r--r--src/supplicant/nm-supplicant-interface.c2
31 files changed, 660 insertions, 251 deletions
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index 232e69ef..1c1d5eaa 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -143,14 +143,6 @@ nm_device_factory_get_connection_iface (NMDeviceFactory *factory,
 		return NULL;
 	}
 
-	if (!nm_utils_is_valid_iface_name (ifname, error)) {
-		g_prefix_error (error,
-		                "failed to determine interface name: name \"%s\" is invalid",
-		                ifname);
-		g_free (ifname);
-		return NULL;
-	}
-
 	return ifname;
 }
 
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index e7a4a059..3bbc9757 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -76,7 +76,20 @@ _LOG_DECLARE_SELF (NMDevice);
 /*****************************************************************************/
 
 #define DEFAULT_AUTOCONNECT    TRUE
-#define DHCP_GRACE_PERIOD_SEC  480
+
+static guint32
+dhcp_grace_period_from_timeout (guint32 timeout)
+{
+#define DHCP_GRACE_PERIOD_MULTIPLIER 2U
+
+	nm_assert (timeout > 0);
+	nm_assert (timeout < G_MAXINT32);
+
+	if (timeout < G_MAXUINT32 / DHCP_GRACE_PERIOD_MULTIPLIER)
+		return timeout * DHCP_GRACE_PERIOD_MULTIPLIER;
+
+	return G_MAXUINT32;
+}
 
 #define CARRIER_WAIT_TIME_MS 6000
 #define CARRIER_WAIT_TIME_AFTER_MTU_MS 10000
@@ -463,12 +476,13 @@ typedef struct _NMDevicePrivate {
 	/* DHCPv4 tracking */
 	struct {
 		NMDhcpClient *  client;
-		gulong          state_sigid;
 		NMDhcp4Config * config;
 		char *          pac_url;
 		char *          root_path;
-		bool            was_active;
+		gulong          state_sigid;
 		guint           grace_id;
+		bool            was_active:1;
+		bool            grace_pending:1;
 	} dhcp4;
 
 	struct {
@@ -533,17 +547,18 @@ typedef struct _NMDevicePrivate {
 
 	struct {
 		NMDhcpClient *   client;
-		NMNDiscDHCPLevel mode;
-		gulong           state_sigid;
-		gulong           prefix_sigid;
 		NMDhcp6Config *  config;
 		/* IP6 config from DHCP */
 		AppliedConfig    ip6_config;
 		/* Event ID of the current IP6 config from DHCP */
 		char *           event_id;
+		gulong           state_sigid;
+		gulong           prefix_sigid;
+		NMNDiscDHCPLevel mode;
 		guint            needed_prefixes;
-		bool             was_active;
 		guint            grace_id;
+		bool             was_active:1;
+		bool             grace_pending:1;
 	} dhcp6;
 
 	gboolean needs_ip6_subnet;
@@ -4240,6 +4255,12 @@ nm_device_create_and_realize (NMDevice *self,
 	return TRUE;
 }
 
+static gboolean
+can_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink)
+{
+	return TRUE;
+}
+
 void
 nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink)
 {
@@ -4248,6 +4269,9 @@ nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink
 	int ifindex;
 	guint32 mtu;
 
+	if (!NM_DEVICE_GET_CLASS (self)->can_update_from_platform_link (self, plink))
+		return;
+
 	g_return_if_fail (plink == NULL || link_type_compatible (self, plink->type, NULL, NULL));
 
 	str = plink ? nm_platform_link_get_udi (nm_device_get_platform (self), plink->ifindex) : NULL;
@@ -7474,6 +7498,51 @@ ensure_con_ip_config (NMDevice *self, int addr_family)
 /*****************************************************************************/
 /* DHCPv4 stuff */
 
+static guint32
+get_dhcp_timeout (NMDevice *self, int addr_family)
+{
+	NMDeviceClass *klass;
+	NMConnection *connection;
+	int timeout_i;
+	guint32 timeout;
+
+	nm_assert (NM_IS_DEVICE (self));
+	nm_assert_addr_family (addr_family);
+
+	connection = nm_device_get_applied_connection (self);
+
+	timeout_i  = nm_setting_ip_config_get_dhcp_timeout (nm_connection_get_setting_ip_config (connection, addr_family));
+	nm_assert (timeout_i >= 0 && timeout_i <= G_MAXINT32);
+
+	timeout = (guint32) timeout_i;
+	if (timeout)
+		goto out;
+
+	timeout = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                       addr_family == AF_INET
+	                                                         ? NM_CON_DEFAULT ("ipv4.dhcp-timeout")
+	                                                         : NM_CON_DEFAULT ("ipv6.dhcp-timeout"),
+	                                                       self,
+	                                                       0, G_MAXINT32, 0);
+	if (timeout)
+		goto out;
+
+	klass = NM_DEVICE_GET_CLASS (self);
+	if (klass->get_dhcp_timeout_for_device) {
+		timeout = klass->get_dhcp_timeout_for_device (self, addr_family);
+		if (timeout)
+			goto out;
+	}
+
+	timeout = NM_DHCP_TIMEOUT_DEFAULT;
+
+out:
+	G_STATIC_ASSERT_EXPR (G_MAXINT32 == NM_DHCP_TIMEOUT_INFINITY);
+	nm_assert (timeout > 0);
+	nm_assert (timeout <= G_MAXINT32);
+	return timeout;
+}
+
 static void
 dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 {
@@ -7481,6 +7550,7 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 
 	priv->dhcp4.was_active = FALSE;
 	nm_clear_g_source (&priv->dhcp4.grace_id);
+	priv->dhcp4.grace_pending = FALSE;
 	g_clear_pointer (&priv->dhcp4.pac_url, g_free);
 	g_clear_pointer (&priv->dhcp4.root_path, g_free);
 
@@ -7723,9 +7793,10 @@ ip_config_merge_and_apply (NMDevice *self,
 }
 
 static gboolean
-dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
+dhcp4_lease_change (NMDevice *self, NMIP4Config *config, gboolean bound)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gs_free_error GError *error = NULL;
 
 	g_return_val_if_fail (config, FALSE);
 
@@ -7736,6 +7807,15 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
 		return FALSE;
 	}
 
+	/* TODO: we should perform DAD again whenever we obtain a
+	 * new lease after an expiry. But what should we do if
+	 * a duplicate address is detected? Fail the connection;
+	 * restart DHCP; continue without an address? */
+	if (bound && !nm_dhcp_client_accept (priv->dhcp4.client, &error)) {
+		_LOGW (LOGD_DHCP4, "error accepting lease: %s", error->message);
+		return FALSE;
+	}
+
 	nm_dispatcher_call_device (NM_DISPATCHER_ACTION_DHCP4_CHANGE,
 	                           self,
 	                           NULL,
@@ -7751,6 +7831,7 @@ dhcp4_grace_period_expired (gpointer user_data)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	priv->dhcp4.grace_id = 0;
+	priv->dhcp4.grace_pending = FALSE;
 	_LOGI (LOGD_DHCP4, "DHCPv4: grace period expired");
 
 	nm_device_ip_method_failed (self, AF_INET,
@@ -7798,13 +7879,26 @@ dhcp4_fail (NMDevice *self, NMDhcpState dhcp_state)
 	/* In any other case (expired lease, assumed connection, etc.),
 	 * wait for some time before failing the IP method.
 	 */
-	if (!priv->dhcp4.grace_id) {
-		priv->dhcp4.grace_id = g_timeout_add_seconds (DHCP_GRACE_PERIOD_SEC,
-		                                              dhcp4_grace_period_expired,
-		                                              self);
-		_LOGI (LOGD_DHCP4,
-		       "DHCPv4: %u seconds grace period started",
-		       DHCP_GRACE_PERIOD_SEC);
+	if (!priv->dhcp4.grace_pending) {
+		guint32 timeout;
+
+		/* Start a grace period equal to the DHCP timeout multiplied
+		 * by a constant factor. */
+		timeout = get_dhcp_timeout (self, AF_INET);
+		if (timeout == NM_DHCP_TIMEOUT_INFINITY) {
+			_LOGI (LOGD_DHCP4, "DHCPv4: trying to acquire a new lease");
+		} else {
+			timeout = dhcp_grace_period_from_timeout (timeout);
+			_LOGI (LOGD_DHCP4,
+			       "DHCPv4: trying to acquire a new lease within %u seconds",
+			       timeout);
+			nm_assert (!priv->dhcp4.grace_id);
+			priv->dhcp4.grace_id = g_timeout_add_seconds (timeout,
+			                                              dhcp4_grace_period_expired,
+			                                              self);
+		}
+
+		priv->dhcp4.grace_pending = TRUE;
 		goto clear_config;
 	}
 	return;
@@ -7853,6 +7947,7 @@ dhcp4_state_changed (NMDhcpClient *client,
 
 	switch (state) {
 	case NM_DHCP_STATE_BOUND:
+	case NM_DHCP_STATE_EXTENDED:
 		if (!ip4_config) {
 			_LOGW (LOGD_DHCP4, "failed to get IPv4 config in response to DHCP event.");
 			dhcp4_fail (self, state);
@@ -7860,6 +7955,7 @@ dhcp4_state_changed (NMDhcpClient *client,
 		}
 
 		nm_clear_g_source (&priv->dhcp4.grace_id);
+		priv->dhcp4.grace_pending = FALSE;
 
 		/* After some failures, we have been able to renew the lease:
 		 * update the ip state
@@ -7895,7 +7991,8 @@ dhcp4_state_changed (NMDhcpClient *client,
 
 			ipv4_dad_start (self, configs, dhcp4_dad_cb);
 		} else if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) {
-			if (dhcp4_lease_change (self, ip4_config))
+			if (dhcp4_lease_change (self, ip4_config,
+			                        state == NM_DHCP_STATE_BOUND))
 				nm_device_update_metered (self);
 			else
 				dhcp4_fail (self, state);
@@ -7919,41 +8016,6 @@ dhcp4_state_changed (NMDhcpClient *client,
 	}
 }
 
-static int
-get_dhcp_timeout (NMDevice *self, int addr_family)
-{
-	NMDeviceClass *klass;
-	NMConnection *connection;
-	NMSettingIPConfig *s_ip;
-	guint32 timeout;
-
-	nm_assert (NM_IS_DEVICE (self));
-	nm_assert_addr_family (addr_family);
-
-	connection = nm_device_get_applied_connection (self);
-
-	s_ip = nm_connection_get_setting_ip_config (connection, addr_family);
-
-	timeout = nm_setting_ip_config_get_dhcp_timeout (s_ip);
-	if (timeout)
-		return timeout;
-
-	timeout = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
-	                                                       addr_family == AF_INET
-	                                                         ? NM_CON_DEFAULT ("ipv4.dhcp-timeout")
-	                                                         : NM_CON_DEFAULT ("ipv6.dhcp-timeout"),
-	                                                       self,
-	                                                       0, G_MAXINT32, 0);
-	if (timeout)
-		return timeout;
-
-	klass = NM_DEVICE_GET_CLASS (self);
-	if (klass->get_dhcp_timeout)
-		timeout = klass->get_dhcp_timeout (self, addr_family);
-
-	return timeout ?: NM_DHCP_TIMEOUT_DEFAULT;
-}
-
 /**
  * dhcp_get_iaid:
  * @self: the #NMDevice
@@ -8520,6 +8582,7 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 	applied_config_clear (&priv->dhcp6.ip6_config);
 	g_clear_pointer (&priv->dhcp6.event_id, g_free);
 	nm_clear_g_source (&priv->dhcp6.grace_id);
+	priv->dhcp6.grace_pending = FALSE;
 
 	if (priv->dhcp6.client) {
 		nm_clear_g_signal_handler (priv->dhcp6.client, &priv->dhcp6.state_sigid);
@@ -8575,6 +8638,7 @@ dhcp6_grace_period_expired (gpointer user_data)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	priv->dhcp6.grace_id = 0;
+	priv->dhcp6.grace_pending = FALSE;
 	_LOGI (LOGD_DHCP6, "DHCPv6: grace period expired");
 
 	nm_device_ip_method_failed (self, AF_INET6,
@@ -8626,13 +8690,26 @@ dhcp6_fail (NMDevice *self, NMDhcpState dhcp_state)
 		/* In any other case (expired lease, assumed connection, etc.),
 		 * wait for some time before failing the IP method.
 		 */
-		if (!priv->dhcp6.grace_id) {
-			priv->dhcp6.grace_id = g_timeout_add_seconds (DHCP_GRACE_PERIOD_SEC,
-			                                              dhcp6_grace_period_expired,
-			                                              self);
-			_LOGI (LOGD_DHCP6,
-			       "DHCPv6: %u seconds grace period started",
-			       DHCP_GRACE_PERIOD_SEC);
+		if (!priv->dhcp6.grace_pending) {
+			guint32 timeout;
+
+			/* Start a grace period equal to the DHCP timeout multiplied
+			 * by a constant factor. */
+			timeout = get_dhcp_timeout (self, AF_INET6);
+			if (timeout == NM_DHCP_TIMEOUT_INFINITY)
+				_LOGI (LOGD_DHCP6, "DHCPv6: trying to acquire a new lease");
+			else {
+				timeout = dhcp_grace_period_from_timeout (timeout);
+				_LOGI (LOGD_DHCP6,
+				       "DHCPv6: trying to acquire a new lease within %u seconds",
+				       timeout);
+				nm_assert (!priv->dhcp6.grace_id);
+				priv->dhcp6.grace_id = g_timeout_add_seconds (timeout,
+				                                              dhcp6_grace_period_expired,
+				                                              self);
+			}
+
+			priv->dhcp6.grace_pending = TRUE;
 			goto clear_config;
 		}
 	} else {
@@ -8670,7 +8747,9 @@ dhcp6_state_changed (NMDhcpClient *client,
 
 	switch (state) {
 	case NM_DHCP_STATE_BOUND:
+	case NM_DHCP_STATE_EXTENDED:
 		nm_clear_g_source (&priv->dhcp6.grace_id);
+		priv->dhcp6.grace_pending = FALSE;
 		/* If the server sends multiple IPv6 addresses, we receive a state
 		 * changed event for each of them. Use the event ID to merge IPv6
 		 * addresses from the same transaction into a single configuration.
@@ -10016,7 +10095,7 @@ addrconf6_start_with_link_ready (NMDevice *self)
 	                                           G_CALLBACK (ndisc_config_changed),
 	                                           self);
 	priv->ndisc_timeout_id = g_signal_connect (priv->ndisc,
-	                                           NM_NDISC_RA_TIMEOUT,
+	                                           NM_NDISC_RA_TIMEOUT_SIGNAL,
 	                                           G_CALLBACK (ndisc_ra_timeout),
 	                                           self);
 
@@ -10035,6 +10114,28 @@ ndisc_node_type (NMDevice *self)
 	return NM_NDISC_NODE_TYPE_HOST;
 }
 
+static gint32
+get_ra_timeout (NMDevice *self)
+{
+	NMConnection *connection;
+	gint32 timeout;
+
+	G_STATIC_ASSERT_EXPR (NM_RA_TIMEOUT_DEFAULT == 0);
+	G_STATIC_ASSERT_EXPR (NM_RA_TIMEOUT_INFINITY == G_MAXINT32);
+
+	connection = nm_device_get_applied_connection (self);
+
+	timeout = nm_setting_ip6_config_get_ra_timeout (NM_SETTING_IP6_CONFIG (nm_connection_get_setting_ip6_config (connection)));
+	nm_assert (timeout >= 0);
+	if (timeout)
+		return timeout;
+
+	return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
+	                                                    NM_CON_DEFAULT ("ipv6.ra-timeout"),
+	                                                    self,
+	                                                    0, G_MAXINT32, 0);
+}
+
 static gboolean
 addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 {
@@ -10065,6 +10166,7 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 	                                 stable_id,
 	                                 nm_setting_ip6_config_get_addr_gen_mode (s_ip6),
 	                                 ndisc_node_type (self),
+	                                 get_ra_timeout (self),
 	                                 &error);
 	if (!priv->ndisc) {
 		_LOGE (LOGD_IP6, "addrconf6: failed to start neighbor discovery: %s", error->message);
@@ -15515,6 +15617,12 @@ _set_state_full (NMDevice *self,
 	       reason_to_string_a (reason),
 	       _sys_iface_state_to_str (priv->sys_iface_state));
 
+	/* in order to prevent triggering any callback caused
+	 * by the device not having any pending action anymore
+	 * we add one here that gets removed at the end of the function */
+	nm_device_add_pending_action (self,
+	                              NM_PENDING_ACTION_IN_STATE_CHANGE,
+	                              TRUE);
 	priv->in_state_changed = TRUE;
 
 	priv->state = state;
@@ -15821,6 +15929,9 @@ _set_state_full (NMDevice *self,
 		g_object_unref (req);
 
 	priv->in_state_changed = FALSE;
+	nm_device_remove_pending_action (self,
+	                                 NM_PENDING_ACTION_IN_STATE_CHANGE,
+	                                 TRUE);
 
 	if ((old_state > NM_DEVICE_STATE_UNMANAGED) != (state > NM_DEVICE_STATE_UNMANAGED))
 		_notify (self, PROP_MANAGED);
@@ -17507,6 +17618,7 @@ nm_device_class_init (NMDeviceClass *klass)
 
 	klass->get_type_description = get_type_description;
 	klass->can_auto_connect = can_auto_connect;
+	klass->can_update_from_platform_link = can_update_from_platform_link;
 	klass->check_connection_compatible = check_connection_compatible;
 	klass->check_connection_available = check_connection_available;
 	klass->can_unmanaged_external_down = can_unmanaged_external_down;
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 518c66ca..8c6c856e 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -53,6 +53,7 @@ nm_device_state_reason_check (NMDeviceStateReason reason)
 }
 
 #define NM_PENDING_ACTION_AUTOACTIVATE              "autoactivate"
+#define NM_PENDING_ACTION_IN_STATE_CHANGE           "in-state-change"
 #define NM_PENDING_ACTION_RECHECK_AVAILABLE         "recheck-available"
 #define NM_PENDING_ACTION_CARRIER_WAIT              "carrier-wait"
 #define NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT    "waiting-for-supplicant"
@@ -436,11 +437,13 @@ typedef struct _NMDeviceClass {
 	                                        NMConnection *con_old,
 	                                        NMConnection *con_new);
 
-	guint32         (* get_dhcp_timeout) (NMDevice *self,
-	                                      int addr_family);
+	guint32         (* get_dhcp_timeout_for_device) (NMDevice *self,
+	                                                 int addr_family);
 
 	gboolean        (* get_guessed_metered) (NMDevice *self);
 
+	gboolean        (* can_update_from_platform_link) (NMDevice *self, const NMPlatformLink *plink);
+
 	/* Controls, whether to call act_stage2_config() callback also for assuming
 	 * a device or for external activations. In this case, act_stage2_config() must
 	 * take care not to touch the device's configuration. */
diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c
index 726e9901..2868dee0 100644
--- a/src/devices/ovs/nm-device-ovs-interface.c
+++ b/src/devices/ovs/nm-device-ovs-interface.c
@@ -98,10 +98,12 @@ link_changed (NMDevice *device,
 {
 	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (device);
 
-	if (   pllink
-	    && priv->waiting_for_interface
-	    && nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
-		priv->waiting_for_interface = FALSE;
+	if (!pllink || !priv->waiting_for_interface)
+		return;
+
+	priv->waiting_for_interface = FALSE;
+
+	if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
 		nm_device_bring_up (device, TRUE, NULL);
 		nm_device_activate_schedule_stage3_ip_config_start (device);
 	}
@@ -125,12 +127,14 @@ act_stage3_ip_config_start (NMDevice *device,
                             gpointer *out_config,
                             NMDeviceStateReason *out_failure_reason)
 {
+	NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE (device);
 	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (device);
 
 	if (!_is_internal_interface (device))
 		return NM_ACT_STAGE_RETURN_IP_FAIL;
 
 	if (nm_device_get_ip_ifindex (device) <= 0) {
+		_LOGT (LOGD_DEVICE, "waiting for link to appear");
 		priv->waiting_for_interface = TRUE;
 		return NM_ACT_STAGE_RETURN_POSTPONE;
 	}
@@ -153,6 +157,160 @@ deactivate (NMDevice *device)
 	priv->waiting_for_interface = FALSE;
 }
 
+typedef struct {
+	NMDeviceOvsInterface *self;
+	GCancellable *cancellable;
+	NMDeviceDeactivateCallback callback;
+	gpointer callback_user_data;
+	gulong link_changed_id;
+	gulong cancelled_id;
+	guint link_timeout_id;
+} DeactivateData;
+
+static void
+deactivate_invoke_cb (DeactivateData *data, GError *error)
+{
+	NMDeviceOvsInterface *self = data->self;
+
+	_LOGT (LOGD_CORE,
+	       "deactivate: async callback (%s)",
+	       error ? error->message : "success");
+	data->callback (NM_DEVICE (data->self),
+	                error,
+	                data->callback_user_data);
+
+	nm_clear_g_signal_handler (nm_device_get_platform (NM_DEVICE (data->self)),
+	                           &data->link_changed_id);
+	nm_clear_g_signal_handler (data->cancellable,
+	                           &data->cancelled_id);
+	nm_clear_g_source (&data->link_timeout_id);
+	g_object_unref (data->self);
+	g_object_unref (data->cancellable);
+	nm_g_slice_free (data);
+}
+
+static void
+deactivate_link_changed_cb (NMPlatform *platform,
+                            int obj_type_i,
+                            int ifindex,
+                            NMPlatformLink *info,
+                            int change_type_i,
+                            DeactivateData *data)
+{
+	NMDeviceOvsInterface *self = data->self;
+	const NMPlatformSignalChangeType change_type = change_type_i;
+
+	if (   change_type == NM_PLATFORM_SIGNAL_REMOVED
+	    && nm_streq0 (info->name, nm_device_get_iface (NM_DEVICE (self)))) {
+		_LOGT (LOGD_DEVICE, "deactivate: link removed, proceeding");
+		nm_device_update_from_platform_link (NM_DEVICE (self), NULL);
+		deactivate_invoke_cb (data, NULL);
+		return;
+	}
+}
+
+static gboolean
+deactivate_link_timeout (gpointer user_data)
+{
+	DeactivateData *data = user_data;
+	NMDeviceOvsInterface *self = data->self;
+
+	_LOGT (LOGD_DEVICE, "deactivate: timeout waiting link removal");
+	deactivate_invoke_cb (data, NULL);
+	return G_SOURCE_REMOVE;
+}
+
+static void
+deactivate_cancelled_cb (GCancellable *cancellable,
+                         gpointer user_data)
+{
+	gs_free_error GError *error = NULL;
+
+	nm_utils_error_set_cancelled (&error, FALSE, NULL);
+	deactivate_invoke_cb ((DeactivateData *) user_data, error);
+}
+
+static void
+deactivate_cb_on_idle (gpointer user_data,
+                       GCancellable *cancellable)
+{
+	DeactivateData *data = user_data;
+	gs_free_error GError *cancelled_error = NULL;
+
+	g_cancellable_set_error_if_cancelled (data->cancellable, &cancelled_error);
+	deactivate_invoke_cb (data, cancelled_error);
+}
+
+static void
+deactivate_async (NMDevice *device,
+                  GCancellable *cancellable,
+                  NMDeviceDeactivateCallback callback,
+                  gpointer callback_user_data) {
+
+	NMDeviceOvsInterface *self = NM_DEVICE_OVS_INTERFACE (device);
+	NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (self);
+	DeactivateData *data;
+
+	_LOGT (LOGD_CORE, "deactivate: start async");
+
+	/* We want to ensure that the kernel link for this device is
+	 * removed upon disconnection so that it will not interfere with
+	 * later activations of the same device. Unfortunately there is
+	 * no synchronization mechanism with vswitchd, we only update
+	 * ovsdb and wait that changes are picked up.
+	 */
+
+	data = g_slice_new (DeactivateData);
+	*data = (DeactivateData) {
+		.self = g_object_ref (self),
+		.cancellable = g_object_ref (cancellable),
+		.callback = callback,
+		.callback_user_data = callback_user_data,
+	};
+
+	if (   !priv->waiting_for_interface
+	    && !nm_platform_link_get_by_ifname (nm_device_get_platform (device),
+	                                        nm_device_get_iface (device))) {
+		_LOGT (LOGD_CORE, "deactivate: link not present, proceeding");
+		nm_device_update_from_platform_link (NM_DEVICE (self), NULL);
+		nm_utils_invoke_on_idle (deactivate_cb_on_idle, data, cancellable);
+		return;
+	}
+
+	if (priv->waiting_for_interface) {
+		/* At this point we have issued an INSERT and a DELETE
+		 * command for the interface to ovsdb. We don't know if
+		 * vswitchd will see the two updates or only one. We
+		 * must add a timeout to avoid waiting forever in case
+		 * the link doesn't appear.
+		 */
+		data->link_timeout_id = g_timeout_add (6000, deactivate_link_timeout, data);
+		_LOGT (LOGD_DEVICE, "deactivate: waiting for link to disappear in 6 seconds");
+	} else
+		_LOGT (LOGD_DEVICE, "deactivate: waiting for link to disappear");
+
+	data->cancelled_id = g_cancellable_connect (cancellable,
+	                                            G_CALLBACK (deactivate_cancelled_cb),
+	                                            data,
+	                                            NULL);
+	data->link_changed_id = g_signal_connect (nm_device_get_platform (device),
+	                                          NM_PLATFORM_SIGNAL_LINK_CHANGED,
+	                                          G_CALLBACK (deactivate_link_changed_cb),
+	                                          data);
+}
+
+static gboolean
+can_update_from_platform_link (NMDevice *device, const NMPlatformLink *plink)
+{
+	/* If the device is deactivating, we already sent the
+	 * deletion command to ovsdb and we don't want to deal
+	 * with any new link appearing from the previous
+	 * activation.
+	 */
+	return    !plink
+	       || nm_device_get_state (device) != NM_DEVICE_STATE_DEACTIVATING;
+}
+
 /*****************************************************************************/
 
 static void
@@ -182,7 +340,9 @@ nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass)
 	device_class->connection_type_check_compatible = NM_SETTING_OVS_INTERFACE_SETTING_NAME;
 	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_OPENVSWITCH);
 
+	device_class->can_update_from_platform_link = can_update_from_platform_link;
 	device_class->deactivate = deactivate;
+	device_class->deactivate_async = deactivate_async;
 	device_class->get_type_description = get_type_description;
 	device_class->create_and_realize = create_and_realize;
 	device_class->get_generic_capabilities = get_generic_capabilities;
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index fb9c9c69..a0749c21 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -356,6 +356,12 @@ teamd_dbus_appeared (GDBusConnection *connection,
 	_LOGI (LOGD_TEAM, "teamd appeared on D-Bus");
 	nm_device_queue_recheck_assume (device);
 
+	if (priv->kill_in_progress) {
+		/* If we are currently killing teamd, we are not
+		 * interested in knowing when it becomes ready. */
+		return;
+	}
+
 	/* If another teamd grabbed the bus name while our teamd was starting,
 	 * just ignore the death of our teamd and run with the existing one.
 	 */
@@ -380,11 +386,14 @@ teamd_dbus_appeared (GDBusConnection *connection,
 			if (pid != priv->teamd_pid)
 				teamd_cleanup (self, FALSE);
 		} else {
-			_LOGW (LOGD_TEAM, "failed to determine D-Bus name owner");
-			/* If we can't determine the bus name owner, don't kill our
-			 * teamd instance. Hopefully another existing teamd just died and
-			 * our instance will be able to grab the bus name.
-			 */
+			/* The process that registered on the bus died. If it's
+			 * the teamd instance we just started, ignore the event
+			 * as we already detect the failure through the process
+			 * watch. If it's a previous instance that got killed,
+			 * also ignore that as our new instance will register
+			 * again. */
+			_LOGD (LOGD_TEAM, "failed to determine D-Bus name owner, ignoring");
+			return;
 		}
 	}
 
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index c19ec766..516dc78b 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -405,7 +405,7 @@ state_changed (NMDevice *device,
 }
 
 static guint32
-get_dhcp_timeout (NMDevice *device, int addr_family)
+get_dhcp_timeout_for_device (NMDevice *device, int addr_family)
 {
 	/* shorter timeout for mesh connectivity */
 	return 20;
@@ -521,7 +521,7 @@ nm_device_olpc_mesh_class_init (NMDeviceOlpcMeshClass *klass)
 	device_class->act_stage1_prepare = act_stage1_prepare;
 	device_class->act_stage2_config = act_stage2_config;
 	device_class->state_changed = state_changed;
-	device_class->get_dhcp_timeout = get_dhcp_timeout;
+	device_class->get_dhcp_timeout_for_device = get_dhcp_timeout_for_device;
 
 	obj_properties[PROP_COMPANION] =
 	     g_param_spec_string (NM_DEVICE_OLPC_MESH_COMPANION, "", "",
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index 3e6ccc8f..3de5ae07 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -732,7 +732,7 @@ set_modem (NMDeviceModem *self, NMModem *modem)
 }
 
 static guint32
-get_dhcp_timeout (NMDevice *device, int addr_family)
+get_dhcp_timeout_for_device (NMDevice *device, int addr_family)
 {
 	/* DHCP is always done by the modem firmware, not by the network, and
 	 * by the time we get around to DHCP the firmware should already know
@@ -897,7 +897,7 @@ nm_device_modem_class_init (NMDeviceModemClass *klass)
 	device_class->is_available = is_available;
 	device_class->get_ip_iface_identifier = get_ip_iface_identifier;
 	device_class->get_configured_mtu = nm_modem_get_configured_mtu;
-	device_class->get_dhcp_timeout = get_dhcp_timeout;
+	device_class->get_dhcp_timeout_for_device = get_dhcp_timeout_for_device;
 
 	device_class->state_changed = device_state_changed;
 
diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c
index 0a07b26c..81c4ad9c 100644
--- a/src/dhcp/nm-dhcp-client.c
+++ b/src/dhcp/nm-dhcp-client.c
@@ -317,6 +317,7 @@ nm_dhcp_client_get_use_fqdn (NMDhcpClient *self)
 static const char *state_table[NM_DHCP_STATE_MAX + 1] = {
 	[NM_DHCP_STATE_UNKNOWN]    = "unknown",
 	[NM_DHCP_STATE_BOUND]      = "bound",
+	[NM_DHCP_STATE_EXTENDED]   = "extended",
 	[NM_DHCP_STATE_TIMEOUT]    = "timeout",
 	[NM_DHCP_STATE_EXPIRE]     = "expire",
 	[NM_DHCP_STATE_DONE]       = "done",
@@ -336,13 +337,14 @@ static NMDhcpState
 reason_to_state (NMDhcpClient *self, const char *iface, const char *reason)
 {
 	if (g_ascii_strcasecmp (reason, "bound") == 0 ||
-	    g_ascii_strcasecmp (reason, "bound6") == 0 ||
-	    g_ascii_strcasecmp (reason, "renew") == 0 ||
-	    g_ascii_strcasecmp (reason, "renew6") == 0 ||
-	    g_ascii_strcasecmp (reason, "reboot") == 0 ||
-	    g_ascii_strcasecmp (reason, "rebind") == 0 ||
-	    g_ascii_strcasecmp (reason, "rebind6") == 0)
+	    g_ascii_strcasecmp (reason, "bound6") == 0)
 		return NM_DHCP_STATE_BOUND;
+	else if (g_ascii_strcasecmp (reason, "renew") == 0 ||
+	         g_ascii_strcasecmp (reason, "renew6") == 0 ||
+	         g_ascii_strcasecmp (reason, "reboot") == 0 ||
+	         g_ascii_strcasecmp (reason, "rebind") == 0 ||
+	         g_ascii_strcasecmp (reason, "rebind6") == 0)
+		return NM_DHCP_STATE_EXTENDED;
 	else if (g_ascii_strcasecmp (reason, "timeout") == 0)
 		return NM_DHCP_STATE_TIMEOUT;
 	else if (g_ascii_strcasecmp (reason, "nak") == 0 ||
@@ -415,7 +417,7 @@ nm_dhcp_client_set_state (NMDhcpClient *self,
 	NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
 	gs_free char *event_id = NULL;
 
-	if (new_state == NM_DHCP_STATE_BOUND) {
+	if (NM_IN_SET (new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) {
 		g_return_if_fail (NM_IS_IP_CONFIG (ip_config, priv->addr_family));
 		g_return_if_fail (options);
 	} else {
@@ -430,10 +432,11 @@ nm_dhcp_client_set_state (NMDhcpClient *self,
 
 	/* The client may send same-state transitions for RENEW/REBIND events and
 	 * the lease may have changed, so handle same-state transitions for the
-	 * BOUND state.  Ignore same-state transitions for other events since
-	 * the lease won't have changed and the state was already handled.
+	 * EXTENDED and BOUND states.  Ignore same-state transitions for other
+	 * events since the lease won't have changed and the state was already handled.
 	 */
-	if ((priv->state == new_state) && (new_state != NM_DHCP_STATE_BOUND))
+	if (   (priv->state == new_state)
+	    && !NM_IN_SET (new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED))
 		return;
 
 	if (_LOGI_ENABLED ()) {
@@ -448,7 +451,7 @@ nm_dhcp_client_set_state (NMDhcpClient *self,
 	}
 
 	if (   priv->addr_family == AF_INET6
-	    && new_state == NM_DHCP_STATE_BOUND) {
+	    && NM_IN_SET (new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) {
 		char *start, *iaid;
 
 		iaid = g_hash_table_lookup (options, "iaid");
@@ -877,7 +880,7 @@ nm_dhcp_client_handle_event (gpointer unused,
 	_LOGD ("DHCP state '%s' -> '%s' (reason: '%s')",
 	       state_to_string (old_state), state_to_string (new_state), reason);
 
-	if (new_state == NM_DHCP_STATE_BOUND) {
+	if (NM_IN_SET (new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) {
 		GVariantIter iter;
 		const char *name;
 		GVariant *value;
@@ -918,7 +921,7 @@ nm_dhcp_client_handle_event (gpointer unused,
 		nm_dhcp_client_emit_ipv6_prefix_delegated (self, &prefix);
 	} else {
 		/* Fail if no valid IP config was received */
-		if (   new_state == NM_DHCP_STATE_BOUND
+		if (   NM_IN_SET (new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)
 		    && !ip_config) {
 			_LOGW ("client bound but IP config not received");
 			new_state = NM_DHCP_STATE_FAIL;
@@ -1007,8 +1010,8 @@ set_property (GObject *object, guint prop_id,
 	case PROP_IFACE:
 		/* construct-only */
 		priv->iface = g_value_dup_string (value);
-		g_return_if_fail (   priv->iface
-		                  && nm_utils_is_valid_iface_name (priv->iface, NULL));
+		g_return_if_fail (priv->iface);
+		nm_assert (nm_utils_ifname_valid_kernel (priv->iface, NULL));
 		break;
 	case PROP_IFINDEX:
 		/* construct-only */
@@ -1200,6 +1203,7 @@ nm_dhcp_client_class_init (NMDhcpClientClass *client_class)
 	                       G_PARAM_READWRITE |
 	                       G_PARAM_STATIC_STRINGS);
 
+	G_STATIC_ASSERT_EXPR (G_MAXINT32 == NM_DHCP_TIMEOUT_INFINITY);
 	obj_properties[PROP_TIMEOUT] =
 	    g_param_spec_uint (NM_DHCP_CLIENT_TIMEOUT, "", "",
 	                       1, G_MAXINT32, NM_DHCP_TIMEOUT_DEFAULT,
diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h
index 6a431fa8..1ff09067 100644
--- a/src/dhcp/nm-dhcp-client.h
+++ b/src/dhcp/nm-dhcp-client.h
@@ -12,8 +12,8 @@
 #include "nm-ip6-config.h"
 #include "nm-dhcp-utils.h"
 
-#define NM_DHCP_TIMEOUT_DEFAULT ((guint32) 45) /* default DHCP timeout, in seconds */
-#define NM_DHCP_TIMEOUT_INFINITY G_MAXINT32
+#define NM_DHCP_TIMEOUT_DEFAULT  ((guint32) 45) /* default DHCP timeout, in seconds */
+#define NM_DHCP_TIMEOUT_INFINITY ((guint32) G_MAXINT32)
 
 #define NM_TYPE_DHCP_CLIENT            (nm_dhcp_client_get_type ())
 #define NM_DHCP_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClient))
@@ -43,7 +43,8 @@
 
 typedef enum {
 	NM_DHCP_STATE_UNKNOWN = 0,
-	NM_DHCP_STATE_BOUND,        /* new lease or lease changed */
+	NM_DHCP_STATE_BOUND,        /* new lease */
+	NM_DHCP_STATE_EXTENDED,     /* lease extended */
 	NM_DHCP_STATE_TIMEOUT,      /* timed out contacting server */
 	NM_DHCP_STATE_DONE,         /* client quit or stopped */
 	NM_DHCP_STATE_EXPIRE,       /* lease expired or NAKed */
diff --git a/src/dhcp/nm-dhcp-nettools.c b/src/dhcp/nm-dhcp-nettools.c
index b4c0a451..45ff46f5 100644
--- a/src/dhcp/nm-dhcp-nettools.c
+++ b/src/dhcp/nm-dhcp-nettools.c
@@ -977,7 +977,7 @@ lease_save (NMDhcpNettools *self, NDhcp4ClientLease *lease, const char *lease_fi
 }
 
 static void
-bound4_handle (NMDhcpNettools *self, NDhcp4ClientLease *lease)
+bound4_handle (NMDhcpNettools *self, NDhcp4ClientLease *lease, gboolean extended)
 {
 	NMDhcpNettoolsPrivate *priv = NM_DHCP_NETTOOLS_GET_PRIVATE (self);
 	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
@@ -985,7 +985,7 @@ bound4_handle (NMDhcpNettools *self, NDhcp4ClientLease *lease)
 	gs_unref_hashtable GHashTable *options = NULL;
 	GError *error = NULL;
 
-	_LOGT ("lease available");
+	_LOGT ("lease available (%s)", extended ? "extended" : "new");
 
 	ip4_config = lease_to_ip4_config (nm_dhcp_client_get_multi_idx (NM_DHCP_CLIENT (self)),
 	                                  iface,
@@ -1006,7 +1006,7 @@ bound4_handle (NMDhcpNettools *self, NDhcp4ClientLease *lease)
 	lease_save (self, lease, priv->lease_file);
 
 	nm_dhcp_client_set_state (NM_DHCP_CLIENT (self),
-	                          NM_DHCP_STATE_BOUND,
+	                          extended ? NM_DHCP_STATE_EXTENDED : NM_DHCP_STATE_BOUND,
 	                          NM_IP_CONFIG_CAST (ip4_config),
 	                          options);
 }
@@ -1037,10 +1037,10 @@ dhcp4_event_handle (NMDhcpNettools *self,
 		break;
 	case N_DHCP4_CLIENT_EVENT_GRANTED:
 		priv->lease = n_dhcp4_client_lease_ref (event->granted.lease);
-		bound4_handle (self, event->granted.lease);
+		bound4_handle (self, event->granted.lease, FALSE);
 		break;
 	case N_DHCP4_CLIENT_EVENT_EXTENDED:
-		bound4_handle (self, event->extended.lease);
+		bound4_handle (self, event->extended.lease, TRUE);
 		break;
 	case N_DHCP4_CLIENT_EVENT_DOWN:
 		/* ignore down events, they are purely informational */
@@ -1064,8 +1064,18 @@ dhcp4_event_cb (GIOChannel *source,
 	int r;
 
 	r = n_dhcp4_client_dispatch (priv->client);
-	if (r < 0)
-		return G_SOURCE_CONTINUE;
+	if (r < 0) {
+		/* FIXME: if any operation (e.g. send()) fails during the
+		 * dispatch, n-dhcp4 returns an error without arming timers
+		 * or progressing state, so the only reasonable thing to do
+		 * is to move to failed state so that the client will be
+		 * restarted. Ideally n-dhcp4 should retry failed operations
+		 * a predefined number of times (possibly infinite).
+		 */
+		_LOGE ("error %d dispatching events", r);
+		nm_dhcp_client_set_state (NM_DHCP_CLIENT (self), NM_DHCP_STATE_FAIL, NULL, NULL);
+		return G_SOURCE_REMOVE;
+	}
 
 	while (!n_dhcp4_client_pop_event (priv->client, &event) && event) {
 		dhcp4_event_handle (self, event);
diff --git a/src/dhcp/nm-dhcp-options.c b/src/dhcp/nm-dhcp-options.c
index 4c003f31..1d391f3e 100644
--- a/src/dhcp/nm-dhcp-options.c
+++ b/src/dhcp/nm-dhcp-options.c
@@ -34,7 +34,7 @@ const NMDhcpOption _nm_dhcp_option_dhcp4_options[] = {
 	REQ (NM_DHCP_OPTION_DHCP4_NIS_DOMAIN,                        "nis_domain",                      TRUE ),
 	REQ (NM_DHCP_OPTION_DHCP4_NIS_SERVERS,                       "nis_servers",                     TRUE ),
 	REQ (NM_DHCP_OPTION_DHCP4_NTP_SERVER,                        "ntp_servers",                     TRUE ),
-	REQ (NM_DHCP_OPTION_DHCP4_SERVER_ID,                         "dhcp_server_identifier",          TRUE ),
+	REQ (NM_DHCP_OPTION_DHCP4_SERVER_ID,                         "dhcp_server_identifier",          FALSE ),
 	REQ (NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST,                "domain_search",                   TRUE ),
 	REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_CLASSLESS_STATIC_ROUTE,    "ms_classless_static_routes",      TRUE ),
 	REQ (NM_DHCP_OPTION_DHCP4_PRIVATE_PROXY_AUTODISCOVERY,       "wpad",                            TRUE ),
diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c
index 1518d465..6e6aa243 100644
--- a/src/dhcp/nm-dhcp-systemd.c
+++ b/src/dhcp/nm-dhcp-systemd.c
@@ -477,7 +477,7 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 /*****************************************************************************/
 
 static void
-bound4_handle (NMDhcpSystemd *self)
+bound4_handle (NMDhcpSystemd *self, gboolean extended)
 {
 	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
@@ -514,7 +514,7 @@ bound4_handle (NMDhcpSystemd *self)
 	dhcp_lease_save (lease, priv->lease_file);
 
 	nm_dhcp_client_set_state (NM_DHCP_CLIENT (self),
-	                          NM_DHCP_STATE_BOUND,
+	                          extended ? NM_DHCP_STATE_EXTENDED : NM_DHCP_STATE_BOUND,
 	                          NM_IP_CONFIG_CAST (ip4_config),
 	                          options);
 }
@@ -538,8 +538,10 @@ dhcp_event_cb (sd_dhcp_client *client, int event, gpointer user_data)
 		break;
 	case SD_DHCP_CLIENT_EVENT_RENEW:
 	case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
+		bound4_handle (self, TRUE);
+		break;
 	case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE:
-		bound4_handle (self);
+		bound4_handle (self, FALSE);
 		break;
 	case SD_DHCP_CLIENT_EVENT_SELECTING:
 		break;
diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c
index c32ef1cf..2cb93a2d 100644
--- a/src/initrd/nmi-cmdline-reader.c
+++ b/src/initrd/nmi-cmdline-reader.c
@@ -195,6 +195,47 @@ _base_setting_set (NMConnection *connection, const char *property, const char *v
 }
 
 static void
+read_all_connections_from_fw (GHashTable *connections, const char *sysfs_dir)
+{
+	gs_unref_hashtable GHashTable *ibft = NULL;
+	NMConnection *connection;
+	GHashTableIter iter;
+	const char *mac;
+	GHashTable *nic;
+	const char *index;
+	GError *error = NULL;
+
+	ibft = nmi_ibft_read (sysfs_dir);
+
+	g_hash_table_iter_init (&iter, ibft);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &mac, (gpointer *) &nic)) {
+		connection = nm_simple_connection_new ();
+
+		index = g_hash_table_lookup (nic, "index");
+		if (!index) {
+			_LOGW (LOGD_CORE, "Ignoring an iBFT entry without an index");
+			continue;
+		}
+
+		if (!nmi_ibft_update_connection_from_nic (connection, nic, &error)) {
+			_LOGW (LOGD_CORE, "Unable to merge iBFT configuration: %s", error->message);
+			g_error_free (error);
+		}
+
+		g_hash_table_insert (connections,
+		                     g_strdup_printf ("ibft%s", index),
+		                     connection);
+	}
+
+	connection = nmi_dt_reader_parse (sysfs_dir);
+	if (connection) {
+		g_hash_table_insert (connections,
+		                     g_strdup ("ofw"),
+		                     connection);
+	}
+}
+
+static void
 parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
 {
 	NMConnection *connection;
@@ -258,44 +299,7 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
 
 	if (ifname == NULL && (   g_strcmp0 (kind, "fw") == 0
 	                       || g_strcmp0 (kind, "ibft") == 0)) {
-		GHashTableIter iter;
-		const char *mac;
-		GHashTable *nic;
-		const char *index;
-
-		/* This is the ip=ibft case. Just take all we got from iBFT
-		 * and don't process anything else, since there's no ifname
-		 * specified to apply it to. */
-		if (!ibft)
-			ibft = nmi_ibft_read (sysfs_dir);
-
-		g_hash_table_iter_init (&iter, ibft);
-		while (g_hash_table_iter_next (&iter, (gpointer)&mac, (gpointer)&nic)) {
-			connection = nm_simple_connection_new ();
-
-			index = g_hash_table_lookup (nic, "index");
-			if (!index) {
-				_LOGW (LOGD_CORE, "Ignoring an iBFT entry without an index");
-				continue;
-			}
-
-			if (!nmi_ibft_update_connection_from_nic (connection, nic, &error)) {
-				_LOGW (LOGD_CORE, "Unable to merge iBFT configuration: %s", error->message);
-				g_error_free (error);
-			}
-
-			g_hash_table_insert (connections,
-			                     g_strdup_printf ("ibft%s", index),
-			                     connection);
-		}
-
-		connection = nmi_dt_reader_parse (sysfs_dir);
-		if (connection) {
-			g_hash_table_insert (connections,
-			                     g_strdup ("ofw"),
-			                     connection);
-		}
-
+		read_all_connections_from_fw (connections, sysfs_dir);
 		return;
 	}
 
@@ -421,8 +425,7 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
 		if (mac) {
 			g_strchomp (mac);
 			mac_up = g_ascii_strup (mac, -1);
-			if (!ibft)
-				ibft = nmi_ibft_read (sysfs_dir);
+			ibft = nmi_ibft_read (sysfs_dir);
 			nic = g_hash_table_lookup (ibft, mac_up);
 			if (!nic)
 				_LOGW (LOGD_CORE, "No iBFT NIC for %s (%s)", ifname, mac_up);
@@ -838,6 +841,8 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv)
 			parse_nameserver (connections, argument);
 		else if (strcmp (tag, "rd.peerdns") == 0)
 			parse_rd_peerdns (connections, argument);
+		else if (strcmp (tag, "rd.iscsi.ibft") == 0 && _nm_utils_ascii_str_to_bool (argument, TRUE))
+			read_all_connections_from_fw (connections, sysfs_dir);
 		else if (strcmp (tag, "rd.bootif") == 0)
 			ignore_bootif = !_nm_utils_ascii_str_to_bool (argument, TRUE);
 		else if (strcmp (tag, "rd.neednet") == 0)
diff --git a/src/initrd/nmi-ibft-reader.c b/src/initrd/nmi-ibft-reader.c
index ffce98fc..47b90ebf 100644
--- a/src/initrd/nmi-ibft-reader.c
+++ b/src/initrd/nmi-ibft-reader.c
@@ -296,6 +296,7 @@ connection_setting_add (GHashTable *nic,
 	              NM_SETTING_CONNECTION_TYPE, type,
 	              NM_SETTING_CONNECTION_UUID, uuid,
 	              NM_SETTING_CONNECTION_ID, id,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, NULL,
 	              NULL);
 
 	g_free (uuid);
diff --git a/src/initrd/tests/test-cmdline-reader.c b/src/initrd/tests/test-cmdline-reader.c
index 1d4bb9a6..8951e491 100644
--- a/src/initrd/tests/test-cmdline-reader.c
+++ b/src/initrd/tests/test-cmdline-reader.c
@@ -768,10 +768,30 @@ test_team (void)
 }
 
 static void
-test_ibft (void)
+test_ibft_ip_dev (void)
+{
+	const char *const*ARGV = NM_MAKE_STRV ("ip=eth0:ibft");
+	gs_unref_hashtable GHashTable *connections = NULL;
+	NMSettingConnection *s_con;
+	NMConnection *connection;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", ARGV);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 1);
+
+	connection = g_hash_table_lookup (connections, "eth0");
+	g_assert (connection);
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_VLAN_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, NULL);
+}
+
+static void
+_test_ibft_ip (const char *const*ARGV)
 {
 	gs_unref_hashtable GHashTable *connections = NULL;
-	const char *const*ARGV = NM_MAKE_STRV ("ip=ibft");
 	NMConnection *connection;
 
 	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", ARGV);
@@ -782,11 +802,29 @@ test_ibft (void)
 	g_assert (connection);
 	nmtst_assert_connection_verifies_without_normalization (connection);
 	g_assert_cmpstr (nm_connection_get_id (connection), ==, "iBFT VLAN Connection 0");
+	g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, NULL);
 
 	connection = g_hash_table_lookup (connections, "ibft2");
 	g_assert (connection);
 	nmtst_assert_connection_verifies_without_normalization (connection);
 	g_assert_cmpstr (nm_connection_get_id (connection), ==, "iBFT Connection 2");
+	g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, NULL);
+}
+
+static void
+test_ibft_ip (void)
+{
+	const char *const*ARGV = NM_MAKE_STRV ("ip=ibft");
+
+	_test_ibft_ip (ARGV);
+}
+
+static void
+test_ibft_rd_iscsi_ibft (void)
+{
+	const char *const*ARGV = NM_MAKE_STRV ("rd.iscsi.ibft");
+
+	_test_ibft_ip (ARGV);
 }
 
 static void
@@ -1045,7 +1083,9 @@ int main (int argc, char **argv)
 	g_test_add_func ("/initrd/cmdline/team", test_team);
 	g_test_add_func ("/initrd/cmdline/bridge", test_bridge);
 	g_test_add_func ("/initrd/cmdline/bridge/default", test_bridge_default);
-	g_test_add_func ("/initrd/cmdline/ibft", test_ibft);
+	g_test_add_func ("/initrd/cmdline/ibft/ip_dev", test_ibft_ip_dev);
+	g_test_add_func ("/initrd/cmdline/ibft/ip", test_ibft_ip);
+	g_test_add_func ("/initrd/cmdline/ibft/rd_iscsi_ibft", test_ibft_rd_iscsi_ibft);
 	g_test_add_func ("/initrd/cmdline/ignore_extra", test_ignore_extra);
 	g_test_add_func ("/initrd/cmdline/rd_znet", test_rd_znet);
 	g_test_add_func ("/initrd/cmdline/rd_znet/legacy", test_rd_znet_legacy);
diff --git a/src/initrd/tests/test-ibft-reader.c b/src/initrd/tests/test-ibft-reader.c
index 932c1a48..f7709543 100644
--- a/src/initrd/tests/test-ibft-reader.c
+++ b/src/initrd/tests/test-ibft-reader.c
@@ -65,6 +65,7 @@ test_read_ibft_dhcp (void)
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "iBFT Connection 1");
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, NULL);
 	g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0);
 	g_assert (nm_setting_connection_get_autoconnect (s_con));
 
@@ -109,6 +110,7 @@ test_read_ibft_static (void)
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "iBFT Connection 0");
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, NULL);
 	g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0);
 	g_assert (nm_setting_connection_get_autoconnect (s_con));
 
@@ -178,6 +180,7 @@ test_read_ibft_vlan (void)
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
 	g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_VLAN_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, NULL);
 
 	/* ===== WIRED SETTING ===== */
 	s_wired = nm_connection_get_setting_wired (connection);
diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c
index 8077099f..6f2815ef 100644
--- a/src/ndisc/nm-lndp-ndisc.c
+++ b/src/ndisc/nm-lndp-ndisc.c
@@ -557,6 +557,7 @@ nm_lndp_ndisc_new (NMPlatform *platform,
                    const char *network_id,
                    NMSettingIP6ConfigAddrGenMode addr_gen_mode,
                    NMNDiscNodeType node_type,
+                   gint32 ra_timeout,
                    GError **error)
 {
 	nm_auto_pop_netns NMPNetns *netns = NULL;
@@ -582,6 +583,7 @@ nm_lndp_ndisc_new (NMPlatform *platform,
 	                      NM_NDISC_MAX_ADDRESSES, ipv6_sysctl_get (platform, ifname,
 	                                                               "max_addresses",
 	                                                               0, G_MAXINT32, NM_NDISC_MAX_ADDRESSES_DEFAULT),
+	                      NM_NDISC_RA_TIMEOUT, (int) ra_timeout,
 	                      NM_NDISC_ROUTER_SOLICITATIONS, ipv6_sysctl_get (platform, ifname,
 	                                                                      "router_solicitations",
 	                                                                      1, G_MAXINT32, NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT),
diff --git a/src/ndisc/nm-lndp-ndisc.h b/src/ndisc/nm-lndp-ndisc.h
index 22f21e41..82e7b2de 100644
--- a/src/ndisc/nm-lndp-ndisc.h
+++ b/src/ndisc/nm-lndp-ndisc.h
@@ -28,6 +28,7 @@ NMNDisc *nm_lndp_ndisc_new (NMPlatform *platform,
                             const char *network_id,
                             NMSettingIP6ConfigAddrGenMode addr_gen_mode,
                             NMNDiscNodeType node_type,
+                            gint32 ra_timeout,
                             GError **error);
 
 #endif /* __NETWORKMANAGER_LNDP_NDISC_H__ */
diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c
index 41201e24..0afca26b 100644
--- a/src/ndisc/nm-ndisc.c
+++ b/src/ndisc/nm-ndisc.c
@@ -48,6 +48,7 @@ struct _NMNDiscPrivate {
 	char *network_id;
 	NMSettingIP6ConfigAddrGenMode addr_gen_mode;
 	NMUtilsStableType stable_type;
+	gint32 ra_timeout;
 	gint32 max_addresses;
 	gint32 router_solicitations;
 	gint32 router_solicitation_interval;
@@ -67,6 +68,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 	PROP_NETWORK_ID,
 	PROP_ADDR_GEN_MODE,
 	PROP_MAX_ADDRESSES,
+	PROP_RA_TIMEOUT,
 	PROP_ROUTER_SOLICITATIONS,
 	PROP_ROUTER_SOLICITATION_INTERVAL,
 	PROP_NODE_TYPE,
@@ -74,7 +76,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 
 enum {
 	CONFIG_RECEIVED,
-	RA_TIMEOUT,
+	RA_TIMEOUT_SIGNAL,
 	LAST_SIGNAL
 };
 
@@ -898,7 +900,7 @@ ndisc_ra_timeout_cb (gpointer user_data)
 	NMNDisc *ndisc = NM_NDISC (user_data);
 
 	NM_NDISC_GET_PRIVATE (ndisc)->ra_timeout_id = 0;
-	g_signal_emit (ndisc, signals[RA_TIMEOUT], 0);
+	g_signal_emit (ndisc, signals[RA_TIMEOUT_SIGNAL], 0);
 	return G_SOURCE_REMOVE;
 }
 
@@ -906,34 +908,46 @@ void
 nm_ndisc_start (NMNDisc *ndisc)
 {
 	nm_auto_pop_netns NMPNetns *netns = NULL;
-	NMNDiscPrivate *priv = NM_NDISC_GET_PRIVATE (ndisc);
-	NMNDiscClass *klass = NM_NDISC_GET_CLASS (ndisc);
-	gint64 ra_wait_secs;
+	NMNDiscPrivate *priv;
+
+	g_return_if_fail (NM_IS_NDISC (ndisc));
+
+	priv = NM_NDISC_GET_PRIVATE (ndisc);
 
-	g_return_if_fail (klass->start);
-	g_return_if_fail (!priv->ra_timeout_id);
+	nm_assert (NM_NDISC_GET_CLASS (ndisc)->start);
+	nm_assert (!priv->ra_timeout_id);
 
-	_LOGD ("starting neighbor discovery: %d", priv->ifindex);
+	_LOGD ("starting neighbor discovery for ifindex %d%s",
+	       priv->ifindex,
+	         priv->node_type == NM_NDISC_NODE_TYPE_HOST
+	       ? " (solicit)"
+	       : " (announce)");
 
 	if (!nm_ndisc_netns_push (ndisc, &netns))
 		return;
 
-	klass->start (ndisc);
+	NM_NDISC_GET_CLASS (ndisc)->start (ndisc);
+
+	if (priv->node_type == NM_NDISC_NODE_TYPE_HOST) {
+		gint32 ra_timeout = priv->ra_timeout;
 
-	switch (priv->node_type) {
-	case NM_NDISC_NODE_TYPE_HOST:
-		ra_wait_secs = (((gint64) priv->router_solicitations) * priv->router_solicitation_interval) + 1;
-		ra_wait_secs = CLAMP (ra_wait_secs, 30, 120);
-		priv->ra_timeout_id = g_timeout_add_seconds (ra_wait_secs, ndisc_ra_timeout_cb, ndisc);
-		_LOGD ("scheduling RA timeout in %d seconds", (int) ra_wait_secs);
+		G_STATIC_ASSERT_EXPR (NM_RA_TIMEOUT_DEFAULT == 0);
+		G_STATIC_ASSERT_EXPR (NM_RA_TIMEOUT_INFINITY == G_MAXINT32);
+		if (ra_timeout != NM_RA_TIMEOUT_INFINITY) {
+			if (ra_timeout == NM_RA_TIMEOUT_DEFAULT) {
+				ra_timeout = NM_MAX ((((gint64) priv->router_solicitations) * priv->router_solicitation_interval) + 1,
+				                     30);
+			}
+			nm_assert (ra_timeout > 0 && ra_timeout < NM_RA_TIMEOUT_INFINITY);
+			_LOGD ("scheduling RA timeout in %d seconds", ra_timeout);
+			priv->ra_timeout_id = g_timeout_add_seconds (ra_timeout, ndisc_ra_timeout_cb, ndisc);
+		}
 		solicit_routers (ndisc);
-		break;
-	case NM_NDISC_NODE_TYPE_ROUTER:
-		announce_router_initial (ndisc);
-		break;
-	default:
-		g_assert_not_reached ();
+		return;
 	}
+
+	nm_assert (priv->node_type == NM_NDISC_NODE_TYPE_ROUTER);
+	announce_router_initial (ndisc);
 }
 
 NMNDiscConfigMap
@@ -1312,6 +1326,10 @@ set_property (GObject *object, guint prop_id,
 		/* construct-only */
 		priv->max_addresses = g_value_get_int (value);
 		break;
+	case PROP_RA_TIMEOUT:
+		/* construct-only */
+		priv->ra_timeout = g_value_get_int (value);
+		break;
 	case PROP_ROUTER_SOLICITATIONS:
 		/* construct-only */
 		priv->router_solicitations = g_value_get_int (value);
@@ -1323,6 +1341,8 @@ set_property (GObject *object, guint prop_id,
 	case PROP_NODE_TYPE:
 		/* construct-only */
 		priv->node_type = g_value_get_int (value);
+		nm_assert (NM_IN_SET (priv->node_type, NM_NDISC_NODE_TYPE_HOST,
+		                                       NM_NDISC_NODE_TYPE_ROUTER));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1401,8 +1421,8 @@ nm_ndisc_class_init (NMNDiscClass *klass)
 	g_type_class_add_private (klass, sizeof (NMNDiscPrivate));
 
 	object_class->set_property = set_property;
-	object_class->dispose = dispose;
-	object_class->finalize = finalize;
+	object_class->dispose      = dispose;
+	object_class->finalize     = finalize;
 
 	obj_properties[PROP_PLATFORM] =
 	    g_param_spec_object (NM_NDISC_PLATFORM, "", "",
@@ -1446,6 +1466,13 @@ nm_ndisc_class_init (NMNDiscClass *klass)
 	                      G_PARAM_WRITABLE |
 	                      G_PARAM_CONSTRUCT_ONLY |
 	                      G_PARAM_STATIC_STRINGS);
+	G_STATIC_ASSERT_EXPR (G_MAXINT32 == NM_RA_TIMEOUT_INFINITY);
+	obj_properties[PROP_RA_TIMEOUT] =
+	    g_param_spec_int (NM_NDISC_RA_TIMEOUT, "", "",
+	                      0, G_MAXINT32, 0,
+	                      G_PARAM_WRITABLE |
+	                      G_PARAM_CONSTRUCT_ONLY |
+	                      G_PARAM_STATIC_STRINGS);
 	obj_properties[PROP_ROUTER_SOLICITATIONS] =
 	    g_param_spec_int (NM_NDISC_ROUTER_SOLICITATIONS, "", "",
 	                      1, G_MAXINT32, NM_NDISC_ROUTER_SOLICITATIONS_DEFAULT,
@@ -1473,8 +1500,8 @@ nm_ndisc_class_init (NMNDiscClass *klass)
 	                  0,
 	                  NULL, NULL, NULL,
 	                  G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
-	signals[RA_TIMEOUT] =
-	    g_signal_new (NM_NDISC_RA_TIMEOUT,
+	signals[RA_TIMEOUT_SIGNAL] =
+	    g_signal_new (NM_NDISC_RA_TIMEOUT_SIGNAL,
 	                  G_OBJECT_CLASS_TYPE (klass),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0,
diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h
index 92295ac6..eeda38e2 100644
--- a/src/ndisc/nm-ndisc.h
+++ b/src/ndisc/nm-ndisc.h
@@ -16,6 +16,9 @@
 #include "platform/nm-platform.h"
 #include "platform/nmp-object.h"
 
+#define NM_RA_TIMEOUT_DEFAULT    ((gint32) 0)
+#define NM_RA_TIMEOUT_INFINITY   G_MAXINT32
+
 #define NM_TYPE_NDISC            (nm_ndisc_get_type ())
 #define NM_NDISC(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_NDISC, NMNDisc))
 #define NM_NDISC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_NDISC, NMNDiscClass))
@@ -31,11 +34,12 @@
 #define NM_NDISC_STABLE_TYPE    "stable-type"
 #define NM_NDISC_NODE_TYPE      "node-type"
 #define NM_NDISC_MAX_ADDRESSES  "max-addresses"
+#define NM_NDISC_RA_TIMEOUT     "ra-timeout"
 #define NM_NDISC_ROUTER_SOLICITATIONS "router-solicitations"
 #define NM_NDISC_ROUTER_SOLICITATION_INTERVAL "router-solicitation-interval"
 
-#define NM_NDISC_CONFIG_RECEIVED "config-received"
-#define NM_NDISC_RA_TIMEOUT      "ra-timeout"
+#define NM_NDISC_CONFIG_RECEIVED   "config-received"
+#define NM_NDISC_RA_TIMEOUT_SIGNAL "ra-timeout-signal"
 
 typedef enum {
 	NM_NDISC_DHCP_LEVEL_UNKNOWN,
diff --git a/src/ndisc/tests/test-ndisc-linux.c b/src/ndisc/tests/test-ndisc-linux.c
index e9ceaa2b..a25478e8 100644
--- a/src/ndisc/tests/test-ndisc-linux.c
+++ b/src/ndisc/tests/test-ndisc-linux.c
@@ -52,6 +52,7 @@ main (int argc, char **argv)
 	                           "8ce666e8-d34d-4fb1-b858-f15a7al28086",
 	                           NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64,
 	                           NM_NDISC_NODE_TYPE_HOST,
+	                           0,
 	                           &error);
 	if (!ndisc) {
 		g_print ("Failed to create NMNDisc instance: %s\n", error->message);
diff --git a/src/nm-config.c b/src/nm-config.c
index a7bb3503..a0995cf9 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -430,7 +430,7 @@ nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device)
 		 *
 		 * Instead, try the interface-name...  */
 		ifname = nm_device_get_ip_iface (device);
-		if (!nm_utils_is_valid_iface_name (ifname, NULL))
+		if (!nm_utils_ifname_valid_kernel (ifname, NULL))
 			return;
 
 		spec_to_free = g_strdup_printf (NM_MATCH_SPEC_INTERFACE_NAME_TAG"=%s", ifname);
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index ccac6376..e941fe7e 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -686,7 +686,6 @@ do_curl_request (NMConnectivityCheckHandle *cb_data)
 	curl_multi_setopt (mhandle, CURLMOPT_SOCKETDATA, cb_data);
 	curl_multi_setopt (mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
 	curl_multi_setopt (mhandle, CURLMOPT_TIMERDATA, cb_data);
-	curl_multi_setopt (mhandle, CURLOPT_VERBOSE, 1);
 
 	switch (cb_data->addr_family) {
 	case AF_INET:
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index fb92289f..e059994a 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -2145,7 +2145,7 @@ nm_utils_sysctl_ip_conf_path (int addr_family, char *buf, const char *ifname, co
 	nm_assert (buf);
 	nm_assert_addr_family (addr_family);
 
-	g_assert (nm_utils_is_valid_iface_name (ifname, NULL));
+	g_assert (nm_utils_ifname_valid_kernel (ifname, NULL));
 	property = NM_ASSERT_VALID_PATH_COMPONENT (property);
 
 	len = g_snprintf (buf,
@@ -2163,7 +2163,7 @@ nm_utils_sysctl_ip_conf_is_path (int addr_family, const char *path, const char *
 {
 	g_return_val_if_fail (path, FALSE);
 	NM_ASSERT_VALID_PATH_COMPONENT (property);
-	g_assert (!ifname || nm_utils_is_valid_iface_name (ifname, NULL));
+	g_assert (!ifname || nm_utils_ifname_valid_kernel (ifname, NULL));
 
 	if (addr_family == AF_INET) {
 		if (!g_str_has_prefix (path, IPV4_PROPERTY_DIR))
@@ -2196,7 +2196,7 @@ nm_utils_sysctl_ip_conf_is_path (int addr_family, const char *path, const char *
 			return FALSE;
 		memcpy (buf, path, l);
 		buf[l] = '\0';
-		if (!nm_utils_is_valid_iface_name (buf, NULL))
+		if (!nm_utils_ifname_valid_kernel (buf, NULL))
 			return FALSE;
 		path = slash + 1;
 	}
@@ -3635,7 +3635,7 @@ nm_utils_ifname_cpy (char *dst, const char *name)
 	g_return_if_fail (dst);
 	g_return_if_fail (name && name[0]);
 
-	nm_assert (nm_utils_is_valid_iface_name (name, NULL));
+	nm_assert (nm_utils_ifname_valid_kernel (name, NULL));
 
 	/* ensures NUL padding of the entire IFNAMSIZ buffer. */
 
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index bfb7af57..df70c71b 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -106,6 +106,7 @@ dhcp4_state_changed (NMDhcpClient *client,
 
 	switch (state) {
 	case NM_DHCP_STATE_BOUND:
+	case NM_DHCP_STATE_EXTENDED:
 		g_assert (ip4_config);
 		g_assert (nm_ip4_config_get_ifindex (ip4_config) == gl.ifindex);
 
@@ -551,10 +552,14 @@ main (int argc, char *argv[])
 			stable_type = (global_opt.stable_id[0] - '0');
 			stable_id = &global_opt.stable_id[2];
 		}
-		ndisc = nm_lndp_ndisc_new (NM_PLATFORM_GET, gl.ifindex, global_opt.ifname,
-		                           stable_type, stable_id,
+		ndisc = nm_lndp_ndisc_new (NM_PLATFORM_GET,
+		                           gl.ifindex,
+		                           global_opt.ifname,
+		                           stable_type,
+		                           stable_id,
 		                           global_opt.addr_gen_mode,
 		                           NM_NDISC_NODE_TYPE_HOST,
+		                           NM_RA_TIMEOUT_DEFAULT,
 		                           NULL);
 		g_assert (ndisc);
 
@@ -572,7 +577,7 @@ main (int argc, char *argv[])
 		                  G_CALLBACK (ndisc_config_changed),
 		                  NULL);
 		g_signal_connect (ndisc,
-		                  NM_NDISC_RA_TIMEOUT,
+		                  NM_NDISC_RA_TIMEOUT_SIGNAL,
 		                  G_CALLBACK (ndisc_ra_timeout),
 		                  NULL);
 		nm_ndisc_start (ndisc);
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 3696c789..6d3a5ddb 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -2011,6 +2011,20 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 		return device;
 	}
 
+	if (!find_master (self,
+	                  connection,
+	                  device,
+	                  NULL,
+	                  NULL,
+	                  NULL,
+	                  &error)) {
+		_LOG3D (LOGD_DEVICE, connection,
+		        "skip activation: %s",
+		        error->message);
+		g_error_free (error);
+		return device;
+	}
+
 	/* Create backing resources if the device has any autoconnect connections */
 	connections = nm_settings_get_connections_clone (priv->settings, NULL,
 	                                                 NULL, NULL,
@@ -4649,6 +4663,19 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	if (nm_active_connection_get_activation_type (active) == NM_ACTIVATION_TYPE_MANAGED)
 		nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
 
+	/* Try to find the master connection/device if the connection has a dependency */
+	if (!find_master (self,
+	                  applied,
+	                  device,
+	                  &master_connection,
+	                  &master_device,
+	                  &master_ac,
+	                  error)) {
+		g_prefix_error (error, "Can not find a master for %s: ",
+		                nm_settings_connection_get_id (sett_conn));
+		return FALSE;
+	}
+
 	/* Create any backing resources the device needs */
 	if (!nm_device_is_real (device)) {
 		NMDevice *parent;
@@ -4717,19 +4744,6 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 		}
 	}
 
-	/* Try to find the master connection/device if the connection has a dependency */
-	if (!find_master (self,
-	                  applied,
-	                  device,
-	                  &master_connection,
-	                  &master_device,
-	                  &master_ac,
-	                  error)) {
-		g_prefix_error (error, "Can not find a master for %s: ",
-		                nm_settings_connection_get_id (sett_conn));
-		return FALSE;
-	}
-
 	/* Ensure there's a master active connection the new connection we're
 	 * activating can depend on.
 	 */
diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c
index 4f0da581..b55fbd66 100644
--- a/src/platform/nm-platform-utils.c
+++ b/src/platform/nm-platform-utils.c
@@ -155,7 +155,7 @@ again:
 		const char *ifname = known_ifnames[try_count % 2];
 
 		nm_assert (ifindex > 0);
-		nm_assert (ifname && nm_utils_is_valid_iface_name (ifname, NULL));
+		nm_assert (ifname && nm_utils_ifname_valid_kernel (ifname, NULL));
 		nm_assert (fd >= 0);
 
 		memset (&ifr, 0, sizeof (ifr));
@@ -343,6 +343,7 @@ ethtool_get_stringset (SocketHandle *shandle, int stringset_id)
 		.info.reserved = 0,
 		.info.sset_mask = (1ULL << stringset_id),
 	};
+	const guint32 *pdata;
 	gs_free struct ethtool_gstrings *gstrings = NULL;
 	gsize gstrings_len;
 	guint32 i, len;
@@ -352,7 +353,9 @@ ethtool_get_stringset (SocketHandle *shandle, int stringset_id)
 	if (!sset_info.info.sset_mask)
 		return NULL;
 
-	len = sset_info.info.data[0];
+	pdata = (guint32 *) sset_info.info.data;
+
+	len = *pdata;
 
 	gstrings_len = sizeof (*gstrings) + (len * ETH_GSTRING_LEN);
 	gstrings = g_malloc0 (gstrings_len);
@@ -838,6 +841,7 @@ nmp_utils_ethtool_get_permanent_address (int ifindex,
 		.e.cmd = ETHTOOL_GPERMADDR,
 		.e.size = NM_UTILS_HWADDR_LEN_MAX,
 	};
+	const guint8 *pdata;
 
 	guint i;
 
@@ -851,20 +855,22 @@ nmp_utils_ethtool_get_permanent_address (int ifindex,
 	if (edata.e.size < 1)
 		return FALSE;
 
-	if (NM_IN_SET (edata.e.data[0], 0, 0xFF)) {
+	pdata = (const guint8 *) edata.e.data;
+
+	if (NM_IN_SET (pdata[0], 0, 0xFF)) {
 		/* Some drivers might return a permanent address of all zeros.
 		 * Reject that (rh#1264024)
 		 *
 		 * Some drivers return a permanent address of all ones. Reject that too */
 		for (i = 1; i < edata.e.size; i++) {
-			if (edata.e.data[0] != edata.e.data[i])
+			if (pdata[0] != pdata[i])
 				goto not_all_0or1;
 		}
 		return FALSE;
 	}
 
 not_all_0or1:
-	memcpy (buf, edata.e.data, edata.e.size);
+	memcpy (buf, pdata, edata.e.size);
 	*length = edata.e.size;
 	return TRUE;
 }
@@ -1410,7 +1416,7 @@ nmp_utils_sysctl_open_netdir (int ifindex,
 				return -1;
 		}
 
-		nm_assert (nm_utils_is_valid_iface_name (ifname, NULL));
+		nm_assert (nm_utils_ifname_valid_kernel (ifname, NULL));
 
 		if (g_strlcpy (&sysdir[NM_STRLEN (SYS_CLASS_NET)], ifname, IFNAMSIZ) >= IFNAMSIZ)
 			g_return_val_if_reached (-1);
diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c
index b93213ef..f831fe94 100644
--- a/src/platform/tests/test-common.c
+++ b/src/platform/tests/test-common.c
@@ -1175,7 +1175,7 @@ nmtstp_link_veth_add (NMPlatform *platform,
 	const NMPlatformLink *pllink = NULL;
 	gboolean success;
 
-	g_assert (nm_utils_is_valid_iface_name (name, NULL));
+	g_assert (nm_utils_ifname_valid_kernel (name, NULL));
 
 	external_command = nmtstp_run_command_check_external (external_command);
 
@@ -1204,7 +1204,7 @@ nmtstp_link_dummy_add (NMPlatform *platform,
 	const NMPlatformLink *pllink = NULL;
 	gboolean success;
 
-	g_assert (nm_utils_is_valid_iface_name (name, NULL));
+	g_assert (nm_utils_ifname_valid_kernel (name, NULL));
 
 	external_command = nmtstp_run_command_check_external (external_command);
 
@@ -1235,7 +1235,7 @@ nmtstp_link_gre_add (NMPlatform *platform,
 	char b2[INET_ADDRSTRLEN];
 	NMLinkType link_type;
 
-	g_assert (nm_utils_is_valid_iface_name (name, NULL));
+	g_assert (nm_utils_ifname_valid_kernel (name, NULL));
 
 	external_command = nmtstp_run_command_check_external (external_command);
 	link_type = lnk->is_tap ? NM_LINK_TYPE_GRETAP : NM_LINK_TYPE_GRE;
@@ -1287,7 +1287,7 @@ nmtstp_link_ip6tnl_add (NMPlatform *platform,
 	gboolean encap_ignore;
 	gboolean tclass_inherit;
 
-	g_assert (nm_utils_is_valid_iface_name (name, NULL));
+	g_assert (nm_utils_ifname_valid_kernel (name, NULL));
 	g_assert (!lnk->is_gre);
 
 	external_command = nmtstp_run_command_check_external (external_command);
@@ -1348,7 +1348,7 @@ nmtstp_link_ip6gre_add (NMPlatform *platform,
 	char tclass[20];
 	gboolean tclass_inherit;
 
-	g_assert (nm_utils_is_valid_iface_name (name, NULL));
+	g_assert (nm_utils_ifname_valid_kernel (name, NULL));
 	g_assert (lnk->is_gre);
 
 	external_command = nmtstp_run_command_check_external (external_command);
@@ -1397,7 +1397,7 @@ nmtstp_link_ipip_add (NMPlatform *platform,
 	char b1[INET_ADDRSTRLEN];
 	char b2[INET_ADDRSTRLEN];
 
-	g_assert (nm_utils_is_valid_iface_name (name, NULL));
+	g_assert (nm_utils_ifname_valid_kernel (name, NULL));
 
 	external_command = nmtstp_run_command_check_external (external_command);
 
@@ -1438,7 +1438,7 @@ nmtstp_link_macvlan_add (NMPlatform *platform,
 	gboolean success;
 	NMLinkType link_type;
 
-	g_assert (nm_utils_is_valid_iface_name (name, NULL));
+	g_assert (nm_utils_ifname_valid_kernel (name, NULL));
 
 	external_command = nmtstp_run_command_check_external (external_command);
 
@@ -1486,7 +1486,7 @@ nmtstp_link_sit_add (NMPlatform *platform,
 	char b1[INET_ADDRSTRLEN];
 	char b2[INET_ADDRSTRLEN];
 
-	g_assert (nm_utils_is_valid_iface_name (name, NULL));
+	g_assert (nm_utils_ifname_valid_kernel (name, NULL));
 
 	external_command = nmtstp_run_command_check_external (external_command);
 
@@ -1532,7 +1532,7 @@ nmtstp_link_tun_add (NMPlatform *platform,
 	int err;
 	int r;
 
-	g_assert (nm_utils_is_valid_iface_name (name, NULL));
+	g_assert (nm_utils_ifname_valid_kernel (name, NULL));
 	g_assert (lnk);
 	g_assert (NM_IN_SET (lnk->type, IFF_TUN, IFF_TAP));
 	g_assert (!out_fd || *out_fd == -1);
@@ -1595,7 +1595,7 @@ nmtstp_link_vxlan_add (NMPlatform *platform,
 	int err;
 	int r;
 
-	g_assert (nm_utils_is_valid_iface_name (name, NULL));
+	g_assert (nm_utils_ifname_valid_kernel (name, NULL));
 
 	external_command = nmtstp_run_command_check_external (external_command);
 
@@ -1684,7 +1684,7 @@ nmtstp_link_get_typed (NMPlatform *platform,
 			g_assert_cmpstr (name, ==, pllink->name);
 	}
 
-	g_assert (!name || nm_utils_is_valid_iface_name (name, NULL));
+	g_assert (!name || nm_utils_ifname_valid_kernel (name, NULL));
 
 	if (pllink && link_type != NM_LINK_TYPE_NONE)
 		g_assert_cmpint (pllink->type, ==, link_type);
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index a1d3236e..e01f7344 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -399,7 +399,7 @@ make_connection_setting (const char *file,
 	if (v) {
 		GError *error = NULL;
 
-		if (nm_utils_is_valid_iface_name (v, &error)) {
+		if (nm_utils_ifname_valid_kernel (v, &error)) {
 			g_object_set (s_con,
 			              NM_SETTING_CONNECTION_INTERFACE_NAME, v,
 			              NULL);
@@ -1692,7 +1692,7 @@ make_ip4_setting (shvarFile *ifcfg,
 
 	g_object_set (s_ip4,
 	              NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCP_SEND_HOSTNAME", TRUE),
-	              NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, svGetValueInt64 (ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0),
+	              NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, (int) svGetValueInt64 (ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0),
 	              NULL);
 
 	nm_clear_g_free (&value);
@@ -2159,9 +2159,11 @@ make_ip6_setting (shvarFile *ifcfg,
 	if (v)
 		g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, v, NULL);
 
-	g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME,
-	              svGetValueBoolean (ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), NULL);
-
+	g_object_set (s_ip6,
+	              NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE),
+	              NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, (int) svGetValueInt64 (ifcfg, "IPV6_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0),
+	              NM_SETTING_IP6_CONFIG_RA_TIMEOUT, (int) svGetValueInt64 (ifcfg, "IPV6_RA_TIMEOUT", 10, 0, G_MAXINT32, 0),
+	              NULL);
 
 	i64 = svGetValueInt64 (ifcfg, "DHCPV6_HOSTNAME_FLAGS", 10, 0, G_MAXUINT32, -1);
 	if (i64 > -1) {
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index d33845c2..f328cc8f 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -2748,30 +2748,6 @@ write_ip4_aliases (NMConnection *connection, const char *base_ifcfg_path)
 	}
 }
 
-static void
-write_ip6_setting_dhcp_hostname (NMSettingIPConfig *s_ip6, shvarFile *ifcfg)
-{
-	NMDhcpHostnameFlags flags;
-	const char *hostname;
-
-	hostname = nm_setting_ip_config_get_dhcp_hostname (s_ip6);
-	svSetValueStr (ifcfg, "DHCPV6_HOSTNAME", hostname);
-
-	/* Missing DHCPV6_SEND_HOSTNAME means TRUE, and we prefer not write it
-	 * explicitly in that case, because it is NM-specific variable
-	 */
-	if (nm_setting_ip_config_get_dhcp_send_hostname (s_ip6))
-		svUnsetValue (ifcfg, "DHCPV6_SEND_HOSTNAME");
-	else
-		svSetValueStr (ifcfg, "DHCPV6_SEND_HOSTNAME", "no");
-
-	flags = nm_setting_ip_config_get_dhcp_hostname_flags (s_ip6);
-	svSetValueInt64_cond (ifcfg,
-	                      "DHCPV6_HOSTNAME_FLAGS",
-	                      flags != NM_DHCP_HOSTNAME_FLAG_NONE,
-	                      flags);
-}
-
 static gboolean
 write_ip6_setting (NMConnection *connection,
                    shvarFile *ifcfg,
@@ -2789,6 +2765,9 @@ write_ip6_setting (NMConnection *connection,
 	NMIPRouteTableSyncMode route_table;
 	GString *ip_str1, *ip_str2, *ip_ptr;
 	NMSettingIP6ConfigAddrGenMode addr_gen_mode;
+	NMDhcpHostnameFlags flags;
+	const char *hostname;
+	int timeout;
 
 	NM_SET_OUT (out_route6_content, NULL);
 
@@ -2855,7 +2834,34 @@ write_ip6_setting (NMConnection *connection,
 	svSetValueStr (ifcfg, "DHCPV6_IAID",
 	               nm_setting_ip_config_get_dhcp_iaid (s_ip6));
 
-	write_ip6_setting_dhcp_hostname (s_ip6, ifcfg);
+	hostname = nm_setting_ip_config_get_dhcp_hostname (s_ip6);
+	svSetValueStr (ifcfg, "DHCPV6_HOSTNAME", hostname);
+
+	/* Missing DHCPV6_SEND_HOSTNAME means TRUE, and we prefer not write it
+	 * explicitly in that case, because it is NM-specific variable
+	 */
+	if (nm_setting_ip_config_get_dhcp_send_hostname (s_ip6))
+		svUnsetValue (ifcfg, "DHCPV6_SEND_HOSTNAME");
+	else
+		svSetValueStr (ifcfg, "DHCPV6_SEND_HOSTNAME", "no");
+
+	timeout = nm_setting_ip6_config_get_ra_timeout (NM_SETTING_IP6_CONFIG (s_ip6));
+	svSetValueInt64_cond (ifcfg,
+	                      "IPV6_RA_TIMEOUT",
+	                      timeout != 0,
+	                      timeout);
+
+	timeout = nm_setting_ip_config_get_dhcp_timeout (s_ip6);
+	svSetValueInt64_cond (ifcfg,
+	                      "IPV6_DHCP_TIMEOUT",
+	                      timeout != 0,
+	                      timeout);
+
+	flags = nm_setting_ip_config_get_dhcp_hostname_flags (s_ip6);
+	svSetValueInt64_cond (ifcfg,
+	                      "DHCPV6_HOSTNAME_FLAGS",
+	                      flags != NM_DHCP_HOSTNAME_FLAG_NONE,
+	                      flags);
 
 	/* Write out IP addresses */
 	num = nm_setting_ip_config_get_num_addresses (s_ip6);
diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
index 6ef29311..c19da11c 100644
--- a/src/supplicant/nm-supplicant-interface.c
+++ b/src/supplicant/nm-supplicant-interface.c
@@ -3184,5 +3184,5 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
 	                  G_SIGNAL_RUN_LAST,
 	                  0,
 	                  NULL, NULL, NULL,
-	                  G_TYPE_NONE, 1, G_TYPE_VARIANT);
+	                  G_TYPE_NONE, 1, G_TYPE_STRING);
 }