summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/devices/nm-device-bond.c215
-rw-r--r--src/devices/nm-device.c41
-rw-r--r--src/devices/team/nm-device-team.c1
-rw-r--r--src/devices/wifi/nm-device-wifi.c7
-rw-r--r--src/dhcp/nm-dhcp-manager.c42
-rw-r--r--src/dhcp/nm-dhcp-manager.h2
-rw-r--r--src/dhcp/nm-dhcp-options.c1
-rw-r--r--src/dhcp/nm-dhcp-options.h2
-rw-r--r--src/dhcp/nm-dhcp-systemd.c36
-rw-r--r--src/initrd/nm-initrd-generator.h2
-rw-r--r--src/initrd/nmi-cmdline-reader.c44
-rw-r--r--src/initrd/tests/test-cmdline-reader.c71
-rw-r--r--src/meson.build6
-rw-r--r--src/ndisc/tests/test-ndisc-fake.c2
-rw-r--r--src/nm-active-connection.c6
-rw-r--r--src/nm-config-data.c2
-rw-r--r--src/nm-hostname-manager.c47
-rw-r--r--src/nm-iface-helper.c11
-rw-r--r--src/nm-ip4-config.c24
-rw-r--r--src/nm-ip4-config.h10
-rw-r--r--src/nm-ip6-config.c20
-rw-r--r--src/nm-ip6-config.h5
-rw-r--r--src/nm-manager.c37
-rw-r--r--src/nm-policy.c35
-rw-r--r--src/platform/nm-linux-platform.c5
-rw-r--r--src/settings/nm-settings.c384
-rw-r--r--src/settings/nm-settings.h6
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c1
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-internal.h5
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-lease-internal.h2
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-option.c120
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-client.c7
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-lease.c39
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-lease.h1
-rw-r--r--src/vpn/nm-vpn-connection.c19
35 files changed, 850 insertions, 408 deletions
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index 2fedc753..71332ba3 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -8,6 +8,7 @@
 #include "nm-device-bond.h"
 
 #include <stdlib.h>
+#include <net/if.h>
 
 #include "NetworkManagerUtils.h"
 #include "nm-device-private.h"
@@ -182,7 +183,6 @@ master_update_slave_connection (NMDevice *self,
 
 static void
 set_arp_targets (NMDevice *device,
-                 NMBondMode mode,
                  const char *cur_arp_ip_target,
                  const char *new_arp_ip_target)
 {
@@ -261,15 +261,73 @@ set_bond_attr_or_default (NMDevice *device,
 	_set_bond_attr (device, opt, value);
 }
 
+static void
+set_bond_attr_active_slave (NMDevice *device, NMSettingBond *s_bond)
+{
+	NMDeviceBond *self = NM_DEVICE_BOND (device);
+	const NMPlatformLink *plink;
+	const char *value;
+	const char *error_reason;
+	int ifindex;
+
+	value = nm_setting_bond_get_option_or_default (s_bond, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE);
+	if (!value)
+		return;
+
+	if (!nm_str_is_empty (value)) {
+		ifindex = nm_device_get_ifindex (device);
+		plink = nm_platform_link_get_by_ifname (nm_device_get_platform (device), value);
+		if (!plink)
+			error_reason = "does not exist";
+		else if (plink->master != ifindex)
+			error_reason = "is not yet enslaved";
+		else if (!NM_FLAGS_HAS (plink->n_ifi_flags, IFF_UP))
+			error_reason = "is not up";
+		else
+			error_reason = NULL;
+
+		if (error_reason) {
+			_LOGT (LOGD_BOND, "bond option 'active_slave' not set as device \"%s\" %s", value, error_reason);
+			return;
+		}
+	}
+
+	_set_bond_attr (device, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, value);
+}
+
+static void
+set_bond_attrs_or_default (NMDevice *device, NMSettingBond *s_bond, const char *const *attr_v)
+{
+	nm_assert (NM_IS_DEVICE (device));
+	nm_assert (s_bond);
+	nm_assert (attr_v);
+
+	for ( ; *attr_v ; ++attr_v)
+		set_bond_attr_or_default (device, s_bond, *attr_v);
+}
+
+static void
+set_bond_arp_ip_targets (NMDevice *device, NMSettingBond *s_bond)
+{
+	int           ifindex           = nm_device_get_ifindex (device);
+	gs_free char *cur_arp_ip_target = NULL;
+
+	/* ARP targets: clear and initialize the list */
+	cur_arp_ip_target = nm_platform_sysctl_master_get_option (nm_device_get_platform (device),
+	                                                          ifindex,
+	                                                          NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
+	set_arp_targets (device,
+	                 cur_arp_ip_target,
+	                 nm_setting_bond_get_option_or_default (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET));
+}
+
 static gboolean
 apply_bonding_config (NMDeviceBond *self)
 {
 	NMDevice *device = NM_DEVICE (self);
-	int ifindex = nm_device_get_ifindex (device);
 	NMSettingBond *s_bond;
 	NMBondMode mode;
 	const char *mode_str;
-	gs_free char *cur_arp_ip_target = NULL;
 
 	s_bond = nm_device_get_applied_setting (device, NM_TYPE_SETTING_BOND);
 	g_return_val_if_fail (s_bond, FALSE);
@@ -283,40 +341,34 @@ apply_bonding_config (NMDeviceBond *self)
 	 */
 	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_MODE);
 
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_MIIMON);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_UPDELAY);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_ARP_VALIDATE);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
-
-	/* ARP targets: clear and initialize the list */
-	cur_arp_ip_target = nm_platform_sysctl_master_get_option (nm_device_get_platform (device),
-	                                                          ifindex,
-	                                                          NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
-	set_arp_targets (device,
-	                 mode,
-	                 cur_arp_ip_target,
-	                 nm_setting_bond_get_option_or_default (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET));
-
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_AD_SELECT);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_FAIL_OVER_MAC);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_LACP_RATE);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_LP_INTERVAL);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_MIN_LINKS);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_PRIMARY_RESELECT);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_RESEND_IGMP);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_USE_CARRIER);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY);
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_NUM_GRAT_ARP);
+	set_bond_arp_ip_targets (device, s_bond);
+	set_bond_attr_active_slave (device, s_bond);
+
+	set_bond_attrs_or_default (device,
+	                           s_bond,
+	                           NM_MAKE_STRV (NM_SETTING_BOND_OPTION_MIIMON,
+	                                         NM_SETTING_BOND_OPTION_UPDELAY,
+	                                         NM_SETTING_BOND_OPTION_DOWNDELAY,
+	                                         NM_SETTING_BOND_OPTION_ARP_INTERVAL,
+	                                         NM_SETTING_BOND_OPTION_ARP_VALIDATE,
+	                                         NM_SETTING_BOND_OPTION_PRIMARY,
+	                                         NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM,
+	                                         NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO,
+	                                         NM_SETTING_BOND_OPTION_AD_SELECT,
+	                                         NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY,
+	                                         NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE,
+	                                         NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS,
+	                                         NM_SETTING_BOND_OPTION_FAIL_OVER_MAC,
+	                                         NM_SETTING_BOND_OPTION_LACP_RATE,
+	                                         NM_SETTING_BOND_OPTION_LP_INTERVAL,
+	                                         NM_SETTING_BOND_OPTION_MIN_LINKS,
+	                                         NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE,
+	                                         NM_SETTING_BOND_OPTION_PRIMARY_RESELECT,
+	                                         NM_SETTING_BOND_OPTION_RESEND_IGMP,
+	                                         NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB,
+	                                         NM_SETTING_BOND_OPTION_USE_CARRIER,
+	                                         NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY,
+	                                         NM_SETTING_BOND_OPTION_NUM_GRAT_ARP));
 	return TRUE;
 }
 
