about summary refs log tree commit diff
path: root/src/devices
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices')
-rw-r--r--src/devices/adsl/nm-atm-manager.c2
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c2
-rw-r--r--src/devices/nm-acd-manager.c48
-rw-r--r--src/devices/nm-acd-manager.h7
-rw-r--r--src/devices/nm-device-bond.c18
-rw-r--r--src/devices/nm-device-bridge.c174
-rw-r--r--src/devices/nm-device-ethernet.c2
-rw-r--r--src/devices/nm-device-private.h3
-rw-r--r--src/devices/nm-device-wireguard.c2
-rw-r--r--src/devices/nm-device.c197
-rw-r--r--src/devices/nm-device.h2
-rw-r--r--src/devices/nm-lldp-listener.c357
-rw-r--r--src/devices/ovs/nm-device-ovs-port.c6
-rw-r--r--src/devices/ovs/nm-ovs-factory.c2
-rw-r--r--src/devices/ovs/nm-ovsdb.c2
-rw-r--r--src/devices/team/nm-device-team.c8
-rw-r--r--src/devices/tests/test-acd.c26
-rw-r--r--src/devices/tests/test-lldp.c76
-rw-r--r--src/devices/wifi/nm-device-iwd.c4
-rw-r--r--src/devices/wifi/nm-device-wifi.c2
-rw-r--r--src/devices/wifi/nm-iwd-manager.c2
-rw-r--r--src/devices/wwan/nm-modem-broadband.c13
-rw-r--r--src/devices/wwan/nm-modem-manager.c2
-rw-r--r--src/devices/wwan/nm-modem.c2
24 files changed, 763 insertions, 196 deletions
diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c
index dddb8342..f312b5a3 100644
--- a/src/devices/adsl/nm-atm-manager.c
+++ b/src/devices/adsl/nm-atm-manager.c
@@ -27,7 +27,7 @@
 #include "nm-device-adsl.h"
 #include "devices/nm-device-factory.h"
 #include "platform/nm-platform.h"
-#include "nm-utils/nm-udev-utils.h"
+#include "nm-udev-aux/nm-udev-utils.h"
 
 /*****************************************************************************/
 
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index dc85a8b2..9cd3a519 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -35,7 +35,7 @@
 #include "nm-device-bt.h"
 #include "nm-core-internal.h"
 #include "platform/nm-platform.h"
-#include "nm-dbus-compat.h"
+#include "nm-std-aux/nm-dbus-compat.h"
 
 /*****************************************************************************/
 
diff --git a/src/devices/nm-acd-manager.c b/src/devices/nm-acd-manager.c
index a8f7a63a..f437ce3a 100644
--- a/src/devices/nm-acd-manager.c
+++ b/src/devices/nm-acd-manager.c
@@ -113,6 +113,30 @@ acd_error_to_string (int error)
 	g_return_val_if_reached (NULL);
 }
 
+static int
+acd_error_to_nmerr (int error, gboolean always_fail)
+{
+	if (error < 0)
+		return -nm_errno_native (error);
+
+	if (always_fail) {
+		if (NM_IN_SET (error, N_ACD_E_PREEMPTED,
+		                      N_ACD_E_INVALID_ARGUMENT))
+			return -NME_UNSPEC;
+		g_return_val_if_reached (-NME_UNSPEC);
+	}
+
+	/* so, @error is either zero (indicating success) or one
+	 * of the special status codes like N_ACD_E_*. In both cases,
+	 * return the positive value here. */
+	if (NM_IN_SET (error, _N_ACD_E_SUCCESS,
+	                      N_ACD_E_PREEMPTED,
+	                      N_ACD_E_INVALID_ARGUMENT))
+		return error;
+
+	g_return_val_if_reached (error);
+}
+
 /*****************************************************************************/
 
 /**
@@ -291,9 +315,9 @@ acd_init (NMAcdManager *self)
  * Start probing IP addresses for duplicates; when the probe terminates a
  * PROBE_TERMINATED signal is emitted.
  *
- * Returns: %TRUE if at least one probe could be started, %FALSE otherwise
+ * Returns: 0 on success or a negative NetworkManager error code (NME_*).
  */
-gboolean
+int
 nm_acd_manager_start_probe (NMAcdManager *self, guint timeout)
 {
 	GHashTableIter iter;
@@ -309,7 +333,7 @@ nm_acd_manager_start_probe (NMAcdManager *self, guint timeout)
 		_LOGW ("couldn't init ACD for probing on interface '%s': %s",
 		       nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex),
 		       acd_error_to_string (r));
-		return FALSE;
+		return acd_error_to_nmerr (r, TRUE);
 	}
 
 	self->completed = 0;
@@ -325,7 +349,7 @@ nm_acd_manager_start_probe (NMAcdManager *self, guint timeout)
 	self->channel = g_io_channel_unix_new (fd);
 	self->event_id = g_io_add_watch (self->channel, G_IO_IN, acd_event, self);
 
-	return success;
+	return success ? 0 : -NME_UNSPEC;
 }
 
 /**
@@ -357,20 +381,23 @@ nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address)
  * @self: a #NMAcdManager
  *
  * Start announcing addresses.
+ *
+ * Returns: a negative NetworkManager error number or zero on success.
  */
-void
+int
 nm_acd_manager_announce_addresses (NMAcdManager *self)
 {
 	GHashTableIter iter;
 	AddressInfo *info;
 	int r;
+	gboolean success = TRUE;
 
 	r = acd_init (self);
 	if (r) {
 		_LOGW ("couldn't init ACD for announcing addresses on interface '%s': %s",
 		       nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex),
 		       acd_error_to_string (r));
-		return;
+		return acd_error_to_nmerr (r, TRUE);
 	}
 
 	if (self->state == STATE_INIT) {
@@ -378,8 +405,10 @@ nm_acd_manager_announce_addresses (NMAcdManager *self)
 		 * start a fake probe with zero timeout and then perform
 		 * the announcement. */
 		g_hash_table_iter_init (&iter, self->addresses);
-		while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info))
-			acd_probe_add (self, info, 0);
+		while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) {
+			if (!acd_probe_add (self, info, 0))
+				success = FALSE;
+		}
 		self->state = STATE_ANNOUNCING;
 	} else if (self->state == STATE_ANNOUNCING) {
 		char sbuf[NM_UTILS_INET_ADDRSTRLEN];
@@ -394,10 +423,13 @@ nm_acd_manager_announce_addresses (NMAcdManager *self)
 				       nm_utils_inet4_ntop (info->address, sbuf),
 				       nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex),
 				       acd_error_to_string (r));
+				success = FALSE;
 			} else
 				_LOGD ("announcing address %s", nm_utils_inet4_ntop (info->address, sbuf));
 		}
 	}
+
+	return success ? 0 : -NME_UNSPEC;
 }
 
 static void
diff --git a/src/devices/nm-acd-manager.h b/src/devices/nm-acd-manager.h
index 75884846..08c0b798 100644
--- a/src/devices/nm-acd-manager.h
+++ b/src/devices/nm-acd-manager.h
@@ -36,8 +36,11 @@ NMAcdManager *nm_acd_manager_new (int ifindex,
 void nm_acd_manager_free (NMAcdManager *self);
 
 gboolean nm_acd_manager_add_address (NMAcdManager *self, in_addr_t address);
-gboolean nm_acd_manager_start_probe (NMAcdManager *self, guint timeout);
+int nm_acd_manager_start_probe (NMAcdManager *self, guint timeout);
 gboolean nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address);
-void nm_acd_manager_announce_addresses (NMAcdManager *self);
+int nm_acd_manager_announce_addresses (NMAcdManager *self);
+
+NM_AUTO_DEFINE_FCN0 (NMAcdManager *, _nm_auto_free_acdmgr, nm_acd_manager_free);
+#define nm_auto_free_acdmgr nm_auto (_nm_auto_free_acdmgr)
 
 #endif /* __NM_ACD_MANAGER__ */
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index 6dabdfe8..37159fca 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -185,20 +185,18 @@ set_arp_targets (NMDevice *device,
                  const char *delim,
                  const char *prefix)
 {
-	char **items, **iter, *tmp;
+	gs_free const char **value_v = NULL;
+	gsize i;
 
-	if (!value || !*value)
+	value_v = nm_utils_strsplit_set (value, delim);
+	if (!value_v)
 		return;
+	for (i = 0; value_v[i]; i++) {
+		gs_free char *tmp = NULL;
 
-	items = g_strsplit_set (value, delim, 0);
-	for (iter = items; iter && *iter; iter++) {
-		if (*iter[0]) {
-			tmp = g_strdup_printf ("%s%s", prefix, *iter);
-			set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, tmp);
-			g_free (tmp);
-		}
+		tmp = g_strdup_printf ("%s%s", prefix, value_v[i]);
+		set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, tmp);
 	}
-	g_strfreev (items);
 }
 
 static void
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index 4c8921c0..4275af91 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -37,6 +37,7 @@ _LOG_DECLARE_SELF(NMDeviceBridge);
 
 struct _NMDeviceBridge {
 	NMDevice parent;
+	bool vlan_configured:1;
 };
 
 struct _NMDeviceBridgeClass {
@@ -267,21 +268,45 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool
 		nm_platform_sysctl_master_set_option (nm_device_get_platform (device), ifindex, option->sysname, value);
 }
 
