summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/NetworkManagerUtils.c7
-rw-r--r--src/devices/nm-device-ethernet.c51
-rw-r--r--src/devices/nm-device-infiniband.c4
-rw-r--r--src/devices/nm-device-ip-tunnel.c4
-rw-r--r--src/devices/nm-device-macsec.c23
-rw-r--r--src/devices/nm-device-macvlan.c30
-rw-r--r--src/devices/nm-device-private.h6
-rw-r--r--src/devices/nm-device-vlan.c23
-rw-r--r--src/devices/nm-device-wireguard.c2
-rw-r--r--src/devices/nm-device.c122
-rw-r--r--src/devices/nm-device.h8
-rw-r--r--src/devices/ovs/nm-device-ovs-port.c5
-rw-r--r--src/devices/ovs/nm-ovsdb.c90
-rw-r--r--src/devices/ovs/nm-ovsdb.h1
-rw-r--r--src/devices/wifi/nm-device-iwd.c4
-rw-r--r--src/devices/wifi/nm-device-wifi-p2p.c4
-rw-r--r--src/devices/wifi/nm-device-wifi.c4
-rw-r--r--src/devices/wwan/nm-modem.c4
-rw-r--r--src/devices/wwan/nm-modem.h2
-rw-r--r--src/dhcp/nm-dhcp-nettools.c11
-rw-r--r--src/dhcp/nm-dhcp-options.c1
-rw-r--r--src/dhcp/nm-dhcp-options.h1
-rw-r--r--src/dhcp/nm-dhcp-systemd.c9
-rw-r--r--src/initrd/nmi-cmdline-reader.c113
-rw-r--r--src/initrd/tests/test-cmdline-reader.c163
-rw-r--r--src/ndisc/nm-lndp-ndisc.c13
-rw-r--r--src/ndisc/nm-ndisc.c8
-rw-r--r--src/ndisc/nm-ndisc.h4
-rw-r--r--src/nm-iface-helper.c17
-rw-r--r--src/nm-manager.c6
-rw-r--r--src/platform/nm-platform.c48
-rw-r--r--src/platform/nm-platform.h6
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c5
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c43
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c6
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h1
36 files changed, 681 insertions, 168 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 1773405d..c33f36ec 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -359,11 +359,12 @@ check_ip6_method (NMConnection *orig,
 		 */
 		allow = TRUE;
 	} else if (   NM_IN_STRSET (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL,
+	                                             NM_SETTING_IP6_CONFIG_METHOD_DISABLED,
 	                                             NM_SETTING_IP6_CONFIG_METHOD_AUTO)
 	           && nm_streq0 (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
-		/* If the generated connection method is 'link-local' or 'auto' and the candidate
-		 * method is 'ignore' we can take the connection, because NM didn't simply take care
-		 * of IPv6.
+		/* If the generated connection method is 'link-local', disabled' or 'auto' and
+		 * the candidate method is 'ignore' we can take the connection, because NM didn't
+		 * simply take care of IPv6.
 		 */
 		allow = TRUE;
 	}
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index 8571e526..b2b05883 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -87,6 +87,7 @@ typedef enum {
 
 typedef struct _NMDeviceEthernetPrivate {
 	guint32             speed;
+	gulong              carrier_id;
 
 	Supplicant          supplicant;
 	guint               supplicant_timeout_id;
@@ -948,7 +949,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 }
 
 static NMActStageReturn
-nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *out_failure_reason)
+supplicant_check_secrets_needed (NMDeviceEthernet *self, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMConnection *connection;
@@ -957,7 +958,6 @@ nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *out_failure
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 
 	connection = nm_device_get_applied_connection (NM_DEVICE (self));
-
 	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
 	security = nm_connection_get_setting_802_1x (connection);
@@ -996,6 +996,44 @@ nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *out_failure
 	return ret;
 }
 
+static void
+carrier_changed (NMSupplicantInterface *iface,
+                 GParamSpec *pspec,
+                 NMDeviceEthernet *self)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	NMDeviceStateReason reason;
+	NMActStageReturn ret;
+
+	if (nm_device_has_carrier (NM_DEVICE (self))) {
+		_LOGD (LOGD_DEVICE | LOGD_ETHER, "got carrier, initializing supplicant");
+		nm_clear_g_signal_handler (self, &priv->carrier_id);
+		ret = supplicant_check_secrets_needed (self, &reason);
+		if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
+			nm_device_state_changed (NM_DEVICE (self),
+			                         NM_DEVICE_STATE_FAILED,
+			                         reason);
+		}
+	}
+}
+
+static NMActStageReturn
+nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *out_failure_reason)
+{
+	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+	if (!nm_device_has_carrier (NM_DEVICE (self))) {
+		_LOGD (LOGD_DEVICE | LOGD_ETHER, "delay supplicant initialization until carrier goes up");
+		priv->carrier_id = g_signal_connect (self,
+		                                     "notify::" NM_DEVICE_CARRIER,
+		                                     G_CALLBACK (carrier_changed),
+		                                     self);
+		return NM_ACT_STAGE_RETURN_POSTPONE;
+	}
+
+	return supplicant_check_secrets_needed (self, out_failure_reason);
+}
+
 /*****************************************************************************/
 /* PPPoE */
 
@@ -1393,13 +1431,15 @@ act_stage3_ip_config_start (NMDevice *device,
 }
 
 static guint32