@@ -378,7 +430,7 @@ enslave_slave (NMDevice *device,
 				if (nm_streq0 (active, nm_device_get_iface (slave))) {
 					nm_platform_sysctl_master_set_option (nm_device_get_platform (device),
 					                                      nm_device_get_ifindex (device),
-					                                      "active_slave",
+					                                      NM_SETTING_BOND_OPTION_ACTIVE_SLAVE,
 					                                      active);
 					_LOGD (LOGD_BOND, "setting slave %s as active one for master %s",
 					       active, nm_device_get_iface (device));
@@ -481,31 +533,41 @@ create_and_realize (NMDevice *device,
 static gboolean
 check_changed_options (NMSettingBond *s_a, NMSettingBond *s_b, GError **error)
 {
-	guint i, num;
-	const char *name = NULL, *value_a = NULL, *value_b = NULL;
+	const char **option_list;
 
-	/* Check that options in @s_a have compatible changes in @s_b */
+	option_list = nm_setting_bond_get_valid_options (NULL);
 
-	num = nm_setting_bond_get_num_options (s_a);
-	for (i = 0; i < num; i++) {
-		nm_setting_bond_get_option (s_a, i, &name, &value_a);
+	for (; *option_list; ++option_list) {
+		const char *name = *option_list;
 
 		/* We support changes to these */
 		if (NM_IN_STRSET (name,
-		                  NM_SETTING_BOND_OPTION_ACTIVE_SLAVE,
-		                  NM_SETTING_BOND_OPTION_PRIMARY)) {
-			continue;
-		}
-
-		/* Missing in @s_b, but has a default value in @s_a */
-		value_b = nm_setting_bond_get_option_by_name (s_b, name);
-		if (   !value_b
-		    && nm_streq0 (value_a, nm_setting_bond_get_option_default (s_a, name))) {
+		                  NM_SETTING_BOND_OPTION_PRIMARY,
+		                  NM_SETTING_BOND_OPTION_MIIMON,
+		                  NM_SETTING_BOND_OPTION_UPDELAY,
+		                  NM_SETTING_BOND_OPTION_DOWNDELAY,
+		                  NM_SETTING_BOND_OPTION_ARP_INTERVAL,
+		                  NM_SETTING_BOND_OPTION_ARP_VALIDATE,
+		                  NM_SETTING_BOND_OPTION_PRIMARY,
+		                  NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM,
+		                  NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO,
+		                  NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE,
+		                  NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS,
+		                  NM_SETTING_BOND_OPTION_FAIL_OVER_MAC,
+		                  NM_SETTING_BOND_OPTION_LP_INTERVAL,
+		                  NM_SETTING_BOND_OPTION_MIN_LINKS,
+		                  NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE,
+		                  NM_SETTING_BOND_OPTION_PRIMARY_RESELECT,
+		                  NM_SETTING_BOND_OPTION_RESEND_IGMP,
+		                  NM_SETTING_BOND_OPTION_USE_CARRIER,
+		                  NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY,
+		                  NM_SETTING_BOND_OPTION_NUM_GRAT_ARP)) {
 			continue;
 		}
 
 		/* Reject any other changes */
-		if (!nm_streq0 (value_a, value_b)) {
+		if (!nm_streq0 (nm_setting_bond_get_option_normalized (s_a, name),
+		                nm_setting_bond_get_option_normalized (s_b, name))) {
 			g_set_error (error,
 			             NM_DEVICE_ERROR,
 			             NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
@@ -527,7 +589,6 @@ can_reapply_change (NMDevice *device,
                     GError **error)
 {
 	NMDeviceClass *device_class;
-	NMSettingBond *s_bond_old, *s_bond_new;
 
 	/* Only handle bond setting here, delegate other settings to parent class */
 	if (nm_streq (setting_name, NM_SETTING_BOND_SETTING_NAME)) {
@@ -537,15 +598,7 @@ can_reapply_change (NMDevice *device,
 		                                        NM_SETTING_BOND_OPTIONS))
 			return FALSE;
 
-		s_bond_old = NM_SETTING_BOND (s_old);
-		s_bond_new = NM_SETTING_BOND (s_new);
-
-		if (   !check_changed_options (s_bond_old, s_bond_new, error)
-		    || !check_changed_options (s_bond_new, s_bond_old, error)) {
-			return FALSE;
-		}
-
-		return TRUE;
+		return check_changed_options (NM_SETTING_BOND (s_old), NM_SETTING_BOND (s_new), error);
 	}
 
 	device_class = NM_DEVICE_CLASS (nm_device_bond_parent_class);
@@ -561,8 +614,8 @@ static void
 reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_new)
 {
 	NMDeviceBond *self = NM_DEVICE_BOND (device);
-	const char *value;
 	NMSettingBond *s_bond;
+	const char *value;
 	NMBondMode mode;
 
 	NM_DEVICE_CLASS (nm_device_bond_parent_class)->reapply_connection (device,
@@ -577,10 +630,34 @@ reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_n
 	mode = _nm_setting_bond_mode_from_string (value);
 	g_return_if_fail (mode != NM_BOND_MODE_UNKNOWN);
 
-	/* Primary */
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
-	/* Active slave */
-	set_bond_attr_or_default (device, s_bond, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE);
+	/* Below we set only the bond options that kernel allows to modify
+	 * while keeping the bond interface up */
+
+	set_bond_attr_active_slave (device, s_bond);
+	set_bond_arp_ip_targets (device, s_bond);
+
+	set_bond_attrs_or_default (device,
+	                           s_bond,
+	                           NM_MAKE_STRV (NM_SETTING_BOND_OPTION_PRIMARY,
+	                                         NM_SETTING_BOND_OPTION_MIIMON,
+	                                         NM_SETTING_BOND_OPTION_UPDELAY,
+	                                         NM_SETTING_BOND_OPTION_DOWNDELAY,
+	                                         NM_SETTING_BOND_OPTION_ARP_INTERVAL,
+	                                         NM_SETTING_BOND_OPTION_ARP_VALIDATE,
+	                                         NM_SETTING_BOND_OPTION_PRIMARY,
+	                                         NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM,
+	                                         NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO,
+	                                         NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE,
+	                                         NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS,
+	                                         NM_SETTING_BOND_OPTION_FAIL_OVER_MAC,
+	                                         NM_SETTING_BOND_OPTION_LP_INTERVAL,
+	                                         NM_SETTING_BOND_OPTION_MIN_LINKS,
+	                                         NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE,
+	                                         NM_SETTING_BOND_OPTION_PRIMARY_RESELECT,
+	                                         NM_SETTING_BOND_OPTION_RESEND_IGMP,
+	                                         NM_SETTING_BOND_OPTION_USE_CARRIER,
+	                                         NM_SETTING_BOND_OPTION_XMIT_HASH_POLICY,
+	                                         NM_SETTING_BOND_OPTION_NUM_GRAT_ARP));
 }
 
 /*****************************************************************************/
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index de09e480..eb0d4d52 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -68,6 +68,7 @@
 
 #include "nm-device-generic.h"
 #include "nm-device-vlan.h"
+#include "nm-device-vrf.h"
 #include "nm-device-wireguard.h"
 
 #include "nm-device-logging.h"
@@ -8097,15 +8098,21 @@ ip_config_merge_and_apply (NMDevice *self,
 	}
 
 	if (commit) {
+		gboolean is_vrf;
+
+		is_vrf = priv->master && nm_device_get_device_type (priv->master) == NM_DEVICE_TYPE_VRF;
+
 		if (IS_IPv4) {
 			nm_ip4_config_add_dependent_routes (NM_IP4_CONFIG (composite),
 			                                    nm_device_get_route_table (self, addr_family),
 			                                    nm_device_get_route_metric (self, addr_family),
+			                                    is_vrf,
 			                                    &ip4_dev_route_blacklist);
 		} else {
 			nm_ip6_config_add_dependent_routes (NM_IP6_CONFIG (composite),
 			                                    nm_device_get_route_table (self, addr_family),
-			                                    nm_device_get_route_metric (self, addr_family));
+			                                    nm_device_get_route_metric (self, addr_family),
+			                                    is_vrf);
 		}
 	}
 
@@ -9534,7 +9541,6 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMSettingIPConfig *s_ip6;
 	gs_unref_bytes GBytes *hwaddr = NULL;
-	gs_unref_bytes GBytes *bcast_hwaddr = NULL;
 	gs_unref_bytes GBytes *duid = NULL;
 	gboolean enforce_duid = FALSE;
 	const NMPlatformLink *pllink;
@@ -9564,20 +9570,16 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	}
 
 	pllink = nm_platform_link_get (nm_device_get_platform (self), nm_device_get_ip_ifindex (self));
-	if (pllink) {
+	if (pllink)
 		hwaddr = nmp_link_address_get_as_bytes (&pllink->l_address);
-		bcast_hwaddr = nmp_link_address_get_as_bytes (&pllink->l_broadcast);
-	}
 
 	iaid = dhcp_get_iaid (self, AF_INET6, connection, &iaid_explicit);
-
 	duid = dhcp6_get_duid (self, connection, hwaddr, &enforce_duid);
+
 	priv->dhcp_data_6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (),
 	                                                      nm_device_get_multi_index (self),
 	                                                      nm_device_get_ip_iface (self),
 	                                                      nm_device_get_ip_ifindex (self),
-	                                                      hwaddr,
-	                                                      bcast_hwaddr,
 	                                                      &ll_addr->address,
 	                                                      nm_connection_get_uuid (connection),
 	                                                      nm_device_get_route_table (self, AF_INET6),
@@ -10261,14 +10263,25 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 			if (!nm_device_sysctl_ip_conf_set (self, AF_INET6, "mtu",
 			                                   nm_sprintf_buf (sbuf, "%u", (unsigned) ip6_mtu))) {
 				int errsv = errno;
+				NMLogLevel level = LOGL_WARN;
+				const char *msg = NULL;
 
-				_NMLOG (anticipated_failure && errsv == EINVAL ? LOGL_DEBUG : LOGL_WARN,
-				        LOGD_DEVICE,
-				        "mtu: failure to set IPv6 MTU%s",
-				        anticipated_failure && errsv == EINVAL
-				           ? ": Is the underlying MTU value successfully set?"
-				           : "");
 				success = FALSE;
+
+				if (anticipated_failure && errsv == EINVAL) {
+					level = LOGL_DEBUG;
+					msg = "Is the underlying MTU value successfully set?";
+				} else if (!g_file_test ("/proc/sys/net/ipv6", G_FILE_TEST_IS_DIR)) {
+					level = LOGL_DEBUG;
+					msg = "IPv6 is disabled";
+					success = TRUE;
+				}
+
+				_NMLOG (level,
+				        LOGD_DEVICE,
+				        "mtu: failure to set IPv6 MTU%s%s",
+				        msg ? ": " : "",
+				        msg ?: "");
 			}
 			priv->carrier_wait_until_ms = nm_utils_get_monotonic_timestamp_msec () + CARRIER_WAIT_TIME_AFTER_MTU_MS;
 		}
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index 217c4bdd..7ba30342 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -371,6 +371,7 @@ teamd_ready (NMDeviceTeam *self)
 		success = teamd_read_config (self);
 
 	if (!success) {
+		teamd_cleanup (self, TRUE);
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
 		return;
 	}
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 2d97f9b1..2a342886 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -1937,6 +1937,13 @@ supplicant_iface_bss_changed_cb (NMSupplicantInterface *iface,
 	} else {
 		gs_unref_object NMWifiAP *ap = NULL;
 
+		if (!bss_info->bssid_valid) {
+			/* We failed to initialize the info about the AP. This can
+			 * happen due to an error in the D-Bus communication. In this case
+			 * we ignore the info. */
+			return;
+		}
+
 		ap = nm_wifi_ap_new_from_properties (bss_info);
 
 		/* Let the manager try to fill in the SSID from seen-bssids lists */
diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c
index 7565bfc8..fc99a30a 100644
--- a/src/dhcp/nm-dhcp-manager.c
+++ b/src/dhcp/nm-dhcp-manager.c
@@ -240,21 +240,27 @@ client_start (NMDhcpManager *self,
 	g_return_val_if_fail (!dhcp_client_id || g_bytes_get_size (dhcp_client_id) >= 2, NULL);
 	g_return_val_if_fail (!error || !*error, NULL);
 
-	if (!hwaddr || !bcast_hwaddr) {
-		nm_utils_error_set (error,
-		                    NM_UTILS_ERROR_UNKNOWN,
-		                    "missing %s address",
-		                    hwaddr ? "broadcast" : "MAC");
-		return NULL;
-	}
+	if (addr_family == AF_INET) {
+		if (!hwaddr || !bcast_hwaddr) {
+			nm_utils_error_set (error,
+			                    NM_UTILS_ERROR_UNKNOWN,
+			                    "missing %s address",
+			                    hwaddr ? "broadcast" : "MAC");
+			return NULL;
+		}
 
-	hwaddr_len = g_bytes_get_size (hwaddr);
-	if (   hwaddr_len == 0
-	    || hwaddr_len > NM_UTILS_HWADDR_LEN_MAX) {
-		nm_utils_error_set (error,
-		                    NM_UTILS_ERROR_UNKNOWN,
-		                    "invalid MAC address");
-		g_return_val_if_reached (NULL) ;
+		hwaddr_len = g_bytes_get_size (hwaddr);
+		if (   hwaddr_len == 0
+		    || hwaddr_len > NM_UTILS_HWADDR_LEN_MAX) {
+			nm_utils_error_set (error,
+			                    NM_UTILS_ERROR_UNKNOWN,
+			                    "invalid MAC address");
+			g_return_val_if_reached (NULL) ;
+		}
+		nm_assert (g_bytes_get_size (hwaddr) == g_bytes_get_size (bcast_hwaddr));
+	} else {
+		hwaddr = NULL;
+		bcast_hwaddr = NULL;
 	}
 
 	if (hostname) {
@@ -268,8 +274,6 @@ client_start (NMDhcpManager *self,
 		}
 	}
 
-	nm_assert (g_bytes_get_size (hwaddr) == g_bytes_get_size (bcast_hwaddr));
-
 	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
 
 	/* Kill any old client instance */
@@ -457,8 +461,6 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
                            NMDedupMultiIndex *multi_idx,
                            const char *iface,
                            int ifindex,
-                           GBytes *hwaddr,
-                           GBytes *bcast_hwaddr,
                            const struct in6_addr *ll_addr,
                            const char *uuid,
                            guint32 route_table,
@@ -493,8 +495,8 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
 	                     multi_idx,
 	                     iface,
 	                     ifindex,
-	                     hwaddr,
-	                     bcast_hwaddr,
+	                     NULL,
+	                     NULL,
 	                     uuid,
 	                     route_table,
 	                     route_metric,
diff --git a/src/dhcp/nm-dhcp-manager.h b/src/dhcp/nm-dhcp-manager.h
index 88435282..e8e65ef3 100644
--- a/src/dhcp/nm-dhcp-manager.h
+++ b/src/dhcp/nm-dhcp-manager.h
@@ -54,8 +54,6 @@ NMDhcpClient * nm_dhcp_manager_start_ip6     (NMDhcpManager *manager,
                                               struct _NMDedupMultiIndex *multi_idx,
                                               const char *iface,
                                               int ifindex,
-                                              GBytes *hwaddr,
-                                              GBytes *bcast_hwaddr,
                                               const struct in6_addr *ll_addr,
                                               const char *uuid,
                                               guint32 route_table,
diff --git a/src/dhcp/nm-dhcp-options.c b/src/dhcp/nm-dhcp-options.c
index b10635fc..d902c77c 100644
--- a/src/dhcp/nm-dhcp-options.c
+++ b/src/dhcp/nm-dhcp-options.c
@@ -183,6 +183,7 @@ const NMDhcpOption _nm_dhcp_option_dhcp6_options[] = {
 	REQ (NM_DHCP_OPTION_DHCP6_DNS_SERVERS,                      "dhcp6_name_servers",  TRUE ),
 	REQ (NM_DHCP_OPTION_DHCP6_DOMAIN_LIST,                      "dhcp6_domain_search", TRUE ),
 	REQ (NM_DHCP_OPTION_DHCP6_SNTP_SERVERS,                     "dhcp6_sntp_servers",  TRUE ),
+	REQ (NM_DHCP_OPTION_DHCP6_FQDN,                             "fqdn_fqdn",           FALSE ),
 	REQ (NM_DHCP_OPTION_DHCP6_MUD_URL,                          "dhcp6_mud_url",       FALSE ),
 
 	/* Internal values */
diff --git a/src/dhcp/nm-dhcp-options.h b/src/dhcp/nm-dhcp-options.h
index 7c012170..bc3df5ac 100644
--- a/src/dhcp/nm-dhcp-options.h
+++ b/src/dhcp/nm-dhcp-options.h
@@ -160,7 +160,9 @@ typedef enum {
 	NM_DHCP_OPTION_DHCP6_DNS_SERVERS       = 23,
 	NM_DHCP_OPTION_DHCP6_DOMAIN_LIST       = 24,
 	NM_DHCP_OPTION_DHCP6_SNTP_SERVERS      = 31,
+	NM_DHCP_OPTION_DHCP6_FQDN              = 39,
 	NM_DHCP_OPTION_DHCP6_MUD_URL           = 112,
+
 	/* Internal values */
 	NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS     = 1026,
 	NM_DHCP_OPTION_DHCP6_NM_PREFIXLEN      = 1027,
diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c
index f65937d8..737faad9 100644
--- a/src/dhcp/nm-dhcp-systemd.c
+++ b/src/dhcp/nm-dhcp-systemd.c
@@ -107,7 +107,7 @@ lease_to_ip4_config (NMDedupMultiIndex *multi_idx,
 	guint32 rebinding;
 	gs_free nm_sd_dhcp_option *private_options = NULL;
 
-	g_return_val_if_fail (lease != NULL, NULL);
+	nm_assert (lease != NULL);
 
 	if (sd_dhcp_lease_get_address (lease, &a_address) < 0) {
 		nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "could not get address from lease");
@@ -481,9 +481,9 @@ bound4_handle (NMDhcpSystemd *self, gboolean extended)
 {
 	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 	const char *iface = nm_dhcp_client_get_iface (NM_DHCP_CLIENT (self));
-	sd_dhcp_lease *lease;
 	gs_unref_object NMIP4Config *ip4_config = NULL;
 	gs_unref_hashtable GHashTable *options = NULL;
+	sd_dhcp_lease *lease = NULL;
 	GError *error = NULL;
 
 	if (   sd_dhcp_client_get_lease (priv->client4, &lease) < 0
@@ -740,10 +740,11 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 	uint32_t lft_pref, lft_valid;
 	char addr_str[NM_UTILS_INET_ADDRSTRLEN];
 	char **domains;
+	const char *s;
 	nm_auto_free_gstring GString *str = NULL;
 	int num, i;
 
-	g_return_val_if_fail (lease, NULL);
+	nm_assert (lease);
 
 	ip6_config = nm_ip6_config_new (multi_idx, ifindex);
 
@@ -808,6 +809,13 @@ lease_to_ip6_config (NMDedupMultiIndex *multi_idx,
 		                           str->str);
 	}
 
+	if (sd_dhcp6_lease_get_fqdn (lease, &s) >= 0) {
+		nm_dhcp_option_add_option (options,
+		                           _nm_dhcp_option_dhcp6_options,
+		                           NM_DHCP_OPTION_DHCP6_FQDN,
+		                           s);
+	}
+
 	NM_SET_OUT (out_options, g_steal_pointer (&options));
 	return g_steal_pointer (&ip6_config);
 }
@@ -822,7 +830,7 @@ bound6_handle (NMDhcpSystemd *self)
 	gs_unref_hashtable GHashTable *options = NULL;
 	gs_free_error GError *error = NULL;
 	NMPlatformIP6Address prefix = { 0 };
-	sd_dhcp6_lease *lease;
+	sd_dhcp6_lease *lease = NULL;
 
 	if (   sd_dhcp6_client_get_lease (priv->client6, &lease) < 0
 	    || !lease) {
@@ -903,16 +911,12 @@ ip6_start (NMDhcpClient *client,
 	NMDhcpSystemd *self = NM_DHCP_SYSTEMD (client);
 	NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
 	nm_auto (sd_dhcp6_client_unrefp) sd_dhcp6_client *sd_client = NULL;
-	GBytes *hwaddr;
 	const char *hostname;
 	const char *mud_url;
 	int r, i;
 	const guint8 *duid_arr;
 	gsize duid_len;
 	GBytes *duid;
-	const uint8_t *hwaddr_arr;
-	gsize hwaddr_len;
-	int arp_type;
 
 	g_return_val_if_fail (!priv->client4, FALSE);
 	g_return_val_if_fail (!priv->client6, FALSE);
@@ -957,22 +961,6 @@ ip6_start (NMDhcpClient *client,
 		return FALSE;
 	}
 
-	hwaddr = nm_dhcp_client_get_hw_addr (client);
-	if (   !hwaddr
-	    || !(hwaddr_arr = g_bytes_get_data (hwaddr, &hwaddr_len))
-	    || (arp_type = nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len)) < 0) {
-		nm_utils_error_set_literal (error, NM_UTILS_ERROR_UNKNOWN, "invalid MAC address");
-		return FALSE;
-	}
-	r = sd_dhcp6_client_set_mac (sd_client,
-	                             hwaddr_arr,
-	                             hwaddr_len,
-	                             (guint16) arp_type);
-	if (r < 0) {
-		nm_utils_error_set_errno (error, r, "failed to set MAC address: %s");
-		return FALSE;
-	}
-
 	r = sd_dhcp6_client_set_ifindex (sd_client,
 	                                 nm_dhcp_client_get_ifindex (client));
 	if (r < 0) {
diff --git a/src/initrd/nm-initrd-generator.h b/src/initrd/nm-initrd-generator.h
index 97199921..8e17f045 100644
--- a/src/initrd/nm-initrd-generator.h
+++ b/src/initrd/nm-initrd-generator.h
@@ -9,6 +9,8 @@
 #include "nm-connection.h"
 #include "nm-utils.h"
 
+#define NMI_WAIT_DEVICE_TIMEOUT_MS 60000
+
 static inline gboolean
 guess_ip_address_family (const char *str)
 {
diff --git a/src/initrd/nmi-cmdline-reader.c b/src/initrd/nmi-cmdline-reader.c
index 17f9e1df..be39ef89 100644
--- a/src/initrd/nmi-cmdline-reader.c
+++ b/src/initrd/nmi-cmdline-reader.c
@@ -584,7 +584,6 @@ reader_parse_master (Reader *reader,
 {
 	NMConnection *connection;
 	NMSettingConnection *s_con;
-	NMSettingBond *s_bond;
 	gs_free char *master_to_free = NULL;
 	const char *master;
 	char *slaves;
@@ -603,8 +602,15 @@ reader_parse_master (Reader *reader,
 	s_con = nm_connection_get_setting_connection (connection);
 	master = nm_setting_connection_get_uuid (s_con);
 
-	if (nm_streq (type_name, NM_SETTING_BOND_SETTING_NAME)) {
-		s_bond = (NMSettingBond *)nm_connection_get_setting_by_name (connection, type_name);
+	if (nm_streq (type_name, NM_SETTING_BRIDGE_SETTING_NAME)) {
+		NMSettingBridge *s_bridge = nm_connection_get_setting_bridge (connection);
+
+		/* Avoid the forwarding delay */
+		g_object_set (s_bridge,
+		              NM_SETTING_BRIDGE_STP, FALSE,
+		              NULL);
+	} else if (nm_streq (type_name, NM_SETTING_BOND_SETTING_NAME)) {
+		NMSettingBond *s_bond = nm_connection_get_setting_bond (connection);
 
 		opts = get_word (&argument, ':');
 		while (opts && *opts) {
@@ -867,6 +873,27 @@ reader_add_nameservers (Reader *reader, GPtrArray *nameservers)
 	}
 }
 
+static void
+connection_set_needed (NMConnection *connection)
+{
+	NMSettingConnection *s_con;
+
+	s_con = nm_connection_get_setting_connection (connection);
+	if (!nm_streq0 (nm_setting_connection_get_connection_type (s_con),
+	                NM_SETTING_WIRED_SETTING_NAME))
+		return;
+
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, (int) NMI_WAIT_DEVICE_TIMEOUT_MS,
+	              NULL);
+}
+
+static void
+connection_set_needed_cb (gpointer key, gpointer value, gpointer user_data)
+{
+	connection_set_needed (value);
+}
+
 GHashTable *
 nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv, char **hostname)
 {
@@ -1001,11 +1028,16 @@ nmi_cmdline_reader_parse (const char *sysfs_dir, const char *const*argv, char **
 
 		connection = reader_get_connection (reader, bootdev, NULL, TRUE);
 		reader->bootdev_connection = connection;
+		connection_set_needed (connection);
 	}
 
-	if (neednet && g_hash_table_size (reader->hash) == 0) {
-		/* Make sure there's some connection. */
-		reader_get_default_connection (reader);
+	if (neednet) {
+		if (g_hash_table_size (reader->hash) == 0) {
+			/* Make sure there's some connection. */
+			reader_get_default_connection (reader);
+		}
+
+		g_hash_table_foreach (reader->hash, connection_set_needed_cb, NULL);
 	}
 
 	if (routes)
diff --git a/src/initrd/tests/test-cmdline-reader.c b/src/initrd/tests/test-cmdline-reader.c
index 04594c48..7787cf5e 100644
--- a/src/initrd/tests/test-cmdline-reader.c
+++ b/src/initrd/tests/test-cmdline-reader.c
@@ -49,6 +49,7 @@ test_auto (void)
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "Wired Connection");
 	g_assert_cmpint (nm_setting_connection_get_timestamp (s_con), ==, 0);
 	g_assert_cmpint (nm_setting_connection_get_multi_connect (s_con), ==, NM_CONNECTION_MULTI_CONNECT_MULTIPLE);
+	g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, -1);
 
 	g_assert (nm_setting_connection_get_autoconnect (s_con));
 
@@ -190,6 +191,7 @@ test_if_ip4_manual (void)
 	                                       "ip=203.0.113.2::203.0.113.1:26:"
 	                                       "hostname1.example.com:eth4");
 	NMConnection *connection;
+	NMSettingConnection *s_con;
 	NMSettingIPConfig *s_ip4;
 	NMSettingIPConfig *s_ip6;
 	NMIPAddress *ip_addr;
@@ -205,6 +207,10 @@ test_if_ip4_manual (void)
 	nmtst_assert_connection_verifies_without_normalization (connection);
 	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth3");
 
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, -1);
+
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	g_assert (s_ip4);
 	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
@@ -294,6 +300,7 @@ test_multiple_merge (void)
 	const char *const*ARGV = NM_MAKE_STRV ("ip=192.0.2.2:::::eth0",
 	                                       "ip=[2001:db8::2]:::::eth0");
 	NMConnection *connection;
+	NMSettingConnection *s_con;
 	NMSettingWired *s_wired;
 	NMSettingIPConfig *s_ip4;
 	NMSettingIPConfig *s_ip6;
@@ -310,6 +317,10 @@ test_multiple_merge (void)
 	nmtst_assert_connection_verifies_without_normalization (connection);
 	g_assert_cmpstr (nm_connection_get_id (connection), ==, "eth0");
 
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, -1);
+
 	s_wired = nm_connection_get_setting_wired (connection);
 	g_assert (s_wired);
 
@@ -341,6 +352,7 @@ test_multiple_bootdev (void)
 	                                       "ip=eth4:dhcp",
 	                                       "bootdev=eth4");
 	NMConnection *connection;
+	NMSettingConnection *s_con;
 	NMSettingIPConfig *s_ip4;
 	NMSettingIPConfig *s_ip6;
 	gs_free char *hostname = NULL;
@@ -352,12 +364,18 @@ test_multiple_bootdev (void)
 
 	connection = g_hash_table_lookup (connections, "eth3");
 	g_assert (connection);
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, -1);
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	g_assert (s_ip6);
 	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip6), ==, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
 
 	connection = g_hash_table_lookup (connections, "eth4");
 	g_assert (connection);
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, NMI_WAIT_DEVICE_TIMEOUT_MS);
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	g_assert (s_ip4);
 	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
@@ -388,6 +406,7 @@ test_bootdev (void)
 	g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_WIRED_SETTING_NAME);
 	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "ens3");
 	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "ens3");
+	g_assert_cmpint (nm_setting_connection_get_wait_device_timeout(s_con), ==, NMI_WAIT_DEVICE_TIMEOUT_MS);
 
 	connection = g_hash_table_lookup (connections, "vlan2");
 	g_assert (connection);
@@ -774,6 +793,7 @@ test_bridge (void)
 
 	s_bridge = nm_connection_get_setting_bridge (connection);
 	g_assert (s_bridge);
+	g_assert_cmpint (nm_setting_bridge_get_stp (s_bridge), ==, FALSE);
 
 	connection = g_hash_table_lookup (connections, "eth0");
 	g_assert (connection);
@@ -1255,6 +1275,56 @@ test_bootif_ip (void)
 }
 
 static void
+test_neednet (void)
+{
+	gs_unref_hashtable GHashTable *connections = NULL;
+	const char *const*ARGV = NM_MAKE_STRV ("rd.neednet",
+	                                       "ip=eno1:dhcp",
+	                                       "ip=172.25.1.100::172.25.1.1:24::eno2",
+	                                       "bridge=br0:eno3");
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	gs_free char *hostname = NULL;
+
+	connections = nmi_cmdline_reader_parse (TEST_INITRD_DIR "/sysfs", ARGV, &hostname);
+	g_assert (connections);
+	g_assert_cmpint (g_hash_table_size (connections), ==, 4);
+	g_assert_cmpstr (hostname, ==, NULL);
+
+	connection = g_hash_table_lookup (connections, "eno1");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "eno1");
+	g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, NMI_WAIT_DEVICE_TIMEOUT_MS);
+
+	connection = g_hash_table_lookup (connections, "eno2");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "eno2");
+	g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, NMI_WAIT_DEVICE_TIMEOUT_MS);
+
+	connection = g_hash_table_lookup (connections, "eno3");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "eno3");
+	g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, NMI_WAIT_DEVICE_TIMEOUT_MS);
+
+	connection = g_hash_table_lookup (connections, "br0");
+	g_assert (connection);
+	nmtst_assert_connection_verifies_without_normalization (connection);
+	s_con = nm_connection_get_setting_connection (connection);
+	g_assert (s_con);
+	g_assert_cmpstr (nm_setting_connection_get_interface_name (s_con), ==, "br0");
+	g_assert_cmpint (nm_setting_connection_get_wait_device_timeout (s_con), ==, -1);
+}
+
+static void
 test_bootif_no_ip (void)
 {
 	gs_unref_hashtable GHashTable *connections = NULL;
@@ -1450,6 +1520,7 @@ int main (int argc, char **argv)
 	g_test_add_func ("/initrd/cmdline/bootif/no_ip", test_bootif_no_ip);
 	g_test_add_func ("/initrd/cmdline/bootif/hwtype", test_bootif_hwtype);
 	g_test_add_func ("/initrd/cmdline/bootif/off", test_bootif_off);
+	g_test_add_func ("/initrd/cmdline/neednet", test_neednet);
 
 	return g_test_run ();
 }