-static void
-commit_master_options (NMDevice *device, NMSettingBridge *setting)
+static const NMPlatformBridgeVlan **
+setting_vlans_to_platform (GPtrArray *array)
 {
-	const Option *option;
-	NMSetting *s = NM_SETTING (setting);
-
-	for (option = master_options; option->name; option++)
-		commit_option (device, s, option, FALSE);
+	NMPlatformBridgeVlan **arr;
+	NMPlatformBridgeVlan *p_data;
+	guint i;
+
+	if (!array || !array->len)
+		return NULL;
+
+	G_STATIC_ASSERT_EXPR (_nm_alignof (NMPlatformBridgeVlan *) >= _nm_alignof (NMPlatformBridgeVlan));
+	arr = g_malloc (  (sizeof (NMPlatformBridgeVlan *) * (array->len + 1))
+	                + (sizeof (NMPlatformBridgeVlan  ) * (array->len    )));
+	p_data = (NMPlatformBridgeVlan *) &arr[array->len + 1];
+
+	for (i = 0; i < array->len; i++) {
+		NMBridgeVlan *vlan = array->pdata[i];
+		guint16 vid_start, vid_end;
+
+		nm_bridge_vlan_get_vid_range (vlan, &vid_start, &vid_end);
+
+		p_data[i] = (NMPlatformBridgeVlan) {
+			.vid_start = vid_start,
+			.vid_end   = vid_end,
+			.pvid      = nm_bridge_vlan_is_pvid (vlan),
+			.untagged  = nm_bridge_vlan_is_untagged (vlan),
+		};
+		arr[i] = &p_data[i];
+	}
+	arr[i] = NULL;
+	return (const NMPlatformBridgeVlan **) arr;
 }
 
 static void
 commit_slave_options (NMDevice *device, NMSettingBridgePort *setting)
 {
 	const Option *option;
-	NMSetting *s, *s_clear = NULL;
+	NMSetting *s;
+	gs_unref_object NMSetting *s_clear = NULL;
 
 	if (setting)
 		s = NM_SETTING (setting);
@@ -290,8 +315,6 @@ commit_slave_options (NMDevice *device, NMSettingBridgePort *setting)
 
 	for (option = slave_options; option->name; option++)
 		commit_option (device, s, option, TRUE);
-
-	g_clear_object (&s_clear);
 }
 
 static void
@@ -396,22 +419,112 @@ master_update_slave_connection (NMDevice *device,
 	return TRUE;
 }
 
+static gboolean
+bridge_set_vlan_options (NMDevice *device, NMSettingBridge *s_bridge)
+{
+	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
+	gconstpointer hwaddr;
+	size_t length;
+	gboolean enabled;
+	guint16 pvid;
+	NMPlatform *plat;
+	int ifindex;
+	gs_unref_ptrarray GPtrArray *vlans = NULL;
+	gs_free const NMPlatformBridgeVlan **plat_vlans = NULL;
+
+	if (self->vlan_configured)
+		return TRUE;
+
+	plat = nm_device_get_platform (device);
+	ifindex = nm_device_get_ifindex (device);
+	enabled = nm_setting_bridge_get_vlan_filtering (s_bridge);
+
+	if (!enabled) {
+		nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "0");
+		nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", "1");
+		nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, NULL);
+		return TRUE;
+	}
+
+	hwaddr = nm_platform_link_get_address (plat, ifindex, &length);
+	g_return_val_if_fail (length == ETH_ALEN, FALSE);
+	if (nm_utils_hwaddr_matches (hwaddr, ETH_ALEN, nm_ip_addr_zero.addr_eth, ETH_ALEN)) {
+		/* We need a non-zero MAC address to set the default pvid.
+		 * Retry later. */
+		return TRUE;
+	}
+
+	self->vlan_configured = TRUE;
+
+	/* Filtering must be disabled to change the default PVID */
+	if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "0"))
+		return FALSE;
+
+	/* Clear the default PVID so that we later can force the re-creation of
+	 * default PVID VLANs by writing the option again. */
+	if (!nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", "0"))
+		return FALSE;
+
+	/* Clear all existing VLANs */
+	if (!nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, NULL))
+		return FALSE;
+
+	/* Now set the default PVID. After this point the kernel creates
+	 * a PVID VLAN on each port, including the bridge itself. */
+	pvid = nm_setting_bridge_get_vlan_default_pvid (s_bridge);
+	if (pvid) {
+		char value[32];
+
+		nm_sprintf_buf (value, "%u", pvid);
+		if (!nm_platform_sysctl_master_set_option (plat, ifindex, "default_pvid", value))
+			return FALSE;
+	}
+
+	/* Create VLANs only after setting the default PVID, so that
+	 * any PVID VLAN overrides the bridge's default PVID. */
+	g_object_get (s_bridge, NM_SETTING_BRIDGE_VLANS, &vlans, NULL);
+	plat_vlans = setting_vlans_to_platform (vlans);
+	if (   plat_vlans
+	    && !nm_platform_link_set_bridge_vlans (plat, ifindex, FALSE, plat_vlans))
+		return FALSE;
+
+	if (!nm_platform_sysctl_master_set_option (plat, ifindex, "vlan_filtering", "1"))
+		return FALSE;
+
+	return TRUE;
+}
+
 static NMActStageReturn
 act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMActStageReturn ret;
-	NMConnection *connection = nm_device_get_applied_connection (device);
+	NMConnection *connection;
+	NMSetting *s_bridge;
+	const Option *option;
 
-	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+	NM_DEVICE_BRIDGE (device)->vlan_configured = FALSE;
 
 	ret = NM_DEVICE_CLASS (nm_device_bridge_parent_class)->act_stage1_prepare (device, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
-	if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE))
+	connection = nm_device_get_applied_connection (device);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+	s_bridge = (NMSetting *) nm_connection_get_setting_bridge (connection);
+	g_return_val_if_fail (s_bridge, NM_ACT_STAGE_RETURN_FAILURE);
+
+	if (!nm_device_hw_addr_set_cloned (device, connection, FALSE)) {
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
 
-	commit_master_options (device, nm_connection_get_setting_bridge (connection));
+	for (option = master_options; option->name; option++)
+		commit_option (device, s_bridge, option, FALSE);
+
+	if (!bridge_set_vlan_options (device, (NMSettingBridge *) s_bridge)) {
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
 
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
@@ -457,12 +570,43 @@ enslave_slave (NMDevice *device,
                gboolean configure)
 {
 	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
+	NMConnection *master_connection;
+	NMSettingBridge *s_bridge;
+	NMSettingBridgePort *s_port;
 
 	if (configure) {
 		if (!nm_platform_link_enslave (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)))
 			return FALSE;
 
-		commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection));
+		master_connection = nm_device_get_applied_connection (device);
+		nm_assert (master_connection);
+		s_bridge = nm_connection_get_setting_bridge (master_connection);
+		nm_assert (s_bridge);
+		s_port = nm_connection_get_setting_bridge_port (connection);
+
+		bridge_set_vlan_options (device, s_bridge);
+
+		if (nm_setting_bridge_get_vlan_filtering (s_bridge)) {
+			gs_free const NMPlatformBridgeVlan **plat_vlans = NULL;
+			gs_unref_ptrarray GPtrArray *vlans = NULL;
+
+			if (s_port)
+				g_object_get (s_port, NM_SETTING_BRIDGE_PORT_VLANS, &vlans, NULL);
+
+			plat_vlans = setting_vlans_to_platform (vlans);
+
+			/* Since the link was just enslaved, there are no existing VLANs
+			 * (except for the default one) and so there's no need to flush. */
+
+			if (   plat_vlans
+			    && !nm_platform_link_set_bridge_vlans (nm_device_get_platform (slave),
+			                                           nm_device_get_ifindex (slave),
+			                                           TRUE,
+			                                           plat_vlans))
+				return FALSE;
+		}
+
+		commit_slave_options (slave, s_port);
 
 		_LOGI (LOGD_BRIDGE, "attached bridge port %s",
 		       nm_device_get_ip_iface (slave));
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index 24c99f76..170c7e28 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -48,7 +48,7 @@
 #include "nm-device-factory.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
-#include "nm-utils/nm-udev-utils.h"
+#include "nm-udev-aux/nm-udev-utils.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceEthernet);
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index 6e2372ab..dd98e92f 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -168,7 +168,8 @@ NMIPConfig *nm_device_ip_config_new (NMDevice *self, int addr_family);
 /*****************************************************************************/
 
 gint64 nm_device_get_configured_mtu_from_connection_default (NMDevice *self,
-                                                             const char *property_name);
+                                                             const char *property_name,
+                                                             guint32 max_mtu);
 
 guint32 nm_device_get_configured_mtu_from_connection (NMDevice *device,
                                                       GType setting_type,
diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c
index a9eb1ab4..f8c7d1c3 100644
--- a/src/devices/nm-device-wireguard.c
+++ b/src/devices/nm-device-wireguard.c
@@ -23,7 +23,7 @@
 
 #include "nm-setting-wireguard.h"
 #include "nm-core-internal.h"
-#include "nm-utils/nm-secret-utils.h"
+#include "nm-glib-aux/nm-secret-utils.h"
 #include "nm-device-private.h"
 #include "platform/nm-platform.h"
 #include "platform/nmp-object.h"
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 7514fa78..bd4fbcc3 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -36,17 +36,18 @@
 #include <linux/rtnetlink.h>
 #include <linux/pkt_sched.h>
 
-#include "nm-utils/nm-dedup-multi.h"
-#include "nm-utils/nm-random-utils.h"
-#include "nm-utils/unaligned.h"
+#include "nm-std-aux/unaligned.h"
+#include "nm-glib-aux/nm-dedup-multi.h"
+#include "nm-glib-aux/nm-random-utils.h"
 
-#include "nm-ethtool-utils.h"
-#include "nm-common-macros.h"
+#include "nm-libnm-core-intern/nm-ethtool-utils.h"
+#include "nm-libnm-core-intern/nm-common-macros.h"
 #include "nm-device-private.h"
 #include "NetworkManagerUtils.h"
 #include "nm-manager.h"
 #include "platform/nm-platform.h"
 #include "platform/nmp-object.h"
+#include "platform/nmp-rules-manager.h"
 #include "ndisc/nm-ndisc.h"
 #include "ndisc/nm-lndp-ndisc.h"
 #include "dhcp/nm-dhcp-manager.h"
@@ -1545,8 +1546,7 @@ _set_ip_ifindex (NMDevice *self,
 		                                        priv->ip_ifindex,
 		                                        priv->ip_iface);
 
-		if (nm_platform_check_kernel_support (platform,
-		                                      NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL))
+		if (nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL))
 			nm_platform_link_set_user_ipv6ll_enabled (platform, priv->ip_ifindex, TRUE);
 
 		if (!nm_platform_link_is_up (platform, priv->ip_ifindex))