-get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
+get_configured_mtu (NMDevice *device,
+                    NMDeviceMtuSource *out_source,
+                    gboolean *out_force)
 {
 	/* 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_source);
+	return nm_device_get_configured_mtu_for_wired (device, out_source, out_force);
 }
 
 static void
@@ -1411,6 +1451,7 @@ deactivate (NMDevice *device)
 	GError *error = NULL;
 
 	nm_clear_g_source (&priv->pppoe_wait_id);
+	nm_clear_g_signal_handler (self, &priv->carrier_id);
 
 	if (priv->ppp_manager) {
 		nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL, NULL);
@@ -1727,6 +1768,8 @@ dispose (GObject *object)
 
 	nm_clear_g_source (&priv->dcb_timeout_id);
 
+	nm_clear_g_signal_handler (self, &priv->carrier_id);
+
 	G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object);
 }
 
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index 89db2af2..e39329a9 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -116,7 +116,9 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 }
 
 static guint32
-get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
+get_configured_mtu (NMDevice *device,
+                    NMDeviceMtuSource *out_source,
+                    gboolean *out_force)
 {
 	return nm_device_get_configured_mtu_from_connection (device,
 	                                                     NM_TYPE_SETTING_INFINIBAND,
diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c
index ede4487e..f070dd5f 100644
--- a/src/devices/nm-device-ip-tunnel.c
+++ b/src/devices/nm-device-ip-tunnel.c
@@ -854,7 +854,9 @@ create_and_realize (NMDevice *device,
 }
 
 static guint32
-get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
+get_configured_mtu (NMDevice *device,
+                    NMDeviceMtuSource *out_source,
+                    gboolean *out_force)
 {
 	return nm_device_get_configured_mtu_from_connection (device,
 	                                                     NM_TYPE_SETTING_IP_TUNNEL,
diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c
index e3e3a895..13798f5e 100644
--- a/src/devices/nm-device-macsec.c
+++ b/src/devices/nm-device-macsec.c
@@ -66,6 +66,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceMacsec,
 typedef struct {
 	NMPlatformLnkMacsec props;
 	gulong parent_state_id;
+	gulong parent_mtu_id;
 	Supplicant supplicant;
 	guint supplicant_timeout_id;
 	NMActRequestGetSecretsCallId *macsec_secrets_id;
@@ -114,6 +115,17 @@ parent_state_changed (NMDevice *parent,
 }
 
 static void
+parent_mtu_maybe_changed (NMDevice *parent,
+                          GParamSpec *pspec,
+                          gpointer user_data)
+{
+	/* the MTU of a MACsec device is limited by the parent's MTU.
+	 *
+	 * When the parent's MTU changes, try to re-set the MTU. */
+	nm_device_commit_mtu (user_data);
+}
+
+static void
 parent_changed_notify (NMDevice *device,
                        int old_ifindex,
                        NMDevice *old_parent,
@@ -133,12 +145,16 @@ parent_changed_notify (NMDevice *device,
 	 *  because NMDevice's dispose() will unset the parent, which in turn calls
 	 *  parent_changed_notify(). */
 	nm_clear_g_signal_handler (old_parent, &priv->parent_state_id);
+	nm_clear_g_signal_handler (old_parent, &priv->parent_mtu_id);
 
 	if (new_parent) {
 		priv->parent_state_id = g_signal_connect (new_parent,
 		                                          NM_DEVICE_STATE_CHANGED,
 		                                          G_CALLBACK (parent_state_changed),
 		                                          device);
+		priv->parent_mtu_id = g_signal_connect (new_parent, "notify::" NM_DEVICE_MTU,
+		                                        G_CALLBACK (parent_mtu_maybe_changed), device);
+
 
 		/* Set parent-dependent unmanaged flag */
 		nm_device_set_unmanaged_by_flags (device,
@@ -791,11 +807,15 @@ static void
 dispose (GObject *object)
 {
 	NMDeviceMacsec *self = NM_DEVICE_MACSEC (object);
+	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
 
 	macsec_secrets_cancel (self);
 	supplicant_interface_release (self);
 
 	G_OBJECT_CLASS (nm_device_macsec_parent_class)->dispose (object);
+
+	nm_assert (priv->parent_state_id == 0);
+	nm_assert (priv->parent_mtu_id == 0);
 }
 
 static const NMDBusInterfaceInfoExtended interface_info_device_macsec = {
@@ -838,6 +858,7 @@ nm_device_macsec_class_init (NMDeviceMacsecClass *klass)
 	device_class->connection_type_supported = NM_SETTING_MACSEC_SETTING_NAME;
 	device_class->connection_type_check_compatible = NM_SETTING_MACSEC_SETTING_NAME;
 	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_MACSEC);
+	device_class->mtu_parent_delta = 32;
 
 	device_class->act_stage2_config = act_stage2_config;
 	device_class->create_and_realize = create_and_realize;
@@ -847,7 +868,7 @@ nm_device_macsec_class_init (NMDeviceMacsecClass *klass)
 	device_class->is_available = is_available;
 	device_class->parent_changed_notify = parent_changed_notify;
 	device_class->state_changed = device_state_changed;
-	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
+	device_class->get_configured_mtu = nm_device_get_configured_mtu_wired_parent;
 
 	obj_properties[PROP_SCI] =
 	    g_param_spec_uint64 (NM_DEVICE_MACSEC_SCI, "", "",
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index ee8b9974..964e0f61 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -48,6 +48,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceMacvlan,
 
 typedef struct {
 	gulong parent_state_id;
+	gulong parent_mtu_id;
 	NMPlatformLnkMacvlan props;
 } NMDeviceMacvlanPrivate;
 
@@ -135,6 +136,17 @@ parent_state_changed (NMDevice *parent,
 }
 
 static void
+parent_mtu_maybe_changed (NMDevice *parent,
+                          GParamSpec *pspec,
+                          gpointer user_data)
+{
+	/* the MTU of a macvlan/macvtap device is limited by the parent's MTU.
+	 *
+	 * When the parent's MTU changes, try to re-set the MTU. */
+	nm_device_commit_mtu (user_data);
+}
+
+static void
 parent_changed_notify (NMDevice *device,
                        int old_ifindex,
                        NMDevice *old_parent,
@@ -150,12 +162,15 @@ parent_changed_notify (NMDevice *device,
 	 *  because NMDevice's dispose() will unset the parent, which in turn calls
 	 *  parent_changed_notify(). */
 	nm_clear_g_signal_handler (old_parent, &priv->parent_state_id);
+	nm_clear_g_signal_handler (old_parent, &priv->parent_mtu_id);
 
 	if (new_parent) {
 		priv->parent_state_id = g_signal_connect (new_parent,
 		                                          NM_DEVICE_STATE_CHANGED,
 		                                          G_CALLBACK (parent_state_changed),
 		                                          device);
+		priv->parent_mtu_id = g_signal_connect (new_parent, "notify::" NM_DEVICE_MTU,
+		                                        G_CALLBACK (parent_mtu_maybe_changed), device);
 
 		/* Set parent-dependent unmanaged flag */
 		nm_device_set_unmanaged_by_flags (device,
@@ -476,6 +491,17 @@ nm_device_macvlan_init (NMDeviceMacvlan *self)
 {
 }
 
+static void
+dispose (GObject *object)
+{
+	NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (object);
+
+	G_OBJECT_CLASS (nm_device_macvlan_parent_class)->dispose (object);
+
+	nm_assert (priv->parent_state_id == 0);
+	nm_assert (priv->parent_mtu_id == 0);
+}
+
 static const NMDBusInterfaceInfoExtended interface_info_device_macvlan = {
 	.parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT (
 		NM_DBUS_INTERFACE_DEVICE_MACVLAN,
@@ -499,6 +525,7 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 	NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
+	object_class->dispose = dispose;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
@@ -507,13 +534,14 @@ nm_device_macvlan_class_init (NMDeviceMacvlanClass *klass)
 	device_class->connection_type_supported = NM_SETTING_MACVLAN_SETTING_NAME;
 	device_class->connection_type_check_compatible = NM_SETTING_MACVLAN_SETTING_NAME;
 	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_MACVLAN, NM_LINK_TYPE_MACVTAP);
+	device_class->mtu_parent_delta = 0;
 
 	device_class->act_stage1_prepare = act_stage1_prepare;
 	device_class->check_connection_compatible = check_connection_compatible;
 	device_class->complete_connection = complete_connection;
 	device_class->create_and_realize = create_and_realize;
 	device_class->get_generic_capabilities = get_generic_capabilities;
-	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
+	device_class->get_configured_mtu = nm_device_get_configured_mtu_wired_parent;
 	device_class->is_available = is_available;
 	device_class->link_changed = link_changed;
 	device_class->parent_changed_notify = parent_changed_notify;
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index 0a414af9..83feb130 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -174,7 +174,11 @@ 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);
+guint32 nm_device_get_configured_mtu_for_wired (NMDevice *self, NMDeviceMtuSource *out_source, gboolean *out_force);
+
+guint32 nm_device_get_configured_mtu_wired_parent (NMDevice *self,
+                                                   NMDeviceMtuSource *out_source,
+                                                   gboolean *out_force);
 
 void nm_device_commit_mtu (NMDevice *self);
 
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index 0467a6e4..f8f6a597 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -523,26 +523,6 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 	return ret;
 }
 
-static guint32
-get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source)
-{
-	guint32 mtu = 0;
-	int ifindex;
-
-	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) {
-		mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)), ifindex);
-		*out_source = NM_DEVICE_MTU_SOURCE_PARENT;
-	}
-
-	return mtu;
-}
-
 /*****************************************************************************/
 
 static void
@@ -598,13 +578,14 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
 	device_class->connection_type_supported = NM_SETTING_VLAN_SETTING_NAME;
 	device_class->connection_type_check_compatible = NM_SETTING_VLAN_SETTING_NAME;
 	device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_VLAN);
+	device_class->mtu_parent_delta = 0; /* VLANs can have the same MTU of parent */
 
 	device_class->create_and_realize = create_and_realize;
 	device_class->link_changed = link_changed;
 	device_class->unrealize_notify = unrealize_notify;
 	device_class->get_generic_capabilities = get_generic_capabilities;
 	device_class->act_stage1_prepare = act_stage1_prepare;
-	device_class->get_configured_mtu = get_configured_mtu;
+	device_class->get_configured_mtu = nm_device_get_configured_mtu_wired_parent;
 	device_class->is_available = is_available;
 	device_class->parent_changed_notify = parent_changed_notify;
 
diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c
index e3b8e8b4..5d640b62 100644
--- a/src/devices/nm-device-wireguard.c
+++ b/src/devices/nm-device-wireguard.c
@@ -1714,7 +1714,7 @@ act_stage3_ip_config_start (NMDevice *device,
 }
 
 static guint32
-get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
+get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source, gboolean *out_force)
 {
 	/* When "MTU" for `wg-quick up` is unset, it calls `ip route get` for
 	 * each configured endpoint, to determine the suitable MTU how to reach
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 67e23b8f..b3d97af7 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -9274,13 +9274,59 @@ nm_device_get_configured_mtu_from_connection (NMDevice *self,
 }
 
 guint32
-nm_device_get_configured_mtu_for_wired (NMDevice *self, NMDeviceMtuSource *out_source)
+nm_device_get_configured_mtu_for_wired (NMDevice *self,
+                                        NMDeviceMtuSource *out_source,
+                                        gboolean *out_force)
 {
 	return nm_device_get_configured_mtu_from_connection (self,
 	                                                     NM_TYPE_SETTING_WIRED,
 	                                                     out_source);
 }
 
+guint32
+nm_device_get_configured_mtu_wired_parent (NMDevice *self,
+                                           NMDeviceMtuSource *out_source,
+                                           gboolean *out_force)
+{
+	guint32 mtu = 0;
+	guint32 parent_mtu = 0;
+	int ifindex;
+
+	ifindex = nm_device_parent_get_ifindex (self);
+	if (ifindex > 0) {
+		parent_mtu = nm_platform_link_get_mtu (nm_device_get_platform (self), ifindex);
+		if (parent_mtu >= NM_DEVICE_GET_CLASS (self)->mtu_parent_delta)
+			parent_mtu -= NM_DEVICE_GET_CLASS (self)->mtu_parent_delta;
+		else
+			parent_mtu = 0;
+	}
+
+	mtu = nm_device_get_configured_mtu_for_wired (self, out_source, NULL);
+
+	if (parent_mtu && mtu > parent_mtu) {
+		/* Trying to set a MTU that is out of range from configuration:
+		 * fall back to the parent MTU and set force flag so that it
+		 * overrides an MTU with higher priority already configured.
+		 */
+		 *out_source = NM_DEVICE_MTU_SOURCE_PARENT;
+		 *out_force = TRUE;
+		 return parent_mtu;
+	}
+
+	if (*out_source != NM_DEVICE_MTU_SOURCE_NONE) {
+		nm_assert (mtu > 0);
+		return mtu;
+	}
+
+	/* Inherit the MTU from parent device, if any */
+	if (parent_mtu) {
+		mtu = parent_mtu;
+		*out_source = NM_DEVICE_MTU_SOURCE_PARENT;
+	}
+
+	return mtu;
+}
+
 /*****************************************************************************/
 
 static void
