summary refs log tree commit diff
path: root/src/devices
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2019-11-25 18:53:53 +0100
committerMichael Biebl <biebl@debian.org>2019-11-25 18:53:53 +0100
commite22609983008e1a669196ad64ba3a59ae8c76e0d (patch)
treec5b97693244d5004cbe735d507a25159512a753d /src/devices
parent9c642eac191ef0dce49855d8da0a9e9af18d5113 (diff)
New upstream version 1.20.8 upstream/1.20.8
Diffstat (limited to 'src/devices')
-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
18 files changed, 303 insertions, 84 deletions
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);