@@ -2787,8 +2787,10 @@ nm_device_check_connectivity_update_interval (NMDevice *self)
 }
 
 static void
-concheck_update_state (NMDevice *self, int addr_family,
-                       NMConnectivityState state, gboolean allow_periodic_bump)
+concheck_update_state (NMDevice *self,
+                       int addr_family,
+                       NMConnectivityState state,
+                       gboolean allow_periodic_bump)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const gboolean IS_IPv4 = (addr_family == AF_INET);
@@ -2821,6 +2823,14 @@ concheck_update_state (NMDevice *self, int addr_family,
 				state = NM_CONNECTIVITY_LIMITED;
 		} else
 			state = NM_CONNECTIVITY_NONE;
+	} else if (state == NM_CONNECTIVITY_LIMITED) {
+		/* NMConnectivity cannot distinguish between NONE and LIMITED connectivity. In both
+		 * cases, it just failed to fetch the URL.
+		 *
+		 * NMDevice coerces a LIMITED state to NONE here, if the logical state of the device
+		 * is disconnected. */
+		if (priv->state <= NM_DEVICE_STATE_DISCONNECTED)
+			state = NM_CONNECTIVITY_NONE;
 	}
 
 	if (priv->concheck_x[IS_IPv4].state == state) {
@@ -4319,8 +4329,7 @@ realize_start_setup (NMDevice *self,
 		if (priv->firmware_version)
 			_notify (self, PROP_FIRMWARE_VERSION);
 
-		if (nm_platform_check_kernel_support (nm_device_get_platform (self),
-		                                      NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL))
+		if (nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL))
 			priv->ipv6ll_handle = nm_platform_link_get_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ifindex);
 
 		if (nm_platform_link_supports_sriov (nm_device_get_platform (self), priv->ifindex))
@@ -5064,7 +5073,6 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection = nm_device_get_applied_connection (self);
-	NMDeviceState new_state;
 	const char *master_status;
 
 	g_return_if_fail (priv->master);
@@ -5073,16 +5081,17 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason)
 	    && priv->state <= NM_DEVICE_STATE_ACTIVATED) {
 		switch (nm_device_state_reason_check (reason)) {
 		case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
-			new_state = NM_DEVICE_STATE_FAILED;
 			master_status = "failed";
 			break;
 		case NM_DEVICE_STATE_REASON_USER_REQUESTED:
-			new_state = NM_DEVICE_STATE_DEACTIVATING;
 			reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED;
 			master_status = "deactivated by user request";
 			break;
+		case NM_DEVICE_STATE_REASON_CONNECTION_REMOVED:
+			reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED;
+			master_status = "deactivated because master was removed";
+			break;
 		default:
-			new_state = NM_DEVICE_STATE_DISCONNECTED;
 			master_status = "deactivated";
 			break;
 		}
@@ -5093,7 +5102,7 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason)
 
 		/* Cancel any pending activation sources */
 		_cancel_activation (self);
-		nm_device_queue_state (self, new_state, reason);
+		nm_device_queue_state (self, NM_DEVICE_STATE_DEACTIVATING, reason);
 	} else
 		_LOGI (LOGD_DEVICE, "released from master device %s", nm_device_get_iface (priv->master));
 
@@ -5304,7 +5313,8 @@ nm_device_autoconnect_allowed (NMDevice *self)
 		if (priv->state < NM_DEVICE_STATE_DISCONNECTED)
 			return FALSE;
 	} else {
-		/* Unrealized devices can always autoconnect. */
+		if (!nm_device_check_unrealized_device_managed (self))
+			return FALSE;
 	}
 
 	/* The 'autoconnect-allowed' signal is emitted on a device to allow
@@ -6403,6 +6413,84 @@ lldp_init (NMDevice *self, gboolean restart)
 	}
 }
 
+/* set-mode can be:
+ *  - TRUE: sync with new rules.
+ *  - FALSE: sync, but remove all rules (== flush)
+ *  - DEFAULT: forget about all the rules that we previously tracked,
+ *       but don't actually remove them. This is when quitting NM
+ *       we want to keep the rules.
+ *       The problem is, after restart of NM, the rule manager will
+ *       no longer remember that NM added these rules and treat them
+ *       as externally added ones. Don't restart NetworkManager if
+ *       you care about that.
+ */
+static void
+_routing_rules_sync (NMDevice *self,
+                     NMTernary set_mode)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMPRulesManager *rules_manager = nm_netns_get_rules_manager (nm_device_get_netns (self));
+	gboolean untrack_only_dirty = FALSE;
+	gboolean keep_deleted_rules;
+	gpointer user_tag;
+
+	user_tag = priv;
+
+	if (set_mode == NM_TERNARY_TRUE) {
+		NMConnection *applied_connection;
+		NMSettingIPConfig *s_ip;
+		guint i, num;
+		int is_ipv4;
+
+		untrack_only_dirty = TRUE;
+		nmp_rules_manager_set_dirty (rules_manager, user_tag);
+
+		applied_connection = nm_device_get_applied_connection (self);
+
+		for (is_ipv4 = 0; applied_connection && is_ipv4 < 2; is_ipv4++) {
+			int addr_family = is_ipv4 ? AF_INET : AF_INET6;
+
+			s_ip = nm_connection_get_setting_ip_config (applied_connection, addr_family);
+			if (!s_ip)
+				continue;
+
+			num = nm_setting_ip_config_get_num_routing_rules (s_ip);
+			for (i = 0; i < num; i++) {
+				NMPlatformRoutingRule plrule;
+				NMIPRoutingRule *rule;
+
+				rule = nm_setting_ip_config_get_routing_rule (s_ip, i);
+				nm_ip_routing_rule_to_platform (rule, &plrule);
+				nmp_rules_manager_track (rules_manager,
+				                         &plrule,
+				                         10,
+				                         user_tag);
+			}
+		}
+	}
+
+	nmp_rules_manager_untrack_all (rules_manager, user_tag, !untrack_only_dirty);
+
+	keep_deleted_rules = FALSE;
+	if (set_mode == NM_TERNARY_DEFAULT) {
+		/* when exiting NM, we leave the device up and the rules configured.
+		 * We just all nmp_rules_manager_sync() to forget about the synced rules,
+		 * but we don't actually delete them.
+		 *
+		 * FIXME: that is a problem after restart of NetworkManager, because these
+		 * rules will look like externally added, and NM will no longer remove
+		 * them.
+		 * To fix that, we could during "assume" mark the rules of the profile
+		 * as owned (and "added" by the device). The problem with that is that it
+		 * wouldn't cover rules that devices add by internal decision (not because
+		 * of a setting in the profile, e.g. WireGuard could setup policy routing).
+		 * Maybe it would be better to remember these orphaned rules at exit in a
+		 * file and track them after restart again. */
+		keep_deleted_rules = TRUE;
+	}
+	nmp_rules_manager_sync (rules_manager, keep_deleted_rules);
+}
+
 static gboolean
 tc_commit (NMDevice *self)
 {
@@ -6514,6 +6602,8 @@ activate_stage2_device_config (NMDevice *self)
 		}
 	}
 
+	_routing_rules_sync (self, NM_TERNARY_TRUE);
+
 	if (!nm_device_sys_iface_state_is_external_or_assume (self)) {
 		if (!nm_device_bring_up (self, FALSE, &no_firmware)) {
 			if (no_firmware)
@@ -6747,7 +6837,8 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb)
 	NMDedupMultiIter ipconf_iter;
 	AcdData *data;
 	guint timeout;
-	gboolean ret, addr_found;
+	gboolean addr_found;
+	int r;
 	const guint8 *hwaddr_arr;
 	size_t length;
 	guint i;
@@ -6801,9 +6892,8 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb)
 			nm_acd_manager_add_address (acd_manager, address->address);
 	}
 
-	ret = nm_acd_manager_start_probe (acd_manager, timeout);
-
-	if (!ret) {
+	r = nm_acd_manager_start_probe (acd_manager, timeout);
+	if (r < 0) {
 		_LOGW (LOGD_DEVICE, "acd probe failed");
 
 		/* DAD could not be started, signal success */
@@ -8830,12 +8920,13 @@ linklocal6_start (NMDevice *self)
 
 gint64
 nm_device_get_configured_mtu_from_connection_default (NMDevice *self,
-                                                      const char *property_name)
+                                                      const char *property_name,
+                                                      guint32 max_mtu)
 {
 	return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA,
 	                                                    property_name,
 	                                                    self,
-	                                                    0, G_MAXUINT32, -1);
+	                                                    0, max_mtu, -1);
 }
 
 guint32