diff --git a/src/meson.build b/src/meson.build
index 8ff9bd11..07bb1a52 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -264,8 +264,8 @@ symbol_map_name = 'NetworkManager.ver'
 # add dependencies with link_whole, only supported in meson >= 0.46.
 # Create an executable with full symbols that we use in place of the
 # library to enumerate the symbols.
-network_manager_sym = executable(
-  'nm-full-symbols',
+network_manager_all_sym = executable(
+  'NetworkManager-all-sym',
   'main.c',
   dependencies: nm_deps,
   c_args: daemon_c_flags,
@@ -277,7 +277,7 @@ network_manager_sym = executable(
 ver_script = custom_target(
   symbol_map_name,
   output: symbol_map_name,
-  depends: [network_manager_sym, core_plugins],
+  depends: [network_manager_all_sym, core_plugins],
   command: [create_exports_networkmanager, '--called-from-build', source_root],
 )
 
diff --git a/src/ndisc/tests/test-ndisc-fake.c b/src/ndisc/tests/test-ndisc-fake.c
index b65511e0..254110e4 100644
--- a/src/ndisc/tests/test-ndisc-fake.c
+++ b/src/ndisc/tests/test-ndisc-fake.c
@@ -61,8 +61,8 @@ match_gateway (const NMNDiscData *rdata, guint idx, const char *addr, guint32 ts
 		_a = &_rdata->addresses[_idx]; \
 		\
 		nmtst_assert_ip6_address (&_a->address, (addr)); \
-		g_assert_cmpint (_a->timestamp, >=, _ts); \
 		g_assert_cmpint (_a->timestamp, <=, _ts + 1); \
+		g_assert_cmpint ((int) _a->timestamp, >=, (int) _ts - 1); \
 		g_assert_cmpint (_a->timestamp + _a->lifetime, ==, _ts + (lt)); \
 		g_assert_cmpint (_a->timestamp + _a->preferred, ==, _ts + (pref)); \
 	} G_STMT_END
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index 08037ebe..151fb4d9 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -810,14 +810,14 @@ master_state_cb (NMActiveConnection *master,
                  gpointer user_data)
 {
 	NMActiveConnection *self = NM_ACTIVE_CONNECTION (user_data);
+	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
 	NMActiveConnectionState master_state = nm_active_connection_get_state (master);
-	NMDevice *master_device = nm_active_connection_get_device (master);
 
 	check_master_ready (self);
 
 	if (   master_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATING
-	    && (!master_device || !nm_device_is_real (master_device))) {
-		/* Master failed without ever creating or realizing its device */
+	    && !priv->master_ready) {
+		/* Master disconnected before the slave was added */
 		if (NM_ACTIVE_CONNECTION_GET_CLASS (self)->master_failed)
 			NM_ACTIVE_CONNECTION_GET_CLASS (self)->master_failed (self);
 	}
diff --git a/src/nm-config-data.c b/src/nm-config-data.c
index 4169a36e..ea3aed47 100644
--- a/src/nm-config-data.c
+++ b/src/nm-config-data.c
@@ -1361,6 +1361,8 @@ nm_config_data_get_device_config (const NMConfigData *self,
 	const MatchSectionInfo *connection_info;
 	char *value = NULL;
 
+	NM_SET_OUT (has_match, FALSE);
+
 	g_return_val_if_fail (self, NULL);
 	g_return_val_if_fail (property && *property, NULL);
 
diff --git a/src/nm-hostname-manager.c b/src/nm-hostname-manager.c
index f44e169e..86beffdf 100644
--- a/src/nm-hostname-manager.c
+++ b/src/nm-hostname-manager.c
@@ -11,6 +11,7 @@
 
 #if HAVE_SELINUX
 #include <selinux/selinux.h>
+#include <selinux/label.h>
 #endif
 
 #include "nm-libnm-core-intern/nm-common-macros.h"
@@ -345,8 +346,8 @@ nm_hostname_manager_write_hostname (NMHostnameManager *self, const char *hostnam
 	gs_unref_variant GVariant *var = NULL;
 	struct stat file_stat;
 #if HAVE_SELINUX
-	security_context_t se_ctx_prev = NULL, se_ctx = NULL;
-	mode_t st_mode = 0;
+	gboolean fcon_was_set = FALSE;
+	char *fcon_prev = NULL;
 #endif
 
 	g_return_val_if_fail (NM_IS_HOSTNAME_MANAGER (self), FALSE);
@@ -376,16 +377,6 @@ nm_hostname_manager_write_hostname (NMHostnameManager *self, const char *hostnam
 	    && (link_path = nm_utils_read_link_absolute (file, NULL)))
 		file = link_path;
 
-#if HAVE_SELINUX
-	/* Get default context for hostname file and set it for fscreate */
-	if (stat (file, &file_stat) == 0)
-		st_mode = file_stat.st_mode;
-	matchpathcon (file, st_mode, &se_ctx);
-	matchpathcon_fini ();
-	getfscreatecon (&se_ctx_prev);
-	setfscreatecon (se_ctx);
-#endif
-
 #if defined (HOSTNAME_PERSIST_GENTOO)
 	hostname_eol = g_strdup_printf ("#Generated by NetworkManager\n"
 	                                "hostname=\"%s\"\n", hostname);
@@ -393,13 +384,39 @@ nm_hostname_manager_write_hostname (NMHostnameManager *self, const char *hostnam
 	hostname_eol = g_strdup_printf ("%s\n", hostname);
 #endif
 
+#if HAVE_SELINUX
+	/* Get default context for hostname file and set it for fscreate */
+	{
+		struct selabel_handle *handle;
+
+		handle = selabel_open (SELABEL_CTX_FILE, NULL, 0);
+		if (handle) {
+			mode_t st_mode = 0;
+			char *fcon = NULL;
+
+			if (stat (file, &file_stat) == 0)
+				st_mode = file_stat.st_mode;
+
+			if (   (selabel_lookup (handle, &fcon, file, st_mode) == 0)
+			    && (getfscreatecon (&fcon_prev) == 0)) {
+				setfscreatecon (fcon);
+				fcon_was_set = TRUE;
+			}
+
+			selabel_close (handle);
+			freecon (fcon);
+		}
+	}
+#endif
+
 	ret = g_file_set_contents (file, hostname_eol, -1, &error);
 
 #if HAVE_SELINUX
 	/* Restore previous context and cleanup */
-	setfscreatecon (se_ctx_prev);
-	freecon (se_ctx);
-	freecon (se_ctx_prev);
+	if (fcon_was_set)
+		setfscreatecon (fcon_prev);
+	if (fcon_prev)
+		freecon (fcon_prev);
 #endif
 
 	g_free (hostname_eol);
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index 6320e722..c7d65d2a 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -41,11 +41,13 @@
 static struct {
 	GMainLoop *main_loop;
 	int ifindex;
+	gboolean is_vrf_device;
 
 	guint dad_failed_id;
 	CList dad_failed_lst_head;
 } gl/*obal*/ = {
 	.ifindex = -1,
+	.is_vrf_device = FALSE,
 };
 
 static struct {
@@ -120,6 +122,7 @@ dhcp4_state_changed (NMDhcpClient *client,
 		nm_ip4_config_add_dependent_routes (existing,
 		                                    RT_TABLE_MAIN,
 		                                    global_opt.priority_v4,
+		                                    gl.is_vrf_device,
 		                                    &ip4_dev_route_blacklist);
 		if (!nm_ip4_config_commit (existing,
 		                           NM_PLATFORM_GET,
@@ -236,7 +239,8 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 	nm_ip6_config_merge (existing, ndisc_config, NM_IP_CONFIG_MERGE_DEFAULT, 0);
 	nm_ip6_config_add_dependent_routes (existing,
 	                                    RT_TABLE_MAIN,
-	                                    global_opt.priority_v6);
+	                                    global_opt.priority_v6,
+	                                    gl.is_vrf_device);
 	if (!nm_ip6_config_commit (existing,
 	                           NM_PLATFORM_GET,
 	                           NM_IP_ROUTE_TABLE_SYNC_MODE_MAIN,
@@ -480,6 +484,11 @@ main (int argc, char *argv[])
 	if (pllink) {
 		hwaddr = nmp_link_address_get_as_bytes (&pllink->l_address);
 		bcast_hwaddr = nmp_link_address_get_as_bytes (&pllink->l_broadcast);
+
+		if (pllink->master > 0) {
+			gl.is_vrf_device
+			    = nm_platform_link_get_type (NM_PLATFORM_GET, pllink->master) == NM_LINK_TYPE_VRF;
+		}
 	}
 
 	if (global_opt.iid_str) {
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 490296c8..23c0d3f9 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -384,7 +384,7 @@ nm_ip4_config_lookup_routes (const NMIP4Config *self)
 void
 nm_ip_config_iter_ip4_route_init (NMDedupMultiIter *ipconf_iter, const NMIP4Config *self)
 {
-	g_return_if_fail (NM_IS_IP4_CONFIG (self));
+	nm_assert (NM_IS_IP4_CONFIG (self));
 	nm_dedup_multi_iter_init (ipconf_iter, nm_ip4_config_lookup_routes (self));
 }
 
@@ -672,9 +672,11 @@ nm_ip4_config_update_routes_metric (NMIP4Config *self, gint64 metric)
 }
 
 static void
-_add_local_route_from_addr4 (NMIP4Config *self,
-                            const NMPlatformIP4Address *addr,
-                            int ifindex)
+_add_local_route_from_addr4 (NMIP4Config *               self,
+                             const NMPlatformIP4Address *addr,
+                             int                         ifindex,
+                             guint32                     route_table,
+                             gboolean                    is_vrf)
 {
 	nm_auto_nmpobj NMPObject *r = NULL;
 	NMPlatformIP4Route *route;
@@ -686,18 +688,19 @@ _add_local_route_from_addr4 (NMIP4Config *self,
 	route->network = addr->address;
 	route->plen = 32;
 	route->pref_src = addr->address;
-	route->table_coerced = nm_platform_route_table_coerce (RT_TABLE_LOCAL);
 	route->type_coerced = nm_platform_route_type_coerce (RTN_LOCAL);
 	route->scope_inv = nm_platform_route_scope_inv (RT_SCOPE_HOST);
+	route->table_coerced = nm_platform_route_table_coerce (is_vrf ? route_table : RT_TABLE_LOCAL);
 
 	_add_route (self, r, NULL, NULL);
 }
 
 void
 nm_ip4_config_add_dependent_routes (NMIP4Config *self,
-                                    guint32 route_table,
-                                    guint32 route_metric,
-                                    GPtrArray **out_ip4_dev_route_blacklist)
+                                    guint32      route_table,
+                                    guint32      route_metric,
+                                    gboolean     is_vrf,
+                                    GPtrArray ** out_ip4_dev_route_blacklist)
 {
 	GPtrArray *ip4_dev_route_blacklist = NULL;
 	const NMPlatformIP4Address *my_addr;
@@ -729,7 +732,7 @@ nm_ip4_config_add_dependent_routes (NMIP4Config *self,
 		if (my_addr->external)
 			continue;
 
-		_add_local_route_from_addr4 (self, my_addr, ifindex);
+		_add_local_route_from_addr4 (self, my_addr, ifindex, route_table, is_vrf);
 
 		if (_ipv4_is_zeronet (network)) {
 			/* Kernel doesn't add device-routes for destinations that
@@ -3224,6 +3227,9 @@ out_addresses_cached:
 
 			nm_assert (_route_valid (route));
 
+			if (route->type_coerced != nm_platform_route_type_coerce (RTN_UNICAST))
+				continue;
+
 			g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}"));
 			g_variant_builder_add (&route_builder, "{sv}",
 			                       "dest",
diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
index d4694d93..ea061749 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -83,7 +83,8 @@ nm_ip_config_best_default_route_is (const NMPObject *obj)
 	 * Note that this only considers the main routing table. */
 	return    r
 	       && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)
-	       && nm_platform_route_table_is_main (r->table_coerced);
+	       && nm_platform_route_table_is_main (r->table_coerced)
+	       && r->type_coerced == nm_platform_route_type_coerce (1 /*RTN_UNICAST*/);
 }
 
 const NMPObject *_nm_ip_config_best_default_route_find_better (const NMPObject *obj_cur, const NMPObject *obj_cmp);
@@ -157,9 +158,10 @@ NMDedupMultiIndex *nm_ip4_config_get_multi_idx (const NMIP4Config *self);
 NMIP4Config *nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex);
 
 void nm_ip4_config_add_dependent_routes (NMIP4Config *self,
-                                         guint32 route_table,
-                                         guint32 route_metric,
-                                         GPtrArray **out_ip4_dev_route_blacklist);
+                                         guint32      route_table,
+                                         guint32      route_metric,
+                                         gboolean     is_vrf,
+                                         GPtrArray ** out_ip4_dev_route_blacklist);
 
 gboolean nm_ip4_config_commit (const NMIP4Config *self,
                                NMPlatform *platform,
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 4911ec1d..deb30e77 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -475,27 +475,32 @@ _add_multicast_route6 (NMIP6Config *self, int ifindex)
 }
 
 static void
-_add_local_route_from_addr6 (NMIP6Config *self, const NMPlatformIP6Address *addr, int ifindex)
+_add_local_route_from_addr6 (NMIP6Config *               self,
+                             const NMPlatformIP6Address *addr,
+                             int                         ifindex,
+                             guint32                     route_table,
+                             gboolean                    is_vrf)
 {
 	nm_auto_nmpobj NMPObject *r = NULL;
-	NMPlatformIP6Route *route;
+	NMPlatformIP6Route *      route;
 
 	r = nmp_object_new (NMP_OBJECT_TYPE_IP6_ROUTE, NULL);
 	route = NMP_OBJECT_CAST_IP6_ROUTE (r);
 	route->ifindex = ifindex;
 	route->network = addr->address;
 	route->plen = 128;
-	route->table_coerced = nm_platform_route_table_coerce (RT_TABLE_LOCAL);
 	route->type_coerced = nm_platform_route_type_coerce (RTN_LOCAL);
 	route->metric = 0;
+	route->table_coerced = nm_platform_route_table_coerce (is_vrf ? route_table : RT_TABLE_LOCAL);
 
 	_add_route (self, r, NULL, NULL);
 }
 
 void
 nm_ip6_config_add_dependent_routes (NMIP6Config *self,
-                                    guint32 route_table,
-                                    guint32 route_metric)
+                                    guint32      route_table,
+                                    guint32      route_metric,
+                                    gboolean     is_vrf)
 {
 	const NMPlatformIP6Address *my_addr;
 	const NMPlatformIP6Route *my_route;
@@ -524,7 +529,7 @@ nm_ip6_config_add_dependent_routes (NMIP6Config *self,
 			continue;
 
 		/* Pre-generate local route added by kernel */
-		_add_local_route_from_addr6 (self, my_addr, ifindex);
+		_add_local_route_from_addr6 (self, my_addr, ifindex, route_table, is_vrf);
 
 		if (NM_FLAGS_HAS (my_addr->n_ifa_flags, IFA_F_NOPREFIXROUTE))
 			continue;
@@ -2677,6 +2682,9 @@ out_addresses_cached:
 
 			nm_assert (_route_valid (route));
 
+			if (route->type_coerced != nm_platform_route_type_coerce (RTN_UNICAST))
+				continue;
+
 			g_variant_builder_init (&route_builder, G_VARIANT_TYPE ("a{sv}"));
 			g_variant_builder_add (&route_builder, "{sv}",
 			                       "dest",
diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h
index 36e8518a..b6f461b2 100644
--- a/src/nm-ip6-config.h
+++ b/src/nm-ip6-config.h
@@ -93,8 +93,9 @@ NMIP6Config *nm_ip6_config_capture (struct _NMDedupMultiIndex *multi_idx, NMPlat
                                     NMSettingIP6ConfigPrivacy use_temporary);
 
 void nm_ip6_config_add_dependent_routes (NMIP6Config *self,
-                                         guint32 route_table,
-                                         guint32 route_metric);
+                                         guint32      route_table,
+                                         guint32      route_metric,
+                                         gboolean     is_vrf);
 
 gboolean nm_ip6_config_commit (const NMIP6Config *self,
                                NMPlatform *platform,
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 92112532..778e3b94 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -1561,13 +1561,6 @@ check_if_startup_complete (NMManager *self)
 	if (!priv->devices_inited)
 		return;
 
-	reason = nm_settings_get_startup_complete_blocked_reason (priv->settings);
-	if (reason) {
-		_LOGD (LOGD_CORE, "startup complete is waiting for connection (%s)",
-		       reason);
-		return;
-	}
-
 	c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 		reason = nm_device_has_pending_action_reason (device);
 		if (reason) {
@@ -1578,6 +1571,31 @@ check_if_startup_complete (NMManager *self)
 		}
 	}
 
+	/* All NMDevice must be ready. But also NMSettings tracks profiles that wait for
+	 * ready devices via "connection.wait-device-timeout".
+	 *
+	 * Note that we only re-check nm_settings_get_startup_complete_blocked_reason() when
+	 * all of the devices become ready (again).
+	 *
+	 * For example, assume we have device "eth1" and "profile-eth2" which waits for "eth2".
+	 * If "eth1" is ready (no pending action), we only need to re-evaluate "profile-eth2"
+	 * if we have another device ("eth2"), that becomes non-ready (had pending actions)
+	 * and again become ready. We don't need to check "profile-eth2" until "eth2" becomes
+	 * non-ready.
+	 * That is why nm_settings_get_startup_complete_blocked_reason() only has any significance
+	 * if all devices are ready too. It allows us to cut down the number of checks whether
+	 * NMSettings is ready. That's because we don't need to re-evaluate on minor changes of
+	 * a device, only when all devices become managed and ready. */
+
+	g_signal_handlers_block_by_func (priv->settings, settings_startup_complete_changed, self);
+	reason = nm_settings_get_startup_complete_blocked_reason (priv->settings, TRUE);
+	g_signal_handlers_unblock_by_func (priv->settings, settings_startup_complete_changed, self);
+	if (reason) {
+		_LOGD (LOGD_CORE, "startup complete is waiting for connection (%s)",
+		       reason);
+		return;
+	}
+
 	_LOGI (LOGD_CORE, "startup complete");
 
 	priv->startup = FALSE;
@@ -6048,8 +6066,9 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 	} else {
 		_LOGD (LOGD_SUSPEND, "sleep: %s...", waking_from_suspend ? "waking up" : "re-enabling");
 
+		sleep_devices_clear (self);
+
 		if (waking_from_suspend) {
-			sleep_devices_clear (self);
 			c_list_for_each_entry (device, &priv->devices_lst_head, devices_lst) {
 				if (nm_device_is_software (device))
 					continue;
@@ -7454,7 +7473,7 @@ constructed (GObject *object)
 
 	G_OBJECT_CLASS (nm_manager_parent_class)->constructed (object);
 
-	priv->settings = nm_settings_new ();
+	priv->settings = nm_settings_new (self);
 
 	nm_dbus_object_export (NM_DBUS_OBJECT (priv->settings));
 
diff --git a/src/nm-policy.c b/src/nm-policy.c
index 62ead242..04cbace6 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -764,7 +764,7 @@ update_system_hostname (NMPolicy *self, const char *msg)
 		/* Grab a hostname out of the device's DHCP6 config */
 		dhcp_config = nm_device_get_dhcp_config (get_default_device (self, AF_INET6), AF_INET6);
 		if (dhcp_config) {
-			dhcp_hostname = nm_dhcp_config_get_option (dhcp_config, "host_name");
+			dhcp_hostname = nm_dhcp_config_get_option (dhcp_config, "fqdn_fqdn");
 			if (dhcp_hostname && dhcp_hostname[0]) {
 				p = nm_str_skip_leading_spaces (dhcp_hostname);
 				if (p[0]) {
@@ -1787,7 +1787,7 @@ device_state_changed (NMDevice *device,
 		if (   sett_conn
 		    && old_state >= NM_DEVICE_STATE_PREPARE
 		    && old_state <= NM_DEVICE_STATE_ACTIVATED) {
-			gboolean block_no_secrets = FALSE;
+			gboolean blocked = FALSE;
 			int tries;
 			guint64 con_v;
 
@@ -1807,15 +1807,32 @@ device_state_changed (NMDevice *device,
 				 */
 				con_v = nm_settings_connection_get_last_secret_agent_version_id (sett_conn);
 				if (   con_v == 0
-				    || con_v == nm_agent_manager_get_agent_version_id (priv->agent_mgr))
-					block_no_secrets = TRUE;
+				    || con_v == nm_agent_manager_get_agent_version_id (priv->agent_mgr)) {
+					_LOGD (LOGD_DEVICE, "connection '%s' now blocked from autoconnect due to no secrets",
+					       nm_settings_connection_get_id (sett_conn));
+					nm_settings_connection_autoconnect_blocked_reason_set (sett_conn,
+					                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS,
+					                                                       TRUE);
+					blocked = TRUE;
+				}
+			} else if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED) {
+				/* A connection that fails due to dependency-failed is not
+				 * able to reconnect until the master connection activates
+				 * again; when this happens, the master clears the blocked
+				 * reason for all its slaves in activate_slave_connections()
+				 * and tries to reconnect them. For this to work, the slave
+				 * should be marked as blocked when it fails with
+				 * dependency-failed.
+				 */
+				_LOGD (LOGD_DEVICE, "connection '%s' now blocked from autoconnect due to failed dependency",
+				       nm_settings_connection_get_id (sett_conn));
+				nm_settings_connection_autoconnect_blocked_reason_set (sett_conn,
+				                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED,
+				                                                       TRUE);
+				blocked = TRUE;
 			}
 
-			if (block_no_secrets) {
-				_LOGD (LOGD_DEVICE, "connection '%s' now blocked from autoconnect due to no secrets",
-				       nm_settings_connection_get_id (sett_conn));
-				nm_settings_connection_autoconnect_blocked_reason_set (sett_conn, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS, TRUE);
-			} else {
+			if (!blocked) {
 				tries = nm_settings_connection_autoconnect_retries_get (sett_conn);
 				if (tries > 0) {
 					_LOGD (LOGD_DEVICE, "connection '%s' failed to autoconnect; %d tries left",
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index fbcc2b31..2f33dc04 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -8196,7 +8196,10 @@ link_get_wake_on_lan (NMPlatform *platform, int ifindex)
 		if (!wifi_data)
 			return FALSE;
 
-		return nm_wifi_utils_get_wake_on_wlan (wifi_data) != NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE;
+		return !NM_IN_SET (nm_wifi_utils_get_wake_on_wlan (wifi_data),
+		                   NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE,
+		                   NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE);
+
 	} else
 		return FALSE;
 }
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 0a1e7b47..4573d5f7 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -60,6 +60,7 @@
 #include "plugins/keyfile/nms-keyfile-storage.h"
 #include "nm-agent-manager.h"
 #include "nm-config.h"
+#include "nm-manager.h"
 #include "nm-audit-manager.h"
 #include "NetworkManagerUtils.h"
 #include "nm-dispatcher.h"
@@ -324,6 +325,7 @@ _sett_conn_entry_find_shadowed_storage (SettConnEntry *sett_conn_entry,
 /*****************************************************************************/
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMSettings,
+	PROP_MANAGER,
 	PROP_UNMANAGED_SPECS,
 	PROP_HOSTNAME,
 	PROP_CAN_MODIFY,
@@ -348,6 +350,8 @@ typedef struct {
 
 	NMPlatform *platform;
 
+	NMManager *manager;
+
 	NMHostnameManager *hostname_manager;
 
 	NMSessionMonitor *session_monitor;
@@ -372,9 +376,9 @@ typedef struct {
 	GSList *unmanaged_specs;
 	GSList *unrecognized_specs;
 
+	gint64 startup_complete_start_timestamp_msec;
 	GHashTable *startup_complete_idx;
-	NMSettingsConnection *startup_complete_blocked_by;
-	gulong startup_complete_platform_change_id;
+	CList startup_complete_scd_lst_head;
 	guint startup_complete_timeout_id;
 
 	guint connections_len;
@@ -423,7 +427,7 @@ static void default_wired_clear_tag (NMSettings *self,
 static void _clear_connections_cached_list (NMSettingsPrivate *priv);
 
 static void _startup_complete_check (NMSettings *self,
-                                     gint64 now_us);
+                                     gint64 now_msec);
 
 /*****************************************************************************/
 
@@ -461,34 +465,53 @@ _emit_connection_flags_changed (NMSettings *self,
 
 typedef struct {
 	NMSettingsConnection *sett_conn;
-	gint64 start_at;
-	gint64 timeout;
+	CList scd_lst;
+	gint64 timeout_msec;
 } StartupCompleteData;
 
 static void
 _startup_complete_data_destroy (StartupCompleteData *scd)
 {
+	c_list_unlink_stale (&scd->scd_lst);
 	g_object_unref (scd->sett_conn);
-	g_slice_free (StartupCompleteData, scd);
+	nm_g_slice_free (scd);
 }
 
 static gboolean
-_startup_complete_check_is_ready (NMPlatform *platform,
-                                  NMSettingsConnection *sett_conn)
+_startup_complete_check_is_ready (NMSettings *self,
+                                  NMSettingsConnection *sett_conn,
+                                  gboolean ignore_pending_actions)
 {
-	const NMPlatformLink *plink;
-	const char *ifname;
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	NMConnection *conn;
+	const CList *tmp_lst;
+	NMDevice *device;
 
-	/* FIXME: instead of just looking for the interface name, it would be better
-	 *        to wait for a device that is compatible with the profile. */
+	if (!priv->manager)
+		return TRUE;
 
-	ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (sett_conn));
+	conn = nm_settings_connection_get_connection (sett_conn);
+
+	nm_manager_for_each_device (priv->manager, device, tmp_lst) {
+
+		if (!nm_device_is_real (device))
+			continue;
+
+		if (   nm_device_get_state (device) < NM_DEVICE_STATE_UNAVAILABLE
+		    || (   !ignore_pending_actions
+		        && nm_device_has_pending_action (device))) {
+			/* while a device is not yet available and still has a pending
+			 * action itself, it's not a suitable candidate. */
+			continue;
+		}
+
+		if (!nm_device_check_connection_compatible (device, conn, NULL))
+			continue;
 
-	if (!ifname)
 		return TRUE;
+	}
 
-	plink = nm_platform_link_get_by_ifname (platform, ifname);
-	return plink && plink->initialized;
+	return FALSE;
 }
 
 static gboolean
@@ -503,116 +526,97 @@ _startup_complete_timeout_cb (gpointer user_data)
 }
 
 static void
-_startup_complete_platform_change_cb (NMPlatform *platform,
-                                      int obj_type_i,
-                                      int ifindex,
-                                      const NMPlatformLink *link,
-                                      int change_type_i,
-                                      NMSettings *self)
-{
-	const NMPlatformSignalChangeType change_type = change_type_i;
-	NMSettingsPrivate *priv;
-	const char *ifname;
-
-	if (change_type == NM_PLATFORM_SIGNAL_REMOVED)
-		return;
-
-	if (!link->initialized)
-		return;
-
-	priv = NM_SETTINGS_GET_PRIVATE (self);
-
-	ifname = nm_connection_get_interface_name (nm_settings_connection_get_connection (priv->startup_complete_blocked_by));
-	if (   ifname
-	    && !nm_streq (ifname, link->name))
-		return;
-
-	nm_assert (priv->startup_complete_timeout_id > 0);
-
-	nm_clear_g_source (&priv->startup_complete_timeout_id);
-	priv->startup_complete_timeout_id = g_idle_add (_startup_complete_timeout_cb, self);
-}
-
-static void
 _startup_complete_check (NMSettings *self,
-                         gint64 now_us)
+                         gint64 now_msec)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	gint64 next_expiry;
+	StartupCompleteData *scd_not_ready;
+	StartupCompleteData *scd_safe;
 	StartupCompleteData *scd;
-	NMSettingsConnection *next_sett_conn = NULL;
-	GHashTableIter iter;
+	gint64 elapsed_msec;
+	CList ready_lst;
+
+	if (priv->startup_complete_start_timestamp_msec == 0) {
+		/* we are already done for good or didn't start yet. */
+		return;
+	}
 
 	if (!priv->started) {
-		/* before we are started, we don't setup the timers... */
+		/* before we are started there is no need to evaluate our list because
+		 * we are anyway blocking startup-complete. */
 		return;
 	}
 
-	if (!priv->startup_complete_idx)
+	if (c_list_is_empty (&priv->startup_complete_scd_lst_head))
 		goto ready;
 
-	if (!now_us)
-		now_us = nm_utils_get_monotonic_timestamp_usec ();
+	nm_utils_get_monotonic_timestamp_msec_cached (&now_msec);
 
-	next_expiry = 0;
+	elapsed_msec = now_msec - priv->startup_complete_start_timestamp_msec;
 
-	g_hash_table_iter_init (&iter, priv->startup_complete_idx);
-	while (g_hash_table_iter_next (&iter, (gpointer *) &scd, NULL)) {
-		gint64 expiry;
-
-		if (scd->start_at == 0) {
-			/* once ready, the decision is remembered and there is nothing
-			 * left to check. */
-			continue;
-		}
+	/* We search the entire list whether they all timed-out or found a compatible device.
+	 * We do that by appending elements that are ready to the end of the list, so that
+	 * we hopefully keep testing the elements that are ready already (and can shortcut
+	 * the test in common cases).
+	 *
+	 * Note that all profiles that we wait for need to have their dependencies satisfied
+	 * at the same time. For example, consider connection A is waiting for device A' which is ready.
+	 * Connection B waits for device B', which isn't ready. Once B'/B becomes ready, A/A' must
+	 * still be ready. Otherwise, we would wait for A/A' to become ready again. */
+	scd_not_ready = NULL;
+	c_list_init (&ready_lst);
+	c_list_for_each_entry_safe (scd, scd_safe, &priv->startup_complete_scd_lst_head, scd_lst) {
 
-		expiry = scd->start_at + scd->timeout;
-		if (expiry <= now_us) {
-			scd->start_at = 0;
-			continue;
-		}
+		if (scd->timeout_msec <= elapsed_msec)
+			goto next_with_ready;
 
-		if (_startup_complete_check_is_ready (priv->platform, scd->sett_conn)) {
-			scd->start_at = 0;
-			continue;
-		}
+		if (_startup_complete_check_is_ready (self, scd->sett_conn, FALSE))
+			goto next_with_ready;
 
-		next_expiry = expiry;
-		next_sett_conn = scd->sett_conn;
-		/* we found one timeout for which to wait. that's good enough. */
+		scd_not_ready = scd;
 		break;
+
+next_with_ready:
+		/* this element is ready. We move it to a temporary list, so that we
+		 * can reorder the list (to next time evaluate the non-ready element first). */
+		nm_c_list_move_tail (&ready_lst, &scd->scd_lst);
 	}
+	c_list_splice (&priv->startup_complete_scd_lst_head, &ready_lst);
 
 	nm_clear_g_source (&priv->startup_complete_timeout_id);
-	nm_g_object_ref_set (&priv->startup_complete_blocked_by, next_sett_conn);
-	if (next_expiry > 0) {
-		nm_assert (priv->startup_complete_blocked_by);
-		if (priv->startup_complete_platform_change_id == 0) {
-			priv->startup_complete_platform_change_id = g_signal_connect (priv->platform,
-			                                                              NM_PLATFORM_SIGNAL_LINK_CHANGED,
-			                                                              G_CALLBACK (_startup_complete_platform_change_cb),
-			                                                              self);
-		}
-		priv->startup_complete_timeout_id = g_timeout_add (NM_MIN (3600u*1000u, (next_expiry - now_us) / 1000u),
+
+	if (scd_not_ready) {
+		gint64 timeout_msec;
+
+		timeout_msec = priv->startup_complete_start_timestamp_msec + scd_not_ready->timeout_msec - nm_utils_get_monotonic_timestamp_msec ();
+		priv->startup_complete_timeout_id = g_timeout_add (NM_CLAMP (0, timeout_msec, 60000),
 		                                                   _startup_complete_timeout_cb,
 		                                                   self);
-		_LOGT ("startup-complete: wait for device \"%s\" due to connection %s (%s)",
-		       nm_connection_get_interface_name (nm_settings_connection_get_connection (priv->startup_complete_blocked_by)),
-		       nm_settings_connection_get_uuid (priv->startup_complete_blocked_by),
-		       nm_settings_connection_get_id (priv->startup_complete_blocked_by));
+		_LOGT ("startup-complete: wait for suitable device for connection \"%s\" (%s) which has \"connection.wait-device-timeout\" set",
+		       nm_settings_connection_get_id (scd_not_ready->sett_conn),
+		       nm_settings_connection_get_uuid (scd_not_ready->sett_conn));
 		return;
 	}
 
-	nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy);
-	nm_clear_g_signal_handler (priv->platform, &priv->startup_complete_platform_change_id);
+	if (_LOGW_ENABLED ()) {
+		c_list_for_each_entry (scd, &priv->startup_complete_scd_lst_head, scd_lst) {
+			if (!_startup_complete_check_is_ready (self, scd->sett_conn, TRUE)) {
+				_LOGW ("startup-complete: profile \"%s\" (%s) was waiting for non-existing device (with timeout \"connection.wait-device-timeout=%"G_GINT64_FORMAT"\")",
+				       nm_settings_connection_get_id (scd->sett_conn),
+				       nm_settings_connection_get_uuid (scd->sett_conn),
+				       scd->timeout_msec);
+			}
+		}
+	}
 
 ready:
-	_LOGT ("startup-complete: ready, no profiles to wait for");
+	nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy);
+	nm_assert (c_list_is_empty (&priv->startup_complete_scd_lst_head));
 	nm_assert (priv->started);
-	nm_assert (!priv->startup_complete_blocked_by);
+	_LOGT ("startup-complete: ready, no more profiles to wait for");
+	priv->startup_complete_start_timestamp_msec = 0;
 	nm_assert (!priv->startup_complete_idx);
 	nm_assert (priv->startup_complete_timeout_id == 0);
-	nm_assert (priv->startup_complete_platform_change_id == 0);
 	_notify (self, PROP_STARTUP_COMPLETE);
 }
 
@@ -622,75 +626,95 @@ _startup_complete_notify_connection (NMSettings *self,
                                      gboolean forget)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	gint64 timeout;
-	gint64 now_us = 0;
-
-	nm_assert (   !priv->started
-	           || priv->startup_complete_idx);
-
-	timeout = 0;
-	if (!forget) {
-		NMSettingConnection *s_con;
-		gint32 v;
-
-		s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (sett_conn));
-		v = nm_setting_connection_get_wait_device_timeout (s_con);
-		if (v > 0) {
-			nm_assert (nm_setting_connection_get_interface_name (s_con));
-			timeout = ((gint64) v) * 1000;
-		}
+	StartupCompleteData *scd;
+	gint64 timeout_msec;
+	gint64 now_msec = 0;
+	NMSettingConnection *s_con;
+	gint32 v;
+
+	nm_assert (priv->startup_complete_start_timestamp_msec != 0);
+
+	if (forget) {
+		if (!priv->startup_complete_idx)
+			return;
+		if (!g_hash_table_remove (priv->startup_complete_idx, &sett_conn))
+			return;
+		goto check;
 	}
 
-	if (timeout == 0) {
-		if (   !priv->startup_complete_idx
-		    || !g_hash_table_remove (priv->startup_complete_idx, &sett_conn))
+	s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (sett_conn));
+	v = nm_setting_connection_get_wait_device_timeout (s_con);
+	if (v > 0)
+		timeout_msec = v;
+	else
+		timeout_msec = 0;
+
+	if (!priv->startup_complete_idx) {
+		nm_assert (!priv->started);
+
+		if (timeout_msec == 0)
+			return;
+
+		priv->startup_complete_idx = g_hash_table_new_full (nm_pdirect_hash,
+		                                                    nm_pdirect_equal,
+		                                                    NULL,
+		                                                    (GDestroyNotify) _startup_complete_data_destroy);
+		scd = NULL;
+	} else
+		scd = g_hash_table_lookup (priv->startup_complete_idx, &sett_conn);
+
+	if (!scd) {
+		if (timeout_msec == 0)
 			return;
+		scd = g_slice_new (StartupCompleteData);
+		*scd = (StartupCompleteData) {
+			.sett_conn    = g_object_ref (sett_conn),
+			.timeout_msec = timeout_msec,
+		};
+		g_hash_table_add (priv->startup_complete_idx, scd);
+		c_list_link_tail (&priv->startup_complete_scd_lst_head, &scd->scd_lst);
 	} else {
-		StartupCompleteData *scd;
-
-		if (!priv->startup_complete_idx) {
-			nm_assert (!priv->started);
-			priv->startup_complete_idx = g_hash_table_new_full (nm_pdirect_hash,
-			                                                    nm_pdirect_equal,
-			                                                    NULL,
-			                                                    (GDestroyNotify) _startup_complete_data_destroy);
-			scd = NULL;
-		} else
-			scd = g_hash_table_lookup (priv->startup_complete_idx, &sett_conn);
-		if (!scd) {
-			now_us = nm_utils_get_monotonic_timestamp_usec ();
-			scd = g_slice_new (StartupCompleteData);
-			*scd = (StartupCompleteData) {
-				.sett_conn = g_object_ref (sett_conn),
-				.start_at  = now_us,
-				.timeout   = timeout,
-			};
-			g_hash_table_add (priv->startup_complete_idx, scd);
-		} else {
-			if (scd->start_at == 0) {
-				/* the entry already is ready and no longer relevant. Ignore it. */
-				return;
-			}
-			scd->timeout = timeout;
-		}
+		scd->timeout_msec = timeout_msec;
+		nm_c_list_move_front (&priv->startup_complete_scd_lst_head, &scd->scd_lst);
 	}
 
-	_startup_complete_check (self, now_us);
+check:
+	_startup_complete_check (self, now_msec);
 }
 
 const char *
-nm_settings_get_startup_complete_blocked_reason (NMSettings *self)
+nm_settings_get_startup_complete_blocked_reason (NMSettings *self,
+                                                 gboolean force_reload)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	const char *uuid = NULL;
+	StartupCompleteData *scd;
+	const char *uuid;
 
-	if (priv->started) {
-		if (!priv->startup_complete_idx)
-			return NULL;
-		if (priv->startup_complete_blocked_by)
-			uuid = nm_settings_connection_get_uuid (priv->startup_complete_blocked_by);
-	}
-	return uuid ?: "unknown";
+	if (priv->startup_complete_start_timestamp_msec == 0)
+		goto out_done;
+
+	if (force_reload)
+		_startup_complete_check (self, 0);
+
+	if (c_list_is_empty (&priv->startup_complete_scd_lst_head))
+		goto out_done;
+
+	scd = c_list_first_entry (&priv->startup_complete_scd_lst_head, StartupCompleteData, scd_lst);
+
+	nm_assert (scd);
+	nm_assert (NM_IS_SETTINGS_CONNECTION (scd->sett_conn));
+	nm_assert (scd == nm_g_hash_table_lookup (priv->startup_complete_idx, &scd->sett_conn));
+
+	uuid = nm_settings_connection_get_uuid (scd->sett_conn);
+	if (uuid)
+		return uuid;
+
+	g_return_val_if_reached ("settings-starting");
+
+out_done:
+	if (!priv->started)
+		return "settings-starting";
+	return NULL;
 }
 
 /*****************************************************************************/
@@ -1138,8 +1162,7 @@ _connection_changed_update (NMSettings *self,
 		_emit_connection_updated (self, sett_conn, update_reason);
 	}
 
-	if (   !priv->started
-	    || priv->startup_complete_idx) {
+	if (priv->startup_complete_start_timestamp_msec != 0) {
 		if (nm_settings_has_connection (self, sett_conn))
 			_startup_complete_notify_connection (self, sett_conn, FALSE);
 	}
@@ -1213,8 +1236,7 @@ _connection_changed_delete (NMSettings *self,
 	nm_key_file_db_remove_key (priv->kf_db_timestamps, uuid);
 	nm_key_file_db_remove_key (priv->kf_db_seen_bssids, uuid);
 
-	if (   !priv->started
-	    || priv->startup_complete_idx)
+	if (priv->startup_complete_start_timestamp_msec != 0)
 		_startup_complete_notify_connection (self, sett_conn, TRUE);
 }
 
@@ -3711,6 +3733,8 @@ nm_settings_start (NMSettings *self, GError **error)
 
 	nm_assert (!priv->started);
 
+	priv->startup_complete_start_timestamp_msec = nm_utils_get_monotonic_timestamp_msec ();
+
 	priv->hostname_manager = g_object_ref (nm_hostname_manager_get ());
 
 	priv->kf_db_timestamps = nm_key_file_db_new (NMSTATEDIR "/timestamps",
@@ -3797,7 +3821,27 @@ get_property (GObject *object, guint prop_id,
 		g_value_take_boxed (value, nm_utils_strv_make_deep_copied (strv));
 		break;
 	case PROP_STARTUP_COMPLETE:
-		g_value_set_boolean (value, !nm_settings_get_startup_complete_blocked_reason (self));
+		g_value_set_boolean (value, !nm_settings_get_startup_complete_blocked_reason (self, FALSE));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSettings *self = NM_SETTINGS (object);
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+
+	switch (prop_id) {
+	case PROP_MANAGER:
+		/* construct-only */
+		priv->manager = g_value_get_pointer (value);
+		nm_assert (NM_IS_MANAGER (priv->manager));
+		g_object_add_weak_pointer (G_OBJECT (priv->manager), (gpointer *) &priv->manager);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -3814,6 +3858,7 @@ nm_settings_init (NMSettings *self)
 
 	c_list_init (&priv->auth_lst_head);
 	c_list_init (&priv->connections_lst_head);
+	c_list_init (&priv->startup_complete_scd_lst_head);
 
 	c_list_init (&priv->sce_dirty_lst_head);
 	priv->sce_idx = g_hash_table_new_full (nm_pstr_hash, nm_pstr_equal,
@@ -3833,9 +3878,13 @@ nm_settings_init (NMSettings *self)
 }
 
 NMSettings *
-nm_settings_new (void)
+nm_settings_new (NMManager *manager)
 {
-	return g_object_new (NM_TYPE_SETTINGS, NULL);
+	nm_assert (NM_IS_MANAGER (manager));
+
+	return g_object_new (NM_TYPE_SETTINGS,
+	                     NM_SETTINGS_MANAGER, manager,
+	                     NULL);
 }
 
 static void
@@ -3849,9 +3898,8 @@ dispose (GObject *object)
 	nm_assert (g_hash_table_size (priv->sce_idx) == 0);
 
 	nm_clear_g_source (&priv->startup_complete_timeout_id);
-	nm_clear_g_signal_handler (priv->platform, &priv->startup_complete_platform_change_id);
 	nm_clear_pointer (&priv->startup_complete_idx, g_hash_table_destroy);
-	g_clear_object (&priv->startup_complete_blocked_by);
+	nm_assert (c_list_is_empty (&priv->startup_complete_scd_lst_head));
 
 	while ((iter = c_list_first (&priv->auth_lst_head)))
 		nm_auth_chain_destroy (nm_auth_chain_parent_lst_entry (iter));
@@ -3915,6 +3963,11 @@ finalize (GObject *object)
 	g_clear_object (&priv->config);
 
 	g_clear_object (&priv->platform);
+
+	if (priv->manager) {
+		g_object_remove_weak_pointer (G_OBJECT (priv->manager), (gpointer *) &priv->manager);
+		priv->manager = NULL;
+	}
 }
 
 static const GDBusSignalInfo signal_info_new_connection = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT (
@@ -4051,9 +4104,16 @@ nm_settings_class_init (NMSettingsClass *class)
 	dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_settings);
 
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 
+	obj_properties[PROP_MANAGER] =
+	    g_param_spec_pointer (NM_SETTINGS_MANAGER, "", "",
+	                          G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_WRITABLE |
+	                          G_PARAM_STATIC_STRINGS);
+
 	obj_properties[PROP_UNMANAGED_SPECS] =
 	    g_param_spec_boxed (NM_SETTINGS_UNMANAGED_SPECS, "", "",
 	                        G_TYPE_STRV,
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index aa7e36e0..35c62ff7 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -26,6 +26,7 @@
 #define NM_SETTINGS_CAN_MODIFY       "can-modify"
 #define NM_SETTINGS_CONNECTIONS      "connections"
 #define NM_SETTINGS_STARTUP_COMPLETE "startup-complete"
+#define NM_SETTINGS_MANAGER          "manager"
 
 #define NM_SETTINGS_SIGNAL_CONNECTION_ADDED              "connection-added"
 #define NM_SETTINGS_SIGNAL_CONNECTION_UPDATED            "connection-updated"
@@ -53,7 +54,7 @@ GType nm_settings_get_type (void);
 NMSettings *nm_settings_get (void);
 #define NM_SETTINGS_GET (nm_settings_get ())
 
-NMSettings *nm_settings_new (void);
+NMSettings *nm_settings_new (NMManager *manager);
 
 gboolean nm_settings_start (NMSettings *self, GError **error);
 
@@ -122,7 +123,8 @@ void nm_settings_device_added (NMSettings *self, NMDevice *device);
 
 void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting);
 
-const char *nm_settings_get_startup_complete_blocked_reason (NMSettings *self);
+const char *nm_settings_get_startup_complete_blocked_reason (NMSettings *self,
+                                                             gboolean force_reload);
 
 void nm_settings_kf_db_write (NMSettings *settings);
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index 577709d8..0e9ad5fa 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -171,6 +171,7 @@ nms_keyfile_reader_from_file (const char *full_filename,
 	NM_SET_OUT (out_is_nm_generated, NM_TERNARY_DEFAULT);
 	NM_SET_OUT (out_is_volatile, NM_TERNARY_DEFAULT);
 	NM_SET_OUT (out_is_external, NM_TERNARY_DEFAULT);
+	NM_SET_OUT (out_shadowed_owned, NM_TERNARY_DEFAULT);
 
 	if (!nms_keyfile_utils_check_file_permissions (NMS_KEYFILE_FILETYPE_KEYFILE,
 	                                               full_filename,
diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h
index b0d1216e..068dcade 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h
@@ -109,8 +109,9 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
 int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
                                 struct in6_addr **addrs, size_t count,
                                 size_t *allocated);
-int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen,
-                                  char ***str_arr);
+int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen,
+                                       char ***str_arr);
+int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str);
 
 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address);
 int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
diff --git a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h
index e004f48b..df6c95e0 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-lease-internal.h
@@ -35,6 +35,7 @@ struct sd_dhcp6_lease {
         size_t ntp_allocated;
         char **ntp_fqdn;
         size_t ntp_fqdn_count;
+        char *fqdn;
 };
 
 int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire);
@@ -57,5 +58,6 @@ int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
 int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
 int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval,
                          size_t optlen) ;
+int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
 
 int dhcp6_lease_new(sd_dhcp6_lease **ret);
diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c
index d596752b..a6dad934 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp6-option.c
@@ -644,59 +644,103 @@ int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
         return count;
 }
 
-int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
-        size_t pos = 0, idx = 0;
-        _cleanup_strv_free_ char **names = NULL;
+static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) {
+        _cleanup_free_ char *ret = NULL;
+        size_t n = 0, allocated = 0;
+        const uint8_t *optval = *data;
+        uint16_t optlen = *len;
+        bool first = true;
         int r;
 
-        assert_return(optlen > 1, -ENODATA);
-        assert_return(optval[optlen - 1] == '\0', -EINVAL);
-
-        while (pos < optlen) {
-                _cleanup_free_ char *ret = NULL;
-                size_t n = 0, allocated = 0;
-                bool first = true;
+        if (optlen <= 1)
+                return -ENODATA;
 
-                for (;;) {
-                        const char *label;
-                        uint8_t c;
+        for (;;) {
+                const char *label;
+                uint8_t c;
 
-                        c = optval[pos++];
+                if (optlen == 0)
+                        break;
 
-                        if (c == 0)
-                                /* End of name */
-                                break;
-                        if (c > 63)
-                                return -EBADMSG;
+                c = *optval;
+                optval++;
+                optlen--;
 
-                        /* Literal label */
-                        label = (const char *)&optval[pos];
-                        pos += c;
-                        if (pos >= optlen)
-                                return -EMSGSIZE;
+                if (c == 0)
+                        /* End label */
+                        break;
+                if (c > 63)
+                        return -EBADMSG;
+                if (c > optlen)
+                        return -EMSGSIZE;
 
-                        if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
-                                return -ENOMEM;
+                /* Literal label */
+                label = (const char *)optval;
+                optval += c;
+                optlen -= c;
 
-                        if (first)
-                                first = false;
-                        else
-                                ret[n++] = '.';
+                if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX))
+                        return -ENOMEM;
 
-                        r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
-                        if (r < 0)
-                                return r;
+                if (first)
+                        first = false;
+                else
+                        ret[n++] = '.';
 
-                        n += r;
-                }
+                r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX);
+                if (r < 0)
+                        return r;
 
-                if (n == 0)
-                        continue;
+                n += r;
+        }
 
+        if (n) {
                 if (!GREEDY_REALLOC(ret, allocated, n + 1))
                         return -ENOMEM;
-
                 ret[n] = 0;
+        }
+
+        *out_domain = TAKE_PTR(ret);
+        *data = optval;
+        *len = optlen;
+
+        return n;
+}
+
+int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str) {
+        _cleanup_free_ char *domain = NULL;
+        int r;
+
+        r = parse_domain(&optval, &optlen, &domain);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -ENODATA;
+        if (optlen != 0)
+                return -EINVAL;
+
+        *str = TAKE_PTR(domain);
+        return 0;
+}
+
+int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
+        size_t idx = 0;
+        _cleanup_strv_free_ char **names = NULL;
+        int r;
+
+        if (optlen <= 1)
+                return -ENODATA;
+        if (optval[optlen - 1] != '\0')
+                return -EINVAL;
+
+        while (optlen > 0) {
+                _cleanup_free_ char *ret = NULL;
+
+                r = parse_domain(&optval, &optlen, &ret);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
 
                 r = strv_extend(&names, ret);
                 if (r < 0)
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
index d653b257..b80e4e54 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
@@ -1288,6 +1288,13 @@ static int client_parse_message(
 
                         break;
 
+                case SD_DHCP6_OPTION_FQDN:
+                        r = dhcp6_lease_set_fqdn(lease, optval, optlen);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
                 case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
                         if (optlen != 4)
                                 return -EINVAL;
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
index b6dc0279..5f5a7fe6 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
@@ -238,7 +238,7 @@ int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
         if (!optlen)
                 return 0;
 
-        r = dhcp6_option_parse_domainname(optval, optlen, &domains);
+        r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
         if (r < 0)
                 return 0;
 
@@ -296,8 +296,8 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
                         break;
 
                 case DHCP6_NTP_SUBOPTION_SRV_FQDN:
-                        r = dhcp6_option_parse_domainname(subval, sublen,
-                                                          &servers);
+                        r = dhcp6_option_parse_domainname_list(subval, sublen,
+                                                               &servers);
                         if (r < 0)
                                 return 0;
 
@@ -367,6 +367,38 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) {
         return -ENOENT;
 }
 
+int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval,
+                         size_t optlen) {
+        int r;
+        char *fqdn;
+
+        assert_return(lease, -EINVAL);
+        assert_return(optval, -EINVAL);
+
+        if (optlen < 2)
+                return -ENODATA;
+
+        /* Ignore the flags field, it doesn't carry any useful
+           information for clients. */
+        r = dhcp6_option_parse_domainname(optval + 1, optlen - 1, &fqdn);
+        if (r < 0)
+                return r;
+
+        return free_and_replace(lease->fqdn, fqdn);
+}
+
+int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn) {
+        assert_return(lease, -EINVAL);
+        assert_return(fqdn, -EINVAL);
+
+        if (lease->fqdn) {
+                *fqdn = lease->fqdn;
+                return 0;
+        }
+
+        return -ENOENT;
+}
+
 static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
         assert(lease);
 
@@ -375,6 +407,7 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
         dhcp6_lease_free_ia(&lease->pd);
 
         free(lease->dns);
+        free(lease->fqdn);
 
         lease->domains = strv_free(lease->domains);
 
diff --git a/src/systemd/src/systemd/sd-dhcp6-lease.h b/src/systemd/src/systemd/sd-dhcp6-lease.h
index 4301c6db..240df74a 100644
--- a/src/systemd/src/systemd/sd-dhcp6-lease.h
+++ b/src/systemd/src/systemd/sd-dhcp6-lease.h
@@ -43,6 +43,7 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs)
 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn);
+int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn);
 
 sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
 sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);
diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c
index 6d995dc4..ff6b8e00 100644
--- a/src/vpn/nm-vpn-connection.c
+++ b/src/vpn/nm-vpn-connection.c
@@ -1447,6 +1447,20 @@ get_route_table (NMVpnConnection *self,
 	return route_table ?: (fallback_main ? RT_TABLE_MAIN : 0);
 }
 
+static gboolean
+_is_device_vrf (NMVpnConnection *self)
+{
+	NMDevice *parent;
+	NMDevice *master;
+
+	parent = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (self));
+	if (!parent)
+		return FALSE;
+
+	master = nm_device_get_master (parent);
+	return master && nm_device_get_link_type (master) == NM_LINK_TYPE_VRF;
+}
+
 static void
 nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 {
@@ -1646,6 +1660,7 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 	nm_ip4_config_add_dependent_routes (config,
 	                                    route_table,
 	                                    nm_vpn_connection_get_ip4_route_metric (self),
+	                                    _is_device_vrf (self),
 	                                    &priv->ip4_dev_route_blacklist);
 
 	if (priv->ip4_config) {
@@ -1840,9 +1855,7 @@ next:
 		nm_ip6_config_add_route (config, &r, NULL);
 	}
 
-	nm_ip6_config_add_dependent_routes (config,
-	                                    route_table,
-	                                    route_metric);
+	nm_ip6_config_add_dependent_routes (config, route_table, route_metric, _is_device_vrf (self));
 
 	if (priv->ip6_config) {
 		nm_ip6_config_replace (priv->ip6_config, config, NULL);