@@ -9332,15 +9378,34 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 
 	{
 		guint32 mtu = 0;
+		gboolean force = FALSE;
 
-		/* preferably, get the MTU from explicit user-configuration.
-		 * Only if that fails, look at the current @config (which contains
-		 * MTUs from DHCP/PPP) or maybe fallback to a device-specific MTU. */
+		/* We take the MTU from various sources: (in order of increasing
+		 * priority) parent link, IP configuration (which contains the
+		 * MTU from DHCP/PPP), connection profile.
+		 *
+		 * We could just compare it with the platform MTU and apply it
+		 * when different, but this would revert at random times manual
+		 * changes done by the user with the MTU from the connection.
+		 *
+		 * Instead, we remember the source of the currently configured
+		 * MTU and apply the new one only when the new source has a
+		 * higher priority, so that we don't set a MTU from same source
+		 * multiple times. An exception to this is for the PARENT
+		 * source, since we need to keep tracking the parent MTU when it
+		 * changes.
+		 *
+		 * The subclass can set the @force argument to TRUE to signal that the
+		 * returned MTU should be applied even if it has a lower priority. This
+		 * is useful when the value from a lower source should
+		 * preempt the one from higher ones.
+		 */
 
 		if (NM_DEVICE_GET_CLASS (self)->get_configured_mtu)
-			mtu = NM_DEVICE_GET_CLASS (self)->get_configured_mtu (self, &source);
+			mtu = NM_DEVICE_GET_CLASS (self)->get_configured_mtu (self, &source, &force);
 
 		if (   config
+		    && !force
 		    && source < NM_DEVICE_MTU_SOURCE_IP_CONFIG
 		    && nm_ip4_config_get_mtu (config)) {
 			mtu = nm_ip4_config_get_mtu (config);
@@ -9349,14 +9414,16 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 
 		if (mtu != 0) {
 			_LOGT (LOGD_DEVICE,
-			       "mtu: value %u from source '%s' (%u), current source '%s' (%u)",
+			       "mtu: value %u from source '%s' (%u), current source '%s' (%u)%s",
 			       (guint) mtu,
 			       mtu_source_to_str (source), (guint) source,
-			       mtu_source_to_str (priv->mtu_source), (guint) priv->mtu_source);
+			       mtu_source_to_str (priv->mtu_source), (guint) priv->mtu_source,
+			       force ? " (forced)" : "");
 		}
 
 		if (   mtu != 0
-		    && (   source > priv->mtu_source
+		    && (   force
+		        || source > priv->mtu_source
 		        || (priv->mtu_source == NM_DEVICE_MTU_SOURCE_PARENT && source == priv->mtu_source)))
 			mtu_desired = mtu;
 		else {
@@ -9600,6 +9667,18 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 	if (changed & NM_NDISC_CONFIG_HOP_LIMIT)
 		nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (nm_device_get_platform (self), nm_device_get_ip_iface (self), rdata->hop_limit);
 
+	if (changed & NM_NDISC_CONFIG_REACHABLE_TIME) {
+		nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time (nm_device_get_platform (self),
+		                                                     nm_device_get_ip_iface (self),
+		                                                     rdata->reachable_time_ms);
+	}
+
+	if (changed & NM_NDISC_CONFIG_RETRANS_TIMER) {
+		nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time (nm_device_get_platform (self),
+		                                                   nm_device_get_ip_iface (self),
+		                                                   rdata->retrans_timer_ms);
+	}
+
 	if (changed & NM_NDISC_CONFIG_MTU) {
 		if (priv->ip6_mtu != rdata->mtu) {
 			_LOGD (LOGD_DEVICE, "mtu: set IPv6 MTU to %u", (guint) rdata->mtu);
@@ -9661,24 +9740,11 @@ addrconf6_start_with_link_ready (NMDevice *self)
 	if (!ip_config_merge_and_apply (self, AF_INET6, TRUE))
 		_LOGW (LOGD_IP6, "failed to apply manual IPv6 configuration");
 
-	/* FIXME: These sysctls would probably be better set by the lndp ndisc itself. */
-	switch (nm_ndisc_get_node_type (priv->ndisc)) {
-	case NM_NDISC_NODE_TYPE_HOST:
-		/* Accepting prefixes from discovered routers. */
-		nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "1");
-		nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_defrtr", "0");
-		nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_pinfo", "0");
-		nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_rtr_pref", "0");
-		break;
-	case NM_NDISC_NODE_TYPE_ROUTER:
-		/* We're the router. */
+	if (nm_ndisc_get_node_type (priv->ndisc) == NM_NDISC_NODE_TYPE_ROUTER) {
 		nm_device_sysctl_ip_conf_set (self, AF_INET6, "forwarding", "1");
 		nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL);
 		priv->needs_ip6_subnet = TRUE;
 		g_signal_emit (self, signals[IP6_SUBNET_NEEDED], 0);
-		break;
-	default:
-		g_assert_not_reached ();
 	}
 
 	priv->ndisc_changed_id = g_signal_connect (priv->ndisc,
@@ -9789,9 +9855,6 @@ save_ip6_properties (NMDevice *self)
 {
 	static const char *const ip6_properties_to_save[] = {
 		"accept_ra",
-		"accept_ra_defrtr",
-		"accept_ra_pinfo",
-		"accept_ra_rtr_pref",
 		"forwarding",
 		"disable_ipv6",
 		"hop_limit",
@@ -10116,6 +10179,7 @@ act_stage3_ip_config_start (NMDevice *self,
 			set_nm_ipv6ll (self, TRUE);
 
 		/* Re-enable IPv6 on the interface */
+		nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "0");
 		set_disable_ipv6 (self, "0");
 
 		/* Synchronize external IPv6 configuration with kernel, since
@@ -12123,7 +12187,7 @@ _carrier_wait_check_queued_act_request (NMDevice *self)
 		_LOGD (LOGD_DEVICE, "Cancel queued activation request as we have no carrier after timeout");
 		_clear_queued_act_request (priv,
 		                           NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED);
-	} else {
+	} else if (priv->state == NM_DEVICE_STATE_DISCONNECTED) {
 		gs_unref_object NMActRequest *queued_req = NULL;
 
 		_LOGD (LOGD_DEVICE, "Activate queued activation request as we now have carrier");
@@ -14744,7 +14808,6 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 	/* Turn off kernel IPv6 */
 	if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) {
 		set_disable_ipv6 (self, "1");
-		nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "0");
 		nm_device_sysctl_ip_conf_set (self, AF_INET6, "use_tempaddr", "0");
 	}
 
@@ -14802,6 +14865,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 	}
 
 	priv->mtu_source = NM_DEVICE_MTU_SOURCE_NONE;
+	priv->ip6_mtu = 0;
 	if (priv->mtu_initial || priv->ip6_mtu_initial) {
 		ifindex = nm_device_get_ip_ifindex (self);
 
@@ -15035,9 +15099,7 @@ ip6_managed_setup (NMDevice *self)
 {
 	set_nm_ipv6ll (self, TRUE);
 	set_disable_ipv6 (self, "1");
-	nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_defrtr", "0");
-	nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_pinfo", "0");
-	nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_rtr_pref", "0");
+	nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "0");
 	nm_device_sysctl_ip_conf_set (self, AF_INET6, "use_tempaddr", "0");
 	nm_device_sysctl_ip_conf_set (self, AF_INET6, "forwarding", "0");
 }
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index ae6aab39..412a57a0 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -239,6 +239,10 @@ typedef struct _NMDeviceClass {
 
 	const NMLinkType *link_types;
 
+	/* if the device MTU is set based on parent's one, this specifies
+	 * a delta in the MTU allowed value due the encapsulation overhead */
+	guint16 mtu_parent_delta;
+
 	/* Whether the device type is a master-type. This depends purely on the
 	 * type (NMDeviceClass), not the actual device instance. */
 	bool is_master:1;
@@ -335,7 +339,9 @@ typedef struct _NMDeviceClass {
 	                                  NMSettingsConnection *sett_conn,
 	                                  char **specific_object);
 
-	guint32     (*get_configured_mtu) (NMDevice *self, NMDeviceMtuSource *out_source);
+	guint32     (*get_configured_mtu) (NMDevice *self,
+	                                   NMDeviceMtuSource *out_source,
+	                                   gboolean *out_force);
 
 	/* allow the subclass to overwrite the routing table. This is mainly useful
 	 * to change from partial mode (route-table=0) to full-sync mode (route-table=254). */
diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c
index 8a93a5a9..39e64b3f 100644
--- a/src/devices/ovs/nm-device-ovs-port.c
+++ b/src/devices/ovs/nm-device-ovs-port.c
@@ -102,6 +102,7 @@ enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection, gboo
 {
 	NMActiveConnection *ac_port = NULL;
 	NMActiveConnection *ac_bridge = NULL;
+	NMDevice *bridge_device;
 
 	if (!configure)
 		return TRUE;
@@ -111,10 +112,14 @@ enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection, gboo
 	if (!ac_bridge)
 		ac_bridge = ac_port;
 
+	bridge_device = nm_active_connection_get_device (ac_bridge);
+
 	nm_ovsdb_add_interface (nm_ovsdb_get (),
 	                        nm_active_connection_get_applied_connection (ac_bridge),
 	                        nm_device_get_applied_connection (device),
 	                        nm_device_get_applied_connection (slave),
+	                        bridge_device,
+	                        slave,
 	                        add_iface_cb, g_object_ref (slave));
 
 	return TRUE;
diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c
index e5de466c..c0e68db3 100644
--- a/src/devices/ovs/nm-ovsdb.c
+++ b/src/devices/ovs/nm-ovsdb.c
@@ -27,6 +27,7 @@
 #include "nm-glib-aux/nm-jansson.h"
 #include "nm-core-utils.h"
 #include "nm-core-internal.h"
+#include "devices/nm-device.h"
 
 /*****************************************************************************/
 
@@ -130,6 +131,8 @@ typedef struct {
 			NMConnection *bridge;
 			NMConnection *port;
 			NMConnection *interface;
+			NMDevice *bridge_device;
+			NMDevice *interface_device;
 		};
 	};
 } OvsdbMethodCall;
@@ -182,6 +185,7 @@ static void
 ovsdb_call_method (NMOvsdb *self, OvsdbCommand command,
                    const char *ifname,
                    NMConnection *bridge, NMConnection *port, NMConnection *interface,
+                   NMDevice *bridge_device, NMDevice *interface_device,
                    OvsdbMethodCallback callback, gpointer user_data)
 {
 	NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
@@ -204,6 +208,8 @@ ovsdb_call_method (NMOvsdb *self, OvsdbCommand command,
 		call->bridge = nm_simple_connection_new_clone (bridge);
 		call->port = nm_simple_connection_new_clone (port);
 		call->interface = nm_simple_connection_new_clone (interface);
+		call->bridge_device = g_object_ref (bridge_device);
+		call->interface_device = g_object_ref (interface_device);
 		break;
 	case OVSDB_DEL_INTERFACE:
 		call->ifname = g_strdup (ifname);
@@ -336,18 +342,33 @@ _set_port_interfaces (json_t *params, const char *ifname, json_t *new_interfaces
  * Returns an commands that adds new interface from a given connection.
  */
 static void
-_insert_interface (json_t *params, NMConnection *interface)
+_insert_interface (json_t *params, NMConnection *interface, NMDevice *interface_device)
 {
 	const char *type = NULL;
 	NMSettingOvsInterface *s_ovs_iface;
 	NMSettingOvsDpdk *s_ovs_dpdk;
 	NMSettingOvsPatch *s_ovs_patch;
 	json_t *options = json_array ();
+	gs_free char *cloned_mac = NULL;
+	gs_free_error GError *error = NULL;
+	json_t *row;
 
 	s_ovs_iface = nm_connection_get_setting_ovs_interface (interface);
 	if (s_ovs_iface)
 		type = nm_setting_ovs_interface_get_interface_type (s_ovs_iface);
 
+	if (!nm_device_hw_addr_get_cloned (interface_device,
+	                                   interface,
+	                                   FALSE,
+	                                   &cloned_mac,
+	                                   NULL,
+	                                   &error)) {
+		_LOGW ("Cannot determine cloned mac for OVS %s '%s': %s",
+		       "interface",
+		       nm_connection_get_interface_name (interface),
+		       error->message);
+	}
+
 	json_array_append_new (options, json_string ("map"));
 
 	s_ovs_dpdk = (NMSettingOvsDpdk *) nm_connection_get_setting (interface,
@@ -367,14 +388,22 @@ _insert_interface (json_t *params, NMConnection *interface)
 		json_array_append_new (options, json_array ());
 	}
 
+	row = json_pack ("{s:s, s:s, s:o, s:[s, [[s, s]]]}",
+	                 "name", nm_connection_get_interface_name (interface),
+	                 "type", type ?: "",
+	                 "options", options,
+	                 "external_ids", "map",
+	                 "NM.connection.uuid", nm_connection_get_uuid (interface));
+
+	if (cloned_mac)
+		json_object_set_new (row, "mac", json_string (cloned_mac));
+
 	json_array_append_new (params,
-		json_pack ("{s:s, s:s, s:{s:s, s:s, s:o, s:[s, [[s, s]]]}, s:s}",
-		           "op", "insert", "table", "Interface", "row",
-		           "name", nm_connection_get_interface_name (interface),
-		           "type", type ?: "",
-		           "options", options,
-		           "external_ids", "map", "NM.connection.uuid", nm_connection_get_uuid (interface),
-		           "uuid-name", "rowInterface"));
+	        json_pack ("{s:s, s:s, s:o, s:s}",
+	                   "op", "insert",
+	                   "table", "Interface",
+	                   "row", row,
+	                   "uuid-name", "rowInterface"));
 }
 
 /**
@@ -438,7 +467,7 @@ _insert_port (json_t *params, NMConnection *port, json_t *new_interfaces)
  * Returns an commands that adds new bridge from a given connection.
  */
 static void
-_insert_bridge (json_t *params, NMConnection *bridge, json_t *new_ports)
+_insert_bridge (json_t *params, NMConnection *bridge, NMDevice *bridge_device, json_t *new_ports)
 {
 	NMSettingOvsBridge *s_ovs_bridge;
 	const char *fail_mode = NULL;
@@ -447,9 +476,23 @@ _insert_bridge (json_t *params, NMConnection *bridge, json_t *new_ports)
 	gboolean stp_enable = FALSE;
 	const char *datapath_type = NULL;
 	json_t *row;
+	gs_free_error GError *error = NULL;
+	gs_free char *cloned_mac = NULL;
 
 	s_ovs_bridge = nm_connection_get_setting_ovs_bridge (bridge);
 
+	if (!nm_device_hw_addr_get_cloned (bridge_device,
+	                                   bridge,
+	                                   FALSE,
+	                                   &cloned_mac,
+	                                   NULL,
+	                                   &error)) {
+		_LOGW ("Cannot determine cloned mac for OVS %s '%s': %s",
+		       "bridge",
+		       nm_connection_get_interface_name (bridge),
+		       error->message);
+	}
+
 	row = json_object ();
 
 	if (s_ovs_bridge) {
@@ -477,6 +520,12 @@ _insert_bridge (json_t *params, NMConnection *bridge, json_t *new_ports)
 		json_pack ("[s, [[s, s]]]", "map",
 		           "NM.connection.uuid", nm_connection_get_uuid (bridge)));
 
+	if (cloned_mac) {
+		json_object_set_new (row, "other_config",
+			json_pack ("[s, [[s, s]]]", "map",
+			           "hwaddr", cloned_mac));
+	}
+
 	/* Create a new one. */
 	json_array_append_new (params,
 		json_pack ("{s:s, s:s, s:o, s:s}", "op", "insert", "table", "Bridge",
@@ -506,7 +555,8 @@ _inc_next_cfg (const char *db_uuid)
  */
 static void
 _add_interface (NMOvsdb *self, json_t *params,
-                NMConnection *bridge, NMConnection *port, NMConnection *interface)
+                NMConnection *bridge, NMConnection *port, NMConnection *interface,
+                NMDevice *bridge_device, NMDevice *interface_device)
 {
 	NMOvsdbPrivate *priv = NM_OVSDB_GET_PRIVATE (self);
 	GHashTableIter iter;
@@ -588,7 +638,7 @@ _add_interface (NMOvsdb *self, json_t *params,
 			_expect_ovs_bridges (params, priv->db_uuid, bridges);
 			json_array_append_new (new_bridges, json_pack ("[s, s]", "named-uuid", "rowBridge"));
 			_set_ovs_bridges (params, priv->db_uuid, new_bridges);
-			_insert_bridge (params, bridge, new_ports);
+			_insert_bridge (params, bridge, bridge_device, new_ports);
 		} else {
 			/* Bridge already exists. */
 			g_return_if_fail (ovs_bridge);
@@ -606,7 +656,7 @@ _add_interface (NMOvsdb *self, json_t *params,
 	}
 
 	if (!has_interface) {
-		_insert_interface (params, interface);
+		_insert_interface (params, interface, interface_device);
 		json_array_append_new (new_interfaces, json_pack ("[s, s]", "named-uuid", "rowInterface"));
 	}
 }
@@ -766,7 +816,8 @@ ovsdb_next_command (NMOvsdb *self)
 		json_array_append_new (params, json_string ("Open_vSwitch"));
 		json_array_append_new (params, _inc_next_cfg (priv->db_uuid));
 
-		_add_interface (self, params, call->bridge, call->port, call->interface);
+		_add_interface (self, params, call->bridge, call->port, call->interface,
+		                call->bridge_device, call->interface_device);
 
 		msg = json_pack ("{s:i, s:s, s:o}",
 		                 "id", call->id,
@@ -1107,7 +1158,7 @@ ovsdb_got_msg (NMOvsdb *self, json_t *msg)
 	OvsdbMethodCall *call = NULL;
 	OvsdbMethodCallback callback;
 	gpointer user_data;
-	GError *local = NULL;
+	gs_free_error GError *local = NULL;
 
 	if (json_unpack_ex (msg, &json_error, 0, "{s?:o, s?:s, s?:o, s?:o, s?:o}",
 	                    "id", &json_id,
@@ -1424,7 +1475,7 @@ ovsdb_try_connect (NMOvsdb *self)
 	/* Queue a monitor call before any other command, ensuring that we have an up
 	 * to date view of existing bridged that we need for add and remove ops. */
 	ovsdb_call_method (self, OVSDB_MONITOR, NULL,
-	                   NULL, NULL, NULL, _monitor_bridges_cb, NULL);
+	                   NULL, NULL, NULL, NULL, NULL, _monitor_bridges_cb, NULL);
 }
 
 /*****************************************************************************/
@@ -1465,6 +1516,7 @@ out:
 void
 nm_ovsdb_add_interface (NMOvsdb *self,
                         NMConnection *bridge, NMConnection *port, NMConnection *interface,
+                        NMDevice *bridge_device, NMDevice *interface_device,
                         NMOvsdbCallback callback, gpointer user_data)
 {
 	OvsdbCall *call;
@@ -1474,7 +1526,9 @@ nm_ovsdb_add_interface (NMOvsdb *self,
 	call->user_data = user_data;
 
 	ovsdb_call_method (self, OVSDB_ADD_INTERFACE, NULL,
-	                   bridge, port, interface, _transact_cb, call);
+	                   bridge, port, interface,
+	                   bridge_device, interface_device,
+	                   _transact_cb, call);
 }
 
 void
@@ -1488,7 +1542,7 @@ nm_ovsdb_del_interface (NMOvsdb *self, const char *ifname,
 	call->user_data = user_data;
 
 	ovsdb_call_method (self, OVSDB_DEL_INTERFACE, ifname,
-	                   NULL, NULL, NULL, _transact_cb, call);
+	                   NULL, NULL, NULL, NULL, NULL, _transact_cb, call);
 }
 
 /*****************************************************************************/
@@ -1505,6 +1559,8 @@ _clear_call (gpointer data)
 		g_clear_object (&call->bridge);
 		g_clear_object (&call->port);
 		g_clear_object (&call->interface);
+		g_clear_object (&call->bridge_device);
+		g_clear_object (&call->interface_device);
 		break;
 	case OVSDB_DEL_INTERFACE:
 		g_clear_pointer (&call->ifname, g_free);
diff --git a/src/devices/ovs/nm-ovsdb.h b/src/devices/ovs/nm-ovsdb.h
index 279155a4..e7f9d719 100644
--- a/src/devices/ovs/nm-ovsdb.h
+++ b/src/devices/ovs/nm-ovsdb.h
@@ -42,6 +42,7 @@ GType nm_ovsdb_get_type (void);
 
 void nm_ovsdb_add_interface (NMOvsdb *self,
                              NMConnection *bridge, NMConnection *port, NMConnection *interface,
+                             NMDevice *bridge_device, NMDevice *interface_device,
                              NMOvsdbCallback callback, gpointer user_data);
 
 void nm_ovsdb_del_interface (NMOvsdb *self, const char *ifname,
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c
index 6ef9ef94..0e6759e1 100644
--- a/src/devices/wifi/nm-device-iwd.c
+++ b/src/devices/wifi/nm-device-iwd.c
@@ -1881,7 +1881,9 @@ out:
 }
 
 static guint32
-get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
+get_configured_mtu (NMDevice *device,
+                    NMDeviceMtuSource *out_source,
+                    gboolean *out_force)
 {
 	return nm_device_get_configured_mtu_from_connection (device,
 	                                                     NM_TYPE_SETTING_WIRELESS,
diff --git a/src/devices/wifi/nm-device-wifi-p2p.c b/src/devices/wifi/nm-device-wifi-p2p.c
index c9e6189b..649f36d0 100644
--- a/src/devices/wifi/nm-device-wifi-p2p.c
+++ b/src/devices/wifi/nm-device-wifi-p2p.c
@@ -629,7 +629,9 @@ deactivate (NMDevice *device)
 }
 
 static guint32
-get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
+get_configured_mtu (NMDevice *device,
+                    NMDeviceMtuSource *out_source,
+                    gboolean *out_force)
 {
 	*out_source = NM_DEVICE_MTU_SOURCE_NONE;
 	return 0;
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 8c80b0aa..f690100b 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -2969,7 +2969,9 @@ act_stage3_ip_config_start (NMDevice *device,
 }
 
 static guint32
-get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source)
+get_configured_mtu (NMDevice *device,
+                    NMDeviceMtuSource *out_source,
+                    gboolean *out_force)
 {
 	return nm_device_get_configured_mtu_from_connection (device,
 	                                                     NM_TYPE_SETTING_WIRELESS,
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 617096a7..fb2316d5 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -875,7 +875,9 @@ nm_modem_stage3_ip6_config_start (NMModem *self,
 }
 
 guint32
-nm_modem_get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source)
+nm_modem_get_configured_mtu (NMDevice *self,
+                             NMDeviceMtuSource *out_source,
+                             gboolean *out_force)
 {
 	NMConnection *connection;
 	NMSetting *setting;
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index f5b386e8..fccb4fec 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -287,7 +287,7 @@ void nm_modem_emit_ip6_config_result (NMModem *self,
 
 const char *nm_modem_ip_type_to_string (NMModemIPType ip_type);
 
-guint32 nm_modem_get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source);
+guint32 nm_modem_get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source, gboolean *out_force);
 
 void _nm_modem_set_operator_code (NMModem *self, const char *operator_code);
 void _nm_modem_set_apn           (NMModem *self, const char *apn);
diff --git a/src/dhcp/nm-dhcp-nettools.c b/src/dhcp/nm-dhcp-nettools.c
index 6140a916..a71a7a13 100644
--- a/src/dhcp/nm-dhcp-nettools.c
+++ b/src/dhcp/nm-dhcp-nettools.c
@@ -371,6 +371,7 @@ lease_parse_address (NDhcp4ClientLease *lease,
 	const gint64 ts_clock_boottime = nm_utils_monotonic_timestamp_as_boottime (ts, 1);
 	struct in_addr a_address;
 	struct in_addr a_netmask;
+	struct in_addr a_next_server;
 	guint32 a_plen;
 	guint64 nettools_lifetime;
 	gint64 a_lifetime;
@@ -440,6 +441,16 @@ lease_parse_address (NDhcp4ClientLease *lease,
 	                               NM_DHCP_OPTION_DHCP4_NM_EXPIRY,
 	                               (guint64) a_expiry);
 
+
+	n_dhcp4_client_lease_get_siaddr (lease, &a_next_server);
+	if (a_next_server.s_addr != INADDR_ANY) {
+		nm_utils_inet4_ntop (a_next_server.s_addr, addr_str);
+		nm_dhcp_option_add_option (options,
+		                           _nm_dhcp_option_dhcp4_options,
+		                           NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER,
+		                           addr_str);
+	}
+
 	nm_ip4_config_add_address (ip4_config,
 	                           &((const NMPlatformIP4Address) {
 	                                   .address      = a_address.s_addr,
diff --git a/src/dhcp/nm-dhcp-options.c b/src/dhcp/nm-dhcp-options.c
index 21963070..22e7d90e 100644
--- a/src/dhcp/nm-dhcp-options.c
+++ b/src/dhcp/nm-dhcp-options.c
@@ -180,6 +180,7 @@ const NMDhcpOption _nm_dhcp_option_dhcp4_options[] = {
 	/* Internal values */
 	REQ (NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS,                     "ip_address",                      FALSE ),
 	REQ (NM_DHCP_OPTION_DHCP4_NM_EXPIRY,                         "expiry",                          FALSE ),
+	REQ (NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER,                    "next_server",                     FALSE ),
 
 	{ 0 }
 };
diff --git a/src/dhcp/nm-dhcp-options.h b/src/dhcp/nm-dhcp-options.h
index 0dc7a276..f56edb19 100644
--- a/src/dhcp/nm-dhcp-options.h
+++ b/src/dhcp/nm-dhcp-options.h
@@ -164,6 +164,7 @@ typedef enum {
 	/* Internal values */
 	NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS                  = 1024,
 	NM_DHCP_OPTION_DHCP4_NM_EXPIRY                      = 1025,
+	NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER                 = 1026,
 } NMDhcpOptionDhcp4Options;
 
 typedef enum {
diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c
index 073a6da0..96409345 100644
--- a/src/dhcp/nm-dhcp-systemd.c
+++ b/src/dhcp/nm-dhcp-systemd.c
@@ -119,6 +119,7 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	gint64 ts_time = time (NULL);
 	struct in_addr a_address;
 	struct in_addr a_netmask;
+	struct in_addr a_next_server;
 	struct in_addr server_id;
 	struct in_addr broadcast;
 	const struct in_addr *a_router;
@@ -178,6 +179,14 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	                               NM_DHCP_OPTION_DHCP4_NM_EXPIRY,
 	                               (guint64) (ts_time + a_lifetime));
 
+	if (sd_dhcp_lease_get_next_server (lease, &a_next_server) == 0) {
+		nm_utils_inet4_ntop (a_next_server.s_addr, addr_str);
+		nm_dhcp_option_add_option (options,
+		                           _nm_dhcp_option_dhcp4_options,
+		                           NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER,
+		                           addr_str);
+	}
+
 	nm_ip4_config_add_address (ip4_config,
 	                           &((const NMPlatformIP4Address) {
 	                               .address      = a_address.s_addr,
diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c
index b90ee203..c305cd4d 100644
--- a/src/initrd/nmi-cmdline-reader.c
+++ b/src/initrd/nmi-cmdline-reader.c
@@ -47,6 +47,49 @@ _connection_matches_type (gpointer key, gpointer value, gpointer user_data)
 }
 
 static NMConnection *
+add_conn (GHashTable *connections,
+          const char *basename,
+          const char *id,
+          const char *ifname,
+          const char *type_name,
+          NMConnectionMultiConnect multi_connect)
+{
+	NMConnection *connection;
+	NMSetting *setting;
+
+	connection = nm_simple_connection_new ();
+	g_hash_table_insert (connections, g_strdup (basename), connection);
+
+	/* Start off assuming dynamic IP configurations. */
+
+	setting = nm_setting_ip4_config_new ();
+	nm_connection_add_setting (connection, setting);
+	g_object_set (setting,
+	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+	              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
+
+	setting = nm_setting_ip6_config_new ();
+	nm_connection_add_setting (connection, setting);
+	g_object_set (setting,
+	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+	              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
+
+	setting = nm_setting_connection_new ();
+	nm_connection_add_setting (connection, setting);
+	g_object_set (setting,
+	              NM_SETTING_CONNECTION_ID, id,
+	              NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
+	              NM_SETTING_CONNECTION_TYPE, type_name,
+	              NM_SETTING_CONNECTION_MULTI_CONNECT, multi_connect,
+	              NULL);
+
+	return connection;
+}
+
+static NMConnection *
 get_conn (GHashTable *connections, const char *ifname, const char *type_name)
 {
 	NMConnection *connection;
@@ -76,40 +119,15 @@ get_conn (GHashTable *connections, const char *ifname, const char *type_name)
 		                                (gpointer) type_name);
 	}
 
-	if (connection) {
-		setting = (NMSetting *)nm_connection_get_setting_connection (connection);
-	} else {
-		connection = nm_simple_connection_new ();
-		g_hash_table_insert (connections, g_strdup (basename), connection);
-
-		/* Start off assuming dynamic IP configurations. */
-
-		setting = nm_setting_ip4_config_new ();
-		nm_connection_add_setting (connection, setting);
-		g_object_set (setting,
-		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
-		              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
-		              NULL);
-
-		setting = nm_setting_ip6_config_new ();
-		nm_connection_add_setting (connection, setting);
-		g_object_set (setting,
-		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
-		              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
-		              NULL);
-
-		setting = nm_setting_connection_new ();
-		nm_connection_add_setting (connection, setting);
-		g_object_set (setting,
-		              NM_SETTING_CONNECTION_ID, ifname ?: "Wired Connection",
-		              NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
-		              NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
-		              NM_SETTING_CONNECTION_MULTI_CONNECT, multi_connect,
-		              NULL);
-
+	if (!connection) {
 		if (!type_name)
 			type_name = NM_SETTING_WIRED_SETTING_NAME;
+
+		connection = add_conn (connections, basename,
+		                       ifname ?: "Wired Connection",
+		                       ifname, type_name, multi_connect);
 	}
+	setting = (NMSetting *)nm_connection_get_setting_connection (connection);
 
 	if (type_name) {
 		g_object_set (setting, NM_SETTING_CONNECTION_TYPE, type_name, NULL);
@@ -480,7 +498,10 @@ parse_ip (GHashTable *connections, const char *sysfs_dir, char *argument)
 }
 
 static void
-parse_master (GHashTable *connections, char *argument, const char *type_name)
+parse_master (GHashTable *connections,
+              char *argument,
+              const char *type_name,
+              const char *default_name)
 {
 	NMConnection *connection;
 	NMSettingConnection *s_con;
@@ -496,7 +517,7 @@ parse_master (GHashTable *connections, char *argument, const char *type_name)
 
 	master = get_word (&argument, ':');
 	if (!master)
-		master = master_to_free = g_strdup_printf ("%s0", type_name);
+		master = master_to_free = g_strdup_printf ("%s0", default_name ?: type_name);
 	slaves = get_word (&argument, ':');
 
 	connection = get_conn (connections, master, type_name);
@@ -634,6 +655,13 @@ parse_bootdev (GHashTable *connections, char *argument)
 
 	connection = get_conn (connections, NULL, NULL);
 
+	if (   nm_connection_get_interface_name (connection)
+	    && strcmp (nm_connection_get_interface_name (connection), argument) != 0) {
+		/* If the default connection already has an interface name,
+		 * we should not overwrite it. Create a new one instead. */
+		connection = get_conn (connections, argument, NULL);
+	}
+
 	s_con = nm_connection_get_setting_connection (connection);
 	g_object_set (s_con,
 	              NM_SETTING_CONNECTION_INTERFACE_NAME, argument,
@@ -803,11 +831,11 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv)
 		else if (strcmp (tag, "rd.route") == 0)
 			parse_rd_route (connections, argument);
 		else if (strcmp (tag, "bridge") == 0)
-			parse_master (connections, argument, NM_SETTING_BRIDGE_SETTING_NAME);
+			parse_master (connections, argument, NM_SETTING_BRIDGE_SETTING_NAME, "br");
 		else if (strcmp (tag, "bond") == 0)
-			parse_master (connections, argument, NM_SETTING_BOND_SETTING_NAME);
+			parse_master (connections, argument, NM_SETTING_BOND_SETTING_NAME, NULL);
 		else if (strcmp (tag, "team") == 0)
-			parse_master (connections, argument, NM_SETTING_TEAM_SETTING_NAME);
+			parse_master (connections, argument, NM_SETTING_TEAM_SETTING_NAME, NULL);
 		else if (strcmp (tag, "vlan") == 0)
 			parse_vlan (connections, argument);
 		else if (strcmp (tag, "bootdev") == 0)
@@ -846,8 +874,19 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv)
 		}
 
 		connection = get_conn (connections, NULL, NM_SETTING_WIRED_SETTING_NAME);
-
 		s_wired = nm_connection_get_setting_wired (connection);
+
+		if (   nm_connection_get_interface_name (connection)
+		    || (   nm_setting_wired_get_mac_address (s_wired)
+		        && !nm_utils_hwaddr_matches (nm_setting_wired_get_mac_address (s_wired), -1,
+		                                     bootif, -1))) {
+			connection = add_conn (connections, "bootif_connection", "BOOTIF Connection",
+			                       NULL, NM_SETTING_WIRED_SETTING_NAME,
+			                       NM_CONNECTION_MULTI_CONNECT_SINGLE);
+			s_wired = (NMSettingWired *) nm_setting_wired_new ();
+			nm_connection_add_setting (connection, (NMSetting *) s_wired);
+		}
+
 		g_object_set (s_wired,
 		              NM_SETTING_WIRED_MAC_ADDRESS, bootif,
 		              NULL);
diff --git a/src/initrd/tests/test-cmdline-reader.c b/src/initrd/tests/test-cmdline-reader.c
index 5ac82f50..a28c634d 100644
--- a/src/initrd/tests/test-cmdline-reader.c
+++ b/src/initrd/tests/test-cmdline-reader.c
@@ -283,8 +283,7 @@ test_multiple (void)
 {
 	gs_unref_hashtable GHashTable *connections = NULL;
 	const char *const*ARGV = NM_MAKE_STRV ("ip=192.0.2.2:::::eth0",
-	                                       "ip=[2001:db8::2]:::::eth0",
-	                                       "BOOTIF=00:53:AB:cd:02:03");
+	                                       "ip=[2001:db8::2]:::::eth0");
 	NMConnection *connection;
 	NMSettingWired *s_wired;
 	NMSettingIPConfig *s_ip4;
@@ -302,7 +301,6 @@ test_multiple (void)
 
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired);
-	g_assert_cmpstr (nm_setting_wired_get_mac_address (s_wired), ==, "00:53:AB:CD:02:03");
 
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	g_assert (s_ip4);
@@ -324,12 +322,45 @@ test_multiple (void)
 }
 
 static void
+test_bootdev (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	const char *const*ARGV = NM_MAKE_STRV ("vlan=vlan2:ens5", "bootdev=ens3");
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", ARGV);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 2);
+
+	connection = g_hash_table_lookup (connections, "ens3");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (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_WIRED_SETTING_NAME);
+	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "ens3");
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "ens3");
+
+	connection = g_hash_table_lookup (connections, "vlan2");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (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_id (s_con), ==, "vlan2");
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "vlan2");
+}
+
+static void
 test_some_more (void)
 {
 	gs_unref_hashtable GHashTable *connections = NULL;
 	const char *const*ARGV = NM_MAKE_STRV ("bootdev=eth1", "hail", "nameserver=[2001:DB8:3::53]",
 	                                       "satan", "nameserver=192.0.2.53", "worship",
-	                                       "BOOTIF=01-00-53-AB-cd-02-03", "doom", "rd.peerdns=0",
+	                                       "doom", "rd.peerdns=0",
 	                                       "rd.route=[2001:DB8:3::/48]:[2001:DB8:2::1]:ens10");
 	NMConnection *connection;
 	NMSettingConnection *s_con;
@@ -355,7 +386,6 @@ test_some_more (void)
 
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired);
-	g_assert_cmpstr (nm_setting_wired_get_mac_address (s_wired), ==, "00:53:AB:CD:02:03");
 
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	g_assert (s_ip4);
@@ -406,17 +436,6 @@ test_some_more (void)
 }
 
 static void
-test_no_bootif (void)
-{
-	gs_unref_hashtable GHashTable *connections = NULL;
-	const char *const*ARGV = NM_MAKE_STRV ("BOOTIF=01-00-53-AB-cd-02-03", "rd.bootif=0");
-
-	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", ARGV);
-	g_assert (connections);
-	g_assert_cmpint (g_hash_table_size (connections), ==, 0);
-}
-
-static void
 test_bond (void)
 {
 	gs_unref_hashtable GHashTable *connections = NULL;
@@ -651,12 +670,12 @@ test_bridge_default (void)
 	g_assert (connections);
 	g_assert_cmpint (g_hash_table_size (connections), ==, 2);
 
-	connection = g_hash_table_lookup (connections, "bridge0");
+	connection = g_hash_table_lookup (connections, "br0");
 
 	g_assert (connection);
 	nmtst_assert_connection_verifies_without_normalization (connection);
 	g_assert_cmpstr (nm_connection_get_connection_type (connection), ==, NM_SETTING_BRIDGE_SETTING_NAME);
-	g_assert_cmpstr (nm_connection_get_id (connection), ==, "bridge0");
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "br0");
 	master_uuid = nm_connection_get_uuid (connection);
 	g_assert (master_uuid);
 
@@ -917,6 +936,109 @@ test_rd_znet_legacy (void)
 	nmtst_assert_connection_verifies_without_normalization (connection);
 }
 
+static void
+test_bootif (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	const char *const*ARGV = NM_MAKE_STRV ("BOOTIF=00:53:AB:cd:02:03",
+	                                       "ip=dhcp");
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+
+	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, "default_connection");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "Wired Connection");
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert_cmpstr (nm_setting_wired_get_mac_address (s_wired), ==, "00:53:AB:CD:02:03");
+	g_assert (s_wired);
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	g_assert (s_ip4);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+	g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert (!nm_setting_ip_config_get_may_fail (s_ip4));
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	g_assert (s_ip6);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
+	g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+}
+
+static void
+test_bootif_hwtype (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	const char *const*ARGV = NM_MAKE_STRV ("ip=eth0:dhcp",
+	                                       "BOOTIF=01-00-53-AB-cd-02-03");
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", ARGV);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 2);
+
+	connection = g_hash_table_lookup (connections, "eth0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth0");
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert (!nm_setting_wired_get_mac_address (s_wired));
+	g_assert (s_wired);
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	g_assert (s_ip4);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+	g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert (!nm_setting_ip_config_get_may_fail (s_ip4));
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	g_assert (s_ip6);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_DISABLED);
+	g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+
+	connection = g_hash_table_lookup (connections, "bootif_connection");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	g_assert_cmpstr (nm_connection_get_id (connection), ==, "BOOTIF Connection");
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert_cmpstr (nm_setting_wired_get_mac_address (s_wired), ==, "00:53:AB:CD:02:03");
+	g_assert (s_wired);
+
+	s_ip4 = nm_connection_get_setting_ip4_config (connection);
+	g_assert (s_ip4);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
+	g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip4));
+	g_assert (nm_setting_ip_config_get_may_fail (s_ip4));
+
+	s_ip6 = nm_connection_get_setting_ip6_config (connection);
+	g_assert (s_ip6);
+	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
+	g_assert (!nm_setting_ip_config_get_ignore_auto_dns (s_ip6));
+	g_assert (nm_setting_ip_config_get_may_fail (s_ip6));
+}
+
+static void
+test_bootif_off (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	const char *const*ARGV = NM_MAKE_STRV ("BOOTIF=01-00-53-AB-cd-02-03", "rd.bootif=0");
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", ARGV);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 0);
+}
 
 NMTST_DEFINE ();
 
@@ -932,7 +1054,7 @@ int main (int argc, char **argv)
 	g_test_add_func ("/initrd/cmdline/if_ip6_manual", test_if_ip6_manual);
 	g_test_add_func ("/initrd/cmdline/multiple", test_multiple);
 	g_test_add_func ("/initrd/cmdline/some_more", test_some_more);
-	g_test_add_func ("/initrd/cmdline/no_bootif", test_no_bootif);
+	g_test_add_func ("/initrd/cmdline/bootdev", test_bootdev);
 	g_test_add_func ("/initrd/cmdline/bond", test_bond);
 	g_test_add_func ("/initrd/cmdline/bond/default", test_bond_default);
 	g_test_add_func ("/initrd/cmdline/team", test_team);
@@ -942,6 +1064,9 @@ int main (int argc, char **argv)
 	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);
+	g_test_add_func ("/initrd/cmdline/bootif", test_bootif);
+	g_test_add_func ("/initrd/cmdline/bootif/hwtype", test_bootif_hwtype);
+	g_test_add_func ("/initrd/cmdline/bootif/off", test_bootif_off);
 
 	return g_test_run ();
 }
diff --git a/src/ndisc/nm-lndp-ndisc.c b/src/ndisc/nm-lndp-ndisc.c
index 5643c192..9352a753 100644
--- a/src/ndisc/nm-lndp-ndisc.c
+++ b/src/ndisc/nm-lndp-ndisc.c
@@ -116,6 +116,7 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 	gint32 now = nm_utils_get_monotonic_timestamp_s ();
 	int offset;
 	int hop_limit;
+	guint32 val;
 
 	/* Router discovery is subject to the following RFC documents:
 	 *
@@ -294,6 +295,18 @@ receive_ra (struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
 		changed |= NM_NDISC_CONFIG_HOP_LIMIT;
 	}
 
+	val = ndp_msgra_reachable_time (msgra);
+	if (val && rdata->public.reachable_time_ms != val) {
+		rdata->public.reachable_time_ms = val;
+		changed |= NM_NDISC_CONFIG_REACHABLE_TIME;
+	}
+
+	val = ndp_msgra_retransmit_time (msgra);
+	if (val && rdata->public.retrans_timer_ms != val) {
+		rdata->public.retrans_timer_ms = val;
+		changed |= NM_NDISC_CONFIG_RETRANS_TIMER;
+	}
+
 	/* MTU */
 	ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_MTU) {
 		guint32 mtu = ndp_msg_opt_mtu(msg, offset);
diff --git a/src/ndisc/nm-ndisc.c b/src/ndisc/nm-ndisc.c
index 1acf5ba9..a8fabb56 100644
--- a/src/ndisc/nm-ndisc.c
+++ b/src/ndisc/nm-ndisc.c
@@ -1039,6 +1039,14 @@ _config_changed_log (NMNDisc *ndisc, NMNDiscConfigMap changed)
 	config_map_to_string (changed, changedstr);
 	_LOGD ("neighbor discovery configuration changed [%s]:", changedstr);
 	_LOGD ("  dhcp-level %s", dhcp_level_to_string (priv->rdata.public.dhcp_level));
+
+	if (rdata->public.hop_limit)
+		_LOGD ("  hop limit      : %d", rdata->public.hop_limit);
+	if (rdata->public.reachable_time_ms)
+		_LOGD ("  reachable time : %u", (guint) rdata->public.reachable_time_ms);
+	if (rdata->public.retrans_timer_ms)
+		_LOGD ("  retrans timer  : %u", (guint) rdata->public.retrans_timer_ms);
+
 	for (i = 0; i < rdata->gateways->len; i++) {
 		NMNDiscGateway *gateway = &g_array_index (rdata->gateways, NMNDiscGateway, i);
 
diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h
index 766d4ab9..8e07ca41 100644
--- a/src/ndisc/nm-ndisc.h
+++ b/src/ndisc/nm-ndisc.h
@@ -112,6 +112,8 @@ typedef enum {
 	NM_NDISC_CONFIG_DNS_DOMAINS                         = 1 << 5,
 	NM_NDISC_CONFIG_HOP_LIMIT                           = 1 << 6,
 	NM_NDISC_CONFIG_MTU                                 = 1 << 7,
+	NM_NDISC_CONFIG_REACHABLE_TIME                      = 1 << 8,
+	NM_NDISC_CONFIG_RETRANS_TIMER                       = 1 << 9,
 } NMNDiscConfigMap;
 
 typedef enum {
@@ -137,6 +139,8 @@ typedef struct {
 	NMNDiscDHCPLevel dhcp_level;
 	guint32 mtu;
 	int hop_limit;
+	guint32 reachable_time_ms;
+	guint32 retrans_timer_ms;
 
 	guint gateways_n;
 	guint addresses_n;
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index dd5bb327..afe3fc5d 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -220,6 +220,18 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 	if (changed & NM_NDISC_CONFIG_HOP_LIMIT)
 		nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NM_PLATFORM_GET, global_opt.ifname, rdata->hop_limit);
 
+	if (changed & NM_NDISC_CONFIG_REACHABLE_TIME) {
+		nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time (NM_PLATFORM_GET,
+		                                                     global_opt.ifname,
+		                                                     rdata->reachable_time_ms);
+	}
+
+	if (changed & NM_NDISC_CONFIG_RETRANS_TIMER) {
+		nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time (NM_PLATFORM_GET,
+		                                                   global_opt.ifname,
+		                                                   rdata->retrans_timer_ms);
+	}
+
 	if (changed & NM_NDISC_CONFIG_MTU) {
 		nm_platform_sysctl_ip_conf_set_int64 (NM_PLATFORM_GET,
 		                                      AF_INET6,
@@ -557,10 +569,7 @@ main (int argc, char *argv[])
 		if (iid)
 			nm_ndisc_set_iid (ndisc, *iid);
 
-		nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra",          "1");
-		nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_defrtr",   "0");
-		nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_pinfo",    "0");
-		nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra_rtr_pref", "0");
+		nm_platform_sysctl_ip_conf_set (NM_PLATFORM_GET, AF_INET6, global_opt.ifname, "accept_ra", "0");
 
 		g_signal_connect (NM_PLATFORM_GET,
 		                  NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED,
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 78f3722c..d112fcf7 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -2741,6 +2741,12 @@ recheck_assume_connection (NMManager *self,
 			activation_type_assume = TRUE;
 
 			if (generated) {
+				/* Reset the IPv4 setting to empty method=auto, regardless of what assumption guessed. */
+				nm_connection_add_setting (nm_settings_connection_get_connection (sett_conn),
+				                           g_object_new (NM_TYPE_SETTING_IP4_CONFIG,
+				                                         NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+				                                         NULL));
+
 				nm_settings_connection_update (sett_conn,
 				                               NULL,
 				                               NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 6ead15ed..0a7acba9 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -610,6 +610,54 @@ nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self,
 	return TRUE;
 }
 
+gboolean
+nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time (NMPlatform *self,
+                                                     const char *iface,
+                                                     guint value_ms)
+{
+	char path[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE];
+	char str[128];
+	guint clamped;
+
+	_CHECK_SELF (self, klass, FALSE);
+
+	if (!value_ms)
+		return TRUE;
+
+	/* RFC 4861 says the value can't be greater than one hour.
+	 * Also use a reasonable lower threshold. */
+	clamped = NM_CLAMP (value_ms, 100, 3600000);
+	nm_sprintf_buf (path, "/proc/sys/net/ipv6/neigh/%s/base_reachable_time_ms", iface);
+	nm_sprintf_buf (str, "%u", clamped);
+	if (!nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), str))
+		return FALSE;
+
+	/* Set stale time in the same way as kernel */
+	nm_sprintf_buf (path, "/proc/sys/net/ipv6/neigh/%s/gc_stale_time", iface);
+	nm_sprintf_buf (str, "%u", clamped * 3 / 1000);
+
+	return nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), str);
+}
+
+gboolean
+nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time (NMPlatform *self,
+                                                   const char *iface,
+                                                   guint value_ms)
+{
+	char path[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE];
+	char str[128];
+
+	_CHECK_SELF (self, klass, FALSE);
+
+	if (!value_ms)
+		return TRUE;
+
+	nm_sprintf_buf (path, "/proc/sys/net/ipv6/neigh/%s/retrans_time_ms", iface);
+	nm_sprintf_buf (str, "%u", NM_CLAMP (value_ms, 10, 3600000));
+
+	return nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), str);
+}
+
 /**
  * nm_platform_sysctl_get:
  * @self: platform instance
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 248eca44..44733809 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -1353,6 +1353,12 @@ gboolean nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *self,
 gboolean nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self,
                                                              const char *iface,
                                                              int value);
+gboolean nm_platform_sysctl_ip_neigh_set_ipv6_reachable_time (NMPlatform *self,
+                                                              const char *iface,
+                                                              guint value_ms);
+gboolean nm_platform_sysctl_ip_neigh_set_ipv6_retrans_time (NMPlatform *self,
+                                                            const char *iface,
+                                                            guint value_ms);
 int nm_platform_sysctl_ip_conf_get_rp_filter_ipv4 (NMPlatform *platform,
                                                    const char *iface,
                                                    gboolean consider_all,
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 020766c8..68ebd781 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -3573,6 +3573,11 @@ next:
 		return NULL;
 	}
 
+	g_object_set (s_8021x,
+	              NM_SETTING_802_1X_SYSTEM_CA_CERTS,
+	              svGetValueBoolean (ifcfg, "IEEE_8021X_SYSTEM_CA_CERTS", FALSE),
+	              NULL);
+
 	nm_clear_g_free (&value);
 	v = svGetValueStr (ifcfg, "IEEE_8021X_SUBJECT_MATCH", &value);
 	g_object_set (s_8021x, NM_SETTING_802_1X_SUBJECT_MATCH, v, NULL);
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 f3cd71bd..809d3769 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -432,35 +432,37 @@ write_8021x_setting (NMConnection *connection,
 	            nm_setting_802_1x_get_password_raw_flags (s_8021x));
 	g_free (tmp);
 
-	/* PEAP version */
+	svSetValueBoolean_cond_true (ifcfg,
+	                             "IEEE_8021X_SYSTEM_CA_CERTS",
+	                             nm_setting_802_1x_get_system_ca_certs (s_8021x));
+
 	value = nm_setting_802_1x_get_phase1_peapver (s_8021x);
-	svUnsetValue (ifcfg, "IEEE_8021X_PEAP_VERSION");
-	if (value && (!strcmp (value, "0") || !strcmp (value, "1")))
+	if (NM_IN_STRSET (value, "0", "1"))
 		svSetValueStr (ifcfg, "IEEE_8021X_PEAP_VERSION", value);
+	else
+		svUnsetValue (ifcfg, "IEEE_8021X_PEAP_VERSION");
 
-	/* Force new PEAP label */
-	value = nm_setting_802_1x_get_phase1_peaplabel (s_8021x);
-	svUnsetValue (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL");
-	if (value && !strcmp (value, "1"))
-		svSetValueStr (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", "yes");
+	svSetValueBoolean_cond_true (ifcfg,
+	                             "IEEE_8021X_PEAP_FORCE_NEW_LABEL",
+	                             nm_streq0 (nm_setting_802_1x_get_phase1_peaplabel (s_8021x), "1"));
 
-	/* PAC file */
-	value = nm_setting_802_1x_get_pac_file (s_8021x);
-	svUnsetValue (ifcfg, "IEEE_8021X_PAC_FILE");
-	if (value)
-		svSetValueStr (ifcfg, "IEEE_8021X_PAC_FILE", value);
+	svSetValueStr (ifcfg,
+	               "IEEE_8021X_PAC_FILE",
+	               nm_setting_802_1x_get_pac_file (s_8021x));
 
 	/* FAST PAC provisioning */
 	value = nm_setting_802_1x_get_phase1_fast_provisioning (s_8021x);
-	svUnsetValue (ifcfg, "IEEE_8021X_FAST_PROVISIONING");
 	if (value) {
 		if (strcmp (value, "1") == 0)
-			svSetValueStr (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth");
+			value = "allow-unauth";
 		else if (strcmp (value, "2") == 0)
-			svSetValueStr (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-auth");
+			value = "allow-auth";
 		else if (strcmp (value, "3") == 0)
-			svSetValueStr (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth allow-auth");
+			value = "allow-unauth allow-auth";
+		else
+			value = NULL;
 	}
+	svSetValueStr (ifcfg, "IEEE_8021X_FAST_PROVISIONING", value);
 
 	/* Phase2 auth methods */
 	svUnsetValue (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
@@ -537,10 +539,9 @@ write_8021x_setting (NMConnection *connection,
 	vint = nm_setting_802_1x_get_auth_timeout (s_8021x);
 	svSetValueInt64_cond (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", vint > 0, vint);
 
-	if (nm_setting_802_1x_get_optional (s_8021x))
-		svSetValueBoolean (ifcfg, "IEEE_8021X_OPTIONAL", TRUE);
-	else
-		svUnsetValue (ifcfg, "IEEE_8021X_OPTIONAL");
+	svSetValueBoolean_cond_true (ifcfg,
+	                             "IEEE_8021X_OPTIONAL",
+	                             nm_setting_802_1x_get_optional (s_8021x));
 
 	if (!write_8021x_certs (s_8021x, secrets, blobs, FALSE, ifcfg, error))
 		return FALSE;
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 94e31aac..d4bc71f3 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -1357,6 +1357,12 @@ svSetValueBoolean (shvarFile *s, const char *key, gboolean value)
 }
 
 gboolean
+svSetValueBoolean_cond_true (shvarFile *s, const char *key, gboolean value)
+{
+	return svSetValue (s, key, value ? "yes" : NULL);
+}
+
+gboolean
 svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value)
 {
 	gs_free char *v = NULL;
diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h
index 67fb5404..2f6912b3 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/settings/plugins/ifcfg-rh/shvar.h
@@ -96,6 +96,7 @@ gboolean svGetValueEnum (shvarFile *s, const char *key,
 gboolean svSetValue (shvarFile *s, const char *key, const char *value);
 gboolean svSetValueStr (shvarFile *s, const char *key, const char *value);
 gboolean svSetValueBoolean (shvarFile *s, const char *key, gboolean value);
+gboolean svSetValueBoolean_cond_true (shvarFile *s, const char *key, gboolean value);
 gboolean svSetValueInt64 (shvarFile *s, const char *key, gint64 value);
 gboolean svSetValueInt64_cond (shvarFile *s, const char *key, gboolean do_set, gint64 value);
 gboolean svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value);