@@ -8848,6 +8939,7 @@ nm_device_get_configured_mtu_from_connection (NMDevice *self,
 	NMSetting *setting;
 	gint64 mtu_default;
 	guint32 mtu = 0;
+	guint32 max_mtu = G_MAXUINT32;
 
 	nm_assert (NM_IS_DEVICE (self));
 	nm_assert (out_source);
@@ -8870,6 +8962,7 @@ nm_device_get_configured_mtu_from_connection (NMDevice *self,
 		if (setting)
 			mtu = nm_setting_infiniband_get_mtu (NM_SETTING_INFINIBAND (setting));
 		global_property_name = NM_CON_DEFAULT ("infiniband.mtu");
+		max_mtu = NM_INFINIBAND_MAX_MTU;
 	} else if (setting_type == NM_TYPE_SETTING_IP_TUNNEL) {
 		if (setting)
 			mtu = nm_setting_ip_tunnel_get_mtu (NM_SETTING_IP_TUNNEL (setting));
@@ -8881,13 +8974,12 @@ nm_device_get_configured_mtu_from_connection (NMDevice *self,
 	} else
 		g_return_val_if_reached (0);
 
-
 	if (mtu) {
 		*out_source = NM_DEVICE_MTU_SOURCE_CONNECTION;
 		return mtu;
 	}
 
-	mtu_default = nm_device_get_configured_mtu_from_connection_default (self, global_property_name);
+	mtu_default = nm_device_get_configured_mtu_from_connection_default (self, global_property_name, max_mtu);
 	if (mtu_default >= 0) {
 		*out_source = NM_DEVICE_MTU_SOURCE_CONNECTION;
 		return (guint32) mtu_default;
@@ -9140,8 +9232,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 		 * addresses as /128. The reason for the /128 is to prevent the kernel
 		 * from adding a prefix route for this address. */
 		ifa_flags = 0;
-		if (nm_platform_check_kernel_support (nm_device_get_platform (self),
-		                                      NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) {
+		if (nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)) {
 			ifa_flags |= IFA_F_NOPREFIXROUTE;
 			if (NM_IN_SET (priv->ndisc_use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
 			                                         NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR))
@@ -9173,8 +9264,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 		                                  rdata->routes_n,
 		                                  nm_device_get_route_table (self, AF_INET6, TRUE),
 		                                  nm_device_get_route_metric (self, AF_INET6),
-		                                  nm_platform_check_kernel_support (nm_device_get_platform (self),
-		                                                                    NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF));
+		                                  nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF));
 		if (priv->ac_ip6_config.current) {
 			nm_ip6_config_reset_routes_ndisc ((NMIP6Config *) priv->ac_ip6_config.current,
 			                                  rdata->gateways,
@@ -9183,8 +9273,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 			                                  rdata->routes_n,
 			                                  nm_device_get_route_table (self, AF_INET6, TRUE),
 			                                  nm_device_get_route_metric (self, AF_INET6),
-			                                  nm_platform_check_kernel_support (nm_device_get_platform (self),
-			                                                                    NM_PLATFORM_KERNEL_SUPPORT_RTA_PREF));
+			                                  nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_RTA_PREF));
 		}
 
 	}
@@ -9372,8 +9461,7 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 	priv->ndisc_use_tempaddr = use_tempaddr;
 
 	if (   NM_IN_SET (use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
-	    && !nm_platform_check_kernel_support (nm_device_get_platform (self),
-	                                          NM_PLATFORM_KERNEL_SUPPORT_EXTENDED_IFA_FLAGS)) {
+	    && !nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_EXTENDED_IFA_FLAGS)) {
 		_LOGW (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for "
 		                 "IPv6 private addresses. This feature is not available");
 	}
@@ -9480,8 +9568,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	int ifindex = nm_device_get_ip_ifindex (self);
 
-	if (!nm_platform_check_kernel_support (nm_device_get_platform (self),
-	                                       NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL))
+	if (!nm_platform_kernel_support_get (NM_PLATFORM_KERNEL_SUPPORT_TYPE_USER_IPV6LL))
 		return;
 
 	priv->ipv6ll_handle = enable;
@@ -9854,7 +9941,7 @@ nm_device_activate_stage3_ip6_start (NMDevice *self)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActStageReturn ret;
 	NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
-	NMIP6Config *ip6_config = NULL;
+	gs_unref_object NMIP6Config *ip6_config = NULL;
 
 	g_assert (priv->ip_state_6 == NM_DEVICE_IP_STATE_WAIT);
 
@@ -13424,6 +13511,32 @@ nm_device_set_unmanaged_by_flags_queue (NMDevice *self,
 	_set_unmanaged_flags (self, flags, set_op, TRUE, FALSE, reason);
 }
 
+/**
+ * nm_device_check_unrealized_device_managed:
+ *
+ * Checks if a unrealized device is managed from user settings
+ * or user configuration.
+ */
+gboolean
+nm_device_check_unrealized_device_managed (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	nm_assert (!nm_device_is_real (self));
+
+	if (!nm_config_data_get_device_config_boolean (NM_CONFIG_GET_DATA,
+	                                               NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED,
+	                                               self,
+	                                               TRUE,
+	                                               TRUE))
+		return FALSE;
+
+	if (nm_device_spec_match_list (self, nm_settings_get_unmanaged_specs (priv->settings)))
+		return FALSE;
+
+	return TRUE;
+}
+
 void
 nm_device_set_unmanaged_by_user_settings (NMDevice *self)
 {
@@ -14345,6 +14458,11 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 		}
 	}
 
+	_routing_rules_sync (self,
+	                       cleanup_type == CLEANUP_TYPE_KEEP
+	                     ? NM_TERNARY_DEFAULT
+	                     : NM_TERNARY_FALSE);
+
 	if (ifindex > 0)
 		nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), ifindex, NULL);
 
@@ -15879,17 +15997,22 @@ nm_device_spec_match_list_full (NMDevice *self, const GSList *specs, int no_matc
 {
 	NMDeviceClass *klass;
 	NMMatchSpecMatchType m;
+	const char *hw_address = NULL;
+	gboolean is_fake;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
 	klass = NM_DEVICE_GET_CLASS (self);
+	hw_address = nm_device_get_permanent_hw_address_full (self,
+	                                                      !nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT),
+	                                                      &is_fake);
 
 	m = nm_match_spec_device (specs,
 	                          nm_device_get_iface (self),
 	                          nm_device_get_type_description (self),
 	                          nm_device_get_driver (self),
 	                          nm_device_get_driver_version (self),
-	                          nm_device_get_permanent_hw_address (self),
+	                          is_fake ? NULL : hw_address,
 	                          klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL,
 	                          nm_dhcp_manager_get_config (nm_dhcp_manager_get ()));
 
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 45c9dda0..d11c33ab 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -691,6 +691,8 @@ void nm_device_set_unmanaged_by_user_udev (NMDevice *self);
 void nm_device_set_unmanaged_by_user_conf (NMDevice *self);
 void nm_device_set_unmanaged_by_quitting (NMDevice *device);
 
+gboolean nm_device_check_unrealized_device_managed (NMDevice *self);
+
 gboolean nm_device_is_nm_owned (NMDevice *device);
 
 gboolean nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps);
diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c
index cb3b5907..831054ba 100644
--- a/src/devices/nm-lldp-listener.c
+++ b/src/devices/nm-lldp-listener.c
@@ -24,7 +24,9 @@
 
 #include <net/ethernet.h>
 
+#include "nm-std-aux/unaligned.h"
 #include "platform/nm-platform.h"
+#include "nm-glib-aux/nm-c-list.h"
 #include "nm-utils.h"
 
 #include "systemd/nm-sd.h"
@@ -40,6 +42,8 @@ typedef enum {
 	LLDP_ATTR_TYPE_NONE,
 	LLDP_ATTR_TYPE_UINT32,
 	LLDP_ATTR_TYPE_STRING,
+	LLDP_ATTR_TYPE_VARDICT,
+	LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS,
 } LldpAttrType;
 
 typedef enum {
@@ -49,12 +53,18 @@ typedef enum {
 	LLDP_ATTR_ID_SYSTEM_NAME,
 	LLDP_ATTR_ID_SYSTEM_DESCRIPTION,
 	LLDP_ATTR_ID_SYSTEM_CAPABILITIES,
+	LLDP_ATTR_ID_MANAGEMENT_ADDRESSES,
 	LLDP_ATTR_ID_IEEE_802_1_PVID,
 	LLDP_ATTR_ID_IEEE_802_1_PPVID,
 	LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS,
+	LLDP_ATTR_ID_IEEE_802_1_PPVIDS,
 	LLDP_ATTR_ID_IEEE_802_1_VID,
 	LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME,
-	_LLDP_PROP_ID_COUNT,
+	LLDP_ATTR_ID_IEEE_802_1_VLANS,
+	LLDP_ATTR_ID_IEEE_802_3_MAC_PHY_CONF,
+	LLDP_ATTR_ID_IEEE_802_3_POWER_VIA_MDI,
+	LLDP_ATTR_ID_IEEE_802_3_MAX_FRAME_SIZE,
+	_LLDP_ATTR_ID_COUNT,
 } LldpAttrId;
 
 typedef struct {
@@ -62,6 +72,8 @@ typedef struct {
 	union {
 		guint32 v_uint32;
 		char *v_string;
+		GVariant *v_variant;
+		CList v_variant_list;
 	};
 } LldpAttrData;
 
@@ -109,7 +121,7 @@ typedef struct {
 
 	bool valid:1;
 
-	LldpAttrData attrs[_LLDP_PROP_ID_COUNT];
+	LldpAttrData attrs[_LLDP_ATTR_ID_COUNT];
 
 	GVariant *variant;
 } LldpNeighbor;
@@ -155,35 +167,26 @@ ether_addr_equal (const struct ether_addr *a1, const struct ether_addr *a2)
 	return memcmp (a1, a2, ETH_ALEN) == 0;
 }
 
-static guint32
-_access_uint8 (const void *data)
-{
-	return *((const guint8 *) data);
-}
-
-static guint32
-_access_uint16 (const void *data)
-{
-	guint16 v;
-
-	memcpy (&v, data, sizeof (v));
-	return ntohs (v);
-}
-
 /*****************************************************************************/
 
 NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_lldp_attr_id_to_name, LldpAttrId,
 	NM_UTILS_LOOKUP_DEFAULT_WARN (NULL),
-	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_PORT_DESCRIPTION,        NM_LLDP_ATTR_PORT_DESCRIPTION),
-	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_NAME,             NM_LLDP_ATTR_SYSTEM_NAME),
-	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_DESCRIPTION,      NM_LLDP_ATTR_SYSTEM_DESCRIPTION),
-	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_CAPABILITIES,     NM_LLDP_ATTR_SYSTEM_CAPABILITIES),
-	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PVID,         NM_LLDP_ATTR_IEEE_802_1_PVID),
-	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID,        NM_LLDP_ATTR_IEEE_802_1_PPVID),
-	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS,  NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS),
-	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VID,          NM_LLDP_ATTR_IEEE_802_1_VID),
-	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME,    NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME),
-	NM_UTILS_LOOKUP_ITEM_IGNORE (_LLDP_PROP_ID_COUNT),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_PORT_DESCRIPTION,         NM_LLDP_ATTR_PORT_DESCRIPTION),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_NAME,              NM_LLDP_ATTR_SYSTEM_NAME),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_DESCRIPTION,       NM_LLDP_ATTR_SYSTEM_DESCRIPTION),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_SYSTEM_CAPABILITIES,      NM_LLDP_ATTR_SYSTEM_CAPABILITIES),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_MANAGEMENT_ADDRESSES,     NM_LLDP_ATTR_MANAGEMENT_ADDRESSES),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PVID,          NM_LLDP_ATTR_IEEE_802_1_PVID),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID,         NM_LLDP_ATTR_IEEE_802_1_PPVID),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS,   NM_LLDP_ATTR_IEEE_802_1_PPVID_FLAGS),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVIDS,        NM_LLDP_ATTR_IEEE_802_1_PPVIDS),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VID,           NM_LLDP_ATTR_IEEE_802_1_VID),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME,     NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLANS,         NM_LLDP_ATTR_IEEE_802_1_VLANS),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_3_MAC_PHY_CONF,  NM_LLDP_ATTR_IEEE_802_3_MAC_PHY_CONF),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_3_POWER_VIA_MDI, NM_LLDP_ATTR_IEEE_802_3_POWER_VIA_MDI),
+	NM_UTILS_LOOKUP_STR_ITEM (LLDP_ATTR_ID_IEEE_802_3_MAX_FRAME_SIZE,NM_LLDP_ATTR_IEEE_802_3_MAX_FRAME_SIZE),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (_LLDP_ATTR_ID_COUNT),
 );
 
 _NM_UTILS_LOOKUP_DEFINE (static, _lldp_attr_id_to_type, LldpAttrId, LldpAttrType,
@@ -192,12 +195,18 @@ _NM_UTILS_LOOKUP_DEFINE (static, _lldp_attr_id_to_type, LldpAttrId, LldpAttrType
 	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_NAME,                 LLDP_ATTR_TYPE_STRING),
 	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_DESCRIPTION,          LLDP_ATTR_TYPE_STRING),
 	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_SYSTEM_CAPABILITIES,         LLDP_ATTR_TYPE_UINT32),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_MANAGEMENT_ADDRESSES,        LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS),
 	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PVID,             LLDP_ATTR_TYPE_UINT32),
 	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID,            LLDP_ATTR_TYPE_UINT32),
 	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS,      LLDP_ATTR_TYPE_UINT32),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_PPVIDS,           LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS),
 	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_VID,              LLDP_ATTR_TYPE_UINT32),
 	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME,        LLDP_ATTR_TYPE_STRING),
-	NM_UTILS_LOOKUP_ITEM_IGNORE (_LLDP_PROP_ID_COUNT),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_1_VLANS,            LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_3_MAC_PHY_CONF,     LLDP_ATTR_TYPE_VARDICT),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_3_POWER_VIA_MDI,    LLDP_ATTR_TYPE_VARDICT),
+	NM_UTILS_LOOKUP_ITEM (LLDP_ATTR_ID_IEEE_802_3_MAX_FRAME_SIZE,   LLDP_ATTR_TYPE_UINT32),
+	NM_UTILS_LOOKUP_ITEM_IGNORE (_LLDP_ATTR_ID_COUNT),
 );
 
 static void
@@ -216,13 +225,8 @@ _lldp_attr_set_str (LldpAttrData *pdata, LldpAttrId attr_id, const char *v_strin
 }
 
 static void
-_lldp_attr_set_str_ptr (LldpAttrData *pdata, LldpAttrId attr_id, const void *str, gsize len)
+_lldp_attr_take_str_ptr (LldpAttrData *pdata, LldpAttrId attr_id, char *str)
 {
-	const char *s = str;
-	const char *tmp;
-	gsize len0 = len;
-	gs_free char *str_free = NULL;
-
 	nm_assert (pdata);
 	nm_assert (_lldp_attr_id_to_type (attr_id) == LLDP_ATTR_TYPE_STRING);
 
@@ -233,23 +237,7 @@ _lldp_attr_set_str_ptr (LldpAttrData *pdata, LldpAttrId attr_id, const void *str
 		return;
 
 	pdata->attr_type = LLDP_ATTR_TYPE_STRING;
-
-	/* truncate at first NUL, including removing trailing NULs*/
-	tmp = memchr (s, '\0', len);
-	if (tmp)
-		len = tmp - s;
-
-	if (!len) {
-		pdata->v_string = g_strdup ("");
-		return;
-	}
-
-	if (len0 <= len || s[len] != '\0') {
-		/* hmpf, g_strescape needs a trailing NUL. Need to clone */
-		s = str_free = g_strndup (s, len);
-	}
-
-	pdata->v_string = g_strescape (s, NULL);
+	pdata->v_string = str;
 }
 
 static void
@@ -267,6 +255,44 @@ _lldp_attr_set_uint32 (LldpAttrData *pdata, LldpAttrId attr_id, guint32 v_uint32
 	pdata->v_uint32 = v_uint32;
 }
 
+static void
+_lldp_attr_set_vardict (LldpAttrData *pdata, LldpAttrId attr_id, GVariant *variant)
+{
+
+	nm_assert (pdata);
+	nm_assert (_lldp_attr_id_to_type (attr_id) == LLDP_ATTR_TYPE_VARDICT);
+
+	pdata = &pdata[attr_id];
+
+	/* we ignore duplicate fields silently */
+	if (pdata->attr_type != LLDP_ATTR_TYPE_NONE) {
+		if (g_variant_is_floating (variant))
+			g_variant_unref (variant);
+		return;
+	}
+
+	pdata->attr_type = LLDP_ATTR_TYPE_VARDICT;
+	pdata->v_variant = g_variant_ref_sink (variant);
+}
+
+static void
+_lldp_attr_add_vardict (LldpAttrData *pdata, LldpAttrId attr_id, GVariant *variant)
+{
+	nm_assert (pdata);
+	nm_assert (_lldp_attr_id_to_type (attr_id) == LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS);
+
+	g_variant_ref_sink (variant);
+	pdata = &pdata[attr_id];
+
+	if (pdata->attr_type == LLDP_ATTR_TYPE_NONE) {
+		c_list_init (&pdata->v_variant_list);
+		pdata->attr_type = LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS;
+	} else
+		nm_assert (pdata->attr_type == LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS);
+
+	c_list_link_tail (&pdata->v_variant_list, &nm_c_list_elem_new_stale (variant)->lst);
+}
+
 /*****************************************************************************/
 
 static guint
@@ -312,13 +338,28 @@ static void
 lldp_neighbor_free (LldpNeighbor *neighbor)
 {
 	LldpAttrId attr_id;
+	LldpAttrType attr_type;
 
 	if (neighbor) {
 		g_free (neighbor->chassis_id);
 		g_free (neighbor->port_id);
-		for (attr_id = 0; attr_id < _LLDP_PROP_ID_COUNT; attr_id++) {
-			if (neighbor->attrs[attr_id].attr_type == LLDP_ATTR_TYPE_STRING)
+		for (attr_id = 0; attr_id < _LLDP_ATTR_ID_COUNT; attr_id++) {
+			attr_type = neighbor->attrs[attr_id].attr_type;
+
+			switch (attr_type) {
+			case LLDP_ATTR_TYPE_STRING:
 				g_free (neighbor->attrs[attr_id].v_string);
+				break;
+			case LLDP_ATTR_TYPE_VARDICT:
+				g_variant_unref (neighbor->attrs[attr_id].v_variant);
+				break;
+			case LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS:
+				nm_c_list_elem_free_all (&neighbor->attrs[attr_id].v_variant_list,
+				                         (GDestroyNotify) g_variant_unref);
+				break;
+			default:
+				;
+			}
 		}
 		g_clear_pointer (&neighbor->variant, g_variant_unref);
 		g_slice_free (LldpNeighbor, neighbor);
@@ -346,7 +387,7 @@ lldp_neighbor_equal (LldpNeighbor *a, LldpNeighbor *b)
 	    || !nm_streq0 (a->port_id, b->port_id))
 		return FALSE;
 
-	for (attr_id = 0; attr_id < _LLDP_PROP_ID_COUNT; attr_id++) {
+	for (attr_id = 0; attr_id < _LLDP_ATTR_ID_COUNT; attr_id++) {
 		if (a->attrs[attr_id].attr_type != b->attrs[attr_id].attr_type)
 			return FALSE;
 		switch (a->attrs[attr_id].attr_type) {
@@ -367,6 +408,75 @@ lldp_neighbor_equal (LldpNeighbor *a, LldpNeighbor *b)
 	return TRUE;
 }
 
+static GVariant *
+parse_management_address_tlv (uint8_t *data, gsize len)
+{
+	GVariantDict dict;
+	GVariant *variant;
+	gsize addr_len, oid_len;
+
+	/* 802.1AB-2009 - Figure 8-11
+	 *
+	 * - TLV type / length        (2 bytes)
+	 * - address string length    (1 byte)
+	 * - address subtype          (1 byte)
+	 * - address                  (1 to 31 bytes)
+	 * - interface number subtype (1 byte)
+	 * - interface number         (4 bytes)
+	 * - OID string length        (1 byte)
+	 * - OID                      (0 to 128 bytes)
+	 */
+
+	if (len < 11)
+		goto err;
+
+	nm_assert ((data[0] >> 1) == SD_LLDP_TYPE_MGMT_ADDRESS);
+	nm_assert ((((data[0] & 1) << 8) + data[1]) + 2 == len);
+
+	data += 2;
+	len -= 2;
+	addr_len = *data; /* length of (address subtype + address) */
+
+	if (addr_len < 2 || addr_len > 32)
+		goto err;
+	if (len < (  1         /* address stringth length */
+	           + addr_len  /* address subtype + address */
+	           + 5         /* interface */
+	           + 1))       /* oid */
+		goto err;
+
+	g_variant_dict_init (&dict, NULL);
+
+	data++;
+	len--;
+	g_variant_dict_insert (&dict, "address-subtype", "u", (guint32) *data);
+	variant = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data + 1, addr_len - 1, 1);
+	g_variant_dict_insert_value (&dict, "address", variant);
+
+	data += addr_len;
+	len -= addr_len;
+	g_variant_dict_insert (&dict, "interface-number-subtype", "u", (guint32) *data);
+
+	data++;
+	len--;
+	g_variant_dict_insert (&dict, "interface-number", "u", unaligned_read_be32 (data));
+
+	data += 4;
+	len -= 4;
+	oid_len = *data;
+
+	if (len < (1 + oid_len))
+		goto err;
+
+	data++;
+	variant = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data, oid_len, 1);
+	g_variant_dict_insert_value (&dict, "object-id", variant);
+	return g_variant_dict_end (&dict);
+err:
+	g_variant_dict_clear (&dict);
+	return NULL;
+}
+
 static LldpNeighbor *
 lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error)
 {
@@ -468,7 +578,29 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error)
 	}
 	do {
 		guint8 oui[3];
-		guint8 subtype;
+		guint8 type, subtype;
+		GVariant *variant;
+
+		if (sd_lldp_neighbor_tlv_get_type (neighbor_sd, &type) < 0)
+			continue;
+
+		if (sd_lldp_neighbor_tlv_get_raw (neighbor_sd, (void *) &data8, &len) < 0)
+			continue;
+
+		switch (type) {
+		case SD_LLDP_TYPE_MGMT_ADDRESS:
+			variant = parse_management_address_tlv (data8, len);
+			if (variant) {
+				_lldp_attr_add_vardict (neigh->attrs,
+				                        LLDP_ATTR_ID_MANAGEMENT_ADDRESSES,
+				                        variant);
+			}
+			continue;
+		case SD_LLDP_TYPE_PRIVATE:
+			break;
+		default:
+			continue;
+		}
 
 		r = sd_lldp_neighbor_tlv_get_oui (neighbor_sd, oui, &subtype);
 		if (r < 0) {
@@ -479,18 +611,12 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error)
 			goto out;
 		}
 
-		if (!(   memcmp (oui, SD_LLDP_OUI_802_1, sizeof (oui)) == 0
-		      && NM_IN_SET (subtype,
-		                    SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID,
-		                    SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID,
-		                    SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME)))
-			continue;
-
-		if (sd_lldp_neighbor_tlv_get_raw (neighbor_sd, (void *) &data8, &len) < 0)
+		if (   memcmp (oui, SD_LLDP_OUI_802_1, sizeof (oui)) != 0
+		    && memcmp (oui, SD_LLDP_OUI_802_3, sizeof (oui)) != 0)
 			continue;
 
 		/* skip over leading TLV, OUI and subtype */
-#ifdef WITH_MORE_ASSERTS
+#if NM_MORE_ASSERTS > 5
 		{
 			guint8 check_hdr[] = {
 				0xfe | (((len - 2) >> 8) & 0x01), ((len - 2) & 0xFF),
@@ -507,25 +633,37 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error)
 		data8 += 6;
 		len -= 6;
 
-		/*if (memcmp (oui, SD_LLDP_OUI_802_1, sizeof (oui)) == 0)*/
-		{
+		if (memcmp (oui, SD_LLDP_OUI_802_1, sizeof (oui)) == 0) {
+			GVariantDict dict;
+
 			switch (subtype) {
 			case SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID:
 				if (len != 2)
 					continue;
 				_lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_PVID,
-				                       _access_uint16 (data8));
+				                       unaligned_read_be16 (data8));
 				break;
 			case SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID:
 				if (len != 3)
 					continue;
 				_lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_PPVID_FLAGS,
-				                       _access_uint8 (&data8[0]));
+				                       data8[0]);
 				_lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_PPVID,
-				                       _access_uint16 (&data8[1]));
+				                       unaligned_read_be16 (&data8[1]));
+
+				g_variant_dict_init (&dict, NULL);
+				g_variant_dict_insert (&dict, "ppvid", "u", (guint32) unaligned_read_be16 (&data8[1]));
+				g_variant_dict_insert (&dict, "flags", "u", (guint32) data8[0]);
+
+				_lldp_attr_add_vardict (neigh->attrs,
+				                        LLDP_ATTR_ID_IEEE_802_1_PPVIDS,
+				                        g_variant_dict_end (&dict));
 				break;
 			case SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME: {
 				int l;
+				guint32 vid;
+				const char *name;
+				char *name_to_free;
 
 				if (len <= 3)
 					continue;
@@ -536,14 +674,62 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error)
 				if (l > 32)
 					continue;
 
-				_lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_VID,
-				                       _access_uint16 (&data8[0]));
-				_lldp_attr_set_str_ptr (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME,
-				                        &data8[3], l);
+				name = nm_utils_buf_utf8safe_escape (&data8[3], l, 0, &name_to_free);
+				vid = unaligned_read_be16 (&data8[0]);
+
+				g_variant_dict_init (&dict, NULL);
+				g_variant_dict_insert (&dict, "vid", "u", vid);
+				g_variant_dict_insert (&dict, "name", "s", name);
+
+				_lldp_attr_add_vardict (neigh->attrs,
+				                        LLDP_ATTR_ID_IEEE_802_1_VLANS,
+				                        g_variant_dict_end (&dict));
+
+				_lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_1_VID, vid);
+				_lldp_attr_take_str_ptr (neigh->attrs,
+				                         LLDP_ATTR_ID_IEEE_802_1_VLAN_NAME,
+				                         name_to_free ?: g_strdup (name));
 				break;
 			}
 			default:
-				g_assert_not_reached ();
+				continue;
+			}
+		} else if (memcmp (oui, SD_LLDP_OUI_802_3, sizeof (oui)) == 0) {
+			GVariantDict dict;
+
+			switch (subtype) {
+			case SD_LLDP_OUI_802_3_SUBTYPE_MAC_PHY_CONFIG_STATUS:
+				if (len != 5)
+					continue;
+
+				g_variant_dict_init (&dict, NULL);
+				g_variant_dict_insert (&dict, "autoneg", "u", (guint32) data8[0]);
+				g_variant_dict_insert (&dict, "pmd-autoneg-cap", "u", (guint32) unaligned_read_be16 (&data8[1]));
+				g_variant_dict_insert (&dict, "operational-mau-type", "u", (guint32) unaligned_read_be16 (&data8[3]));
+
+				_lldp_attr_set_vardict (neigh->attrs,
+				                        LLDP_ATTR_ID_IEEE_802_3_MAC_PHY_CONF,
+				                        g_variant_dict_end (&dict));
+				break;
+			case SD_LLDP_OUI_802_3_SUBTYPE_POWER_VIA_MDI:
+				if (len != 3)
+					continue;
+
+				g_variant_dict_init (&dict, NULL);
+				g_variant_dict_insert (&dict, "mdi-power-support", "u", (guint32) data8[0]);
+				g_variant_dict_insert (&dict, "pse-power-pair", "u", (guint32) data8[1]);
+				g_variant_dict_insert (&dict, "power-class", "u", (guint32) data8[2]);
+
+				_lldp_attr_set_vardict (neigh->attrs,
+				                        LLDP_ATTR_ID_IEEE_802_3_POWER_VIA_MDI,
+				                        g_variant_dict_end (&dict));
+				break;
+			case SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE:
+				if (len != 2)
+					continue;
+				_lldp_attr_set_uint32 (neigh->attrs, LLDP_ATTR_ID_IEEE_802_3_MAX_FRAME_SIZE,
+				                       unaligned_read_be16 (data8));
+				break;
 			}
 		}
 	} while (sd_lldp_neighbor_tlv_next (neighbor_sd) > 0);
@@ -593,7 +779,7 @@ lldp_neighbor_to_variant (LldpNeighbor *neigh)
 		                       g_variant_new_string (dest_str));
 	}
 
-	for (attr_id = 0; attr_id < _LLDP_PROP_ID_COUNT; attr_id++) {
+	for (attr_id = 0; attr_id < _LLDP_ATTR_ID_COUNT; attr_id++) {
 		const LldpAttrData *data = &neigh->attrs[attr_id];
 
 		nm_assert (NM_IN_SET (data->attr_type, _lldp_attr_id_to_type (attr_id), LLDP_ATTR_TYPE_NONE));
@@ -608,7 +794,26 @@ lldp_neighbor_to_variant (LldpNeighbor *neigh)
 			                       _lldp_attr_id_to_name (attr_id),
 			                       g_variant_new_string (data->v_string));
 			break;
-		default:
+		case LLDP_ATTR_TYPE_VARDICT:
+			g_variant_builder_add (&builder, "{sv}",
+			                       _lldp_attr_id_to_name (attr_id),
+			                       data->v_variant);
+			break;
+		case LLDP_ATTR_TYPE_ARRAY_OF_VARDICTS: {
+			NMCListElem *elem;
+			GVariantBuilder builder2;
+
+			g_variant_builder_init (&builder2, G_VARIANT_TYPE ("aa{sv}"));
+
+			c_list_for_each_entry (elem, &data->v_variant_list, lst)
+				g_variant_builder_add_value (&builder2, elem->data);
+
+			g_variant_builder_add (&builder, "{sv}",
+			                       _lldp_attr_id_to_name (attr_id),
+			                       g_variant_builder_end (&builder2));
+			break;
+		}
+		case LLDP_ATTR_TYPE_NONE:
 			break;
 		}
 	}
diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c
index b96eba68..35eb739f 100644
--- a/src/devices/ovs/nm-device-ovs-port.c
+++ b/src/devices/ovs/nm-device-ovs-port.c
@@ -20,6 +20,7 @@
 #include "nm-default.h"
 
 #include "nm-device-ovs-port.h"
+#include "nm-device-ovs-interface.h"
 #include "nm-ovsdb.h"
 
 #include "devices/nm-device-private.h"
@@ -141,6 +142,11 @@ release_slave (NMDevice *device, NMDevice *slave, gboolean configure)
 {
 	nm_ovsdb_del_interface (nm_ovsdb_get (), nm_device_get_iface (slave),
 	                        del_iface_cb, g_object_ref (slave));
+
+	/* Open VSwitch is going to delete this one. We must ignore what happens
+	 * next with the interface. */
+	if (NM_IS_DEVICE_OVS_INTERFACE (slave))
+		nm_device_update_from_platform_link (slave, NULL);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/ovs/nm-ovs-factory.c b/src/devices/ovs/nm-ovs-factory.c
index 766c650b..2124b2a0 100644
--- a/src/devices/ovs/nm-ovs-factory.c
+++ b/src/devices/ovs/nm-ovs-factory.c
@@ -132,7 +132,7 @@ ovsdb_device_removed (NMOvsdb *ovsdb, const char *name, NMDeviceType device_type
 	    && device_state < NM_DEVICE_STATE_DEACTIVATING) {
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_DEACTIVATING,
-		                         NM_DEVICE_STATE_REASON_REMOVED);
+		                         NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED);
 	} else if (device_state == NM_DEVICE_STATE_UNMANAGED) {
 		nm_device_unrealize (device, TRUE, NULL);
 	}
diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c
index 9d73c3ac..5b50f840 100644
--- a/src/devices/ovs/nm-ovsdb.c
+++ b/src/devices/ovs/nm-ovsdb.c
@@ -24,7 +24,7 @@
 #include <gmodule.h>
 #include <gio/gunixsocketaddress.h>
 
-#include "nm-utils/nm-jansson.h"
+#include "nm-glib-aux/nm-jansson.h"
 #include "devices/nm-device.h"
 #include "platform/nm-platform.h"
 #include "nm-core-internal.h"
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index 38a6dd8c..287f4d1b 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -30,14 +30,14 @@
 #include <teamdctl.h>
 #include <stdlib.h>
 
-#include "nm-utils/nm-jansson.h"
+#include "nm-glib-aux/nm-jansson.h"
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
 #include "platform/nm-platform.h"
 #include "nm-config.h"
 #include "nm-core-internal.h"
 #include "nm-ip4-config.h"
-#include "nm-dbus-compat.h"
+#include "nm-std-aux/nm-dbus-compat.h"
 
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceTeam);
@@ -408,8 +408,10 @@ teamd_dbus_appeared (GDBusConnection *connection,
 			success = teamd_read_config (device);
 		if (success)
 			nm_device_activate_schedule_stage2_device_config (device);
-		else if (!nm_device_sys_iface_state_is_external_or_assume (device))
+		else if (!nm_device_sys_iface_state_is_external_or_assume (device)) {
+			teamd_cleanup (device, TRUE);
 			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
+		}
 	}
 }
 
diff --git a/src/devices/tests/test-acd.c b/src/devices/tests/test-acd.c
index aff71825..8b52ee2c 100644
--- a/src/devices/tests/test-acd.c
+++ b/src/devices/tests/test-acd.c
@@ -111,8 +111,8 @@ acd_manager_probe_terminated (NMAcdManager *acd_manager, gpointer user_data)
 static void
 test_acd_common (test_fixture *fixture, TestInfo *info)
 {
-	NMAcdManager *manager;
-	GMainLoop *loop;
+	nm_auto_free_acdmgr NMAcdManager *manager = NULL;
+	nm_auto_unref_gmainloop GMainLoop *loop = NULL;
 	int i;
 	const guint WAIT_TIME_OPTIMISTIC = 50;
 	guint wait_time;
@@ -120,6 +120,7 @@ test_acd_common (test_fixture *fixture, TestInfo *info)
 		.probe_terminated_callback = acd_manager_probe_terminated,
 		.user_data_destroy         = (GDestroyNotify) g_main_loop_unref,
 	};
+	int r;
 
 	if (_skip_acd_test ())
 		return;
@@ -131,8 +132,10 @@ test_acd_common (test_fixture *fixture, TestInfo *info)
 	wait_time = WAIT_TIME_OPTIMISTIC;
 again:
 
+	nm_clear_pointer (&loop, g_main_loop_unref);
 	loop = g_main_loop_new (NULL, FALSE);
 
+	nm_clear_pointer (&manager, nm_acd_manager_free);
 	manager = nm_acd_manager_new (fixture->ifindex0,
 	                              fixture->hwaddr0,
 	                              fixture->hwaddr0_len,
@@ -148,9 +151,10 @@ again:
 		                        24, 0, 3600, 1800, 0, NULL);
 	}
 
-	g_assert (nm_acd_manager_start_probe (manager, wait_time));
+	r = nm_acd_manager_start_probe (manager, wait_time);
+	g_assert_cmpint (r, ==, 0);
+
 	g_assert (nmtst_main_loop_run (loop, 2000));
-	g_main_loop_unref (loop);
 
 	for (i = 0; info->addresses[i]; i++) {
 		gboolean val;
@@ -164,7 +168,6 @@ again:
 			/* probably we just had a glitch and the system took longer than
 			 * expected. Re-verify with a large timeout this time. */
 			wait_time = 1000;
-			nm_clear_pointer (&manager, nm_acd_manager_free);
 			goto again;
 		}
 
@@ -172,8 +175,6 @@ again:
 		         i, nm_utils_inet4_ntop (info->addresses[i], sbuf),
 		         info->expected_result[i] ? "detect no duplicated" : "detect a duplicate");
 	}
-
-	nm_acd_manager_free (manager);
 }
 
 static void
@@ -199,8 +200,9 @@ test_acd_probe_2 (test_fixture *fixture, gconstpointer user_data)
 static void
 test_acd_announce (test_fixture *fixture, gconstpointer user_data)
 {
-	NMAcdManager *manager;
-	GMainLoop *loop;
+	nm_auto_free_acdmgr NMAcdManager *manager = NULL;
+	nm_auto_unref_gmainloop GMainLoop *loop = NULL;
+	int r;
 
 	if (_skip_acd_test ())
 		return;
@@ -216,11 +218,9 @@ test_acd_announce (test_fixture *fixture, gconstpointer user_data)
 	g_assert (nm_acd_manager_add_address (manager, ADDR2));
 
 	loop = g_main_loop_new (NULL, FALSE);
-	nm_acd_manager_announce_addresses (manager);
+	r = nm_acd_manager_announce_addresses (manager);
+	g_assert_cmpint (r, ==, 0);
 	g_assert (!nmtst_main_loop_run (loop, 200));
-	g_main_loop_unref (loop);
-
-	nm_acd_manager_free (manager);
 }
 
 static void
diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c
index 7227d082..7b135f58 100644
--- a/src/devices/tests/test-lldp.c
+++ b/src/devices/tests/test-lldp.c
@@ -231,8 +231,10 @@ TEST_RECV_FRAME_DEFINE (_test_recv_data1_frame0,
 static void
 _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener)
 {
-	GVariant *neighbors, *attr;
+	GVariant *neighbors, *attr, *child;
 	gs_unref_variant GVariant *neighbor = NULL;
+	guint v_uint = 0;
+	const char *v_str = NULL;
 
 	neighbors = nm_lldp_listener_get_neighbors (listener);
 	nmtst_assert_variant_is_of_type (neighbors, G_VARIANT_TYPE ("aa{sv}"));
@@ -242,7 +244,7 @@ _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener)
 	                              SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS, "00:01:30:F9:AD:A0",
 	                              SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME, "1/1");
 	g_assert (neighbor);
-	g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 10);
+	g_assert_cmpint (g_variant_n_children (neighbor), ==, 4 + 16);
 
 	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_DESTINATION, G_VARIANT_TYPE_STRING);
 	nmtst_assert_variant_string (attr, NM_LLDP_DEST_NEAREST_BRIDGE);
@@ -270,11 +272,49 @@ _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener)
 	nmtst_assert_variant_uint32 (attr, 20);
 	nm_clear_g_variant (&attr);
 
-	/* unsupported: Management Address */
-	/* unsupported: IEEE 802.3 - Power Via MDI */
-	/* unsupported: IEEE 802.3 - MAC/PHY Configuration/Status */
+	/* Management Address */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_MANAGEMENT_ADDRESSES, G_VARIANT_TYPE ("aa{sv}"));
+	g_assert (attr);
+	g_assert_cmpuint (g_variant_n_children (attr), ==, 1);
+	child = g_variant_get_child_value (attr, 0);
+	g_assert (child);
+	g_variant_lookup (child, "interface-number", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 1001);
+	g_variant_lookup (child, "interface-number-subtype", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 2);
+	g_variant_lookup (child, "address-subtype", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 6);
+	nm_clear_g_variant (&child);
+	nm_clear_g_variant (&attr);
+
+	/* IEEE 802.3 - Power Via MDI */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_3_POWER_VIA_MDI, G_VARIANT_TYPE_VARDICT);
+	g_assert (attr);
+	g_variant_lookup (attr, "mdi-power-support", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 7);
+	g_variant_lookup (attr, "pse-power-pair", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 1);
+	g_variant_lookup (attr, "power-class", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 0);
+	nm_clear_g_variant (&attr);
+
+	/* IEEE 802.3 - MAC/PHY Configuration/Status */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_3_MAC_PHY_CONF, G_VARIANT_TYPE_VARDICT);
+	g_assert (attr);
+	g_variant_lookup (attr, "autoneg", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 3);
+	g_variant_lookup (attr, "pmd-autoneg-cap", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 0x6c00);
+	g_variant_lookup (attr, "operational-mau-type", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 16);
+	nm_clear_g_variant (&attr);
+
 	/* unsupported: IEEE 802.3 - Link Aggregation */
-	/* unsupported: IEEE 802.3 - Maximum Frame Size*/
+
+	/* Maximum Frame Size */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_3_MAX_FRAME_SIZE, G_VARIANT_TYPE_UINT32);
+	nmtst_assert_variant_uint32 (attr, 1522);
+	nm_clear_g_variant (&attr);
 
 	/* IEEE 802.1 - Port VLAN ID */
 	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PVID, G_VARIANT_TYPE_UINT32);
@@ -289,6 +329,18 @@ _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener)
 	nmtst_assert_variant_uint32 (attr, 1);
 	nm_clear_g_variant (&attr);
 
+	/* new PPVID attributes */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_PPVIDS, G_VARIANT_TYPE ("aa{sv}"));
+	g_assert_cmpuint (g_variant_n_children (attr), ==, 1);
+	child = g_variant_get_child_value (attr, 0);
+	g_assert (child);
+	g_variant_lookup (child, "ppvid", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 0);
+	g_variant_lookup (child, "flags", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 1);
+	nm_clear_g_variant (&child);
+	nm_clear_g_variant (&attr);
+
 	/* IEEE 802.1 - VLAN Name */
 	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_VLAN_NAME, G_VARIANT_TYPE_STRING);
 	nmtst_assert_variant_string (attr, "v2-0488-03-0505");
@@ -297,6 +349,18 @@ _test_recv_data1_check (GMainLoop *loop, NMLldpListener *listener)
 	nmtst_assert_variant_uint32 (attr, 488);
 	nm_clear_g_variant (&attr);
 
+	/* new VLAN attributes */
+	attr = g_variant_lookup_value (neighbor, NM_LLDP_ATTR_IEEE_802_1_VLANS, G_VARIANT_TYPE ("aa{sv}"));
+	g_assert_cmpuint (g_variant_n_children (attr), ==, 1);
+	child = g_variant_get_child_value (attr, 0);
+	g_assert (child);
+	g_variant_lookup (child, "vid", "u", &v_uint);
+	g_assert_cmpint (v_uint, ==, 488);
+	g_variant_lookup (child, "name", "&s", &v_str);
+	g_assert_cmpstr (v_str, ==, "v2-0488-03-0505");
+	nm_clear_g_variant (&child);
+	nm_clear_g_variant (&attr);
+
 	/* unsupported: IEEE 802.1 - Protocol Identity */
 }
 
diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c
index dcc161d2..da7d0685 100644
--- a/src/devices/wifi/nm-device-iwd.c
+++ b/src/devices/wifi/nm-device-iwd.c
@@ -22,7 +22,7 @@
 
 #include "nm-device-iwd.h"
 
-#include "nm-common-macros.h"
+#include "nm-libnm-core-intern/nm-common-macros.h"
 #include "devices/nm-device.h"
 #include "devices/nm-device-private.h"
 #include "nm-utils.h"
@@ -39,7 +39,7 @@
 #include "nm-config.h"
 #include "nm-iwd-manager.h"
 #include "nm-dbus-manager.h"
-#include "nm-dbus-compat.h"
+#include "nm-std-aux/nm-dbus-compat.h"
 
 #include "devices/nm-device-logging.h"
 _LOG_DECLARE_SELF(NMDeviceIwd);
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 64869672..3a58c620 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -28,7 +28,7 @@
 
 #include "nm-device-wifi-p2p.h"
 #include "nm-wifi-ap.h"
-#include "nm-common-macros.h"
+#include "nm-libnm-core-intern/nm-common-macros.h"
 #include "devices/nm-device.h"
 #include "devices/nm-device-private.h"
 #include "nm-dbus-manager.h"
diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c
index d668f0d8..494fca32 100644
--- a/src/devices/wifi/nm-iwd-manager.c
+++ b/src/devices/wifi/nm-iwd-manager.c
@@ -29,7 +29,7 @@
 #include "nm-manager.h"
 #include "nm-device-iwd.h"
 #include "nm-wifi-utils.h"
-#include "nm-utils/nm-random-utils.h"
+#include "nm-glib-aux/nm-random-utils.h"
 #include "settings/nm-settings.h"
 
 /*****************************************************************************/
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 1cb549b0..ffdc61a6 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -679,19 +679,6 @@ complete_connection (NMModem *_self,
 	}
 
 	if (MODEM_CAPS_3GPP (modem_caps)) {
-		NMSettingGsm *s_gsm;
-
-		s_gsm = nm_connection_get_setting_gsm (connection);
-		if (!s_gsm) {
-			/* Need a GSM setting at least */
-			g_set_error_literal (error,
-			                     NM_CONNECTION_ERROR,
-			                     NM_CONNECTION_ERROR_MISSING_SETTING,
-			                     _("GSM mobile broadband connection requires a 'gsm' setting"));
-			g_prefix_error (error, "%s: ", NM_SETTING_GSM_SETTING_NAME);
-			return FALSE;
-		}
-
 		nm_utils_complete_generic (NM_PLATFORM_GET,
 		                           connection,
 		                           NM_SETTING_GSM_SETTING_NAME,
diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c
index fac14d69..4be089c0 100644
--- a/src/devices/wwan/nm-modem-manager.c
+++ b/src/devices/wwan/nm-modem-manager.c
@@ -32,7 +32,7 @@
 #define sd_booted() FALSE
 #endif
 
-#include "nm-dbus-compat.h"
+#include "nm-std-aux/nm-dbus-compat.h"
 #include "nm-modem.h"
 #include "nm-modem-broadband.h"
 
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 2217f2a2..17794229 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -885,7 +885,7 @@ nm_modem_get_configured_mtu (NMDevice *self, NMDeviceMtuSource *out_source)
 		}
 
 		property_name = NM_IS_SETTING_GSM (setting) ? "gsm.mtu" : "cdma.mtu";
-		mtu_default = nm_device_get_configured_mtu_from_connection_default (self, property_name);
+		mtu_default = nm_device_get_configured_mtu_from_connection_default (self, property_name, G_MAXUINT32);
 		if (mtu_default >= 0) {
 			*out_source = NM_DEVICE_MTU_SOURCE_CONNECTION;
 			return (guint32) mtu_default;