summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/NetworkManagerUtils.c61
-rw-r--r--src/NetworkManagerUtils.h2
-rw-r--r--src/devices/adsl/nm-atm-manager.c83
-rw-r--r--src/devices/adsl/nm-device-adsl.c73
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c6
-rw-r--r--src/devices/bluetooth/nm-bluez-manager.c5
-rw-r--r--src/devices/bluetooth/nm-bt-error.c10
-rw-r--r--src/devices/bluetooth/nm-device-bt.c111
-rw-r--r--src/devices/bluetooth/nm-device-bt.h2
-rw-r--r--src/devices/nm-arping-manager.c7
-rw-r--r--src/devices/nm-device-bond.c156
-rw-r--r--src/devices/nm-device-bridge.c22
-rw-r--r--src/devices/nm-device-dummy.c205
-rw-r--r--src/devices/nm-device-dummy.h38
-rw-r--r--src/devices/nm-device-ethernet.c283
-rw-r--r--src/devices/nm-device-factory.c8
-rw-r--r--src/devices/nm-device-generic.c8
-rw-r--r--src/devices/nm-device-infiniband.c30
-rw-r--r--src/devices/nm-device-ip-tunnel.c65
-rw-r--r--src/devices/nm-device-logging.h7
-rw-r--r--src/devices/nm-device-macsec.c115
-rw-r--r--src/devices/nm-device-macvlan.c16
-rw-r--r--src/devices/nm-device-private.h5
-rw-r--r--src/devices/nm-device-tun.c14
-rw-r--r--src/devices/nm-device-veth.c2
-rw-r--r--src/devices/nm-device-vlan.c27
-rw-r--r--src/devices/nm-device-vxlan.c12
-rw-r--r--src/devices/nm-device.c2177
-rw-r--r--src/devices/nm-device.h96
-rw-r--r--src/devices/nm-lldp-listener.c4
-rw-r--r--src/devices/team/nm-device-team.c74
-rw-r--r--src/devices/tests/test-lldp.c14
-rw-r--r--src/devices/wifi/nm-device-olpc-mesh.c27
-rw-r--r--src/devices/wifi/nm-device-wifi.c544
-rw-r--r--src/devices/wifi/nm-wifi-ap.c181
-rw-r--r--src/devices/wifi/nm-wifi-ap.h14
-rw-r--r--src/devices/wifi/nm-wifi-utils.h10
-rw-r--r--src/devices/wwan/libnm-wwan.ver1
-rw-r--r--src/devices/wwan/nm-device-modem.c51
-rw-r--r--src/devices/wwan/nm-modem-broadband.c34
-rw-r--r--src/devices/wwan/nm-modem-ofono.c60
-rw-r--r--src/devices/wwan/nm-modem.c192
-rw-r--r--src/devices/wwan/nm-modem.h23
-rw-r--r--src/dhcp/nm-dhcp-client-logging.h4
-rw-r--r--src/dhcp/nm-dhcp-client.c27
-rw-r--r--src/dhcp/nm-dhcp-client.h14
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.c85
-rw-r--r--src/dhcp/nm-dhcp-dhclient-utils.h6
-rw-r--r--src/dhcp/nm-dhcp-dhclient.c30
-rw-r--r--src/dhcp/nm-dhcp-dhcpcd.c32
-rw-r--r--src/dhcp/nm-dhcp-listener.c2
-rw-r--r--src/dhcp/nm-dhcp-manager.c54
-rw-r--r--src/dhcp/nm-dhcp-systemd.c29
-rw-r--r--src/dhcp/nm-dhcp-utils.h4
-rw-r--r--src/dhcp/tests/test-dhcp-dhclient.c129
-rw-r--r--src/dns/nm-dns-manager.c226
-rw-r--r--src/dns/nm-dns-manager.h5
-rw-r--r--src/dns/nm-dns-plugin.c2
-rw-r--r--src/dns/nm-dns-systemd-resolved.c3
-rw-r--r--src/dns/nm-dns-unbound.c7
-rw-r--r--src/dnsmasq/nm-dnsmasq-manager.c41
-rw-r--r--src/main-utils.c3
-rw-r--r--src/main.c47
-rw-r--r--src/ndisc/nm-ndisc-private.h12
-rw-r--r--src/nm-act-request.c11
-rw-r--r--src/nm-act-request.h1
-rw-r--r--src/nm-active-connection.c137
-rw-r--r--src/nm-active-connection.h12
-rw-r--r--src/nm-audit-manager.c11
-rw-r--r--src/nm-audit-manager.h7
-rw-r--r--src/nm-auth-manager.c7
-rw-r--r--src/nm-auth-subject.c12
-rw-r--r--src/nm-auth-utils.c6
-rw-r--r--src/nm-checkpoint.c154
-rw-r--r--src/nm-config-data.c12
-rw-r--r--src/nm-config-data.h2
-rw-r--r--src/nm-config.c90
-rw-r--r--src/nm-config.h16
-rw-r--r--src/nm-connectivity.c658
-rw-r--r--src/nm-connectivity.h19
-rw-r--r--src/nm-core-utils.c276
-rw-r--r--src/nm-core-utils.h39
-rw-r--r--src/nm-default-route-manager.c292
-rw-r--r--src/nm-default-route-manager.h13
-rw-r--r--src/nm-dispatcher.c218
-rw-r--r--src/nm-dispatcher.h73
-rw-r--r--src/nm-exported-object.c8
-rw-r--r--src/nm-firewall-manager.c314
-rw-r--r--src/nm-firewall-manager.h6
-rw-r--r--src/nm-iface-helper.c22
-rw-r--r--src/nm-ip4-config.c51
-rw-r--r--src/nm-ip4-config.h4
-rw-r--r--src/nm-ip6-config.c72
-rw-r--r--src/nm-ip6-config.h8
-rw-r--r--src/nm-logging.c24
-rw-r--r--src/nm-logging.h79
-rw-r--r--src/nm-manager.c1002
-rw-r--r--src/nm-manager.h6
-rw-r--r--src/nm-netns.c176
-rw-r--r--src/nm-netns.h47
-rw-r--r--src/nm-pacrunner-manager.c373
-rw-r--r--src/nm-pacrunner-manager.h21
-rw-r--r--src/nm-policy.c477
-rw-r--r--src/nm-policy.h17
-rw-r--r--src/nm-rfkill-manager.c137
-rw-r--r--src/nm-route-manager.c76
-rw-r--r--src/nm-route-manager.h9
-rw-r--r--src/nm-session-monitor.c18
-rw-r--r--src/nm-sleep-monitor.c8
-rw-r--r--src/nm-test-utils-core.h41
-rw-r--r--src/nm-types.h19
-rw-r--r--src/org.freedesktop.NetworkManager.conf30
-rw-r--r--src/platform/nm-fake-platform.c130
-rw-r--r--src/platform/nm-linux-platform.c556
-rw-r--r--src/platform/nm-linux-platform.h2
-rw-r--r--src/platform/nm-platform-utils.c25
-rw-r--r--src/platform/nm-platform-utils.h6
-rw-r--r--src/platform/nm-platform.c318
-rw-r--r--src/platform/nm-platform.h81
-rw-r--r--src/platform/nmp-netns.c2
-rw-r--r--src/platform/nmp-object.c94
-rw-r--r--src/platform/nmp-object.h26
-rw-r--r--src/platform/tests/test-cleanup.c12
-rw-r--r--src/platform/tests/test-common.c50
-rw-r--r--src/platform/tests/test-common.h22
-rw-r--r--src/platform/tests/test-general.c4
-rw-r--r--src/platform/tests/test-link.c14
-rw-r--r--src/platform/tests/test-nmp-object.c42
-rw-r--r--src/platform/tests/test-route.c174
-rw-r--r--src/platform/wifi/wifi-utils-nl80211.c81
-rw-r--r--src/platform/wifi/wifi-utils-nl80211.h2
-rw-r--r--src/platform/wifi/wifi-utils-private.h3
-rw-r--r--src/platform/wifi/wifi-utils-wext.c276
-rw-r--r--src/platform/wifi/wifi-utils-wext.h2
-rw-r--r--src/platform/wifi/wifi-utils.c19
-rw-r--r--src/platform/wifi/wifi-utils.h4
-rw-r--r--src/ppp/nm-ppp-manager.c57
-rw-r--r--src/settings/nm-agent-manager.c4
-rw-r--r--src/settings/nm-secret-agent.c2
-rw-r--r--src/settings/nm-secret-agent.h2
-rw-r--r--src/settings/nm-settings-connection.c229
-rw-r--r--src/settings/nm-settings-connection.h29
-rw-r--r--src/settings/nm-settings-plugin.h2
-rw-r--r--src/settings/nm-settings.c278
-rw-r--r--src/settings/nm-settings.h26
-rw-r--r--src/settings/plugins/ibft/nms-ibft-reader.h2
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c37
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c4
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c1429
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c134
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h6
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c1125
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h4
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c158
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h27
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected8
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected17
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected22
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected34
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected16
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected8
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected11
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected6
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected20
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected17
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected16
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected22
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem311
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3:14
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected41
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected13
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected12
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-eth-type7
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected12
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c572
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-connection-parser.c157
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-connection-parser.h2
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-net-utils.h4
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.h2
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.c90
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-connection.c16
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c13
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c28
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.h7
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.c94
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.h6
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection2
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c101
-rw-r--r--src/supplicant/nm-supplicant-config.c105
-rw-r--r--src/supplicant/nm-supplicant-config.h8
-rw-r--r--src/supplicant/nm-supplicant-interface.c729
-rw-r--r--src/supplicant/nm-supplicant-interface.h26
-rw-r--r--src/supplicant/nm-supplicant-manager.c7
-rw-r--r--src/supplicant/nm-supplicant-settings-verify.c5
-rw-r--r--src/systemd/sd-adapt/env-util.h3
-rw-r--r--src/systemd/sd-adapt/nm-sd-adapt.h2
-rw-r--r--src/systemd/src/basic/fileio.c149
-rw-r--r--src/systemd/src/basic/fileio.h3
-rw-r--r--src/systemd/src/basic/fs-util.c29
-rw-r--r--src/systemd/src/basic/fs-util.h6
-rw-r--r--src/systemd/src/basic/hexdecoct.c17
-rw-r--r--src/systemd/src/basic/hostname-util.c4
-rw-r--r--src/systemd/src/basic/in-addr-util.c12
-rw-r--r--src/systemd/src/basic/in-addr-util.h2
-rw-r--r--src/systemd/src/basic/log.h4
-rw-r--r--src/systemd/src/basic/parse-util.c1
-rw-r--r--src/systemd/src/basic/path-util.c25
-rw-r--r--src/systemd/src/basic/path-util.h2
-rw-r--r--src/systemd/src/basic/socket-util.c101
-rw-r--r--src/systemd/src/basic/socket-util.h7
-rw-r--r--src/systemd/src/basic/string-util.c11
-rw-r--r--src/systemd/src/basic/string-util.h5
-rw-r--r--src/systemd/src/basic/time-util.c32
-rw-r--r--src/systemd/src/basic/time-util.h11
-rw-r--r--src/systemd/src/basic/util.c143
-rw-r--r--src/systemd/src/basic/util.h2
-rw-r--r--src/systemd/src/libsystemd-network/arp-util.c2
-rw-r--r--src/systemd/src/libsystemd-network/lldp-network.c21
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-client.c9
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4acd.c2
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4ll.c6
-rw-r--r--src/systemd/src/libsystemd-network/sd-lldp.c1
-rw-r--r--src/systemd/src/libsystemd/sd-event/sd-event.c8
-rw-r--r--src/systemd/src/systemd/_sd-common.h4
-rw-r--r--src/systemd/src/systemd/sd-event.h2
-rw-r--r--src/systemd/src/systemd/sd-id128.h3
-rw-r--r--src/systemd/src/systemd/sd-ipv4ll.h1
-rw-r--r--src/tests/config/test-config.c2
-rw-r--r--src/tests/test-general-with-expect.c2
-rw-r--r--src/tests/test-general.c167
-rw-r--r--src/tests/test-ip6-config.c10
-rw-r--r--src/tests/test-route-manager.c71
-rw-r--r--src/tests/test-systemd.c2
-rw-r--r--src/vpn/nm-vpn-connection.c234
-rw-r--r--src/vpn/nm-vpn-connection.h4
248 files changed, 12863 insertions, 6948 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 55b660db..ecb1d8ae 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -217,7 +217,7 @@ nm_utils_complete_generic (NMPlatform *platform,
                            gboolean default_enable_ipv6)
 {
 	NMSettingConnection *s_con;
-	char *id, *uuid, *ifname;
+	char *id, *ifname;
 	GHashTable *parameters;
 
 	g_assert (fallback_id_prefix);
@@ -230,9 +230,9 @@ nm_utils_complete_generic (NMPlatform *platform,
 	g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_TYPE, ctype, NULL);
 
 	if (!nm_setting_connection_get_uuid (s_con)) {
-		uuid = nm_utils_uuid_generate ();
-		g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_UUID, uuid, NULL);
-		g_free (uuid);
+		char uuid[37];
+
+		g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_buf (uuid), NULL);
 	}
 
 	/* Add a connection ID if absent */
@@ -342,18 +342,19 @@ static int
 route_compare (NMIPRoute *route1, NMIPRoute *route2, gint64 default_metric)
 {
 	gint64 r, metric1, metric2;
+	int family;
+	guint plen;
+	NMIPAddr a1 = { 0 }, a2 = { 0 };
 
-	r = g_strcmp0 (nm_ip_route_get_dest (route1), nm_ip_route_get_dest (route2));
-	if (r)
-		return r;
-
-	r = nm_ip_route_get_prefix (route1) - nm_ip_route_get_prefix (route2);
+	family = nm_ip_route_get_family (route1);
+	r = family - nm_ip_route_get_family (route2);
 	if (r)
 		return r > 0 ? 1 : -1;
 
-	r = g_strcmp0 (nm_ip_route_get_next_hop (route1), nm_ip_route_get_next_hop (route2));
+	plen = nm_ip_route_get_prefix (route1);
+	r = plen - nm_ip_route_get_prefix (route2);
 	if (r)
-		return r;
+		return r > 0 ? 1 : -1;
 
 	metric1 = nm_ip_route_get_metric (route1) == -1 ? default_metric : nm_ip_route_get_metric (route1);
 	metric2 = nm_ip_route_get_metric (route2) == -1 ? default_metric : nm_ip_route_get_metric (route2);
@@ -362,17 +363,26 @@ route_compare (NMIPRoute *route1, NMIPRoute *route2, gint64 default_metric)
 	if (r)
 		return r > 0 ? 1 : -1;
 
-	r = nm_ip_route_get_family (route1) - nm_ip_route_get_family (route2);
+	r = g_strcmp0 (nm_ip_route_get_next_hop (route1), nm_ip_route_get_next_hop (route2));
 	if (r)
-		return r > 0 ? 1 : -1;
+		return r;
+
+	/* NMIPRoute validates family and dest. inet_pton() is not expected to fail. */
+	inet_pton (family, nm_ip_route_get_dest (route1), &a1);
+	inet_pton (family, nm_ip_route_get_dest (route2), &a2);
+	nm_utils_ipx_address_clear_host_address (family, &a1, &a1, plen);
+	nm_utils_ipx_address_clear_host_address (family, &a2, &a2, plen);
+	r = memcmp (&a1, &a2, sizeof (a1));
+	if (r)
+		return r;
 
 	return 0;
 }
 
 static int
-route_ptr_compare (const void *a, const void *b)
+route_ptr_compare (const void *a, const void *b, gpointer metric)
 {
-	return route_compare (*(NMIPRoute **) a, *(NMIPRoute **) b, -1);
+	return route_compare (*(NMIPRoute **) a, *(NMIPRoute **) b, *((gint64 *) metric));
 }
 
 static gboolean
@@ -384,6 +394,7 @@ check_ip_routes (NMConnection *orig,
 {
 	gs_free NMIPRoute **routes1 = NULL, **routes2 = NULL;
 	NMSettingIPConfig *s_ip1, *s_ip2;
+	gint64 m;
 	const char *s_name;
 	GHashTable *props;
 	guint i, num;
@@ -415,8 +426,12 @@ check_ip_routes (NMConnection *orig,
 		routes2[i] = nm_setting_ip_config_get_route (s_ip2, i);
 	}
 
-	qsort (routes1, num, sizeof (NMIPRoute *), route_ptr_compare);
-	qsort (routes2, num, sizeof (NMIPRoute *), route_ptr_compare);
+	m = nm_setting_ip_config_get_route_metric (s_ip2);
+	if (m != -1)
+		default_metric = m;
+
+	g_qsort_with_data (routes1, num, sizeof (NMIPRoute *), route_ptr_compare, &default_metric);
+	g_qsort_with_data (routes2, num, sizeof (NMIPRoute *), route_ptr_compare, &default_metric);
 
 	for (i = 0; i < num; i++) {
 		if (route_compare (routes1[i], routes2[i], default_metric))
@@ -584,7 +599,7 @@ check_connection_cloned_mac_address (NMConnection *orig,
 	if (s_wired_cand)
 		cand_mac = nm_setting_wired_get_cloned_mac_address (s_wired_cand);
 
-	/* special cloned mac address entires are accepted. */
+	/* special cloned mac address entries are accepted. */
 	if (NM_CLONED_MAC_IS_SPECIAL (orig_mac))
 		orig_mac = NULL;
 	if (NM_CLONED_MAC_IS_SPECIAL (cand_mac))
@@ -710,7 +725,7 @@ check_possible_match (NMConnection *orig,
  * matches well enough.
  */
 NMConnection *
-nm_utils_match_connection (GSList *connections,
+nm_utils_match_connection (NMConnection *const*connections,
                            NMConnection *original,
                            gboolean device_has_carrier,
                            gint64 default_v4_metric,
@@ -719,10 +734,12 @@ nm_utils_match_connection (GSList *connections,
                            gpointer match_filter_data)
 {
 	NMConnection *best_match = NULL;
-	GSList *iter;
 
-	for (iter = connections; iter; iter = iter->next) {
-		NMConnection *candidate = NM_CONNECTION (iter->data);
+	if (!connections)
+		return NULL;
+
+	for (; *connections; connections++) {
+		NMConnection *candidate = NM_CONNECTION (*connections);
 		GHashTable *diffs = NULL;
 
 		if (match_filter_func) {
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index 9b5b9106..c20f2439 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -39,7 +39,7 @@ void nm_utils_complete_generic (NMPlatform *platform,
 
 typedef gboolean (NMUtilsMatchFilterFunc) (NMConnection *connection, gpointer user_data);
 
-NMConnection *nm_utils_match_connection (GSList *connections,
+NMConnection *nm_utils_match_connection (NMConnection *const*connections,
                                          NMConnection *original,
                                          gboolean device_has_carrier,
                                          gint64 default_v4_metric,
diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c
index b04e9fe7..32c4c386 100644
--- a/src/devices/adsl/nm-atm-manager.c
+++ b/src/devices/adsl/nm-atm-manager.c
@@ -21,13 +21,14 @@
 #include "nm-default.h"
 
 #include <string.h>
-#include <gudev/gudev.h>
 #include <gmodule.h>
+#include <libudev.h>
 
 #include "nm-setting-adsl.h"
 #include "nm-device-adsl.h"
 #include "devices/nm-device-factory.h"
 #include "platform/nm-platform.h"
+#include "nm-utils/nm-udev-utils.h"
 
 /*****************************************************************************/
 
@@ -39,7 +40,7 @@
 #define NM_ATM_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_ATM_MANAGER, NMAtmManagerClass))
 
 typedef struct {
-	GUdevClient *client;
+	NMUdevClient *udev_client;
 	GSList *devices;
 } NMAtmManagerPrivate;
 
@@ -73,35 +74,34 @@ nm_device_factory_create (GError **error)
 /*****************************************************************************/
 
 static gboolean
-dev_get_attrs (GUdevDevice *udev_device,
+dev_get_attrs (struct udev_device *udev_device,
                const char **out_path,
                char **out_driver)
 {
-	GUdevDevice *parent = NULL;
+	struct udev_device *parent = NULL;
 	const char *driver, *path;
 
 	g_return_val_if_fail (udev_device != NULL, FALSE);
 	g_return_val_if_fail (out_path != NULL, FALSE);
 	g_return_val_if_fail (out_driver != NULL, FALSE);
 
-	path = g_udev_device_get_sysfs_path (udev_device);
+	path = udev_device_get_syspath (udev_device);
 	if (!path) {
 		nm_log_warn (LOGD_PLATFORM, "couldn't determine device path; ignoring...");
 		return FALSE;
 	}
 
-	driver = g_udev_device_get_driver (udev_device);
+	driver = udev_device_get_driver (udev_device);
 	if (!driver) {
 		/* Try the parent */
-		parent = g_udev_device_get_parent (udev_device);
+		parent = udev_device_get_parent (udev_device);
 		if (parent)
-			driver = g_udev_device_get_driver (parent);
+			driver = udev_device_get_driver (parent);
 	}
 
 	*out_path = path;
 	*out_driver = g_strdup (driver);
 
-	g_clear_object (&parent);
 	return TRUE;
 }
 
@@ -115,7 +115,7 @@ device_destroyed (gpointer user_data, GObject *dead)
 }
 
 static void
-adsl_add (NMAtmManager *self, GUdevDevice *udev_device)
+adsl_add (NMAtmManager *self, struct udev_device *udev_device)
 {
 	NMAtmManagerPrivate *priv = NM_ATM_MANAGER_GET_PRIVATE (self);
 	const char *ifname, *sysfs_path = NULL;
@@ -126,7 +126,7 @@ adsl_add (NMAtmManager *self, GUdevDevice *udev_device)
 
 	g_return_if_fail (udev_device != NULL);
 
-	ifname = g_udev_device_get_name (udev_device);
+	ifname = udev_device_get_sysname (udev_device);
 	if (!ifname) {
 		nm_log_warn (LOGD_PLATFORM, "failed to get device's interface name");
 		return;
@@ -165,10 +165,10 @@ adsl_add (NMAtmManager *self, GUdevDevice *udev_device)
 }
 
 static void
-adsl_remove (NMAtmManager *self, GUdevDevice *udev_device)
+adsl_remove (NMAtmManager *self, struct udev_device *udev_device)
 {
 	NMAtmManagerPrivate *priv = NM_ATM_MANAGER_GET_PRIVATE (self);
-	const char *iface = g_udev_device_get_name (udev_device);
+	const char *iface = udev_device_get_sysname (udev_device);
 	GSList *iter;
 
 	nm_log_dbg (LOGD_PLATFORM, "(%s): removing ATM device", iface);
@@ -194,42 +194,49 @@ start (NMDeviceFactory *factory)
 {
 	NMAtmManager *self = NM_ATM_MANAGER (factory);
 	NMAtmManagerPrivate *priv = NM_ATM_MANAGER_GET_PRIVATE (self);
-	GUdevEnumerator *enumerator;
-	GList *devices, *iter;
-
-	enumerator = g_udev_enumerator_new (priv->client);
-	g_udev_enumerator_add_match_subsystem (enumerator, "atm");
-	g_udev_enumerator_add_match_is_initialized (enumerator);
-	devices = g_udev_enumerator_execute (enumerator);
-	for (iter = devices; iter; iter = g_list_next (iter)) {
-		adsl_add (self, G_UDEV_DEVICE (iter->data));
-		g_object_unref (G_UDEV_DEVICE (iter->data));
+	struct udev_enumerate *enumerate;
+	struct udev_list_entry *devices;
+
+	enumerate = nm_udev_client_enumerate_new (priv->udev_client);
+	udev_enumerate_add_match_is_initialized (enumerate);
+	udev_enumerate_scan_devices (enumerate);
+	devices = udev_enumerate_get_list_entry (enumerate);
+	for (; devices; devices = udev_list_entry_get_next (devices)) {
+		struct udev_device *udevice;
+
+		udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate),
+		                                        udev_list_entry_get_name (devices));
+		if (udevice) {
+			adsl_add (self, udevice);
+			udev_device_unref (udevice);
+		}
 	}
-	g_list_free (devices);
-	g_object_unref (enumerator);
+	udev_enumerate_unref (enumerate);
 }
 
 static void
-handle_uevent (GUdevClient *client,
-               const char *action,
-               GUdevDevice *device,
+handle_uevent (NMUdevClient *client,
+               struct udev_device *device,
                gpointer user_data)
 {
 	NMAtmManager *self = NM_ATM_MANAGER (user_data);
 	const char *subsys;
 	const char *ifindex;
 	guint64 seqnum;
+	const char *action;
+
+	action = udev_device_get_action (device);
 
 	g_return_if_fail (action != NULL);
 
 	/* A bit paranoid */
-	subsys = g_udev_device_get_subsystem (device);
+	subsys = udev_device_get_subsystem (device);
 	g_return_if_fail (!g_strcmp0 (subsys, "atm"));
 
-	ifindex = g_udev_device_get_property (device, "IFINDEX");
-	seqnum = g_udev_device_get_seqnum (device);
+	ifindex = udev_device_get_property_value (device, "IFINDEX");
+	seqnum = udev_device_get_seqnum (device);
 	nm_log_dbg (LOGD_PLATFORM, "UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT,
-	            action, subsys, g_udev_device_get_name (device), ifindex ? ifindex : "unknown", seqnum);
+	            action, subsys, udev_device_get_sysname (device), ifindex ? ifindex : "unknown", seqnum);
 
 	if (!strcmp (action, "add"))
 		adsl_add (self, device);
@@ -243,10 +250,9 @@ static void
 nm_atm_manager_init (NMAtmManager *self)
 {
 	NMAtmManagerPrivate *priv = NM_ATM_MANAGER_GET_PRIVATE (self);
-	const char *subsys[] = { "atm", NULL };
 
-	priv->client = g_udev_client_new (subsys);
-	g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self);
+	priv->udev_client = nm_udev_client_new ((const char *[]) {"atm", NULL },
+	                                        handle_uevent, self);
 }
 
 static void
@@ -256,15 +262,12 @@ dispose (GObject *object)
 	NMAtmManagerPrivate *priv = NM_ATM_MANAGER_GET_PRIVATE (self);
 	GSList *iter;
 
-	if (priv->client) {
-		g_signal_handlers_disconnect_by_func (priv->client, handle_uevent, self);
-		g_clear_object (&priv->client);
-	}
-
 	for (iter = priv->devices; iter; iter = iter->next)
 		g_object_weak_unref (G_OBJECT (iter->data), device_destroyed, self);
 	g_clear_pointer (&priv->devices, g_slist_free);
 
+	priv->udev_client = nm_udev_client_unref (priv->udev_client);
+
 	G_OBJECT_CLASS (nm_atm_manager_parent_class)->dispose (object);
 }
 
diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c
index 53841a7f..fe622bdf 100644
--- a/src/devices/adsl/nm-device-adsl.c
+++ b/src/devices/adsl/nm-device-adsl.c
@@ -129,7 +129,7 @@ complete_connection (NMDevice *device,
 	if (s_adsl && !nm_setting_verify (NM_SETTING (s_adsl), NULL, error))
 		return FALSE;
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_ADSL_SETTING_NAME,
 	                           existing_connections,
@@ -273,14 +273,14 @@ pppoe_vcc_config (NMDeviceAdsl *self)
 		return FALSE;
 
 	/* Watch for the 'nas' interface going away */
-	g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED,
+	g_signal_connect (nm_device_get_platform (device), NM_PLATFORM_SIGNAL_LINK_CHANGED,
 	                  G_CALLBACK (link_changed_cb),
 	                  self);
 
 	_LOGD (LOGD_ADSL, "ATM setup successful");
 
 	/* otherwise we're good for stage3 */
-	nm_platform_link_set_up (NM_PLATFORM_GET, priv->nas_ifindex, NULL);
+	nm_platform_link_set_up (nm_device_get_platform (device), priv->nas_ifindex, NULL);
 
 	return TRUE;
 }
@@ -306,7 +306,7 @@ nas_update_cb (gpointer user_data)
 	}
 
 	g_warn_if_fail (priv->nas_ifindex < 0);
-	priv->nas_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->nas_ifname);
+	priv->nas_ifindex = nm_platform_link_get_ifindex (nm_device_get_platform (device), priv->nas_ifname);
 	if (priv->nas_ifindex < 0) {
 		/* Keep waiting for it to appear */
 		return G_SOURCE_CONTINUE;
@@ -329,12 +329,12 @@ nas_update_cb (gpointer user_data)
 static NMActStageReturn
 br2684_create_iface (NMDeviceAdsl *self,
                      NMSettingAdsl *s_adsl,
-                     NMDeviceStateReason *out_reason)
+                     NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
 	struct atm_newif_br2684 ni;
-	int err, fd, errsv;
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
+	nm_auto_close int fd = -1;
+	int err, errsv;
 	guint num = 0;
 
 	g_return_val_if_fail (s_adsl != NULL, FALSE);
@@ -348,7 +348,7 @@ br2684_create_iface (NMDeviceAdsl *self,
 	if (fd < 0) {
 		errsv = errno;
 		_LOGE (LOGD_ADSL, "failed to open ATM control socket (%d)", errsv);
-		*out_reason = NM_DEVICE_STATE_REASON_BR2684_FAILED;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_BR2684_FAILED);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
@@ -374,39 +374,36 @@ br2684_create_iface (NMDeviceAdsl *self,
 
 			priv->nas_update_count = 0;
 			priv->nas_update_id = g_timeout_add (100, nas_update_cb, self);
-			ret = NM_ACT_STAGE_RETURN_POSTPONE;
-			break;
-		} else if (errno != EEXIST) {
+			return NM_ACT_STAGE_RETURN_POSTPONE;
+		}
+		if (errno != EEXIST) {
 			errsv = errno;
 			_LOGW (LOGD_ADSL, "failed to create br2684 interface (%d)", errsv);
-			*out_reason = NM_DEVICE_STATE_REASON_BR2684_FAILED;
 			break;
 		}
 	}
 
-	close (fd);
-	return ret;
+	NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_BR2684_FAILED);
+	return NM_ACT_STAGE_RETURN_FAILURE;
 }
 
 static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *out_reason)
+act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceAdsl *self = NM_DEVICE_ADSL (device);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 	NMSettingAdsl *s_adsl;
 	const char *protocol;
 
-	g_assert (out_reason);
-
 	s_adsl = nm_connection_get_setting_adsl (nm_device_get_applied_connection (device));
-	g_assert (s_adsl);
+	g_return_val_if_fail (s_adsl, NM_ACT_STAGE_RETURN_FAILURE);
 
 	protocol = nm_setting_adsl_get_protocol (s_adsl);
 	_LOGD (LOGD_ADSL, "using ADSL protocol '%s'", protocol);
 
 	if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOE) == 0) {
 		/* PPPoE needs RFC2684 bridging before we can do PPP over it */
-		ret = br2684_create_iface (self, s_adsl, out_reason);
+		ret = br2684_create_iface (self, s_adsl, out_failure_reason);
 	} else if (g_strcmp0 (protocol, NM_SETTING_ADSL_PROTOCOL_PPPOA) == 0) {
 		/* PPPoA doesn't need anything special */
 		ret = NM_ACT_STAGE_RETURN_SUCCESS;
@@ -451,20 +448,19 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 static NMActStageReturn
 act_stage3_ip4_config_start (NMDevice *device,
                              NMIP4Config **out_config,
-                             NMDeviceStateReason *reason)
+                             NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceAdsl *self = NM_DEVICE_ADSL (device);
 	NMDeviceAdslPrivate *priv = NM_DEVICE_ADSL_GET_PRIVATE (self);
 	NMSettingAdsl *s_adsl;
 	NMActRequest *req;
 	GError *err = NULL;
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 	const char *ppp_iface;
 
 	req = nm_device_get_act_request (device);
-	g_assert (req);
+	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
 	s_adsl = (NMSettingAdsl *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_ADSL);
-	g_assert (s_adsl);
+	g_return_val_if_fail (s_adsl, NM_ACT_STAGE_RETURN_FAILURE);
 
 	/* PPPoE uses the NAS interface, not the ATM interface */
 	if (g_strcmp0 (nm_setting_adsl_get_protocol (s_adsl), NM_SETTING_ADSL_PROTOCOL_PPPOE) == 0) {
@@ -478,27 +474,26 @@ act_stage3_ip4_config_start (NMDevice *device,
 	}
 
 	priv->ppp_manager = nm_ppp_manager_create (ppp_iface, &err);
-	if (   priv->ppp_manager
-	    && nm_ppp_manager_start (priv->ppp_manager, req,
-	                             nm_setting_adsl_get_username (s_adsl),
-	                             30, 0, &err)) {
-		g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
-		                  G_CALLBACK (ppp_state_changed),
-		                  self);
-		g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
-		                  G_CALLBACK (ppp_ip4_config),
-		                  self);
-		ret = NM_ACT_STAGE_RETURN_POSTPONE;
-	} else {
+	if (   !priv->ppp_manager
+	    || !nm_ppp_manager_start (priv->ppp_manager, req,
+	                              nm_setting_adsl_get_username (s_adsl),
+	                              30, 0, &err)) {
 		_LOGW (LOGD_ADSL, "PPP failed to start: %s", err->message);
 		g_error_free (err);
 
 		g_clear_object (&priv->ppp_manager);
 
-		*reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED);
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	return ret;
+	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
+	                  G_CALLBACK (ppp_state_changed),
+	                  self);
+	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
+	                  G_CALLBACK (ppp_ip4_config),
+	                  self);
+	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
 static void
@@ -513,7 +508,7 @@ adsl_cleanup (NMDeviceAdsl *self)
 		g_clear_object (&priv->ppp_manager);
 	}
 
-	g_signal_handlers_disconnect_by_func (NM_PLATFORM_GET, G_CALLBACK (link_changed_cb), self);
+	g_signal_handlers_disconnect_by_func (nm_device_get_platform (NM_DEVICE (self)), G_CALLBACK (link_changed_cb), self);
 
 	if (priv->brfd >= 0) {
 		close (priv->brfd);
@@ -547,7 +542,7 @@ carrier_update_cb (gpointer user_data)
 
 	path  = g_strdup_printf ("/sys/class/atm/%s/carrier",
 	                         NM_ASSERT_VALID_PATH_COMPONENT (nm_device_get_iface (NM_DEVICE (self))));
-	carrier = (int) nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (path), 10, 0, 1, -1);
+	carrier = (int) nm_platform_sysctl_get_int_checked (nm_device_get_platform (NM_DEVICE (self)), NMP_SYSCTL_PATHID_ABSOLUTE (path), 10, 0, 1, -1);
 	g_free (path);
 
 	if (carrier != -1)
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index 92b2c918..ebfa0d64 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -183,7 +183,8 @@ pan_connection_check_create (NMBluezDevice *self)
 	NMConnection *connection;
 	NMConnection *added;
 	NMSetting *setting;
-	char *uuid, *id;
+	char *id;
+	char uuid[37];
 	GError *error = NULL;
 	NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self);
 
@@ -205,7 +206,7 @@ pan_connection_check_create (NMBluezDevice *self)
 	connection = nm_simple_connection_new ();
 
 	/* Setting: Connection */
-	uuid = nm_utils_uuid_generate ();
+	nm_utils_uuid_generate_buf (uuid);
 	id = g_strdup_printf (_("%s Network"), priv->name);
 	setting = nm_setting_connection_new ();
 	g_object_set (setting,
@@ -266,7 +267,6 @@ pan_connection_check_create (NMBluezDevice *self)
 	g_object_unref (connection);
 
 	g_free (id);
-	g_free (uuid);
 }
 
 static gboolean
diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c
index 58fa9b01..2f0afa16 100644
--- a/src/devices/bluetooth/nm-bluez-manager.c
+++ b/src/devices/bluetooth/nm-bluez-manager.c
@@ -134,10 +134,7 @@ cleanup_checking (NMBluezManager *self, gboolean do_unwatch_name)
 {
 	NMBluezManagerPrivate *priv = NM_BLUEZ_MANAGER_GET_PRIVATE (self);
 
-	if (priv->async_cancellable) {
-		g_cancellable_cancel (priv->async_cancellable);
-		g_clear_object (&priv->async_cancellable);
-	}
+	nm_clear_g_cancellable (&priv->async_cancellable);
 
 	g_clear_object (&priv->introspect_proxy);
 
diff --git a/src/devices/bluetooth/nm-bt-error.c b/src/devices/bluetooth/nm-bt-error.c
index 18391187..66c65b6d 100644
--- a/src/devices/bluetooth/nm-bt-error.c
+++ b/src/devices/bluetooth/nm-bt-error.c
@@ -22,13 +22,5 @@
 
 #include "nm-bt-error.h"
 
-GQuark
-nm_bt_error_quark (void)
-{
-	static GQuark quark = 0;
-	if (!quark)
-		quark = g_quark_from_static_string ("nm-bt-error");
-	return quark;
-}
-
+NM_CACHED_QUARK_FCN ("nm-bt-error", nm_bt_error_quark)
 
diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c
index 31b9bbf9..4ee71489 100644
--- a/src/devices/bluetooth/nm-device-bt.c
+++ b/src/devices/bluetooth/nm-device-bt.c
@@ -99,7 +99,7 @@ G_DEFINE_TYPE (NMDeviceBt, nm_device_bt, NM_TYPE_DEVICE)
 
 /*****************************************************************************/
 
-static gboolean modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *reason);
+static gboolean modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *out_failure_reason);
 
 /*****************************************************************************/
 
@@ -328,7 +328,7 @@ complete_connection (NMDevice *device,
 		return FALSE;
 	}
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_BLUETOOTH_SETTING_NAME,
 	                           existing_connections,
@@ -362,18 +362,24 @@ complete_connection (NMDevice *device,
 
 static void
 ppp_stats (NMModem *modem,
-           guint32 in_bytes,
-           guint32 out_bytes,
+           guint i_in_bytes,
+           guint i_out_bytes,
            gpointer user_data)
 {
-	g_signal_emit (NM_DEVICE_BT (user_data), signals[PPP_STATS], 0, in_bytes, out_bytes);
+	guint32 in_bytes = i_in_bytes;
+	guint32 out_bytes = i_out_bytes;
+
+	g_signal_emit (NM_DEVICE_BT (user_data), signals[PPP_STATS], 0, (guint) in_bytes, (guint) out_bytes);
 }
 
 static void
-ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data)
+ppp_failed (NMModem *modem,
+            guint i_reason,
+            gpointer user_data)
 {
 	NMDevice *device = NM_DEVICE (user_data);
 	NMDeviceBt *self = NM_DEVICE_BT (user_data);
+	NMDeviceStateReason reason = i_reason;
 
 	switch (nm_device_get_state (device)) {
 	case NM_DEVICE_STATE_PREPARE:
@@ -430,28 +436,30 @@ modem_auth_result (NMModem *modem, GError *error, gpointer user_data)
 {
 	NMDevice *device = NM_DEVICE (user_data);
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 
 	if (error) {
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_FAILED,
 		                         NM_DEVICE_STATE_REASON_NO_SECRETS);
 	} else {
+		NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
+
 		/* Otherwise, on success for GSM/CDMA secrets we need to schedule modem stage1 again */
 		g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH);
-		if (!modem_stage1 (NM_DEVICE_BT (device), priv->modem, &reason))
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
+		if (!modem_stage1 (NM_DEVICE_BT (device), priv->modem, &failure_reason))
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, failure_reason);
 	}
 }
 
 static void
 modem_prepare_result (NMModem *modem,
                       gboolean success,
-                      NMDeviceStateReason reason,
+                      guint i_reason,
                       gpointer user_data)
 {
 	NMDeviceBt *self = NM_DEVICE_BT (user_data);
 	NMDevice *device = NM_DEVICE (self);
+	NMDeviceStateReason reason = i_reason;
 	NMDeviceState state;
 
 	state = nm_device_get_state (device);
@@ -460,12 +468,12 @@ modem_prepare_result (NMModem *modem,
 	if (success) {
 		NMActRequest *req;
 		NMActStageReturn ret;
-		NMDeviceStateReason stage2_reason = NM_DEVICE_STATE_REASON_NONE;
+		NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 
 		req = nm_device_get_act_request (device);
-		g_assert (req);
+		g_return_if_fail (req);
 
-		ret = nm_modem_act_stage2_config (modem, req, &stage2_reason);
+		ret = nm_modem_act_stage2_config (modem, req, &failure_reason);
 		switch (ret) {
 		case NM_ACT_STAGE_RETURN_POSTPONE:
 			break;
@@ -474,17 +482,17 @@ modem_prepare_result (NMModem *modem,
 			break;
 		case NM_ACT_STAGE_RETURN_FAILURE:
 		default:
-			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, stage2_reason);
+			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, failure_reason);
 			break;
 		}
 	} else {
-		if (reason == NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT) {
+		if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT) {
 			/* If the connect failed because the SIM PIN was wrong don't allow
 			 * the device to be auto-activated anymore, which would risk locking
 			 * the SIM if the incorrect PIN continues to be used.
 			 */
-			nm_device_set_autoconnect (device, FALSE);
 			_LOGI (LOGD_MB, "disabling autoconnect due to failed SIM PIN");
+			nm_device_set_autoconnect_intern (device, FALSE);
 		}
 
 		nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason);
@@ -500,7 +508,7 @@ device_state_changed (NMDevice *device,
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
 
 	if (priv->modem)
-		nm_modem_device_state_changed (priv->modem, new_state, old_state, reason);
+		nm_modem_device_state_changed (priv->modem, new_state, old_state);
 
 	/* Need to recheck available connections whenever MM appears or disappears,
 	 * since the device could be both DUN and NAP capable and thus may not
@@ -541,17 +549,15 @@ data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
 }
 
 static gboolean
-modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *reason)
+modem_stage1 (NMDeviceBt *self, NMModem *modem, NMDeviceStateReason *out_failure_reason)
 {
 	NMActRequest *req;
 	NMActStageReturn ret;
 
-	g_return_val_if_fail (reason != NULL, FALSE);
-
 	req = nm_device_get_act_request (NM_DEVICE (self));
-	g_assert (req);
+	g_return_val_if_fail (req, FALSE);
 
-	ret = nm_modem_act_stage1_prepare (modem, req, reason);
+	ret = nm_modem_act_stage1_prepare (modem, req, out_failure_reason);
 	switch (ret) {
 	case NM_ACT_STAGE_RETURN_POSTPONE:
 	case NM_ACT_STAGE_RETURN_SUCCESS:
@@ -639,7 +645,7 @@ component_added (NMDevice *device, GObject *component)
 	const gchar *modem_control_port;
 	char *base;
 	NMDeviceState state;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 
 	if (!NM_IS_MODEM (component))
 		return FALSE;
@@ -694,8 +700,8 @@ component_added (NMDevice *device, GObject *component)
 	g_signal_connect (modem, "notify::" NM_MODEM_DATA_PORT, G_CALLBACK (data_port_changed_cb), self);
 
 	/* Kick off the modem connection */
-	if (!modem_stage1 (self, modem, &reason))
-		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, reason);
+	if (!modem_stage1 (self, modem, &failure_reason))
+		nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, failure_reason);
 
 	return TRUE;
 }
@@ -836,14 +842,15 @@ bt_connect_timeout (gpointer user_data)
 }
 
 static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceBt *self = NM_DEVICE_BT (device);
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self);
 	NMConnection *connection;
 
 	connection = nm_device_get_applied_connection (device);
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+
 	priv->bt_type = get_connection_bt_type (connection);
 	if (priv->bt_type == NM_BT_CAPABILITY_NONE) {
 		// FIXME: set a reason code
@@ -851,7 +858,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	}
 
 	if (priv->bt_type == NM_BT_CAPABILITY_DUN && !priv->mm_running) {
-		*reason = NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
@@ -862,8 +869,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	                               priv->bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP),
 	                               bluez_connect_cb, g_object_ref (device));
 
-	if (priv->timeout_id)
-		g_source_remove (priv->timeout_id);
+	nm_clear_g_source (&priv->timeout_id);
 	priv->timeout_id = g_timeout_add_seconds (30, bt_connect_timeout, device);
 
 	return NM_ACT_STAGE_RETURN_POSTPONE;
@@ -872,38 +878,34 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 static NMActStageReturn
 act_stage3_ip4_config_start (NMDevice *device,
                              NMIP4Config **out_config,
-                             NMDeviceStateReason *reason)
+                             NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
-	NMActStageReturn ret;
 
 	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
-		ret = nm_modem_stage3_ip4_config_start (priv->modem,
-		                                        device,
-		                                        NM_DEVICE_CLASS (nm_device_bt_parent_class),
-		                                        reason);
-	} else
-		ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip4_config_start (device, out_config, reason);
+		return nm_modem_stage3_ip4_config_start (priv->modem,
+		                                         device,
+		                                         NM_DEVICE_CLASS (nm_device_bt_parent_class),
+		                                         out_failure_reason);
+	}
 
-	return ret;
+	return NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip4_config_start (device, out_config, out_failure_reason);
 }
 
 static NMActStageReturn
 act_stage3_ip6_config_start (NMDevice *device,
                              NMIP6Config **out_config,
-                             NMDeviceStateReason *reason)
+                             NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device);
-	NMActStageReturn ret;
 
 	if (priv->bt_type == NM_BT_CAPABILITY_DUN) {
-		ret = nm_modem_stage3_ip6_config_start (priv->modem,
-		                                        nm_device_get_act_request (device),
-		                                        reason);
-	} else
-		ret = NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip6_config_start (device, out_config, reason);
+		return nm_modem_stage3_ip6_config_start (priv->modem,
+		                                         nm_device_get_act_request (device),
+		                                         out_failure_reason);
+	}
 
-	return ret;
+	return NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip6_config_start (device, out_config, out_failure_reason);
 }
 
 static void
@@ -923,8 +925,7 @@ deactivate (NMDevice *device)
 			 */
 			nm_modem_device_state_changed (priv->modem,
 			                               NM_DEVICE_STATE_DISCONNECTED,
-			                               NM_DEVICE_STATE_ACTIVATED,
-			                               NM_DEVICE_STATE_REASON_USER_REQUESTED);
+			                               NM_DEVICE_STATE_ACTIVATED);
 			modem_cleanup (NM_DEVICE_BT (device));
 		}
 	}
@@ -1028,15 +1029,15 @@ set_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_BT_NAME:
-		/* Construct only */
+		/* construct-only */
 		priv->name = g_value_dup_string (value);
 		break;
 	case PROP_BT_CAPABILITIES:
-		/* Construct only */
+		/* construct-only */
 		priv->capabilities = g_value_get_uint (value);
 		break;
 	case PROP_BT_DEVICE:
-		/* Construct only */
+		/* construct-only */
 		priv->bt_device = g_value_dup_object (value);
 		g_signal_connect (priv->bt_device, "removed", G_CALLBACK (bluez_device_removed), object);
 		break;
@@ -1174,6 +1175,7 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 	device_class->complete_connection = complete_connection;
 	device_class->is_available = is_available;
 	device_class->component_added = component_added;
+	device_class->get_configured_mtu = nm_modem_get_configured_mtu;
 
 	device_class->state_changed = device_state_changed;
 
@@ -1198,12 +1200,13 @@ nm_device_bt_class_init (NMDeviceBtClass *klass)
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	signals[PPP_STATS] =
-	    g_signal_new ("ppp-stats",
+	    g_signal_new (NM_DEVICE_BT_PPP_STATS,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 2,
-	                  G_TYPE_UINT, G_TYPE_UINT);
+	                  G_TYPE_UINT /*guint32 in_bytes*/,
+	                  G_TYPE_UINT /*guint32 out_bytes*/);
 
 	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
 	                                        NMDBUS_TYPE_DEVICE_BLUETOOTH_SKELETON,
diff --git a/src/devices/bluetooth/nm-device-bt.h b/src/devices/bluetooth/nm-device-bt.h
index 43bd4257..9bcf6ca8 100644
--- a/src/devices/bluetooth/nm-device-bt.h
+++ b/src/devices/bluetooth/nm-device-bt.h
@@ -37,6 +37,8 @@
 #define NM_DEVICE_BT_CAPABILITIES "bt-capabilities"
 #define NM_DEVICE_BT_DEVICE       "bt-device"
 
+#define NM_DEVICE_BT_PPP_STATS    "ppp-stats"
+
 typedef struct _NMDeviceBt NMDeviceBt;
 typedef struct _NMDeviceBtClass NMDeviceBtClass;
 
diff --git a/src/devices/nm-arping-manager.c b/src/devices/nm-arping-manager.c
index 7b765844..51f80e08 100644
--- a/src/devices/nm-arping-manager.c
+++ b/src/devices/nm-arping-manager.c
@@ -81,13 +81,14 @@ G_DEFINE_TYPE (NMArpingManager, nm_arping_manager, G_TYPE_OBJECT)
 #define _NMLOG(level, ...) \
     G_STMT_START { \
         char _sbuf[64]; \
+        int _ifindex = (self) ? NM_ARPING_MANAGER_GET_PRIVATE (self)->ifindex : 0; \
         \
         nm_log ((level), _NMLOG_DOMAIN, \
+                nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex), \
+                NULL, \
                 "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                 _NMLOG_PREFIX_NAME, \
-                self ? nm_sprintf_buf (_sbuf, "[%p,%d]", \
-                                       self, \
-                                       NM_ARPING_MANAGER_GET_PRIVATE (self)->ifindex) : "" \
+                self ? nm_sprintf_buf (_sbuf, "[%p,%d]", self, _ifindex) : "" \
                 _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
     } G_STMT_END
 
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index 34d34eb4..3325c948 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -101,7 +101,7 @@ complete_connection (NMDevice *device,
 {
 	NMSettingBond *s_bond;
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_BOND_SETTING_NAME,
 	                           existing_connections,
@@ -131,7 +131,7 @@ set_bond_attr (NMDevice *device, NMBondMode mode, const char *attr, const char *
 	if (!_nm_setting_bond_option_supported (attr, mode))
 		return FALSE;
 
-	ret = nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, attr, value);
+	ret = nm_platform_sysctl_master_set_option (nm_device_get_platform (device), ifindex, attr, value);
 	if (!ret)
 		_LOGW (LOGD_PLATFORM, "failed to set bonding attribute '%s' to '%s'", attr, value);
 	return ret;
@@ -165,7 +165,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 	/* Read bond options from sysfs and update the Bond setting to match */
 	options = nm_setting_bond_get_valid_options (s_bond);
 	while (options && *options) {
-		gs_free char *value = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, *options);
+		gs_free char *value = nm_platform_sysctl_master_get_option (nm_device_get_platform (device), ifindex, *options);
 		const char *defvalue = nm_setting_bond_get_option_default (s_bond, *options);
 		char *p;
 
@@ -328,7 +328,7 @@ apply_bonding_config (NMDevice *device)
 	set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_PRIMARY, value ? value : "");
 
 	/* ARP targets: clear and initialize the list */
-	contents = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex,
+	contents = nm_platform_sysctl_master_get_option (nm_device_get_platform (device), ifindex,
 	                                                 NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
 	set_arp_targets (device, mode, contents, " \n", "-");
 	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
@@ -363,21 +363,19 @@ apply_bonding_config (NMDevice *device)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *out_failure_reason)
 {
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
 	gboolean no_firmware = FALSE;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	ret = NM_DEVICE_CLASS (nm_device_bond_parent_class)->act_stage1_prepare (dev, reason);
+	ret = NM_DEVICE_CLASS (nm_device_bond_parent_class)->act_stage1_prepare (dev, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
 	/* Interface must be down to set bond options */
 	nm_device_take_down (dev, TRUE);
 	ret = apply_bonding_config (dev);
-	if (ret)
+	if (ret != NM_ACT_STAGE_RETURN_FAILURE)
 		ret = nm_device_hw_addr_set_cloned (dev, nm_device_get_applied_connection (dev), FALSE);
 	nm_device_bring_up (dev, TRUE, &no_firmware);
 
@@ -393,12 +391,13 @@ enslave_slave (NMDevice *device,
 	NMDeviceBond *self = NM_DEVICE_BOND (device);
 	gboolean success = TRUE, no_firmware = FALSE;
 	const char *slave_iface = nm_device_get_ip_iface (slave);
+	NMConnection *master_con;
 
 	nm_device_master_check_slave_physical_port (device, slave, LOGD_BOND);
 
 	if (configure) {
 		nm_device_take_down (slave, TRUE);
-		success = nm_platform_link_enslave (NM_PLATFORM_GET,
+		success = nm_platform_link_enslave (nm_device_get_platform (device),
 		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
 		nm_device_bring_up (slave, TRUE, &no_firmware);
@@ -407,6 +406,25 @@ enslave_slave (NMDevice *device,
 			return FALSE;
 
 		_LOGI (LOGD_BOND, "enslaved bond slave %s", slave_iface);
+
+		/* The active_slave option can be set only after the interface is enslaved */
+		master_con = nm_device_get_applied_connection (device);
+		if (master_con) {
+			NMSettingBond *s_bond = nm_connection_get_setting_bond (master_con);
+			const char *active;
+
+			if (s_bond) {
+				active = nm_setting_bond_get_option_by_name (s_bond, "active_slave");
+				if (active && 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",
+					                                      active);
+					_LOGD (LOGD_BOND, "setting slave %s as active one for master %s",
+					       active, nm_device_get_iface (device));
+				}
+			}
+		}
 	} else
 		_LOGI (LOGD_BOND, "bond slave %s was enslaved", slave_iface);
 
@@ -428,7 +446,7 @@ release_slave (NMDevice *device,
 		 */
 		address = g_strdup (nm_device_get_hw_address (device));
 
-		success = nm_platform_link_release (NM_PLATFORM_GET,
+		success = nm_platform_link_release (nm_device_get_platform (device),
 		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
 
@@ -440,7 +458,7 @@ release_slave (NMDevice *device,
 			       nm_device_get_ip_iface (slave));
 		}
 
-		nm_platform_process_events (NM_PLATFORM_GET);
+		nm_platform_process_events (nm_device_get_platform (device));
 		if (nm_device_update_hw_address (device))
 			nm_device_hw_addr_set (device, address, "restore", FALSE);
 
@@ -468,7 +486,7 @@ create_and_realize (NMDevice *device,
 
 	g_assert (iface);
 
-	plerr = nm_platform_link_bond_add (NM_PLATFORM_GET, iface, out_plink);
+	plerr = nm_platform_link_bond_add (nm_device_get_platform (device), iface, out_plink);
 	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 		             "Failed to create bond interface '%s' for '%s': %s",
@@ -480,6 +498,116 @@ create_and_realize (NMDevice *device,
 	return TRUE;
 }
 
+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;
+
+	/* Check that options in @s_a have compatible changes in @s_b */
+
+	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);
+
+		/* 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))) {
+			continue;
+		}
+
+		/* Reject any other changes */
+		if (!nm_streq0 (value_a, value_b)) {
+			g_set_error (error,
+			             NM_DEVICE_ERROR,
+			             NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+			             "Can't reapply '%s' bond option",
+			             name);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
+can_reapply_change (NMDevice *device,
+                    const char *setting_name,
+                    NMSetting *s_old,
+                    NMSetting *s_new,
+                    GHashTable *diffs,
+                    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)) {
+		if (!nm_device_hash_check_invalid_keys (diffs,
+		                                        NM_SETTING_BOND_SETTING_NAME,
+		                                        error,
+		                                        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;
+	}
+
+	device_class = NM_DEVICE_CLASS (nm_device_bond_parent_class);
+	return device_class->can_reapply_change (device,
+	                                         setting_name,
+	                                         s_old,
+	                                         s_new,
+	                                         diffs,
+	                                         error);
+}
+
+static void
+reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_new)
+{
+	NMDeviceBond *self = NM_DEVICE_BOND (device);
+	const char *value;
+	NMSettingBond *s_bond;
+	NMBondMode mode;
+
+	NM_DEVICE_CLASS (nm_device_bond_parent_class)->reapply_connection (device,
+	                                                                   con_old,
+	                                                                   con_new);
+
+	_LOGD (LOGD_BOND, "reapplying bond settings");
+	s_bond = nm_connection_get_setting_bond (con_new);
+	g_return_if_fail (s_bond);
+
+	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE);
+	if (!value)
+		value = "balance-rr";
+
+	mode = _nm_setting_bond_mode_from_string (value);
+	g_return_if_fail (mode != NM_BOND_MODE_UNKNOWN);
+
+	/* Primary */
+	value = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
+	set_bond_attr (device, mode, NM_SETTING_BOND_OPTION_PRIMARY, value ? value : "");
+
+	/* Active slave */
+	set_simple_option (device, mode, s_bond, NM_SETTING_BOND_OPTION_ACTIVE_SLAVE);
+}
+
 /*****************************************************************************/
 
 static void
@@ -508,6 +636,8 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
 	parent_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
 	parent_class->enslave_slave = enslave_slave;
 	parent_class->release_slave = release_slave;
+	parent_class->can_reapply_change = can_reapply_change;
+	parent_class->reapply_connection = reapply_connection;
 
 	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
 	                                        NMDBUS_TYPE_DEVICE_BOND_SKELETON,
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index 613d59cd..01c4eb22 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -107,7 +107,7 @@ complete_connection (NMDevice *device,
 {
 	NMSettingBridge *s_bridge;
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_BRIDGE_SETTING_NAME,
 	                           existing_connections,
@@ -197,9 +197,9 @@ commit_option (NMDevice *device, NMSetting *setting, const Option *option, gbool
 
 	value = g_strdup_printf ("%u", uval);
 	if (slave)
-		nm_platform_sysctl_slave_set_option (NM_PLATFORM_GET, ifindex, option->sysname, value);
+		nm_platform_sysctl_slave_set_option (nm_device_get_platform (device), ifindex, option->sysname, value);
 	else
-		nm_platform_sysctl_master_set_option (NM_PLATFORM_GET, ifindex, option->sysname, value);
+		nm_platform_sysctl_master_set_option (nm_device_get_platform (device), ifindex, option->sysname, value);
 }
 
 static void
@@ -243,7 +243,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 	}
 
 	for (option = master_options; option->name; option++) {
-		gs_free char *str = nm_platform_sysctl_master_get_option (NM_PLATFORM_GET, ifindex, option->sysname);
+		gs_free char *str = nm_platform_sysctl_master_get_option (nm_device_get_platform (device), ifindex, option->sysname);
 		int value;
 
 		if (str) {
@@ -282,7 +282,7 @@ master_update_slave_connection (NMDevice *device,
 	}
 
 	for (option = slave_options; option->name; option++) {
-		gs_free char *str = nm_platform_sysctl_slave_get_option (NM_PLATFORM_GET, ifindex_slave, option->sysname);
+		gs_free char *str = nm_platform_sysctl_slave_get_option (nm_device_get_platform (device), ifindex_slave, option->sysname);
 		int value;
 
 		if (str) {
@@ -305,14 +305,14 @@ master_update_slave_connection (NMDevice *device,
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMActStageReturn ret;
 	NMConnection *connection = nm_device_get_applied_connection (device);
 
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
-	ret = NM_DEVICE_CLASS (nm_device_bridge_parent_class)->act_stage1_prepare (device, reason);
+	ret = NM_DEVICE_CLASS (nm_device_bridge_parent_class)->act_stage1_prepare (device, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
@@ -333,7 +333,7 @@ enslave_slave (NMDevice *device,
 	NMDeviceBridge *self = NM_DEVICE_BRIDGE (device);
 
 	if (configure) {
-		if (!nm_platform_link_enslave (NM_PLATFORM_GET, nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)))
+		if (!nm_platform_link_enslave (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)))
 			return FALSE;
 
 		commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection));
@@ -357,7 +357,7 @@ release_slave (NMDevice *device,
 	gboolean success;
 
 	if (configure) {
-		success = nm_platform_link_release (NM_PLATFORM_GET,
+		success = nm_platform_link_release (nm_device_get_platform (device),
 		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
 
@@ -401,7 +401,7 @@ create_and_realize (NMDevice *device,
 		}
 	}
 
-	plerr = nm_platform_link_bridge_add (NM_PLATFORM_GET,
+	plerr = nm_platform_link_bridge_add (nm_device_get_platform (device),
 	                                     iface,
 	                                     hwaddr ? mac_address : NULL,
 	                                     hwaddr ? ETH_ALEN : 0,
diff --git a/src/devices/nm-device-dummy.c b/src/devices/nm-device-dummy.c
new file mode 100644
index 00000000..dce4f7bc
--- /dev/null
+++ b/src/devices/nm-device-dummy.c
@@ -0,0 +1,205 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-device-dummy.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "nm-act-request.h"
+#include "nm-device-private.h"
+#include "nm-ip4-config.h"
+#include "platform/nm-platform.h"
+#include "nm-device-factory.h"
+#include "nm-setting-dummy.h"
+#include "nm-core-internal.h"
+
+#include "introspection/org.freedesktop.NetworkManager.Device.Dummy.h"
+
+#include "nm-device-logging.h"
+_LOG_DECLARE_SELF(NMDeviceDummy);
+
+/*****************************************************************************/
+
+struct _NMDeviceDummy {
+	NMDevice parent;
+};
+
+struct _NMDeviceDummyClass {
+	NMDeviceClass parent;
+};
+
+G_DEFINE_TYPE (NMDeviceDummy, nm_device_dummy, NM_TYPE_DEVICE)
+
+/*****************************************************************************/
+
+static NMDeviceCapabilities
+get_generic_capabilities (NMDevice *dev)
+{
+	return NM_DEVICE_CAP_IS_SOFTWARE;
+}
+
+static gboolean
+complete_connection (NMDevice *device,
+                     NMConnection *connection,
+                     const char *specific_object,
+                     const GSList *existing_connections,
+                     GError **error)
+{
+	NMSettingDummy *s_dummy;
+
+	nm_utils_complete_generic (nm_device_get_platform (device),
+	                           connection,
+	                           NM_SETTING_DUMMY_SETTING_NAME,
+	                           existing_connections,
+	                           NULL,
+	                           _("Dummy connection"),
+	                           NULL,
+	                           TRUE);
+
+	s_dummy = nm_connection_get_setting_dummy (connection);
+	if (!s_dummy) {
+		g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION,
+		                     "A 'dummy' setting is required.");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+update_connection (NMDevice *device, NMConnection *connection)
+{
+	NMSettingDummy *s_dummy = nm_connection_get_setting_dummy (connection);
+
+	if (!s_dummy) {
+		s_dummy = (NMSettingDummy *) nm_setting_dummy_new ();
+		nm_connection_add_setting (connection, (NMSetting *) s_dummy);
+	}
+}
+
+static gboolean
+create_and_realize (NMDevice *device,
+                    NMConnection *connection,
+                    NMDevice *parent,
+                    const NMPlatformLink **out_plink,
+                    GError **error)
+{
+	const char *iface = nm_device_get_iface (device);
+	NMPlatformError plerr;
+	NMSettingDummy *s_dummy;
+
+	s_dummy = nm_connection_get_setting_dummy (connection);
+	g_assert (s_dummy);
+
+	plerr = nm_platform_link_dummy_add (nm_device_get_platform (device), iface, out_plink);
+	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
+		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+		             "Failed to create dummy interface '%s' for '%s': %s",
+		             iface,
+		             nm_connection_get_id (connection),
+		             nm_platform_error_to_string (plerr));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static gboolean
+check_connection_compatible (NMDevice *device, NMConnection *connection)
+{
+	NMSettingDummy *s_dummy;
+
+	if (!NM_DEVICE_CLASS (nm_device_dummy_parent_class)->check_connection_compatible (device, connection))
+		return FALSE;
+
+	s_dummy = nm_connection_get_setting_dummy (connection);
+	if (!s_dummy)
+		return FALSE;
+
+	return TRUE;
+}
+
+static NMActStageReturn
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
+{
+	NMActStageReturn ret;
+
+	ret = NM_DEVICE_CLASS (nm_device_dummy_parent_class)->act_stage1_prepare (device, out_failure_reason);
+	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
+		return ret;
+
+	if (!nm_device_hw_addr_set_cloned (device, nm_device_get_applied_connection (device), FALSE))
+		return NM_ACT_STAGE_RETURN_FAILURE;
+
+	return NM_ACT_STAGE_RETURN_SUCCESS;
+}
+
+/*****************************************************************************/
+
+static void
+nm_device_dummy_init (NMDeviceDummy *self)
+{
+}
+
+static void
+nm_device_dummy_class_init (NMDeviceDummyClass *klass)
+{
+	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
+
+	NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_DUMMY)
+
+	device_class->connection_type = NM_SETTING_DUMMY_SETTING_NAME;
+	device_class->complete_connection = complete_connection;
+	device_class->check_connection_compatible = check_connection_compatible;
+	device_class->create_and_realize = create_and_realize;
+	device_class->get_generic_capabilities = get_generic_capabilities;
+	device_class->update_connection = update_connection;
+	device_class->act_stage1_prepare = act_stage1_prepare;
+	device_class->get_configured_mtu = nm_device_get_configured_mtu_for_wired;
+
+	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
+	                                        NMDBUS_TYPE_DEVICE_DUMMY_SKELETON,
+	                                        NULL);
+}
+
+
+/*****************************************************************************/
+
+#define NM_TYPE_DUMMY_DEVICE_FACTORY (nm_dummy_device_factory_get_type ())
+#define NM_DUMMY_DEVICE_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DUMMY_DEVICE_FACTORY, NMDummyDeviceFactory))
+
+static NMDevice *
+create_device (NMDeviceFactory *factory,
+               const char *iface,
+               const NMPlatformLink *plink,
+               NMConnection *connection,
+               gboolean *out_ignore)
+{
+	return (NMDevice *) g_object_new (NM_TYPE_DEVICE_DUMMY,
+	                                  NM_DEVICE_IFACE, iface,
+	                                  NM_DEVICE_TYPE_DESC, "Dummy",
+	                                  NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_DUMMY,
+	                                  NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_DUMMY,
+	                                  NULL);
+}
+
+NM_DEVICE_FACTORY_DEFINE_INTERNAL (DUMMY, Dummy, dummy,
+	NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_DUMMY)
+	NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_DUMMY_SETTING_NAME),
+	factory_class->create_device = create_device;
+);
diff --git a/src/devices/nm-device-dummy.h b/src/devices/nm-device-dummy.h
new file mode 100644
index 00000000..cc89a847
--- /dev/null
+++ b/src/devices/nm-device-dummy.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2017 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DEVICE_DUMMY_H__
+#define __NETWORKMANAGER_DEVICE_DUMMY_H__
+
+#include "nm-device-generic.h"
+
+#define NM_TYPE_DEVICE_DUMMY            (nm_device_dummy_get_type ())
+#define NM_DEVICE_DUMMY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_DUMMY, NMDeviceDummy))
+#define NM_DEVICE_DUMMY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE_DUMMY, NMDeviceDummyClass))
+#define NM_IS_DEVICE_DUMMY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_DUMMY))
+#define NM_IS_DEVICE_DUMMY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_DEVICE_DUMMY))
+#define NM_DEVICE_DUMMY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_DEVICE_DUMMY, NMDeviceDummyClass))
+
+typedef struct _NMDeviceDummy NMDeviceDummy;
+typedef struct _NMDeviceDummyClass NMDeviceDummyClass;
+
+GType nm_device_dummy_get_type (void);
+
+#endif /* __NETWORKMANAGER_DEVICE_DUMMY_H__ */
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index 5df16df0..4c5aeb5e 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -29,7 +29,7 @@
 #include <unistd.h>
 #include <errno.h>
 
-#include <gudev/gudev.h>
+#include <libudev.h>
 
 #include "nm-device-private.h"
 #include "nm-act-request.h"
@@ -59,8 +59,6 @@ _LOG_DECLARE_SELF(NMDeviceEthernet);
 
 /*****************************************************************************/
 
-#define WIRED_SECRETS_TRIES "wired-secrets-tries"
-
 #define PPPOE_RECONNECT_DELAY 7
 #define PPPOE_ENCAP_OVERHEAD  8 /* 2 bytes for PPP, 6 for PPPoE */
 
@@ -71,7 +69,6 @@ typedef struct Supplicant {
 	NMSupplicantInterface *iface;
 
 	/* signal handler ids */
-	gulong iface_error_id;
 	gulong iface_state_id;
 
 	/* Timeouts and idles */
@@ -158,8 +155,8 @@ static void
 _update_s390_subchannels (NMDeviceEthernet *self)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-	gs_unref_object GUdevDevice *dev = NULL;
-	gs_unref_object GUdevDevice *parent = NULL;
+	struct udev_device *dev = NULL;
+	struct udev_device *parent = NULL;
 	const char *parent_path, *item;
 	int ifindex;
 	GDir *dir;
@@ -175,21 +172,20 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 	}
 
 	ifindex = nm_device_get_ifindex ((NMDevice *) self);
-	dev = (GUdevDevice *) nm_g_object_ref (nm_platform_link_get_udev_device (NM_PLATFORM_GET, ifindex));
+	dev = nm_platform_link_get_udev_device (nm_device_get_platform (NM_DEVICE (self)), ifindex);
 	if (!dev)
 		return;
 
 	/* Try for the "ccwgroup" parent */
-	parent = g_udev_device_get_parent_with_subsystem (dev, "ccwgroup", NULL);
+	parent = udev_device_get_parent_with_subsystem_devtype (dev, "ccwgroup", NULL);
 	if (!parent) {
 		/* FIXME: whatever 'lcs' devices' subsystem is here... */
-		if (!parent) {
-			/* Not an s390 device */
-			return;
-		}
+
+		/* Not an s390 device */
+		return;
 	}
 
-	parent_path = g_udev_device_get_sysfs_path (parent);
+	parent_path = udev_device_get_syspath (parent);
 	dir = g_dir_open (parent_path, 0, &error);
 	if (!dir) {
 		_LOGW (LOGD_DEVICE | LOGD_PLATFORM, "update-s390: failed to open directory '%s': %s",
@@ -213,7 +209,7 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 			gs_free char *path = NULL, *value = NULL;
 
 			path = g_strdup_printf ("%s/%s", parent_path, item);
-			value = nm_platform_sysctl_get (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (path));
+			value = nm_platform_sysctl_get (nm_device_get_platform (NM_DEVICE (self)), NMP_SYSCTL_PATHID_ABSOLUTE (path));
 
 			if (   !strcmp (item, "portname")
 			    && !g_strcmp0 (value, "no portname required")) {
@@ -259,16 +255,18 @@ _update_s390_subchannels (NMDeviceEthernet *self)
 }
 
 static void
-clear_secrets_tries (NMDevice *device)
+reset_8021x_autoconnect_retries (NMDevice *device)
 {
 	NMActRequest *req;
-	NMConnection *connection;
+	NMSettingsConnection *connection;
 
 	req = nm_device_get_act_request (device);
-	if (req) {
-		connection = nm_act_request_get_applied_connection (req);
-		/* Clear wired secrets tries on success, failure, or when deactivating */
-		g_object_set_data (G_OBJECT (connection), WIRED_SECRETS_TRIES, NULL);
+	if (   req
+	    && nm_device_get_applied_setting (device, NM_TYPE_SETTING_802_1X)) {
+		connection = nm_act_request_get_settings_connection (req);
+		g_return_if_fail (connection);
+		/* Reset autoconnect retries on success, failure, or when deactivating */
+		nm_settings_connection_reset_autoconnect_retries (connection);
 	}
 }
 
@@ -281,10 +279,11 @@ device_state_changed (NMDevice *device,
 	if (new_state > NM_DEVICE_STATE_ACTIVATED)
 		wired_secrets_cancel (NM_DEVICE_ETHERNET (device));
 
-	if (   new_state == NM_DEVICE_STATE_ACTIVATED
-	    || new_state == NM_DEVICE_STATE_FAILED
-	    || new_state == NM_DEVICE_STATE_DISCONNECTED)
-		clear_secrets_tries (device);
+	if (NM_IN_SET (new_state,
+	               NM_DEVICE_STATE_ACTIVATED,
+	               NM_DEVICE_STATE_FAILED,
+	               NM_DEVICE_STATE_DISCONNECTED))
+		reset_8021x_autoconnect_retries (device);
 }
 
 static void
@@ -305,7 +304,7 @@ get_generic_capabilities (NMDevice *device)
 	int ifindex = nm_device_get_ifindex (device);
 
 	if (ifindex > 0) {
-		if (nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, ifindex))
+		if (nm_platform_link_supports_carrier_detect (nm_device_get_platform (device), ifindex))
 			return NM_DEVICE_CAP_CARRIER_DETECT;
 		else {
 			_LOGI (LOGD_PLATFORM, "driver '%s' does not support carrier detection.",
@@ -423,22 +422,12 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 /* 802.1X */
 
 static void
-supplicant_interface_clear_handlers (NMDeviceEthernet *self)
+supplicant_interface_release (NMDeviceEthernet *self)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 
 	nm_clear_g_source (&priv->supplicant_timeout_id);
 	nm_clear_g_source (&priv->supplicant.con_timeout_id);
-	nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.iface_error_id);
-}
-
-static void
-supplicant_interface_release (NMDeviceEthernet *self)
-{
-	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-
-	supplicant_interface_clear_handlers (self);
-
 	nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.iface_state_id);
 
 	if (priv->supplicant.iface) {
@@ -580,7 +569,7 @@ build_supplicant_config (NMDeviceEthernet *self,
 	connection = nm_device_get_applied_connection (NM_DEVICE (self));
 	g_assert (connection);
 	con_uuid = nm_connection_get_uuid (connection);
-	mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET,
+	mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)),
 	                                nm_device_get_ifindex (NM_DEVICE (self)));
 
 	config = nm_supplicant_config_new ();
@@ -595,9 +584,24 @@ build_supplicant_config (NMDeviceEthernet *self,
 }
 
 static void
+supplicant_iface_assoc_cb (NMSupplicantInterface *iface,
+                           GError *error,
+                           gpointer user_data)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
+
+	if (error && !nm_utils_error_is_cancelled (error, TRUE)) {
+		supplicant_interface_release (self);
+		nm_device_queue_state (NM_DEVICE (self),
+		                       NM_DEVICE_STATE_FAILED,
+		                       NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
+	}
+}
+
+static void
 supplicant_iface_state_cb (NMSupplicantInterface *iface,
-                           guint32 new_state,
-                           guint32 old_state,
+                           int new_state_i,
+                           int old_state_i,
                            int disconnect_reason,
                            gpointer user_data)
 {
@@ -605,9 +609,10 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
 	NMSupplicantConfig *config;
-	gboolean success = FALSE;
 	NMDeviceState devstate;
 	GError *error = NULL;
+	NMSupplicantInterfaceState new_state = new_state_i;
+	NMSupplicantInterfaceState old_state = old_state_i;
 
 	if (new_state == old_state)
 		return;
@@ -622,30 +627,23 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	case NM_SUPPLICANT_INTERFACE_STATE_READY:
 		config = build_supplicant_config (self, &error);
 		if (config) {
-			success = nm_supplicant_interface_set_config (priv->supplicant.iface, config, &error);
+			nm_supplicant_interface_assoc (priv->supplicant.iface, config,
+			                               supplicant_iface_assoc_cb, self);
 			g_object_unref (config);
-
-			if (!success) {
-				_LOGE (LOGD_DEVICE | LOGD_ETHER,
-				       "Activation: (ethernet) couldn't send security configuration to the supplicant: %s",
-				       error->message);
-				g_clear_error (&error);
-			}
 		} else {
 			_LOGE (LOGD_DEVICE | LOGD_ETHER,
 			       "Activation: (ethernet) couldn't build security configuration: %s",
 			       error->message);
 			g_clear_error (&error);
-		}
 
-		if (!success) {
 			nm_device_state_changed (device,
 			                         NM_DEVICE_STATE_FAILED,
 			                         NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
 		}
 		break;
 	case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
-		supplicant_interface_clear_handlers (self);
+		nm_clear_g_source (&priv->supplicant_timeout_id);
+		nm_clear_g_source (&priv->supplicant.con_timeout_id);
 
 		/* If this is the initial association during device activation,
 		 * schedule the next activation stage.
@@ -677,39 +675,26 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	}
 }
 
-static void
-supplicant_iface_connection_error_cb (NMSupplicantInterface *iface,
-                                      const char *name,
-                                      const char *message,
-                                      gpointer user_data)
-{
-	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
-
-	_LOGW (LOGD_DEVICE | LOGD_ETHER,
-	       "Activation: (ethernet) association request to the supplicant failed: %s - %s",
-	       name, message);
-
-	supplicant_interface_release (self);
-	nm_device_queue_state (NM_DEVICE (self),
-	                       NM_DEVICE_STATE_FAILED,
-	                       NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
-}
-
 static NMActStageReturn
 handle_auth_or_fail (NMDeviceEthernet *self,
                      NMActRequest *req,
                      gboolean new_secrets)
 {
 	const char *setting_name;
-	guint32 tries;
 	NMConnection *applied_connection;
+	NMSettingsConnection *settings_connection;
+	int tries_left;
 
 	applied_connection = nm_act_request_get_applied_connection (req);
+	settings_connection = nm_act_request_get_settings_connection (req);
 
-	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (applied_connection), WIRED_SECRETS_TRIES));
-	if (tries > 3)
+	tries_left = nm_settings_connection_get_autoconnect_retries (settings_connection);
+	if (tries_left == 0)
 		return NM_ACT_STAGE_RETURN_FAILURE;
 
+	if (tries_left > 0)
+		nm_settings_connection_set_autoconnect_retries (settings_connection, tries_left - 1);
+
 	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
 
 	nm_active_connection_clear_secrets (NM_ACTIVE_CONNECTION (req));
@@ -719,7 +704,6 @@ handle_auth_or_fail (NMDeviceEthernet *self,
 		wired_secrets_get_secrets (self, setting_name,
 		                           NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
 		                             | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0));
-		g_object_set_data (G_OBJECT (applied_connection), WIRED_SECRETS_TRIES, GUINT_TO_POINTER (++tries));
 	} else
 		_LOGI (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
 
@@ -771,6 +755,7 @@ static gboolean
 supplicant_interface_init (NMDeviceEthernet *self)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+	guint timeout;
 
 	supplicant_interface_release (self);
 
@@ -790,14 +775,11 @@ supplicant_interface_init (NMDeviceEthernet *self)
 	                                                    G_CALLBACK (supplicant_iface_state_cb),
 	                                                    self);
 
-	/* Hook up error signal handler to capture association errors */
-	priv->supplicant.iface_error_id = g_signal_connect (priv->supplicant.iface,
-	                                                    NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
-	                                                    G_CALLBACK (supplicant_iface_connection_error_cb),
-	                                                    self);
-
-	/* Set up a timeout on the connection attempt to fail it after 25 seconds */
-	priv->supplicant.con_timeout_id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self);
+	/* Set up a timeout on the connection attempt */
+	timeout = nm_device_get_supplicant_timeout (NM_DEVICE (self));
+	priv->supplicant.con_timeout_id = g_timeout_add_seconds (timeout,
+	                                                         supplicant_connection_timeout_cb,
+	                                                         self);
 
 	return TRUE;
 }
@@ -846,7 +828,7 @@ link_negotiation_set (NMDevice *device)
 		}
 	}
 
-	if (!nm_platform_ethtool_get_link_settings (NM_PLATFORM_GET, nm_device_get_ifindex (device),
+	if (!nm_platform_ethtool_get_link_settings (nm_device_get_platform (device), nm_device_get_ifindex (device),
 	                                            &link_autoneg, &link_speed, &link_duplex)) {
 		_LOGW (LOGD_DEVICE, "set-link: unable to retrieve link negotiation");
 		return;
@@ -870,7 +852,7 @@ link_negotiation_set (NMDevice *device)
 		       duplex ? "" : "*");
 	}
 
-	if (!nm_platform_ethtool_set_link_settings (NM_PLATFORM_GET,
+	if (!nm_platform_ethtool_set_link_settings (nm_device_get_platform (device),
 	                                            nm_device_get_ifindex (device),
 	                                            autoneg,
 	                                            speed,
@@ -893,15 +875,13 @@ pppoe_reconnect_delay (gpointer user_data)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMActStageReturn ret;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	ret = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage1_prepare (dev, reason);
+	ret = NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage1_prepare (dev, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
@@ -935,7 +915,7 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 }
 
 static NMActStageReturn
-nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason)
+nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMConnection *connection;
@@ -944,11 +924,12 @@ nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason)
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 
 	connection = nm_device_get_applied_connection (NM_DEVICE (self));
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+
 	security = nm_connection_get_setting_802_1x (connection);
 	if (!security) {
 		_LOGE (LOGD_DEVICE, "Invalid or missing 802.1X security");
-		*reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 		return ret;
 	}
 
@@ -966,7 +947,7 @@ nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason)
 
 		ret = handle_auth_or_fail (self, req, FALSE);
 		if (ret != NM_ACT_STAGE_RETURN_POSTPONE)
-			*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
 	} else {
 		_LOGI (LOGD_DEVICE | LOGD_ETHER,
 		       "Activation: (ethernet) connection '%s' requires no security. No secrets needed.",
@@ -975,7 +956,7 @@ nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason)
 		if (supplicant_interface_init (self))
 			ret = NM_ACT_STAGE_RETURN_POSTPONE;
 		else
-			*reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 	}
 
 	return ret;
@@ -1017,43 +998,42 @@ ppp_ip4_config (NMPPPManager *ppp_manager,
 }
 
 static NMActStageReturn
-pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *reason)
+pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	NMSettingPppoe *s_pppoe;
 	NMActRequest *req;
 	GError *err = NULL;
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 
 	req = nm_device_get_act_request (NM_DEVICE (self));
-	g_assert (req);
+	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
 
 	s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting ((NMDevice *) self, NM_TYPE_SETTING_PPPOE);
-	g_assert (s_pppoe);
+	g_return_val_if_fail (s_pppoe, NM_ACT_STAGE_RETURN_FAILURE);
 
 	priv->ppp_manager = nm_ppp_manager_create (nm_device_get_iface (NM_DEVICE (self)),
 	                                           &err);
-	if (   priv->ppp_manager
-	    && nm_ppp_manager_start (priv->ppp_manager, req,
-	                             nm_setting_pppoe_get_username (s_pppoe),
-	                             30, 0, &err)) {
-		g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
-		                  G_CALLBACK (ppp_state_changed),
-		                  self);
-		g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
-		                  G_CALLBACK (ppp_ip4_config),
-		                  self);
-		ret = NM_ACT_STAGE_RETURN_POSTPONE;
-	} else {
+
+	if (   !priv->ppp_manager
+	    || !nm_ppp_manager_start (priv->ppp_manager, req,
+	                              nm_setting_pppoe_get_username (s_pppoe),
+	                              30, 0, &err)) {
 		_LOGW (LOGD_DEVICE, "PPPoE failed to start: %s", err->message);
 		g_error_free (err);
 
 		g_clear_object (&priv->ppp_manager);
 
-		*reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED);
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	return ret;
+	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
+	                  G_CALLBACK (ppp_state_changed),
+	                  self);
+	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
+	                  G_CALLBACK (ppp_ip4_config),
+	                  self);
+	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
 /*****************************************************************************/
@@ -1144,7 +1124,7 @@ dcb_state (NMDevice *device, gboolean timeout)
 	g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG);
 
 
-	carrier = nm_platform_link_is_connected (NM_PLATFORM_GET, nm_device_get_ifindex (device));
+	carrier = nm_platform_link_is_connected (nm_device_get_platform (device), nm_device_get_ifindex (device));
 	_LOGD (LOGD_DCB, "dcb_state() wait %d carrier %d timeout %d", priv->dcb_wait, carrier, timeout);
 
 	switch (priv->dcb_wait) {
@@ -1262,13 +1242,13 @@ wake_on_lan_enable (NMDevice *device)
 	}
 	wol = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE;
 found:
-	return nm_platform_ethtool_set_wake_on_lan (NM_PLATFORM_GET, nm_device_get_ifindex (device), wol, password);
+	return nm_platform_ethtool_set_wake_on_lan (nm_device_get_platform (device), nm_device_get_ifindex (device), wol, password);
 }
 
 /*****************************************************************************/
 
 static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceEthernet *self = (NMDeviceEthernet *) device;
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
@@ -1277,11 +1257,9 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
 	NMSettingDcb *s_dcb;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
 	s_con = NM_SETTING_CONNECTION (nm_device_get_applied_setting (device,
 	                                                              NM_TYPE_SETTING_CONNECTION));
-	g_assert (s_con);
+	g_return_val_if_fail (s_con, NM_ACT_STAGE_RETURN_FAILURE);
 
 	nm_clear_g_source (&priv->dcb_timeout_id);
 	nm_clear_g_signal_handler (device, &priv->dcb_carrier_id);
@@ -1297,7 +1275,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 		                                                             NM_TYPE_SETTING_802_1X);
 		if (security) {
 			/* FIXME: for now 802.1x is mutually exclusive with DCB */
-			return nm_8021x_stage2_config (self, reason);
+			return nm_8021x_stage2_config (self, out_failure_reason);
 		}
 	}
 
@@ -1307,9 +1285,9 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	s_dcb = (NMSettingDcb *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB);
 	if (s_dcb) {
 		/* lldpad really really wants the carrier to be up */
-		if (nm_platform_link_is_connected (NM_PLATFORM_GET, nm_device_get_ifindex (device))) {
+		if (nm_platform_link_is_connected (nm_device_get_platform (device), nm_device_get_ifindex (device))) {
 			if (!dcb_enable (device)) {
-				*reason = NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED;
+				NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED);
 				return NM_ACT_STAGE_RETURN_FAILURE;
 			}
 		} else {
@@ -1343,7 +1321,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 			if (mxu) {
 				_LOGD (LOGD_PPP, "set MTU to %u (PPP interface MRU %u, MTU %u)",
 				       mxu + PPPOE_ENCAP_OVERHEAD, mru, mtu);
-				nm_platform_link_set_mtu (NM_PLATFORM_GET,
+				nm_platform_link_set_mtu (nm_device_get_platform (device),
 				                          nm_device_get_ifindex (device),
 				                          mxu + PPPOE_ENCAP_OVERHEAD);
 			}
@@ -1356,21 +1334,19 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 static NMActStageReturn
 act_stage3_ip4_config_start (NMDevice *device,
                              NMIP4Config **out_config,
-                             NMDeviceStateReason *reason)
+                             NMDeviceStateReason *out_failure_reason)
 {
 	NMSettingConnection *s_con;
 	const char *connection_type;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
 	s_con = NM_SETTING_CONNECTION (nm_device_get_applied_setting (device, NM_TYPE_SETTING_CONNECTION));
-	g_assert (s_con);
+	g_return_val_if_fail (s_con, NM_ACT_STAGE_RETURN_FAILURE);
 
 	connection_type = nm_setting_connection_get_connection_type (s_con);
 	if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME))
-		return pppoe_stage3_ip4_config_start (NM_DEVICE_ETHERNET (device), reason);
+		return pppoe_stage3_ip4_config_start (NM_DEVICE_ETHERNET (device), out_failure_reason);
 
-	return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage3_ip4_config_start (device, out_config, reason);
+	return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage3_ip4_config_start (device, out_config, out_failure_reason);
 }
 
 static guint32
@@ -1392,7 +1368,7 @@ deactivate (NMDevice *device)
 	GError *error = NULL;
 
 	/* Clear wired secrets tries when deactivating */
-	clear_secrets_tries (device);
+	reset_8021x_autoconnect_retries (device);
 
 	nm_clear_g_source (&priv->pppoe_wait_id);
 
@@ -1451,7 +1427,7 @@ complete_connection (NMDevice *device,
 	/* Default to an ethernet-only connection, but if a PPPoE setting was given
 	 * then PPPoE should be our connection type.
 	 */
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           s_pppoe ? NM_SETTING_PPPOE_SETTING_NAME : NM_SETTING_WIRED_SETTING_NAME,
 	                           existing_connections,
@@ -1609,7 +1585,7 @@ get_link_speed (NMDevice *device)
 	NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
 	guint32 speed;
 
-	if (!nm_platform_ethtool_get_link_settings (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL, &speed, NULL))
+	if (!nm_platform_ethtool_get_link_settings (nm_device_get_platform (device), nm_device_get_ifindex (device), NULL, &speed, NULL))
 		return;
 	if (priv->speed == speed)
 		return;
@@ -1647,6 +1623,53 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 	return !!nm_device_get_initial_hw_address (device);
 }
 
+static gboolean
+can_reapply_change (NMDevice *device,
+                    const char *setting_name,
+                    NMSetting *s_old,
+                    NMSetting *s_new,
+                    GHashTable *diffs,
+                    GError **error)
+{
+	NMDeviceClass *device_class;
+
+	/* Only handle wired setting here, delegate other settings to parent class */
+	if (nm_streq (setting_name, NM_SETTING_WIRED_SETTING_NAME)) {
+		return nm_device_hash_check_invalid_keys (diffs,
+		                                          NM_SETTING_WIRED_SETTING_NAME,
+		                                          error,
+		                                          NM_SETTING_WIRED_MTU, /* reapplied with IP config */
+		                                          NM_SETTING_WIRED_SPEED,
+		                                          NM_SETTING_WIRED_DUPLEX,
+		                                          NM_SETTING_WIRED_AUTO_NEGOTIATE,
+		                                          NM_SETTING_WIRED_WAKE_ON_LAN,
+		                                          NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD);
+	}
+
+	device_class = NM_DEVICE_CLASS (nm_device_ethernet_parent_class);
+	return device_class->can_reapply_change (device,
+	                                         setting_name,
+	                                         s_old,
+	                                         s_new,
+	                                         diffs,
+	                                         error);
+}
+
+static void
+reapply_connection (NMDevice *device, NMConnection *con_old, NMConnection *con_new)
+{
+	NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+
+	NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->reapply_connection (device,
+	                                                                       con_old,
+	                                                                       con_new);
+
+	_LOGD (LOGD_DEVICE, "reapplying wired settings");
+
+	link_negotiation_set (device);
+	wake_on_lan_enable (device);
+}
+
 static void
 dispose (GObject *object)
 {
@@ -1744,6 +1767,8 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
 	parent_class->carrier_changed = carrier_changed;
 	parent_class->link_changed = link_changed;
 	parent_class->is_available = is_available;
+	parent_class->can_reapply_change = can_reapply_change;
+	parent_class->reapply_connection = reapply_connection;
 
 	parent_class->state_changed = device_state_changed;
 
diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c
index 0ce2cb48..f512b8b2 100644
--- a/src/devices/nm-device-factory.c
+++ b/src/devices/nm-device-factory.c
@@ -32,7 +32,8 @@
 #include "nm-utils.h"
 
 #define PLUGIN_PREFIX "libnm-device-plugin-"
-#define PLUGIN_PATH_TAG "NMManager-plugin-path"
+
+static NM_CACHED_QUARK_FCN ("NMManager-plugin-path", plugin_path_quark)
 
 /*****************************************************************************/
 
@@ -350,13 +351,13 @@ _add_factory (NMDeviceFactory *factory,
 		if (found) {
 			nm_log_warn (LOGD_PLATFORM, "Loading device plugin failed: multiple plugins "
 			             "for same type (using '%s' instead of '%s')",
-			             (char *) g_object_get_data (G_OBJECT (found), PLUGIN_PATH_TAG),
+			             (char *) g_object_get_qdata (G_OBJECT (found), plugin_path_quark ()),
 			             path);
 			return FALSE;
 		}
 	}
 
-	g_object_set_data_full (G_OBJECT (factory), PLUGIN_PATH_TAG, g_strdup (path), g_free);
+	g_object_set_qdata_full (G_OBJECT (factory), plugin_path_quark (), g_strdup (path), g_free);
 	for (i = 0; link_types && link_types[i] > NM_LINK_TYPE_UNKNOWN; i++)
 		g_hash_table_insert (factories_by_link, GUINT_TO_POINTER (link_types[i]), g_object_ref (factory));
 	for (i = 0; setting_types && setting_types[i]; i++)
@@ -402,6 +403,7 @@ nm_device_factory_manager_load_factories (NMDeviceFactoryManagerFactoryFunc call
 
 	_ADD_INTERNAL (nm_bond_device_factory_get_type);
 	_ADD_INTERNAL (nm_bridge_device_factory_get_type);
+	_ADD_INTERNAL (nm_dummy_device_factory_get_type);
 	_ADD_INTERNAL (nm_ethernet_device_factory_get_type);
 	_ADD_INTERNAL (nm_infiniband_device_factory_get_type);
 	_ADD_INTERNAL (nm_ip_tunnel_device_factory_get_type);
diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c
index 2f51c69d..f6a670b2 100644
--- a/src/devices/nm-device-generic.c
+++ b/src/devices/nm-device-generic.c
@@ -54,11 +54,11 @@ G_DEFINE_TYPE (NMDeviceGeneric, nm_device_generic, NM_TYPE_DEVICE)
 /*****************************************************************************/
 
 static NMDeviceCapabilities
-get_generic_capabilities (NMDevice *dev)
+get_generic_capabilities (NMDevice *device)
 {
-	int ifindex = nm_device_get_ifindex (dev);
+	int ifindex = nm_device_get_ifindex (device);
 
-	if (ifindex > 0 && nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, ifindex))
+	if (ifindex > 0 && nm_platform_link_supports_carrier_detect (nm_device_get_platform (device), ifindex))
 		return NM_DEVICE_CAP_CARRIER_DETECT;
 	else
 		return NM_DEVICE_CAP_NONE;
@@ -84,7 +84,7 @@ realize_start_notify (NMDevice *device, const NMPlatformLink *plink)
 	g_clear_pointer (&priv->type_description, g_free);
 	ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self));
 	if (ifindex > 0)
-		priv->type_description = g_strdup (nm_platform_link_get_type_name (NM_PLATFORM_GET, ifindex));
+		priv->type_description = g_strdup (nm_platform_link_get_type_name (nm_device_get_platform (device), ifindex));
 }
 
 static gboolean
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index 60f1aefa..f7875d09 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -75,7 +75,7 @@ get_generic_capabilities (NMDevice *device)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	nm_auto_close int dirfd = -1;
 	NMActStageReturn ret;
@@ -84,34 +84,32 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
 	const char *transport_mode;
 	gboolean ok, no_firmware = FALSE;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	ret = NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->act_stage1_prepare (dev, reason);
+	ret = NM_DEVICE_CLASS (nm_device_infiniband_parent_class)->act_stage1_prepare (device, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
-	s_infiniband = (NMSettingInfiniband *) nm_device_get_applied_setting (dev, NM_TYPE_SETTING_INFINIBAND);
-	g_assert (s_infiniband);
+	s_infiniband = (NMSettingInfiniband *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_INFINIBAND);
+	g_return_val_if_fail (s_infiniband, NM_ACT_STAGE_RETURN_FAILURE);
 
 	transport_mode = nm_setting_infiniband_get_transport_mode (s_infiniband);
 
-	dirfd = nm_platform_sysctl_open_netdir (NM_PLATFORM_GET, nm_device_get_ifindex (dev), ifname_verified);
+	dirfd = nm_platform_sysctl_open_netdir (nm_device_get_platform (device), nm_device_get_ifindex (device), ifname_verified);
 	if (dirfd < 0) {
 		if (!strcmp (transport_mode, "datagram"))
 			return NM_ACT_STAGE_RETURN_SUCCESS;
 		else {
-			*reason = NM_DEVICE_STATE_REASON_INFINIBAND_MODE;
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_INFINIBAND_MODE);
 			return NM_ACT_STAGE_RETURN_FAILURE;
 		}
 	}
 
 	/* With some drivers the interface must be down to set transport mode */
-	nm_device_take_down (dev, TRUE);
-	ok = nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "mode"), transport_mode);
-	nm_device_bring_up (dev, TRUE, &no_firmware);
+	nm_device_take_down (device, TRUE);
+	ok = nm_platform_sysctl_set (nm_device_get_platform (device), NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "mode"), transport_mode);
+	nm_device_bring_up (device, TRUE, &no_firmware);
 
 	if (!ok) {
-		*reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
@@ -186,7 +184,7 @@ complete_connection (NMDevice *device,
 	const char *setting_mac;
 	const char *hw_address;
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_INFINIBAND_SETTING_NAME,
 	                           existing_connections,
@@ -242,7 +240,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 
 	ifindex = nm_device_get_ifindex (device);
 	if (ifindex > 0) {
-		if (!nm_platform_link_infiniband_get_properties (NM_PLATFORM_GET, ifindex, NULL, NULL, &transport_mode))
+		if (!nm_platform_link_infiniband_get_properties (nm_device_get_platform (device), ifindex, NULL, NULL, &transport_mode))
 			transport_mode = "datagram";
 	}
 	g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_TRANSPORT_MODE, transport_mode, NULL);
@@ -291,7 +289,7 @@ create_and_realize (NMDevice *device,
 		return FALSE;
 	}
 
-	plerr = nm_platform_link_infiniband_add (NM_PLATFORM_GET, priv->parent_ifindex, priv->p_key, out_plink);
+	plerr = nm_platform_link_infiniband_add (nm_device_get_platform (device), priv->parent_ifindex, priv->p_key, out_plink);
 	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 		             "Failed to create InfiniBand P_Key interface '%s' for '%s': %s",
@@ -321,7 +319,7 @@ unrealize (NMDevice *device, GError **error)
 		return FALSE;
 	}
 
-	plerr = nm_platform_link_infiniband_delete (NM_PLATFORM_GET, priv->parent_ifindex, priv->p_key);
+	plerr = nm_platform_link_infiniband_delete (nm_device_get_platform (device), priv->parent_ifindex, priv->p_key);
 	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 		             "Failed to remove InfiniBand P_Key interface '%s': %s",
diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c
index 58544377..53b7cf4e 100644
--- a/src/devices/nm-device-ip-tunnel.c
+++ b/src/devices/nm-device-ip-tunnel.c
@@ -124,8 +124,8 @@ update_properties_from_ifindex (NMDevice *device, int ifindex)
 	NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (device);
 	NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (self);
 	int parent_ifindex = 0;
-	in_addr_t local4, remote4;
-	struct in6_addr local6, remote6;
+	in_addr_t local4 = 0, remote4 = 0;
+	struct in6_addr local6 = { 0 }, remote6 = { 0 };
 	guint8 ttl = 0, tos = 0, encap_limit = 0;
 	gboolean pmtud = FALSE;
 	guint32 flow_label = 0;
@@ -157,7 +157,7 @@ clear:
 	if (priv->mode == NM_IP_TUNNEL_MODE_GRE) {
 		const NMPlatformLnkGre *lnk;
 
-		lnk = nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL);
+		lnk = nm_platform_link_get_lnk_gre (nm_device_get_platform (device), ifindex, NULL);
 		if (!lnk) {
 			_LOGW (LOGD_PLATFORM, "could not read %s properties", "gre");
 			goto clear;
@@ -202,7 +202,7 @@ clear:
 	} else if (priv->mode == NM_IP_TUNNEL_MODE_SIT) {
 		const NMPlatformLnkSit *lnk;
 
-		lnk = nm_platform_link_get_lnk_sit (NM_PLATFORM_GET, ifindex, NULL);
+		lnk = nm_platform_link_get_lnk_sit (nm_device_get_platform (device), ifindex, NULL);
 		if (!lnk) {
 			_LOGW (LOGD_PLATFORM, "could not read %s properties", "sit");
 			goto clear;
@@ -217,7 +217,7 @@ clear:
 	} else if (priv->mode == NM_IP_TUNNEL_MODE_IPIP) {
 		const NMPlatformLnkIpIp *lnk;
 
-		lnk = nm_platform_link_get_lnk_ipip (NM_PLATFORM_GET, ifindex, NULL);
+		lnk = nm_platform_link_get_lnk_ipip (nm_device_get_platform (device), ifindex, NULL);
 		if (!lnk) {
 			_LOGW (LOGD_PLATFORM, "could not read %s properties", "ipip");
 			goto clear;
@@ -233,7 +233,7 @@ clear:
 	           || priv->mode == NM_IP_TUNNEL_MODE_IP6IP6) {
 		const NMPlatformLnkIp6Tnl *lnk;
 
-		lnk = nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, ifindex, NULL);
+		lnk = nm_platform_link_get_lnk_ip6tnl (nm_device_get_platform (device), ifindex, NULL);
 		if (!lnk) {
 			_LOGW (LOGD_PLATFORM, "could not read %s properties", "ip6tnl");
 			goto clear;
@@ -332,7 +332,7 @@ complete_connection (NMDevice *device,
 {
 	NMSettingIPTunnel *s_ip_tunnel;
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_IP_TUNNEL_SETTING_NAME,
 	                           existing_connections,
@@ -641,7 +641,7 @@ create_and_realize (NMDevice *device,
 			lnk_gre.output_flags = NM_GRE_KEY;
 		}
 
-		plerr = nm_platform_link_gre_add (NM_PLATFORM_GET, iface, &lnk_gre, out_plink);
+		plerr = nm_platform_link_gre_add (nm_device_get_platform (device), iface, &lnk_gre, out_plink);
 		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 			             "Failed to create GRE interface '%s' for '%s': %s",
@@ -667,7 +667,7 @@ create_and_realize (NMDevice *device,
 		lnk_sit.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
 		lnk_sit.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel);
 
-		plerr = nm_platform_link_sit_add (NM_PLATFORM_GET, iface, &lnk_sit, out_plink);
+		plerr = nm_platform_link_sit_add (nm_device_get_platform (device), iface, &lnk_sit, out_plink);
 		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 					"Failed to create SIT interface '%s' for '%s': %s",
@@ -693,7 +693,7 @@ create_and_realize (NMDevice *device,
 		lnk_ipip.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
 		lnk_ipip.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel);
 
-		plerr = nm_platform_link_ipip_add (NM_PLATFORM_GET, iface, &lnk_ipip, out_plink);
+		plerr = nm_platform_link_ipip_add (nm_device_get_platform (device), iface, &lnk_ipip, out_plink);
 		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 					"Failed to create IPIP interface '%s' for '%s': %s",
@@ -722,7 +722,7 @@ create_and_realize (NMDevice *device,
 		lnk_ip6tnl.flow_label = nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel);
 		lnk_ip6tnl.proto = nm_setting_ip_tunnel_get_mode (s_ip_tunnel) == NM_IP_TUNNEL_MODE_IPIP6 ? IPPROTO_IPIP : IPPROTO_IPV6;
 
-		plerr = nm_platform_link_ip6tnl_add (NM_PLATFORM_GET, iface, &lnk_ip6tnl, out_plink);
+		plerr = nm_platform_link_ip6tnl_add (nm_device_get_platform (device), iface, &lnk_ip6tnl, out_plink);
 		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 			g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 			             "Failed to create IPIP interface '%s' for '%s': %s",
@@ -784,6 +784,33 @@ unrealize_notify (NMDevice *device)
 	update_properties_from_ifindex (device, 0);
 }
 
+static gboolean
+can_reapply_change (NMDevice *device,
+                    const char *setting_name,
+                    NMSetting *s_old,
+                    NMSetting *s_new,
+                    GHashTable *diffs,
+                    GError **error)
+{
+	NMDeviceClass *device_class;
+
+	/* Only handle ip-tunnel setting here, delegate other settings to parent class */
+	if (nm_streq (setting_name, NM_SETTING_IP_TUNNEL_SETTING_NAME)) {
+		return nm_device_hash_check_invalid_keys (diffs,
+		                                          NM_SETTING_IP_TUNNEL_SETTING_NAME,
+		                                          error,
+		                                          NM_SETTING_IP_TUNNEL_MTU); /* reapplied with IP config */
+	}
+
+	device_class = NM_DEVICE_CLASS (nm_device_ip_tunnel_parent_class);
+	return device_class->can_reapply_change (device,
+	                                         setting_name,
+	                                         s_old,
+	                                         s_new,
+	                                         diffs,
+	                                         error);
+}
+
 /*****************************************************************************/
 
 static void
@@ -866,16 +893,32 @@ constructed (GObject *object)
 }
 
 static void
+dispose (GObject *object)
+{
+	NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (object);
+	NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (self);
+
+	g_clear_pointer (&priv->local, g_free);
+	g_clear_pointer (&priv->remote, g_free);
+	g_clear_pointer (&priv->input_key, g_free);
+	g_clear_pointer (&priv->output_key, g_free);
+
+	G_OBJECT_CLASS (nm_device_ip_tunnel_parent_class)->dispose (object);
+}
+
+static void
 nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMDeviceClass *device_class = NM_DEVICE_CLASS (klass);
 
 	object_class->constructed = constructed;
+	object_class->dispose = dispose;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 
 	device_class->link_changed = link_changed;
+	device_class->can_reapply_change = can_reapply_change;
 	device_class->complete_connection = complete_connection;
 	device_class->update_connection = update_connection;
 	device_class->check_connection_compatible = check_connection_compatible;
diff --git a/src/devices/nm-device-logging.h b/src/devices/nm-device-logging.h
index b7ec58fd..419a4a51 100644
--- a/src/devices/nm-device-logging.h
+++ b/src/devices/nm-device-logging.h
@@ -24,7 +24,8 @@
 #include "nm-device.h"
 
 #define _LOG_DECLARE_SELF(t) \
-_nm_unused inline static NMDevice * \
+_nm_unused \
+static inline NMDevice * \
 _nm_device_log_self_to_device (t *self) \
 { \
     return (NMDevice *) self; \
@@ -33,7 +34,9 @@ _nm_device_log_self_to_device (t *self) \
 #undef  _NMLOG_ENABLED
 #define _NMLOG_ENABLED(level, domain) ( nm_logging_enabled ((level), (domain)) )
 #define _NMLOG(level, domain, ...) \
-    nm_log_obj ((level), (domain), (self), "device", \
+    nm_log_obj ((level), (domain), \
+                (self) ? nm_device_get_iface (_nm_device_log_self_to_device (self)) : NULL, \
+                NULL, (self), "device", \
                 "(%s): " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                 (self) ? (nm_device_get_iface (_nm_device_log_self_to_device (self)) ?: "(null)") : "(none)" \
                 _NM_UTILS_MACRO_REST(__VA_ARGS__))
diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c
index c511a0e7..8add3f6f 100644
--- a/src/devices/nm-device-macsec.c
+++ b/src/devices/nm-device-macsec.c
@@ -45,7 +45,6 @@ typedef struct Supplicant {
 	NMSupplicantInterface *iface;
 
 	/* signal handler ids */
-	gulong iface_error_id;
 	gulong iface_state_id;
 
 	/* Timeouts and idles */
@@ -90,8 +89,6 @@ G_DEFINE_TYPE (NMDeviceMacsec, nm_device_macsec, NM_TYPE_DEVICE)
 
 /******************************************************************/
 
-#define MACSEC_SECRETS_TRIES "macsec-secrets-tries"
-
 static void macsec_secrets_cancel (NMDeviceMacsec *self);
 
 /******************************************************************/
@@ -113,7 +110,7 @@ parent_state_changed (NMDevice *parent,
 	NMDeviceMacsec *self = NM_DEVICE_MACSEC (user_data);
 
 	/* We'll react to our own carrier state notifications. Ignore the parent's. */
-	if (reason == NM_DEVICE_STATE_REASON_CARRIER)
+	if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_CARRIER)
 		return;
 
 	nm_device_set_unmanaged_by_flags (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent, FALSE), reason);
@@ -176,7 +173,7 @@ update_properties (NMDevice *device)
 
 	ifindex = nm_device_get_ifindex (device);
 	g_return_if_fail (ifindex > 0);
-	props = nm_platform_link_get_lnk_macsec (NM_PLATFORM_GET, ifindex, &plink);
+	props = nm_platform_link_get_lnk_macsec (nm_device_get_platform (device), ifindex, &plink);
 
 	if (!props) {
 		_LOGW (LOGD_PLATFORM, "could not get macsec properties");
@@ -222,7 +219,7 @@ build_supplicant_config (NMDeviceMacsec *self, GError **error)
 	connection = nm_device_get_applied_connection (NM_DEVICE (self));
 	g_assert (connection);
 	con_uuid = nm_connection_get_uuid (connection);
-	mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET,
+	mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)),
 	                                nm_device_get_ifindex (NM_DEVICE (self)));
 
 	config = nm_supplicant_config_new ();
@@ -248,22 +245,12 @@ build_supplicant_config (NMDeviceMacsec *self, GError **error)
 }
 
 static void
-supplicant_interface_clear_handlers (NMDeviceMacsec *self)
+supplicant_interface_release (NMDeviceMacsec *self)
 {
 	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
 
 	nm_clear_g_source (&priv->supplicant_timeout_id);
 	nm_clear_g_source (&priv->supplicant.con_timeout_id);
-	nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.iface_error_id);
-}
-
-static void
-supplicant_interface_release (NMDeviceMacsec *self)
-{
-	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
-
-	supplicant_interface_clear_handlers (self);
-
 	nm_clear_g_signal_handler (priv->supplicant.iface, &priv->supplicant.iface_state_id);
 
 	if (priv->supplicant.iface) {
@@ -273,21 +260,18 @@ supplicant_interface_release (NMDeviceMacsec *self)
 }
 
 static void
-supplicant_iface_connection_error_cb (NMSupplicantInterface *iface,
-                                      const char *name,
-                                      const char *message,
-                                      gpointer user_data)
+supplicant_iface_assoc_cb (NMSupplicantInterface *iface,
+                           GError *error,
+                           gpointer user_data)
 {
 	NMDeviceMacsec *self = NM_DEVICE_MACSEC (user_data);
 
-	_LOGW (LOGD_DEVICE,
-	       "Activation: association request to the supplicant failed: %s - %s",
-	       name, message);
-
-	supplicant_interface_release (self);
-	nm_device_queue_state (NM_DEVICE (self),
-	                       NM_DEVICE_STATE_FAILED,
-	                       NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
+	if (error && !nm_utils_error_is_cancelled (error, TRUE)) {
+		supplicant_interface_release (self);
+		nm_device_queue_state (NM_DEVICE (self),
+		                       NM_DEVICE_STATE_FAILED,
+		                       NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
+	}
 }
 
 static void
@@ -412,8 +396,8 @@ time_out:
 
 static void
 supplicant_iface_state_cb (NMSupplicantInterface *iface,
-                           guint32 new_state,
-                           guint32 old_state,
+                           int new_state_i,
+                           int old_state_i,
                            int disconnect_reason,
                            gpointer user_data)
 {
@@ -421,9 +405,10 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
 	NMDevice *device = NM_DEVICE (self);
 	NMSupplicantConfig *config;
-	gboolean success = FALSE;
 	NMDeviceState devstate;
 	GError *error = NULL;
+	NMSupplicantInterfaceState new_state = new_state_i;
+	NMSupplicantInterfaceState old_state = old_state_i;
 
 	if (new_state == old_state)
 		return;
@@ -438,30 +423,23 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	case NM_SUPPLICANT_INTERFACE_STATE_READY:
 		config = build_supplicant_config (self, &error);
 		if (config) {
-			success = nm_supplicant_interface_set_config (priv->supplicant.iface, config, &error);
+			nm_supplicant_interface_assoc (priv->supplicant.iface, config,
+			                               supplicant_iface_assoc_cb, self);
 			g_object_unref (config);
-
-			if (!success) {
-				_LOGE (LOGD_DEVICE,
-				       "Activation: couldn't send security configuration to the supplicant: %s",
-				       error->message);
-				g_clear_error (&error);
-			}
 		} else {
 			_LOGE (LOGD_DEVICE,
 			       "Activation: couldn't build security configuration: %s",
 			       error->message);
 			g_clear_error (&error);
-		}
 
-		if (!success) {
 			nm_device_state_changed (device,
 			                         NM_DEVICE_STATE_FAILED,
 			                         NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
 		}
 		break;
 	case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
-		supplicant_interface_clear_handlers (self);
+		nm_clear_g_source (&priv->supplicant_timeout_id);
+		nm_clear_g_source (&priv->supplicant.con_timeout_id);
 		nm_device_bring_up (device, TRUE, NULL);
 
 		/* If this is the initial association during device activation,
@@ -500,15 +478,20 @@ handle_auth_or_fail (NMDeviceMacsec *self,
                      gboolean new_secrets)
 {
 	const char *setting_name;
-	guint32 tries;
+	int tries_left;
 	NMConnection *applied_connection;
+	NMSettingsConnection *settings_connection;
 
 	applied_connection = nm_act_request_get_applied_connection (req);
+	settings_connection = nm_act_request_get_settings_connection (req);
 
-	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (applied_connection), MACSEC_SECRETS_TRIES));
-	if (tries > 3)
+	tries_left = nm_settings_connection_get_autoconnect_retries (settings_connection);
+	if (tries_left == 0)
 		return NM_ACT_STAGE_RETURN_FAILURE;
 
+	if (tries_left > 0)
+		nm_settings_connection_set_autoconnect_retries (settings_connection, tries_left - 1);
+
 	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
 
 	nm_active_connection_clear_secrets (NM_ACTIVE_CONNECTION (req));
@@ -518,7 +501,6 @@ handle_auth_or_fail (NMDeviceMacsec *self,
 		macsec_secrets_get_secrets (self, setting_name,
 		                            NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
 		                             | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0));
-		g_object_set_data (G_OBJECT (applied_connection), MACSEC_SECRETS_TRIES, GUINT_TO_POINTER (++tries));
 	} else
 		_LOGI (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
 
@@ -571,6 +553,7 @@ supplicant_interface_init (NMDeviceMacsec *self)
 {
 	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
 	NMDevice *parent;
+	guint timeout;
 
 	parent = nm_device_parent_get_device (NM_DEVICE (self));
 	g_return_val_if_fail (parent, FALSE);
@@ -593,20 +576,16 @@ supplicant_interface_init (NMDeviceMacsec *self)
 	                                                    G_CALLBACK (supplicant_iface_state_cb),
 	                                                    self);
 
-	/* Hook up error signal handler to capture association errors */
-	priv->supplicant.iface_error_id = g_signal_connect (priv->supplicant.iface,
-	                                                    NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
-	                                                    G_CALLBACK (supplicant_iface_connection_error_cb),
-	                                                    self);
-
-	/* Set up a timeout on the connection attempt to fail it after 25 seconds */
-	priv->supplicant.con_timeout_id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self);
-
+	/* Set up a timeout on the connection attempt  */
+	timeout = nm_device_get_supplicant_timeout (NM_DEVICE (self));
+	priv->supplicant.con_timeout_id = g_timeout_add_seconds (timeout,
+	                                                         supplicant_connection_timeout_cb,
+	                                                         self);
 	return TRUE;
 }
 
 static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceMacsec *self = NM_DEVICE_MACSEC (device);
 	NMDeviceMacsecPrivate *priv = NM_DEVICE_MACSEC_GET_PRIVATE (self);
@@ -615,7 +594,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	const char *setting_name;
 
 	connection = nm_device_get_applied_connection (NM_DEVICE (self));
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
 	if (!priv->supplicant.mgr)
 		priv->supplicant.mgr = g_object_ref (nm_supplicant_manager_get ());
@@ -631,7 +610,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 
 		ret = handle_auth_or_fail (self, req, FALSE);
 		if (ret != NM_ACT_STAGE_RETURN_POSTPONE)
-			*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
 	} else {
 		_LOGI (LOGD_DEVICE | LOGD_ETHER,
 		       "Activation: connection '%s' requires no security. No secrets needed.",
@@ -640,7 +619,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 		if (supplicant_interface_init (self))
 			ret = NM_ACT_STAGE_RETURN_POSTPONE;
 		else
-			*reason = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 	}
 
 	return ret;
@@ -735,7 +714,7 @@ create_and_realize (NMDevice *device,
 	parent_ifindex = nm_device_get_ifindex (parent);
 	g_warn_if_fail (parent_ifindex > 0);
 
-	plerr = nm_platform_link_macsec_add (NM_PLATFORM_GET, iface, parent_ifindex, &lnk, out_plink);
+	plerr = nm_platform_link_macsec_add (nm_device_get_platform (device), iface, parent_ifindex, &lnk, out_plink);
 	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 		             "Failed to create macsec interface '%s' for '%s': %s",
@@ -758,17 +737,19 @@ link_changed (NMDevice *device,
 	update_properties (device);
 }
 
+
 static void
-clear_secrets_tries (NMDevice *device)
+reset_autoconnect_retries (NMDevice *device)
 {
 	NMActRequest *req;
-	NMConnection *connection;
+	NMSettingsConnection *connection;
 
 	req = nm_device_get_act_request (device);
 	if (req) {
-		connection = nm_act_request_get_applied_connection (req);
-		/* Clear macsec secrets tries on success, failure, or when deactivating */
-		g_object_set_data (G_OBJECT (connection), MACSEC_SECRETS_TRIES, NULL);
+		connection = nm_act_request_get_settings_connection (req);
+		g_return_if_fail (connection);
+		/* Reset autoconnect retries on success, failure, or when deactivating */
+		nm_settings_connection_reset_autoconnect_retries (connection);
 	}
 }
 
@@ -784,7 +765,7 @@ device_state_changed (NMDevice *device,
 	if (   new_state == NM_DEVICE_STATE_ACTIVATED
 	    || new_state == NM_DEVICE_STATE_FAILED
 	    || new_state == NM_DEVICE_STATE_DISCONNECTED)
-		clear_secrets_tries (device);
+		reset_autoconnect_retries (device);
 }
 
 /******************************************************************/
diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c
index 94206716..cea2b984 100644
--- a/src/devices/nm-device-macvlan.c
+++ b/src/devices/nm-device-macvlan.c
@@ -131,7 +131,7 @@ parent_state_changed (NMDevice *parent,
 	NMDeviceMacvlan *self = NM_DEVICE_MACVLAN (user_data);
 
 	/* We'll react to our own carrier state notifications. Ignore the parent's. */
-	if (reason == NM_DEVICE_STATE_REASON_CARRIER)
+	if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_CARRIER)
 		return;
 
 	nm_device_set_unmanaged_by_flags (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent, FALSE), reason);
@@ -185,9 +185,9 @@ update_properties (NMDevice *device)
 	const NMPlatformLink *plink;
 
 	if (priv->props.tap)
-		props = nm_platform_link_get_lnk_macvtap (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink);
+		props = nm_platform_link_get_lnk_macvtap (nm_device_get_platform (device), nm_device_get_ifindex (device), &plink);
 	else
-		props = nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink);
+		props = nm_platform_link_get_lnk_macvlan (nm_device_get_platform (device), nm_device_get_ifindex (device), &plink);
 
 	if (!props) {
 		_LOGW (LOGD_PLATFORM, "could not get %s properties", priv->props.tap ? "macvtap" : "macvlan");
@@ -251,7 +251,7 @@ create_and_realize (NMDevice *device,
 	lnk.no_promisc = !nm_setting_macvlan_get_promiscuous (s_macvlan);
 	lnk.tap = nm_setting_macvlan_get_tap (s_macvlan);
 
-	plerr = nm_platform_link_macvlan_add (NM_PLATFORM_GET, iface, parent_ifindex, &lnk, out_plink);
+	plerr = nm_platform_link_macvlan_add (nm_device_get_platform (device), iface, parent_ifindex, &lnk, out_plink);
 	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 		             "Failed to create %s interface '%s' for '%s': %s",
@@ -399,7 +399,7 @@ complete_connection (NMDevice *device,
 {
 	NMSettingMacvlan *s_macvlan;
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_MACVLAN_SETTING_NAME,
 	                           existing_connections,
@@ -474,13 +474,11 @@ update_connection (NMDevice *device, NMConnection *connection)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *out_failure_reason)
 {
 	NMActStageReturn ret;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	ret = NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->act_stage1_prepare (dev, reason);
+	ret = NM_DEVICE_CLASS (nm_device_macvlan_parent_class)->act_stage1_prepare (dev, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h
index 2023feb5..a4067f9c 100644
--- a/src/devices/nm-device-private.h
+++ b/src/devices/nm-device-private.h
@@ -134,4 +134,9 @@ guint32 nm_device_get_configured_mtu_for_wired (NMDevice *self, gboolean *out_is
 		NM_DEVICE_CLASS (klass)->link_types = link_types; \
 	}
 
+gboolean _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name,
+                                             GError **error, const char **argv);
+#define nm_device_hash_check_invalid_keys(hash, setting_name, error, ...) \
+	_nm_device_hash_check_invalid_keys (hash, setting_name, error, ((const char *[]) { __VA_ARGS__, NULL }))
+
 #endif	/* NM_DEVICE_PRIVATE_H */
diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c
index a937ae2d..b4af4416 100644
--- a/src/devices/nm-device-tun.c
+++ b/src/devices/nm-device-tun.c
@@ -80,7 +80,7 @@ update_properties (NMDeviceTun *self)
 
 	ifindex = nm_device_get_ifindex (NM_DEVICE (self));
 	if (ifindex > 0) {
-		if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, ifindex, &props)) {
+		if (!nm_platform_link_tun_get_properties (nm_device_get_platform (NM_DEVICE (self)), ifindex, &props)) {
 			_LOGD (LOGD_DEVICE, "tun-properties: cannot loading tun properties from platform for ifindex %d", ifindex);
 			ifindex = 0;
 		} else if (g_strcmp0 (priv->mode, props.mode) != 0) {
@@ -138,7 +138,7 @@ complete_connection (NMDevice *device,
 {
 	NMSettingTun *s_tun;
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_TUN_SETTING_NAME,
 	                           existing_connections,
@@ -181,7 +181,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 		nm_connection_add_setting (connection, (NMSetting *) s_tun);
 	}
 
-	if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) {
+	if (!nm_platform_link_tun_get_properties (nm_device_get_platform (device), nm_device_get_ifindex (device), &props)) {
 		_LOGW (LOGD_PLATFORM, "failed to get TUN interface info while updating connection.");
 		return;
 	}
@@ -232,7 +232,7 @@ create_and_realize (NMDevice *device,
 	user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1);
 	group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1);
 
-	plerr = nm_platform_link_tun_add (NM_PLATFORM_GET, iface,
+	plerr = nm_platform_link_tun_add (nm_device_get_platform (device), iface,
 	                                  nm_setting_tun_get_mode (s_tun) == NM_SETTING_TUN_MODE_TAP,
 	                                  user, group,
 	                                  nm_setting_tun_get_pi (s_tun),
@@ -291,15 +291,13 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceTun *self = NM_DEVICE_TUN (device);
 	NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self);
 	NMActStageReturn ret;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	ret = NM_DEVICE_CLASS (nm_device_tun_parent_class)->act_stage1_prepare (device, reason);
+	ret = NM_DEVICE_CLASS (nm_device_tun_parent_class)->act_stage1_prepare (device, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c
index 1971aeeb..11916c59 100644
--- a/src/devices/nm-device-veth.c
+++ b/src/devices/nm-device-veth.c
@@ -69,7 +69,7 @@ update_properties (NMDevice *device)
 
 	ifindex = nm_device_get_ifindex (device);
 
-	if (!nm_platform_link_veth_get_properties (NM_PLATFORM_GET, ifindex, &peer_ifindex))
+	if (!nm_platform_link_veth_get_properties (nm_device_get_platform (device), ifindex, &peer_ifindex))
 		peer_ifindex = 0;
 
 	nm_device_parent_set_ifindex (device, peer_ifindex);
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index 45d7cf67..06db6446 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -79,7 +79,7 @@ parent_state_changed (NMDevice *parent,
 	NMDeviceVlan *self = NM_DEVICE_VLAN (user_data);
 
 	/* We'll react to our own carrier state notifications. Ignore the parent's. */
-	if (reason == NM_DEVICE_STATE_REASON_CARRIER)
+	if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_CARRIER)
 		return;
 
 	nm_device_set_unmanaged_by_flags (NM_DEVICE (self), NM_UNMANAGED_PARENT, !nm_device_get_managed (parent, FALSE), reason);
@@ -90,13 +90,14 @@ parent_hwaddr_maybe_changed (NMDevice *parent,
                              GParamSpec *pspec,
                              gpointer user_data)
 {
-	NMDeviceVlan *self = NM_DEVICE_VLAN (user_data);
+	NMDevice *device = NM_DEVICE (user_data);
+	NMDeviceVlan *self = NM_DEVICE_VLAN (device);
 	NMConnection *connection;
 	const char *new_mac, *old_mac;
 	NMSettingIPConfig *s_ip6;
 
 	/* Never touch assumed devices */
-	if (nm_device_uses_assumed_connection ((NMDevice *) self))
+	if (nm_device_sys_iface_state_is_external_or_assume (device))
 		return;
 
 	connection = nm_device_get_applied_connection ((NMDevice *) self);
@@ -121,7 +122,7 @@ parent_hwaddr_maybe_changed (NMDevice *parent,
 		 */
 		s_ip6 = nm_connection_get_setting_ip6_config (connection);
 		if (s_ip6)
-			nm_device_reactivate_ip6_config (NM_DEVICE (self), s_ip6, s_ip6);
+			nm_device_reactivate_ip6_config (NM_DEVICE (self), s_ip6, s_ip6, FALSE);
 	}
 }
 
@@ -185,7 +186,7 @@ update_properties (NMDevice *device)
 	ifindex = nm_device_get_ifindex (device);
 
 	if (ifindex > 0)
-		plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, &plink);
+		plnk = nm_platform_link_get_lnk_vlan (nm_device_get_platform (device), ifindex, &plink);
 
 	if (   plnk
 	    && plink->parent > 0)
@@ -248,7 +249,7 @@ create_and_realize (NMDevice *device,
 
 	vlan_id = nm_setting_vlan_get_id (s_vlan);
 
-	plerr = nm_platform_link_vlan_add (NM_PLATFORM_GET,
+	plerr = nm_platform_link_vlan_add (nm_device_get_platform (device),
 	                                   iface,
 	                                   parent_ifindex,
 	                                   vlan_id,
@@ -424,7 +425,7 @@ complete_connection (NMDevice *device,
 {
 	NMSettingVlan *s_vlan;
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_VLAN_SETTING_NAME,
 	                           existing_connections,
@@ -471,7 +472,7 @@ update_connection (NMDevice *device, NMConnection *connection)
 		nm_connection_add_setting (connection, (NMSetting *) s_vlan);
 	}
 
-	polnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, NM_LINK_TYPE_VLAN, &plink);
+	polnk = nm_platform_link_get_lnk (nm_device_get_platform (device), ifindex, NM_LINK_TYPE_VLAN, &plink);
 
 	if (polnk)
 		vlan_id = polnk->lnk_vlan.id;
@@ -522,15 +523,13 @@ update_connection (NMDevice *device, NMConnection *connection)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDevice *parent_device;
 	NMSettingVlan *s_vlan;
 	NMActStageReturn ret;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	ret = NM_DEVICE_CLASS (nm_device_vlan_parent_class)->act_stage1_prepare (device, reason);
+	ret = NM_DEVICE_CLASS (nm_device_vlan_parent_class)->act_stage1_prepare (device, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
@@ -557,7 +556,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 		                                 &egress_map,
 		                                 &n_egress_map);
 
-		nm_platform_link_vlan_change (NM_PLATFORM_GET,
+		nm_platform_link_vlan_change (nm_device_get_platform (device),
 		                              nm_device_get_ifindex (device),
 		                              NM_VLAN_FLAGS_ALL,
 		                              nm_setting_vlan_get_flags (s_vlan),
@@ -585,7 +584,7 @@ get_configured_mtu (NMDevice *self, gboolean *out_is_user_config)
 	/* Inherit the MTU from parent device, if any */
 	ifindex = nm_device_parent_get_ifindex (self);
 	if (ifindex > 0)
-		mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex);
+		mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)), ifindex);
 
 	return mtu ?: NM_DEVICE_DEFAULT_MTU_WIRED;
 }
diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c
index b4508133..d0b88874 100644
--- a/src/devices/nm-device-vxlan.c
+++ b/src/devices/nm-device-vxlan.c
@@ -87,7 +87,7 @@ update_properties (NMDevice *device)
 	GObject *object = G_OBJECT (device);
 	const NMPlatformLnkVxlan *props;
 
-	props = nm_platform_link_get_lnk_vxlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL);
+	props = nm_platform_link_get_lnk_vxlan (nm_device_get_platform (device), nm_device_get_ifindex (device), NULL);
 	if (!props) {
 		_LOGW (LOGD_PLATFORM, "could not get vxlan properties");
 		return;
@@ -217,7 +217,7 @@ create_and_realize (NMDevice *device,
 	props.l2miss = nm_setting_vxlan_get_l2_miss (s_vxlan);
 	props.l3miss = nm_setting_vxlan_get_l3_miss (s_vxlan);
 
-	plerr = nm_platform_link_vxlan_add (NM_PLATFORM_GET, iface, &props, out_plink);
+	plerr = nm_platform_link_vxlan_add (nm_device_get_platform (device), iface, &props, out_plink);
 	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 		             "Failed to create VXLAN interface '%s' for '%s': %s",
@@ -361,7 +361,7 @@ complete_connection (NMDevice *device,
 {
 	NMSettingVxlan *s_vxlan;
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_VXLAN_SETTING_NAME,
 	                           existing_connections,
@@ -496,13 +496,11 @@ update_connection (NMDevice *device, NMConnection *connection)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMActStageReturn ret;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	ret = NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->act_stage1_prepare (device, reason);
+	ret = NM_DEVICE_CLASS (nm_device_vxlan_parent_class)->act_stage1_prepare (device, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 2a2d276d..da581a0d 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2005 - 2013 Red Hat, Inc.
+ * Copyright (C) 2005 - 2017 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
@@ -56,6 +56,7 @@
 #include "settings/nm-settings-connection.h"
 #include "settings/nm-settings.h"
 #include "nm-auth-utils.h"
+#include "nm-netns.h"
 #include "nm-dispatcher.h"
 #include "nm-config.h"
 #include "dns/nm-dns-manager.h"
@@ -66,6 +67,8 @@
 #include "nm-lldp-listener.h"
 #include "nm-audit-manager.h"
 #include "nm-arping-manager.h"
+#include "nm-connectivity.h"
+#include "nm-dbus-interface.h"
 
 #include "nm-device-logging.h"
 _LOG_DECLARE_SELF (NMDevice);
@@ -130,6 +133,13 @@ typedef enum {
 	HW_ADDR_TYPE_GENERATED,
 } HwAddrType;
 
+typedef enum {
+	FIREWALL_STATE_UNMANAGED = 0,
+	FIREWALL_STATE_INITIALIZED,
+	FIREWALL_STATE_WAIT_STAGE_3,
+	FIREWALL_STATE_WAIT_IP_CONFIG,
+} FirewallState;
+
 /*****************************************************************************/
 
 enum {
@@ -189,6 +199,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice,
 	PROP_REFRESH_RATE_MS,
 	PROP_TX_BYTES,
 	PROP_RX_BYTES,
+	PROP_CONNECTIVITY,
 );
 
 typedef struct _NMDevicePrivate {
@@ -206,6 +217,7 @@ typedef struct _NMDevicePrivate {
 		NMDeviceState state;
 		NMDeviceStateReason reason;
 	} queued_state;
+
 	guint queued_ip4_config_id;
 	guint queued_ip6_config_id;
 	GSList *pending_actions;
@@ -250,6 +262,8 @@ typedef struct _NMDevicePrivate {
 
 	NMUtilsStableType current_stable_id_type:3;
 
+	bool          is_nm_owned:1; /* whether the device is a device owned and created by NM */
+
 	GHashTable *  available_connections;
 	char *        hw_addr;
 	char *        hw_addr_perm;
@@ -259,7 +273,6 @@ typedef struct _NMDevicePrivate {
 
 	NMUnmanagedFlags        unmanaged_mask;
 	NMUnmanagedFlags        unmanaged_flags;
-	bool                    is_nm_owned; /* whether the device is a device owned and created by NM */
 	DeleteOnDeactivateData *delete_on_deactivate_data; /* data for scheduled cleanup when deleting link (g_idle_add) */
 
 	GCancellable *deactivating_cancellable;
@@ -289,19 +302,25 @@ typedef struct _NMDevicePrivate {
 	guint           link_connected_id;
 	guint           link_disconnected_id;
 	guint           carrier_defer_id;
-	bool            carrier;
 	guint           carrier_wait_id;
-	bool            ignore_carrier;
-	gulong          ignore_carrier_id;
+	gulong          config_changed_id;
 	guint32         mtu;
 	guint32         ip6_mtu;
 	guint32 mtu_initial;
 	guint32 ip6_mtu_initial;
 
+	bool            carrier:1;
+	bool            ignore_carrier:1;
+
 	bool mtu_initialized:1;
 
 	bool            up:1;   /* IFF_UP */
 
+	bool            v4_commit_first_time:1;
+	bool            v6_commit_first_time:1;
+
+	NMDeviceSysIfaceState sys_iface_state:2;
+
 	/* Generic DHCP stuff */
 	guint32         dhcp_timeout;
 	char *          dhcp_anycast_address;
@@ -311,6 +330,7 @@ typedef struct _NMDevicePrivate {
 	/* Proxy Configuration */
 	NMProxyConfig *proxy_config;
 	NMPacrunnerManager *pacrunner_manager;
+	NMPacrunnerCallId *pacrunner_call_id;
 
 	/* IP4 configuration info */
 	NMIP4Config *   ip4_config;     /* Combined config from VPN, settings, and device */
@@ -331,9 +351,8 @@ typedef struct _NMDevicePrivate {
 		NMPlatformIP4Route v4;
 		NMPlatformIP6Route v6;
 	} default_route;
-
-	bool v4_commit_first_time;
-	bool v6_commit_first_time;
+	bool v4_has_shadowed_routes;
+	const char *ip4_rp_filter;
 
 	/* DHCPv4 tracking */
 	struct {
@@ -342,6 +361,8 @@ typedef struct _NMDevicePrivate {
 		NMDhcp4Config * config;
 		guint           restart_id;
 		guint           num_tries_left;
+		char *          pac_url;
+		bool            was_active;
 	} dhcp4;
 
 	struct {
@@ -359,7 +380,8 @@ typedef struct _NMDevicePrivate {
 	gulong            dnsmasq_state_id;
 
 	/* Firewall */
-	bool fw_ready;
+	FirewallState fw_state:4;
+	NMFirewallManager *fw_mgr;
 	NMFirewallManagerCallId fw_call;
 
 	/* IPv4LL stuff */
@@ -411,12 +433,14 @@ typedef struct _NMDevicePrivate {
 		guint            restart_id;
 		guint            num_tries_left;
 		guint            needed_prefixes;
+		bool             was_active;
 	} dhcp6;
 
 	gboolean needs_ip6_subnet;
 
 	/* allow autoconnect feature */
-	bool autoconnect;
+	bool autoconnect_intern:1;
+	bool autoconnect_user:1;
 
 	/* master interface for bridge/bond/team slave */
 	NMDevice *      master;
@@ -432,7 +456,12 @@ typedef struct _NMDevicePrivate {
 
 	NMSettings *settings;
 
+	NMNetns *netns;
+
 	NMLldpListener *lldp_listener;
+	NMConnectivityState connectivity_state;
+	guint concheck_periodic_id;
+	guint64 concheck_seq;
 
 	guint check_delete_unrealized_id;
 
@@ -451,29 +480,25 @@ G_DEFINE_ABSTRACT_TYPE (NMDevice, nm_device, NM_TYPE_EXPORTED_OBJECT)
 
 /*****************************************************************************/
 
-static void nm_device_set_proxy_config (NMDevice *self, GHashTable *options);
+static void nm_device_set_proxy_config (NMDevice *self, const char *pac_url);
 
 static gboolean nm_device_set_ip4_config (NMDevice *self,
                                           NMIP4Config *config,
                                           guint32 default_route_metric,
                                           gboolean commit,
-                                          gboolean routes_full_sync,
-                                          NMDeviceStateReason *reason);
+                                          gboolean routes_full_sync);
 static gboolean ip4_config_merge_and_apply (NMDevice *self,
                                             NMIP4Config *config,
-                                            gboolean commit,
-                                            NMDeviceStateReason *out_reason);
+                                            gboolean commit);
 
 static gboolean nm_device_set_ip6_config (NMDevice *self,
                                           NMIP6Config *config,
                                           gboolean commit,
-                                          gboolean routes_full_sync,
-                                          NMDeviceStateReason *reason);
+                                          gboolean routes_full_sync);
 static gboolean ip6_config_merge_and_apply (NMDevice *self,
-                                            gboolean commit,
-                                            NMDeviceStateReason *out_reason);
+                                            gboolean commit);
 
-static void nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure);
+static gboolean nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure);
 static void nm_device_slave_notify_enslave (NMDevice *self, gboolean success);
 static void nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason);
 
@@ -482,6 +507,9 @@ static NMActStageReturn linklocal6_start (NMDevice *self);
 
 static void _carrier_wait_check_queued_act_request (NMDevice *self);
 
+static void nm_device_set_autoconnect_both (NMDevice *self, gboolean autoconnect);
+static void nm_device_set_autoconnect_full (NMDevice *self, int autoconnect_intern, int autoconnect_user);
+
 static const char *_activation_func_to_string (ActivationHandleFunc func);
 static void activation_source_handle_cb (NMDevice *self, int family);
 
@@ -494,10 +522,12 @@ static gboolean queued_ip4_config_change (gpointer user_data);
 static gboolean queued_ip6_config_change (gpointer user_data);
 static void ip_check_ping_watch_cb (GPid pid, gint status, gpointer user_data);
 static gboolean ip_config_valid (NMDeviceState state);
-static NMActStageReturn dhcp4_start (NMDevice *self, NMConnection *connection, NMDeviceStateReason *reason);
-static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason);
+static NMActStageReturn dhcp4_start (NMDevice *self, NMConnection *connection);
+static gboolean dhcp6_start (NMDevice *self, gboolean wait_for_ll);
 static void nm_device_start_ip_check (NMDevice *self);
-static void realize_start_setup (NMDevice *self, const NMPlatformLink *plink);
+static void realize_start_setup (NMDevice *self,
+                                 const NMPlatformLink *plink,
+                                 NMUnmanFlagOp unmanaged_user_explicit);
 static void _commit_mtu (NMDevice *self, const NMIP4Config *config);
 static void dhcp_schedule_restart (NMDevice *self, int family, const char *reason);
 static void _cancel_activation (NMDevice *self);
@@ -605,6 +635,81 @@ nm_device_get_settings (NMDevice *self)
 	return NM_DEVICE_GET_PRIVATE (self)->settings;
 }
 
+NMNetns *
+nm_device_get_netns (NMDevice *self)
+{
+	return NM_DEVICE_GET_PRIVATE (self)->netns;
+}
+
+NMPlatform *
+nm_device_get_platform (NMDevice *self)
+{
+	return nm_netns_get_platform (nm_device_get_netns (self));
+}
+
+/*****************************************************************************/
+
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_sys_iface_state_to_str, NMDeviceSysIfaceState,
+	NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_SYS_IFACE_STATE_EXTERNAL, "external"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_SYS_IFACE_STATE_ASSUME,   "assume"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_SYS_IFACE_STATE_MANAGED,  "managed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_SYS_IFACE_STATE_REMOVED,  "removed"),
+);
+
+NMDeviceSysIfaceState
+nm_device_sys_iface_state_get (NMDevice *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_SYS_IFACE_STATE_EXTERNAL);
+
+	return NM_DEVICE_GET_PRIVATE (self)->sys_iface_state;
+}
+
+gboolean
+nm_device_sys_iface_state_is_external (NMDevice *self)
+{
+	return NM_IN_SET (nm_device_sys_iface_state_get (self),
+	                  NM_DEVICE_SYS_IFACE_STATE_EXTERNAL);
+}
+
+gboolean
+nm_device_sys_iface_state_is_external_or_assume (NMDevice *self)
+{
+	return NM_IN_SET (nm_device_sys_iface_state_get (self),
+	                  NM_DEVICE_SYS_IFACE_STATE_EXTERNAL,
+	                  NM_DEVICE_SYS_IFACE_STATE_ASSUME);
+}
+
+void
+nm_device_sys_iface_state_set (NMDevice *self,
+                               NMDeviceSysIfaceState sys_iface_state)
+{
+	NMDevicePrivate *priv;
+
+	g_return_if_fail (NM_IS_DEVICE (self));
+	g_return_if_fail (NM_IN_SET (sys_iface_state,
+	                             NM_DEVICE_SYS_IFACE_STATE_EXTERNAL,
+	                             NM_DEVICE_SYS_IFACE_STATE_ASSUME,
+	                             NM_DEVICE_SYS_IFACE_STATE_MANAGED,
+	                             NM_DEVICE_SYS_IFACE_STATE_REMOVED));
+
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	if (priv->sys_iface_state != sys_iface_state) {
+		_LOGT (LOGD_DEVICE, "sys-iface-state: %s -> %s",
+		       _sys_iface_state_to_str (priv->sys_iface_state),
+		       _sys_iface_state_to_str (sys_iface_state));
+		priv->sys_iface_state = sys_iface_state;
+	}
+
+	/* this function only sets a flag, no immediate actions are initiated.
+	 *
+	 * If you change this, make sure that all callers are fine with such actions. */
+
+	nm_assert (priv->sys_iface_state == sys_iface_state);
+}
+
+/*****************************************************************************/
+
 static void
 init_ip4_config_dns_priority (NMDevice *self, NMIP4Config *config)
 {
@@ -633,16 +738,48 @@ init_ip6_config_dns_priority (NMDevice *self, NMIP6Config *config)
 
 /*****************************************************************************/
 
+static gboolean
+nm_device_ipv4_sysctl_set (NMDevice *self, const char *property, const char *value)
+{
+	NMPlatform *platform = nm_device_get_platform (self);
+	gs_free char *value_to_free = NULL;
+	const char *value_to_set;
+
+	if (value) {
+		value_to_set = value;
+	} else {
+		/* Set to a default value when we've got a NULL @value. */
+		value_to_free = nm_platform_sysctl_get (platform,
+		                                        NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path ("default", property)));
+		value_to_set = value_to_free;
+	}
+
+	return nm_platform_sysctl_set (platform,
+	                               NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path (nm_device_get_ip_iface (self), property)),
+	                               value_to_set);
+}
+
+static guint32
+nm_device_ipv4_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 fallback)
+{
+	return nm_platform_sysctl_get_int_checked (nm_device_get_platform (self),
+	                                           NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip4_property_path (nm_device_get_ip_iface (self), property)),
+	                                           10,
+	                                           0,
+	                                           G_MAXUINT32,
+	                                           fallback);
+}
+
 gboolean
 nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value)
 {
-	return nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property)), value);
+	return nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property)), value);
 }
 
 static guint32
 nm_device_ipv6_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 fallback)
 {
-	return nm_platform_sysctl_get_int_checked (NM_PLATFORM_GET,
+	return nm_platform_sysctl_get_int_checked (nm_device_get_platform (self),
 	                                           NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), property)),
 	                                           10,
 	                                           0,
@@ -872,7 +1009,7 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
 	if (nm_streq0 (iface, priv->ip_iface)) {
 		if (!iface)
 			return FALSE;
-		ifindex = nm_platform_if_nametoindex (NM_PLATFORM_GET, iface);
+		ifindex = nm_platform_if_nametoindex (nm_device_get_platform (self), iface);
 		if (   ifindex <= 0
 		    || priv->ip_ifindex == ifindex)
 			return FALSE;
@@ -890,7 +1027,7 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
 			 * with this name still exists and we resolve the ifindex
 			 * anew.
 			 */
-			priv->ip_ifindex = nm_platform_if_nametoindex (NM_PLATFORM_GET, iface);
+			priv->ip_ifindex = nm_platform_if_nametoindex (nm_device_get_platform (self), iface);
 			if (priv->ip_ifindex > 0)
 				_LOGD (LOGD_DEVICE, "ip-ifname: set ifname '%s', ifindex %d", iface, priv->ip_ifindex);
 			else
@@ -902,11 +1039,11 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
 	}
 
 	if (priv->ip_ifindex > 0) {
-		if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET))
-			nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, priv->ip_ifindex, TRUE);
+		if (nm_platform_check_support_user_ipv6ll (nm_device_get_platform (self)))
+			nm_platform_link_set_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ip_ifindex, TRUE);
 
-		if (!nm_platform_link_is_up (NM_PLATFORM_GET, priv->ip_ifindex))
-			nm_platform_link_set_up (NM_PLATFORM_GET, priv->ip_ifindex, NULL);
+		if (!nm_platform_link_is_up (nm_device_get_platform (self), priv->ip_ifindex))
+			nm_platform_link_set_up (nm_device_get_platform (self), priv->ip_ifindex, NULL);
 	}
 
 	/* We don't care about any saved values from the old iface */
@@ -1108,7 +1245,7 @@ _stats_timeout_cb (gpointer user_data)
 	_LOGT (LOGD_DEVICE, "stats: refresh %d", ifindex);
 
 	if (ifindex > 0)
-		nm_platform_link_refresh (NM_PLATFORM_GET, ifindex);
+		nm_platform_link_refresh (nm_device_get_platform (self), ifindex);
 
 	return G_SOURCE_CONTINUE;
 }
@@ -1165,7 +1302,7 @@ _stats_set_refresh_rate (NMDevice *self, guint refresh_rate_ms)
 	 * we don't get the result right away. */
 	ifindex = nm_device_get_ip_ifindex (self);
 	if (ifindex > 0)
-		nm_platform_link_refresh (NM_PLATFORM_GET, ifindex);
+		nm_platform_link_refresh (nm_device_get_platform (self), ifindex);
 
 	priv->stats.timeout_id = g_timeout_add (refresh_rate_ms, _stats_timeout_cb, self);
 }
@@ -1184,7 +1321,7 @@ get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
 	ifindex = nm_device_get_ip_ifindex (self);
 	g_return_val_if_fail (ifindex > 0, FALSE);
 
-	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex);
+	pllink = nm_platform_link_get (nm_device_get_platform (self), ifindex);
 	if (   !pllink
 	    || NM_IN_SET (pllink->type, NM_LINK_TYPE_NONE, NM_LINK_TYPE_UNKNOWN))
 		return FALSE;
@@ -1231,7 +1368,7 @@ nm_device_get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *iid, gboo
 
 	if (!ignore_token) {
 		s_ip6 = (NMSettingIP6Config *)
-			nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG);
+		    nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG);
 		g_return_val_if_fail (s_ip6, FALSE);
 		token = nm_setting_ip6_config_get_token (s_ip6);
 	}
@@ -1345,6 +1482,8 @@ nm_device_get_priority (NMDevice *self)
 		return 450;
 	case NM_DEVICE_TYPE_VXLAN:
 		return 500;
+	case NM_DEVICE_TYPE_DUMMY:
+		return 550;
 	case NM_DEVICE_TYPE_WIFI:
 		return 600;
 	case NM_DEVICE_TYPE_OLPC_MESH:
@@ -1368,6 +1507,26 @@ nm_device_get_priority (NMDevice *self)
 }
 
 static guint32
+route_metric_with_penalty (NMDevice *self, guint32 metric)
+{
+#if WITH_CONCHECK
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	const guint32 PENALTY = 20000;
+
+	/* Beware: for IPv6, a metric of 0 effectively means 1024.
+	 * Only pass a normalized IPv6 metric (nm_utils_ip6_route_metric_normalize). */
+
+	if (   priv->connectivity_state != NM_CONNECTIVITY_FULL
+	    && nm_connectivity_check_enabled (nm_connectivity_get ())) {
+		if (metric >= G_MAXUINT32 - PENALTY)
+			return G_MAXUINT32;
+		return metric + PENALTY;
+	}
+#endif
+	return metric;
+}
+
+static guint32
 _get_ipx_route_metric (NMDevice *self,
                        gboolean is_v4)
 {
@@ -1449,9 +1608,9 @@ _update_default_route (NMDevice *self, int addr_family, gboolean has, gboolean i
 	*p_is_assumed = is_assumed;
 
 	if (addr_family == AF_INET)
-		nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
+		nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self);
 	else
-		nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
+		nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self);
 }
 
 const NMPlatformIP4Route *
@@ -1529,7 +1688,7 @@ nm_device_has_carrier (NMDevice *self)
 NMActRequest *
 nm_device_get_act_request (NMDevice *self)
 {
-	g_return_val_if_fail (self != NULL, NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (self), NULL);
 
 	return NM_DEVICE_GET_PRIVATE (self)->act_request;
 }
@@ -1590,33 +1749,174 @@ nm_device_get_physical_port_id (NMDevice *self)
 
 /*****************************************************************************/
 
+static void
+update_connectivity_state (NMDevice *self, NMConnectivityState state)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	/* If the connectivity check is disabled, make an optimistic guess. */
+	if (state == NM_CONNECTIVITY_UNKNOWN) {
+		if (priv->state == NM_DEVICE_STATE_ACTIVATED) {
+			if (priv->default_route.v4_has || priv->default_route.v6_has)
+				state = NM_CONNECTIVITY_FULL;
+			else
+				state = NM_CONNECTIVITY_LIMITED;
+		} else {
+			state = NM_CONNECTIVITY_NONE;
+		}
+	}
+
+	if (priv->connectivity_state != state) {
+#if WITH_CONCHECK
+		_LOGD (LOGD_CONCHECK, "state changed from %s to %s",
+		       nm_connectivity_state_to_string (priv->connectivity_state),
+		       nm_connectivity_state_to_string (state));
+#endif
+		priv->connectivity_state = state;
+		_notify (self, PROP_CONNECTIVITY);
+
+		if (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED) {
+			if (!ip4_config_merge_and_apply (self, NULL, TRUE))
+				_LOGW (LOGD_IP4, "Failed to update IPv4 default route metric");
+			if (!ip6_config_merge_and_apply (self, TRUE))
+				_LOGW (LOGD_IP6, "Failed to update IPv6 default route metric");
+		}
+	}
+}
+
+typedef struct {
+	NMDevice *self;
+	NMDeviceConnectivityCallback callback;
+	gpointer user_data;
+	guint64 seq;
+} ConnectivityCheckData;
+
+static void
+concheck_done (ConnectivityCheckData *data)
+{
+	NMDevice *self = data->self;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	/* The unsolicited connectivity checks don't hook a callback. */
+	if (data->callback)
+		data->callback (data->self, priv->connectivity_state, data->user_data);
+	g_object_unref (data->self);
+	g_slice_free (ConnectivityCheckData, data);
+}
+
+#if WITH_CONCHECK
+static void
+concheck_cb (GObject *source_object, GAsyncResult *result, gpointer user_data)
+{
+	ConnectivityCheckData *data = user_data;
+	NMDevice *self = data->self;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnectivity *connectivity = NM_CONNECTIVITY (source_object);
+	NMConnectivityState state;
+	GError *error = NULL;
+
+	state = nm_connectivity_check_finish (connectivity, result, &error);
+	if (error) {
+		_LOGW (LOGD_DEVICE, "connectivity checking on '%s' failed: %s",
+		       nm_device_get_iface (self), error->message);
+		g_error_free (error);
+	}
+
+	if (data->seq == priv->concheck_seq)
+		update_connectivity_state (data->self, state);
+	concheck_done (data);
+}
+#endif /* WITH_CONCHECK */
+
 static gboolean
-nm_device_uses_generated_assumed_connection (NMDevice *self)
+no_concheck (gpointer user_data)
 {
+	ConnectivityCheckData *data = user_data;
+
+	concheck_done (data);
+	return G_SOURCE_REMOVE;
+}
+
+void
+nm_device_check_connectivity (NMDevice *self,
+                              NMDeviceConnectivityCallback callback,
+                              gpointer user_data)
+{
+	ConnectivityCheckData *data;
+#if WITH_CONCHECK
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMSettingsConnection *connection;
+#endif
 
-	if (   priv->act_request
-	    && nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (priv->act_request))) {
-		connection = nm_act_request_get_settings_connection (priv->act_request);
-		if (   connection
-		    && nm_settings_connection_get_nm_generated_assumed (connection))
-			return TRUE;
+	data = g_slice_new0 (ConnectivityCheckData);
+	data->self = g_object_ref (self);
+	data->callback = callback;
+	data->user_data = user_data;
+
+#if WITH_CONCHECK
+	if (priv->concheck_periodic_id) {
+		data->seq = ++priv->concheck_seq;
+
+		/* Kick off a real connectivity check. */
+		nm_connectivity_check_async (nm_connectivity_get (),
+		                             nm_device_get_iface (self),
+		                             concheck_cb,
+		                             data);
+		return;
 	}
-	return FALSE;
+#endif
+
+	/* Fake one. */
+	g_idle_add (no_concheck, data);
 }
 
-gboolean
-nm_device_uses_assumed_connection (NMDevice *self)
+NMConnectivityState
+nm_device_get_connectivity_state (NMDevice *self)
+{
+	g_return_val_if_fail (NM_IS_DEVICE (self), NM_CONNECTIVITY_UNKNOWN);
+
+	return NM_DEVICE_GET_PRIVATE (self)->connectivity_state;
+}
+
+#if WITH_CONCHECK
+static void
+concheck_periodic (NMConnectivity *connectivity, NMDevice *self)
+{
+	nm_device_check_connectivity (self, NULL, NULL);
+}
+#endif
+
+static void
+concheck_periodic_update (NMDevice *self)
 {
+#if WITH_CONCHECK
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gboolean check_enable;
 
-	if (   priv->act_request
-	    && nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (priv->act_request)))
-		return TRUE;
-	return FALSE;
+	check_enable =    (priv->state == NM_DEVICE_STATE_ACTIVATED)
+	               && (priv->default_route.v4_has || priv->default_route.v6_has);
+
+	if (check_enable && !priv->concheck_periodic_id) {
+		/* We just gained a default route. Enable periodic checking. */
+		priv->concheck_periodic_id = g_signal_connect (nm_connectivity_get (),
+		                                               NM_CONNECTIVITY_PERIODIC_CHECK,
+		                                               G_CALLBACK (concheck_periodic), self);
+		/* Also kick off a check right away. */
+		nm_device_check_connectivity (self, NULL, NULL);
+	} else if (!check_enable && priv->concheck_periodic_id) {
+		/* The default route has gone off, and so has connectivity. */
+		g_signal_handler_disconnect (nm_connectivity_get (), priv->concheck_periodic_id);
+		priv->concheck_periodic_id = 0;
+		update_connectivity_state (self, NM_CONNECTIVITY_NONE);
+	}
+#else
+	/* update_connectivity_state() figures out how to lie about
+	 * connectivity state if the actual state is not really known. */
+	update_connectivity_state (self, NM_CONNECTIVITY_UNKNOWN);
+#endif
 }
 
+/*****************************************************************************/
+
 static SlaveInfo *
 find_slave_info (NMDevice *self, NMDevice *slave)
 {
@@ -1722,7 +2022,7 @@ nm_device_master_release_one_slave (NMDevice *self, NMDevice *slave, gboolean co
 
 	info = find_slave_info (self, slave);
 
-	_LOGt (LOGD_CORE, "master: release one slave %p/%s%s", slave, nm_device_get_iface (slave),
+	_LOGT (LOGD_CORE, "master: release one slave %p/%s%s", slave, nm_device_get_iface (slave),
 	       !info ? " (not registered)" : "");
 
 	if (!info)
@@ -1785,7 +2085,7 @@ is_unmanaged_external_down (NMDevice *self, gboolean consider_can)
 	/* Manage externally-created software interfaces only when they are IFF_UP */
 	if (   priv->ifindex <= 0
 	    || !priv->up
-	    || !(priv->slaves || nm_platform_link_can_assume (NM_PLATFORM_GET, priv->ifindex)))
+	    || !(priv->slaves || nm_platform_link_can_assume (nm_device_get_platform (self), priv->ifindex)))
 		return NM_UNMAN_FLAG_OP_SET_UNMANAGED;
 
 	return NM_UNMAN_FLAG_OP_SET_MANAGED;
@@ -1857,7 +2157,7 @@ nm_device_update_dynamic_ip_setup (NMDevice *self)
 
 	if (priv->lldp_listener && nm_lldp_listener_is_running (priv->lldp_listener)) {
 		nm_lldp_listener_stop (priv->lldp_listener);
-		addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length);
+		addr = nm_platform_link_get_address (nm_device_get_platform (self), priv->ifindex, &addr_length);
 
 		if (!nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error)) {
 			_LOGD (LOGD_DEVICE, "LLDP listener %p could not be restarted: %s",
@@ -2029,7 +2329,7 @@ device_recheck_slave_status (NMDevice *self, const NMPlatformLink *plink)
 		} else {
 			_LOGW (LOGD_DEVICE, "enslaved to unknown device %d %s",
 			       plink->master,
-			       nm_platform_link_get_name (NM_PLATFORM_GET, plink->master));
+			       nm_platform_link_get_name (nm_device_get_platform (self), plink->master));
 		}
 	}
 }
@@ -2120,13 +2420,13 @@ device_link_changed (NMDevice *self)
 	priv->device_link_changed_id = 0;
 
 	ifindex = nm_device_get_ifindex (self);
-	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex);
+	pllink = nm_platform_link_get (nm_device_get_platform (self), ifindex);
 	if (!pllink)
 		return G_SOURCE_REMOVE;
 
 	info = *pllink;
 
-	udi = nm_platform_link_get_udi (NM_PLATFORM_GET, info.ifindex);
+	udi = nm_platform_link_get_udi (nm_device_get_platform (self), info.ifindex);
 	if (udi && g_strcmp0 (udi, priv->udi)) {
 		/* Update UDI to what udev gives us */
 		g_free (priv->udi);
@@ -2234,11 +2534,11 @@ device_link_changed (NMDevice *self)
 		/* the link was down and just came up. That happens for example, while changing MTU.
 		 * We must restore IP configuration. */
 		if (priv->ip4_state == IP_DONE) {
-			if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL))
+			if (!ip4_config_merge_and_apply (self, NULL, TRUE))
 				_LOGW (LOGD_IP4, "failed applying IP4 config after link comes up again");
 		}
 		if (priv->ip6_state == IP_DONE) {
-			if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+			if (!ip6_config_merge_and_apply (self, TRUE))
 				_LOGW (LOGD_IP6, "failed applying IP6 config after link comes up again");
 		}
 	}
@@ -2274,7 +2574,7 @@ device_ip_link_changed (NMDevice *self)
 	if (!priv->ip_ifindex)
 		return G_SOURCE_REMOVE;
 
-	pllink = nm_platform_link_get (NM_PLATFORM_GET, priv->ip_ifindex);
+	pllink = nm_platform_link_get (nm_device_get_platform (self), priv->ip_ifindex);
 	if (!pllink)
 		return G_SOURCE_REMOVE;
 
@@ -2316,6 +2616,45 @@ link_changed_cb (NMPlatform *platform,
 }
 
 static void
+ip4_rp_filter_update (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	const char *ip4_rp_filter;
+
+	if (   priv->v4_has_shadowed_routes
+	    || priv->default_route.v4_has) {
+		if (nm_device_ipv4_sysctl_get_uint32 (self, "rp_filter", 0) != 1) {
+			/* Don't touch the rp_filter if it's not strict. */
+			return;
+		}
+		/* Loose rp_filter */
+		ip4_rp_filter = "2";
+	} else {
+		/* Default rp_filter */
+		ip4_rp_filter = NULL;
+	}
+
+	if (ip4_rp_filter != priv->ip4_rp_filter) {
+		nm_device_ipv4_sysctl_set (self, "rp_filter", ip4_rp_filter);
+		priv->ip4_rp_filter = ip4_rp_filter;
+	}
+}
+
+static void
+ip4_routes_changed_changed_cb (NMRouteManager *route_manager, NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	int ifindex = nm_device_get_ip_ifindex (self);
+
+	if (nm_device_sys_iface_state_is_external_or_assume (self))
+		return;
+
+	priv->v4_has_shadowed_routes = nm_route_manager_ip4_routes_shadowed (route_manager,
+	                                                                     ifindex);
+	ip4_rp_filter_update (self);
+}
+
+static void
 link_changed (NMDevice *self, const NMPlatformLink *pllink)
 {
 	/* stub implementation of virtual function to allow subclasses to chain up. */
@@ -2368,6 +2707,8 @@ link_type_compatible (NMDevice *self,
  * nm_device_realize_start():
  * @self: the #NMDevice
  * @plink: an existing platform link or %NULL
+ * @unmanaged_user_explicit: the user-explicit unmanaged flag to apply
+ *   on the device initially.
  * @out_compatible: %TRUE on return if @self is compatible with @plink
  * @error: location to store error, or %NULL
  *
@@ -2383,6 +2724,7 @@ link_type_compatible (NMDevice *self,
 gboolean
 nm_device_realize_start (NMDevice *self,
                          const NMPlatformLink *plink,
+                         NMUnmanFlagOp unmanaged_user_explicit,
                          gboolean *out_compatible,
                          GError **error)
 {
@@ -2406,7 +2748,7 @@ nm_device_realize_start (NMDevice *self,
 		plink_copy = *plink;
 		plink = &plink_copy;
 	}
-	realize_start_setup (self, plink);
+	realize_start_setup (self, plink, unmanaged_user_explicit);
 
 	return TRUE;
 }
@@ -2434,7 +2776,7 @@ nm_device_create_and_realize (NMDevice *self,
 	const NMPlatformLink *plink = NULL;
 
 	/* Must be set before device is realized */
-	priv->is_nm_owned = !nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface);
+	priv->is_nm_owned = !nm_platform_link_get_by_ifname (nm_device_get_platform (self), priv->iface);
 
 	_LOGD (LOGD_DEVICE, "create (is %snm-owned)", priv->is_nm_owned ? "" : "not ");
 
@@ -2446,7 +2788,7 @@ nm_device_create_and_realize (NMDevice *self,
 		plink = &plink_copy;
 	}
 
-	realize_start_setup (self, plink);
+	realize_start_setup (self, plink, NM_UNMAN_FLAG_OP_FORGET);
 	nm_device_realize_finish (self, plink);
 
 	if (nm_device_get_managed (self, FALSE)) {
@@ -2465,7 +2807,7 @@ update_device_from_platform_link (NMDevice *self, const NMPlatformLink *plink)
 
 	g_return_if_fail (plink != NULL);
 
-	udi = nm_platform_link_get_udi (NM_PLATFORM_GET, plink->ifindex);
+	udi = nm_platform_link_get_udi (nm_device_get_platform (self), plink->ifindex);
 	if (udi && !g_strcmp0 (udi, priv->udi)) {
 		g_free (priv->udi);
 		priv->udi = g_strdup (udi);
@@ -2492,17 +2834,41 @@ update_device_from_platform_link (NMDevice *self, const NMPlatformLink *plink)
 }
 
 static void
-config_changed_update_ignore_carrier (NMConfig *config,
-                                      NMConfigData *config_data,
-                                      NMConfigChangeFlags changes,
-                                      NMConfigData *old_data,
-                                      NMDevice *self)
+device_init_sriov_num_vfs (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	gs_free char *value = NULL;
+	int num_vfs;
+
+	if (   priv->ifindex > 0
+	    && nm_device_has_capability (self, NM_DEVICE_CAP_SRIOV)) {
+		value = nm_config_data_get_device_config (NM_CONFIG_GET_DATA,
+		                                          "sriov-num-vfs",
+		                                          self,
+		                                          NULL);
+		num_vfs = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32, -1);
+		if (num_vfs >= 0) {
+			nm_platform_link_set_sriov_num_vfs (nm_device_get_platform (self),
+			                                    priv->ifindex, num_vfs);
+		}
+	}
+}
+
+static void
+config_changed (NMConfig *config,
+                NMConfigData *config_data,
+                NMConfigChangeFlags changes,
+                NMConfigData *old_data,
+                NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (   priv->state <= NM_DEVICE_STATE_DISCONNECTED
 	    || priv->state > NM_DEVICE_STATE_ACTIVATED)
 		priv->ignore_carrier = nm_config_data_get_ignore_carrier (config_data, self);
+
+	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_VALUES))
+		device_init_sriov_num_vfs (self);
 }
 
 static void
@@ -2511,7 +2877,7 @@ check_carrier (NMDevice *self)
 	int ifindex = nm_device_get_ip_ifindex (self);
 
 	if (!nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
-		nm_device_set_carrier (self, nm_platform_link_is_connected (NM_PLATFORM_GET, ifindex));
+		nm_device_set_carrier (self, nm_platform_link_is_connected (nm_device_get_platform (self), ifindex));
 }
 
 static void
@@ -2527,6 +2893,7 @@ realize_start_notify (NMDevice *self,
  * realize_start_setup():
  * @self: the #NMDevice
  * @plink: the #NMPlatformLink if backed by a kernel netdevice
+ * @unmanaged_user_explicit: the user-explict unmanaged flag to set.
  *
  * Update the device from backing resource properties (like hardware
  * addresses, carrier states, driver/firmware info, etc).  This function
@@ -2535,7 +2902,9 @@ realize_start_notify (NMDevice *self,
  * stuff).
  */
 static void
-realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
+realize_start_setup (NMDevice *self,
+                     const NMPlatformLink *plink,
+                     NMUnmanFlagOp unmanaged_user_explicit)
 {
 	NMDevicePrivate *priv;
 	NMDeviceClass *klass;
@@ -2573,6 +2942,8 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 		_notify (self, PROP_MTU);
 	}
 
+	nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL);
+
 	if (plink) {
 		g_return_if_fail (link_type_compatible (self, plink->type, NULL, NULL));
 		update_device_from_platform_link (self, plink);
@@ -2580,21 +2951,21 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 	}
 
 	if (priv->ifindex > 0) {
-		priv->physical_port_id = nm_platform_link_get_physical_port_id (NM_PLATFORM_GET, priv->ifindex);
+		priv->physical_port_id = nm_platform_link_get_physical_port_id (nm_device_get_platform (self), priv->ifindex);
 		_notify (self, PROP_PHYSICAL_PORT_ID);
 
-		priv->dev_id = nm_platform_link_get_dev_id (NM_PLATFORM_GET, priv->ifindex);
+		priv->dev_id = nm_platform_link_get_dev_id (nm_device_get_platform (self), priv->ifindex);
 
-		if (nm_platform_link_is_software (NM_PLATFORM_GET, priv->ifindex))
+		if (nm_platform_link_is_software (nm_device_get_platform (self), priv->ifindex))
 			capabilities |= NM_DEVICE_CAP_IS_SOFTWARE;
 
-		mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET, priv->ifindex);
+		mtu = nm_platform_link_get_mtu (nm_device_get_platform (self), priv->ifindex);
 		if (priv->mtu != mtu) {
 			priv->mtu = mtu;
 			_notify (self, PROP_MTU);
 		}
 
-		nm_platform_link_get_driver_info (NM_PLATFORM_GET,
+		nm_platform_link_get_driver_info (nm_device_get_platform (self),
 		                                  priv->ifindex,
 		                                  NULL,
 		                                  &priv->driver_version,
@@ -2604,8 +2975,11 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 		if (priv->firmware_version)
 			_notify (self, PROP_FIRMWARE_VERSION);
 
-		if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET))
-			priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, priv->ifindex);
+		if (nm_platform_check_support_user_ipv6ll (nm_device_get_platform (self)))
+			priv->nm_ipv6ll = nm_platform_link_get_user_ipv6ll_enabled (nm_device_get_platform (self), priv->ifindex);
+
+		if (nm_platform_link_supports_sriov (nm_device_get_platform (self), priv->ifindex))
+			capabilities |= NM_DEVICE_CAP_SRIOV;
 	}
 
 	if (klass->get_generic_capabilities)
@@ -2629,10 +3003,10 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 	/* Note: initial hardware address must be read before calling get_ignore_carrier() */
 	config = nm_config_get ();
 	priv->ignore_carrier = nm_config_data_get_ignore_carrier (nm_config_get_data (config), self);
-	if (!priv->ignore_carrier_id) {
-		priv->ignore_carrier_id = g_signal_connect (config,
+	if (!priv->config_changed_id) {
+		priv->config_changed_id = g_signal_connect (config,
 		                                            NM_CONFIG_SIGNAL_CONFIG_CHANGED,
-		                                            G_CALLBACK (config_changed_update_ignore_carrier),
+		                                            G_CALLBACK (config_changed),
 		                                            self);
 	}
 
@@ -2647,13 +3021,22 @@ realize_start_setup (NMDevice *self, const NMPlatformLink *plink)
 		priv->carrier = TRUE;
 	}
 
+	device_init_sriov_num_vfs (self);
+
 	nm_assert (!priv->stats.timeout_id);
 	real_rate = _stats_refresh_rate_real (priv->stats.refresh_rate_ms);
 	if (real_rate)
 		priv->stats.timeout_id = g_timeout_add (real_rate, _stats_timeout_cb, self);
 
+	nm_device_set_autoconnect_full (self, !!DEFAULT_AUTOCONNECT, TRUE);
+
 	klass->realize_start_notify (self, plink);
 
+	nm_assert (!nm_device_get_unmanaged_mask (self, NM_UNMANAGED_USER_EXPLICIT));
+	nm_device_set_unmanaged_flags (self,
+	                               NM_UNMANAGED_USER_EXPLICIT,
+	                               unmanaged_user_explicit);
+
 	/* Do not manage externally created software devices until they are IFF_UP
 	 * or have IP addressing */
 	nm_device_set_unmanaged_flags (self,
@@ -2787,7 +3170,7 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
 			if (!NM_DEVICE_GET_CLASS (self)->unrealize (self, error))
 				return FALSE;
 		} else if (ifindex > 0) {
-			nm_platform_link_delete (NM_PLATFORM_GET, ifindex);
+			nm_platform_link_delete (nm_device_get_platform (self), ifindex);
 		}
 	}
 
@@ -2841,12 +3224,12 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error)
 		priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
 	_notify (self, PROP_CAPABILITIES);
 
-	nm_clear_g_signal_handler (nm_config_get (), &priv->ignore_carrier_id);
+	nm_clear_g_signal_handler (nm_config_get (), &priv->config_changed_id);
 
 	priv->real = FALSE;
 	_notify (self, PROP_REAL);
 
-	nm_device_set_autoconnect (self, DEFAULT_AUTOCONNECT);
+	nm_device_set_autoconnect_both (self, FALSE);
 
 	g_object_thaw_notify (G_OBJECT (self));
 
@@ -2939,7 +3322,6 @@ slave_state_changed (NMDevice *slave,
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	gboolean release = FALSE;
-	gboolean configure = TRUE;
 
 	_LOGD (LOGD_DEVICE, "slave %s state change %d (%s) -> %d (%s)",
 	       nm_device_get_iface (slave),
@@ -2962,12 +3344,10 @@ slave_state_changed (NMDevice *slave,
 		release = TRUE;
 	}
 
-	/* Don't touch the device if its state changed externally. */
-	if (reason == NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED)
-		configure = FALSE;
-
 	if (release) {
-		nm_device_master_release_one_slave (self, slave, configure, reason);
+		nm_device_master_release_one_slave (self, slave,
+		                                    priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED,
+		                                    reason);
 		/* Bridge/bond/team interfaces are left up until manually deactivated */
 		if (priv->slaves == NULL && priv->state == NM_DEVICE_STATE_ACTIVATED)
 			_LOGD (LOGD_DEVICE, "last slave removed; remaining activated");
@@ -2983,32 +3363,36 @@ slave_state_changed (NMDevice *slave,
  *
  * If @self is capable of enslaving other devices (ie it's a bridge, bond, team,
  * etc) then this function adds @slave to the slave list for later enslavement.
+ *
+ * Returns: %TRUE if the slave was enslaved. %FALSE means, the slave was already
+ *   enslaved and nothing was done.
  */
-static void
+static gboolean
 nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure)
 {
 	NMDevicePrivate *priv;
 	NMDevicePrivate *slave_priv;
 	SlaveInfo *info;
+	gboolean changed = FALSE;
 
-	g_return_if_fail (NM_IS_DEVICE (self));
-	g_return_if_fail (NM_IS_DEVICE (slave));
-	g_return_if_fail (NM_DEVICE_GET_CLASS (self)->enslave_slave != NULL);
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (NM_IS_DEVICE (slave), FALSE);
+	g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->enslave_slave != NULL, FALSE);
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	slave_priv = NM_DEVICE_GET_PRIVATE (slave);
 
 	info = find_slave_info (self, slave);
 
-	_LOGt (LOGD_CORE, "master: add one slave %p/%s%s", slave, nm_device_get_iface (slave),
+	_LOGT (LOGD_CORE, "master: add one slave %p/%s%s", slave, nm_device_get_iface (slave),
 	       info ? " (already registered)" : "");
 
 	if (configure)
-		g_return_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED);
+		g_return_val_if_fail (nm_device_get_state (slave) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
 
 	if (!info) {
-		g_return_if_fail (!slave_priv->master);
-		g_return_if_fail (!slave_priv->is_enslaved);
+		g_return_val_if_fail (!slave_priv->master, FALSE);
+		g_return_val_if_fail (!slave_priv->is_enslaved, FALSE);
 
 		info = g_slice_new0 (SlaveInfo);
 		info->slave = g_object_ref (slave);
@@ -3028,13 +3412,15 @@ nm_device_master_add_slave (NMDevice *self, NMDevice *slave, gboolean configure)
 
 		g_warn_if_fail (!NM_FLAGS_HAS (slave_priv->unmanaged_mask, NM_UNMANAGED_IS_SLAVE));
 		nm_device_set_unmanaged_by_flags (slave, NM_UNMANAGED_IS_SLAVE, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+		changed = TRUE;
 	} else
-		g_return_if_fail (slave_priv->master == self);
+		g_return_val_if_fail (slave_priv->master == self, FALSE);
 
 	nm_device_queue_recheck_assume (self);
 	nm_device_queue_recheck_assume (slave);
-}
 
+	return changed;
+}
 
 /**
  * nm_device_master_get_slaves:
@@ -3127,14 +3513,14 @@ nm_device_master_release_slaves (NMDevice *self)
 	gboolean configure = TRUE;
 
 	/* Don't release the slaves if this connection doesn't belong to NM. */
-	if (nm_device_uses_generated_assumed_connection (self))
+	if (nm_device_sys_iface_state_is_external (self))
 		return;
 
 	reason = priv->state_reason;
 	if (priv->state == NM_DEVICE_STATE_FAILED)
 		reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED;
 
-	if (!nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex))
+	if (!nm_platform_link_get (nm_device_get_platform (self), priv->ifindex))
 		configure = FALSE;
 
 	while (priv->slaves) {
@@ -3205,6 +3591,14 @@ nm_device_slave_notify_enslave (NMDevice *self, gboolean success)
 				_LOGI (LOGD_DEVICE, "enslaved to %s", nm_device_get_iface (priv->master));
 
 			priv->is_enslaved = TRUE;
+
+			if (   NM_IN_SET_TYPED (NMDeviceSysIfaceState,
+			                        priv->sys_iface_state,
+			                        NM_DEVICE_SYS_IFACE_STATE_EXTERNAL,
+			                        NM_DEVICE_SYS_IFACE_STATE_ASSUME)
+			    && nm_device_sys_iface_state_get (priv->master) == NM_DEVICE_SYS_IFACE_STATE_MANAGED)
+				nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
+
 			_notify (self, PROP_MASTER);
 			_notify (priv->master, PROP_SLAVES);
 		} else if (activating) {
@@ -3243,15 +3637,19 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason)
 
 	if (   priv->state > NM_DEVICE_STATE_DISCONNECTED
 	    && priv->state <= NM_DEVICE_STATE_ACTIVATED) {
-		if (reason == NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED) {
+		switch (nm_device_state_reason_check (reason)) {
+		case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
 			new_state = NM_DEVICE_STATE_FAILED;
 			master_status = "failed";
-		} else if (reason == NM_DEVICE_STATE_REASON_USER_REQUESTED) {
+			break;
+		case NM_DEVICE_STATE_REASON_USER_REQUESTED:
 			new_state = NM_DEVICE_STATE_DEACTIVATING;
 			master_status = "deactivated by user request";
-		} else {
+			break;
+		default:
 			new_state = NM_DEVICE_STATE_DISCONNECTED;
 			master_status = "deactivated";
+			break;
 		}
 
 		_LOGD (LOGD_DEVICE, "Activation: connection '%s' master %s",
@@ -3319,8 +3717,8 @@ nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config)
 	_update_default_route (self, AF_INET6, priv->default_route.v6_has, TRUE);
 	_update_default_route (self, AF_INET,  FALSE, TRUE);
 	_update_default_route (self, AF_INET6, FALSE, TRUE);
-	nm_device_set_ip4_config (self, NULL, 0, FALSE, FALSE, NULL);
-	nm_device_set_ip6_config (self, NULL, FALSE, FALSE, NULL);
+	nm_device_set_ip4_config (self, NULL, 0, FALSE, FALSE);
+	nm_device_set_ip6_config (self, NULL, FALSE, FALSE);
 }
 
 static gboolean
@@ -3398,25 +3796,50 @@ nm_device_set_enabled (NMDevice *self, gboolean enabled)
 gboolean
 nm_device_get_autoconnect (NMDevice *self)
 {
+	NMDevicePrivate *priv;
+
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
-	return NM_DEVICE_GET_PRIVATE (self)->autoconnect;
+	priv = NM_DEVICE_GET_PRIVATE (self);
+	return priv->autoconnect_intern && priv->autoconnect_user;
 }
 
-void
-nm_device_set_autoconnect (NMDevice *self, gboolean autoconnect)
+static void
+nm_device_set_autoconnect_full (NMDevice *self, int autoconnect_intern, int autoconnect_user)
 {
 	NMDevicePrivate *priv;
+	gboolean old_value;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
-	autoconnect = !!autoconnect;
-
 	priv = NM_DEVICE_GET_PRIVATE (self);
-	if (priv->autoconnect != autoconnect) {
-		priv->autoconnect = autoconnect;
+
+	old_value = nm_device_get_autoconnect (self);
+	if (autoconnect_intern != -1)
+		priv->autoconnect_intern = autoconnect_intern;
+	if (autoconnect_user != -1)
+		priv->autoconnect_user = autoconnect_user;
+	if (old_value != nm_device_get_autoconnect (self))
 		_notify (self, PROP_AUTOCONNECT);
-	}
+}
+
+void
+nm_device_set_autoconnect_intern (NMDevice *self, gboolean autoconnect)
+{
+	nm_device_set_autoconnect_full (self, !!autoconnect, -1);
+}
+
+static void
+nm_device_set_autoconnect_both (NMDevice *self, gboolean autoconnect)
+{
+	autoconnect = !!autoconnect;
+	nm_device_set_autoconnect_full (self, autoconnect, autoconnect);
+}
+
+static gboolean
+get_autoconnect_allowed (NMDevice *self)
+{
+	return TRUE;
 }
 
 static gboolean
@@ -3441,10 +3864,12 @@ gboolean
 nm_device_autoconnect_allowed (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
 	GValue instance = G_VALUE_INIT;
 	GValue retval = G_VALUE_INIT;
 
-	if (!priv->autoconnect)
+	if (   !nm_device_get_autoconnect (self)
+	    || !klass->get_autoconnect_allowed (self))
 		return FALSE;
 
 	/* Unrealized devices can always autoconnect. */
@@ -3535,7 +3960,7 @@ device_has_config (NMDevice *self)
 		return TRUE;
 
 	/* Master-slave relationship is also a configuration */
-	if (priv->slaves || nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) > 0)
+	if (priv->slaves || nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) > 0)
 		return TRUE;
 
 	return FALSE;
@@ -3598,7 +4023,7 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master)
 	NMSetting *s_con;
 	NMSetting *s_ip4;
 	NMSetting *s_ip6;
-	gs_free char *uuid = NULL;
+	char uuid[37];
 	const char *ip4_method, *ip6_method;
 	GError *error = NULL;
 	const NMPlatformLink *pllink;
@@ -3615,10 +4040,9 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master)
 
 	connection = nm_simple_connection_new ();
 	s_con = nm_setting_connection_new ();
-	uuid = nm_utils_uuid_generate ();
 
 	g_object_set (s_con,
-	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_buf (uuid),
 	              NM_SETTING_CONNECTION_ID, ifname,
 	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
 	              NM_SETTING_CONNECTION_INTERFACE_NAME, ifname,
@@ -3649,7 +4073,7 @@ nm_device_generate_connection (NMDevice *self, NMDevice *master)
 		s_ip6 = nm_ip6_config_create_setting (priv->ip6_config);
 		nm_connection_add_setting (connection, s_ip6);
 
-		pllink = nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex);
+		pllink = nm_platform_link_get (nm_device_get_platform (self), priv->ifindex);
 		if (pllink && pllink->inet6_token.id) {
 			_LOGD (LOGD_IP6, "IPv6 tokenized identifier present");
 			g_object_set (s_ip6,
@@ -3889,12 +4313,14 @@ recheck_available (gpointer user_data)
 {
 	NMDevice *self = NM_DEVICE (user_data);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	gboolean now_available = nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
+	gboolean now_available;
 	NMDeviceState state = nm_device_get_state (self);
 	NMDeviceState new_state = NM_DEVICE_STATE_UNKNOWN;
 
 	priv->recheck_available.call_id = 0;
 
+	now_available = nm_device_is_available (self, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE);
+
 	if (state == NM_DEVICE_STATE_UNAVAILABLE && now_available) {
 		new_state = NM_DEVICE_STATE_DISCONNECTED;
 		nm_device_queue_state (self, new_state, priv->recheck_available.available_reason);
@@ -4101,7 +4527,7 @@ get_ip_config_may_fail (NMDevice *self, int family)
 		g_assert_not_reached ();
 	}
 
-	return nm_setting_ip_config_get_may_fail (s_ip);
+	return !s_ip || nm_setting_ip_config_get_may_fail (s_ip);
 }
 
 static void
@@ -4133,7 +4559,7 @@ master_ready (NMDevice *self,
 	/* If the master didn't change, add-slave only rechecks whether to assume a connection. */
 	nm_device_master_add_slave (master,
 	                            self,
-	                            nm_active_connection_get_assumed (active) ? FALSE : TRUE);
+	                            !nm_device_sys_iface_state_is_external_or_assume (self));
 }
 
 static void
@@ -4185,7 +4611,7 @@ lldp_rx_enabled (NMDevice *self)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason)
 {
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
@@ -4201,8 +4627,6 @@ activate_stage1_device_prepare (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
-	NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request);
 
 	_set_ip_state (self, AF_INET, IP_NONE);
 	_set_ip_state (self, AF_INET6, IP_NONE);
@@ -4214,15 +4638,17 @@ activate_stage1_device_prepare (NMDevice *self)
 	nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
 
 	/* Assumed connections were already set up outside NetworkManager */
-	if (!nm_active_connection_get_assumed (active)) {
-		ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason);
+	if (!nm_device_sys_iface_state_is_external_or_assume (self)) {
+		NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
+
+		ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &failure_reason);
 		if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
 			return;
 		} else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
-			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, failure_reason);
 			return;
 		}
-		g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
+		g_return_if_fail (ret == NM_ACT_STAGE_RETURN_SUCCESS);
 	}
 
 	nm_device_activate_schedule_stage2_device_config (self);
@@ -4249,12 +4675,48 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self)
 }
 
 static NMActStageReturn
-act_stage2_config (NMDevice *self, NMDeviceStateReason *reason)
+act_stage2_config (NMDevice *self, NMDeviceStateReason *out_failure_reason)
 {
-	/* Nothing to do */
 	return NM_ACT_STAGE_RETURN_SUCCESS;
 }
 
+static void
+lldp_init (NMDevice *self, gboolean restart)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (priv->ifindex > 0 && lldp_rx_enabled (self)) {
+		gs_free_error GError *error = NULL;
+		gconstpointer addr;
+		size_t addr_length;
+
+		if (priv->lldp_listener) {
+			if (restart && nm_lldp_listener_is_running (priv->lldp_listener))
+				nm_lldp_listener_stop (priv->lldp_listener);
+		} else {
+			priv->lldp_listener = nm_lldp_listener_new ();
+			g_signal_connect (priv->lldp_listener,
+			                  "notify::" NM_LLDP_LISTENER_NEIGHBORS,
+			                  G_CALLBACK (lldp_neighbors_changed),
+			                  self);
+		}
+
+		if (!nm_lldp_listener_is_running (priv->lldp_listener)) {
+			addr = nm_platform_link_get_address (nm_device_get_platform (self), priv->ifindex, &addr_length);
+
+			if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error))
+				_LOGD (LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener);
+			else {
+				_LOGD (LOGD_DEVICE, "LLDP listener %p could not be started: %s",
+				       priv->lldp_listener, error->message);
+			}
+		}
+	} else {
+		if (priv->lldp_listener)
+			nm_lldp_listener_stop (priv->lldp_listener);
+	}
+}
+
 /*
  * activate_stage2_device_config
  *
@@ -4267,15 +4729,15 @@ activate_stage2_device_config (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActStageReturn ret;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 	gboolean no_firmware = FALSE;
-	NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request);
 	GSList *iter;
 
 	nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
 
 	/* Assumed connections were already set up outside NetworkManager */
-	if (!nm_active_connection_get_assumed (active)) {
+	if (!nm_device_sys_iface_state_is_external_or_assume (self)) {
+		NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
+
 		if (!nm_device_bring_up (self, FALSE, &no_firmware)) {
 			if (no_firmware)
 				nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_FIRMWARE_MISSING);
@@ -4284,11 +4746,11 @@ activate_stage2_device_config (NMDevice *self)
 			return;
 		}
 
-		ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &reason);
+		ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &failure_reason);
 		if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
 			return;
 		else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
-			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, failure_reason);
 			return;
 		}
 		g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
@@ -4301,36 +4763,13 @@ activate_stage2_device_config (NMDevice *self)
 
 		if (slave_state == NM_DEVICE_STATE_IP_CONFIG)
 			nm_device_master_enslave_slave (self, info->slave, nm_device_get_applied_connection (info->slave));
-		else if (   nm_device_uses_generated_assumed_connection (self)
+		else if (   priv->act_request
+		         && nm_device_sys_iface_state_is_external (self)
 		         && slave_state <= NM_DEVICE_STATE_DISCONNECTED)
 			nm_device_queue_recheck_assume (info->slave);
 	}
 
-	if (lldp_rx_enabled (self) && priv->ifindex > 0) {
-		gs_free_error GError *error = NULL;
-		gconstpointer addr;
-		size_t addr_length;
-
-		if (priv->lldp_listener)
-			nm_lldp_listener_stop (priv->lldp_listener);
-		else {
-			priv->lldp_listener = nm_lldp_listener_new ();
-			g_signal_connect (priv->lldp_listener,
-			                  "notify::" NM_LLDP_LISTENER_NEIGHBORS,
-			                  G_CALLBACK (lldp_neighbors_changed),
-			                  self);
-		}
-
-		addr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &addr_length);
-
-		if (nm_lldp_listener_start (priv->lldp_listener, nm_device_get_ifindex (self), &error))
-			_LOGD (LOGD_DEVICE, "LLDP listener %p started", priv->lldp_listener);
-		else {
-			_LOGD (LOGD_DEVICE, "LLDP listener %p could not be started: %s",
-			       priv->lldp_listener, error->message);
-		}
-	}
-
+	lldp_init (self, TRUE);
 	nm_device_activate_schedule_stage3_ip_config_start (self);
 }
 
@@ -4426,7 +4865,7 @@ check_ip_state (NMDevice *self, gboolean may_fail)
 	    && (priv->ip6_state == IP_FAIL || (ip6_ignore && priv->ip6_state == IP_DONE))) {
 		/* Either both methods failed, or only one failed and the other is
 		 * disabled */
-		if (nm_device_uses_assumed_connection (self)) {
+		if (nm_device_sys_iface_state_is_external_or_assume (self)) {
 			/* We have assumed configuration, but couldn't redo it. No problem,
 			 * move to check state. */
 			_set_ip_state (self, AF_INET, IP_DONE);
@@ -4610,7 +5049,7 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb)
 	}
 
 	timeout = get_ipv4_dad_timeout (self);
-	hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET,
+	hw_addr = nm_platform_link_get_address (nm_device_get_platform (self),
 	                                        nm_device_get_ip_ifindex (self),
 	                                        &hw_addr_len);
 
@@ -4618,7 +5057,7 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, ArpingCallback cb)
 	    || !hw_addr
 	    || !hw_addr_len
 	    || !addr_found
-	    || nm_device_uses_assumed_connection (self)) {
+	    || nm_device_sys_iface_state_is_external_or_assume (self)) {
 
 		/* DAD not needed, signal success */
 		cb (self, configs, TRUE);
@@ -4759,7 +5198,7 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data)
 			nm_clear_g_source (&priv->ipv4ll_timeout);
 			nm_device_activate_schedule_ip4_config_result (self, config);
 		} else if (priv->ip4_state == IP_DONE) {
-			if (!ip4_config_merge_and_apply (self, config, TRUE, NULL)) {
+			if (!ip4_config_merge_and_apply (self, config, TRUE)) {
 				_LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change.");
 				nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED);
 			}
@@ -4793,7 +5232,7 @@ ipv4ll_timeout_cb (gpointer user_data)
 }
 
 static NMActStageReturn
-ipv4ll_start (NMDevice *self, NMDeviceStateReason *reason)
+ipv4ll_start (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	const struct ether_addr *addr;
@@ -4805,55 +5244,51 @@ ipv4ll_start (NMDevice *self, NMDeviceStateReason *reason)
 	r = sd_ipv4ll_new (&priv->ipv4ll);
 	if (r < 0) {
 		_LOGE (LOGD_AUTOIP4, "IPv4LL: new() failed with error %d", r);
-		goto fail;
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	r = sd_ipv4ll_attach_event (priv->ipv4ll, NULL, 0);
 	if (r < 0) {
 		_LOGE (LOGD_AUTOIP4, "IPv4LL: attach_event() failed with error %d", r);
-		goto fail;
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	ifindex = nm_device_get_ip_ifindex (self);
-	addr = nm_platform_link_get_address (NM_PLATFORM_GET, ifindex, &addr_len);
+	addr = nm_platform_link_get_address (nm_device_get_platform (self), ifindex, &addr_len);
 	if (!addr || addr_len != ETH_ALEN) {
 		_LOGE (LOGD_AUTOIP4, "IPv4LL: can't retrieve hardware address");
-		goto fail;
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	r = sd_ipv4ll_set_mac (priv->ipv4ll, addr);
 	if (r < 0) {
 		_LOGE (LOGD_AUTOIP4, "IPv4LL: set_mac() failed with error %d", r);
-		goto fail;
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	r = sd_ipv4ll_set_ifindex (priv->ipv4ll, ifindex);
 	if (r < 0) {
 		_LOGE (LOGD_AUTOIP4, "IPv4LL: set_ifindex() failed with error %d", r);
-		goto fail;
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	r = sd_ipv4ll_set_callback (priv->ipv4ll, nm_device_handle_ipv4ll_event, self);
 	if (r < 0) {
 		_LOGE (LOGD_AUTOIP4, "IPv4LL: set_callback() failed with error %d", r);
-		goto fail;
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	r = sd_ipv4ll_start (priv->ipv4ll);
 	if (r < 0) {
 		_LOGE (LOGD_AUTOIP4, "IPv4LL: start() failed with error %d", r);
-		goto fail;
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	_LOGI (LOGD_DEVICE | LOGD_AUTOIP4, "IPv4LL: started");
 
 	/* Start a timeout to bound the address attempt */
 	priv->ipv4ll_timeout = g_timeout_add_seconds (20, ipv4ll_timeout_cb, self);
-
 	return NM_ACT_STAGE_RETURN_POSTPONE;
-fail:
-	*reason = NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED;
-	return NM_ACT_STAGE_RETURN_FAILURE;
 }
 
 /*****************************************************************************/
@@ -4866,9 +5301,9 @@ _device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlat
 	GArray *routes;
 
 	if (addr_family == AF_INET)
-		routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
+		routes = nm_platform_ip4_route_get_all (nm_device_get_platform (self), ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
 	else
-		routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
+		routes = nm_platform_ip6_route_get_all (nm_device_get_platform (self), ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT);
 
 	if (routes) {
 		guint route_metric = G_MAXUINT32, m;
@@ -4923,7 +5358,7 @@ ensure_con_ip4_config (NMDevice *self)
 	                             nm_connection_get_setting_ip4_config (connection),
 	                             nm_device_get_ip4_route_metric (self));
 
-	if (nm_device_uses_assumed_connection (self)) {
+	if (nm_device_sys_iface_state_is_external_or_assume (self)) {
 		/* For assumed connections ignore all addresses and routes. */
 		nm_ip4_config_reset_addresses (priv->con_ip4_config);
 		nm_ip4_config_reset_routes (priv->con_ip4_config);
@@ -4949,7 +5384,7 @@ ensure_con_ip6_config (NMDevice *self)
 	                             nm_connection_get_setting_ip6_config (connection),
 	                             nm_device_get_ip6_route_metric (self));
 
-	if (nm_device_uses_assumed_connection (self)) {
+	if (nm_device_sys_iface_state_is_external_or_assume (self)) {
 		/* For assumed connections ignore all addresses and routes. */
 		nm_ip6_config_reset_addresses (priv->con_ip6_config);
 		nm_ip6_config_reset_routes (priv->con_ip6_config);
@@ -4965,6 +5400,7 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release)
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	nm_clear_g_source (&priv->dhcp4.restart_id);
+	g_clear_pointer (&priv->dhcp4.pac_url, g_free);
 
 	if (priv->dhcp4.client) {
 		/* Stop any ongoing DHCP transaction on this device */
@@ -4997,8 +5433,7 @@ _ip4_config_merge_default (gpointer value, gpointer user_data)
 static gboolean
 ip4_config_merge_and_apply (NMDevice *self,
                             NMIP4Config *config,
-                            gboolean commit,
-                            NMDeviceStateReason *out_reason)
+                            gboolean commit)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
@@ -5091,7 +5526,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 	 * but if the IP method is automatic we need to update the default route to
 	 * maintain connectivity.
 	 */
-	if (nm_device_uses_generated_assumed_connection (self) && !auto_method)
+	if (nm_device_sys_iface_state_is_external (self) && !auto_method)
 		goto END_ADD_DEFAULT_ROUTE;
 
 	/* At this point, we treat assumed and non-assumed connections alike.
@@ -5100,7 +5535,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 	 */
 
 	connection_has_default_route
-	    = nm_default_route_manager_ip4_connection_has_default_route (nm_default_route_manager_get (),
+	    = nm_default_route_manager_ip4_connection_has_default_route (nm_netns_get_default_route_manager (priv->netns),
 	                                                                 connection, &connection_is_never_default);
 
 	if (   !priv->v4_commit_first_time
@@ -5136,7 +5571,7 @@ ip4_config_merge_and_apply (NMDevice *self,
 	memset (&priv->default_route.v4, 0, sizeof (priv->default_route.v4));
 	priv->default_route.v4.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	priv->default_route.v4.gateway = gateway;
-	priv->default_route.v4.metric = default_route_metric;
+	priv->default_route.v4.metric = route_metric_with_penalty (self, default_route_metric);
 	priv->default_route.v4.mss = nm_ip4_config_get_mss (composite);
 
 	if (!has_direct_route) {
@@ -5167,9 +5602,9 @@ END_ADD_DEFAULT_ROUTE:
 
 	routes_full_sync =    commit
 	                   && priv->v4_commit_first_time
-	                   && !nm_device_uses_assumed_connection (self);
+	                   && !nm_device_sys_iface_state_is_external_or_assume (self);
 
-	success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, routes_full_sync, out_reason);
+	success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, routes_full_sync);
 	g_object_unref (composite);
 
 	if (commit)
@@ -5180,23 +5615,17 @@ END_ADD_DEFAULT_ROUTE:
 static gboolean
 dhcp4_lease_change (NMDevice *self, NMIP4Config *config)
 {
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	g_return_val_if_fail (config, FALSE);
 
-	g_return_val_if_fail (config != NULL, FALSE);
-
-	if (!ip4_config_merge_and_apply (self, config, TRUE, &reason)) {
+	if (!ip4_config_merge_and_apply (self, config, TRUE)) {
 		_LOGW (LOGD_DHCP4, "failed to update IPv4 config for DHCP change.");
 		return FALSE;
 	}
 
-	/* Notify dispatcher scripts of new DHCP4 config */
-	nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE,
-	                    nm_device_get_settings_connection (self),
-	                    nm_device_get_applied_connection (self),
-	                    self,
-	                    NULL,
-	                    NULL,
-	                    NULL);
+	nm_dispatcher_call_device (NM_DISPATCHER_ACTION_DHCP4_CHANGE,
+	                           self,
+	                           NULL,
+	                           NULL, NULL, NULL);
 
 	nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP4, FALSE);
 
@@ -5208,7 +5637,6 @@ dhcp4_restart_cb (gpointer user_data)
 {
 	NMDevice *self = user_data;
 	NMDevicePrivate *priv;
-	NMDeviceStateReason reason;
 	NMConnection *connection;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
@@ -5217,7 +5645,7 @@ dhcp4_restart_cb (gpointer user_data)
 	priv->dhcp4.restart_id = 0;
 	connection = nm_device_get_applied_connection (self);
 
-	if (dhcp4_start (self, connection, &reason) == NM_ACT_STAGE_RETURN_FAILURE)
+	if (dhcp4_start (self, connection) == NM_ACT_STAGE_RETURN_FAILURE)
 		dhcp_schedule_restart (self, AF_INET, NULL);
 
 	return FALSE;
@@ -5243,19 +5671,11 @@ dhcp4_fail (NMDevice *self, gboolean timeout)
 		return;
 	}
 
-	/* Instead of letting an assumed connection fail (which means that the
-	 * device will transition to the ACTIVATED state without IP configuration),
-	 * retry DHCP again.
-	 */
-	if (nm_device_uses_assumed_connection (self)) {
-		dhcp_schedule_restart (self, AF_INET, "connection is assumed");
-		return;
-	}
-
 	if (   priv->dhcp4.num_tries_left == DHCP_NUM_TRIES_MAX
-	    && (timeout || (priv->ip4_state == IP_CONF)))
+	    && (timeout || (priv->ip4_state == IP_CONF))
+	    && !priv->dhcp4.was_active)
 		nm_device_activate_schedule_ip4_config_timeout (self);
-	else if (priv->ip4_state == IP_DONE) {
+	else if (priv->ip4_state == IP_DONE || priv->dhcp4.was_active) {
 		/* Don't fail immediately when the lease expires but try to
 		 * restart DHCP for a predefined number of times.
 		 */
@@ -5305,7 +5725,9 @@ dhcp4_state_changed (NMDhcpClient *client,
 			break;
 		}
 
-		nm_device_set_proxy_config (self, options);
+		g_free (priv->dhcp4.pac_url);
+		priv->dhcp4.pac_url = g_strdup (g_hash_table_lookup (options, "wpad"));
+		nm_device_set_proxy_config (self, priv->dhcp4.pac_url);
 
 		nm_dhcp4_config_set_options (priv->dhcp4.config, options);
 		_notify (self, PROP_DHCP4_CONFIG);
@@ -5373,8 +5795,7 @@ dhcp4_get_timeout (NMDevice *self, NMSettingIP4Config *s_ip4)
 
 static NMActStageReturn
 dhcp4_start (NMDevice *self,
-             NMConnection *connection,
-             NMDeviceStateReason *reason)
+             NMConnection *connection)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMSettingIPConfig *s_ip4;
@@ -5388,7 +5809,7 @@ dhcp4_start (NMDevice *self,
 	nm_exported_object_clear_and_unexport (&priv->dhcp4.config);
 	priv->dhcp4.config = nm_dhcp4_config_new ();
 
-	hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), &hw_addr_len);
+	hw_addr = nm_platform_link_get_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), &hw_addr_len);
 	if (hw_addr_len) {
 		tmp = g_byte_array_sized_new (hw_addr_len);
 		g_byte_array_append (tmp, hw_addr, hw_addr_len);
@@ -5413,10 +5834,8 @@ dhcp4_start (NMDevice *self,
 	if (tmp)
 		g_byte_array_free (tmp, TRUE);
 
-	if (!priv->dhcp4.client) {
-		*reason = NM_DEVICE_STATE_REASON_DHCP_START_FAILED;
+	if (!priv->dhcp4.client)
 		return NM_ACT_STAGE_RETURN_FAILURE;
-	}
 
 	priv->dhcp4.state_sigid = g_signal_connect (priv->dhcp4.client,
 	                                            NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
@@ -5425,6 +5844,9 @@ dhcp4_start (NMDevice *self,
 
 	nm_device_add_pending_action (self, NM_PENDING_ACTION_DHCP4, TRUE);
 
+	if (nm_device_sys_iface_state_is_external_or_assume (self))
+		priv->dhcp4.was_active = TRUE;
+
 	/* DHCP devices will be notified by the DHCP manager when stuff happens */
 	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
@@ -5433,8 +5855,6 @@ gboolean
 nm_device_dhcp4_renew (NMDevice *self, gboolean release)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
-	NMActStageReturn ret;
-	NMDeviceStateReason reason;
 	NMConnection *connection;
 
 	g_return_val_if_fail (priv->dhcp4.client != NULL, FALSE);
@@ -5445,12 +5865,10 @@ nm_device_dhcp4_renew (NMDevice *self, gboolean release)
 	dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, release);
 
 	connection = nm_device_get_applied_connection (self);
-	g_assert (connection);
+	g_return_val_if_fail (connection, FALSE);
 
 	/* Start DHCP again on the interface */
-	ret = dhcp4_start (self, connection, &reason);
-
-	return (ret != NM_ACT_STAGE_RETURN_FAILURE);
+	return dhcp4_start (self, connection) != NM_ACT_STAGE_RETURN_FAILURE;
 }
 
 /*****************************************************************************/
@@ -5499,24 +5917,22 @@ reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Addres
 }
 
 static NMIP4Config *
-shared4_new_config (NMDevice *self, NMConnection *connection, NMDeviceStateReason *reason)
+shared4_new_config (NMDevice *self, NMConnection *connection)
 {
 	NMIP4Config *config = NULL;
 	NMPlatformIP4Address address;
 
 	g_return_val_if_fail (self != NULL, NULL);
 
-	if (!reserve_shared_ip (self, nm_connection_get_setting_ip4_config (connection), &address)) {
-		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+	if (!reserve_shared_ip (self, nm_connection_get_setting_ip4_config (connection), &address))
 		return NULL;
-	}
 
 	config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
 	address.addr_source = NM_IP_CONFIG_SOURCE_SHARED;
 	nm_ip4_config_add_address (config, &address);
 
 	/* Remove the address lock when the object gets disposed */
-	g_object_set_data_full (G_OBJECT (config), "shared-ip",
+	g_object_set_qdata_full (G_OBJECT (config), NM_CACHED_QUARK ("shared-ip"),
 	                        GUINT_TO_POINTER (address.address),
 	                        release_shared_ip);
 
@@ -5586,7 +6002,7 @@ connection_requires_carrier (NMConnection *connection)
 			return TRUE;
 	}
 
-	/* If an IP version wants a carrier and and the other IP version isn't
+	/* If an IP version wants a carrier and the other IP version isn't
 	 * used, the connection requires carrier since it will just fail without one.
 	 */
 	if (ip4_carrier_wanted && !ip6_used)
@@ -5626,7 +6042,7 @@ ip4_requires_slaves (NMConnection *connection)
 static NMActStageReturn
 act_stage3_ip4_config_start (NMDevice *self,
                              NMIP4Config **out_config,
-                             NMDeviceStateReason *reason)
+                             NMDeviceStateReason *out_failure_reason)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
@@ -5635,10 +6051,8 @@ act_stage3_ip4_config_start (NMDevice *self,
 	GSList *slaves;
 	gboolean ready_slaves;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
 	connection = nm_device_get_applied_connection (self);
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
 	if (   connection_ip4_method_requires_carrier (connection, NULL)
 	    && priv->is_master
@@ -5667,11 +6081,15 @@ act_stage3_ip4_config_start (NMDevice *self,
 	priv->dhcp4.num_tries_left = DHCP_NUM_TRIES_MAX;
 
 	/* Start IPv4 addressing based on the method requested */
-	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0)
-		ret = dhcp4_start (self, connection, reason);
-	else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0)
-		ret = ipv4ll_start (self, reason);
-	else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) {
+	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) {
+		ret = dhcp4_start (self, connection);
+		if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_DHCP_START_FAILED);
+	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) == 0) {
+		ret = ipv4ll_start (self);
+		if (ret == NM_ACT_STAGE_RETURN_FAILURE)
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED);
+	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL) == 0) {
 		NMIP4Config **configs, *config;
 
 		config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
@@ -5685,12 +6103,14 @@ act_stage3_ip4_config_start (NMDevice *self,
 		ret = NM_ACT_STAGE_RETURN_POSTPONE;
 	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) {
 		if (out_config) {
-			*out_config = shared4_new_config (self, connection, reason);
+			*out_config = shared4_new_config (self, connection);
 			if (*out_config) {
 				priv->dnsmasq_manager = nm_dnsmasq_manager_new (nm_device_get_ip_iface (self));
 				ret = NM_ACT_STAGE_RETURN_SUCCESS;
-			} else
+			} else {
+				NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 				ret = NM_ACT_STAGE_RETURN_FAILURE;
+			}
 		} else
 			g_return_val_if_reached (NM_ACT_STAGE_RETURN_FAILURE);
 	} else if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
@@ -5744,8 +6164,7 @@ _ip6_config_merge_default (gpointer value, gpointer user_data)
 
 static gboolean
 ip6_config_merge_and_apply (NMDevice *self,
-                            gboolean commit,
-                            NMDeviceStateReason *out_reason)
+                            gboolean commit)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
@@ -5849,7 +6268,7 @@ ip6_config_merge_and_apply (NMDevice *self,
 	 * but if the IP method is automatic we need to update the default route to
 	 * maintain connectivity.
 	 */
-	if (nm_device_uses_generated_assumed_connection (self) && !auto_method)
+	if (nm_device_sys_iface_state_is_external (self) && !auto_method)
 		goto END_ADD_DEFAULT_ROUTE;
 
 	/* At this point, we treat assumed and non-assumed connections alike.
@@ -5858,7 +6277,7 @@ ip6_config_merge_and_apply (NMDevice *self,
 	 */
 
 	connection_has_default_route
-	    = nm_default_route_manager_ip6_connection_has_default_route (nm_default_route_manager_get (),
+	    = nm_default_route_manager_ip6_connection_has_default_route (nm_netns_get_default_route_manager (priv->netns),
 	                                                                 connection, &connection_is_never_default);
 
 	if (   !priv->v6_commit_first_time
@@ -5894,7 +6313,8 @@ ip6_config_merge_and_apply (NMDevice *self,
 	memset (&priv->default_route.v6, 0, sizeof (priv->default_route.v6));
 	priv->default_route.v6.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	priv->default_route.v6.gateway = *gateway;
-	priv->default_route.v6.metric = nm_device_get_ip6_route_metric (self);
+	priv->default_route.v6.metric = route_metric_with_penalty (self,
+	                                                           nm_device_get_ip6_route_metric (self));
 	priv->default_route.v6.mss = nm_ip6_config_get_mss (composite);
 
 	if (!has_direct_route) {
@@ -5923,7 +6343,7 @@ END_ADD_DEFAULT_ROUTE:
 		NMUtilsIPv6IfaceId iid;
 
 		if (token && nm_utils_ipv6_interface_identifier_get_from_token (&iid, token)) {
-			nm_platform_link_set_ipv6_token (NM_PLATFORM_GET,
+			nm_platform_link_set_ipv6_token (nm_device_get_platform (self),
 			                                 nm_device_get_ip_ifindex (self),
 			                                 iid);
 		}
@@ -5931,9 +6351,9 @@ END_ADD_DEFAULT_ROUTE:
 
 	routes_full_sync =    commit
 	                   && priv->v6_commit_first_time
-	                   && !nm_device_uses_assumed_connection (self);
+	                   && !nm_device_sys_iface_state_is_external_or_assume (self);
 
-	success = nm_device_set_ip6_config (self, composite, commit, routes_full_sync, out_reason);
+	success = nm_device_set_ip6_config (self, composite, commit, routes_full_sync);
 	g_object_unref (composite);
 	if (commit)
 		priv->v6_commit_first_time = FALSE;
@@ -5945,7 +6365,6 @@ dhcp6_lease_change (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMSettingsConnection *settings_connection;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 
 	if (priv->dhcp6.ip6_config == NULL) {
 		_LOGW (LOGD_DHCP6, "failed to get DHCPv6 config for rebind");
@@ -5958,16 +6377,15 @@ dhcp6_lease_change (NMDevice *self)
 	g_assert (settings_connection);
 
 	/* Apply the updated config */
-	if (!ip6_config_merge_and_apply (self, TRUE, &reason)) {
+	if (!ip6_config_merge_and_apply (self, TRUE)) {
 		_LOGW (LOGD_DHCP6, "failed to update IPv6 config in response to DHCP event");
 		return FALSE;
 	}
 
-	/* Notify dispatcher scripts of new DHCPv6 config */
-	nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE,
-	                    settings_connection,
-	                    nm_device_get_applied_connection (self),
-	                    self, NULL, NULL, NULL);
+	nm_dispatcher_call_device (NM_DISPATCHER_ACTION_DHCP6_CHANGE,
+	                           self,
+	                           NULL,
+	                           NULL, NULL, NULL);
 
 	nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP6, FALSE);
 
@@ -5979,14 +6397,13 @@ dhcp6_restart_cb (gpointer user_data)
 {
 	NMDevice *self = user_data;
 	NMDevicePrivate *priv;
-	NMDeviceStateReason reason;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 	priv->dhcp6.restart_id = 0;
 
-	if (!dhcp6_start (self, FALSE, &reason))
+	if (!dhcp6_start (self, FALSE))
 		dhcp_schedule_restart (self, AF_INET6, NULL);
 
 	return FALSE;
@@ -6044,19 +6461,11 @@ dhcp6_fail (NMDevice *self, gboolean timeout)
 			return;
 		}
 
-		/* Instead of letting an assumed connection fail (which means that the
-		 * device will transition to the ACTIVATED state without IP configuration),
-		 * retry DHCP again.
-		 */
-		if (nm_device_uses_assumed_connection (self)) {
-			dhcp_schedule_restart (self, AF_INET6, "connection is assumed");
-			return;
-		}
-
 		if (   priv->dhcp6.num_tries_left == DHCP_NUM_TRIES_MAX
-		    && (timeout || (priv->ip6_state == IP_CONF)))
+		    && (timeout || (priv->ip6_state == IP_CONF))
+		    && !priv->dhcp6.was_active)
 			nm_device_activate_schedule_ip6_config_timeout (self);
-		else if (priv->ip6_state == IP_DONE) {
+		else if (priv->ip6_state == IP_DONE || priv->dhcp6.was_active) {
 			/* Don't fail immediately when the lease expires but try to
 			 * restart DHCP for a predefined number of times.
 			 */
@@ -6194,17 +6603,20 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	g_assert (s_ip6);
 
-	hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), &hw_addr_len);
+	if (priv->ext_ip6_config_captured)
+		ll_addr = nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE);
+
+	if (!ll_addr) {
+		_LOGW (LOGD_DHCP6, "can't start DHCPv6: no link-local address");
+		return FALSE;
+	}
+
+	hw_addr = nm_platform_link_get_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), &hw_addr_len);
 	if (hw_addr_len) {
 		tmp = g_byte_array_sized_new (hw_addr_len);
 		g_byte_array_append (tmp, hw_addr, hw_addr_len);
 	}
 
-	if (priv->ext_ip6_config_captured)
-		ll_addr = nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE);
-
-	g_return_val_if_fail (ll_addr, FALSE);
-
 	priv->dhcp6.client = nm_dhcp_manager_start_ip6 (nm_dhcp_manager_get (),
 	                                                nm_device_get_ip_iface (self),
 	                                                nm_device_get_ip_ifindex (self),
@@ -6233,11 +6645,14 @@ dhcp6_start_with_link_ready (NMDevice *self, NMConnection *connection)
 		                                             self);
 	}
 
+	if (nm_device_sys_iface_state_is_external_or_assume (self))
+		priv->dhcp4.was_active = TRUE;
+
 	return !!priv->dhcp6.client;
 }
 
 static gboolean
-dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason)
+dhcp6_start (NMDevice *self, gboolean wait_for_ll)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *connection;
@@ -6271,10 +6686,8 @@ dhcp6_start (NMDevice *self, gboolean wait_for_ll, NMDeviceStateReason *reason)
 		g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
 	}
 
-	if (!dhcp6_start_with_link_ready (self, connection)) {
-		NM_SET_OUT (reason, NM_DEVICE_STATE_REASON_DHCP_START_FAILED);
+	if (!dhcp6_start_with_link_ready (self, connection))
 		return FALSE;
-	}
 
 	return TRUE;
 }
@@ -6292,7 +6705,7 @@ nm_device_dhcp6_renew (NMDevice *self, gboolean release)
 	dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, release);
 
 	/* Start DHCP again on the interface */
-	return dhcp6_start (self, FALSE, NULL);
+	return dhcp6_start (self, FALSE);
 }
 
 /*****************************************************************************/
@@ -6344,7 +6757,7 @@ nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet)
 	       subnet->preferred);
 
 	/* This also updates the ndisc if there are actual changes. */
-	if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+	if (!ip6_config_merge_and_apply (self, TRUE))
 		_LOGW (LOGD_IP6, "ipv6-pd: failed applying IP6 config for connection sharing");
 }
 
@@ -6380,7 +6793,7 @@ nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device)
 		                              nm_ip6_config_get_search (from_config, i));
 	}
 
-	if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+	if (!ip6_config_merge_and_apply (self, TRUE))
 		_LOGW (LOGD_IP6, "ipv6-pd: failed applying DNS config for connection sharing");
 }
 
@@ -6419,7 +6832,8 @@ linklocal6_complete (NMDevice *self)
 	const char *method;
 
 	g_assert (priv->linklocal6_timeout_id);
-	g_assert (nm_ip6_config_get_address_first_nontentative (priv->ip6_config, TRUE));
+	g_assert (priv->ext_ip6_config_captured);
+	g_assert (nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE));
 
 	linklocal6_cleanup (self);
 
@@ -6521,7 +6935,7 @@ check_and_add_ipv6ll_addr (NMDevice *self)
 	}
 
 	_LOGD (LOGD_IP6, "linklocal6: adding IPv6LL address %s", nm_utils_inet6_ntop (&lladdr, NULL));
-	if (!nm_platform_ip6_address_add (NM_PLATFORM_GET,
+	if (!nm_platform_ip6_address_add (nm_device_get_platform (self),
 	                                  ip_ifindex,
 	                                  lladdr,
 	                                  64,
@@ -6543,8 +6957,8 @@ linklocal6_start (NMDevice *self)
 
 	linklocal6_cleanup (self);
 
-	if (   priv->ip6_config
-	    && nm_ip6_config_get_address_first_nontentative (priv->ip6_config, TRUE))
+	if (   priv->ext_ip6_config_captured
+	    && nm_ip6_config_get_address_first_nontentative (priv->ext_ip6_config_captured, TRUE))
 		return NM_ACT_STAGE_RETURN_SUCCESS;
 
 	connection = nm_device_get_applied_connection (self);
@@ -6632,7 +7046,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 	if (ifindex <= 0)
 		return;
 
-	if (nm_device_uses_assumed_connection (self)) {
+	if (nm_device_sys_iface_state_is_external_or_assume (self)) {
 		/* for assumed connections we don't tamper with the MTU. This is
 		 * a bug and supposed to be fixed by the unmanaged/assumed rework. */
 		return;
@@ -6701,7 +7115,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 	mtu_desired_orig = mtu_desired;
 	ip6_mtu_orig = ip6_mtu;
 
-	mtu_plat = nm_platform_link_get_mtu (NM_PLATFORM_GET, ifindex);
+	mtu_plat = nm_platform_link_get_mtu (nm_device_get_platform (self), ifindex);
 
 	if (ip6_mtu) {
 		ip6_mtu = NM_MAX (1280, ip6_mtu);
@@ -6743,7 +7157,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config)
 		}
 
 		if (mtu_desired && mtu_desired != mtu_plat)
-			nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, mtu_desired);
+			nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, mtu_desired);
 
 		if (ip6_mtu && ip6_mtu != _IP6_MTU_SYS ()) {
 			nm_device_ipv6_sysctl_set (self, "mtu",
@@ -6768,7 +7182,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 	 * addresses as /128. The reason for the /128 is to prevent the kernel
 	 * from adding a prefix route for this address.
 	 **/
-	system_support = nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET);
+	system_support = nm_platform_check_support_kernel_extended_ifa_flags (nm_device_get_platform (self));
 
 	if (system_support)
 		ifa_flags = IFA_F_NOPREFIXROUTE;
@@ -6859,14 +7273,13 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 
 		priv->dhcp6.mode = rdata->dhcp_level;
 		if (priv->dhcp6.mode != NM_NDISC_DHCP_LEVEL_NONE) {
-			NMDeviceStateReason reason;
-
 			_LOGD (LOGD_DEVICE | LOGD_DHCP6,
 			       "Activation: Stage 3 of 5 (IP Configure Start) starting DHCPv6"
 			       " as requested by IPv6 router...");
-			if (!dhcp6_start (self, FALSE, &reason)) {
+			if (!dhcp6_start (self, FALSE)) {
 				if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED) {
-					nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+					nm_device_state_changed (self, NM_DEVICE_STATE_FAILED,
+					                         NM_DEVICE_STATE_REASON_DHCP_START_FAILED);
 					return;
 				}
 			}
@@ -6874,7 +7287,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 	}
 
 	if (changed & NM_NDISC_CONFIG_HOP_LIMIT)
-		nm_platform_sysctl_set_ip6_hop_limit_safe (NM_PLATFORM_GET, nm_device_get_ip_iface (self), rdata->hop_limit);
+		nm_platform_sysctl_set_ip6_hop_limit_safe (nm_device_get_platform (self), nm_device_get_ip_iface (self), rdata->hop_limit);
 
 	if (changed & NM_NDISC_CONFIG_MTU) {
 		if (priv->ip6_mtu != rdata->mtu) {
@@ -6929,7 +7342,7 @@ addrconf6_start_with_link_ready (NMDevice *self)
 	}
 
 	/* Apply any manual configuration before starting RA */
-	if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+	if (!ip6_config_merge_and_apply (self, TRUE))
 		_LOGW (LOGD_IP6, "failed to apply manual IPv6 configuration");
 
 	/* XXX: These sysctls would probably be better set by the lndp ndisc itself. */
@@ -7005,16 +7418,15 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 	g_assert (s_ip6);
 
 	stable_id = _get_stable_id (self, connection, &stable_type);
-	if (stable_id) {
-		priv->ndisc = nm_lndp_ndisc_new (NM_PLATFORM_GET,
-		                                 nm_device_get_ip_ifindex (self),
-		                                 nm_device_get_ip_iface (self),
-		                                 stable_type,
-		                                 stable_id,
-		                                 nm_setting_ip6_config_get_addr_gen_mode (s_ip6),
-		                                 ndisc_node_type (self),
-		                                 &error);
-	}
+	g_assert (stable_id);
+	priv->ndisc = nm_lndp_ndisc_new (nm_device_get_platform (self),
+	                                 nm_device_get_ip_ifindex (self),
+	                                 nm_device_get_ip_iface (self),
+	                                 stable_type,
+	                                 stable_id,
+	                                 nm_setting_ip6_config_get_addr_gen_mode (s_ip6),
+	                                 ndisc_node_type (self),
+	                                 &error);
 	if (!priv->ndisc) {
 		_LOGE (LOGD_IP6, "addrconf6: failed to start neighbor discovery: %s", error->message);
 		g_error_free (error);
@@ -7024,7 +7436,7 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr)
 	priv->ndisc_use_tempaddr = use_tempaddr;
 
 	if (   NM_IN_SET (use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
-	    && !nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET)) {
+	    && !nm_platform_check_support_kernel_extended_ifa_flags (nm_device_get_platform (self))) {
 		_LOGW (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for "
 		                 "IPv6 private addresses. This feature is not available");
 	}
@@ -7082,7 +7494,7 @@ save_ip6_properties (NMDevice *self)
 	g_hash_table_remove_all (priv->ip6_saved_properties);
 
 	for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) {
-		value = nm_platform_sysctl_get (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i])));
+		value = nm_platform_sysctl_get (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (ifname, ip6_properties_to_save[i])));
 		if (value) {
 			g_hash_table_insert (priv->ip6_saved_properties,
 			                     (char *) ip6_properties_to_save[i],
@@ -7122,7 +7534,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 	int ifindex = nm_device_get_ip_ifindex (self);
 	char *value;
 
-	if (!nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET))
+	if (!nm_platform_check_support_user_ipv6ll (nm_device_get_platform (self)))
 		return;
 
 	priv->nm_ipv6ll = enable;
@@ -7131,7 +7543,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 		const char *detail = enable ? "enable" : "disable";
 
 		_LOGD (LOGD_IP6, "will %s userland IPv6LL", detail);
-		plerr = nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, ifindex, enable);
+		plerr = nm_platform_link_set_user_ipv6ll_enabled (nm_device_get_platform (self), ifindex, enable);
 		if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 			_NMLOG (plerr == NM_PLATFORM_ERROR_NOT_FOUND ? LOGL_DEBUG : LOGL_WARN,
 			        LOGD_IP6,
@@ -7142,7 +7554,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable)
 
 		if (enable) {
 			/* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */
-			value = nm_platform_sysctl_get (NM_PLATFORM_GET,
+			value = nm_platform_sysctl_get (nm_device_get_platform (self),
 			                                NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_ip6_property_path (nm_device_get_ip_iface (self), "disable_ipv6")));
 			if (g_strcmp0 (value, "0") == 0)
 				nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1");
@@ -7209,7 +7621,7 @@ _ip6_privacy_get (NMDevice *self)
 	 * Instead of reading static config files in /etc, just read the current sysctl value.
 	 * This works as NM only writes to "/proc/sys/net/ipv6/conf/IFNAME/use_tempaddr", but leaves
 	 * the "default" entry untouched. */
-	ip6_privacy = nm_platform_sysctl_get_int32 (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/default/use_tempaddr"), NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+	ip6_privacy = nm_platform_sysctl_get_int32 (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/default/use_tempaddr"), NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
 	return _ip6_privacy_clamp (ip6_privacy);
 }
 
@@ -7232,7 +7644,7 @@ ip6_requires_slaves (NMConnection *connection)
 static NMActStageReturn
 act_stage3_ip6_config_start (NMDevice *self,
                              NMIP6Config **out_config,
-                             NMDeviceStateReason *reason)
+                             NMDeviceStateReason *out_failure_reason)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
@@ -7243,10 +7655,8 @@ act_stage3_ip6_config_start (NMDevice *self,
 	GSList *slaves;
 	gboolean ready_slaves;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
 	connection = nm_device_get_applied_connection (self);
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
 	if (   connection_ip6_method_requires_carrier (connection, NULL)
 	    && priv->is_master
@@ -7302,12 +7712,23 @@ act_stage3_ip6_config_start (NMDevice *self,
 	 * IPv6LL if this is not an assumed connection, since assumed connections
 	 * will already have IPv6 set up.
 	 */
-	if (!nm_device_uses_assumed_connection (self))
+	if (!nm_device_sys_iface_state_is_external_or_assume (self))
 		set_nm_ipv6ll (self, TRUE);
 
 	/* Re-enable IPv6 on the interface */
 	set_disable_ipv6 (self, "0");
 
+	/* Synchronize external IPv6 configuration with kernel, since
+	 * linklocal6_start() uses the information there to determine if we can
+	 * proceed with the selected method (SLAAC, DHCP, link-local).
+	 */
+	nm_platform_process_events (nm_device_get_platform (self));
+	g_clear_object (&priv->ext_ip6_config_captured);
+	priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_platform (self),
+	                                                       nm_device_get_ifindex (self),
+	                                                       FALSE,
+	                                                       NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+
 	ip6_privacy = _ip6_privacy_get (self);
 
 	if (   strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
@@ -7321,7 +7742,7 @@ act_stage3_ip6_config_start (NMDevice *self,
 		ret = linklocal6_start (self);
 	} else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) {
 		priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_MANAGED;
-		if (!dhcp6_start (self, TRUE, reason)) {
+		if (!dhcp6_start (self, TRUE)) {
 			/* IPv6 might be disabled; allow IPv4 to proceed */
 			ret = NM_ACT_STAGE_RETURN_IP_FAIL;
 		} else
@@ -7332,7 +7753,7 @@ act_stage3_ip6_config_start (NMDevice *self,
 		_LOGW (LOGD_IP6, "unhandled IPv6 config method '%s'; will fail", method);
 
 	if (   ret != NM_ACT_STAGE_RETURN_FAILURE
-	    && !nm_device_uses_assumed_connection (self)) {
+	    && !nm_device_sys_iface_state_is_external_or_assume (self)) {
 		switch (ip6_privacy) {
 		case NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN:
 		case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
@@ -7362,13 +7783,19 @@ nm_device_activate_stage3_ip4_start (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActStageReturn ret;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 	NMIP4Config *ip4_config = NULL;
 
 	g_assert (priv->ip4_state == IP_WAIT);
 
+	/* Slaves stay in IP_CONFIG state until master is ready, and then
+	 * they go directly to SECONDARIES without configuring IPv4.
+	 */
+	if (nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)))
+		return TRUE;
+
 	_set_ip_state (self, AF_INET, IP_CONF);
-	ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &ip4_config, &reason);
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &ip4_config, &failure_reason);
 	if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
 		if (!ip4_config)
 			ip4_config = nm_ip4_config_new (nm_device_get_ip_ifindex (self));
@@ -7378,7 +7805,7 @@ nm_device_activate_stage3_ip4_start (NMDevice *self)
 		_set_ip_state (self, AF_INET, IP_DONE);
 		check_ip_state (self, FALSE);
 	} else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, failure_reason);
 		return FALSE;
 	} else if (ret == NM_ACT_STAGE_RETURN_IP_FAIL) {
 		/* Activation not wanted */
@@ -7403,13 +7830,19 @@ nm_device_activate_stage3_ip6_start (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActStageReturn ret;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 	NMIP6Config *ip6_config = NULL;
 
 	g_assert (priv->ip6_state == IP_WAIT);
 
+	/* Slaves stay in IP_CONFIG state until master is ready, and then
+	 * they go directly to SECONDARIES without configuring IPv6.
+	 */
+	if (nm_active_connection_get_master (NM_ACTIVE_CONNECTION (priv->act_request)))
+		return TRUE;
+
 	_set_ip_state (self, AF_INET6, IP_CONF);
-	ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &reason);
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &failure_reason);
 	if (ret == NM_ACT_STAGE_RETURN_SUCCESS) {
 		if (!ip6_config)
 			ip6_config = nm_ip6_config_new (nm_device_get_ip_ifindex (self));
@@ -7423,7 +7856,7 @@ nm_device_activate_stage3_ip6_start (NMDevice *self)
 		_set_ip_state (self, AF_INET6, IP_DONE);
 		check_ip_state (self, FALSE);
 	} else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, failure_reason);
 		return FALSE;
 	} else if (ret == NM_ACT_STAGE_RETURN_IP_FAIL) {
 		/* Activation not wanted */
@@ -7456,7 +7889,7 @@ activate_stage3_ip_config_start (NMDevice *self)
 	nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
 
 	/* Device should be up before we can do anything with it */
-	if (!nm_platform_link_is_up (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self)))
+	if (!nm_platform_link_is_up (nm_device_get_platform (self), nm_device_get_ip_ifindex (self)))
 		_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
 
 	/* If the device is a slave, then we don't do any IP configuration but we
@@ -7495,60 +7928,72 @@ activate_stage3_ip_config_start (NMDevice *self)
 	check_ip_state (self, TRUE);
 }
 
-static gboolean
-fw_change_zone_handle (NMDevice *self,
-                       NMFirewallManagerCallId call_id,
-                       GError *error)
+static void
+fw_change_zone_cb (NMFirewallManager *firewall_manager,
+                   NMFirewallManagerCallId call_id,
+                   GError *error,
+                   gpointer user_data)
 {
+	NMDevice *self = user_data;
 	NMDevicePrivate *priv;
 
-	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_if_fail (NM_IS_DEVICE (self));
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	g_return_val_if_fail (priv->fw_call == call_id, FALSE);
+	if (priv->fw_call != call_id)
+		g_return_if_reached ();
 	priv->fw_call = NULL;
 
-	return !nm_utils_error_is_cancelled (error, FALSE);
+	if (nm_utils_error_is_cancelled (error, FALSE))
+		return;
+
+	switch (priv->fw_state) {
+	case FIREWALL_STATE_WAIT_STAGE_3:
+		priv->fw_state = FIREWALL_STATE_INITIALIZED;
+		nm_device_activate_schedule_stage3_ip_config_start (self);
+		break;
+	case FIREWALL_STATE_WAIT_IP_CONFIG:
+		priv->fw_state = FIREWALL_STATE_INITIALIZED;
+		if (priv->ip4_state == IP_DONE || priv->ip6_state == IP_DONE)
+			nm_device_start_ip_check (self);
+		break;
+	case FIREWALL_STATE_INITIALIZED:
+		break;
+	default:
+		g_return_if_reached ();
+	}
 }
 
 static void
-fw_change_zone_cb_stage2 (NMFirewallManager *firewall_manager,
-                          NMFirewallManagerCallId call_id,
-                          GError *error,
-                          gpointer user_data)
+fw_change_zone (NMDevice *self)
 {
-	NMDevice *self = user_data;
-	NMDevicePrivate *priv;
-
-	if (!fw_change_zone_handle (self, call_id, error))
-		return;
-
-	/* FIXME: fail the device on error? */
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+	NMConnection *applied_connection;
+	NMSettingConnection *s_con;
 
-	priv = NM_DEVICE_GET_PRIVATE (self);
-	priv->fw_ready = TRUE;
+	nm_assert (priv->fw_state >= FIREWALL_STATE_INITIALIZED);
 
-	nm_device_activate_schedule_stage3_ip_config_start (self);
-}
+	applied_connection = nm_device_get_applied_connection (self);
+	nm_assert (applied_connection);
 
-static void
-fw_change_zone_cb_ip_check (NMFirewallManager *firewall_manager,
-                            NMFirewallManagerCallId call_id,
-                            GError *error,
-                            gpointer user_data)
-{
-	NMDevice *self = user_data;
-	NMDevicePrivate *priv;
+	s_con = nm_connection_get_setting_connection (applied_connection);
+	nm_assert (s_con);
 
-	if (!fw_change_zone_handle (self, call_id, error))
-		return;
+	if (priv->fw_call) {
+		nm_firewall_manager_cancel_call (priv->fw_call);
+		nm_assert (!priv->fw_call);
+	}
 
-	/* FIXME: fail the device on error? */
+	if (G_UNLIKELY (!priv->fw_mgr))
+		priv->fw_mgr = g_object_ref (nm_firewall_manager_get ());
 
-	priv = NM_DEVICE_GET_PRIVATE (self);
-	if (priv->ip4_state == IP_DONE || priv->ip6_state == IP_DONE)
-		nm_device_start_ip_check (self);
+	priv->fw_call = nm_firewall_manager_add_or_change_zone (priv->fw_mgr,
+	                                                        nm_device_get_ip_iface (self),
+	                                                        nm_setting_connection_get_zone (s_con),
+	                                                        FALSE, /* change zone */
+	                                                        fw_change_zone_cb,
+	                                                        self);
 }
 
 /*
@@ -7560,9 +8005,6 @@ void
 nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self)
 {
 	NMDevicePrivate *priv;
-	NMConnection *connection;
-	NMSettingConnection *s_con = NULL;
-	const char *zone;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
@@ -7570,37 +8012,30 @@ nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self)
 	g_return_if_fail (priv->act_request);
 
 	/* Add the interface to the specified firewall zone */
-	connection = nm_device_get_applied_connection (self);
-	g_assert (connection);
-	s_con = nm_connection_get_setting_connection (connection);
-
-	if (!priv->fw_ready) {
-		if (nm_device_uses_generated_assumed_connection (self))
-			priv->fw_ready = TRUE;
-		else {
-			if (!priv->fw_call) {
-				zone = nm_setting_connection_get_zone (s_con);
-
-				_LOGD (LOGD_DEVICE, "Activation: setting firewall zone '%s'", zone ? zone : "default");
-				priv->fw_call = nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (),
-				                                                        nm_device_get_ip_iface (self),
-				                                                        zone,
-				                                                        FALSE,
-				                                                        fw_change_zone_cb_stage2,
-				                                                        self);
-			}
+	if (priv->fw_state == FIREWALL_STATE_UNMANAGED) {
+		if (!nm_device_sys_iface_state_is_external (self)) {
+			priv->fw_state = FIREWALL_STATE_WAIT_STAGE_3;
+			fw_change_zone (self);
 			return;
 		}
+
+		/* fake success. */
+		priv->fw_state = FIREWALL_STATE_INITIALIZED;
+	} else if (priv->fw_state == FIREWALL_STATE_WAIT_STAGE_3) {
+		/* a firewall call for stage3 is pending. Return and wait. */
+		return;
 	}
 
+	nm_assert (priv->fw_state == FIREWALL_STATE_INITIALIZED);
+
 	activation_source_schedule (self, activate_stage3_ip_config_start, AF_INET);
 }
 
 static NMActStageReturn
-act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *reason)
+act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *out_failure_reason)
 {
 	if (!get_ip_config_may_fail (self, AF_INET)) {
-		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 	return NM_ACT_STAGE_RETURN_SUCCESS;
@@ -7616,13 +8051,13 @@ static void
 activate_stage4_ip4_config_timeout (NMDevice *self)
 {
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 
-	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &reason);
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &failure_reason);
 	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
 		return;
 	else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, failure_reason);
 		return;
 	}
 	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
@@ -7652,10 +8087,10 @@ nm_device_activate_schedule_ip4_config_timeout (NMDevice *self)
 }
 
 static NMActStageReturn
-act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *reason)
+act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *out_failure_reason)
 {
 	if (!get_ip_config_may_fail (self, AF_INET6)) {
-		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
@@ -7672,13 +8107,13 @@ static void
 activate_stage4_ip6_config_timeout (NMDevice *self)
 {
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 
-	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip6_config_timeout (self, &reason);
+	ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip6_config_timeout (self, &failure_reason);
 	if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
 		return;
 	if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
-		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason);
+		nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, failure_reason);
 		return;
 	}
 	g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
@@ -7708,7 +8143,7 @@ nm_device_activate_schedule_ip6_config_timeout (NMDevice *self)
 }
 
 static gboolean
-share_init (void)
+share_init (NMDevice *self)
 {
 	char *modules[] = { "ip_tables", "iptable_nat", "nf_nat_ftp", "nf_nat_irc",
 	                    "nf_nat_sip", "nf_nat_tftp", "nf_nat_pptp", "nf_nat_h323",
@@ -7716,14 +8151,14 @@ share_init (void)
 	char **iter;
 	int errsv;
 
-	if (!nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_forward"), "1")) {
+	if (!nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_forward"), "1")) {
 		errsv = errno;
 		nm_log_err (LOGD_SHARING, "share: error enabling IPv4 forwarding: (%d) %s",
 		            errsv, strerror (errsv));
 		return FALSE;
 	}
 
-	if (!nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_dynaddr"), "1")) {
+	if (!nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_dynaddr"), "1")) {
 		errsv = errno;
 		nm_log_err (LOGD_SHARING, "share: error enabling dynamic addresses: (%d) %s",
 		            errsv, strerror (errsv));
@@ -7770,7 +8205,7 @@ start_sharing (NMDevice *self, NMIP4Config *config)
 	if (!inet_ntop (AF_INET, &network, str_addr, sizeof (str_addr)))
 		return FALSE;
 
-	if (!share_init ())
+	if (!share_init (self))
 		return FALSE;
 
 	req = nm_device_get_act_request (self);
@@ -7826,7 +8261,7 @@ arp_announce (NMDevice *self)
 
 	arp_cleanup (self);
 
-	hw_addr = nm_platform_link_get_address (NM_PLATFORM_GET,
+	hw_addr = nm_platform_link_get_address (nm_device_get_platform (self),
 	                                        nm_device_get_ip_ifindex (self),
 	                                        &hw_addr_len);
 
@@ -7868,7 +8303,6 @@ activate_stage5_ip4_config_commit (NMDevice *self)
 	NMActRequest *req;
 	const char *method;
 	NMConnection *connection;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 	int ip_ifindex;
 
 	req = nm_device_get_act_request (self);
@@ -7878,16 +8312,16 @@ activate_stage5_ip4_config_commit (NMDevice *self)
 
 	/* Interface must be IFF_UP before IP config can be applied */
 	ip_ifindex = nm_device_get_ip_ifindex (self);
-	if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex) && !nm_device_uses_assumed_connection (self)) {
-		nm_platform_link_set_up (NM_PLATFORM_GET, ip_ifindex, NULL);
-		if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex))
+	if (!nm_platform_link_is_up (nm_device_get_platform (self), ip_ifindex) && !nm_device_sys_iface_state_is_external_or_assume (self)) {
+		nm_platform_link_set_up (nm_device_get_platform (self), ip_ifindex, NULL);
+		if (!nm_platform_link_is_up (nm_device_get_platform (self), ip_ifindex))
 			_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
 	}
 
 	/* NULL to use the existing priv->dev_ip4_config */
-	if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) {
+	if (!ip4_config_merge_and_apply (self, NULL, TRUE)) {
 		_LOGD (LOGD_DEVICE | LOGD_IP4, "Activation: Stage 5 of 5 (IPv4 Commit) failed");
-		nm_device_ip_method_failed (self, AF_INET, reason);
+		nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 		return;
 	}
 
@@ -7908,14 +8342,10 @@ activate_stage5_ip4_config_commit (NMDevice *self)
 	if (   priv->dhcp4.client
 	    && nm_device_activate_ip4_state_in_conf (self)
 	    && (nm_device_get_state (self) > NM_DEVICE_STATE_IP_CONFIG)) {
-		/* Notify dispatcher scripts of new DHCP4 config */
-		nm_dispatcher_call (DISPATCHER_ACTION_DHCP4_CHANGE,
-		                    nm_device_get_settings_connection (self),
-		                    nm_device_get_applied_connection (self),
-		                    self,
-		                    NULL,
-		                    NULL,
-		                    NULL);
+		nm_dispatcher_call_device (NM_DISPATCHER_ACTION_DHCP4_CHANGE,
+		                           self,
+		                           NULL,
+		                           NULL, NULL, NULL);
 	}
 
 	arp_announce (self);
@@ -7991,7 +8421,7 @@ dad6_get_pending_addresses (NMDevice *self)
 			num = nm_ip6_config_get_num_addresses (confs[i]);
 			for (j = 0; j < num; j++) {
 				addr = nm_ip6_config_get_address (confs[i], j);
-				pl_addr = nm_platform_ip6_address_get (NM_PLATFORM_GET,
+				pl_addr = nm_platform_ip6_address_get (nm_device_get_platform (self),
 				                                       ifindex,
 				                                       addr->address,
 				                                       addr->plen);
@@ -8021,7 +8451,6 @@ activate_stage5_ip6_config_commit (NMDevice *self)
 	NMActRequest *req;
 	const char *method;
 	NMConnection *connection;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 	int ip_ifindex;
 	int errsv;
 
@@ -8032,26 +8461,23 @@ activate_stage5_ip6_config_commit (NMDevice *self)
 
 	/* Interface must be IFF_UP before IP config can be applied */
 	ip_ifindex = nm_device_get_ip_ifindex (self);
-	if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex) && !nm_device_uses_assumed_connection (self)) {
-		nm_platform_link_set_up (NM_PLATFORM_GET, ip_ifindex, NULL);
-		if (!nm_platform_link_is_up (NM_PLATFORM_GET, ip_ifindex))
+	if (!nm_platform_link_is_up (nm_device_get_platform (self), ip_ifindex) && !nm_device_sys_iface_state_is_external_or_assume (self)) {
+		nm_platform_link_set_up (nm_device_get_platform (self), ip_ifindex, NULL);
+		if (!nm_platform_link_is_up (nm_device_get_platform (self), ip_ifindex))
 			_LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self));
 	}
 
-	if (ip6_config_merge_and_apply (self, TRUE, &reason)) {
+	if (ip6_config_merge_and_apply (self, TRUE)) {
 		if (   priv->dhcp6.mode != NM_NDISC_DHCP_LEVEL_NONE
 		    && priv->ip6_state == IP_CONF) {
 			if (priv->dhcp6.ip6_config) {
 				/* If IPv6 wasn't the first IP to complete, and DHCP was used,
 				 * then ensure dispatcher scripts get the DHCP lease information.
 				 */
-				nm_dispatcher_call (DISPATCHER_ACTION_DHCP6_CHANGE,
-				                    nm_device_get_settings_connection (self),
-				                    nm_device_get_applied_connection (self),
-				                    self,
-				                    NULL,
-				                    NULL,
-				                    NULL);
+				nm_dispatcher_call_device (NM_DISPATCHER_ACTION_DHCP6_CHANGE,
+				                           self,
+				                           NULL,
+				                           NULL, NULL, NULL);
 			} else {
 				/* still waiting for first dhcp6 lease. */
 				return;
@@ -8064,7 +8490,7 @@ activate_stage5_ip6_config_commit (NMDevice *self)
 		method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
 
 		if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) {
-			if (!nm_platform_sysctl_set (NM_PLATFORM_GET, NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/all/forwarding"), "1")) {
+			if (!nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/all/forwarding"), "1")) {
 				errsv = errno;
 				_LOGE (LOGD_SHARING, "share: error enabling IPv6 forwarding: (%d) %s", errsv, strerror (errsv));
 				nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
@@ -8073,18 +8499,21 @@ activate_stage5_ip6_config_commit (NMDevice *self)
 
 		/* Check if we have to wait for DAD */
 		if (priv->ip6_state == IP_CONF && !priv->dad6_ip6_config) {
-			priv->dad6_ip6_config = dad6_get_pending_addresses (self);
+			if (!priv->carrier && priv->ignore_carrier && get_ip_config_may_fail (self, AF_INET6))
+				_LOGI (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD: carrier missing and ignored, not delaying activation");
+			else
+				priv->dad6_ip6_config = dad6_get_pending_addresses (self);
+
 			if (priv->dad6_ip6_config) {
-				_LOGD (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD: waiting termination");
+				_LOGD (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD: awaiting termination");
 			} else {
-				/* No tentative addresses, proceed right away */
 				_set_ip_state (self, AF_INET6, IP_DONE);
 				check_ip_state (self, FALSE);
 			}
 		}
 	} else {
 		_LOGW (LOGD_DEVICE | LOGD_IP6, "Activation: Stage 5 of 5 (IPv6 Commit) failed");
-		nm_device_ip_method_failed (self, AF_INET6, reason);
+		nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 	}
 }
 
@@ -8164,7 +8593,24 @@ act_request_set (NMDevice *self, NMActRequest *act_request)
 		                                         "notify::"NM_EXPORTED_OBJECT_PATH,
 		                                         G_CALLBACK (act_request_set_cb),
 		                                         self);
+
+		switch (nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (act_request))) {
+		case NM_ACTIVATION_TYPE_EXTERNAL:
+			break;
+		case NM_ACTIVATION_TYPE_ASSUME:
+			if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_EXTERNAL)
+				nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_ASSUME);
+			break;
+		case NM_ACTIVATION_TYPE_MANAGED:
+			if (NM_IN_SET_TYPED (NMDeviceSysIfaceState,
+			                     priv->sys_iface_state,
+			                     NM_DEVICE_SYS_IFACE_STATE_EXTERNAL,
+			                     NM_DEVICE_SYS_IFACE_STATE_ASSUME))
+				nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
+			break;
+		}
 	}
+
 	_notify (self, PROP_ACTIVE_CONNECTION);
 }
 
@@ -8234,7 +8680,7 @@ delete_on_deactivate_link_delete (gpointer user_data)
 		if (!nm_device_unrealize (data->device, TRUE, &error))
 			_LOGD (LOGD_DEVICE, "delete_on_deactivate: unrealizing %d failed (%s)", data->ifindex, error->message);
 	} else
-		nm_platform_link_delete (NM_PLATFORM_GET, data->ifindex);
+		nm_platform_link_delete (nm_device_get_platform (self), data->ifindex);
 
 	g_free (data);
 	return FALSE;
@@ -8323,12 +8769,14 @@ _cleanup_ip6_pre (NMDevice *self, CleanupType cleanup_type)
 	addrconf6_cleanup (self);
 }
 
-static gboolean
-_hash_check_invalid_keys_impl (GHashTable *hash, const char *setting_name, GError **error, const char **argv)
+gboolean
+_nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name,
+                                    GError **error, const char **argv)
 {
 	guint found_keys = 0;
 	guint i;
 
+	nm_assert (hash && g_hash_table_size (hash) > 0);
 	nm_assert (argv && argv[0]);
 
 #if NM_MORE_ASSERTS > 10
@@ -8344,9 +8792,6 @@ _hash_check_invalid_keys_impl (GHashTable *hash, const char *setting_name, GErro
 	}
 #endif
 
-	if (!hash || g_hash_table_size (hash) == 0)
-		return TRUE;
-
 	for (i = 0; argv[i]; i++) {
 		if (g_hash_table_contains (hash, argv[i]))
 			found_keys++;
@@ -8362,7 +8807,7 @@ _hash_check_invalid_keys_impl (GHashTable *hash, const char *setting_name, GErro
 
 		g_hash_table_iter_init (&iter, hash);
 		while (g_hash_table_iter_next (&iter, (gpointer *) &k, NULL)) {
-			if (_nm_utils_strv_find_first ((char **) argv, -1, k) < 0) {
+			if (nm_utils_strv_find_first ((char **) argv, -1, k) < 0) {
 				first_invalid_key = k;
 				break;
 			}
@@ -8387,12 +8832,12 @@ _hash_check_invalid_keys_impl (GHashTable *hash, const char *setting_name, GErro
 
 	return TRUE;
 }
-#define _hash_check_invalid_keys(hash, setting_name, error, ...) _hash_check_invalid_keys_impl (hash, setting_name, error, ((const char *[]) { __VA_ARGS__, NULL }))
 
 void
 nm_device_reactivate_ip4_config (NMDevice *self,
                                  NMSettingIPConfig *s_ip4_old,
-                                 NMSettingIPConfig *s_ip4_new)
+                                 NMSettingIPConfig *s_ip4_new,
+                                 gboolean force_restart)
 {
 	NMDevicePrivate *priv;
 	const char *method_old, *method_new;
@@ -8408,20 +8853,23 @@ nm_device_reactivate_ip4_config (NMDevice *self,
 		                             s_ip4_new,
 		                             nm_device_get_ip4_route_metric (self));
 
-		method_old = s_ip4_old ?
-			nm_setting_ip_config_get_method (s_ip4_old) :
-			NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
-		method_new = s_ip4_new ?
-			nm_setting_ip_config_get_method (s_ip4_new) :
-			NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+		if (!force_restart) {
+			method_old = s_ip4_old
+			             ? nm_setting_ip_config_get_method (s_ip4_old)
+			             : NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+			method_new = s_ip4_new
+			             ? nm_setting_ip_config_get_method (s_ip4_new)
+			             : NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+			force_restart = !nm_streq0 (method_old, method_new);
+		}
 
-		if (!nm_streq0 (method_old, method_new)) {
+		if (force_restart) {
 			_cleanup_ip4_pre (self, CLEANUP_TYPE_DECONFIGURE);
 			_set_ip_state (self, AF_INET, IP_WAIT);
 			if (!nm_device_activate_stage3_ip4_start (self))
 				_LOGW (LOGD_IP4, "Failed to apply IPv4 configuration");
 		} else {
-			if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL))
+			if (!ip4_config_merge_and_apply (self, NULL, TRUE))
 				_LOGW (LOGD_IP4, "Failed to reapply IPv4 configuration");
 		}
 	}
@@ -8430,7 +8878,8 @@ nm_device_reactivate_ip4_config (NMDevice *self,
 void
 nm_device_reactivate_ip6_config (NMDevice *self,
                                  NMSettingIPConfig *s_ip6_old,
-                                 NMSettingIPConfig *s_ip6_new)
+                                 NMSettingIPConfig *s_ip6_new,
+                                 gboolean force_restart)
 {
 	NMDevicePrivate *priv;
 	const char *method_old, *method_new;
@@ -8446,31 +8895,108 @@ nm_device_reactivate_ip6_config (NMDevice *self,
 		                             s_ip6_new,
 		                             nm_device_get_ip6_route_metric (self));
 
-		method_old = s_ip6_old ?
-			nm_setting_ip_config_get_method (s_ip6_old) :
-			NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
-		method_new = s_ip6_new ?
-			nm_setting_ip_config_get_method (s_ip6_new) :
-			NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
+		if (!force_restart) {
+			method_old = s_ip6_old
+			             ? nm_setting_ip_config_get_method (s_ip6_old)
+			             : NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
+			method_new = s_ip6_new
+			             ? nm_setting_ip_config_get_method (s_ip6_new)
+			             : NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
+			force_restart = !nm_streq0 (method_old, method_new);
+		}
 
-		if (!nm_streq0 (method_old, method_new)) {
+		if (force_restart) {
 			_cleanup_ip6_pre (self, CLEANUP_TYPE_DECONFIGURE);
 			_set_ip_state (self, AF_INET6, IP_WAIT);
 			if (!nm_device_activate_stage3_ip6_start (self))
 				_LOGW (LOGD_IP6, "Failed to apply IPv6 configuration");
 		} else {
-			if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+			if (!ip6_config_merge_and_apply (self, TRUE))
 				_LOGW (LOGD_IP4, "Failed to reapply IPv6 configuration");
 		}
 	}
 }
 
+static void
+_pacrunner_manager_send (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	nm_pacrunner_manager_remove_clear (priv->pacrunner_manager,
+	                                   &priv->pacrunner_call_id);
+
+	if (!priv->pacrunner_manager)
+		priv->pacrunner_manager = g_object_ref (nm_pacrunner_manager_get ());
+
+	priv->pacrunner_call_id = nm_pacrunner_manager_send (priv->pacrunner_manager,
+	                                                     nm_device_get_ip_iface (self),
+	                                                     priv->proxy_config,
+	                                                     NULL,
+	                                                     NULL);
+}
+
+static void
+reactivate_proxy_config (NMDevice *self)
+{
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!priv->pacrunner_call_id)
+		return;
+	nm_device_set_proxy_config (self, priv->dhcp4.pac_url);
+	_pacrunner_manager_send (self);
+}
+
+static gboolean
+can_reapply_change (NMDevice *self, const char *setting_name,
+                    NMSetting *s_old, NMSetting *s_new,
+                    GHashTable *diffs, GError **error)
+{
+	if (nm_streq (setting_name, NM_SETTING_CONNECTION_SETTING_NAME)) {
+		/* Whitelist allowed properties from "connection" setting which are
+		 * allowed to differ.
+		 *
+		 * This includes UUID, there is no principal problem with reapplying a
+		 * connection and changing it's UUID. In fact, disallowing it makes it
+		 * cumbersome for the user to reapply any connection but the original
+		 * settings-connection. */
+		return nm_device_hash_check_invalid_keys (diffs,
+		                                          NM_SETTING_CONNECTION_SETTING_NAME,
+		                                          error,
+		                                          NM_SETTING_CONNECTION_ID,
+		                                          NM_SETTING_CONNECTION_UUID,
+		                                          NM_SETTING_CONNECTION_STABLE_ID,
+		                                          NM_SETTING_CONNECTION_AUTOCONNECT,
+		                                          NM_SETTING_CONNECTION_ZONE,
+		                                          NM_SETTING_CONNECTION_METERED,
+		                                          NM_SETTING_CONNECTION_LLDP);
+	} else if (NM_IN_STRSET (setting_name,
+	                         NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	                         NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	                         NM_SETTING_PROXY_SETTING_NAME)) {
+		/* accept all */
+		return TRUE;
+	} else {
+		g_set_error (error,
+		             NM_DEVICE_ERROR,
+		             NM_DEVICE_ERROR_INCOMPATIBLE_CONNECTION,
+		             "Can't reapply any changes to '%s' setting",
+		             setting_name);
+		return FALSE;
+	}
+}
+
+static void
+reapply_connection (NMDevice *self, NMConnection *con_old, NMConnection *con_new)
+{
+
+}
 
-/* reapply_connection:
+/* check_and_reapply_connection:
  * @connection: the new connection settings to be applied or %NULL to reapply
  *   the current settings connection
  * @version_id: either zero, or the current version id for the applied
  *   connection.
+ * @audit_args: on return, a string representing the changes
  * @error: the error if %FALSE is returned
  *
  * Change configuration of an already configured device if possible.
@@ -8479,11 +9005,13 @@ nm_device_reactivate_ip6_config (NMDevice *self,
  * Return: %FALSE if the new configuration can not be reapplied.
  */
 static gboolean
-reapply_connection (NMDevice *self,
-                    NMConnection *connection,
-                    guint64 version_id,
-                    GError **error)
+check_and_reapply_connection (NMDevice *self,
+                              NMConnection *connection,
+                              guint64 version_id,
+                              char **audit_args,
+                              GError **error)
 {
+	NMDeviceClass *klass = NM_DEVICE_GET_CLASS (self);
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMConnection *applied = nm_device_get_applied_connection (self);
 	gs_unref_object NMConnection *applied_clone = NULL;
@@ -8491,6 +9019,7 @@ reapply_connection (NMDevice *self,
 	NMConnection *con_old, *con_new;
 	NMSettingIPConfig *s_ip4_old, *s_ip4_new;
 	NMSettingIPConfig *s_ip6_old, *s_ip6_new;
+	GHashTableIter iter;
 
 	if (priv->state != NM_DEVICE_STATE_ACTIVATED) {
 		g_set_error_literal (error,
@@ -8506,30 +9035,29 @@ reapply_connection (NMDevice *self,
 	                    NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS,
 	                    &diffs);
 
+	if (diffs && nm_audit_manager_audit_enabled (nm_audit_manager_get ()))
+		*audit_args = nm_utils_format_con_diff_for_audit (diffs);
+	else
+		*audit_args = NULL;
+
 	/**************************************************************************
 	 * check for unsupported changes and reject to reapply
 	 *************************************************************************/
-	if (!_hash_check_invalid_keys (diffs, NULL, error,
-	                               NM_SETTING_IP4_CONFIG_SETTING_NAME,
-	                               NM_SETTING_IP6_CONFIG_SETTING_NAME,
-	                               NM_SETTING_CONNECTION_SETTING_NAME))
-		return FALSE;
-
-	/* whitelist allowed properties from "connection" setting which are allowed to differ.
-	 *
-	 * This includes UUID, there is no principal problem with reapplying a connection
-	 * and changing it's UUID. In fact, disallowing it makes it cumbersome for the user
-	 * to reapply any connection but the original settings-connection. */
-	if (!_hash_check_invalid_keys (diffs ? g_hash_table_lookup (diffs, NM_SETTING_CONNECTION_SETTING_NAME) : NULL,
-	                               NM_SETTING_CONNECTION_SETTING_NAME,
-	                               error,
-	                               NM_SETTING_CONNECTION_ID,
-	                               NM_SETTING_CONNECTION_UUID,
-	                               NM_SETTING_CONNECTION_STABLE_ID,
-	                               NM_SETTING_CONNECTION_AUTOCONNECT,
-	                               NM_SETTING_CONNECTION_ZONE,
-	                               NM_SETTING_CONNECTION_METERED))
-		return FALSE;
+	if (diffs) {
+		char *setting_name;
+		GHashTable *setting_diff;
+
+		g_hash_table_iter_init (&iter, diffs);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &setting_name, (gpointer *) &setting_diff)) {
+			if (!klass->can_reapply_change (self,
+			                                setting_name,
+			                                nm_connection_get_setting_by_name (applied, setting_name),
+			                                nm_connection_get_setting_by_name (connection, setting_name),
+			                                setting_diff,
+			                                error))
+				return FALSE;
+		}
+	}
 
 	if (   version_id != 0
 	    && version_id != nm_active_connection_version_id_get ((NMActiveConnection *) priv->act_request)) {
@@ -8548,7 +9076,7 @@ reapply_connection (NMDevice *self,
 		nm_active_connection_version_id_bump ((NMActiveConnection *) priv->act_request);
 
 	_LOGD (LOGD_DEVICE, "reapply (version-id %llu%s)",
-	       (long long unsigned) nm_active_connection_version_id_get (((NMActiveConnection *) priv->act_request)),
+	       (unsigned long long) nm_active_connection_version_id_get (((NMActiveConnection *) priv->act_request)),
 	       diffs ? "" : " (unmodified)");
 
 	if (diffs) {
@@ -8559,7 +9087,7 @@ reapply_connection (NMDevice *self,
 			NMSettingConnection *s_con_a, *s_con_n;
 
 			/* we allow re-applying a connection with differing ID, UUID, STABLE_ID and AUTOCONNECT.
-			 * This is for convenience but these values are not actually changable. So, check
+			 * This is for convenience but these values are not actually changeable. So, check
 			 * if they changed, and if the did revert to the original values. */
 			s_con_a = nm_connection_get_setting_connection (applied);
 			s_con_n = nm_connection_get_setting_connection (connection);
@@ -8587,20 +9115,27 @@ reapply_connection (NMDevice *self,
 	} else
 		con_old = con_new = applied;
 
-	s_ip4_new = nm_connection_get_setting_ip4_config (con_new);
-	s_ip4_old = nm_connection_get_setting_ip4_config (con_old);
-	s_ip6_new = nm_connection_get_setting_ip6_config (con_new);
-	s_ip6_old = nm_connection_get_setting_ip6_config (con_old);
+	priv->v4_commit_first_time = TRUE;
+	priv->v6_commit_first_time = TRUE;
 
 	/**************************************************************************
 	 * Reapply changes
 	 *************************************************************************/
+	klass->reapply_connection (self, con_old, con_new);
 
 	nm_device_update_firewall_zone (self);
 	nm_device_update_metered (self);
+	lldp_init (self, FALSE);
+
+	s_ip4_old = nm_connection_get_setting_ip4_config (con_old);
+	s_ip4_new = nm_connection_get_setting_ip4_config (con_new);
+	s_ip6_old = nm_connection_get_setting_ip6_config (con_old);
+	s_ip6_new = nm_connection_get_setting_ip6_config (con_new);
 
-	nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new);
-	nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new);
+	nm_device_reactivate_ip4_config (self, s_ip4_old, s_ip4_new, TRUE);
+	nm_device_reactivate_ip6_config (self, s_ip6_old, s_ip6_new, TRUE);
+
+	reactivate_proxy_config (self);
 
 	return TRUE;
 }
@@ -8621,6 +9156,7 @@ reapply_cb (NMDevice *self,
 	guint64 version_id = 0;
 	gs_unref_object NMConnection *connection = NULL;
 	GError *local = NULL;
+	gs_free char *audit_args = NULL;
 
 	if (reapply_data) {
 		connection = reapply_data->connection;
@@ -8629,20 +9165,21 @@ reapply_cb (NMDevice *self,
 	}
 
 	if (error) {
-		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, error->message);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, subject, error->message);
 		g_dbus_method_invocation_return_gerror (context, error);
 		return;
 	}
 
-	if (!reapply_connection (self,
-	                         connection ? : (NMConnection *) nm_device_get_settings_connection (self),
-	                         version_id,
-	                         &local)) {
-		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, subject, local->message);
+	if (!check_and_reapply_connection (self,
+	                                   connection ? : (NMConnection *) nm_device_get_settings_connection (self),
+	                                   version_id,
+	                                   &audit_args,
+	                                   &local)) {
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, audit_args, subject, local->message);
 		g_dbus_method_invocation_take_error (context, local);
 		local = NULL;
 	} else {
-		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, TRUE, subject, NULL);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, TRUE, audit_args, subject, NULL);
 		g_dbus_method_invocation_return_value (context, NULL);
 	}
 }
@@ -8665,7 +9202,7 @@ impl_device_reapply (NMDevice *self,
 		error = g_error_new_literal (NM_DEVICE_ERROR,
 		                             NM_DEVICE_ERROR_FAILED,
 		                             "Invalid flags specified");
-		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, context, error->message);
 		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
@@ -8674,7 +9211,7 @@ impl_device_reapply (NMDevice *self,
 		error = g_error_new_literal (NM_DEVICE_ERROR,
 		                             NM_DEVICE_ERROR_NOT_ACTIVE,
 		                             "Device is not activated");
-		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, context, error->message);
 		g_dbus_method_invocation_take_error (context, error);
 		return;
 	}
@@ -8690,7 +9227,7 @@ impl_device_reapply (NMDevice *self,
 		                                                  &error);
 		if (!connection) {
 			g_prefix_error (&error, "The settings specified are invalid: ");
-			nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, context, error->message);
+			nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_REAPPLY, self, FALSE, NULL, context, error->message);
 			g_dbus_method_invocation_take_error (context, error);
 			return;
 		}
@@ -8820,7 +9357,7 @@ disconnect_cb (NMDevice *self,
 
 	if (error) {
 		g_dbus_method_invocation_return_gerror (context, error);
-		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, FALSE, subject, error->message);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, FALSE, NULL, subject, error->message);
 		return;
 	}
 
@@ -8829,16 +9366,16 @@ disconnect_cb (NMDevice *self,
 		local = g_error_new_literal (NM_DEVICE_ERROR,
 		                             NM_DEVICE_ERROR_NOT_ACTIVE,
 		                             "Device is not active");
-		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, FALSE, subject, local->message);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, FALSE, NULL, subject, local->message);
 		g_dbus_method_invocation_take_error (context, local);
 	} else {
-		nm_device_set_autoconnect (self, FALSE);
+		nm_device_set_autoconnect_intern (self, FALSE);
 
 		nm_device_state_changed (self,
 		                         NM_DEVICE_STATE_DEACTIVATING,
 		                         NM_DEVICE_STATE_REASON_USER_REQUESTED);
 		g_dbus_method_invocation_return_value (context, NULL);
-		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, TRUE, subject, NULL);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DISCONNECT, self, TRUE, NULL, subject, NULL);
 	}
 }
 
@@ -8846,7 +9383,9 @@ static void
 _clear_queued_act_request (NMDevicePrivate *priv)
 {
 	if (priv->queued_act_request) {
-		nm_active_connection_set_state ((NMActiveConnection *) priv->queued_act_request, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
+		nm_active_connection_set_state ((NMActiveConnection *) priv->queued_act_request,
+		                                NM_ACTIVE_CONNECTION_STATE_DEACTIVATED,
+		                                NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED);
 		g_clear_object (&priv->queued_act_request);
 	}
 }
@@ -8889,12 +9428,12 @@ delete_cb (NMDevice *self,
 
 	if (error) {
 		g_dbus_method_invocation_return_gerror (context, error);
-		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DELETE, self, FALSE, subject, error->message);
+		nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DELETE, self, FALSE, NULL, subject, error->message);
 		return;
 	}
 
 	/* Authorized */
-	nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DELETE, self, TRUE, subject, NULL);
+	nm_audit_log_device_op (NM_AUDIT_OP_DEVICE_DELETE, self, TRUE, NULL, subject, NULL);
 	if (nm_device_unrealize (self, TRUE, &local))
 		g_dbus_method_invocation_return_value (context, NULL);
 	else
@@ -9108,12 +9647,11 @@ nm_device_get_proxy_config (NMDevice *self)
 }
 
 static void
-nm_device_set_proxy_config (NMDevice *self, GHashTable *options)
+nm_device_set_proxy_config (NMDevice *self, const char *pac_url)
 {
 	NMDevicePrivate *priv;
 	NMConnection *connection;
 	NMSettingProxy *s_proxy = NULL;
-	char *pac = NULL;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
@@ -9122,17 +9660,12 @@ nm_device_set_proxy_config (NMDevice *self, GHashTable *options)
 	g_clear_object (&priv->proxy_config);
 	priv->proxy_config = nm_proxy_config_new ();
 
-	if (options) {
-		pac = g_hash_table_lookup (options, "wpad");
-		if (pac) {
-			nm_proxy_config_set_method (priv->proxy_config, NM_PROXY_CONFIG_METHOD_AUTO);
-			nm_proxy_config_set_pac_url (priv->proxy_config, pac);
-			_LOGD (LOGD_PROXY, "proxy: PAC url \"%s\"", pac);
-		} else {
-			nm_proxy_config_set_method (priv->proxy_config, NM_PROXY_CONFIG_METHOD_NONE);
-			_LOGD (LOGD_PROXY, "proxy: PAC url not obtained from DHCP server");
-		}
-	}
+	if (pac_url) {
+		nm_proxy_config_set_method (priv->proxy_config, NM_PROXY_CONFIG_METHOD_AUTO);
+		nm_proxy_config_set_pac_url (priv->proxy_config, pac_url);
+		_LOGD (LOGD_PROXY, "proxy: PAC url \"%s\"", pac_url);
+	} else
+		nm_proxy_config_set_method (priv->proxy_config, NM_PROXY_CONFIG_METHOD_NONE);
 
 	connection = nm_device_get_applied_connection (self);
 	if (connection)
@@ -9165,14 +9698,13 @@ nm_device_set_ip4_config (NMDevice *self,
                           NMIP4Config *new_config,
                           guint32 default_route_metric,
                           gboolean commit,
-                          gboolean routes_full_sync,
-                          NMDeviceStateReason *reason)
+                          gboolean routes_full_sync)
 {
 	NMDevicePrivate *priv;
 	NMIP4Config *old_config = NULL;
 	gboolean has_changes = FALSE;
 	gboolean success = TRUE;
-	NMDeviceStateReason reason_local = NM_DEVICE_STATE_REASON_NONE;
+	gboolean def_route_changed;
 	int ip_ifindex, config_ifindex;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
@@ -9193,17 +9725,18 @@ nm_device_set_ip4_config (NMDevice *self,
 
 	/* Always commit to nm-platform to update lifetimes */
 	if (commit && new_config) {
-		gboolean assumed = nm_device_uses_assumed_connection (self);
+		gboolean assumed = nm_device_sys_iface_state_is_external_or_assume (self);
 
 		_commit_mtu (self, new_config);
 		/* For assumed devices we must not touch the kernel-routes, such as the device-route.
 		 * FIXME: this is wrong in case where "assumed" means "take-over-seamlessly". In this
 		 * case, we should manage the device route, for example on new DHCP lease. */
-		success = nm_ip4_config_commit (new_config, ip_ifindex,
+		success = nm_ip4_config_commit (new_config,
+		                                nm_device_get_platform (self),
+		                                nm_netns_get_route_manager (priv->netns),
+		                                ip_ifindex,
 		                                routes_full_sync,
 		                                assumed ? (gint64) -1 : (gint64) default_route_metric);
-		if (!success)
-			reason_local = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
 	}
 
 	if (new_config) {
@@ -9234,9 +9767,15 @@ nm_device_set_ip4_config (NMDevice *self,
 		g_clear_object (&priv->dev_ip4_config);
 	}
 
-	nm_default_route_manager_ip4_update_default_route (nm_default_route_manager_get (), self);
+	def_route_changed = nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self);
+	concheck_periodic_update (self);
+
+	if (!nm_device_sys_iface_state_is_external_or_assume (self))
+		ip4_rp_filter_update (self);
 
 	if (has_changes) {
+		NMSettingsConnection *settings_connection;
+
 		_update_ip4_address (self);
 
 		if (old_config != priv->ip4_config)
@@ -9246,25 +9785,27 @@ nm_device_set_ip4_config (NMDevice *self,
 		if (old_config != priv->ip4_config)
 			nm_exported_object_clear_and_unexport (&old_config);
 
-		if (nm_device_uses_generated_assumed_connection (self)) {
-			NMConnection *settings_connection = NM_CONNECTION (nm_device_get_settings_connection (self));
+		if (   nm_device_sys_iface_state_is_external (self)
+		    && (settings_connection = nm_device_get_settings_connection (self))
+		    && nm_settings_connection_get_nm_generated (settings_connection)
+		    && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request)) == NM_ACTIVATION_TYPE_EXTERNAL) {
 			NMSetting *s_ip4;
 
 			g_object_freeze_notify (G_OBJECT (settings_connection));
 
-			nm_connection_remove_setting (settings_connection, NM_TYPE_SETTING_IP4_CONFIG);
+			nm_connection_remove_setting (NM_CONNECTION (settings_connection), NM_TYPE_SETTING_IP4_CONFIG);
 			s_ip4 = nm_ip4_config_create_setting (priv->ip4_config);
-			nm_connection_add_setting (settings_connection, s_ip4);
+			nm_connection_add_setting (NM_CONNECTION (settings_connection), s_ip4);
 
 			g_object_thaw_notify (G_OBJECT (settings_connection));
 		}
 
 		nm_device_queue_recheck_assume (self);
+	} else if (def_route_changed) {
+		_LOGD (LOGD_IP4, "ip4-config: default route changed");
+		g_signal_emit (self, signals[IP4_CONFIG_CHANGED], 0, priv->ip4_config, priv->ip4_config);
 	}
 
-	if (reason)
-		*reason = reason_local;
-
 	return success;
 }
 
@@ -9310,7 +9851,7 @@ nm_device_replace_vpn4_config (NMDevice *self, NMIP4Config *old, NMIP4Config *co
 		return;
 
 	/* NULL to use existing configs */
-	if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL))
+	if (!ip4_config_merge_and_apply (self, NULL, TRUE))
 		_LOGW (LOGD_IP4, "failed to set VPN routes for device");
 }
 
@@ -9327,7 +9868,7 @@ nm_device_set_wwan_ip4_config (NMDevice *self, NMIP4Config *config)
 		priv->wwan_ip4_config = g_object_ref (config);
 
 	/* NULL to use existing configs */
-	if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL))
+	if (!ip4_config_merge_and_apply (self, NULL, TRUE))
 		_LOGW (LOGD_IP4, "failed to set WWAN IPv4 configuration");
 }
 
@@ -9335,14 +9876,13 @@ static gboolean
 nm_device_set_ip6_config (NMDevice *self,
                           NMIP6Config *new_config,
                           gboolean commit,
-                          gboolean routes_full_sync,
-                          NMDeviceStateReason *reason)
+                          gboolean routes_full_sync)
 {
 	NMDevicePrivate *priv;
 	NMIP6Config *old_config = NULL;
 	gboolean has_changes = FALSE;
 	gboolean success = TRUE;
-	NMDeviceStateReason reason_local = NM_DEVICE_STATE_REASON_NONE;
+	gboolean def_route_changed;
 	int ip_ifindex, config_ifindex;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
@@ -9365,10 +9905,10 @@ nm_device_set_ip6_config (NMDevice *self,
 	if (commit && new_config) {
 		_commit_mtu (self, priv->ip4_config);
 		success = nm_ip6_config_commit (new_config,
+		                                nm_device_get_platform (self),
+		                                nm_netns_get_route_manager (priv->netns),
 		                                ip_ifindex,
 		                                routes_full_sync);
-		if (!success)
-			reason_local = NM_DEVICE_STATE_REASON_CONFIG_FAILED;
 	}
 
 	if (new_config) {
@@ -9393,13 +9933,16 @@ nm_device_set_ip6_config (NMDevice *self,
 	} else if (old_config) {
 		has_changes = TRUE;
 		priv->ip6_config = NULL;
+		priv->needs_ip6_subnet = FALSE;
 		_LOGD (LOGD_IP6, "ip6-config: clear IP6Config instance (%s)",
 		       nm_exported_object_get_path (NM_EXPORTED_OBJECT (old_config)));
 	}
 
-	nm_default_route_manager_ip6_update_default_route (nm_default_route_manager_get (), self);
+	def_route_changed = nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self);
 
 	if (has_changes) {
+		NMSettingsConnection *settings_connection;
+
 		if (old_config != priv->ip6_config)
 			_notify (self, PROP_IP6_CONFIG);
 		g_signal_emit (self, signals[IP6_CONFIG_CHANGED], 0, priv->ip6_config, old_config);
@@ -9407,15 +9950,17 @@ nm_device_set_ip6_config (NMDevice *self,
 		if (old_config != priv->ip6_config)
 			nm_exported_object_clear_and_unexport (&old_config);
 
-		if (nm_device_uses_generated_assumed_connection (self)) {
-			NMConnection *settings_connection = NM_CONNECTION (nm_device_get_settings_connection (self));
+		if (   nm_device_sys_iface_state_is_external (self)
+		    && (settings_connection = nm_device_get_settings_connection (self))
+		    && nm_settings_connection_get_nm_generated (settings_connection)
+		    && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request)) == NM_ACTIVATION_TYPE_EXTERNAL) {
 			NMSetting *s_ip6;
 
 			g_object_freeze_notify (G_OBJECT (settings_connection));
 
-			nm_connection_remove_setting (settings_connection, NM_TYPE_SETTING_IP6_CONFIG);
+			nm_connection_remove_setting (NM_CONNECTION (settings_connection), NM_TYPE_SETTING_IP6_CONFIG);
 			s_ip6 = nm_ip6_config_create_setting (priv->ip6_config);
-			nm_connection_add_setting (settings_connection, s_ip6);
+			nm_connection_add_setting (NM_CONNECTION (settings_connection), s_ip6);
 
 			g_object_thaw_notify (G_OBJECT (settings_connection));
 		}
@@ -9424,11 +9969,11 @@ nm_device_set_ip6_config (NMDevice *self,
 
 		if (priv->ndisc)
 			ndisc_set_router_config (priv->ndisc, self);
+	} else if (def_route_changed) {
+		_LOGD (LOGD_IP6, "ip6-config: default route changed");
+		g_signal_emit (self, signals[IP6_CONFIG_CHANGED], 0, priv->ip6_config, priv->ip6_config);
 	}
 
-	if (reason)
-		*reason = reason_local;
-
 	return success;
 }
 
@@ -9441,7 +9986,7 @@ nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *co
 		return;
 
 	/* NULL to use existing configs */
-	if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+	if (!ip6_config_merge_and_apply (self, TRUE))
 		_LOGW (LOGD_IP6, "failed to set VPN routes for device");
 }
 
@@ -9458,7 +10003,7 @@ nm_device_set_wwan_ip6_config (NMDevice *self, NMIP6Config *config)
 		priv->wwan_ip6_config = g_object_ref (config);
 
 	/* NULL to use existing configs */
-	if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+	if (!ip6_config_merge_and_apply (self, TRUE))
 		_LOGW (LOGD_IP6, "failed to set WWAN IPv6 configuration");
 }
 
@@ -9522,13 +10067,12 @@ ip_check_pre_up (NMDevice *self)
 
 	priv->dispatcher.post_state = NM_DEVICE_STATE_SECONDARIES;
 	priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
-	if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_UP,
-	                         nm_device_get_settings_connection (self),
-	                         nm_device_get_applied_connection (self),
-	                         self,
-	                         dispatcher_complete_proceed_state,
-	                         self,
-	                         &priv->dispatcher.call_id)) {
+	if (!nm_dispatcher_call_device (NM_DISPATCHER_ACTION_PRE_UP,
+	                                self,
+	                                NULL,
+	                                dispatcher_complete_proceed_state,
+	                                self,
+	                                &priv->dispatcher.call_id)) {
 		/* Just proceed on errors */
 		dispatcher_complete_proceed_state (0, self);
 	}
@@ -9759,7 +10303,7 @@ nm_device_is_up (NMDevice *self)
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
 
 	ifindex = nm_device_get_ip_ifindex (self);
-	return ifindex > 0 ? nm_platform_link_is_up (NM_PLATFORM_GET, ifindex) : TRUE;
+	return ifindex > 0 ? nm_platform_link_is_up (nm_device_get_platform (self), ifindex) : TRUE;
 }
 
 gboolean
@@ -9784,7 +10328,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 	if (ifindex <= 0) {
 		/* assume success. */
 	} else {
-		if (!nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, no_firmware))
+		if (!nm_platform_link_set_up (nm_device_get_platform (self), ifindex, no_firmware))
 			return FALSE;
 	}
 
@@ -9798,7 +10342,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 
 		do {
 			g_usleep (200);
-			if (!nm_platform_link_refresh (NM_PLATFORM_GET, ifindex))
+			if (!nm_platform_link_refresh (nm_device_get_platform (self), ifindex))
 				return FALSE;
 			device_is_up = nm_device_is_up (self);
 		} while (!device_is_up && nm_utils_get_monotonic_timestamp_us () < wait_until);
@@ -9837,11 +10381,11 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 
 	/* when the link comes up, we must restore IP configuration if necessary. */
 	if (priv->ip4_state == IP_DONE) {
-		if (!ip4_config_merge_and_apply (self, NULL, TRUE, NULL))
+		if (!ip4_config_merge_and_apply (self, NULL, TRUE))
 			_LOGW (LOGD_IP4, "failed applying IP4 config after bringing link up");
 	}
 	if (priv->ip6_state == IP_DONE) {
-		if (!ip6_config_merge_and_apply (self, TRUE, NULL))
+		if (!ip6_config_merge_and_apply (self, TRUE))
 			_LOGW (LOGD_IP6, "failed applying IP6 config after bringing link up");
 	}
 
@@ -9863,7 +10407,7 @@ nm_device_take_down (NMDevice *self, gboolean block)
 		return;
 	}
 
-	if (!nm_platform_link_set_down (NM_PLATFORM_GET, ifindex))
+	if (!nm_platform_link_set_down (nm_device_get_platform (self), ifindex))
 		return;
 
 	device_is_up = nm_device_is_up (self);
@@ -9872,7 +10416,7 @@ nm_device_take_down (NMDevice *self, gboolean block)
 
 		do {
 			g_usleep (200);
-			if (!nm_platform_link_refresh (NM_PLATFORM_GET, ifindex))
+			if (!nm_platform_link_refresh (nm_device_get_platform (self), ifindex))
 				return;
 			device_is_up = nm_device_is_up (self);
 		} while (device_is_up && nm_utils_get_monotonic_timestamp_us () < wait_until);
@@ -10055,7 +10599,9 @@ update_ip4_config (NMDevice *self, gboolean initial)
 
 	/* IPv4 */
 	g_clear_object (&priv->ext_ip4_config);
-	priv->ext_ip4_config = nm_ip4_config_capture (ifindex, capture_resolv_conf);
+	priv->ext_ip4_config = nm_ip4_config_capture (nm_device_get_platform (self),
+	                                              ifindex,
+	                                              capture_resolv_conf);
 	if (priv->ext_ip4_config) {
 		if (initial) {
 			g_clear_object (&priv->dev_ip4_config);
@@ -10094,7 +10640,7 @@ update_ip4_config (NMDevice *self, gboolean initial)
 		if (priv->wwan_ip4_config)
 			nm_ip4_config_subtract (priv->ext_ip4_config, priv->wwan_ip4_config);
 
-		ip4_config_merge_and_apply (self, NULL, FALSE, NULL);
+		ip4_config_merge_and_apply (self, NULL, FALSE);
 	}
 }
 
@@ -10147,7 +10693,7 @@ update_ip6_config (NMDevice *self, gboolean initial)
 	/* IPv6 */
 	g_clear_object (&priv->ext_ip6_config);
 	g_clear_object (&priv->ext_ip6_config_captured);
-	priv->ext_ip6_config_captured = nm_ip6_config_capture (ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
+	priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_platform (self), ifindex, capture_resolv_conf, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN);
 	if (priv->ext_ip6_config_captured) {
 
 		priv->ext_ip6_config = nm_ip6_config_new_cloned (priv->ext_ip6_config_captured);
@@ -10179,7 +10725,7 @@ update_ip6_config (NMDevice *self, gboolean initial)
 			nm_ip6_config_subtract (priv->ext_ip6_config, priv->wwan_ip6_config);
 		g_slist_foreach (priv->vpn6_configs, _ip6_config_subtract, priv->ext_ip6_config);
 
-		ip6_config_merge_and_apply (self, FALSE, NULL);
+		ip6_config_merge_and_apply (self, FALSE);
 	}
 
 	if (   priv->linklocal6_timeout_id
@@ -10245,7 +10791,7 @@ queued_ip6_config_change (gpointer user_data)
 	update_ip6_config (self, FALSE);
 
 	if (priv->state < NM_DEVICE_STATE_DEACTIVATING
-	    && nm_platform_link_get (NM_PLATFORM_GET, priv->ifindex)) {
+	    && nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) {
 		/* Handle DAD failures */
 		for (iter = priv->dad6_failed_addrs; iter; iter = g_slist_next (iter)) {
 			NMPlatformIP6Address *addr = iter->data;
@@ -10573,6 +11119,8 @@ _set_unmanaged_flags (NMDevice *self,
 	const char *operation = NULL;
 	char str1[512];
 	char str2[512];
+	gboolean do_notify_has_pending_actions = FALSE;
+	gboolean had_pending_actions = FALSE;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 	g_return_if_fail (flags);
@@ -10608,6 +11156,11 @@ _set_unmanaged_flags (NMDevice *self,
 			nm_assert_se (!nm_clear_g_source (&priv->queued_ip6_config_id));
 			priv->queued_ip6_config_id = g_idle_add (queued_ip6_config_change, self);
 		}
+
+		if (!priv->pending_actions) {
+			do_notify_has_pending_actions = TRUE;
+			had_pending_actions = nm_device_has_pending_action (self);
+		}
 	}
 
 	old_flags = priv->unmanaged_flags;
@@ -10643,19 +11196,16 @@ _set_unmanaged_flags (NMDevice *self,
 	                       || (   !was_managed
 	                           && nm_device_get_state (self) == NM_DEVICE_STATE_UNMANAGED));
 
-#define _FMTX "[%s%s0x%0x/0x%x/%s"
-#define _FMT(flags, mask, str) \
-	_unmanaged_flags2str ((flags), (mask), str, sizeof (str)), \
-	((flags) | (mask)) ? "=" : "", \
-	(flags), \
-	(mask), \
-	(_get_managed_by_flags (flags, mask, FALSE) \
-	     ? "managed" \
-	     : (_get_managed_by_flags (flags, mask, TRUE) \
-	            ? "manageable" \
-	            : "unmanaged"))
-	_LOGD (LOGD_DEVICE, "unmanaged: flags set to "_FMTX"%s, %s [%s=0x%0x]%s%s%s)",
-	       _FMT (priv->unmanaged_flags, priv->unmanaged_mask, str1),
+	_LOGD (LOGD_DEVICE, "unmanaged: flags set to [%s%s0x%0x/0x%x/%s%s], %s [%s=0x%0x]%s%s%s)",
+	       _unmanaged_flags2str (priv->unmanaged_flags, priv->unmanaged_mask, str1, sizeof (str1)), \
+	       (priv->unmanaged_flags | priv->unmanaged_mask) ? "=" : "", \
+	       (guint) priv->unmanaged_flags, \
+	       (guint) priv->unmanaged_mask, \
+	       (_get_managed_by_flags (priv->unmanaged_flags, priv->unmanaged_mask, FALSE) \
+	            ? "managed" \
+	            : (_get_managed_by_flags (priv->unmanaged_flags, priv->unmanaged_mask, TRUE) \
+	                   ? "manageable" \
+	                   : "unmanaged")),
 	       priv->real ? "" : "/unrealized",
 	       operation,
 	       nm_unmanaged_flags2str (flags, str2, sizeof (str2)),
@@ -10665,7 +11215,10 @@ _set_unmanaged_flags (NMDevice *self,
 	                            reason_to_string (reason),
 	                            transition_state ? ", transition-state" : "",
 	                            ""));
-#undef _FMT
+
+	if (   do_notify_has_pending_actions
+	    && had_pending_actions != nm_device_has_pending_action (self))
+		_notify (self, PROP_HAS_PENDING_ACTION);
 
 	if (transition_state) {
 		new_state = was_managed ? NM_DEVICE_STATE_UNMANAGED : NM_DEVICE_STATE_UNAVAILABLE;
@@ -10762,7 +11315,7 @@ nm_device_set_unmanaged_by_user_udev (NMDevice *self)
 	ifindex = self->_priv->ifindex;
 
 	if (   ifindex <= 0
-	    || !nm_platform_link_get_unmanaged (NM_PLATFORM_GET, ifindex, &platform_unmanaged))
+	    || !nm_platform_link_get_unmanaged (nm_device_get_platform (self), ifindex, &platform_unmanaged))
 		return;
 
 	nm_device_set_unmanaged_by_flags (self,
@@ -10847,7 +11400,7 @@ nm_device_reapply_settings_immediately (NMDevice *self)
 	               nm_setting_connection_get_zone (s_con_applied)) != 0) {
 
 		version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->_priv->act_request);
-		_LOGD (LOGD_DEVICE, "reapply setting: zone = %s%s%s (version-id %llu)", NM_PRINT_FMT_QUOTE_STRING (zone), (long long unsigned) version_id);
+		_LOGD (LOGD_DEVICE, "reapply setting: zone = %s%s%s (version-id %llu)", NM_PRINT_FMT_QUOTE_STRING (zone), (unsigned long long) version_id);
 
 		g_object_set (G_OBJECT (s_con_applied),
 		              NM_SETTING_CONNECTION_ZONE, zone,
@@ -10859,7 +11412,7 @@ nm_device_reapply_settings_immediately (NMDevice *self)
 	if ((metered = nm_setting_connection_get_metered (s_con_settings)) != nm_setting_connection_get_metered (s_con_applied)) {
 
 		version_id = nm_active_connection_version_id_bump ((NMActiveConnection *) self->_priv->act_request);
-		_LOGD (LOGD_DEVICE, "reapply setting: metered = %d (version-id %llu)", (int) metered, (long long unsigned) version_id);
+		_LOGD (LOGD_DEVICE, "reapply setting: metered = %d (version-id %llu)", (int) metered, (unsigned long long) version_id);
 
 		g_object_set (G_OBJECT (s_con_applied),
 		              NM_SETTING_CONNECTION_METERED, metered,
@@ -10872,25 +11425,15 @@ nm_device_reapply_settings_immediately (NMDevice *self)
 void
 nm_device_update_firewall_zone (NMDevice *self)
 {
-	NMConnection *applied_connection;
-	NMSettingConnection *s_con;
+	NMDevicePrivate *priv;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
-	applied_connection = nm_device_get_applied_connection (self);
-	if (!applied_connection)
-		return;
+	priv = NM_DEVICE_GET_PRIVATE (self);
 
-	s_con = nm_connection_get_setting_connection (applied_connection);
-	if (   nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED
-	    && !nm_device_uses_generated_assumed_connection (self)) {
-		nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (),
-		                                        nm_device_get_ip_iface (self),
-		                                        nm_setting_connection_get_zone (s_con),
-		                                        FALSE, /* change zone */
-		                                        NULL,
-		                                        NULL);
-	}
+	if (   priv->fw_state >= FIREWALL_STATE_INITIALIZED
+	    && !nm_device_sys_iface_state_is_external (self))
+		fw_change_zone (self);
 }
 
 void
@@ -11234,7 +11777,7 @@ cp_connection_removed (NMConnectionProvider *cp, NMConnection *connection, gpoin
 gboolean
 nm_device_supports_vlans (NMDevice *self)
 {
-	return nm_platform_link_supports_vlans (NM_PLATFORM_GET, nm_device_get_ifindex (self));
+	return nm_platform_link_supports_vlans (nm_device_get_platform (self), nm_device_get_ifindex (self));
 }
 
 /**
@@ -11340,7 +11883,16 @@ nm_device_has_pending_action (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	return !!priv->pending_actions;
+	if (priv->pending_actions)
+		return TRUE;
+
+	if (nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) {
+		/* as long as the platform link is not yet initialized, we have a pending
+		 * action. */
+		return TRUE;
+	}
+
+	return FALSE;
 }
 
 /*****************************************************************************/
@@ -11350,13 +11902,12 @@ _cancel_activation (NMDevice *self)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
-	/* Clean up when device was deactivated during call to firewall */
 	if (priv->fw_call) {
 		nm_firewall_manager_cancel_call (priv->fw_call);
-		g_warn_if_fail (!priv->fw_call);
+		nm_assert (!priv->fw_call);
 		priv->fw_call = NULL;
+		priv->fw_state = FIREWALL_STATE_INITIALIZED;
 	}
-	priv->fw_ready = FALSE;
 
 	ip_check_gw_ping_cleanup (self);
 
@@ -11368,20 +11919,22 @@ _cancel_activation (NMDevice *self)
 static void
 _cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type)
 {
-	NMConnection *connection;
+	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 
 	_cancel_activation (self);
 
-	connection = nm_device_get_applied_connection (self);
 	if (   cleanup_type == CLEANUP_TYPE_DECONFIGURE
-	    && connection
-	    && !nm_device_uses_generated_assumed_connection (self)) {
-		nm_firewall_manager_remove_from_zone (nm_firewall_manager_get (),
+	    && priv->fw_state >= FIREWALL_STATE_INITIALIZED
+	    && priv->fw_mgr
+	    && !nm_device_sys_iface_state_is_external (self)) {
+		nm_firewall_manager_remove_from_zone (priv->fw_mgr,
 		                                      nm_device_get_ip_iface (self),
 		                                      NULL,
 		                                      NULL,
 		                                      NULL);
 	}
+	priv->fw_state = FIREWALL_STATE_UNMANAGED;
+	g_clear_object (&priv->fw_mgr);
 
 	queued_state_clear (self);
 
@@ -11412,8 +11965,8 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 	/* Clean up IP configs; this does not actually deconfigure the
 	 * interface; the caller must flush routes and addresses explicitly.
 	 */
-	nm_device_set_ip4_config (self, NULL, 0, TRUE, TRUE, NULL);
-	nm_device_set_ip6_config (self, NULL, TRUE, TRUE, NULL);
+	nm_device_set_ip4_config (self, NULL, 0, TRUE, TRUE);
+	nm_device_set_ip6_config (self, NULL, TRUE, TRUE);
 	g_clear_object (&priv->proxy_config);
 	g_clear_object (&priv->con_ip4_config);
 	g_clear_object (&priv->dev_ip4_config);
@@ -11433,7 +11986,9 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 	g_slist_free_full (priv->vpn6_configs, g_object_unref);
 	priv->vpn6_configs = NULL;
 
-	priv->needs_ip6_subnet = FALSE;
+	/* We no longer accept the delegations. nm_device_set_ip6_config(NULL)
+	 * above disables them. */
+	nm_assert (priv->needs_ip6_subnet == FALSE);
 
 	if (priv->act_request) {
 		nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request), FALSE);
@@ -11499,20 +12054,20 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 	if (NM_DEVICE_GET_CLASS (self)->deactivate)
 		NM_DEVICE_GET_CLASS (self)->deactivate (self);
 
-	if (cleanup_type != CLEANUP_TYPE_KEEP) {
+	if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) {
 		/* master: release slaves */
 		nm_device_master_release_slaves (self);
 
 		/* slave: mark no longer enslaved */
 		if (   priv->master
-		    && nm_platform_link_get_master (NM_PLATFORM_GET, priv->ifindex) <= 0)
+		    && nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) <= 0)
 			nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 
 		/* Take out any entries in the routing table and any IP address the device had. */
 		ifindex = nm_device_get_ip_ifindex (self);
 		if (ifindex > 0) {
-			nm_route_manager_route_flush (nm_route_manager_get (), ifindex);
-			nm_platform_address_flush (NM_PLATFORM_GET, ifindex);
+			nm_route_manager_route_flush (nm_netns_get_route_manager (priv->netns), ifindex);
+			nm_platform_address_flush (nm_device_get_platform (self), ifindex);
 		}
 	}
 
@@ -11543,7 +12098,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 			_LOGT (LOGD_DEVICE, "mtu: reset device-mtu: %u, ipv6-mtu: %u, ifindex: %d",
 			       (guint) priv->mtu_initial, (guint) priv->ip6_mtu_initial, ifindex);
 			if (priv->mtu_initial)
-				nm_platform_link_set_mtu (NM_PLATFORM_GET, ifindex, priv->mtu_initial);
+				nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, priv->mtu_initial);
 			if (priv->ip6_mtu_initial) {
 				char sbuf[64];
 
@@ -11655,7 +12210,7 @@ nm_device_spawn_iface_helper (NMDevice *self)
 			g_ptr_array_add (argv, g_strdup ("--dhcp4-required"));
 
 		if (priv->dhcp4.client) {
-			const char *hostname, *fqdn;
+			const char *hostname;
 			GBytes *client_id;
 
 			client_id = nm_dhcp_client_get_client_id (priv->dhcp4.client);
@@ -11669,15 +12224,12 @@ nm_device_spawn_iface_helper (NMDevice *self)
 
 			hostname = nm_dhcp_client_get_hostname (priv->dhcp4.client);
 			if (hostname) {
-				g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname"));
+				if (nm_dhcp_client_get_use_fqdn (priv->dhcp4.client))
+					g_ptr_array_add (argv, g_strdup ("--dhcp4-fqdn"));
+				else
+					g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname"));
 				g_ptr_array_add (argv, g_strdup (hostname));
 			}
-
-			fqdn = nm_dhcp_client_get_fqdn (priv->dhcp4.client);
-			if (fqdn) {
-				g_ptr_array_add (argv, g_strdup ("--dhcp4-fqdn"));
-				g_ptr_array_add (argv, g_strdup (fqdn));
-			}
 		}
 
 		configured = TRUE;
@@ -11788,9 +12340,8 @@ deactivate_async_ready (NMDevice *self,
 	if (   g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)
 	    || (priv->deactivating_cancellable && g_cancellable_is_cancelled (priv->deactivating_cancellable))) {
 		_LOGW (LOGD_DEVICE, "Deactivation cancelled");
-	}
-	/* In every other case, transition to the DISCONNECTED state */
-	else {
+	} else {
+		/* In every other case, transition to the DISCONNECTED state */
 		if (error) {
 			_LOGW (LOGD_DEVICE, "Deactivation failed: %s",
 			       error->message);
@@ -11818,11 +12369,8 @@ deactivate_dispatcher_complete (guint call_id, gpointer user_data)
 	priv->dispatcher.post_state = NM_DEVICE_STATE_UNKNOWN;
 	priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE;
 
-	if (priv->deactivating_cancellable) {
+	if (nm_clear_g_cancellable (&priv->deactivating_cancellable))
 		g_warn_if_reached ();
-		g_cancellable_cancel (priv->deactivating_cancellable);
-		g_clear_object (&priv->deactivating_cancellable);
-	}
 
 	if (   NM_DEVICE_GET_CLASS (self)->deactivate_async
 	    && NM_DEVICE_GET_CLASS (self)->deactivate_async_finish) {
@@ -11846,7 +12394,6 @@ _set_state_full (NMDevice *self,
 	NMActRequest *req;
 	gboolean no_firmware = FALSE;
 	NMSettingsConnection *connection;
-	NMConnection *applied_connection;
 
 	g_return_if_fail (NM_IS_DEVICE (self));
 
@@ -11897,6 +12444,15 @@ _set_state_full (NMDevice *self,
 	/* Cache the activation request for the dispatcher */
 	req = nm_g_object_ref (priv->act_request);
 
+	if (   state >  NM_DEVICE_STATE_UNMANAGED
+	    && state <= NM_DEVICE_STATE_ACTIVATED
+	    && nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_NOW_MANAGED
+	    && NM_IN_SET_TYPED (NMDeviceSysIfaceState,
+	                        priv->sys_iface_state,
+	                        NM_DEVICE_SYS_IFACE_STATE_EXTERNAL,
+	                        NM_DEVICE_SYS_IFACE_STATE_ASSUME))
+		nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
+
 	if (state <= NM_DEVICE_STATE_UNAVAILABLE) {
 		if (available_connections_del_all (self))
 			_notify (self, PROP_AVAILABLE_CONNECTIONS);
@@ -11920,10 +12476,11 @@ _set_state_full (NMDevice *self,
 	case NM_DEVICE_STATE_UNMANAGED:
 		nm_device_set_firmware_missing (self, FALSE);
 		if (old_state > NM_DEVICE_STATE_UNMANAGED) {
-			if (reason == NM_DEVICE_STATE_REASON_REMOVED) {
-				nm_device_cleanup (self, reason, CLEANUP_TYPE_REMOVED);
-			} else if (reason == NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
-				nm_device_cleanup (self, reason, CLEANUP_TYPE_KEEP);
+			if (priv->sys_iface_state != NM_DEVICE_SYS_IFACE_STATE_MANAGED) {
+				nm_device_cleanup (self, reason,
+				                   priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_REMOVED
+				                       ? CLEANUP_TYPE_REMOVED
+				                       : CLEANUP_TYPE_KEEP);
 			} else {
 				/* Clean up if the device is now unmanaged but was activated */
 				if (nm_device_get_act_request (self))
@@ -11932,17 +12489,18 @@ _set_state_full (NMDevice *self,
 				nm_device_hw_addr_reset (self, "unmanage");
 				set_nm_ipv6ll (self, FALSE);
 				restore_ip6_properties (self);
+				break;
 			}
 		}
 		break;
 	case NM_DEVICE_STATE_UNAVAILABLE:
 		if (old_state == NM_DEVICE_STATE_UNMANAGED) {
 			save_ip6_properties (self);
-			if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED)
+			if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED)
 				ip6_managed_setup (self);
 		}
 
-		if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
+		if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED) {
 			if (old_state == NM_DEVICE_STATE_UNMANAGED || priv->firmware_missing) {
 				if (!nm_device_bring_up (self, TRUE, &no_firmware) && no_firmware)
 					_LOGW (LOGD_PLATFORM, "firmware may be missing.");
@@ -11969,7 +12527,7 @@ _set_state_full (NMDevice *self,
 
 			nm_device_cleanup (self, reason, CLEANUP_TYPE_DECONFIGURE);
 		} else if (old_state < NM_DEVICE_STATE_DISCONNECTED) {
-			if (reason != NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
+			if (priv->sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED) {
 				/* Ensure IPv6 is set up as it may not have been done when
 				 * entering the UNAVAILABLE state depending on the reason.
 				 */
@@ -11996,11 +12554,11 @@ _set_state_full (NMDevice *self,
 	/* Reset autoconnect flag when the device is activating or connected. */
 	if (   state >= NM_DEVICE_STATE_PREPARE
 	    && state <= NM_DEVICE_STATE_ACTIVATED)
-		nm_device_set_autoconnect (self, TRUE);
+		nm_device_set_autoconnect_intern  (self, TRUE);
 
 	_notify (self, PROP_STATE);
 	_notify (self, PROP_STATE_REASON);
-	g_signal_emit (self, signals[STATE_CHANGED], 0, state, old_state, reason);
+	g_signal_emit (self, signals[STATE_CHANGED], 0, (guint) state, (guint) old_state, (guint) reason);
 
 	/* Post-process the event after internal notification */
 
@@ -12028,27 +12586,24 @@ _set_state_full (NMDevice *self,
 		priv->ignore_carrier = nm_config_data_get_ignore_carrier (NM_CONFIG_GET_DATA, self);
 
 		if (quitting) {
-			nm_dispatcher_call_sync (DISPATCHER_ACTION_PRE_DOWN,
-			                         nm_act_request_get_settings_connection (req),
-			                         nm_act_request_get_applied_connection (req),
-			                         self);
+			nm_dispatcher_call_device_sync (NM_DISPATCHER_ACTION_PRE_DOWN,
+			                                self, req);
 		} else {
 			priv->dispatcher.post_state = NM_DEVICE_STATE_DISCONNECTED;
 			priv->dispatcher.post_state_reason = reason;
-			if (!nm_dispatcher_call (DISPATCHER_ACTION_PRE_DOWN,
-			                         nm_act_request_get_settings_connection (req),
-			                         nm_act_request_get_applied_connection (req),
-			                         self,
-			                         deactivate_dispatcher_complete,
-			                         self,
-			                         &priv->dispatcher.call_id)) {
+			if (!nm_dispatcher_call_device (NM_DISPATCHER_ACTION_PRE_DOWN,
+			                                self,
+			                                req,
+			                                deactivate_dispatcher_complete,
+			                                self,
+			                                &priv->dispatcher.call_id)) {
 				/* Just proceed on errors */
 				deactivate_dispatcher_complete (0, self);
 			}
 		}
 
-		/* Remove config from PacRunner */
-		nm_pacrunner_manager_remove (priv->pacrunner_manager, nm_device_get_ip_iface (self));
+		nm_pacrunner_manager_remove_clear (priv->pacrunner_manager,
+		                                   &priv->pacrunner_call_id);
 		break;
 	case NM_DEVICE_STATE_DISCONNECTED:
 		if (   priv->queued_act_request
@@ -12068,18 +12623,13 @@ _set_state_full (NMDevice *self,
 	case NM_DEVICE_STATE_ACTIVATED:
 		_LOGI (LOGD_DEVICE, "Activation: successful, device activated.");
 		nm_device_update_metered (self);
-		nm_dispatcher_call (DISPATCHER_ACTION_UP,
-		                    nm_act_request_get_settings_connection (req),
-		                    nm_act_request_get_applied_connection (req),
-		                    self, NULL, NULL, NULL);
-
-		if (priv->proxy_config) {
-			nm_pacrunner_manager_send (priv->pacrunner_manager,
-			                           nm_device_get_ip_iface (self),
-			                           priv->proxy_config,
-			                           priv->ip4_config,
-			                           priv->ip6_config);
-		}
+		nm_dispatcher_call_device (NM_DISPATCHER_ACTION_UP,
+		                           self,
+		                           req,
+		                           NULL, NULL, NULL);
+
+		if (priv->proxy_config)
+			_pacrunner_manager_send (self);
 		break;
 	case NM_DEVICE_STATE_FAILED:
 		/* Usually upon failure the activation chain is interrupted in
@@ -12090,7 +12640,7 @@ _set_state_full (NMDevice *self,
 		 */
 		_cancel_activation (self);
 
-		if (nm_device_uses_assumed_connection (self)) {
+		if (nm_device_sys_iface_state_is_external_or_assume (self)) {
 			/* Avoid tearing down assumed connection, assume it's connected */
 			nm_device_queue_state (self,
 			                       NM_DEVICE_STATE_ACTIVATED,
@@ -12121,26 +12671,11 @@ _set_state_full (NMDevice *self,
 		nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_NONE);
 		break;
 	case NM_DEVICE_STATE_IP_CHECK:
-		/* Now that IP config has completed, check if the firewall
-		 * zone must be set again for the IP interface.
-		 */
-		applied_connection = nm_device_get_applied_connection (self);
-
-		if (   applied_connection
-		    && priv->ifindex != priv->ip_ifindex
-		    && !nm_device_uses_generated_assumed_connection (self)) {
-			NMSettingConnection *s_con;
-			const char *zone;
-
-			s_con = nm_connection_get_setting_connection (applied_connection);
-			zone = nm_setting_connection_get_zone (s_con);
-			g_assert (!priv->fw_call);
-			priv->fw_call = nm_firewall_manager_add_or_change_zone (nm_firewall_manager_get (),
-			                                                        nm_device_get_ip_iface (self),
-			                                                        zone,
-			                                                        FALSE,
-			                                                        fw_change_zone_cb_ip_check,
-			                                                        self);
+		if (   priv->fw_state >= FIREWALL_STATE_INITIALIZED
+		    && priv->ip_iface
+		    && !nm_device_sys_iface_state_is_external (self)) {
+			priv->fw_state = FIREWALL_STATE_WAIT_IP_CONFIG;
+			fw_change_zone (self);
 		} else
 			nm_device_start_ip_check (self);
 
@@ -12163,15 +12698,13 @@ _set_state_full (NMDevice *self,
 	if (   (old_state == NM_DEVICE_STATE_ACTIVATED || old_state == NM_DEVICE_STATE_DEACTIVATING)
 	    && (state != NM_DEVICE_STATE_DEACTIVATING)) {
 		if (quitting) {
-			nm_dispatcher_call_sync (DISPATCHER_ACTION_DOWN,
-			                         nm_act_request_get_settings_connection (req),
-			                         nm_act_request_get_applied_connection (req),
-			                         self);
+			nm_dispatcher_call_device_sync (NM_DISPATCHER_ACTION_DOWN,
+			                                self, req);
 		} else {
-			nm_dispatcher_call (DISPATCHER_ACTION_DOWN,
-			                    nm_act_request_get_settings_connection (req),
-			                    nm_act_request_get_applied_connection (req),
-			                    self, NULL, NULL, NULL);
+			nm_dispatcher_call_device (NM_DISPATCHER_ACTION_DOWN,
+			                           self,
+			                           req,
+			                           NULL, NULL, NULL);
 		}
 	}
 
@@ -12181,6 +12714,8 @@ _set_state_full (NMDevice *self,
 	if (ip_config_valid (old_state) && !ip_config_valid (state))
 	    notify_ip_properties (self);
 
+	concheck_periodic_update (self);
+
 	/* Dispose of the cached activation request */
 	if (req)
 		g_object_unref (req);
@@ -12334,7 +12869,7 @@ nm_device_update_hw_address (NMDevice *self)
 	if (priv->ifindex <= 0)
 		return FALSE;
 
-	hwaddr = nm_platform_link_get_address (NM_PLATFORM_GET, priv->ifindex, &hwaddrlen);
+	hwaddr = nm_platform_link_get_address (nm_device_get_platform (self), priv->ifindex, &hwaddrlen);
 
 	if (   priv->type == NM_DEVICE_TYPE_ETHERNET
 	    && hwaddr
@@ -12428,7 +12963,7 @@ nm_device_update_permanent_hw_address (NMDevice *self, gboolean force_freeze)
 
 	/* the user is advised to configure stable MAC addresses for software devices via
 	 * UDEV. Thus, check whether the link is fully initialized. */
-	pllink = nm_platform_link_get (NM_PLATFORM_GET, ifindex);
+	pllink = nm_platform_link_get (nm_device_get_platform (self), ifindex);
 	if (   !pllink
 	    || !pllink->initialized) {
 		if (!force_freeze) {
@@ -12437,7 +12972,7 @@ nm_device_update_permanent_hw_address (NMDevice *self, gboolean force_freeze)
 			return;
 		}
 		/* try to refresh the link just to give UDEV a bit more time... */
-		nm_platform_link_refresh (NM_PLATFORM_GET, ifindex);
+		nm_platform_link_refresh (nm_device_get_platform (self), ifindex);
 		/* maybe the MAC address changed... */
 		nm_device_update_hw_address (self);
 	} else if (!priv->hw_addr_len)
@@ -12451,7 +12986,7 @@ nm_device_update_permanent_hw_address (NMDevice *self, gboolean force_freeze)
 		return;
 	}
 
-	success_read = nm_platform_link_get_permanent_address (NM_PLATFORM_GET, ifindex, buf, &len);
+	success_read = nm_platform_link_get_permanent_address (nm_device_get_platform (self), ifindex, buf, &len);
 	if (success_read && priv->hw_addr_len == len) {
 		priv->hw_addr_perm_fake = FALSE;
 		priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, len);
@@ -12474,7 +13009,7 @@ nm_device_update_permanent_hw_address (NMDevice *self, gboolean force_freeze)
 	{
 		gs_free NMConfigDeviceStateData *dev_state = NULL;
 
-		dev_state = nm_config_device_state_load (nm_config_get (), ifindex);
+		dev_state = nm_config_device_state_load (ifindex);
 		if (   dev_state
 		    && dev_state->perm_hw_addr_fake
 		    && nm_utils_hwaddr_aton (dev_state->perm_hw_addr_fake, buf, priv->hw_addr_len)
@@ -12639,7 +13174,7 @@ _hw_addr_set (NMDevice *self,
 		nm_device_take_down (self, FALSE);
 	}
 
-	plerr = nm_platform_link_set_address (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self), addr_bytes, addr_len);
+	plerr = nm_platform_link_set_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), addr_bytes, addr_len);
 	success = (plerr == NM_PLATFORM_ERROR_SUCCESS);
 	if (success) {
 		/* MAC address succesfully changed; update the current MAC to match */
@@ -12670,7 +13205,7 @@ _hw_addr_set (NMDevice *self,
 
 			poll_end = nm_utils_get_monotonic_timestamp_us () + (100 * 1000);
 			for (;;) {
-				if (!nm_platform_link_refresh (NM_PLATFORM_GET, nm_device_get_ip_ifindex (self)))
+				if (!nm_platform_link_refresh (nm_device_get_platform (self), nm_device_get_ip_ifindex (self)))
 					goto handle_fail;
 				if (!nm_device_update_hw_address (self))
 					goto handle_wait;
@@ -12739,49 +13274,92 @@ nm_device_hw_addr_set (NMDevice *self,
 	return _hw_addr_set (self, addr, "set", detail);
 }
 
-gboolean
-nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean is_wifi)
+/*
+ * _hw_addr_get_cloned:
+ * @self: a #NMDevice
+ * @connection: a #NMConnection
+ * @is_wifi: whether the device is Wi-Fi
+ * @preserve: (out): whether the address must be reset to initial one
+ * @hwaddr: (out): the cloned MAC address to set on interface
+ * @hwaddr_type: (out): the type of address to set
+ * @hwaddr_detail: (out): the detail (origin) of address to set
+ * @error: (out): on return, an error or %NULL
+ *
+ * Computes the MAC to be set on a interface. On success, one of the
+ * following exclusive conditions are verified:
+ *
+ *  - @preserve is %TRUE: the address must be reset to the initial one
+ *  - @hwaddr is not %NULL: the given address must be set on the device
+ *  - @hwaddr is %NULL and @preserve is %FALSE: no action needed
+ *
+ * Returns: %FALSE in case of error in determining the cloned MAC address,
+ * %TRUE otherwise
+ */
+static gboolean
+_hw_addr_get_cloned (NMDevice *self, NMConnection *connection, gboolean is_wifi,
+                     gboolean *preserve, char **hwaddr, HwAddrType *hwaddr_type,
+                     char **hwaddr_detail, GError **error)
 {
 	NMDevicePrivate *priv;
-	gs_free char *hw_addr_tmp = NULL;
+	gs_free char *addr_setting_free = NULL;
 	gs_free char *hw_addr_generated = NULL;
 	gs_free char *generate_mac_address_mask_tmp = NULL;
 	const char *addr, *addr_setting;
+	char *addr_out;
+	HwAddrType type_out;
 
 	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
 
 	priv = NM_DEVICE_GET_PRIVATE (self);
 
 	if (!connection)
 		g_return_val_if_reached (FALSE);
 
-	addr = addr_setting = _get_cloned_mac_address_setting (self, connection, is_wifi, &hw_addr_tmp);
+	addr = addr_setting = _get_cloned_mac_address_setting (self, connection, is_wifi, &addr_setting_free);
 
 	if (nm_streq (addr, NM_CLONED_MAC_PRESERVE)) {
 		/* "preserve" means to reset the initial MAC address. */
-		return nm_device_hw_addr_reset (self, addr_setting);
+		NM_SET_OUT (preserve, TRUE);
+		NM_SET_OUT (hwaddr, NULL);
+		NM_SET_OUT (hwaddr_type, HW_ADDR_TYPE_UNSET);
+		NM_SET_OUT (hwaddr_detail, g_steal_pointer (&addr_setting_free) ?: g_strdup (addr_setting));
+		return TRUE;
 	}
 
 	if (nm_streq (addr, NM_CLONED_MAC_PERMANENT)) {
 		addr = nm_device_get_permanent_hw_address (self);
-		if (!addr)
+		if (!addr) {
+			g_set_error_literal (error,
+			                     NM_DEVICE_ERROR,
+			                     NM_DEVICE_ERROR_FAILED,
+			                     "failed to retrieve permanent address");
 			return FALSE;
-		priv->hw_addr_type = HW_ADDR_TYPE_PERMANENT;
+		}
+		addr_out = g_strdup (addr);
+		type_out = HW_ADDR_TYPE_PERMANENT;
 	} else if (NM_IN_STRSET (addr, NM_CLONED_MAC_RANDOM)) {
 		if (priv->hw_addr_type == HW_ADDR_TYPE_GENERATED) {
 			/* hm, we already use a generate MAC address. Most certainly, that is from the same
 			 * activation request, so we should not create a new random address, instead keep
 			 * the current. */
-			return TRUE;
+			goto out_no_action;
 		}
 		hw_addr_generated = nm_utils_hw_addr_gen_random_eth (nm_device_get_initial_hw_address (self),
-		                                                     _get_generate_mac_address_mask_setting (self, connection, is_wifi, &generate_mac_address_mask_tmp));
+		                                                     _get_generate_mac_address_mask_setting (self, connection,
+		                                                                                             is_wifi,
+		                                                                                             &generate_mac_address_mask_tmp));
 		if (!hw_addr_generated) {
-			_LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "random");
+			g_set_error (error,
+			             NM_DEVICE_ERROR,
+			             NM_DEVICE_ERROR_FAILED,
+			             "failed to generate %s MAC address", "random");
 			return FALSE;
 		}
-		priv->hw_addr_type = HW_ADDR_TYPE_GENERATED;
-		addr = hw_addr_generated;
+
+		addr_out = g_steal_pointer (&hw_addr_generated);
+		type_out = HW_ADDR_TYPE_GENERATED;
 	} else if (NM_IN_STRSET (addr, NM_CLONED_MAC_STABLE)) {
 		NMUtilsStableType stable_type;
 		const char *stable_id;
@@ -12789,7 +13367,7 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean
 		if (priv->hw_addr_type == HW_ADDR_TYPE_GENERATED) {
 			/* hm, we already use a generate MAC address. Most certainly, that is from the same
 			 * activation request, so let's skip creating the stable address anew. */
-			return TRUE;
+			goto out_no_action;
 		}
 
 		stable_id = _get_stable_id (self, connection, &stable_type);
@@ -12800,19 +13378,74 @@ nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean
 			                                                     _get_generate_mac_address_mask_setting (self, connection, is_wifi, &generate_mac_address_mask_tmp));
 		}
 		if (!hw_addr_generated) {
-			_LOGW (LOGD_DEVICE, "set-hw-addr: failed to generate %s MAC address", "stable");
+			g_set_error (error,
+			             NM_DEVICE_ERROR,
+			             NM_DEVICE_ERROR_FAILED,
+			             "failed to generate %s MAC address", "stable");
 			return FALSE;
 		}
-		priv->hw_addr_type = HW_ADDR_TYPE_GENERATED;
-		addr = hw_addr_generated;
+
+		addr_out = g_steal_pointer (&hw_addr_generated);
+		type_out = HW_ADDR_TYPE_GENERATED;
 	} else {
 		/* this must be a valid address. Otherwise, we shouldn't come here. */
 		if (!nm_utils_hwaddr_valid (addr, -1))
 			g_return_val_if_reached (FALSE);
-		priv->hw_addr_type = HW_ADDR_TYPE_EXPLICIT;
+
+		addr_out = g_strdup (addr);
+		type_out = HW_ADDR_TYPE_EXPLICIT;
+	}
+
+	NM_SET_OUT (preserve, FALSE);
+	NM_SET_OUT (hwaddr, addr_out);
+	NM_SET_OUT (hwaddr_type, type_out);
+	NM_SET_OUT (hwaddr_detail, g_steal_pointer (&addr_setting_free) ?: g_strdup (addr_setting));
+	return TRUE;
+out_no_action:
+	NM_SET_OUT (preserve, FALSE);
+	NM_SET_OUT (hwaddr, NULL);
+	NM_SET_OUT (hwaddr_type, HW_ADDR_TYPE_UNSET);
+	NM_SET_OUT (hwaddr_detail, NULL);
+	return TRUE;
+}
+
+gboolean
+nm_device_hw_addr_get_cloned (NMDevice *self, NMConnection *connection, gboolean is_wifi,
+                              char **hwaddr, gboolean *preserve, GError **error)
+{
+	if (!_hw_addr_get_cloned (self, connection, is_wifi, preserve, hwaddr, NULL, NULL, error))
+		return FALSE;
+
+	return TRUE;
+}
+
+gboolean
+nm_device_hw_addr_set_cloned (NMDevice *self, NMConnection *connection, gboolean is_wifi)
+{
+	NMDevicePrivate *priv;
+	gboolean preserve = FALSE;
+	gs_free char *hwaddr = NULL;
+	gs_free char *detail = NULL;
+	HwAddrType type = HW_ADDR_TYPE_UNSET;
+	gs_free_error GError *error = NULL;
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
+	priv = NM_DEVICE_GET_PRIVATE (self);
+
+	if (!_hw_addr_get_cloned (self, connection, is_wifi, &preserve, &hwaddr, &type, &detail, &error)) {
+		_LOGW (LOGD_DEVICE, "set-hw-addr: %s", error->message);
+		return FALSE;
+	}
+
+	if (preserve)
+		return nm_device_hw_addr_reset (self, detail);
+
+	if (hwaddr) {
+		priv->hw_addr_type = type;
+		return _hw_addr_set (self, hwaddr, "set-cloned", detail);
 	}
 
-	return _hw_addr_set (self, addr, "set-cloned", addr_setting);
+	return TRUE;
 }
 
 gboolean
@@ -12908,11 +13541,40 @@ nm_device_spec_match_list (NMDevice *self, const GSList *specs)
 	m = nm_match_spec_device (specs,
 	                          nm_device_get_iface (self),
 	                          nm_device_get_type_description (self),
+	                          nm_device_get_driver (self),
+	                          nm_device_get_driver_version (self),
 	                          nm_device_get_permanent_hw_address (self),
 	                          klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL);
 	return m == NM_MATCH_SPEC_MATCH;
 }
 
+guint
+nm_device_get_supplicant_timeout (NMDevice *self)
+{
+	NMConnection *connection;
+	NMSetting8021x *s_8021x;
+	gs_free char *value = NULL;
+	gint timeout;
+#define SUPPLICANT_DEFAULT_TIMEOUT 25
+
+	g_return_val_if_fail (NM_IS_DEVICE (self), SUPPLICANT_DEFAULT_TIMEOUT);
+
+	connection = nm_device_get_applied_connection (self);
+	g_return_val_if_fail (connection, SUPPLICANT_DEFAULT_TIMEOUT);
+	s_8021x = nm_connection_get_setting_802_1x (connection);
+	if (s_8021x) {
+		timeout = nm_setting_802_1x_get_auth_timeout (s_8021x);
+		if (timeout > 0)
+			return timeout;
+	}
+
+	value = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA,
+	                                               "802-1x.auth-timeout",
+	                                               self);
+	return _nm_utils_ascii_str_to_int64 (value, 10, 1, G_MAXINT32,
+	                                     SUPPLICANT_DEFAULT_TIMEOUT);
+}
+
 /*****************************************************************************/
 
 static const char *
@@ -12944,19 +13606,19 @@ nm_device_init (NMDevice *self)
 
 	self->_priv = priv;
 
+	priv->netns = g_object_ref (NM_NETNS_GET);
+
 	priv->type = NM_DEVICE_TYPE_UNKNOWN;
 	priv->capabilities = NM_DEVICE_CAP_NM_SUPPORTED;
 	priv->state = NM_DEVICE_STATE_UNMANAGED;
 	priv->state_reason = NM_DEVICE_STATE_REASON_NONE;
 	priv->dhcp_timeout = 0;
 	priv->rfkill_type = RFKILL_TYPE_UNKNOWN;
-	priv->autoconnect = DEFAULT_AUTOCONNECT;
 	priv->unmanaged_flags = NM_UNMANAGED_PLATFORM_INIT;
 	priv->unmanaged_mask = priv->unmanaged_flags;
 	priv->available_connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
 	priv->ip6_saved_properties = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
-
-	priv->pacrunner_manager = g_object_ref (nm_pacrunner_manager_get ());
+	priv->sys_iface_state = NM_DEVICE_SYS_IFACE_STATE_EXTERNAL;
 
 	priv->default_route.v4_is_assumed = TRUE;
 	priv->default_route.v6_is_assumed = TRUE;
@@ -12986,7 +13648,7 @@ constructor (GType type,
 
 	if (   priv->iface
 	    && G_LIKELY (!nm_utils_get_testing ())) {
-		pllink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, priv->iface);
+		pllink = nm_platform_link_get_by_ifname (nm_device_get_platform (self), priv->iface);
 
 		if (pllink && link_type_compatible (self, pllink->type, NULL, NULL)) {
 			priv->ifindex = pllink->ifindex;
@@ -13022,13 +13684,16 @@ constructed (GObject *object)
 		priv->capabilities |= NM_DEVICE_GET_CLASS (self)->get_generic_capabilities (self);
 
 	/* Watch for external IP config changes */
-	platform = NM_PLATFORM_GET;
+	platform = nm_device_get_platform (self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self);
 	g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self);
 
+	g_signal_connect (nm_netns_get_route_manager (priv->netns), NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED,
+	                  G_CALLBACK (ip4_routes_changed_changed_cb), self);
+
 	priv->settings = g_object_ref (NM_SETTINGS_GET);
 	g_assert (priv->settings);
 
@@ -13059,21 +13724,28 @@ dispose (GObject *object)
 
 	_LOGD (LOGD_DEVICE, "disposing");
 
+	nm_clear_g_cancellable (&priv->deactivating_cancellable);
+
 	_parent_set_ifindex (self, 0, FALSE);
 
-	platform = NM_PLATFORM_GET;
+	platform = nm_device_get_platform (self);
 	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self);
 	g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self);
 
+	g_signal_handlers_disconnect_by_func (nm_netns_get_route_manager (priv->netns),
+	                                      G_CALLBACK (ip4_routes_changed_changed_cb), self);
+
 	g_slist_free_full (priv->arping.dad_list, (GDestroyNotify) nm_arping_manager_destroy);
 	priv->arping.dad_list = NULL;
 
 	arp_cleanup (self);
 
-	nm_clear_g_signal_handler (nm_config_get (), &priv->ignore_carrier_id);
+	nm_clear_g_signal_handler (nm_config_get (), &priv->config_changed_id);
 
 	dispatcher_cleanup (self);
 
+	nm_pacrunner_manager_remove_clear (priv->pacrunner_manager,
+	                                   &priv->pacrunner_call_id);
 	g_clear_object (&priv->pacrunner_manager);
 
 	_cleanup_generic_pre (self, CLEANUP_TYPE_KEEP);
@@ -13169,6 +13841,8 @@ finalize (GObject *object)
 	 * and thus @settings might be unset. */
 	if (priv->settings)
 		g_object_unref (priv->settings);
+
+	g_object_unref (priv->netns);
 }
 
 static void
@@ -13213,8 +13887,10 @@ set_property (GObject *object, guint prop_id,
 			managed = g_value_get_boolean (value);
 			if (managed)
 				reason = NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED;
-			else
+			else {
 				reason = NM_DEVICE_STATE_REASON_REMOVED;
+				nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_REMOVED);
+			}
 			nm_device_set_unmanaged_by_flags (self,
 			                                  NM_UNMANAGED_USER_EXPLICIT,
 			                                  !managed,
@@ -13222,7 +13898,7 @@ set_property (GObject *object, guint prop_id,
 		}
 		break;
 	case PROP_AUTOCONNECT:
-		nm_device_set_autoconnect (self, g_value_get_boolean (value));
+		nm_device_set_autoconnect_both (self, g_value_get_boolean (value));
 		break;
 	case PROP_FIRMWARE_MISSING:
 		/* construct-only */
@@ -13348,7 +14024,7 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_boolean (value, nm_device_get_state (self) > NM_DEVICE_STATE_UNMANAGED);
 		break;
 	case PROP_AUTOCONNECT:
-		g_value_set_boolean (value, priv->autoconnect);
+		g_value_set_boolean (value, nm_device_get_autoconnect (self));
 		break;
 	case PROP_FIRMWARE_MISSING:
 		g_value_set_boolean (value, priv->firmware_missing);
@@ -13440,6 +14116,9 @@ get_property (GObject *object, guint prop_id,
 	case PROP_RX_BYTES:
 		g_value_set_uint64 (value, priv->stats.rx_bytes);
 		break;
+	case PROP_CONNECTIVITY:
+		g_value_set_uint (value, priv->connectivity_state);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -13475,6 +14154,7 @@ nm_device_class_init (NMDeviceClass *klass)
 	klass->have_any_ready_slaves = have_any_ready_slaves;
 
 	klass->get_type_description = get_type_description;
+	klass->get_autoconnect_allowed = get_autoconnect_allowed;
 	klass->can_auto_connect = can_auto_connect;
 	klass->check_connection_compatible = check_connection_compatible;
 	klass->check_connection_available = check_connection_available;
@@ -13486,6 +14166,8 @@ nm_device_class_init (NMDeviceClass *klass)
 	klass->unmanaged_on_quit = unmanaged_on_quit;
 	klass->deactivate_reset_hw_addr = deactivate_reset_hw_addr;
 	klass->parent_changed_notify = parent_changed_notify;
+	klass->can_reapply_change = can_reapply_change;
+	klass->reapply_connection = reapply_connection;
 
 	obj_properties[PROP_UDI] =
 	    g_param_spec_string (NM_DEVICE_UDI, "", "",
@@ -13707,6 +14389,13 @@ nm_device_class_init (NMDeviceClass *klass)
 	                         G_PARAM_READABLE |
 	                         G_PARAM_STATIC_STRINGS);
 
+	/* Connectivity */
+	obj_properties[PROP_CONNECTIVITY] =
+	     g_param_spec_uint (NM_DEVICE_CONNECTIVITY, "", "",
+	                        NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN,
+	                        G_PARAM_READABLE |
+	                        G_PARAM_STATIC_STRINGS);
+
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	signals[STATE_CHANGED] =
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index e512f2ca..be328eb6 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2005 - 2013 Red Hat, Inc.
+ * Copyright (C) 2005 - 2017 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
@@ -30,6 +30,34 @@
 #include "nm-rfkill-manager.h"
 #include "NetworkManagerUtils.h"
 
+typedef enum {
+	NM_DEVICE_SYS_IFACE_STATE_EXTERNAL,
+	NM_DEVICE_SYS_IFACE_STATE_ASSUME,
+	NM_DEVICE_SYS_IFACE_STATE_MANAGED,
+
+	/* the REMOVED state applies when the device is manually set to unmanaged
+	 * or the link was externally removed. In both cases, we move the device
+	 * to UNMANAGED state, without touching the link -- be it, because the link
+	 * is already gone or because we want to release it (give it up).
+	 */
+	NM_DEVICE_SYS_IFACE_STATE_REMOVED,
+} NMDeviceSysIfaceState;
+
+static inline NMDeviceStateReason
+nm_device_state_reason_check (NMDeviceStateReason reason)
+{
+	/* the device-state-reason serves mostly informational purpse during a state
+	 * change. In some cases however, decisions are made based on the reason.
+	 * I tend to think that interpreting the state reason to derive some behaviors
+	 * is confusing, because the cause and effect are so far apart.
+	 *
+	 * This function is here to mark source that inspects the reason to make
+	 * a decision -- contrary to places that set the reason. Thus, by grepping
+	 * for nm_device_state_reason_check() you can find the "effect" to a certain
+	 * reason.
+	 */
+	return reason;
+}
 
 #define NM_PENDING_ACTION_AUTOACTIVATE              "autoactivate"
 #define NM_PENDING_ACTION_DHCP4                     "dhcp4"
@@ -113,6 +141,8 @@
 #define NM_DEVICE_STATISTICS_TX_BYTES        "tx-bytes"
 #define NM_DEVICE_STATISTICS_RX_BYTES        "rx-bytes"
 
+#define NM_DEVICE_CONNECTIVITY               "connectivity"
+
 #define NM_TYPE_DEVICE            (nm_device_get_type ())
 #define NM_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice))
 #define NM_DEVICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  NM_TYPE_DEVICE, NMDeviceClass))
@@ -243,6 +273,11 @@ typedef struct {
 
 	void        (* set_enabled) (NMDevice *self, gboolean enabled);
 
+	/* allow derived classes to override the result of nm_device_autoconnect_allowed().
+	 * If the value changes, the class should call nm_device_emit_recheck_auto_activate(),
+	 * which emits NM_DEVICE_RECHECK_AUTO_ACTIVATE signal. */
+	gboolean    (* get_autoconnect_allowed) (NMDevice *self);
+
 	gboolean    (* can_auto_connect) (NMDevice *self,
 	                                  NMConnection *connection,
 	                                  char **specific_object);
@@ -278,19 +313,19 @@ typedef struct {
 	                                             GError **error);
 
 	NMActStageReturn    (* act_stage1_prepare)  (NMDevice *self,
-	                                             NMDeviceStateReason *reason);
+	                                             NMDeviceStateReason *out_failure_reason);
 	NMActStageReturn    (* act_stage2_config)   (NMDevice *self,
-	                                             NMDeviceStateReason *reason);
+	                                             NMDeviceStateReason *out_failure_reason);
 	NMActStageReturn    (* act_stage3_ip4_config_start) (NMDevice *self,
 	                                                     NMIP4Config **out_config,
-	                                                     NMDeviceStateReason *reason);
+	                                                     NMDeviceStateReason *out_failure_reason);
 	NMActStageReturn    (* act_stage3_ip6_config_start) (NMDevice *self,
 	                                                     NMIP6Config **out_config,
-	                                                     NMDeviceStateReason *reason);
+	                                                     NMDeviceStateReason *out_failure_reason);
 	NMActStageReturn    (* act_stage4_ip4_config_timeout)   (NMDevice *self,
-	                                                         NMDeviceStateReason *reason);
+	                                                         NMDeviceStateReason *out_failure_reason);
 	NMActStageReturn    (* act_stage4_ip6_config_timeout)   (NMDevice *self,
-	                                                         NMDeviceStateReason *reason);
+	                                                         NMDeviceStateReason *out_failure_reason);
 
 	void                (* ip4_config_pre_commit) (NMDevice *self, NMIP4Config *config);
 
@@ -360,6 +395,17 @@ typedef struct {
 	NMConnection *  (* new_default_connection) (NMDevice *self);
 
 	gboolean        (* unmanaged_on_quit) (NMDevice *self);
+
+	gboolean        (* can_reapply_change) (NMDevice *self,
+	                                        const char *setting_name,
+	                                        NMSetting *s_old,
+	                                        NMSetting *s_new,
+	                                        GHashTable *diffs,
+	                                        GError **error);
+
+	void            (* reapply_connection) (NMDevice *self,
+	                                        NMConnection *con_old,
+	                                        NMConnection *con_new);
 } NMDeviceClass;
 
 typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device,
@@ -370,6 +416,9 @@ typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device,
 
 GType nm_device_get_type (void);
 
+NMNetns *nm_device_get_netns (NMDevice *self);
+NMPlatform *nm_device_get_platform (NMDevice *self);
+
 const char *    nm_device_get_udi               (NMDevice *dev);
 const char *    nm_device_get_iface             (NMDevice *dev);
 int             nm_device_get_ifindex           (NMDevice *dev);
@@ -458,8 +507,6 @@ gboolean nm_device_complete_connection (NMDevice *device,
 gboolean nm_device_check_connection_compatible (NMDevice *device, NMConnection *connection);
 gboolean nm_device_check_slave_connection_compatible (NMDevice *device, NMConnection *connection);
 
-gboolean nm_device_uses_assumed_connection (NMDevice *device);
-
 gboolean nm_device_unmanage_on_quit (NMDevice *self);
 
 gboolean nm_device_spec_match_list (NMDevice *device, const GSList *specs);
@@ -564,6 +611,7 @@ gboolean nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps);
 
 gboolean nm_device_realize_start      (NMDevice *device,
                                        const NMPlatformLink *plink,
+                                       NMUnmanFlagOp unmanaged_user_explicit,
                                        gboolean *out_compatible,
                                        GError **error);
 void     nm_device_realize_finish     (NMDevice *self,
@@ -577,9 +625,16 @@ gboolean nm_device_unrealize          (NMDevice *device,
                                        GError **error);
 
 gboolean nm_device_get_autoconnect (NMDevice *device);
-void nm_device_set_autoconnect (NMDevice *device, gboolean autoconnect);
+void nm_device_set_autoconnect_intern (NMDevice *device, gboolean autoconnect);
 void nm_device_emit_recheck_auto_activate (NMDevice *device);
 
+NMDeviceSysIfaceState nm_device_sys_iface_state_get (NMDevice *device);
+
+gboolean nm_device_sys_iface_state_is_external (NMDevice *self);
+gboolean nm_device_sys_iface_state_is_external_or_assume (NMDevice *self);
+
+void nm_device_sys_iface_state_set (NMDevice *device, NMDeviceSysIfaceState sys_iface_state);
+
 void nm_device_state_changed (NMDevice *device,
                               NMDeviceState state,
                               NMDeviceStateReason reason);
@@ -626,14 +681,31 @@ void nm_device_update_firewall_zone (NMDevice *self);
 void nm_device_update_metered (NMDevice *self);
 void nm_device_reactivate_ip4_config (NMDevice *device,
                                       NMSettingIPConfig *s_ip4_old,
-                                      NMSettingIPConfig *s_ip4_new);
+                                      NMSettingIPConfig *s_ip4_new,
+                                      gboolean force_restart);
 void nm_device_reactivate_ip6_config (NMDevice *device,
                                       NMSettingIPConfig *s_ip6_old,
-                                      NMSettingIPConfig *s_ip6_new);
+                                      NMSettingIPConfig *s_ip6_new,
+                                      gboolean force_restart);
 
 gboolean nm_device_update_hw_address (NMDevice *self);
 void nm_device_update_initial_hw_address (NMDevice *self);
 void nm_device_update_permanent_hw_address (NMDevice *self, gboolean force_freeze);
 void nm_device_update_dynamic_ip_setup (NMDevice *self);
+guint nm_device_get_supplicant_timeout (NMDevice *self);
+gboolean nm_device_hw_addr_get_cloned (NMDevice *self,
+                                       NMConnection *connection,
+                                       gboolean is_wifi,
+                                       char **hwaddr,
+                                       gboolean *preserve,
+                                       GError **error);
+
+typedef void (*NMDeviceConnectivityCallback) (NMDevice *self,
+                                              NMConnectivityState state,
+                                              gpointer user_data);
+void nm_device_check_connectivity (NMDevice *self,
+                                   NMDeviceConnectivityCallback callback,
+                                   gpointer user_data);
+NMConnectivityState nm_device_get_connectivity_state (NMDevice *self);
 
 #endif /* __NETWORKMANAGER_DEVICE_H__ */
diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c
index ceec6063..bfd631f0 100644
--- a/src/devices/nm-lldp-listener.c
+++ b/src/devices/nm-lldp-listener.c
@@ -128,6 +128,8 @@ typedef struct {
             int _ifindex = (self) ? NM_LLDP_LISTENER_GET_PRIVATE (self)->ifindex : 0; \
             \
             _nm_log (_level, _NMLOG_DOMAIN, 0, \
+                     nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex), \
+                     NULL, \
                      "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      _NMLOG_PREFIX_NAME, \
                      ((_ifindex > 0) \
@@ -695,7 +697,7 @@ process_lldp_neighbor (NMLldpListener *self, sd_lldp_neighbor *neighbor_sd, gboo
 		return;
 	}
 
-	/* ensure that we have at most MAX_NEIGHBORS entires */
+	/* ensure that we have at most MAX_NEIGHBORS entries */
 	if (   !neigh_old /* only matters in the "add" case. */
 	    && (g_hash_table_size (priv->lldp_neighbors) + 1 > MAX_NEIGHBORS)) {
 		_LOGT ("process: ignore neighbor due to overall limit of %d", MAX_NEIGHBORS);
diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c
index 28d91ab3..1c4d2ef6 100644
--- a/src/devices/team/nm-device-team.c
+++ b/src/devices/team/nm-device-team.c
@@ -28,6 +28,7 @@
 #include <sys/wait.h>
 #include <teamdctl.h>
 #include <stdlib.h>
+#include <jansson.h>
 
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
@@ -72,7 +73,7 @@ G_DEFINE_TYPE (NMDeviceTeam, nm_device_team, NM_TYPE_DEVICE)
 
 /*****************************************************************************/
 
-static gboolean teamd_start (NMDevice *device, NMSettingTeam *s_team);
+static gboolean teamd_start (NMDevice *device, NMConnection *connection);
 
 /*****************************************************************************/
 
@@ -126,7 +127,7 @@ complete_connection (NMDevice *device,
 {
 	NMSettingTeam *s_team;
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_TEAM_SETTING_NAME,
 	                           existing_connections,
@@ -415,7 +416,7 @@ teamd_dbus_appeared (GDBusConnection *connection,
 			success = teamd_read_config (device);
 		if (success)
 			nm_device_activate_schedule_stage2_device_config (device);
-		else if (!nm_device_uses_assumed_connection (device))
+		else if (!nm_device_sys_iface_state_is_external_or_assume (device))
 			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
 	}
 }
@@ -449,7 +450,7 @@ teamd_dbus_vanished (GDBusConnection *dbus_connection,
 		NMConnection *connection = nm_device_get_applied_connection (device);
 
 		g_assert (connection);
-		if (!teamd_start (device, nm_connection_get_setting_team (connection)))
+		if (!teamd_start (device, connection))
 			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
 	}
 }
@@ -513,7 +514,7 @@ teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error)
 }
 
 static gboolean
-teamd_start (NMDevice *device, NMSettingTeam *s_team)
+teamd_start (NMDevice *device, NMConnection *connection)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
@@ -523,6 +524,12 @@ teamd_start (NMDevice *device, NMSettingTeam *s_team)
 	gs_free char *tmp_str = NULL;
 	const char *teamd_binary;
 	const char *config;
+	nm_auto_free const char *config_free = NULL;
+	NMSettingTeam *s_team;
+	gs_free char *cloned_mac = NULL;
+
+	s_team = nm_connection_get_setting_team (connection);
+	g_return_val_if_fail (s_team, FALSE);
 
 	teamd_binary = nm_utils_find_helper ("teamd", NULL, NULL);
 	if (!teamd_binary) {
@@ -548,7 +555,39 @@ teamd_start (NMDevice *device, NMSettingTeam *s_team)
 	g_ptr_array_add (argv, (gpointer) "-t");
 	g_ptr_array_add (argv, (gpointer) iface);
 
-	config = nm_setting_team_get_config(s_team);
+	config = nm_setting_team_get_config (s_team);
+	if (!nm_device_hw_addr_get_cloned (device, connection, FALSE, &cloned_mac, NULL, &error)) {
+		_LOGW (LOGD_DEVICE, "set-hw-addr: %s", error->message);
+		return FALSE;
+	}
+
+	if (cloned_mac) {
+		json_t *json, *hwaddr;
+		json_error_t jerror;
+
+		/* Inject the hwaddr property into the JSON configuration.
+		 * While doing so, detect potential conflicts */
+
+		json = json_loads (config ?: "{}", 0, &jerror);
+		g_return_val_if_fail (json, FALSE);
+
+		hwaddr = json_object_get (json, "hwaddr");
+		if (hwaddr) {
+			if (   !json_is_string (hwaddr)
+			    || !nm_streq0 (json_string_value (hwaddr), cloned_mac))
+				_LOGW (LOGD_TEAM, "set-hw-addr: can't set team cloned-mac-address as the JSON configuration already contains \"hwaddr\"");
+		} else {
+			hwaddr = json_string (cloned_mac);
+			json_object_set (json, "hwaddr", hwaddr);
+			config = config_free = json_dumps (json, JSON_INDENT(0) |
+			                                         JSON_ENSURE_ASCII |
+			                                         JSON_SORT_KEYS);
+			_LOGD (LOGD_TEAM, "set-hw-addr: injected \"hwaddr\" \"%s\" into team configuration", cloned_mac);
+			json_decref (hwaddr);
+		}
+		json_decref (json);
+	}
+
 	if (config) {
 		g_ptr_array_add (argv, (gpointer) "-c");
 		g_ptr_array_add (argv, (gpointer) config);
@@ -580,23 +619,24 @@ teamd_start (NMDevice *device, NMSettingTeam *s_team)
 }
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceTeam *self = NM_DEVICE_TEAM (device);
 	NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
 	gs_free_error GError *error = NULL;
 	NMSettingTeam *s_team;
+	NMConnection *connection;
 	const char *cfg;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	ret = NM_DEVICE_CLASS (nm_device_team_parent_class)->act_stage1_prepare (device, reason);
+	ret = NM_DEVICE_CLASS (nm_device_team_parent_class)->act_stage1_prepare (device, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
-	s_team = (NMSettingTeam *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_TEAM);
-	g_assert (s_team);
+	connection = nm_device_get_applied_connection (device);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+	s_team = nm_connection_get_setting_team (connection);
+	g_return_val_if_fail (s_team, NM_ACT_STAGE_RETURN_FAILURE);
 
 	if (priv->tdc) {
 		/* If the existing teamd config is the same as we're about to use,
@@ -614,7 +654,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 			_LOGD (LOGD_TEAM, "existing teamd config mismatch; killing existing via teamdctl");
 			if (!teamd_kill (self, NULL, &error)) {
 				_LOGW (LOGD_TEAM, "existing teamd config mismatch; failed to kill existing teamd: %s", error->message);
-				*reason = NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED;
+				NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
 				return NM_ACT_STAGE_RETURN_FAILURE;
 			}
 		}
@@ -623,7 +663,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 		teamd_cleanup (device, TRUE);
 	}
 
-	return teamd_start (device, s_team) ?
+	return teamd_start (device, connection) ?
 		NM_ACT_STAGE_RETURN_POSTPONE : NM_ACT_STAGE_RETURN_FAILURE;
 }
 
@@ -681,7 +721,7 @@ enslave_slave (NMDevice *device,
 				}
 			}
 		}
-		success = nm_platform_link_enslave (NM_PLATFORM_GET,
+		success = nm_platform_link_enslave (nm_device_get_platform (device),
 		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
 		nm_device_bring_up (slave, TRUE, &no_firmware);
@@ -711,7 +751,7 @@ release_slave (NMDevice *device,
 	gboolean success, no_firmware = FALSE;
 
 	if (configure) {
-		success = nm_platform_link_release (NM_PLATFORM_GET,
+		success = nm_platform_link_release (nm_device_get_platform (device),
 		                                    nm_device_get_ip_ifindex (device),
 		                                    nm_device_get_ip_ifindex (slave));
 
@@ -746,7 +786,7 @@ create_and_realize (NMDevice *device,
 	const char *iface = nm_device_get_iface (device);
 	NMPlatformError plerr;
 
-	plerr = nm_platform_link_team_add (NM_PLATFORM_GET, iface, out_plink);
+	plerr = nm_platform_link_team_add (nm_device_get_platform (device), iface, out_plink);
 	if (plerr != NM_PLATFORM_ERROR_SUCCESS) {
 		g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
 		             "Failed to create team master interface '%s' for '%s': %s",
diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c
index 4d25f9a6..1e600cdf 100644
--- a/src/devices/tests/test-lldp.c
+++ b/src/devices/tests/test-lldp.c
@@ -351,7 +351,11 @@ _test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data)
 	int fd, s;
 
 	fd = open ("/dev/net/tun", O_RDWR | O_CLOEXEC);
-	g_assert (fd >= 0);
+	if (fd == -1) {
+		g_test_skip ("Unable to open /dev/net/tun");
+		fixture->ifindex = 0;
+		return;
+	}
 
 	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
 	nm_utils_ifname_cpy (ifr.ifr_name, TEST_IFNAME);
@@ -395,6 +399,11 @@ test_recv (TestRecvFixture *fixture, gconstpointer user_data)
 	GError *error = NULL;
 	guint sd_id;
 
+	if (fixture->ifindex == 0) {
+		g_test_skip ("Tun device not available");
+		return;
+	}
+
 	listener = nm_lldp_listener_new ();
 	g_assert (listener != NULL);
 	g_assert (nm_lldp_listener_start (listener, fixture->ifindex, &error));
@@ -427,7 +436,8 @@ test_recv (TestRecvFixture *fixture, gconstpointer user_data)
 static void
 _test_recv_fixture_teardown (TestRecvFixture *fixture, gconstpointer user_data)
 {
-	nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex);
+	if (fixture->ifindex)
+		nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex);
 }
 
 /*****************************************************************************/
diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c
index 0fdccb8a..24811931 100644
--- a/src/devices/wifi/nm-device-olpc-mesh.c
+++ b/src/devices/wifi/nm-device-olpc-mesh.c
@@ -148,7 +148,7 @@ complete_connection (NMDevice *device,
 
 	}
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_OLPC_MESH_SETTING_NAME,
 	                           existing_connections,
@@ -163,14 +163,14 @@ complete_connection (NMDevice *device,
 /*****************************************************************************/
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
 	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
 	NMActStageReturn ret;
 	gboolean scanning;
 
-	ret = NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->act_stage1_prepare (device, reason);
+	ret = NM_DEVICE_CLASS (nm_device_olpc_mesh_parent_class)->act_stage1_prepare (device, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
@@ -200,16 +200,18 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 static void
 _mesh_set_channel (NMDeviceOlpcMesh *self, guint32 channel)
 {
+	NMPlatform *platform;
 	int ifindex = nm_device_get_ifindex (NM_DEVICE (self));
 
-	if (nm_platform_mesh_get_channel (NM_PLATFORM_GET, ifindex) != channel) {
-		if (nm_platform_mesh_set_channel (NM_PLATFORM_GET, ifindex, channel))
+	platform = nm_device_get_platform (NM_DEVICE (self));
+	if (nm_platform_mesh_get_channel (platform, ifindex) != channel) {
+		if (nm_platform_mesh_set_channel (platform, ifindex, channel))
 			_notify (self, PROP_ACTIVE_CHANNEL);
 	}
 }
 
 static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device);
 	NMConnection *connection;
@@ -219,17 +221,17 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	const char *anycast_addr;
 
 	connection = nm_device_get_applied_connection (device);
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
 	s_mesh = nm_connection_get_setting_olpc_mesh (connection);
-	g_assert (s_mesh);
+	g_return_val_if_fail (s_mesh, NM_ACT_STAGE_RETURN_FAILURE);
 
 	channel = nm_setting_olpc_mesh_get_channel (s_mesh);
 	if (channel != 0)
 		_mesh_set_channel (self, channel);
 
 	ssid = nm_setting_olpc_mesh_get_ssid (s_mesh);
-	nm_platform_mesh_set_ssid (NM_PLATFORM_GET,
+	nm_platform_mesh_set_ssid (nm_device_get_platform (device),
 	                           nm_device_get_ifindex (device),
 	                           g_bytes_get_data (ssid, NULL),
 	                           g_bytes_get_size (ssid));
@@ -429,15 +431,16 @@ static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
-	NMDeviceOlpcMesh *device = NM_DEVICE_OLPC_MESH (object);
-	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (device);
+	NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (object);
+	NMDevice *device = NM_DEVICE (self);
+	NMDeviceOlpcMeshPrivate *priv = NM_DEVICE_OLPC_MESH_GET_PRIVATE (self);
 
 	switch (prop_id) {
 	case PROP_COMPANION:
 		nm_utils_g_value_set_object_path (value, priv->companion);
 		break;
 	case PROP_ACTIVE_CHANNEL:
-		g_value_set_uint (value, nm_platform_mesh_get_channel (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (device))));
+		g_value_set_uint (value, nm_platform_mesh_get_channel (nm_device_get_platform (device), nm_device_get_ifindex (device)));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 188ed54e..7359be96 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -63,7 +63,7 @@ _LOG_DECLARE_SELF(NMDeviceWifi);
 
 #define SCAN_RAND_MAC_ADDRESS_EXPIRE_MIN 5
 
-#define WIRELESS_SECRETS_TRIES "wireless-secrets-tries"
+static NM_CACHED_QUARK_FCN ("wireless-secrets-tries", wireless_secrets_tries_quark)
 
 /*****************************************************************************/
 
@@ -119,7 +119,6 @@ typedef struct {
 	NMDeviceWifiCapabilities capabilities;
 
 	gint32 hw_addr_scan_expire;
-	char *hw_addr_scan;
 } NMDeviceWifiPrivate;
 
 struct _NMDeviceWifi
@@ -152,16 +151,11 @@ static void cleanup_association_attempt (NMDeviceWifi * self,
                                          gboolean disconnect);
 
 static void supplicant_iface_state_cb (NMSupplicantInterface *iface,
-                                       guint32 new_state,
-                                       guint32 old_state,
+                                       int new_state_i,
+                                       int old_state_i,
                                        int disconnect_reason,
                                        gpointer user_data);
 
-static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface,
-                                         const char *object_path,
-                                         GVariant *properties,
-                                         NMDeviceWifi * self);
-
 static void supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
                                              const char *object_path,
                                              GVariant *properties,
@@ -190,14 +184,13 @@ static void ap_add_remove (NMDeviceWifi *self,
                            NMWifiAP *ap,
                            gboolean recheck_available_connections);
 
-static void remove_supplicant_interface_error_handler (NMDeviceWifi *self);
-
 static void _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset);
 
 /*****************************************************************************/
 
 static void
 _ap_dump (NMDeviceWifi *self,
+          NMLogLevel log_level,
           const NMWifiAP *ap,
           const char *prefix,
           gint32 now_s)
@@ -205,9 +198,9 @@ _ap_dump (NMDeviceWifi *self,
 	char buf[1024];
 
 	buf[0] = '\0';
-	_LOGD (LOGD_WIFI_SCAN, "wifi-ap: %-7s %s",
-	       prefix,
-	       nm_wifi_ap_to_string (ap, buf, sizeof (buf), now_s));
+	_NMLOG (log_level, LOGD_WIFI_SCAN, "wifi-ap: %-7s %s",
+	        prefix,
+	        nm_wifi_ap_to_string (ap, buf, sizeof (buf), now_s));
 }
 
 static void
@@ -263,10 +256,6 @@ supplicant_interface_acquire (NMDeviceWifi *self)
 	                  G_CALLBACK (supplicant_iface_state_cb),
 	                  self);
 	g_signal_connect (priv->sup_iface,
-	                  NM_SUPPLICANT_INTERFACE_NEW_BSS,
-	                  G_CALLBACK (supplicant_iface_new_bss_cb),
-	                  self);
-	g_signal_connect (priv->sup_iface,
 	                  NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
 	                  G_CALLBACK (supplicant_iface_bss_updated_cb),
 	                  self);
@@ -306,8 +295,10 @@ _requested_scan_set (NMDeviceWifi *self, gboolean value)
 	priv->requested_scan = value;
 	if (value)
 		nm_device_add_pending_action ((NMDevice *) self, NM_PENDING_ACTION_WIFI_SCAN, TRUE);
-	else
+	else {
+		nm_device_emit_recheck_auto_activate (NM_DEVICE (self));
 		nm_device_remove_pending_action ((NMDevice *) self, NM_PENDING_ACTION_WIFI_SCAN, TRUE);
+	}
 }
 
 static void
@@ -428,7 +419,7 @@ periodic_update (NMDeviceWifi *self)
 	guint32 new_rate;
 	int percent;
 	NMDeviceState state;
-	guint32 supplicant_state;
+	NMSupplicantInterfaceState supplicant_state;
 
 	/* BSSID and signal strength have meaningful values only if the device
 	 * is activated and not scanning.
@@ -453,14 +444,18 @@ periodic_update (NMDeviceWifi *self)
 
 	if (priv->current_ap) {
 		/* Smooth out the strength to work around crappy drivers */
-		percent = nm_platform_wifi_get_quality (NM_PLATFORM_GET, ifindex);
+		percent = nm_platform_wifi_get_quality (nm_device_get_platform (NM_DEVICE (self)), ifindex);
 		if (percent >= 0 || ++priv->invalid_strength_counter > 3) {
-			nm_wifi_ap_set_strength (priv->current_ap, (gint8) percent);
+			if (nm_wifi_ap_set_strength (priv->current_ap, (gint8) percent)) {
+#ifdef NM_MORE_LOGGING
+				_ap_dump (self, LOGL_TRACE, priv->current_ap, "updated", 0);
+#endif
+			}
 			priv->invalid_strength_counter = 0;
 		}
 	}
 
-	new_rate = nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex);
+	new_rate = nm_platform_wifi_get_rate (nm_device_get_platform (NM_DEVICE (self)), ifindex);
 	if (new_rate != priv->rate) {
 		priv->rate = new_rate;
 		_notify (self, PROP_BITRATE);
@@ -488,7 +483,9 @@ ap_add_remove (NMDeviceWifi *self,
 		g_hash_table_insert (priv->aps,
 		                     (gpointer) nm_exported_object_export ((NMExportedObject *) ap),
 		                     g_object_ref (ap));
-	}
+		_ap_dump (self, LOGL_DEBUG, ap, "added", 0);
+	} else
+		_ap_dump (self, LOGL_DEBUG, ap, "removed", 0);
 
 	g_signal_emit (self, signals[signum], 0, ap);
 
@@ -544,14 +541,14 @@ deactivate (NMDevice *device)
 	set_current_ap (self, NULL, TRUE);
 
 	/* Clear any critical protocol notification in the Wi-Fi stack */
-	nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, ifindex, FALSE);
+	nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), ifindex, FALSE);
 
 	/* Ensure we're in infrastructure mode after deactivation; some devices
 	 * (usually older ones) don't scan well in adhoc mode.
 	 */
-	if (nm_platform_wifi_get_mode (NM_PLATFORM_GET, ifindex) != NM_802_11_MODE_INFRA) {
+	if (nm_platform_wifi_get_mode (nm_device_get_platform (device), ifindex) != NM_802_11_MODE_INFRA) {
 		nm_device_take_down (NM_DEVICE (self), TRUE);
-		nm_platform_wifi_set_mode (NM_PLATFORM_GET, ifindex, NM_802_11_MODE_INFRA);
+		nm_platform_wifi_set_mode (nm_device_get_platform (device), ifindex, NM_802_11_MODE_INFRA);
 		nm_device_bring_up (NM_DEVICE (self), TRUE, NULL);
 	}
 
@@ -904,7 +901,7 @@ complete_connection (NMDevice *device,
 
 	str_ssid = nm_utils_ssid_to_utf8 (ssid->data, ssid->len);
 
-	nm_utils_complete_generic (NM_PLATFORM_GET,
+	nm_utils_complete_generic (nm_device_get_platform (device),
 	                           connection,
 	                           NM_SETTING_WIRELESS_SETTING_NAME,
 	                           existing_connections,
@@ -955,7 +952,7 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	guint32 state;
+	NMSupplicantInterfaceState supplicant_state;
 
 	if (!priv->enabled)
 		return FALSE;
@@ -963,15 +960,27 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags)
 	if (!priv->sup_iface)
 		return FALSE;
 
-	state = nm_supplicant_interface_get_state (priv->sup_iface);
-	if (   state < NM_SUPPLICANT_INTERFACE_STATE_READY
-	    || state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)
+	supplicant_state = nm_supplicant_interface_get_state (priv->sup_iface);
+	if (   supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_READY
+	    || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED)
 		return FALSE;
 
 	return TRUE;
 }
 
 static gboolean
+get_autoconnect_allowed (NMDevice *device)
+{
+	NMDeviceWifiPrivate *priv;
+
+	if (!NM_DEVICE_CLASS (nm_device_wifi_parent_class)->get_autoconnect_allowed (device))
+		return FALSE;
+
+	priv = NM_DEVICE_WIFI_GET_PRIVATE (NM_DEVICE_WIFI (device));
+	return !priv->requested_scan;
+}
+
+static gboolean
 can_auto_connect (NMDevice *device,
                   NMConnection *connection,
                   char **specific_object)
@@ -1128,7 +1137,9 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset)
 	                                                      TRUE, TRUE);
 
 	if (!randomize) {
-		g_clear_pointer (&priv->hw_addr_scan, g_free);
+		/* expire the temporary MAC address used during scanning */
+		priv->hw_addr_scan_expire = 0;
+
 		if (do_reset)
 			nm_device_hw_addr_reset (device, "scanning");
 		return;
@@ -1136,9 +1147,9 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset)
 
 	now = nm_utils_get_monotonic_timestamp_s ();
 
-	if (   !priv->hw_addr_scan
-	    || now >= priv->hw_addr_scan_expire) {
+	if (now >= priv->hw_addr_scan_expire) {
 		gs_free char *generate_mac_address_mask = NULL;
+		gs_free char *hw_addr_scan = NULL;
 
 		/* the random MAC address for scanning expires after a while.
 		 *
@@ -1152,12 +1163,10 @@ _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset)
 		                                                              device,
 		                                                              NULL);
 
-		g_free (priv->hw_addr_scan);
-		priv->hw_addr_scan = nm_utils_hw_addr_gen_random_eth (nm_device_get_initial_hw_address (device),
-		                                                      generate_mac_address_mask);
+		hw_addr_scan = nm_utils_hw_addr_gen_random_eth (nm_device_get_initial_hw_address (device),
+		                                                generate_mac_address_mask);
+		nm_device_hw_addr_set (device, hw_addr_scan, "scanning", TRUE);
 	}
-
-	nm_device_hw_addr_set (device, priv->hw_addr_scan, "scanning", TRUE);
 }
 
 static void
@@ -1242,7 +1251,7 @@ static gboolean
 scanning_allowed (NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	guint32 sup_state;
+	NMSupplicantInterfaceState supplicant_state;
 	NMConnection *connection;
 
 	g_return_val_if_fail (priv->sup_iface != NULL, FALSE);
@@ -1274,11 +1283,11 @@ scanning_allowed (NMDeviceWifi *self)
 	}
 
 	/* Don't scan if the supplicant is busy */
-	sup_state = nm_supplicant_interface_get_state (priv->sup_iface);
-	if (   sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING
-	    || sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED
-	    || sup_state == NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE
-	    || sup_state == NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE
+	supplicant_state = nm_supplicant_interface_get_state (priv->sup_iface);
+	if (   supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING
+	    || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED
+	    || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE
+	    || supplicant_state == NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE
 	    || nm_supplicant_interface_get_scanning (priv->sup_iface))
 		return FALSE;
 
@@ -1338,12 +1347,14 @@ check_scanning_allowed (NMDeviceWifi *self)
 
 static gboolean
 hidden_filter_func (NMSettings *settings,
-                    NMConnection *connection,
+                    NMSettingsConnection *connection,
                     gpointer user_data)
 {
 	NMSettingWireless *s_wifi;
 
-	s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection);
+	if (!nm_connection_is_type (NM_CONNECTION (connection), NM_SETTING_WIRELESS_SETTING_NAME))
+		return FALSE;
+	s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (NM_CONNECTION (connection));
 	return s_wifi ? nm_setting_wireless_get_hidden (s_wifi) : FALSE;
 }
 
@@ -1352,7 +1363,8 @@ build_hidden_probe_list (NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	guint max_scan_ssids = nm_supplicant_interface_get_max_scan_ssids (priv->sup_iface);
-	GSList *connections, *iter;
+	gs_free NMSettingsConnection **connections = NULL;
+	guint i, len;
 	GPtrArray *ssids = NULL;
 	static GByteArray *nullssid = NULL;
 
@@ -1360,28 +1372,31 @@ build_hidden_probe_list (NMDeviceWifi *self)
 	if (max_scan_ssids < 2)
 		return NULL;
 
-	/* Static wildcard SSID used for every scan */
+	connections = nm_settings_get_connections_clone (nm_device_get_settings ((NMDevice *) self),
+	                                                 &len,
+	                                                 hidden_filter_func,
+	                                                 NULL);
+	if (!connections[0])
+		return NULL;
+
+	g_qsort_with_data (connections, len, sizeof (NMSettingsConnection *), nm_settings_connection_cmp_timestamp_p_with_data, NULL);
+
+	ssids = g_ptr_array_new_full (max_scan_ssids, (GDestroyNotify) g_byte_array_unref);
+
+	/* Add wildcard SSID using a static wildcard SSID used for every scan */
 	if (G_UNLIKELY (nullssid == NULL))
 		nullssid = g_byte_array_new ();
+	g_ptr_array_add (ssids, g_byte_array_ref (nullssid));
 
-	connections = nm_settings_get_best_connections (nm_device_get_settings ((NMDevice *) self),
-	                                                max_scan_ssids - 1,
-	                                                NM_SETTING_WIRELESS_SETTING_NAME,
-	                                                NULL,
-	                                                hidden_filter_func,
-	                                                NULL);
-	if (connections && connections->data) {
-		ssids = g_ptr_array_new_full (max_scan_ssids - 1, (GDestroyNotify) g_byte_array_unref);
-		g_ptr_array_add (ssids, g_byte_array_ref (nullssid));  /* Add wildcard SSID */
-	}
-
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMConnection *connection = iter->data;
+	for (i = 0; connections[i]; i++) {
 		NMSettingWireless *s_wifi;
 		GBytes *ssid;
 		GByteArray *ssid_array;
 
-		s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (connection);
+		if (i >= max_scan_ssids - 1)
+			break;
+
+		s_wifi = (NMSettingWireless *) nm_connection_get_setting_wireless (NM_CONNECTION (connections[i]));
 		g_assert (s_wifi);
 		ssid = nm_setting_wireless_get_ssid (s_wifi);
 		g_assert (ssid);
@@ -1391,7 +1406,6 @@ build_hidden_probe_list (NMDeviceWifi *self)
 		                     g_bytes_get_size (ssid));
 		g_ptr_array_add (ssids, ssid_array);
 	}
-	g_slist_free (connections);
 
 	return ssids;
 }
@@ -1424,9 +1438,7 @@ static void
 request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant *scan_options)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-	gboolean backoff = FALSE;
-	GPtrArray *ssids = NULL;
-	gboolean new_scan_requested = FALSE;
+	gboolean request_started = FALSE;
 
 	nm_clear_g_source (&priv->pending_scan_id);
 
@@ -1436,6 +1448,8 @@ request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant
 	}
 
 	if (check_scanning_allowed (self)) {
+		gs_unref_ptrarray GPtrArray *ssids = NULL;
+
 		_LOGD (LOGD_WIFI, "wifi-scan: scanning requested");
 
 		if (scan_options) {
@@ -1473,22 +1487,14 @@ request_wireless_scan (NMDeviceWifi *self, gboolean force_if_scanning, GVariant
 
 		_hw_addr_set_scanning (self, FALSE);
 
-		if (nm_supplicant_interface_request_scan (priv->sup_iface, ssids)) {
-			/* success */
-			backoff = TRUE;
-			_requested_scan_set (self, TRUE);
-			new_scan_requested = TRUE;
-		}
-
-		if (ssids)
-			g_ptr_array_unref (ssids);
+		nm_supplicant_interface_request_scan (priv->sup_iface, ssids);
+		request_started = TRUE;
 	} else
 		_LOGD (LOGD_WIFI, "wifi-scan: scanning requested but not allowed at this time");
 
-	if (!new_scan_requested)
-		_requested_scan_set (self, FALSE);
+	_requested_scan_set (self, request_started);
 
-	schedule_scan (self, backoff);
+	schedule_scan (self, request_started);
 }
 
 static gboolean
@@ -1589,7 +1595,7 @@ ap_list_dump (gpointer user_data)
 		       priv->scheduled_scan_time);
 		list = ap_list_get_sorted (self, TRUE);
 		for (i = 0; list[i]; i++)
-			_ap_dump (self, list[i], "dump", now_s);
+			_ap_dump (self, LOGL_DEBUG, list[i], "dump", now_s);
 	}
 	return G_SOURCE_REMOVE;
 }
@@ -1639,14 +1645,13 @@ try_fill_ssid_for_hidden_ap (NMDeviceWifi *self,
 }
 
 static void
-supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
-                             const char *object_path,
-                             GVariant *properties,
-                             NMDeviceWifi *self)
+supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
+                                 const char *object_path,
+                                 GVariant *properties,
+                                 NMDeviceWifi *self)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	NMDeviceState state;
-	NMWifiAP *ap;
 	NMWifiAP *found_ap = NULL;
 	const GByteArray *ssid;
 
@@ -1661,41 +1666,41 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
 	if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP)
 		return;
 
-	ap = nm_wifi_ap_new_from_properties (object_path, properties);
-	if (!ap) {
-		_LOGD (LOGD_WIFI, "invalid AP properties received for %s", object_path);
-		return;
-	}
+	found_ap = get_ap_by_supplicant_path (self, object_path);
+	if (found_ap) {
+		if (!nm_wifi_ap_update_from_properties (found_ap, object_path, properties))
+			return;
+		_ap_dump (self, LOGL_DEBUG, found_ap, "updated", 0);
+	} else {
+		gs_unref_object NMWifiAP *ap = NULL;
 
-	/* Let the manager try to fill in the SSID from seen-bssids lists */
-	ssid = nm_wifi_ap_get_ssid (ap);
-	if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
-		/* Try to fill the SSID from the AP database */
-		try_fill_ssid_for_hidden_ap (self, ap);
+		ap = nm_wifi_ap_new_from_properties (object_path, properties);
+		if (!ap) {
+			_LOGD (LOGD_WIFI, "invalid AP properties received for %s", object_path);
+			return;
+		}
 
+		/* Let the manager try to fill in the SSID from seen-bssids lists */
 		ssid = nm_wifi_ap_get_ssid (ap);
-		if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) {
-			/* Yay, matched it, no longer treat as hidden */
-			_LOGD (LOGD_WIFI, "matched hidden AP %s => '%s'",
-			       nm_wifi_ap_get_address (ap), nm_utils_escape_ssid (ssid->data, ssid->len));
-		} else {
-			/* Didn't have an entry for this AP in the database */
-			_LOGD (LOGD_WIFI, "failed to match hidden AP %s",
-			       nm_wifi_ap_get_address (ap));
+		if (!ssid || nm_utils_is_empty_ssid (ssid->data, ssid->len)) {
+			/* Try to fill the SSID from the AP database */
+			try_fill_ssid_for_hidden_ap (self, ap);
+
+			ssid = nm_wifi_ap_get_ssid (ap);
+			if (ssid && (nm_utils_is_empty_ssid (ssid->data, ssid->len) == FALSE)) {
+				/* Yay, matched it, no longer treat as hidden */
+				_LOGD (LOGD_WIFI, "matched hidden AP %s => '%s'",
+				       nm_wifi_ap_get_address (ap), nm_utils_escape_ssid (ssid->data, ssid->len));
+			} else {
+				/* Didn't have an entry for this AP in the database */
+				_LOGD (LOGD_WIFI, "failed to match hidden AP %s",
+				       nm_wifi_ap_get_address (ap));
+			}
 		}
-	}
 
-	found_ap = get_ap_by_supplicant_path (self, object_path);
-	if (found_ap) {
-		_ap_dump (self, ap, "updated", 0);
-		nm_wifi_ap_update_from_properties (found_ap, object_path, properties);
-	} else {
-		_ap_dump (self, ap, "added", 0);
 		ap_add_remove (self, ACCESS_POINT_ADDED, ap, TRUE);
 	}
 
-	g_object_unref (ap);
-
 	/* Update the current AP if the supplicant notified a current BSS change
 	 * before it sent the current BSS's scan result.
 	 */
@@ -1706,32 +1711,6 @@ supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
 }
 
 static void
-supplicant_iface_bss_updated_cb (NMSupplicantInterface *iface,
-                                 const char *object_path,
-                                 GVariant *properties,
-                                 NMDeviceWifi *self)
-{
-	NMDeviceState state;
-	NMWifiAP *ap;
-
-	g_return_if_fail (self != NULL);
-	g_return_if_fail (object_path != NULL);
-	g_return_if_fail (properties != NULL);
-
-	/* Ignore new APs when unavailable or unmanaged */
-	state = nm_device_get_state (NM_DEVICE (self));
-	if (state <= NM_DEVICE_STATE_UNAVAILABLE)
-		return;
-
-	ap = get_ap_by_supplicant_path (self, object_path);
-	if (ap) {
-		_ap_dump (self, ap, "updated", 0);
-		nm_wifi_ap_update_from_properties (ap, object_path, properties);
-		schedule_ap_list_dump (self);
-	}
-}
-
-static void
 supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
                                  const char *object_path,
                                  NMDeviceWifi *self)
@@ -1744,38 +1723,30 @@ supplicant_iface_bss_removed_cb (NMSupplicantInterface *iface,
 
 	priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 	ap = get_ap_by_supplicant_path (self, object_path);
-	if (ap) {
-		if (ap == priv->current_ap) {
-			/* The current AP cannot be removed (to prevent NM indicating that
-			 * it is connected, but to nothing), but it must be removed later
-			 * when the current AP is changed or cleared.  Set 'fake' to
-			 * indicate that this AP is now unknown to the supplicant.
-			 */
-			nm_wifi_ap_set_fake (ap, TRUE);
-		} else {
-			_ap_dump (self, ap, "removed", 0);
-			ap_add_remove (self, ACCESS_POINT_REMOVED, ap, TRUE);
-			schedule_ap_list_dump (self);
-		}
+	if (!ap)
+		return;
+
+	if (ap == priv->current_ap) {
+		/* The current AP cannot be removed (to prevent NM indicating that
+		 * it is connected, but to nothing), but it must be removed later
+		 * when the current AP is changed or cleared.  Set 'fake' to
+		 * indicate that this AP is now unknown to the supplicant.
+		 */
+		if (nm_wifi_ap_set_fake (ap, TRUE))
+			_ap_dump (self, LOGL_DEBUG, ap, "updated", 0);
+	} else {
+		ap_add_remove (self, ACCESS_POINT_REMOVED, ap, TRUE);
+		schedule_ap_list_dump (self);
 	}
 }
 
 static void
-remove_supplicant_timeouts (NMDeviceWifi *self)
+cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect)
 {
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
 
 	nm_clear_g_source (&priv->sup_timeout_id);
 	nm_clear_g_source (&priv->link_timeout_id);
-}
-
-static void
-cleanup_association_attempt (NMDeviceWifi *self, gboolean disconnect)
-{
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-
-	remove_supplicant_interface_error_handler (self);
-	remove_supplicant_timeouts (self);
 	if (disconnect && priv->sup_iface)
 		nm_supplicant_interface_disconnect (priv->sup_iface);
 }
@@ -1893,7 +1864,7 @@ link_timeout_cb (gpointer user_data)
 
 static gboolean
 need_new_8021x_secrets (NMDeviceWifi *self,
-                        guint32 old_state,
+                        NMSupplicantInterfaceState old_state,
                         const char **setting_name)
 {
 	NMSetting8021x *s_8021x;
@@ -1947,7 +1918,7 @@ need_new_8021x_secrets (NMDeviceWifi *self,
 
 static gboolean
 need_new_wpa_psk (NMDeviceWifi *self,
-                  guint32 old_state,
+                  NMSupplicantInterfaceState old_state,
                   gint disconnect_reason,
                   const char **setting_name)
 {
@@ -1988,8 +1959,8 @@ need_new_wpa_psk (NMDeviceWifi *self,
 
 static gboolean
 handle_8021x_or_psk_auth_fail (NMDeviceWifi *self,
-                               guint32 new_state,
-                               guint32 old_state,
+                               NMSupplicantInterfaceState new_state,
+                               NMSupplicantInterfaceState old_state,
                                int disconnect_reason)
 {
 	NMDevice *device = NM_DEVICE (self);
@@ -2042,8 +2013,8 @@ reacquire_interface_cb (gpointer user_data)
 
 static void
 supplicant_iface_state_cb (NMSupplicantInterface *iface,
-                           guint32 new_state,
-                           guint32 old_state,
+                           int new_state_i,
+                           int old_state_i,
                            int disconnect_reason,
                            gpointer user_data)
 {
@@ -2052,6 +2023,8 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 	NMDevice *device = NM_DEVICE (self);
 	NMDeviceState devstate;
 	gboolean scanning;
+	NMSupplicantInterfaceState new_state = new_state_i;
+	NMSupplicantInterfaceState old_state = old_state_i;
 
 	if (new_state == old_state)
 		return;
@@ -2080,8 +2053,8 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 			nm_device_remove_pending_action (device, NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE);
 		break;
 	case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
-		remove_supplicant_interface_error_handler (self);
-		remove_supplicant_timeouts (self);
+		nm_clear_g_source (&priv->sup_timeout_id);
+		nm_clear_g_source (&priv->link_timeout_id);
 
 		/* If this is the initial association during device activation,
 		 * schedule the next activation stage.
@@ -2171,36 +2144,21 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface,
 }
 
 static void
-supplicant_iface_connection_error_cb (NMSupplicantInterface *iface,
-                                      const char *name,
-                                      const char *message,
-                                      NMDeviceWifi *self)
+supplicant_iface_assoc_cb (NMSupplicantInterface *iface,
+                           GError *error,
+                           gpointer user_data)
 {
+	NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
 	NMDevice *device = NM_DEVICE (self);
 
-	if (nm_device_is_activating (device)) {
-		_LOGW (LOGD_DEVICE | LOGD_WIFI,
-		       "Activation: (wifi) supplicant association failed: %s - %s",
-		       name, message);
-
+	if (   error && !nm_utils_error_is_cancelled (error, TRUE)
+	    && nm_device_is_activating (device)) {
 		cleanup_association_attempt (self, TRUE);
 		nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
 	}
 }
 
 static void
-remove_supplicant_interface_error_handler (NMDeviceWifi *self)
-{
-	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-
-	if (priv->sup_iface) {
-		g_signal_handlers_disconnect_by_func (priv->sup_iface,
-		                                      supplicant_iface_connection_error_cb,
-		                                      self);
-	}
-}
-
-static void
 supplicant_iface_notify_scanning_cb (NMSupplicantInterface *iface,
                                      GParamSpec *pspec,
                                      NMDeviceWifi *self)
@@ -2259,7 +2217,7 @@ supplicant_iface_notify_current_bss (NMSupplicantInterface *iface,
 	}
 }
 
-static NMActStageReturn
+static gboolean
 handle_auth_or_fail (NMDeviceWifi *self,
                      NMActRequest *req,
                      gboolean new_secrets)
@@ -2267,35 +2225,34 @@ handle_auth_or_fail (NMDeviceWifi *self,
 	const char *setting_name;
 	guint32 tries;
 	NMConnection *applied_connection;
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 
-	g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (NM_IS_DEVICE_WIFI (self), FALSE);
 
 	if (!req) {
 		req = nm_device_get_act_request (NM_DEVICE (self));
-		g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
+		g_return_val_if_fail (req, FALSE);
 	}
 
 	applied_connection = nm_act_request_get_applied_connection (req);
 
-	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (applied_connection), WIRELESS_SECRETS_TRIES));
+	tries = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark ()));
 	if (tries > 3)
-		return NM_ACT_STAGE_RETURN_FAILURE;
+		return FALSE;
 
 	nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE);
 
 	nm_act_request_clear_secrets (req);
 	setting_name = nm_connection_need_secrets (applied_connection, NULL);
-	if (setting_name) {
-		wifi_secrets_get_secrets (self, setting_name,
-		                          NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
-		                          | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0));
-		g_object_set_data (G_OBJECT (applied_connection), WIRELESS_SECRETS_TRIES, GUINT_TO_POINTER (++tries));
-		ret = NM_ACT_STAGE_RETURN_POSTPONE;
-	} else
+	if (!setting_name) {
 		_LOGW (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets.");
+		return FALSE;
+	}
 
-	return ret;
+	wifi_secrets_get_secrets (self, setting_name,
+	                          NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
+	                          | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0));
+	g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), GUINT_TO_POINTER (++tries));
+	return TRUE;
 }
 
 /*
@@ -2364,7 +2321,7 @@ supplicant_connection_timeout_cb (gpointer user_data)
 		if (nm_settings_connection_get_timestamp (nm_act_request_get_settings_connection (req), &timestamp))
 			new_secrets = !timestamp;
 
-		if (handle_auth_or_fail (self, req, new_secrets) == NM_ACT_STAGE_RETURN_POSTPONE)
+		if (handle_auth_or_fail (self, req, new_secrets))
 			_LOGW (LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) asking for new secrets");
 		else {
 			nm_device_state_changed (device, NM_DEVICE_STATE_FAILED,
@@ -2417,7 +2374,7 @@ build_supplicant_config (NMDeviceWifi *self,
 	if (s_wireless_sec) {
 		NMSetting8021x *s_8021x;
 		const char *con_uuid = nm_connection_get_uuid (connection);
-		guint32 mtu = nm_platform_link_get_mtu (NM_PLATFORM_GET,
+		guint32 mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)),
 		                                        nm_device_get_ifindex (NM_DEVICE (self)));
 
 		g_assert (con_uuid);
@@ -2448,7 +2405,7 @@ error:
 /*****************************************************************************/
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
@@ -2460,18 +2417,18 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 	const char *mode;
 	const char *ap_path;
 
-	ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage1_prepare (device, reason);
+	ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage1_prepare (device, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
 	req = nm_device_get_act_request (NM_DEVICE (self));
-	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
 
 	connection = nm_act_request_get_applied_connection (req);
-	g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
 	s_wireless = nm_connection_get_setting_wireless (connection);
-	g_assert (s_wireless);
+	g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE);
 
 	mode = nm_setting_wireless_get_mode (s_wireless);
 	if (g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) == 0)
@@ -2492,12 +2449,12 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
 	 */
 	if (is_adhoc_wpa (connection)) {
 		_LOGW (LOGD_WIFI, "Ad-Hoc WPA disabled due to kernel bugs");
-		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	/* forget the temporary MAC address used during scanning */
-	g_clear_pointer (&priv->hw_addr_scan, g_free);
+	/* expire the temporary MAC address used during scanning */
+	priv->hw_addr_scan_expire = 0;
 
 	/* Set spoof MAC to the interface */
 	if (!nm_device_hw_addr_set_cloned (device, connection, TRUE))
@@ -2549,6 +2506,7 @@ ensure_hotspot_frequency (NMDeviceWifi *self,
                           NMSettingWireless *s_wifi,
                           NMWifiAP *ap)
 {
+	NMDevice *device = NM_DEVICE (self);
 	const char *band = nm_setting_wireless_get_band (s_wifi);
 	const guint32 a_freqs[] = { 5180, 5200, 5220, 5745, 5765, 5785, 5805, 0 };
 	const guint32 bg_freqs[] = { 2412, 2437, 2462, 2472, 0 };
@@ -2560,14 +2518,15 @@ ensure_hotspot_frequency (NMDeviceWifi *self,
 		return;
 
 	if (g_strcmp0 (band, "a") == 0)
-		freq = nm_platform_wifi_find_frequency (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), a_freqs);
+		freq = nm_platform_wifi_find_frequency (nm_device_get_platform (device), nm_device_get_ifindex (device), a_freqs);
 	else
-		freq = nm_platform_wifi_find_frequency (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), bg_freqs);
+		freq = nm_platform_wifi_find_frequency (nm_device_get_platform (device), nm_device_get_ifindex (device), bg_freqs);
 
 	if (!freq)
 		freq = (g_strcmp0 (band, "a") == 0) ? 5180 : 2462;
 
-	nm_wifi_ap_set_freq (ap, freq);
+	if (nm_wifi_ap_set_freq (ap, freq))
+		_ap_dump (self, LOGL_DEBUG, ap, "updated", 0);
 }
 
 static void
@@ -2592,18 +2551,18 @@ set_powersave (NMDevice *device)
 		                                          NM_SETTING_WIRELESS_POWERSAVE_IGNORE);
 	}
 
-	_LOGT (LOGD_WIFI, "powersave is set to %u", (unsigned int) powersave);
+	_LOGT (LOGD_WIFI, "powersave is set to %u", (unsigned) powersave);
 
 	if (powersave == NM_SETTING_WIRELESS_POWERSAVE_IGNORE)
 		return;
 
-	nm_platform_wifi_set_powersave (NM_PLATFORM_GET,
+	nm_platform_wifi_set_powersave (nm_device_get_platform (device),
 	                                nm_device_get_ifindex (device),
 	                                powersave == NM_SETTING_WIRELESS_POWERSAVE_ENABLE);
 }
 
 static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMDeviceWifi *self = NM_DEVICE_WIFI (device);
 	NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
@@ -2615,17 +2574,17 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 	const char *setting_name;
 	NMSettingWireless *s_wireless;
 	GError *error = NULL;
+	guint timeout;
 
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
-	remove_supplicant_timeouts (self);
+	nm_clear_g_source (&priv->sup_timeout_id);
+	nm_clear_g_source (&priv->link_timeout_id);
 
 	req = nm_device_get_act_request (device);
-	g_assert (req);
+	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
 
 	ap = priv->current_ap;
 	if (!ap) {
-		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
 		goto out;
 	}
 
@@ -2642,9 +2601,12 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 		       "Activation: (wifi) access point '%s' has security, but secrets are required.",
 		       nm_connection_get_id (connection));
 
-		ret = handle_auth_or_fail (self, req, FALSE);
-		if (ret == NM_ACT_STAGE_RETURN_FAILURE)
-			*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+		if (handle_auth_or_fail (self, req, FALSE))
+			ret = NM_ACT_STAGE_RETURN_POSTPONE;
+		else {
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
+			ret = NM_ACT_STAGE_RETURN_FAILURE;
+		}
 		goto out;
 	}
 
@@ -2678,27 +2640,18 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
 		       "Activation: (wifi) couldn't build wireless configuration: %s",
 		       error->message);
 		g_clear_error (&error);
-		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
 		goto out;
 	}
 
-	/* Hook up error signal handler to capture association errors */
-	g_signal_connect (priv->sup_iface,
-	                  NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
-	                  G_CALLBACK (supplicant_iface_connection_error_cb),
-	                  self);
-
-	if (!nm_supplicant_interface_set_config (priv->sup_iface, config, &error)) {
-		_LOGE (LOGD_DEVICE | LOGD_WIFI,
-		       "Activation: (wifi) couldn't send wireless configuration to the supplicant: %s",
-		       error->message);
-		g_clear_error (&error);
-		*reason = NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED;
-		goto out;
-	}
+	nm_supplicant_interface_assoc (priv->sup_iface, config,
+	                               supplicant_iface_assoc_cb, self);
 
-	/* Set up a timeout on the association attempt to fail after 25 seconds */
-	priv->sup_timeout_id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self);
+	/* Set up a timeout on the association attempt */
+	timeout = nm_device_get_supplicant_timeout (NM_DEVICE (self));
+	priv->sup_timeout_id = g_timeout_add_seconds (timeout,
+	                                              supplicant_connection_timeout_cb,
+	                                              self);
 
 	if (!priv->periodic_source_id)
 		priv->periodic_source_id = g_timeout_add_seconds (6, periodic_update_cb, self);
@@ -2722,36 +2675,38 @@ out:
 static NMActStageReturn
 act_stage3_ip4_config_start (NMDevice *device,
                              NMIP4Config **out_config,
-                             NMDeviceStateReason *reason)
+                             NMDeviceStateReason *out_failure_reason)
 {
 	NMConnection *connection;
 	NMSettingIPConfig *s_ip4;
 	const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
 
 	connection = nm_device_get_applied_connection (device);
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	if (s_ip4)
 		method = nm_setting_ip_config_get_method (s_ip4);
 
 	/* Indicate that a critical protocol is about to start */
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0)
-		nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), TRUE);
+		nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), TRUE);
 
-	return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip4_config_start (device, out_config, reason);
+	return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip4_config_start (device, out_config, out_failure_reason);
 }
 
 static NMActStageReturn
 act_stage3_ip6_config_start (NMDevice *device,
                              NMIP6Config **out_config,
-                             NMDeviceStateReason *reason)
+                             NMDeviceStateReason *out_failure_reason)
 {
 	NMConnection *connection;
 	NMSettingIPConfig *s_ip6;
 	const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
 
 	connection = nm_device_get_applied_connection (device);
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	if (s_ip6)
 		method = nm_setting_ip_config_get_method (s_ip6);
@@ -2759,9 +2714,9 @@ act_stage3_ip6_config_start (NMDevice *device,
 	/* Indicate that a critical protocol is about to start */
 	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 ||
 	    strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0)
-		nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), TRUE);
+		nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), TRUE);
 
-	return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip6_config_start (device, out_config, reason);
+	return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip6_config_start (device, out_config, out_failure_reason);
 }
 
 static guint32
@@ -2818,7 +2773,7 @@ handle_ip_config_timeout (NMDeviceWifi *self,
                           NMConnection *connection,
                           gboolean may_fail,
                           gboolean *chain_up,
-                          NMDeviceStateReason *reason)
+                          NMDeviceStateReason *out_failure_reason)
 {
 	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 
@@ -2826,7 +2781,7 @@ handle_ip_config_timeout (NMDeviceWifi *self,
 
 	if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP) {
 		*chain_up = TRUE;
-		return ret;
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	/* If IP configuration times out and it's a static WEP connection, that
@@ -2842,12 +2797,13 @@ handle_ip_config_timeout (NMDeviceWifi *self,
 		       "Activation: (wifi) could not get IP configuration for connection '%s'.",
 		       nm_connection_get_id (connection));
 
-		ret = handle_auth_or_fail (self, NULL, TRUE);
-		if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
+		if (handle_auth_or_fail (self, NULL, TRUE)) {
 			_LOGI (LOGD_DEVICE | LOGD_WIFI,
 			       "Activation: (wifi) asking for new secrets");
+			ret = NM_ACT_STAGE_RETURN_POSTPONE;
 		} else {
-			*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS);
+			ret = NM_ACT_STAGE_RETURN_FAILURE;
 		}
 	} else {
 		/* Not static WEP or failure allowed; let superclass handle it */
@@ -2859,7 +2815,7 @@ handle_ip_config_timeout (NMDeviceWifi *self,
 
 
 static NMActStageReturn
-act_stage4_ip4_config_timeout (NMDevice *device, NMDeviceStateReason *reason)
+act_stage4_ip4_config_timeout (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMConnection *connection;
 	NMSettingIPConfig *s_ip4;
@@ -2867,20 +2823,20 @@ act_stage4_ip4_config_timeout (NMDevice *device, NMDeviceStateReason *reason)
 	NMActStageReturn ret;
 
 	connection = nm_device_get_applied_connection (device);
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
 	s_ip4 = nm_connection_get_setting_ip4_config (connection);
 	may_fail = nm_setting_ip_config_get_may_fail (s_ip4);
 
-	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (device), connection, may_fail, &chain_up, reason);
+	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (device), connection, may_fail, &chain_up, out_failure_reason);
 	if (chain_up)
-		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (device, reason);
+		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (device, out_failure_reason);
 
 	return ret;
 }
 
 static NMActStageReturn
-act_stage4_ip6_config_timeout (NMDevice *device, NMDeviceStateReason *reason)
+act_stage4_ip6_config_timeout (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMConnection *connection;
 	NMSettingIPConfig *s_ip6;
@@ -2888,14 +2844,14 @@ act_stage4_ip6_config_timeout (NMDevice *device, NMDeviceStateReason *reason)
 	NMActStageReturn ret;
 
 	connection = nm_device_get_applied_connection (device);
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	may_fail = nm_setting_ip_config_get_may_fail (s_ip6);
 
-	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (device), connection, may_fail, &chain_up, reason);
+	ret = handle_ip_config_timeout (NM_DEVICE_WIFI (device), connection, may_fail, &chain_up, out_failure_reason);
 	if (chain_up)
-		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (device, reason);
+		ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (device, out_failure_reason);
 
 	return ret;
 }
@@ -2915,10 +2871,10 @@ activation_success_handler (NMDevice *device)
 	applied_connection = nm_act_request_get_applied_connection (req);
 
 	/* Clear any critical protocol notification in the wifi stack */
-	nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, ifindex, FALSE);
+	nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), ifindex, FALSE);
 
 	/* Clear wireless secrets tries on success */
-	g_object_set_data (G_OBJECT (applied_connection), WIRELESS_SECRETS_TRIES, NULL);
+	g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), NULL);
 
 	/* There should always be a current AP, either a fake one because we haven't
 	 * seen a scan result for the activated AP yet, or a real one from the
@@ -2927,6 +2883,8 @@ activation_success_handler (NMDevice *device)
 	g_warn_if_fail (priv->current_ap);
 	if (priv->current_ap) {
 		if (nm_wifi_ap_get_fake (priv->current_ap)) {
+			gboolean ap_changed = FALSE;
+
 			/* If the activation AP hasn't been seen by the supplicant in a scan
 			 * yet, it will be "fake".  This usually happens for Ad-Hoc and
 			 * AP-mode connections.  Fill in the details from the device itself
@@ -2936,16 +2894,19 @@ activation_success_handler (NMDevice *device)
 				guint8 bssid[ETH_ALEN] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
 				gs_free char *bssid_str = NULL;
 
-				if (   nm_platform_wifi_get_bssid (NM_PLATFORM_GET, ifindex, bssid)
+				if (   nm_platform_wifi_get_bssid (nm_device_get_platform (device), ifindex, bssid)
 				    && nm_ethernet_address_is_valid (bssid, ETH_ALEN)) {
 					bssid_str = nm_utils_hwaddr_ntoa (bssid, ETH_ALEN);
-					nm_wifi_ap_set_address (priv->current_ap, bssid_str);
+					ap_changed |= nm_wifi_ap_set_address (priv->current_ap, bssid_str);
 				}
 			}
 			if (!nm_wifi_ap_get_freq (priv->current_ap))
-				nm_wifi_ap_set_freq (priv->current_ap, nm_platform_wifi_get_frequency (NM_PLATFORM_GET, ifindex));
+				ap_changed |= nm_wifi_ap_set_freq (priv->current_ap, nm_platform_wifi_get_frequency (nm_device_get_platform (device), ifindex));
 			if (!nm_wifi_ap_get_max_bitrate (priv->current_ap))
-				nm_wifi_ap_set_max_bitrate (priv->current_ap, nm_platform_wifi_get_rate (NM_PLATFORM_GET, ifindex));
+				ap_changed |= nm_wifi_ap_set_max_bitrate (priv->current_ap, nm_platform_wifi_get_rate (nm_device_get_platform (device), ifindex));
+
+			if (ap_changed)
+				_ap_dump (self, LOGL_DEBUG, priv->current_ap, "updated", 0);
 		}
 
 		nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req),
@@ -2969,10 +2930,10 @@ activation_failure_handler (NMDevice *device)
 	g_assert (applied_connection);
 
 	/* Clear wireless secrets tries on failure */
-	g_object_set_data (G_OBJECT (applied_connection), WIRELESS_SECRETS_TRIES, NULL);
+	g_object_set_qdata (G_OBJECT (applied_connection), wireless_secrets_tries_quark (), NULL);
 
 	/* Clear any critical protocol notification in the wifi stack */
-	nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), FALSE);
+	nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), FALSE);
 }
 
 static void
@@ -3023,7 +2984,7 @@ device_state_changed (NMDevice *device,
 		break;
 	case NM_DEVICE_STATE_IP_CHECK:
 		/* Clear any critical protocol notification in the wifi stack */
-		nm_platform_wifi_indicate_addressing_running (NM_PLATFORM_GET, nm_device_get_ifindex (device), FALSE);
+		nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), FALSE);
 		break;
 	case NM_DEVICE_STATE_ACTIVATED:
 		activation_success_handler (device);
@@ -3106,6 +3067,33 @@ set_enabled (NMDevice *device, gboolean enabled)
 	}
 }
 
+static gboolean
+can_reapply_change (NMDevice *device,
+                    const char *setting_name,
+                    NMSetting *s_old,
+                    NMSetting *s_new,
+                    GHashTable *diffs,
+                    GError **error)
+{
+	NMDeviceClass *device_class;
+
+	/* Only handle wireless setting here, delegate other settings to parent class */
+	if (nm_streq (setting_name, NM_SETTING_WIRELESS_SETTING_NAME)) {
+		return nm_device_hash_check_invalid_keys (diffs,
+		                                          NM_SETTING_WIRELESS_SETTING_NAME,
+		                                          error,
+		                                          NM_SETTING_WIRELESS_MTU); /* reapplied with IP config */
+	}
+
+	device_class = NM_DEVICE_CLASS (nm_device_wifi_parent_class);
+	return device_class->can_reapply_change (device,
+	                                         setting_name,
+	                                         s_old,
+	                                         s_new,
+	                                         diffs,
+	                                         error);
+}
+
 /*****************************************************************************/
 
 static void
@@ -3233,8 +3221,6 @@ finalize (GObject *object)
 
 	g_hash_table_unref (priv->aps);
 
-	g_free (priv->hw_addr_scan);
-
 	G_OBJECT_CLASS (nm_device_wifi_parent_class)->finalize (object);
 }
 
@@ -3253,6 +3239,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	object_class->finalize = finalize;
 
 	parent_class->can_auto_connect = can_auto_connect;
+	parent_class->get_autoconnect_allowed = get_autoconnect_allowed;
 	parent_class->is_available = is_available;
 	parent_class->check_connection_compatible = check_connection_compatible;
 	parent_class->check_connection_available = check_connection_available;
@@ -3270,6 +3257,7 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
 	parent_class->deactivate = deactivate;
 	parent_class->deactivate_reset_hw_addr = deactivate_reset_hw_addr;
 	parent_class->unmanaged_on_quit = unmanaged_on_quit;
+	parent_class->can_reapply_change = can_reapply_change;
 
 	parent_class->state_changed = device_state_changed;
 
diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c
index 96e9d19c..7de0838f 100644
--- a/src/devices/wifi/nm-wifi-ap.c
+++ b/src/devices/wifi/nm-wifi-ap.c
@@ -29,6 +29,7 @@
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
 #include "nm-core-internal.h"
+#include "platform/nm-platform.h"
 
 #include "nm-setting-wireless.h"
 
@@ -68,8 +69,8 @@ typedef struct {
 	NM80211ApSecurityFlags rsn_flags;  /* RSN (WPA2) -related flags */
 
 	/* Non-scanned attributes */
-	bool                fake;       /* Whether or not the AP is from a scan */
-	bool                hotspot;    /* Whether the AP is a local device's hotspot network */
+	bool                fake:1;       /* Whether or not the AP is from a scan */
+	bool                hotspot:1;    /* Whether the AP is a local device's hotspot network */
 	gint32              last_seen;  /* Timestamp when the AP was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */
 } NMWifiAPPrivate;
 
@@ -122,20 +123,20 @@ const GByteArray * nm_wifi_ap_get_ssid (const NMWifiAP *ap)
 	return NM_WIFI_AP_GET_PRIVATE (ap)->ssid;
 }
 
-void
+gboolean
 nm_wifi_ap_set_ssid (NMWifiAP *ap, const guint8 *ssid, gsize len)
 {
 	NMWifiAPPrivate *priv;
 
-	g_return_if_fail (NM_IS_WIFI_AP (ap));
-	g_return_if_fail (ssid == NULL || len > 0);
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+	g_return_val_if_fail (ssid == NULL || len > 0, FALSE);
 
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	/* same SSID */
 	if ((ssid && priv->ssid) && (len == priv->ssid->len)) {
 		if (!memcmp (ssid, priv->ssid->data, len))
-			return;
+			return FALSE;
 	}
 
 	if (priv->ssid) {
@@ -149,49 +150,56 @@ nm_wifi_ap_set_ssid (NMWifiAP *ap, const guint8 *ssid, gsize len)
 	}
 
 	_notify (ap, PROP_SSID);
+	return TRUE;
 }
 
-static void
+static gboolean
 nm_wifi_ap_set_flags (NMWifiAP *ap, NM80211ApFlags flags)
 {
 	NMWifiAPPrivate *priv;
 
-	g_return_if_fail (NM_IS_WIFI_AP (ap));
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
 
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	if (priv->flags != flags) {
 		priv->flags = flags;
 		_notify (ap, PROP_FLAGS);
+		return TRUE;
 	}
+	return FALSE;
 }
 
-static void
+static gboolean
 nm_wifi_ap_set_wpa_flags (NMWifiAP *ap, NM80211ApSecurityFlags flags)
 {
 	NMWifiAPPrivate *priv;
 
-	g_return_if_fail (NM_IS_WIFI_AP (ap));
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
 
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 	if (priv->wpa_flags != flags) {
 		priv->wpa_flags = flags;
 		_notify (ap, PROP_WPA_FLAGS);
+		return TRUE;
 	}
+	return FALSE;
 }
 
-static void
+static gboolean
 nm_wifi_ap_set_rsn_flags (NMWifiAP *ap, NM80211ApSecurityFlags flags)
 {
 	NMWifiAPPrivate *priv;
 
-	g_return_if_fail (NM_IS_WIFI_AP (ap));
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
 
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 	if (priv->rsn_flags != flags) {
 		priv->rsn_flags = flags;
 		_notify (ap, PROP_RSN_FLAGS);
+		return TRUE;
 	}
+	return FALSE;
 }
 
 const char *
@@ -202,25 +210,34 @@ nm_wifi_ap_get_address (const NMWifiAP *ap)
 	return NM_WIFI_AP_GET_PRIVATE (ap)->address;
 }
 
-void
-nm_wifi_ap_set_address (NMWifiAP *ap, const char *addr)
+static gboolean
+nm_wifi_ap_set_address_bin (NMWifiAP *ap, const guint8 *addr /* ETH_ALEN bytes */)
 {
 	NMWifiAPPrivate *priv;
-	guint8 addr_buf[ETH_ALEN];
-
-	g_return_if_fail (NM_IS_WIFI_AP (ap));
-	if (   !addr
-	    || !nm_utils_hwaddr_aton (addr, addr_buf, sizeof (addr_buf)))
-		g_return_if_reached ();
 
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	if (   !priv->address
-	    || !nm_utils_hwaddr_matches (addr_buf, sizeof (addr_buf), priv->address, -1)) {
+	    || !nm_utils_hwaddr_matches (addr, ETH_ALEN, priv->address, -1)) {
 		g_free (priv->address);
-		priv->address = nm_utils_hwaddr_ntoa (addr_buf, sizeof (addr_buf));
+		priv->address = nm_utils_hwaddr_ntoa (addr, ETH_ALEN);
 		_notify (ap, PROP_HW_ADDRESS);
+		return TRUE;
 	}
+	return FALSE;
+}
+
+gboolean
+nm_wifi_ap_set_address (NMWifiAP *ap, const char *addr)
+{
+	guint8 addr_buf[ETH_ALEN];
+
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+	if (   !addr
+	    || !nm_utils_hwaddr_aton (addr, addr_buf, sizeof (addr_buf)))
+		g_return_val_if_reached (FALSE);
+
+	return nm_wifi_ap_set_address_bin (ap, addr_buf);
 }
 
 NM80211Mode
@@ -231,21 +248,23 @@ nm_wifi_ap_get_mode (NMWifiAP *ap)
 	return NM_WIFI_AP_GET_PRIVATE (ap)->mode;
 }
 
-static void
+static gboolean
 nm_wifi_ap_set_mode (NMWifiAP *ap, const NM80211Mode mode)
 {
 	NMWifiAPPrivate *priv;
 
-	g_return_if_fail (NM_IS_WIFI_AP (ap));
-	g_return_if_fail (   mode == NM_802_11_MODE_ADHOC
-	                  || mode == NM_802_11_MODE_INFRA);
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+	g_return_val_if_fail (   mode == NM_802_11_MODE_ADHOC
+	                     || mode == NM_802_11_MODE_INFRA, FALSE);
 
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	if (priv->mode != mode) {
 		priv->mode = mode;
 		_notify (ap, PROP_MODE);
+		return TRUE;
 	}
+	return FALSE;
 }
 
 gboolean
@@ -264,19 +283,21 @@ nm_wifi_ap_get_strength (NMWifiAP *ap)
 	return NM_WIFI_AP_GET_PRIVATE (ap)->strength;
 }
 
-void
+gboolean
 nm_wifi_ap_set_strength (NMWifiAP *ap, const gint8 strength)
 {
 	NMWifiAPPrivate *priv;
 
-	g_return_if_fail (NM_IS_WIFI_AP (ap));
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
 
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	if (priv->strength != strength) {
 		priv->strength = strength;
 		_notify (ap, PROP_STRENGTH);
+		return TRUE;
 	}
+	return FALSE;
 }
 
 guint32
@@ -287,20 +308,22 @@ nm_wifi_ap_get_freq (NMWifiAP *ap)
 	return NM_WIFI_AP_GET_PRIVATE (ap)->freq;
 }
 
-void
+gboolean
 nm_wifi_ap_set_freq (NMWifiAP *ap,
                      const guint32 freq)
 {
 	NMWifiAPPrivate *priv;
 
-	g_return_if_fail (NM_IS_WIFI_AP (ap));
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
 
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	if (priv->freq != freq) {
 		priv->freq = freq;
 		_notify (ap, PROP_FREQUENCY);
+		return TRUE;
 	}
+	return FALSE;
 }
 
 guint32
@@ -312,19 +335,21 @@ nm_wifi_ap_get_max_bitrate (NMWifiAP *ap)
 	return NM_WIFI_AP_GET_PRIVATE (ap)->max_bitrate;
 }
 
-void
+gboolean
 nm_wifi_ap_set_max_bitrate (NMWifiAP *ap, guint32 bitrate)
 {
 	NMWifiAPPrivate *priv;
 
-	g_return_if_fail (NM_IS_WIFI_AP (ap));
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
 
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	if (priv->max_bitrate != bitrate) {
 		priv->max_bitrate = bitrate;
 		_notify (ap, PROP_MAX_BITRATE);
+		return TRUE;
 	}
+	return FALSE;
 }
 
 gboolean
@@ -335,27 +360,37 @@ nm_wifi_ap_get_fake (const NMWifiAP *ap)
 	return NM_WIFI_AP_GET_PRIVATE (ap)->fake;
 }
 
-void
+gboolean
 nm_wifi_ap_set_fake (NMWifiAP *ap, gboolean fake)
 {
-	g_return_if_fail (NM_IS_WIFI_AP (ap));
+	NMWifiAPPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+
+	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
-	NM_WIFI_AP_GET_PRIVATE (ap)->fake = fake;
+	if (priv->fake != !!fake) {
+		priv->fake = fake;
+		return TRUE;
+	}
+	return FALSE;
 }
 
-static void
+static gboolean
 nm_wifi_ap_set_last_seen (NMWifiAP *ap, gint32 last_seen)
 {
 	NMWifiAPPrivate *priv;
 
-	g_return_if_fail (NM_IS_WIFI_AP (ap));
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
 
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	if (priv->last_seen != last_seen) {
 		priv->last_seen = last_seen;
 		_notify (ap, PROP_LAST_SEEN);
+		return TRUE;
 	}
+	return FALSE;
 }
 
 /*****************************************************************************/
@@ -400,13 +435,12 @@ security_from_vardict (GVariant *security)
 	return flags;
 }
 
-void
+gboolean
 nm_wifi_ap_update_from_properties (NMWifiAP *ap,
                                    const char *supplicant_path,
                                    GVariant *properties)
 {
 	NMWifiAPPrivate *priv;
-	char *addr;
 	const guint8 *bytes;
 	GVariant *v;
 	gsize len;
@@ -414,28 +448,30 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
 	const char *s;
 	gint16 i16;
 	guint16 u16;
+	gboolean changed = FALSE;
+
+	g_return_val_if_fail (NM_IS_WIFI_AP (ap), FALSE);
+	g_return_val_if_fail (properties, FALSE);
 
-	g_return_if_fail (ap != NULL);
-	g_return_if_fail (properties != NULL);
 	priv = NM_WIFI_AP_GET_PRIVATE (ap);
 
 	g_object_freeze_notify (G_OBJECT (ap));
 
 	if (g_variant_lookup (properties, "Privacy", "b", &b) && b)
-		nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_PRIVACY);
+		changed |= nm_wifi_ap_set_flags (ap, priv->flags | NM_802_11_AP_FLAGS_PRIVACY);
 
 	if (g_variant_lookup (properties, "Mode", "&s", &s)) {
 		if (!g_strcmp0 (s, "infrastructure"))
-			nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA);
+			changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_INFRA);
 		else if (!g_strcmp0 (s, "ad-hoc"))
-			nm_wifi_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
+			changed |= nm_wifi_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
 	}
 
 	if (g_variant_lookup (properties, "Signal", "n", &i16))
-		nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (i16));
+		changed |= nm_wifi_ap_set_strength (ap, nm_wifi_utils_level_to_quality (i16));
 
 	if (g_variant_lookup (properties, "Frequency", "q", &u16))
-		nm_wifi_ap_set_freq (ap, u16);
+		changed |= nm_wifi_ap_set_freq (ap, u16);
 
 	v = g_variant_lookup_value (properties, "SSID", G_VARIANT_TYPE_BYTESTRING);
 	if (v) {
@@ -446,7 +482,7 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
 		if (   bytes && len
 		    && !(((len == 8) || (len == 9)) && !memcmp (bytes, "<hidden>", 8))
 		    && !nm_utils_is_empty_ssid (bytes, len))
-			nm_wifi_ap_set_ssid (ap, bytes, len);
+			changed |= nm_wifi_ap_set_ssid (ap, bytes, len);
 
 		g_variant_unref (v);
 	}
@@ -454,11 +490,10 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
 	v = g_variant_lookup_value (properties, "BSSID", G_VARIANT_TYPE_BYTESTRING);
 	if (v) {
 		bytes = g_variant_get_fixed_array (v, &len, 1);
-		if (len == ETH_ALEN) {
-			addr = nm_utils_hwaddr_ntoa (bytes, len);
-			nm_wifi_ap_set_address (ap, addr);
-			g_free (addr);
-		}
+		if (   len == ETH_ALEN
+		    && memcmp (bytes, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0
+		    && memcmp (bytes, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0)
+			changed |= nm_wifi_ap_set_address_bin (ap, bytes);
 		g_variant_unref (v);
 	}
 
@@ -470,33 +505,37 @@ nm_wifi_ap_update_from_properties (NMWifiAP *ap,
 
 		/* Find the max AP rate */
 		for (i = 0; i < len; i++) {
-			if (rates[i] > maxrate) {
+			if (rates[i] > maxrate)
 				maxrate = rates[i];
-				nm_wifi_ap_set_max_bitrate (ap, rates[i] / 1000);
-			}
 		}
+		if (maxrate)
+			changed |= nm_wifi_ap_set_max_bitrate (ap, maxrate / 1000);
 		g_variant_unref (v);
 	}
 
 	v = g_variant_lookup_value (properties, "WPA", G_VARIANT_TYPE_VARDICT);
 	if (v) {
-		nm_wifi_ap_set_wpa_flags (ap, priv->wpa_flags | security_from_vardict (v));
+		changed |= nm_wifi_ap_set_wpa_flags (ap, priv->wpa_flags | security_from_vardict (v));
 		g_variant_unref (v);
 	}
 
 	v = g_variant_lookup_value (properties, "RSN", G_VARIANT_TYPE_VARDICT);
 	if (v) {
-		nm_wifi_ap_set_rsn_flags (ap, priv->rsn_flags | security_from_vardict (v));
+		changed |= nm_wifi_ap_set_rsn_flags (ap, priv->rsn_flags | security_from_vardict (v));
 		g_variant_unref (v);
 	}
 
-	if (!priv->supplicant_path)
+	if (!priv->supplicant_path) {
 		priv->supplicant_path = g_strdup (supplicant_path);
+		changed = TRUE;
+	}
 
-	nm_wifi_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
-	priv->fake = FALSE;
+	changed |= nm_wifi_ap_set_last_seen (ap, nm_utils_get_monotonic_timestamp_s ());
+	changed |= nm_wifi_ap_set_fake (ap, FALSE);
 
 	g_object_thaw_notify (G_OBJECT (ap));
+
+	return changed;
 }
 
 static gboolean
@@ -583,6 +622,7 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
 {
 	const NMWifiAPPrivate *priv;
 	const char *supplicant_id = "-";
+	const char *export_path;
 	guint32 chan;
 	char b1[200];
 
@@ -591,10 +631,16 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
 	priv = NM_WIFI_AP_GET_PRIVATE (self);
 	chan = nm_utils_wifi_freq_to_channel (priv->freq);
 	if (priv->supplicant_path)
-		supplicant_id = strrchr (priv->supplicant_path, '/');
+		supplicant_id = strrchr (priv->supplicant_path, '/') ?: supplicant_id;
+
+	export_path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (self));
+	if (export_path)
+		export_path = strrchr (export_path, '/') ?: export_path;
+	else
+		export_path = "/";
 
 	g_snprintf (str_buf, buf_len,
-	            "%17s %-32s [ %c %3u %3u%% %c W:%04X R:%04X ] %3us %s",
+	            "%17s %-32s [ %c %3u %3u%% %c W:%04X R:%04X ] %3us sup:%s [nm:%s]",
 	            priv->address ?: "(none)",
 	            nm_sprintf_buf (b1, "%s%s%s",
 	                            NM_PRINT_FMT_QUOTED (priv->ssid, "\"", nm_utils_escape_ssid (priv->ssid->data, priv->ssid->len), "\"", "(none)")),
@@ -611,7 +657,8 @@ nm_wifi_ap_to_string (const NMWifiAP *self,
 	            priv->wpa_flags & 0xFFFF,
 	            priv->rsn_flags & 0xFFFF,
 	            priv->last_seen > 0 ? ((now_s > 0 ? now_s : nm_utils_get_monotonic_timestamp_s ()) - priv->last_seen) : -1,
-	            supplicant_id);
+	            supplicant_id,
+	            export_path);
 	return str_buf;
 }
 
@@ -793,10 +840,7 @@ nm_wifi_ap_init (NMWifiAP *ap)
 NMWifiAP *
 nm_wifi_ap_new_from_properties (const char *supplicant_path, GVariant *properties)
 {
-	const char bad_bssid1[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-	const char bad_bssid2[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
 	NMWifiAP *ap;
-	const char *addr;
 
 	g_return_val_if_fail (supplicant_path != NULL, NULL);
 	g_return_val_if_fail (properties != NULL, NULL);
@@ -805,10 +849,7 @@ nm_wifi_ap_new_from_properties (const char *supplicant_path, GVariant *propertie
 	nm_wifi_ap_update_from_properties (ap, supplicant_path, properties);
 
 	/* ignore APs with invalid or missing BSSIDs */
-	addr = nm_wifi_ap_get_address (ap);
-	if (   !addr
-	    || nm_utils_hwaddr_matches (addr, -1, bad_bssid1, ETH_ALEN)
-	    || nm_utils_hwaddr_matches (addr, -1, bad_bssid2, ETH_ALEN)) {
+	if (!nm_wifi_ap_get_address (ap)) {
 		g_object_unref (ap);
 		return NULL;
 	}
diff --git a/src/devices/wifi/nm-wifi-ap.h b/src/devices/wifi/nm-wifi-ap.h
index a68aece8..5e64087c 100644
--- a/src/devices/wifi/nm-wifi-ap.h
+++ b/src/devices/wifi/nm-wifi-ap.h
@@ -53,7 +53,7 @@ NMWifiAP *   nm_wifi_ap_new_from_properties      (const char *supplicant_path,
                                                   GVariant *properties);
 NMWifiAP *   nm_wifi_ap_new_fake_from_connection (NMConnection *connection);
 
-void              nm_wifi_ap_update_from_properties   (NMWifiAP *ap,
+gboolean          nm_wifi_ap_update_from_properties   (NMWifiAP *ap,
                                                        const char *supplicant_path,
                                                        GVariant *properties);
 
@@ -68,25 +68,25 @@ gboolean          nm_wifi_ap_complete_connection      (NMWifiAP *self,
 const char *      nm_wifi_ap_get_supplicant_path      (NMWifiAP *ap);
 guint64           nm_wifi_ap_get_id                   (NMWifiAP *ap);
 const GByteArray *nm_wifi_ap_get_ssid                 (const NMWifiAP *ap);
-void              nm_wifi_ap_set_ssid                 (NMWifiAP *ap,
+gboolean          nm_wifi_ap_set_ssid                 (NMWifiAP *ap,
                                                        const guint8 *ssid,
                                                        gsize len);
 const char *      nm_wifi_ap_get_address              (const NMWifiAP *ap);
-void              nm_wifi_ap_set_address              (NMWifiAP *ap,
+gboolean          nm_wifi_ap_set_address              (NMWifiAP *ap,
                                                        const char *addr);
 NM80211Mode       nm_wifi_ap_get_mode                 (NMWifiAP *ap);
 gboolean          nm_wifi_ap_is_hotspot               (NMWifiAP *ap);
 gint8             nm_wifi_ap_get_strength             (NMWifiAP *ap);
-void              nm_wifi_ap_set_strength             (NMWifiAP *ap,
+gboolean          nm_wifi_ap_set_strength             (NMWifiAP *ap,
                                                        gint8 strength);
 guint32           nm_wifi_ap_get_freq                 (NMWifiAP *ap);
-void              nm_wifi_ap_set_freq                 (NMWifiAP *ap,
+gboolean          nm_wifi_ap_set_freq                 (NMWifiAP *ap,
                                                        guint32 freq);
 guint32           nm_wifi_ap_get_max_bitrate          (NMWifiAP *ap);
-void              nm_wifi_ap_set_max_bitrate          (NMWifiAP *ap,
+gboolean          nm_wifi_ap_set_max_bitrate          (NMWifiAP *ap,
                                                        guint32 bitrate);
 gboolean          nm_wifi_ap_get_fake                 (const NMWifiAP *ap);
-void              nm_wifi_ap_set_fake                 (NMWifiAP *ap,
+gboolean          nm_wifi_ap_set_fake                 (NMWifiAP *ap,
                                                        gboolean fake);
 
 const char       *nm_wifi_ap_to_string                (const NMWifiAP *self,
diff --git a/src/devices/wifi/nm-wifi-utils.h b/src/devices/wifi/nm-wifi-utils.h
index 39c7fa17..1b6c2f4b 100644
--- a/src/devices/wifi/nm-wifi-utils.h
+++ b/src/devices/wifi/nm-wifi-utils.h
@@ -21,11 +21,11 @@
 #ifndef __NM_WIFI_UTILS_H__
 #define __NM_WIFI_UTILS_H__
 
-#include <nm-dbus-interface.h>
-#include <nm-connection.h>
-#include <nm-setting-wireless.h>
-#include <nm-setting-wireless-security.h>
-#include <nm-setting-8021x.h>
+#include "nm-dbus-interface.h"
+#include "nm-connection.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-8021x.h"
 
 gboolean nm_wifi_utils_complete_connection (const GByteArray *ssid,
                                             const char *bssid,
diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver
index 23412de6..eb577aaf 100644
--- a/src/devices/wwan/libnm-wwan.ver
+++ b/src/devices/wwan/libnm-wwan.ver
@@ -9,6 +9,7 @@ global:
 	nm_modem_deactivate_async_finish;
 	nm_modem_device_state_changed;
 	nm_modem_get_capabilities;
+	nm_modem_get_configured_mtu;
 	nm_modem_get_control_port;
 	nm_modem_get_data_port;
 	nm_modem_get_driver;
diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c
index 7a70a0bf..4a4d2f2c 100644
--- a/src/devices/wwan/nm-device-modem.c
+++ b/src/devices/wwan/nm-device-modem.c
@@ -68,10 +68,13 @@ G_DEFINE_TYPE (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE)
 /*****************************************************************************/
 
 static void
-ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data)
+ppp_failed (NMModem *modem,
+            guint i_reason,
+            gpointer user_data)
 {
 	NMDevice *device = NM_DEVICE (user_data);
 	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
+	NMDeviceStateReason reason = i_reason;
 
 	switch (nm_device_get_state (device)) {
 	case NM_DEVICE_STATE_PREPARE:
@@ -110,12 +113,13 @@ ppp_failed (NMModem *modem, NMDeviceStateReason reason, gpointer user_data)
 static void
 modem_prepare_result (NMModem *modem,
                       gboolean success,
-                      NMDeviceStateReason reason,
+                      guint i_reason,
                       gpointer user_data)
 {
 	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
 	NMDevice *device = NM_DEVICE (self);
 	NMDeviceState state;
+	NMDeviceStateReason reason = i_reason;
 
 	state = nm_device_get_state (device);
 	g_return_if_fail (state == NM_DEVICE_STATE_PREPARE);
@@ -123,12 +127,12 @@ modem_prepare_result (NMModem *modem,
 	if (success)
 		nm_device_activate_schedule_stage2_device_config (device);
 	else {
-		if (reason == NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT) {
+		if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_SIM_PIN_INCORRECT) {
 			/* If the connect failed because the SIM PIN was wrong don't allow
 			 * the device to be auto-activated anymore, which would risk locking
 			 * the SIM if the incorrect PIN continues to be used.
 			 */
-			nm_device_set_autoconnect (device, FALSE);
+			nm_device_set_autoconnect_intern (device, FALSE);
 			_LOGI (LOGD_MB, "disabling autoconnect due to failed SIM PIN");
 		}
 
@@ -201,7 +205,7 @@ modem_ip6_config_result (NMModem *modem,
 	NMDeviceModem *self = NM_DEVICE_MODEM (user_data);
 	NMDevice *device = NM_DEVICE (self);
 	NMActStageReturn ret;
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
+	NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 	NMIP6Config *ignored = NULL;
 	gboolean got_config = !!config;
 
@@ -235,11 +239,11 @@ modem_ip6_config_result (NMModem *modem,
 	}
 
 	/* Start SLAAC now that we have a link-local address from the modem */
-	ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage3_ip6_config_start (device, &ignored, &reason);
+	ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage3_ip6_config_start (device, &ignored, &failure_reason);
 	g_assert (ignored == NULL);
 	switch (ret) {
 	case NM_ACT_STAGE_RETURN_FAILURE:
-		nm_device_ip_method_failed (device, AF_INET6, reason);
+		nm_device_ip_method_failed (device, AF_INET6, failure_reason);
 		break;
 	case NM_ACT_STAGE_RETURN_IP_FAIL:
 		/* all done */
@@ -371,9 +375,9 @@ device_state_changed (NMDevice *device,
 		       nm_modem_state_to_string (nm_modem_get_state (priv->modem)));
 	}
 
-	nm_modem_device_state_changed (priv->modem, new_state, old_state, reason);
+	nm_modem_device_state_changed (priv->modem, new_state, old_state);
 
-	switch (reason) {
+	switch (nm_device_state_reason_check (reason)) {
 	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_DENIED:
 	case NM_DEVICE_STATE_REASON_GSM_REGISTRATION_NOT_SEARCHING:
 	case NM_DEVICE_STATE_REASON_GSM_SIM_NOT_INSERTED:
@@ -386,8 +390,10 @@ device_state_changed (NMDevice *device,
 		/* Block autoconnect of the just-failed connection for situations
 		 * where a retry attempt would just fail again.
 		 */
-		if (connection)
-			nm_settings_connection_set_autoconnect_blocked_reason (connection, reason);
+		if (connection) {
+			nm_settings_connection_set_autoconnect_blocked_reason (connection,
+			                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED);
+		}
 		break;
 	default:
 		break;
@@ -509,41 +515,41 @@ deactivate_async (NMDevice *self,
 /*****************************************************************************/
 
 static NMActStageReturn
-act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
+act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMActStageReturn ret;
 	NMActRequest *req;
 
-	ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage1_prepare (device, reason);
+	ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage1_prepare (device, out_failure_reason);
 	if (ret != NM_ACT_STAGE_RETURN_SUCCESS)
 		return ret;
 
 	req = nm_device_get_act_request (device);
-	g_assert (req);
+	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
 
-	return nm_modem_act_stage1_prepare (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, req, reason);
+	return nm_modem_act_stage1_prepare (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, req, out_failure_reason);
 }
 
 static NMActStageReturn
-act_stage2_config (NMDevice *device, NMDeviceStateReason *reason)
+act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason)
 {
 	NMActRequest *req;
 
 	req = nm_device_get_act_request (device);
-	g_assert (req);
+	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
 
-	return nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, req, reason);
+	return nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, req, out_failure_reason);
 }
 
 static NMActStageReturn
 act_stage3_ip4_config_start (NMDevice *device,
                              NMIP4Config **out_config,
-                             NMDeviceStateReason *reason)
+                             NMDeviceStateReason *out_failure_reason)
 {
 	return nm_modem_stage3_ip4_config_start (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem,
 	                                         device,
 	                                         NM_DEVICE_CLASS (nm_device_modem_parent_class),
-	                                         reason);
+	                                         out_failure_reason);
 }
 
 static void
@@ -555,11 +561,11 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config)
 static NMActStageReturn
 act_stage3_ip6_config_start (NMDevice *device,
                              NMIP6Config **out_config,
-                             NMDeviceStateReason *reason)
+                             NMDeviceStateReason *out_failure_reason)
 {
 	return nm_modem_stage3_ip6_config_start (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem,
 	                                         nm_device_get_act_request (device),
-	                                         reason);
+	                                         out_failure_reason);
 }
 
 static gboolean
@@ -800,6 +806,7 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
 	device_class->owns_iface = owns_iface;
 	device_class->is_available = is_available;
 	device_class->get_ip_iface_identifier = get_ip_iface_identifier;
+	device_class->get_configured_mtu = nm_modem_get_configured_mtu;
 
 	device_class->state_changed = device_state_changed;
 
diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c
index 7dfd38c5..4b16fb14 100644
--- a/src/devices/wwan/nm-modem-broadband.c
+++ b/src/devices/wwan/nm-modem-broadband.c
@@ -119,7 +119,10 @@ G_DEFINE_TYPE (NMModemBroadband, nm_modem_broadband, NM_TYPE_MODEM)
             char __prefix_name[128]; \
             const char *__uid; \
             \
-            _nm_log (_level, (_NMLOG_DOMAIN), 0, \
+            _nm_log (_level, (_NMLOG_DOMAIN), 0, NULL, \
+                     ((__self && __self->_priv.ctx) \
+                         ? nm_connection_get_uuid (__self->_priv.ctx->connection) \
+                         : NULL), \
                      "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      _NMLOG_PREFIX_NAME, \
                      (__self \
@@ -407,7 +410,7 @@ connect_ready (MMModemSimple *simple_iface,
 	if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN &&
 	    ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) {
 		_LOGW ("failed to connect modem: invalid bearer IP configuration");
-		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+		nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
 		connect_context_clear (self);
 		return;
 	}
@@ -439,11 +442,10 @@ send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self)
 	if (error) {
 		if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) ||
 		    (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) &&
-		     mm_modem_get_unlock_required (self->_priv.modem_iface) == MM_MODEM_LOCK_SIM_PIN)) {
+		     mm_modem_get_unlock_required (self->_priv.modem_iface) == MM_MODEM_LOCK_SIM_PIN))
 			ask_for_pin (self);
-		} else {
-			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (self, error));
-		}
+		else
+			nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, translate_mm_error (self, error));
 		return;
 	}
 
@@ -506,7 +508,7 @@ connect_context_step (NMModemBroadband *self)
 			_LOGW ("failed to connect '%s': not a mobile broadband modem",
 			       nm_connection_get_id (ctx->connection));
 
-			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
+			nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
 			connect_context_clear (self);
 			break;
 		}
@@ -520,7 +522,7 @@ connect_context_step (NMModemBroadband *self)
 			       error->message);
 			g_clear_error (&error);
 
-			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
+			nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
 			connect_context_clear (self);
 			break;
 		}
@@ -559,9 +561,9 @@ connect_context_step (NMModemBroadband *self)
 		/* fall through */
 
 	case CONNECT_STEP_LAST:
-		if (self->_priv.ipv4_config || self->_priv.ipv6_config) {
-			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE);
-		} else {
+		if (self->_priv.ipv4_config || self->_priv.ipv6_config)
+			nm_modem_emit_prepare_result (NM_MODEM (self), TRUE, NM_DEVICE_STATE_REASON_NONE);
+		else {
 			/* If we have a saved error from a previous attempt, use it */
 			if (!ctx->first_error)
 				ctx->first_error = g_error_new_literal (NM_DEVICE_ERROR,
@@ -570,7 +572,7 @@ connect_context_step (NMModemBroadband *self)
 
 			_LOGW ("failed to connect modem: %s",
 			       ctx->first_error->message);
-			g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (self, ctx->first_error));
+			nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, translate_mm_error (self, ctx->first_error));
 		}
 
 		connect_context_clear (self);
@@ -581,7 +583,7 @@ connect_context_step (NMModemBroadband *self)
 static NMActStageReturn
 act_stage1_prepare (NMModem *_self,
                     NMConnection *connection,
-                    NMDeviceStateReason *reason)
+                    NMDeviceStateReason *out_failure_reason)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
 
@@ -590,7 +592,7 @@ act_stage1_prepare (NMModem *_self,
 		self->_priv.simple_iface = mm_object_get_modem_simple (self->_priv.modem_object);
 		if (!self->_priv.simple_iface) {
 			_LOGW ("cannot access the Simple mobile broadband modem interface");
-			*reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED;
+			NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED);
 			return NM_ACT_STAGE_RETURN_FAILURE;
 		}
 	}
@@ -944,7 +946,7 @@ out:
 static NMActStageReturn
 static_stage3_ip4_config_start (NMModem *_self,
                                 NMActRequest *req,
-                                NMDeviceStateReason *reason)
+                                NMDeviceStateReason *out_failure_reason)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
 
@@ -1050,7 +1052,7 @@ out:
 }
 
 static NMActStageReturn
-stage3_ip6_config_request (NMModem *_self, NMDeviceStateReason *reason)
+stage3_ip6_config_request (NMModem *_self, NMDeviceStateReason *out_failure_reason)
 {
 	NMModemBroadband *self = NM_MODEM_BROADBAND (_self);
 
diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c
index 41d422aa..52b335c7 100644
--- a/src/devices/wwan/nm-modem-ofono.c
+++ b/src/devices/wwan/nm-modem-ofono.c
@@ -83,7 +83,7 @@ G_DEFINE_TYPE (NMModemOfono, nm_modem_ofono, NM_TYPE_MODEM)
             char __prefix_name[128]; \
             const char *__uid; \
             \
-            _nm_log (_level, (_NMLOG_DOMAIN), 0, \
+            _nm_log (_level, (_NMLOG_DOMAIN), 0, NULL, NULL, \
                      "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      _NMLOG_PREFIX_NAME, \
                      (__self \
@@ -724,8 +724,8 @@ stage1_prepare_done (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data
 	if (error) {
 		_LOGW ("connection failed: %s", error->message);
 
-		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE,
-		                       NM_DEVICE_STATE_REASON_MODEM_BUSY);
+		nm_modem_emit_prepare_result (NM_MODEM (self), FALSE,
+		                              NM_DEVICE_STATE_REASON_MODEM_BUSY);
 		/*
 		 * FIXME: add code to check for InProgress so that the
 		 * connection doesn't continue to try and activate,
@@ -745,7 +745,6 @@ context_property_changed (GDBusProxy *proxy,
 {
 	NMModemOfono *self = NM_MODEM_OFONO (user_data);
 	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
-	NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
 	NMPlatformIP4Address addr;
 	gboolean ret = FALSE;
 	GVariant *v_dict;
@@ -910,39 +909,40 @@ context_property_changed (GDBusProxy *proxy,
 out:
 	if (nm_modem_get_state (NM_MODEM (self)) != NM_MODEM_STATE_CONNECTED) {
 		_LOGI ("emitting PREPARE_RESULT: %s", ret ? "TRUE" : "FALSE");
-		if (!ret)
-			reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
-		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, ret, reason);
+		nm_modem_emit_prepare_result (NM_MODEM (self), ret,
+		                              ret
+		                                  ? NM_DEVICE_STATE_REASON_NONE
+		                                  : NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 	} else {
 		_LOGW ("MODEM_PPP_FAILED");
-		g_signal_emit_by_name (self, NM_MODEM_PPP_FAILED, NM_DEVICE_STATE_REASON_PPP_FAILED);
+		nm_modem_emit_ppp_failed (NM_MODEM (self), NM_DEVICE_STATE_REASON_PPP_FAILED);
 	}
 }
 
 static NMActStageReturn
 static_stage3_ip4_config_start (NMModem *modem,
                                 NMActRequest *req,
-                                NMDeviceStateReason *reason)
+                                NMDeviceStateReason *out_failure_reason)
 {
 	NMModemOfono *self = NM_MODEM_OFONO (modem);
 	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
-	NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
 	GError *error = NULL;
 
-	if (priv->ip4_config) {
-		_LOGD ("IP4 config is done; setting modem_state -> CONNECTED");
-		g_signal_emit_by_name (self, NM_MODEM_IP4_CONFIG_RESULT, priv->ip4_config, error);
+	if (!priv->ip4_config) {
+		_LOGD ("IP4 config not ready(?)");
+		return NM_ACT_STAGE_RETURN_FAILURE;
+	}
 
-		/* Signal listener takes ownership of the IP4Config */
-		priv->ip4_config = NULL;
+	_LOGD ("IP4 config is done; setting modem_state -> CONNECTED");
+	g_signal_emit_by_name (self, NM_MODEM_IP4_CONFIG_RESULT, priv->ip4_config, error);
 
-		nm_modem_set_state (NM_MODEM (self),
-		                    NM_MODEM_STATE_CONNECTED,
-		                    nm_modem_state_to_string (NM_MODEM_STATE_CONNECTED));
-		ret = NM_ACT_STAGE_RETURN_POSTPONE;
-	}
+	/* Signal listener takes ownership of the IP4Config */
+	priv->ip4_config = NULL;
 
-	return ret;
+	nm_modem_set_state (NM_MODEM (self),
+	                    NM_MODEM_STATE_CONNECTED,
+	                    nm_modem_state_to_string (NM_MODEM_STATE_CONNECTED));
+	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
 static void
@@ -955,14 +955,14 @@ context_proxy_new_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_dat
 	priv->context_proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
 	if (error) {
 		_LOGE ("failed to create ofono ConnectionContext DBus proxy: %s", error->message);
-		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE,
-		                       NM_DEVICE_STATE_REASON_MODEM_BUSY);
+		nm_modem_emit_prepare_result (NM_MODEM (self), FALSE,
+		                              NM_DEVICE_STATE_REASON_MODEM_BUSY);
 		return;
 	}
 
 	if (!priv->gprs_attached) {
-		g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE,
-		                       NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
+		nm_modem_emit_prepare_result (NM_MODEM (self), FALSE,
+		                              NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
 		return;
 	}
 
@@ -1038,7 +1038,7 @@ create_connect_properties (NMConnection *connection)
 static NMActStageReturn
 act_stage1_prepare (NMModem *modem,
                     NMConnection *connection,
-                    NMDeviceStateReason *reason)
+                    NMDeviceStateReason *out_failure_reason)
 {
 	NMModemOfono *self = NM_MODEM_OFONO (modem);
 	NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self);
@@ -1047,7 +1047,7 @@ act_stage1_prepare (NMModem *modem,
 
 	context_id = nm_connection_get_id (connection);
 	id = g_strsplit (context_id, "/", 0);
-	g_assert (id[2]);
+	g_return_val_if_fail (id[2], NM_ACT_STAGE_RETURN_FAILURE);
 
 	_LOGD ("trying %s %s", id[1], id[2]);
 
@@ -1058,8 +1058,8 @@ act_stage1_prepare (NMModem *modem,
 	g_strfreev (id);
 
 	if (!priv->context_path) {
-		*reason = NM_DEVICE_STATE_REASON_GSM_APN_FAILED;
-			return NM_ACT_STAGE_RETURN_FAILURE;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_GSM_APN_FAILED);
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	if (priv->connect_properties)
@@ -1073,7 +1073,7 @@ act_stage1_prepare (NMModem *modem,
 		do_context_activate (self);
 	} else {
 		_LOGW ("could not activate context: modem is not registered.");
-		*reason = NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_MODEM_NO_CARRIER);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 1dec0dd1..6494b849 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -33,6 +33,7 @@
 #include "NetworkManagerUtils.h"
 #include "devices/nm-device-private.h"
 #include "nm-route-manager.h"
+#include "nm-netns.h"
 #include "nm-act-request.h"
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
@@ -158,7 +159,7 @@ nm_modem_set_state (NMModem *self,
 
 		priv->state = new_state;
 		_notify (self, PROP_STATE);
-		g_signal_emit (self, signals[STATE_CHANGED], 0, (int) new_state, (int) old_state, reason);
+		g_signal_emit (self, signals[STATE_CHANGED], 0, (int) new_state, (int) old_state);
 	}
 }
 
@@ -201,7 +202,7 @@ nm_modem_set_mm_enabled (NMModem *self,
 
 		/* Try to unlock the modem if it's being enabled */
 		if (enabled)
-			g_signal_emit_by_name (self, NM_MODEM_AUTH_REQUESTED, 0);
+			g_signal_emit (self, signals[AUTH_REQUESTED], 0);
 		return;
 	}
 
@@ -222,6 +223,22 @@ nm_modem_emit_removed (NMModem *self)
 	g_signal_emit (self, signals[REMOVED], 0);
 }
 
+void
+nm_modem_emit_prepare_result (NMModem *self, gboolean success, NMDeviceStateReason reason)
+{
+	nm_assert (NM_IS_MODEM (self));
+
+	g_signal_emit (self, signals[PREPARE_RESULT], 0, success, (guint) reason);
+}
+
+void
+nm_modem_emit_ppp_failed (NMModem *self, NMDeviceStateReason reason)
+{
+	nm_assert (NM_IS_MODEM (self));
+
+	g_signal_emit (self, signals[PPP_FAILED], 0, (guint) reason);
+}
+
 NMModemIPType
 nm_modem_get_supported_ip_types (NMModem *self)
 {
@@ -382,10 +399,10 @@ ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_
 {
 	switch (status) {
 	case NM_PPP_STATUS_DISCONNECT:
-		g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_DISCONNECT);
+		nm_modem_emit_ppp_failed (user_data, NM_DEVICE_STATE_REASON_PPP_DISCONNECT);
 		break;
 	case NM_PPP_STATUS_DEAD:
-		g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_FAILED);
+		nm_modem_emit_ppp_failed (user_data, NM_DEVICE_STATE_REASON_PPP_FAILED);
 		break;
 	default:
 		break;
@@ -479,18 +496,19 @@ ppp_ip6_config (NMPPPManager *ppp_manager,
 
 static void
 ppp_stats (NMPPPManager *ppp_manager,
-           guint32 in_bytes,
-           guint32 out_bytes,
+           guint i_in_bytes,
+           guint i_out_bytes,
            gpointer user_data)
 {
 	NMModem *self = NM_MODEM (user_data);
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
+	guint32 in_bytes = i_in_bytes;
+	guint32 out_bytes = i_out_bytes;
 
 	if (priv->in_bytes != in_bytes || priv->out_bytes != out_bytes) {
 		priv->in_bytes = in_bytes;
 		priv->out_bytes = out_bytes;
-
-		g_signal_emit (self, signals[PPP_STATS], 0, in_bytes, out_bytes);
+		g_signal_emit (self, signals[PPP_STATS], 0, (guint) in_bytes, (guint) out_bytes);
 	}
 }
 
@@ -514,18 +532,16 @@ port_speed_is_zero (const char *port)
 static NMActStageReturn
 ppp_stage3_ip_config_start (NMModem *self,
                             NMActRequest *req,
-                            NMDeviceStateReason *reason)
+                            NMDeviceStateReason *out_failure_reason)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 	const char *ppp_name = NULL;
 	GError *error = NULL;
-	NMActStageReturn ret;
 	guint ip_timeout = 30;
 	guint baud_override = 0;
 
 	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (reason !=	NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
 	/* If we're already running PPP don't restart it; for example, if both
 	 * IPv4 and IPv6 are requested, IPv4 gets started first, but we use the
@@ -560,24 +576,10 @@ ppp_stage3_ip_config_start (NMModem *self,
 		baud_override = 57600;
 
 	priv->ppp_manager = nm_ppp_manager_create (priv->data_port, &error);
-	if (   priv->ppp_manager
-	    && nm_ppp_manager_start (priv->ppp_manager, req, ppp_name,
-	                             ip_timeout, baud_override, &error)) {
-		g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
-		                  G_CALLBACK (ppp_state_changed),
-		                  self);
-		g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
-		                  G_CALLBACK (ppp_ip4_config),
-		                  self);
-		g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP6_CONFIG,
-		                  G_CALLBACK (ppp_ip6_config),
-		                  self);
-		g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATS,
-		                  G_CALLBACK (ppp_stats),
-		                  self);
-
-		ret = NM_ACT_STAGE_RETURN_POSTPONE;
-	} else {
+
+	if (   !priv->ppp_manager
+	    || !nm_ppp_manager_start (priv->ppp_manager, req, ppp_name,
+	                              ip_timeout, baud_override, &error)) {
 		nm_log_err (LOGD_PPP, "(%s): error starting PPP: %s",
 		            nm_modem_get_uid (self),
 		            error->message);
@@ -585,11 +587,24 @@ ppp_stage3_ip_config_start (NMModem *self,
 
 		g_clear_object (&priv->ppp_manager);
 
-		*reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED;
-		ret = NM_ACT_STAGE_RETURN_FAILURE;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_PPP_START_FAILED);
+		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
-	return ret;
+	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATE_CHANGED,
+	                  G_CALLBACK (ppp_state_changed),
+	                  self);
+	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP4_CONFIG,
+	                  G_CALLBACK (ppp_ip4_config),
+	                  self);
+	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_IP6_CONFIG,
+	                  G_CALLBACK (ppp_ip6_config),
+	                  self);
+	g_signal_connect (priv->ppp_manager, NM_PPP_MANAGER_SIGNAL_STATS,
+	                  G_CALLBACK (ppp_stats),
+	                  self);
+
+	return NM_ACT_STAGE_RETURN_POSTPONE;
 }
 
 /*****************************************************************************/
@@ -598,7 +613,7 @@ NMActStageReturn
 nm_modem_stage3_ip4_config_start (NMModem *self,
                                   NMDevice *device,
                                   NMDeviceClass *device_class,
-                                  NMDeviceStateReason *reason)
+                                  NMDeviceStateReason *out_failure_reason)
 {
 	NMModemPrivate *priv;
 	NMActRequest *req;
@@ -611,12 +626,13 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
 	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (NM_IS_DEVICE_CLASS (device_class), NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
 	req = nm_device_get_act_request (device);
-	g_assert (req);
+	g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE);
+
 	connection = nm_act_request_get_applied_connection (req);
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
 
 	/* Only Disabled and Auto methods make sense for WWAN */
@@ -627,22 +643,22 @@ nm_modem_stage3_ip4_config_start (NMModem *self,
 		nm_log_warn (LOGD_MB | LOGD_IP4,
 		             "(%s): unhandled WWAN IPv4 method '%s'; will fail",
 		             nm_modem_get_uid (self), method);
-		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	priv = NM_MODEM_GET_PRIVATE (self);
 	switch (priv->ip4_method) {
 	case NM_MODEM_IP_METHOD_PPP:
-		ret = ppp_stage3_ip_config_start (self, req, reason);
+		ret = ppp_stage3_ip_config_start (self, req, out_failure_reason);
 		break;
 	case NM_MODEM_IP_METHOD_STATIC:
 		nm_log_dbg (LOGD_MB, "MODEM_IP_METHOD_STATIC");
-		ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, reason);
+		ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, out_failure_reason);
 		break;
 	case NM_MODEM_IP_METHOD_AUTO:
 		nm_log_dbg (LOGD_MB, "MODEM_IP_METHOD_AUTO");
-		ret = device_class->act_stage3_ip4_config_start (device, NULL, reason);
+		ret = device_class->act_stage3_ip4_config_start (device, NULL, out_failure_reason);
 		break;
 	default:
 		nm_log_info (LOGD_MB, "(%s): IPv4 configuration disabled", nm_modem_get_uid (self));
@@ -670,7 +686,7 @@ nm_modem_ip4_pre_commit (NMModem *modem,
 
 		g_assert (address);
 		if (address->plen == 32)
-			nm_platform_link_set_noarp (NM_PLATFORM_GET, nm_device_get_ip_ifindex (device));
+			nm_platform_link_set_noarp (nm_device_get_platform (device), nm_device_get_ip_ifindex (device));
 	}
 }
 
@@ -712,30 +728,28 @@ nm_modem_emit_ip6_config_result (NMModem *self,
 }
 
 static NMActStageReturn
-stage3_ip6_config_request (NMModem *self, NMDeviceStateReason *reason)
+stage3_ip6_config_request (NMModem *self, NMDeviceStateReason *out_failure_reason)
 {
-	*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+	NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 	return NM_ACT_STAGE_RETURN_FAILURE;
 }
 
 NMActStageReturn
 nm_modem_stage3_ip6_config_start (NMModem *self,
                                   NMActRequest *req,
-                                  NMDeviceStateReason *reason)
+                                  NMDeviceStateReason *out_failure_reason)
 {
 	NMModemPrivate *priv;
 	NMActStageReturn ret;
 	NMConnection *connection;
 	const char *method;
 
-	g_return_val_if_fail (self != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
-	g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
 
 	connection = nm_act_request_get_applied_connection (req);
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
+
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
 
 	/* Only Ignore and Auto methods make sense for WWAN */
@@ -746,14 +760,14 @@ nm_modem_stage3_ip6_config_start (NMModem *self,
 		nm_log_warn (LOGD_MB | LOGD_IP6,
 		             "(%s): unhandled WWAN IPv6 method '%s'; will fail",
 		             nm_modem_get_uid (self), method);
-		*reason = NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE;
+		NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE);
 		return NM_ACT_STAGE_RETURN_FAILURE;
 	}
 
 	priv = NM_MODEM_GET_PRIVATE (self);
 	switch (priv->ip6_method) {
 	case NM_MODEM_IP_METHOD_PPP:
-		ret = ppp_stage3_ip_config_start (self, req, reason);
+		ret = ppp_stage3_ip_config_start (self, req, out_failure_reason);
 		break;
 	case NM_MODEM_IP_METHOD_STATIC:
 	case NM_MODEM_IP_METHOD_AUTO:
@@ -761,7 +775,7 @@ nm_modem_stage3_ip6_config_start (NMModem *self,
 		 * which in the static case is the full config, and the DHCP/Auto case
 		 * is just the IPv6LL address to use for SLAAC.
 		 */
-		ret = NM_MODEM_GET_CLASS (self)->stage3_ip6_config_request (self, reason);
+		ret = NM_MODEM_GET_CLASS (self)->stage3_ip6_config_request (self, out_failure_reason);
 		break;
 	default:
 		nm_log_info (LOGD_MB, "(%s): IPv6 configuration disabled", nm_modem_get_uid (self));
@@ -772,6 +786,45 @@ nm_modem_stage3_ip6_config_start (NMModem *self,
 	return ret;
 }
 
+guint32
+nm_modem_get_configured_mtu (NMDevice *self, gboolean *out_is_user_config)
+{
+	NMConnection *connection;
+	NMSetting *setting;
+	gint64 mtu_default;
+	guint mtu = 0;
+	const char *property_name;
+
+	nm_assert (NM_IS_DEVICE (self));
+	nm_assert (out_is_user_config);
+
+	connection = nm_device_get_applied_connection (self);
+	if (!connection)
+		g_return_val_if_reached (0);
+
+	setting = (NMSetting *) nm_connection_get_setting_gsm (connection);
+	if (!setting)
+		setting = (NMSetting *) nm_connection_get_setting_cdma (connection);
+
+	if (setting) {
+		g_object_get (setting, "mtu", &mtu, NULL);
+		if (mtu) {
+			*out_is_user_config = TRUE;
+			return mtu;
+		}
+
+		property_name = NM_IS_SETTING_GSM (setting) ? "gsm.mtu" : "cdma.mtu";
+		mtu_default = nm_device_get_configured_mtu_from_connection_default (self, property_name);
+		if (mtu_default >= 0) {
+			*out_is_user_config = TRUE;
+			return (guint32) mtu_default;
+		}
+	}
+
+	*out_is_user_config = FALSE;
+	return 0;
+}
+
 /*****************************************************************************/
 
 static void
@@ -835,16 +888,16 @@ nm_modem_get_secrets (NMModem *self,
 static NMActStageReturn
 act_stage1_prepare (NMModem *modem,
                     NMConnection *connection,
-                    NMDeviceStateReason *reason)
+                    NMDeviceStateReason *out_failure_reason)
 {
-	*reason = NM_DEVICE_STATE_REASON_UNKNOWN;
+	NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_UNKNOWN);
 	return NM_ACT_STAGE_RETURN_FAILURE;
 }
 
 NMActStageReturn
 nm_modem_act_stage1_prepare (NMModem *self,
                              NMActRequest *req,
-                             NMDeviceStateReason *reason)
+                             NMDeviceStateReason *out_failure_reason)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 	gs_unref_ptrarray GPtrArray *hints = NULL;
@@ -857,13 +910,13 @@ nm_modem_act_stage1_prepare (NMModem *self,
 	priv->act_request = g_object_ref (req);
 
 	connection = nm_act_request_get_applied_connection (req);
-	g_assert (connection);
+	g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE);
 
 	setting_name = nm_connection_need_secrets (connection, &hints);
 	if (!setting_name) {
 		/* Ready to connect */
 		g_assert (!hints);
-		return NM_MODEM_GET_CLASS (self)->act_stage1_prepare (self, connection, reason);
+		return NM_MODEM_GET_CLASS (self)->act_stage1_prepare (self, connection, out_failure_reason);
 	}
 
 	/* Secrets required... */
@@ -887,7 +940,7 @@ nm_modem_act_stage1_prepare (NMModem *self,
 NMActStageReturn
 nm_modem_act_stage2_config (NMModem *self,
                             NMActRequest *req,
-                            NMDeviceStateReason *reason)
+                            NMDeviceStateReason *out_failure_reason)
 {
 	NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
 
@@ -1016,9 +1069,10 @@ deactivate_cleanup (NMModem *self, NMDevice *device)
 		    priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) {
 			ifindex = nm_device_get_ip_ifindex (device);
 			if (ifindex > 0) {
-				nm_route_manager_route_flush (nm_route_manager_get (), ifindex);
-				nm_platform_address_flush (NM_PLATFORM_GET, ifindex);
-				nm_platform_link_set_down (NM_PLATFORM_GET, ifindex);
+				nm_route_manager_route_flush (nm_netns_get_route_manager (nm_device_get_netns (device)),
+				                              ifindex);
+				nm_platform_address_flush (nm_device_get_platform (device), ifindex);
+				nm_platform_link_set_down (nm_device_get_platform (device), ifindex);
 			}
 		}
 	}
@@ -1205,8 +1259,7 @@ nm_modem_deactivate (NMModem *self, NMDevice *device)
 void
 nm_modem_device_state_changed (NMModem *self,
                                NMDeviceState new_state,
-                               NMDeviceState old_state,
-                               NMDeviceStateReason reason)
+                               NMDeviceState old_state)
 {
 	gboolean was_connected = FALSE, warn = TRUE;
 	NMModemPrivate *priv;
@@ -1396,15 +1449,15 @@ set_property (GObject *object, guint prop_id,
 
 	switch (prop_id) {
 	case PROP_PATH:
-		/* Construct only */
+		/* construct-only */
 		priv->path = g_value_dup_string (value);
 		break;
 	case PROP_DRIVER:
-		/* Construct only */
+		/* construct-only */
 		priv->driver = g_value_dup_string (value);
 		break;
 	case PROP_CONTROL_PORT:
-		/* Construct only */
+		/* construct-only */
 		priv->control_port = g_value_dup_string (value);
 		break;
 	case PROP_DATA_PORT:
@@ -1412,7 +1465,7 @@ set_property (GObject *object, guint prop_id,
 		priv->data_port = g_value_dup_string (value);
 		break;
 	case PROP_UID:
-		/* Construct only */
+		/* construct-only */
 		priv->uid = g_value_dup_string (value);
 		break;
 	case PROP_IP4_METHOD:
@@ -1628,15 +1681,16 @@ nm_modem_class_init (NMModemClass *klass)
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	signals[PPP_STATS] =
-	    g_signal_new ("ppp-stats",
+	    g_signal_new (NM_MODEM_PPP_STATS,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0, NULL, NULL, NULL,
 	                  G_TYPE_NONE, 2,
-	                  G_TYPE_UINT, G_TYPE_UINT);
+	                  G_TYPE_UINT /*guint32 in_bytes*/,
+	                  G_TYPE_UINT /*guint32 out_bytes*/);
 
 	signals[PPP_FAILED] =
-	    g_signal_new ("ppp-failed",
+	    g_signal_new (NM_MODEM_PPP_FAILED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0, NULL, NULL, NULL,
diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h
index c590b9e4..a50727a9 100644
--- a/src/devices/wwan/nm-modem.h
+++ b/src/devices/wwan/nm-modem.h
@@ -132,17 +132,17 @@ typedef struct {
 
 	NMActStageReturn (*act_stage1_prepare)     (NMModem *modem,
 	                                            NMConnection *connection,
-	                                            NMDeviceStateReason *reason);
+	                                            NMDeviceStateReason *out_failure_reason);
 
 	NMActStageReturn (*static_stage3_ip4_config_start) (NMModem *self,
 	                                                    NMActRequest *req,
-	                                                    NMDeviceStateReason *reason);
+	                                                    NMDeviceStateReason *out_failure_reason);
 
 	/* Request the IP6 config; when the config returns the modem
 	 * subclass should emit the ip6_config_result signal.
 	 */
 	NMActStageReturn (*stage3_ip6_config_request) (NMModem *self,
-	                                               NMDeviceStateReason *reason);
+	                                               NMDeviceStateReason *out_failure_reason);
 
 	void (*set_mm_enabled)                     (NMModem *self, gboolean enabled);
 
@@ -187,20 +187,20 @@ gboolean nm_modem_complete_connection (NMModem *self,
 
 NMActStageReturn nm_modem_act_stage1_prepare (NMModem *modem,
                                               NMActRequest *req,
-                                              NMDeviceStateReason *reason);
+                                              NMDeviceStateReason *out_failure_reason);
 
 NMActStageReturn nm_modem_act_stage2_config (NMModem *modem,
                                              NMActRequest *req,
-                                             NMDeviceStateReason *reason);
+                                             NMDeviceStateReason *out_failure_reason);
 
 NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *modem,
                                                    NMDevice *device,
                                                    NMDeviceClass *device_class,
-                                                   NMDeviceStateReason *reason);
+                                                   NMDeviceStateReason *out_failure_reason);
 
 NMActStageReturn nm_modem_stage3_ip6_config_start (NMModem *modem,
                                                    NMActRequest *req,
-                                                   NMDeviceStateReason *reason);
+                                                   NMDeviceStateReason *out_failure_reason);
 
 void nm_modem_ip4_pre_commit (NMModem *modem, NMDevice *device, NMIP4Config *config);
 
@@ -222,8 +222,7 @@ gboolean nm_modem_deactivate_async_finish (NMModem *self,
 
 void nm_modem_device_state_changed (NMModem *modem,
                                     NMDeviceState new_state,
-                                    NMDeviceState old_state,
-                                    NMDeviceStateReason reason);
+                                    NMDeviceState old_state);
 
 void          nm_modem_set_mm_enabled (NMModem *self, gboolean enabled);
 
@@ -239,6 +238,10 @@ NMModemIPType nm_modem_get_supported_ip_types (NMModem *self);
 /* For the modem-manager only */
 void          nm_modem_emit_removed (NMModem *self);
 
+void          nm_modem_emit_prepare_result (NMModem *self, gboolean success, NMDeviceStateReason reason);
+
+void          nm_modem_emit_ppp_failed (NMModem *self, NMDeviceStateReason reason);
+
 GArray       *nm_modem_get_connection_ip_type (NMModem *self,
                                                NMConnection *connection,
                                                GError **error);
@@ -250,4 +253,6 @@ void nm_modem_emit_ip6_config_result (NMModem *self,
 
 const gchar *nm_modem_ip_type_to_string (NMModemIPType ip_type);
 
+guint32 nm_modem_get_configured_mtu (NMDevice *self, gboolean *out_is_user_config);
+
 #endif /* __NETWORKMANAGER_MODEM_H__ */
diff --git a/src/dhcp/nm-dhcp-client-logging.h b/src/dhcp/nm-dhcp-client-logging.h
index 8dd18bf2..1047a7d7 100644
--- a/src/dhcp/nm-dhcp-client-logging.h
+++ b/src/dhcp/nm-dhcp-client-logging.h
@@ -42,7 +42,7 @@
                                             ? LOGD_DHCP \
                                             : (nm_dhcp_client_get_ipv6 (_self) ? LOGD_DHCP6 : LOGD_DHCP4); \
             \
-            nm_log (_level, _domain, \
+            nm_log (_level, _domain, __ifname, NULL, \
                     "%s%s%s%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                     _NMLOG_PREFIX_NAME, \
                     (_domain == LOGD_DHCP4 ? "4" : (_domain == LOGD_DHCP6 ? "6" : "")), \
@@ -65,7 +65,7 @@
         if (nm_logging_enabled (_level, _domain)) { \
             const char *__ifname = (ifname); \
             \
-            nm_log (_level, _domain, \
+            nm_log (_level, _domain, __ifname, NULL, \
                     "%s%s%s%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                     _NMLOG_PREFIX_NAME, \
                     (_domain == LOGD_DHCP4 ? "4" : (_domain == LOGD_DHCP6 ? "6" : "")), \
diff --git a/src/dhcp/nm-dhcp-client.c b/src/dhcp/nm-dhcp-client.c
index ba7c6dbf..0906f5be 100644
--- a/src/dhcp/nm-dhcp-client.c
+++ b/src/dhcp/nm-dhcp-client.c
@@ -68,7 +68,7 @@ typedef struct _NMDhcpClientPrivate {
 	GByteArray * duid;
 	GBytes *     client_id;
 	char *       hostname;
-	char *       fqdn;
+	gboolean     use_fqdn;
 
 	NMDhcpState  state;
 	pid_t        pid;
@@ -147,6 +147,14 @@ nm_dhcp_client_get_priority (NMDhcpClient *self)
 	return NM_DHCP_CLIENT_GET_PRIVATE (self)->priority;
 }
 
+guint32
+nm_dhcp_client_get_timeout (NMDhcpClient *self)
+{
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), 0);
+
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->timeout;
+}
+
 GBytes *
 nm_dhcp_client_get_client_id (NMDhcpClient *self)
 {
@@ -178,12 +186,12 @@ nm_dhcp_client_get_hostname (NMDhcpClient *self)
 	return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname;
 }
 
-const char *
-nm_dhcp_client_get_fqdn (NMDhcpClient *self)
+gboolean
+nm_dhcp_client_get_use_fqdn (NMDhcpClient *self)
 {
-	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+	g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
 
-	return NM_DHCP_CLIENT_GET_PRIVATE (self)->fqdn;
+	return NM_DHCP_CLIENT_GET_PRIVATE (self)->use_fqdn;
 }
 
 /*****************************************************************************/
@@ -298,7 +306,6 @@ nm_dhcp_client_set_state (NMDhcpClient *self,
 		g_assert (   (priv->ipv6 && NM_IS_IP6_CONFIG (ip_config))
 		          || (!priv->ipv6 && NM_IS_IP4_CONFIG (ip_config)));
 		g_assert (options);
-		g_assert_cmpint (g_hash_table_size (options), >, 0);
 	} else {
 		g_assert (ip_config == NULL);
 		g_assert (options == NULL);
@@ -409,7 +416,7 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self,
                           const char *dhcp_client_id,
                           const char *dhcp_anycast_addr,
                           const char *hostname,
-                          const char *fqdn,
+                          gboolean use_fqdn,
                           const char *last_ip4_address)
 {
 	NMDhcpClientPrivate *priv;
@@ -430,8 +437,7 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self,
 
 	g_clear_pointer (&priv->hostname, g_free);
 	priv->hostname = g_strdup (hostname);
-	g_free (priv->fqdn);
-	priv->fqdn = g_strdup (fqdn);
+	priv->use_fqdn = use_fqdn;
 
 	return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address);
 }
@@ -571,7 +577,7 @@ nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name)
 	if (start_time == 0)
 		goto out;
 
-	nm_sprintf_buf (proc_path, "/proc/%lu/cmdline", (long unsigned) pid);
+	nm_sprintf_buf (proc_path, "/proc/%lu/cmdline", (unsigned long) pid);
 	if (!g_file_get_contents (proc_path, &proc_contents, NULL, NULL))
 		goto out;
 
@@ -904,7 +910,6 @@ dispose (GObject *object)
 
 	g_clear_pointer (&priv->iface, g_free);
 	g_clear_pointer (&priv->hostname, g_free);
-	g_clear_pointer (&priv->fqdn, g_free);
 	g_clear_pointer (&priv->uuid, g_free);
 	g_clear_pointer (&priv->client_id, g_bytes_unref);
 
diff --git a/src/dhcp/nm-dhcp-client.h b/src/dhcp/nm-dhcp-client.h
index 7a083ae7..e41a59a2 100644
--- a/src/dhcp/nm-dhcp-client.h
+++ b/src/dhcp/nm-dhcp-client.h
@@ -19,10 +19,10 @@
 #ifndef __NETWORKMANAGER_DHCP_CLIENT_H__
 #define __NETWORKMANAGER_DHCP_CLIENT_H__
 
-#include <nm-setting-ip4-config.h>
-#include <nm-setting-ip6-config.h>
-#include <nm-ip4-config.h>
-#include <nm-ip6-config.h>
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
 
 #define NM_TYPE_DHCP_CLIENT            (nm_dhcp_client_get_type ())
 #define NM_DHCP_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_CLIENT, NMDhcpClient))
@@ -117,17 +117,19 @@ const GByteArray *nm_dhcp_client_get_hw_addr (NMDhcpClient *self);
 
 guint32 nm_dhcp_client_get_priority (NMDhcpClient *self);
 
+guint32 nm_dhcp_client_get_timeout (NMDhcpClient *self);
+
 GBytes *nm_dhcp_client_get_client_id (NMDhcpClient *self);
 
 const char *nm_dhcp_client_get_hostname (NMDhcpClient *self);
 
-const char *nm_dhcp_client_get_fqdn (NMDhcpClient *self);
+gboolean nm_dhcp_client_get_use_fqdn (NMDhcpClient *self);
 
 gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self,
                                    const char *dhcp_client_id,
                                    const char *dhcp_anycast_addr,
                                    const char *hostname,
-                                   const char *fqdn,
+                                   gboolean use_fqdn,
                                    const char *last_ip4_address);
 
 gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self,
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.c b/src/dhcp/nm-dhcp-dhclient-utils.c
index f36451b2..216319b3 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp/nm-dhcp-dhclient-utils.c
@@ -93,29 +93,21 @@ grab_request_options (GPtrArray *store, const char* line)
 
 
 static void
-add_hostname4 (GString *str, const char *hostname, const char *fqdn)
+add_hostname4 (GString *str, const char *hostname, gboolean use_fqdn)
 {
-	char *plain_hostname, *dot;
-
-	if (fqdn) {
-		g_string_append_printf (str, FQDN_FORMAT "\n", fqdn);
-		g_string_append (str,
-		                 "send fqdn.encoded on;\n"
-		                 "send fqdn.server-update on;\n");
-	} else if (hostname) {
-		plain_hostname = g_strdup (hostname);
-		dot = strchr (plain_hostname, '.');
-		/* get rid of the domain */
-		if (dot)
-			*dot = '\0';
-
-		g_string_append_printf (str, HOSTNAME4_FORMAT "\n", plain_hostname);
-		g_free (plain_hostname);
+	if (hostname) {
+		if (use_fqdn) {
+			g_string_append_printf (str, FQDN_FORMAT "\n", hostname);
+			g_string_append (str,
+			                 "send fqdn.encoded on;\n"
+			                 "send fqdn.server-update on;\n");
+		} else
+			g_string_append_printf (str, HOSTNAME4_FORMAT "\n", hostname);
 	}
 }
 
 static void
-add_ip4_config (GString *str, GBytes *client_id, const char *hostname, const char *fqdn)
+add_ip4_config (GString *str, GBytes *client_id, const char *hostname, gboolean use_fqdn)
 {
 	if (client_id) {
 		const char *p;
@@ -150,7 +142,7 @@ add_ip4_config (GString *str, GBytes *client_id, const char *hostname, const cha
 		g_string_append (str, "; # added by NetworkManager\n");
 	}
 
-	add_hostname4 (str, hostname, fqdn);
+	add_hostname4 (str, hostname, use_fqdn);
 
 	g_string_append_c (str, '\n');
 
@@ -232,13 +224,46 @@ nm_dhcp_dhclient_get_client_id_from_config_file (const char *path)
 	return NULL;
 }
 
+static gboolean
+read_interface (const char *line, char *interface, guint size)
+{
+	gs_free char *dup = g_strdup (line + NM_STRLEN ("interface"));
+	char *ptr = dup, *end;
+
+	while (g_ascii_isspace (*ptr))
+		ptr++;
+
+	if (*ptr == '"') {
+		ptr++;
+		end = strchr (ptr, '"');
+		if (!end)
+			return FALSE;
+		*end = '\0';
+	} else {
+		end = strchr (ptr, ' ');
+		if (!end)
+			end = strchr (ptr, '{');
+		if (!end)
+			return FALSE;
+		*end = '\0';
+	}
+
+	if (   ptr[0] == '\0'
+	    || strlen (ptr) + 1 > size)
+		return FALSE;
+
+	snprintf (interface, size, "%s", ptr);
+
+	return TRUE;
+}
+
 char *
 nm_dhcp_dhclient_create_config (const char *interface,
                                 gboolean is_ip6,
                                 GBytes *client_id,
                                 const char *anycast_addr,
                                 const char *hostname,
-                                const char *fqdn,
+                                gboolean use_fqdn,
                                 const char *orig_path,
                                 const char *orig_contents,
                                 GBytes **out_new_client_id)
@@ -258,8 +283,10 @@ nm_dhcp_dhclient_create_config (const char *interface,
 		char **lines, **line;
 		gboolean in_alsoreq = FALSE;
 		gboolean in_req = FALSE;
+		char intf[IFNAMSIZ];
 
 		g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig_path);
+		intf[0] = '\0';
 
 		lines = g_strsplit_set (orig_contents, "\n\r", 0);
 		for (line = lines; lines && *line; line++) {
@@ -268,6 +295,20 @@ nm_dhcp_dhclient_create_config (const char *interface,
 			if (!strlen (g_strstrip (p)))
 				continue;
 
+			if (   !intf[0]
+			    && g_str_has_prefix (p, "interface")) {
+				if (read_interface (p, intf, sizeof (intf)))
+					continue;
+			}
+
+			if (intf[0] && strchr (p, '}')) {
+				intf[0] = '\0';
+				continue;
+			}
+
+			if (intf[0] && !nm_streq (intf, interface))
+				continue;
+
 			if (!strncmp (p, CLIENTID_TAG, strlen (CLIENTID_TAG))) {
 				/* Override config file "dhcp-client-id" and use one from the connection */
 				if (client_id)
@@ -279,7 +320,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
 			}
 
 			/* Override config file hostname and use one from the connection */
-			if (hostname || fqdn) {
+			if (hostname) {
 				if (strncmp (p, HOSTNAME4_TAG, strlen (HOSTNAME4_TAG)) == 0)
 					continue;
 				if (strncmp (p, FQDN_TAG, strlen (FQDN_TAG)) == 0)
@@ -339,7 +380,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
 		add_request (reqs, "dhcp6.domain-search");
 		add_request (reqs, "dhcp6.client-id");
 	} else {
-		add_ip4_config (new_contents, client_id, hostname, fqdn);
+		add_ip4_config (new_contents, client_id, hostname, use_fqdn);
 		add_request (reqs, "rfc3442-classless-static-routes");
 		add_request (reqs, "ms-classless-static-routes");
 		add_request (reqs, "static-routes");
diff --git a/src/dhcp/nm-dhcp-dhclient-utils.h b/src/dhcp/nm-dhcp-dhclient-utils.h
index 83d5a23d..994b1b9f 100644
--- a/src/dhcp/nm-dhcp-dhclient-utils.h
+++ b/src/dhcp/nm-dhcp-dhclient-utils.h
@@ -19,15 +19,15 @@
 #ifndef __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__
 #define __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__
 
-#include <nm-setting-ip4-config.h>
-#include <nm-setting-ip6-config.h>
+#include "nm-setting-ip4-config.h"
+#include "nm-setting-ip6-config.h"
 
 char *nm_dhcp_dhclient_create_config (const char *interface,
                                       gboolean is_ip6,
                                       GBytes *client_id,
                                       const char *anycast_addr,
                                       const char *hostname,
-                                      const char *fqdn,
+                                      gboolean use_fqdn,
                                       const char *orig_path,
                                       const char *orig_contents,
                                       GBytes **out_new_client_id);
diff --git a/src/dhcp/nm-dhcp-dhclient.c b/src/dhcp/nm-dhcp-dhclient.c
index 64d93744..a56e5a3c 100644
--- a/src/dhcp/nm-dhcp-dhclient.c
+++ b/src/dhcp/nm-dhcp-dhclient.c
@@ -182,7 +182,7 @@ merge_dhclient_config (NMDhcpDhclient *self,
                        GBytes *client_id,
                        const char *anycast_addr,
                        const char *hostname,
-                       const char *fqdn,
+                       gboolean use_fqdn,
                        const char *orig_path,
                        GBytes **out_new_client_id,
                        GError **error)
@@ -206,7 +206,7 @@ merge_dhclient_config (NMDhcpDhclient *self,
 	if (is_ip6 && hostname && !strchr (hostname, '.'))
 		_LOGW ("hostname is not a FQDN, it will be ignored");
 
-	new = nm_dhcp_dhclient_create_config (iface, is_ip6, client_id, anycast_addr, hostname, fqdn, orig_path, orig, out_new_client_id);
+	new = nm_dhcp_dhclient_create_config (iface, is_ip6, client_id, anycast_addr, hostname, use_fqdn, orig_path, orig, out_new_client_id);
 	g_assert (new);
 	success = g_file_set_contents (conf_file, new, -1, error);
 	g_free (new);
@@ -294,7 +294,7 @@ create_dhclient_config (NMDhcpDhclient *self,
                         GBytes *client_id,
                         const char *dhcp_anycast_addr,
                         const char *hostname,
-                        const char *fqdn,
+                        gboolean use_fqdn,
                         GBytes **out_new_client_id)
 {
 	char *orig = NULL, *new = NULL;
@@ -314,7 +314,7 @@ create_dhclient_config (NMDhcpDhclient *self,
 
 	error = NULL;
 	success = merge_dhclient_config (self, iface, new, is_ip6, client_id, dhcp_anycast_addr,
-			                         hostname, fqdn, orig, out_new_client_id, &error);
+			                         hostname, use_fqdn, orig, out_new_client_id, &error);
 	if (!success) {
 		_LOGW ("error creating dhclient configuration: %s", error->message);
 		g_error_free (error);
@@ -342,6 +342,8 @@ dhclient_start (NMDhcpClient *client,
 	char *binary_name, *cmd_str, *pid_file = NULL, *system_bus_address_env = NULL;
 	gboolean ipv6, success;
 	char *escaped, *preferred_leasefile_path = NULL;
+	guint32 timeout;
+	char timeout_str[64];
 
 	g_return_val_if_fail (priv->pid_file == NULL, FALSE);
 
@@ -444,6 +446,17 @@ dhclient_start (NMDhcpClient *client,
 		g_ptr_array_add (argv, (gpointer) priv->conf_file);
 	}
 
+	/* Specify a timeout longer than configuration's one,
+	 * so that dhclient doesn't send back a FAIL event before
+	 * that time.
+	 */
+	timeout = nm_dhcp_client_get_timeout (client);
+	if (timeout >= 60) {
+		timeout = timeout < G_MAXINT32 ? timeout + 1 : G_MAXINT32;
+		g_ptr_array_add (argv, (gpointer) "-timeout");
+		g_ptr_array_add (argv, (gpointer) nm_sprintf_buf (timeout_str, "%u", (unsigned) timeout));
+	}
+
 	/* Usually the system bus address is well-known; but if it's supposed
 	 * to be something else, we need to push it to dhclient, since dhclient
 	 * sanitizes the environment it gives the action scripts.
@@ -492,17 +505,18 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (self);
 	GBytes *client_id;
 	gs_unref_bytes GBytes *new_client_id = NULL;
-	const char *iface, *uuid, *hostname, *fqdn;
+	const char *iface, *uuid, *hostname;
 	gboolean success = FALSE;
+	gboolean use_fqdn;
 
 	iface = nm_dhcp_client_get_iface (client);
 	uuid = nm_dhcp_client_get_uuid (client);
 	client_id = nm_dhcp_client_get_client_id (client);
 	hostname = nm_dhcp_client_get_hostname (client);
-	fqdn = nm_dhcp_client_get_fqdn (client);
+	use_fqdn = nm_dhcp_client_get_use_fqdn (client);
 
 	priv->conf_file = create_dhclient_config (self, iface, FALSE, uuid, client_id, dhcp_anycast_addr,
-	                                          hostname, fqdn, &new_client_id);
+	                                          hostname, use_fqdn, &new_client_id);
 	if (priv->conf_file) {
 		if (new_client_id)
 			nm_dhcp_client_set_client_id (client, new_client_id);
@@ -530,7 +544,7 @@ ip6_start (NMDhcpClient *client,
 	uuid = nm_dhcp_client_get_uuid (client);
 	hostname = nm_dhcp_client_get_hostname (client);
 
-	priv->conf_file = create_dhclient_config (self, iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname, NULL, NULL);
+	priv->conf_file = create_dhclient_config (self, iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname, TRUE, NULL);
 	if (!priv->conf_file) {
 		_LOGW ("error creating dhclient configuration file");
 		return FALSE;
diff --git a/src/dhcp/nm-dhcp-dhcpcd.c b/src/dhcp/nm-dhcp-dhcpcd.c
index c8643881..66a31acf 100644
--- a/src/dhcp/nm-dhcp-dhcpcd.c
+++ b/src/dhcp/nm-dhcp-dhcpcd.c
@@ -88,9 +88,8 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	GPtrArray *argv = NULL;
 	pid_t pid = -1;
 	GError *error = NULL;
-	char *pid_contents = NULL, *binary_name, *cmd_str, *dot;
-	const char *iface, *dhcpcd_path, *hostname, *fqdn;
-	gs_free char *prefix = NULL;
+	char *pid_contents = NULL, *binary_name, *cmd_str;
+	const char *iface, *dhcpcd_path, *hostname;
 
 	g_return_val_if_fail (priv->pid_file == NULL, FALSE);
 
@@ -138,22 +137,17 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 #endif
 
 	hostname = nm_dhcp_client_get_hostname (client);
-	fqdn = nm_dhcp_client_get_fqdn (client);
-
-	if (fqdn) {
-		g_ptr_array_add (argv, (gpointer) "-h");
-		g_ptr_array_add (argv, (gpointer) fqdn);
-		g_ptr_array_add (argv, (gpointer) "-F");
-		g_ptr_array_add (argv, (gpointer) "both");
-	} else if (hostname) {
-		prefix = strdup (hostname);
-		dot = strchr (prefix, '.');
-		/* get rid of the domain */
-		if (dot)
-			*dot = '\0';
-
-		g_ptr_array_add (argv, (gpointer) "-h");	/* Send hostname to DHCP server */
-		g_ptr_array_add (argv, (gpointer) prefix);
+
+	if (hostname) {
+		if (nm_dhcp_client_get_use_fqdn (client)) {
+			g_ptr_array_add (argv, (gpointer) "-h");
+			g_ptr_array_add (argv, (gpointer) hostname);
+			g_ptr_array_add (argv, (gpointer) "-F");
+			g_ptr_array_add (argv, (gpointer) "both");
+		} else {
+			g_ptr_array_add (argv, (gpointer) "-h");
+			g_ptr_array_add (argv, (gpointer) hostname);
+		}
 	}
 
 	g_ptr_array_add (argv, (gpointer) iface);
diff --git a/src/dhcp/nm-dhcp-listener.c b/src/dhcp/nm-dhcp-listener.c
index 56bd9d17..ca697ab3 100644
--- a/src/dhcp/nm-dhcp-listener.c
+++ b/src/dhcp/nm-dhcp-listener.c
@@ -92,7 +92,7 @@ NM_DEFINE_SINGLETON_GETTER (NMDhcpListener, nm_dhcp_listener_get, NM_TYPE_DHCP_L
         const NMDhcpListener *_self = (self); \
         char _prefix[64]; \
         \
-        nm_log ((level), (_NMLOG_DOMAIN), \
+        nm_log ((level), (_NMLOG_DOMAIN), NULL, NULL, \
                 "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                 (_self != singleton_instance \
                     ? nm_sprintf_buf (_prefix, "%s[%p]", _NMLOG_PREFIX_NAME, _self) \
diff --git a/src/dhcp/nm-dhcp-manager.c b/src/dhcp/nm-dhcp-manager.c
index 9c1fbb38..fff9f9ec 100644
--- a/src/dhcp/nm-dhcp-manager.c
+++ b/src/dhcp/nm-dhcp-manager.c
@@ -163,7 +163,7 @@ client_start (NMDhcpManager *self,
               guint32 timeout,
               const char *dhcp_anycast_addr,
               const char *hostname,
-              const char *fqdn,
+              gboolean hostname_use_fqdn,
               gboolean info_only,
               NMSettingIP6ConfigPrivacy privacy,
               const char *last_ip4_address,
@@ -209,7 +209,7 @@ client_start (NMDhcpManager *self,
 	if (ipv6)
 		success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, ipv6_ll_addr, hostname, info_only, privacy, needed_prefixes);
 	else
-		success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, fqdn, last_ip4_address);
+		success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, hostname_use_fqdn, last_ip4_address);
 
 	if (!success) {
 		remove_client (self, client);
@@ -219,15 +219,6 @@ client_start (NMDhcpManager *self,
 	return client;
 }
 
-static const char *
-get_send_hostname (NMDhcpManager *self, const char *setting_hostname)
-{
-	NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
-
-	/* Always prefer the explicit dhcp-send-hostname if given */
-	return setting_hostname ? setting_hostname : priv->default_hostname;
-}
-
 /* Caller owns a reference to the NMDhcpClient on return */
 NMDhcpClient *
 nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
@@ -244,18 +235,41 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
                            const char *dhcp_anycast_addr,
                            const char *last_ip_address)
 {
+	NMDhcpManagerPrivate *priv;
 	const char *hostname = NULL;
-	const char *fqdn = NULL;
+	gs_free char *hostname_tmp = NULL;
+	gboolean use_fqdn = FALSE;
+	char *dot;
 
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
 
 	if (send_hostname) {
-		hostname = get_send_hostname (self, dhcp_hostname);
-		fqdn = dhcp_fqdn;
+		/* Use, in order of preference:
+		 *  1. FQDN from configuration
+		 *  2. hostname from configuration
+		 *  3. system hostname (only host part)
+		 */
+		if (dhcp_fqdn) {
+			hostname = dhcp_fqdn;
+			use_fqdn = TRUE;
+		} else if (dhcp_hostname)
+			hostname = dhcp_hostname;
+		else {
+			hostname = priv->default_hostname;
+			if (hostname) {
+				hostname_tmp = g_strdup (hostname);
+				dot = strchr (hostname_tmp, '.');
+				if (dot)
+					*dot = '\0';
+				hostname = hostname_tmp;
+			}
+		}
 	}
+
 	return client_start (self, iface, ifindex, hwaddr, uuid, priority, FALSE, NULL,
 	                     dhcp_client_id, timeout, dhcp_anycast_addr, hostname,
-	                     fqdn, FALSE, 0, last_ip_address, 0);
+	                     use_fqdn, FALSE, 0, last_ip_address, 0);
 }
 
 /* Caller owns a reference to the NMDhcpClient on return */
@@ -275,14 +289,18 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
                            NMSettingIP6ConfigPrivacy privacy,
                            guint needed_prefixes)
 {
+	NMDhcpManagerPrivate *priv;
 	const char *hostname = NULL;
 
 	g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
+	priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
 
-	if (send_hostname)
-		hostname = get_send_hostname (self, dhcp_hostname);
+	if (send_hostname) {
+		/* Always prefer the explicit dhcp-hostname if given */
+		hostname = dhcp_hostname ? dhcp_hostname : priv->default_hostname;
+	}
 	return client_start (self, iface, ifindex, hwaddr, uuid, priority, TRUE,
-	                     ll_addr, NULL, timeout, dhcp_anycast_addr, hostname, NULL, info_only,
+	                     ll_addr, NULL, timeout, dhcp_anycast_addr, hostname, TRUE, info_only,
 	                     privacy, NULL, needed_prefixes);
 }
 
diff --git a/src/dhcp/nm-dhcp-systemd.c b/src/dhcp/nm-dhcp-systemd.c
index 7067275b..aa902701 100644
--- a/src/dhcp/nm-dhcp-systemd.c
+++ b/src/dhcp/nm-dhcp-systemd.c
@@ -580,7 +580,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 	const uint8_t *client_id = NULL;
 	size_t client_id_len = 0;
 	struct in_addr last_addr = { 0 };
-	const char *hostname, *fqdn;
+	const char *hostname;
 	int r, i;
 	gboolean success = FALSE;
 	guint16 arp_type;
@@ -687,28 +687,13 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last
 
 	hostname = nm_dhcp_client_get_hostname (client);
 	if (hostname) {
-		char *prefix, *dot;
-
-		prefix = strdup (hostname);
-		dot = strchr (prefix, '.');
-		/* get rid of the domain */
-		if (dot)
-			*dot = '\0';
-
-		r = sd_dhcp_client_set_hostname (priv->client4, prefix);
-		free (prefix);
-
-		if (r < 0) {
-			_LOGW ("failed to set DHCP hostname (%d)", r);
-			goto error;
-		}
-	}
-
-	fqdn = nm_dhcp_client_get_fqdn (client);
-	if (fqdn) {
-		r = sd_dhcp_client_set_hostname (priv->client4, fqdn);
+		/* FIXME: sd-dhcp decides which hostname/FQDN option to send (12 or 81)
+		 * only based on whether the hostname has a domain part or not. At the
+		 * moment there is no way to force one or another.
+		 */
+		r = sd_dhcp_client_set_hostname (priv->client4, hostname);
 		if (r < 0) {
-			_LOGW ("failed to set DHCP FQDN (%d)", r);
+			_LOGW ("failed to set DHCP hostname to '%s' (%d)", hostname, r);
 			goto error;
 		}
 	}
diff --git a/src/dhcp/nm-dhcp-utils.h b/src/dhcp/nm-dhcp-utils.h
index b45c5e89..05982b16 100644
--- a/src/dhcp/nm-dhcp-utils.h
+++ b/src/dhcp/nm-dhcp-utils.h
@@ -21,8 +21,8 @@
 
 #include <stdlib.h>
 
-#include <nm-ip4-config.h>
-#include <nm-ip6-config.h>
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
 
 NMIP4Config *nm_dhcp_utils_ip4_config_from_options (int ifindex,
                                                     const char *iface,
diff --git a/src/dhcp/tests/test-dhcp-dhclient.c b/src/dhcp/tests/test-dhcp-dhclient.c
index f4cf9c9f..40a3e072 100644
--- a/src/dhcp/tests/test-dhcp-dhclient.c
+++ b/src/dhcp/tests/test-dhcp-dhclient.c
@@ -40,7 +40,7 @@ test_config (const char *orig,
              const char *expected,
              gboolean ipv6,
              const char *hostname,
-             const char *fqdn,
+             gboolean use_fqdn,
              const char *dhcp_client_id,
              GBytes *expected_new_client_id,
              const char *iface,
@@ -60,7 +60,7 @@ test_config (const char *orig,
 	                                      client_id,
 	                                      anycast_addr,
 	                                      hostname,
-	                                      fqdn,
+	                                      use_fqdn,
 	                                      "/path/to/dhclient.conf",
 	                                      orig,
 	                                      &new_client_id);
@@ -105,7 +105,7 @@ static const char *orig_missing_expected = \
 static void
 test_orig_missing (void)
 {
-	test_config (NULL, orig_missing_expected, FALSE, NULL, NULL, NULL, NULL, "eth0", NULL);
+	test_config (NULL, orig_missing_expected, FALSE, NULL, FALSE, NULL, NULL, "eth0", NULL);
 }
 
 /*****************************************************************************/
@@ -134,7 +134,7 @@ static void
 test_override_client_id (void)
 {
 	test_config (override_client_id_orig, override_client_id_expected,
-	             FALSE, NULL, NULL,
+	             FALSE, NULL, FALSE,
 	             "11:22:33:44:55:66",
 	             NULL,
 	             "eth0",
@@ -163,7 +163,7 @@ static void
 test_quote_client_id (void)
 {
 	test_config (NULL, quote_client_id_expected,
-	             FALSE, NULL, NULL,
+	             FALSE, NULL, FALSE,
 	             "1234",
 	             NULL,
 	             "eth0",
@@ -192,7 +192,7 @@ static void
 test_ascii_client_id (void)
 {
 	test_config (NULL, ascii_client_id_expected,
-	             FALSE, NULL, NULL,
+	             FALSE, NULL, FALSE,
 	             "qb:cd:ef:12:34:56",
 	             NULL,
 	             "eth0",
@@ -221,7 +221,7 @@ static void
 test_hex_single_client_id (void)
 {
 	test_config (NULL, hex_single_client_id_expected,
-	             FALSE, NULL, NULL,
+	             FALSE, NULL, FALSE,
 	             "ab:cd:e:12:34:56",
 	             NULL,
 	             "eth0",
@@ -258,7 +258,7 @@ test_existing_hex_client_id (void)
 
 	new_client_id = g_bytes_new (bytes, sizeof (bytes));
 	test_config (existing_hex_client_id_orig, existing_hex_client_id_expected,
-	             FALSE, NULL, NULL,
+	             FALSE, NULL, FALSE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -298,7 +298,7 @@ test_existing_ascii_client_id (void)
 	memcpy (buf + 1, EACID, NM_STRLEN (EACID));
 	new_client_id = g_bytes_new (buf, sizeof (buf));
 	test_config (existing_ascii_client_id_orig, existing_ascii_client_id_expected,
-	             FALSE, NULL, NULL,
+	             FALSE, NULL, FALSE,
 	             NULL,
 	             new_client_id,
 	             "eth0",
@@ -327,8 +327,8 @@ static void
 test_fqdn (void)
 {
 	test_config (NULL, fqdn_expected,
-	             FALSE, NULL,
-	             "foo.bar.com", NULL,
+	             FALSE, "foo.bar.com",
+	             TRUE, NULL,
 	             NULL,
 	             "eth0",
 	             NULL);
@@ -367,8 +367,8 @@ test_fqdn_options_override (void)
 {
 	test_config (fqdn_options_override_orig,
 	             fqdn_options_override_expected,
-	             FALSE, NULL,
-	             "example2.com", NULL,
+	             FALSE, "example2.com",
+	             TRUE, NULL,
 	             NULL,
 	             "eth0",
 	             NULL);
@@ -400,7 +400,7 @@ static void
 test_override_hostname (void)
 {
 	test_config (override_hostname_orig, override_hostname_expected,
-	             FALSE, "blahblah", NULL,
+	             FALSE, "blahblah", FALSE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -429,7 +429,7 @@ static void
 test_override_hostname6 (void)
 {
 	test_config (override_hostname6_orig, override_hostname6_expected,
-	             TRUE, "blahblah.local", NULL,
+	             TRUE, "blahblah.local", TRUE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -452,7 +452,7 @@ test_nonfqdn_hostname6 (void)
 	/* Non-FQDN hostname can't be used with dhclient */
 	test_config (NULL, nonfqdn_hostname6_expected,
 	             TRUE, "blahblah",
-	             NULL, NULL,
+	             TRUE, NULL,
 	             NULL,
 	             "eth0",
 	             NULL);
@@ -487,7 +487,7 @@ test_existing_alsoreq (void)
 {
 	test_config (existing_alsoreq_orig, existing_alsoreq_expected,
 	             FALSE, NULL,
-	             NULL,
+	             FALSE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -526,7 +526,7 @@ test_existing_req (void)
 {
 	test_config (existing_req_orig, existing_req_expected,
 	             FALSE, NULL,
-	             NULL,
+	             FALSE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -565,7 +565,7 @@ static void
 test_existing_multiline_alsoreq (void)
 {
 	test_config (existing_multiline_alsoreq_orig, existing_multiline_alsoreq_expected,
-	             FALSE, NULL, NULL,
+	             FALSE, NULL, FALSE,
 	             NULL,
 	             NULL,
 	             "eth0",
@@ -744,6 +744,95 @@ test_write_existing_commented_duid (void)
 
 /*****************************************************************************/
 
+static const char *interface1_orig = \
+	"interface \"eth0\" {\n"
+	"	also request my-option;\n"
+	"	initial-delay 5;\n"
+	"}\n"
+	"interface \"eth1\" {\n"
+	"	also request another-option;\n"
+	"	initial-delay 0;\n"
+	"}\n"
+	"\n"
+	"also request yet-another-option;\n";
+
+static const char *interface1_expected = \
+	"# Created by NetworkManager\n"
+	"# Merged from /path/to/dhclient.conf\n"
+	"\n"
+	"initial-delay 5;\n"
+	"\n"
+	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
+	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
+	"option wpad code 252 = string;\n"
+	"\n"
+	"also request my-option;\n"
+	"also request yet-another-option;\n"
+	"also request rfc3442-classless-static-routes;\n"
+	"also request ms-classless-static-routes;\n"
+	"also request static-routes;\n"
+	"also request wpad;\n"
+	"also request ntp-servers;\n"
+	"\n";
+
+static void
+test_interface1 (void)
+{
+	test_config (interface1_orig, interface1_expected,
+	             FALSE, NULL, FALSE,
+	             NULL,
+	             NULL,
+	             "eth0",
+	             NULL);
+}
+
+/*****************************************************************************/
+
+static const char *interface2_orig = \
+	"interface eth0 {\n"
+	"	also request my-option;\n"
+	"	initial-delay 5;\n"
+	" }\n"
+	"interface eth1 {\n"
+	"	initial-delay 0;\n"
+	"	request another-option;\n"
+	" } \n"
+	"\n"
+	"also request yet-another-option;\n";
+
+static const char *interface2_expected = \
+	"# Created by NetworkManager\n"
+	"# Merged from /path/to/dhclient.conf\n"
+	"\n"
+	"initial-delay 0;\n"
+	"\n"
+	"option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
+	"option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
+	"option wpad code 252 = string;\n"
+	"\n"
+	"request; # override dhclient defaults\n"
+	"also request another-option;\n"
+	"also request yet-another-option;\n"
+	"also request rfc3442-classless-static-routes;\n"
+	"also request ms-classless-static-routes;\n"
+	"also request static-routes;\n"
+	"also request wpad;\n"
+	"also request ntp-servers;\n"
+	"\n";
+
+static void
+test_interface2 (void)
+{
+	test_config (interface2_orig, interface2_expected,
+	             FALSE, NULL, FALSE,
+	             NULL,
+	             NULL,
+	             "eth1",
+	             NULL);
+}
+
+/*****************************************************************************/
+
 static void
 test_read_lease_ip4_config_basic (void)
 {
@@ -891,6 +980,8 @@ main (int argc, char **argv)
 	g_test_add_func ("/dhcp/dhclient/existing_alsoreq", test_existing_alsoreq);
 	g_test_add_func ("/dhcp/dhclient/existing_multiline_alsoreq", test_existing_multiline_alsoreq);
 	g_test_add_func ("/dhcp/dhclient/duids", test_duids);
+	g_test_add_func ("/dhcp/dhclient/interface/1", test_interface1);
+	g_test_add_func ("/dhcp/dhclient/interface/2", test_interface2);
 
 	g_test_add_func ("/dhcp/dhclient/read_duid_from_leasefile", test_read_duid_from_leasefile);
 	g_test_add_func ("/dhcp/dhclient/read_commented_duid_from_leasefile", test_read_commented_duid_from_leasefile);
diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c
index 55e04180..1f7eb964 100644
--- a/src/dns/nm-dns-manager.c
+++ b/src/dns/nm-dns-manager.c
@@ -16,7 +16,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com>
- * Copyright (C) 2004 - 2013 Red Hat, Inc.
+ * Copyright (C) 2004 - 2017 Red Hat, Inc.
  * Copyright (C) 2005 - 2008 Novell, Inc.
  *   and others
  */
@@ -35,6 +35,10 @@
 
 #include <linux/fs.h>
 
+#if WITH_LIBPSL
+#include <libpsl.h>
+#endif
+
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 #include "nm-dns-manager.h"
@@ -52,20 +56,6 @@
 
 #include "introspection/org.freedesktop.NetworkManager.DnsManager.h"
 
-#if WITH_LIBSOUP
-#include <libsoup/soup.h>
-
-#ifdef SOUP_CHECK_VERSION
-#if SOUP_CHECK_VERSION (2, 40, 0)
-#define DOMAIN_IS_VALID(domain) (*(domain) && !soup_tld_domain_is_public_suffix (domain))
-#endif
-#endif
-#endif
-
-#ifndef DOMAIN_IS_VALID
-#define DOMAIN_IS_VALID(domain) (*(domain))
-#endif
-
 #define HASH_LEN 20
 
 #ifndef RESOLVCONF_PATH
@@ -114,7 +104,7 @@ NM_DEFINE_SINGLETON_GETTER (NMDnsManager, nm_dns_manager_get, NM_TYPE_DNS_MANAGE
             char __prefix[20]; \
             const NMDnsManager *const __self = (self); \
             \
-            _nm_log (__level, _NMLOG_DOMAIN, 0, \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \
                      "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      _NMLOG_PREFIX_NAME, \
                      ((!__self || __self == singleton_instance) \
@@ -130,7 +120,10 @@ typedef struct {
 	GPtrArray *configs;
 	GVariant *config_variant;
 	NMDnsIPConfigData *best_conf4, *best_conf6;
-	gboolean need_sort;
+
+	bool need_sort:1;
+	bool dns_touched:1;
+	bool is_stopped:1;
 
 	char *hostname;
 	guint updates_queue;
@@ -144,8 +137,6 @@ typedef struct {
 
 	NMConfig *config;
 
-	gboolean dns_touched;
-
 	struct {
 		guint64 ts;
 		guint num_restarts;
@@ -166,6 +157,18 @@ G_DEFINE_TYPE (NMDnsManager, nm_dns_manager, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_DNS_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMDnsManager, NM_IS_DNS_MANAGER)
 
+static gboolean
+domain_is_valid (const gchar *domain)
+{
+	if (*domain == '\0')
+		return FALSE;
+#if WITH_LIBPSL
+	if (psl_is_public_suffix (psl_builtin (), domain))
+		return FALSE;
+#endif
+	return TRUE;
+}
+
 /*****************************************************************************/
 
 typedef struct {
@@ -305,7 +308,7 @@ merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src)
 		const char *search;
 
 		search = nm_ip4_config_get_search (src, i);
-		if (!DOMAIN_IS_VALID (search))
+		if (!domain_is_valid (search))
 			continue;
 		add_string_item (rc->searches, search);
 	}
@@ -315,7 +318,7 @@ merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src)
 			const char *domain;
 
 			domain = nm_ip4_config_get_domain (src, i);
-			if (!DOMAIN_IS_VALID (domain))
+			if (!domain_is_valid (domain))
 				continue;
 			add_string_item (rc->searches, domain);
 		}
@@ -375,7 +378,7 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src, const char *iface)
 		const char *search;
 
 		search = nm_ip6_config_get_search (src, i);
-		if (!DOMAIN_IS_VALID (search))
+		if (!domain_is_valid (search))
 			continue;
 		add_string_item (rc->searches, search);
 	}
@@ -385,7 +388,7 @@ merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src, const char *iface)
 			const char *domain;
 
 			domain = nm_ip6_config_get_domain (src, i);
-			if (!DOMAIN_IS_VALID (domain))
+			if (!domain_is_valid (domain))
 				continue;
 			add_string_item (rc->searches, domain);
 		}
@@ -606,6 +609,8 @@ dispatch_resolvconf (NMDnsManager *self,
 	FILE *f;
 	gboolean success = FALSE;
 	int errnosv, err;
+	char *argv[] = { RESOLVCONF_PATH, "-d", "NetworkManager", NULL };
+	int status;
 
 	if (!g_file_test (RESOLVCONF_PATH, G_FILE_TEST_IS_EXECUTABLE)) {
 		g_set_error_literal (error,
@@ -618,9 +623,17 @@ dispatch_resolvconf (NMDnsManager *self,
 	if (!searches && !nameservers) {
 		_LOGI ("Removing DNS information from %s", RESOLVCONF_PATH);
 
-		cmd = g_strconcat (RESOLVCONF_PATH, " -d ", "NetworkManager", NULL);
-		if (nm_spawn_process (cmd, error) != 0)
+		if (!g_spawn_sync ("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, error))
+			return SR_ERROR;
+
+		if (status != 0) {
+			g_set_error (error,
+			             NM_MANAGER_ERROR,
+			             NM_MANAGER_ERROR_FAILED,
+			             "%s returned error code",
+			             RESOLVCONF_PATH);
 			return SR_ERROR;
+		}
 
 		return SR_SUCCESS;
 	}
@@ -657,6 +670,20 @@ dispatch_resolvconf (NMDnsManager *self,
 	return success ? SR_SUCCESS : SR_ERROR;
 }
 
+static const char *
+_read_link_cached (const char *path, gboolean *is_cached, char **cached)
+{
+	nm_assert (is_cached);
+	nm_assert (cached);
+
+	if (*is_cached)
+		return *cached;
+
+	nm_assert (!*cached);
+	*is_cached = TRUE;
+	return (*cached = g_file_read_link (path, NULL));
+}
+
 #define MY_RESOLV_CONF NMRUNDIR "/resolv.conf"
 #define MY_RESOLV_CONF_TMP MY_RESOLV_CONF ".tmp"
 #define RESOLV_CONF_TMP "/etc/.resolv.conf.NetworkManager"
@@ -670,13 +697,14 @@ update_resolv_conf (NMDnsManager *self,
                     NMDnsManagerResolvConfManager rc_manager)
 {
 	FILE *f;
-	struct stat st;
 	gboolean success;
 	gs_free char *content = NULL;
 	SpawnResult write_file_result = SR_SUCCESS;
 	int errsv;
 	const char *rc_path = _PATH_RESCONF;
 	nm_auto_free char *rc_path_real = NULL;
+	gboolean resconf_link_cached = FALSE;
+	gs_free char *resconf_link = NULL;
 
 	/* If we are not managing /etc/resolv.conf and it points to
 	 * MY_RESOLV_CONF, don't write the private DNS configuration to
@@ -686,9 +714,8 @@ update_resolv_conf (NMDnsManager *self,
 	 * This is the only situation, where we don't try to update our
 	 * internal resolv.conf file. */
 	if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED) {
-		gs_free char *path = g_file_read_link (_PATH_RESCONF, NULL);
-
-		if (g_strcmp0 (path, MY_RESOLV_CONF) == 0) {
+		if (nm_streq0 (_read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link),
+		               MY_RESOLV_CONF)) {
 			_LOGD ("update-resolv-conf: not updating " _PATH_RESCONF
 			       " since it points to " MY_RESOLV_CONF);
 			return SR_SUCCESS;
@@ -697,12 +724,16 @@ update_resolv_conf (NMDnsManager *self,
 
 	content = create_resolv_conf (searches, nameservers, options);
 
-	if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) {
+	if (   rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE
+	    || (   rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK
+	        && !_read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link))) {
 		GError *local = NULL;
 
-		rc_path_real = realpath (rc_path, NULL);
-		if (rc_path_real)
-			rc_path = rc_path_real;
+		if (rc_manager == NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE) {
+			rc_path_real = realpath (rc_path, NULL);
+			if (rc_path_real)
+				rc_path = rc_path_real;
+		}
 
 		/* we first write to /etc/resolv.conf directly. If that fails,
 		 * we still continue to write to runstatedir but remember the
@@ -777,60 +808,23 @@ update_resolv_conf (NMDnsManager *self,
 		return write_file_result;
 	}
 
-	if (rc_manager != NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK) {
+	if (   rc_manager != NM_DNS_MANAGER_RESOLV_CONF_MAN_SYMLINK
+	    || !_read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link)) {
 		_LOGT ("update-resolv-conf: write internal file %s succeeded", MY_RESOLV_CONF);
 		return SR_SUCCESS;
 	}
 
-	/* A symlink pointing to NM's own resolv.conf (MY_RESOLV_CONF) is always
-	 * overwritten to ensure that changes are indicated with inotify.  Symlinks
-	 * pointing to any other file are never overwritten.
-	 */
-	if (lstat (_PATH_RESCONF, &st) != 0) {
-		errsv = errno;
-		if (errsv != ENOENT) {
-			/* NM cannot read /etc/resolv.conf */
-			_LOGT ("update-resolv-conf: write internal file %s succeeded but lstat(%s) failed (%s)",
-			       MY_RESOLV_CONF, _PATH_RESCONF, g_strerror (errsv));
-			g_set_error (error,
-			             NM_MANAGER_ERROR,
-			             NM_MANAGER_ERROR_FAILED,
-			             "Could not lstat %s: %s",
-			             _PATH_RESCONF,
-			             g_strerror (errsv));
-			return SR_ERROR;
-		}
-	} else {
-		if (S_ISLNK (st.st_mode)) {
-			if (stat (_PATH_RESCONF, &st) != -1) {
-				gs_free char *path = g_file_read_link (_PATH_RESCONF, NULL);
-
-				if (!path || !nm_streq (path, MY_RESOLV_CONF)) {
-					/* It's not NM's symlink; do nothing */
-					_LOGT ("update-resolv-conf: write internal file %s succeeded "
-					       "but don't update %s as it points to %s",
-					       MY_RESOLV_CONF, _PATH_RESCONF, path ?: "");
-					return SR_SUCCESS;
-				}
-
-				/* resolv.conf is a symlink owned by NM and the target is accessible
-				 */
-			} else {
-				/* resolv.conf is a symlink but the target is not accessible;
-				 * some other program is probably managing resolv.conf and
-				 * NM should not touch it.
-				 */
-				_LOGT ("update-resolv-conf: write internal file %s succeeded "
-				       "but don't update %s as the symlinks points somewhere else",
-				       MY_RESOLV_CONF, _PATH_RESCONF);
-				return SR_SUCCESS;
-			}
-		}
+	if (!nm_streq0 (_read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link),
+	                MY_RESOLV_CONF)) {
+		_LOGT ("update-resolv-conf: write internal file %s succeeded (don't touch symlink %s linking to %s)",
+		       MY_RESOLV_CONF, _PATH_RESCONF,
+		       _read_link_cached (_PATH_RESCONF, &resconf_link_cached, &resconf_link));
+		return SR_SUCCESS;
 	}
 
-	/* By this point, either /etc/resolv.conf does not exist, is a regular
-	 * file, or is a symlink already owned by NM.  In all cases /etc/resolv.conf
-	 * is replaced with a symlink pointing to NM's resolv.conf in /var/run/.
+	/* By this point, /etc/resolv.conf exists and is a symlink to our internal
+	 * resolv.conf. We update the symlink so that applications get an inotify
+	 * notification.
 	 */
 	if (   unlink (RESOLV_CONF_TMP) != 0
 	    && ((errsv = errno) != ENOENT)) {
@@ -925,7 +919,7 @@ merge_global_dns_config (NMResolvConfData *rc, NMGlobalDnsConfig *global_conf)
 	options = nm_global_dns_config_get_options (global_conf);
 
 	for (i = 0; searches && searches[i]; i++) {
-		if (DOMAIN_IS_VALID (searches[i]))
+		if (domain_is_valid (searches[i]))
 			add_string_item (rc->searches, searches[i]);
 	}
 
@@ -1066,9 +1060,9 @@ _collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no o
 		if (   hostdomain
 		    && !nm_utils_ipaddr_valid (AF_UNSPEC, hostname)) {
 			hostdomain++;
-			if (DOMAIN_IS_VALID (hostdomain))
+			if (domain_is_valid (hostdomain))
 				add_string_item (rc.searches, hostdomain);
-			else if (DOMAIN_IS_VALID (hostname))
+			else if (domain_is_valid (hostname))
 				add_string_item (rc.searches, hostname);
 		}
 	}
@@ -1114,6 +1108,11 @@ update_dns (NMDnsManager *self,
 
 	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
+	if (priv->is_stopped) {
+		_LOGD ("update-dns: not updating resolv.conf (is stopped)");
+		return TRUE;
+	}
+
 	nm_clear_g_source (&priv->plugin_ratelimit.timer);
 
 	if (NM_IN_SET (priv->rc_manager, NM_DNS_MANAGER_RESOLV_CONF_MAN_UNMANAGED,
@@ -1187,6 +1186,10 @@ update_dns (NMDnsManager *self,
 		case NM_DNS_MANAGER_RESOLV_CONF_MAN_FILE:
 			result = update_resolv_conf (self, searches, nameservers, options, error, priv->rc_manager);
 			resolv_conf_updated = TRUE;
+			/* If we have ended with no nameservers avoid updating again resolv.conf
+			 * on stop, as some external changes may be applied to it in the meanwhile */
+			if (!nameservers && !options)
+				priv->dns_touched = FALSE;
 			break;
 		case NM_DNS_MANAGER_RESOLV_CONF_MAN_RESOLVCONF:
 			result = dispatch_resolvconf (self, searches, nameservers, options, error);
@@ -1437,7 +1440,8 @@ nm_dns_manager_set_initial_hostname (NMDnsManager *self,
 
 void
 nm_dns_manager_set_hostname (NMDnsManager *self,
-                             const char *hostname)
+                             const char *hostname,
+                             gboolean skip_update)
 {
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
@@ -1458,6 +1462,8 @@ nm_dns_manager_set_hostname (NMDnsManager *self,
 	g_free (priv->hostname);
 	priv->hostname = g_strdup (filtered);
 
+	if (skip_update)
+		return;
 	if (!priv->updates_queue && !update_dns (self, FALSE, &error)) {
 		_LOGW ("could not commit DNS changes: %s", error->message);
 		g_clear_error (&error);
@@ -1536,6 +1542,35 @@ nm_dns_manager_end_updates (NMDnsManager *self, const char *func)
 	memset (priv->prev_hash, 0, sizeof (priv->prev_hash));
 }
 
+void
+nm_dns_manager_stop (NMDnsManager *self)
+{
+	NMDnsManagerPrivate *priv;
+	GError *error = NULL;
+
+	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+
+	if (priv->is_stopped)
+		g_return_if_reached ();
+
+	_LOGT ("stopping...");
+
+	/* If we're quitting, leave a valid resolv.conf in place, not one
+	 * pointing to 127.0.0.1 if any plugins were active.  Thus update
+	 * DNS after disposing of all plugins.  But if we haven't done any
+	 * DNS updates yet, there's no reason to touch resolv.conf on shutdown.
+	 */
+	if (priv->dns_touched) {
+		if (!update_dns (self, TRUE, &error)) {
+			_LOGW ("could not commit DNS changes on shutdown: %s", error->message);
+			g_clear_error (&error);
+		}
+		priv->dns_touched = FALSE;
+	}
+
+	priv->is_stopped = TRUE;
+}
+
 /*****************************************************************************/
 
 static gboolean
@@ -1624,9 +1659,9 @@ _resolvconf_resolved_managed (void)
 	                          NULL, NULL);
 
 	if (info && g_file_info_get_is_symlink (info)) {
-		ret = _nm_utils_strv_find_first ((gchar **) resolved_paths,
-		                                 G_N_ELEMENTS (resolved_paths),
-		                                 g_file_info_get_symlink_target (info)) >= 0;
+		ret = nm_utils_strv_find_first ((gchar **) resolved_paths,
+		                                G_N_ELEMENTS (resolved_paths),
+		                                g_file_info_get_symlink_target (info)) >= 0;
 	}
 
 	g_clear_object(&info);
@@ -2018,23 +2053,14 @@ dispose (GObject *object)
 	NMDnsManager *self = NM_DNS_MANAGER (object);
 	NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 	NMDnsIPConfigData *data;
-	GError *error = NULL;
 	guint i;
 
 	_LOGT ("disposing");
 
-	_clear_plugin (self);
+	if (!priv->is_stopped)
+		nm_dns_manager_stop (self);
 
-	/* If we're quitting, leave a valid resolv.conf in place, not one
-	 * pointing to 127.0.0.1 if any plugins were active.  Thus update
-	 * DNS after disposing of all plugins.  But if we haven't done any
-	 * DNS updates yet, there's no reason to touch resolv.conf on shutdown.
-	 */
-	if (priv->dns_touched && !update_dns (self, TRUE, &error)) {
-		_LOGW ("could not commit DNS changes on shutdown: %s", error->message);
-		g_clear_error (&error);
-		priv->dns_touched = FALSE;
-	}
+	_clear_plugin (self);
 
 	if (priv->config) {
 		g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self);
diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h
index 2fc2031b..899e4bb8 100644
--- a/src/dns/nm-dns-manager.h
+++ b/src/dns/nm-dns-manager.h
@@ -87,7 +87,8 @@ gboolean nm_dns_manager_remove_ip6_config (NMDnsManager *self, NMIP6Config *conf
 void nm_dns_manager_set_initial_hostname (NMDnsManager *self,
                                           const char *hostname);
 void nm_dns_manager_set_hostname         (NMDnsManager *self,
-                                          const char *hostname);
+                                          const char *hostname,
+                                          gboolean skip_update);
 
 /**
  * NMDnsManagerResolvConfManager
@@ -120,4 +121,6 @@ typedef enum {
 
 gboolean nm_dns_manager_get_resolv_conf_explicit (NMDnsManager *self);
 
+void nm_dns_manager_stop (NMDnsManager *self);
+
 #endif /* __NETWORKMANAGER_DNS_MANAGER_H__ */
diff --git a/src/dns/nm-dns-plugin.c b/src/dns/nm-dns-plugin.c
index 3a3bc646..00729c93 100644
--- a/src/dns/nm-dns-plugin.c
+++ b/src/dns/nm-dns-plugin.c
@@ -63,7 +63,7 @@ G_DEFINE_TYPE_EXTENDED (NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT, G_TYPE_FLAG_A
             char __prefix[20]; \
             const NMDnsPlugin *const __self = (self); \
             \
-            _nm_log (__level, _NMLOG_DOMAIN, 0, \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \
                      "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      _NMLOG_PREFIX_NAME, \
                      (!__self \
diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c
index 325088e2..ed165618 100644
--- a/src/dns/nm-dns-systemd-resolved.c
+++ b/src/dns/nm-dns-systemd-resolved.c
@@ -118,9 +118,6 @@ add_interface_configuration (NMDnsSystemdResolved *self,
 
 	device = nm_manager_get_device_by_ifindex (nm_manager_get (), ifindex);
 
-	if (!nm_device_get_managed (device, FALSE))
-		return;
-
 	for (i = 0; i < interfaces->len; i++) {
 		InterfaceConfig *tic = &g_array_index (interfaces, InterfaceConfig, i);
 		if (ifindex == tic->ifindex) {
diff --git a/src/dns/nm-dns-unbound.c b/src/dns/nm-dns-unbound.c
index 3659beac..6af4bad8 100644
--- a/src/dns/nm-dns-unbound.c
+++ b/src/dns/nm-dns-unbound.c
@@ -43,6 +43,9 @@ update (NMDnsPlugin *plugin,
         const NMGlobalDnsConfig *global_config,
         const char *hostname)
 {
+	char *argv[] = { DNSSEC_TRIGGER_SCRIPT, "--async", "--update", NULL };
+	int status;
+
 	/* TODO: We currently call a script installed with the dnssec-trigger
 	 * package that queries all information itself. Later, the dependency
 	 * on that package will be optional and the only hard dependency will
@@ -52,7 +55,9 @@ update (NMDnsPlugin *plugin,
 	 * without calling custom scripts. The dnssec-trigger functionality
 	 * may be eventually merged into NetworkManager.
 	 */
-	return nm_spawn_process (DNSSEC_TRIGGER_SCRIPT " --async --update", NULL) == 0;
+	if (!g_spawn_sync ("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, NULL))
+		return FALSE;
+	return (status == 0);
 }
 
 static gboolean
diff --git a/src/dnsmasq/nm-dnsmasq-manager.c b/src/dnsmasq/nm-dnsmasq-manager.c
index 9b24e69e..f76c983d 100644
--- a/src/dnsmasq/nm-dnsmasq-manager.c
+++ b/src/dnsmasq/nm-dnsmasq-manager.c
@@ -152,13 +152,15 @@ create_dm_cmd_line (const char *iface,
                     GError **error)
 {
 	NMCmdLine *cmd;
-	GString *s;
+	nm_auto_free_gstring GString *s = NULL;
 	char first[INET_ADDRSTRLEN];
 	char last[INET_ADDRSTRLEN];
 	char localaddr[INET_ADDRSTRLEN];
+	char tmpaddr[INET_ADDRSTRLEN];
 	char *error_desc = NULL;
 	const char *dm_binary;
 	const NMPlatformIP4Address *listen_address;
+	guint i, n;
 
 	listen_address = nm_ip4_config_get_address (ip4_config, 0);
 	g_return_val_if_fail (listen_address, NULL);
@@ -167,6 +169,8 @@ create_dm_cmd_line (const char *iface,
 	if (!dm_binary)
 		return NULL;
 
+	s = g_string_sized_new (100);
+
 	/* Create dnsmasq command line */
 	cmd = nm_cmd_line_new ();
 	nm_cmd_line_add_string (cmd, dm_binary);
@@ -196,11 +200,11 @@ create_dm_cmd_line (const char *iface,
 	 */
 	nm_cmd_line_add_string (cmd, "--strict-order");
 
-	s = g_string_new ("--listen-address=");
 	nm_utils_inet4_ntop (listen_address->address, localaddr);
+	g_string_append (s, "--listen-address=");
 	g_string_append (s, localaddr);
 	nm_cmd_line_add_string (cmd, s->str);
-	g_string_free (s, TRUE);
+	g_string_truncate (s, 0);
 
 	if (!nm_dnsmasq_utils_get_range (listen_address, first, last, &error_desc)) {
 		g_set_error_literal (error,
@@ -213,24 +217,41 @@ create_dm_cmd_line (const char *iface,
 		return NULL;
 	}
 
-	s = g_string_new ("--dhcp-range=");
-	g_string_append_printf (s, "%s,%s,60m", first, last);
+	g_string_append_printf (s, "--dhcp-range=%s,%s,60m", first, last);
 	nm_cmd_line_add_string (cmd, s->str);
-	g_string_free (s, TRUE);
+	g_string_truncate (s, 0);
 
 	if (!nm_ip4_config_get_never_default (ip4_config)) {
-		s = g_string_new ("--dhcp-option=option:router,");
+		g_string_append (s, "--dhcp-option=option:router,");
 		g_string_append (s, localaddr);
 		nm_cmd_line_add_string (cmd, s->str);
-		g_string_free (s, TRUE);
+		g_string_truncate (s, 0);
+	}
+
+	if ((n = nm_ip4_config_get_num_nameservers (ip4_config))) {
+		g_string_append (s, "--dhcp-option=option:dns-server");
+		for (i = 0; i < n; i++) {
+			g_string_append_c (s, ',');
+			g_string_append (s, nm_utils_inet4_ntop (nm_ip4_config_get_nameserver (ip4_config, i), tmpaddr));
+		}
+		g_string_truncate (s, 0);
+	}
+
+	if ((n = nm_ip4_config_get_num_searches (ip4_config))) {
+		g_string_append (s, "--dhcp-option=option:domain-search");
+		for (i = 0; i < n; i++) {
+			g_string_append_c (s, ',');
+			g_string_append (s, nm_ip4_config_get_search (ip4_config, i));
+		}
+		g_string_truncate (s, 0);
 	}
 
 	nm_cmd_line_add_string (cmd, "--dhcp-lease-max=50");
 
-	s = g_string_new ("--pid-file=");
+	g_string_append (s, "--pid-file=");
 	g_string_append (s, pidfile);
 	nm_cmd_line_add_string (cmd, s->str);
-	g_string_free (s, TRUE);
+	g_string_truncate (s, 0);
 
 	/* dnsmasq exits if the conf dir is not present */
 	if (g_file_test (CONFDIR, G_FILE_TEST_IS_DIR))
diff --git a/src/main-utils.c b/src/main-utils.c
index 9e3aa7bd..c84f1e4c 100644
--- a/src/main-utils.c
+++ b/src/main-utils.c
@@ -149,6 +149,9 @@ nm_main_utils_ensure_rundir ()
 		exit (1);
 	}
 
+	/* NM_CONFIG_DEVICE_STATE_DIR is used to determine whether NM is restarted or not.
+	 * It is important to set NMConfigCmdLineOptions.first_start before creating
+	 * the directory. */
 	nm_assert (g_str_has_prefix (NM_CONFIG_DEVICE_STATE_DIR, NMRUNDIR"/"));
 	if (g_mkdir (NM_CONFIG_DEVICE_STATE_DIR, 0755) != 0) {
 		errsv = errno;
diff --git a/src/main.c b/src/main.c
index 20e6f30f..52f0b7c8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2004 - 2012 Red Hat, Inc.
+ * Copyright (C) 2004 - 2017 Red Hat, Inc.
  * Copyright (C) 2005 - 2008 Novell, Inc.
  */
 
@@ -49,7 +49,10 @@
 #include "nm-auth-manager.h"
 #include "nm-core-internal.h"
 #include "nm-exported-object.h"
+#include "nm-connectivity.h"
+#include "dns/nm-dns-manager.h"
 #include "systemd/nm-sd.h"
+#include "nm-netns.h"
 
 #if !defined(NM_DIST_VERSION)
 # define NM_DIST_VERSION VERSION
@@ -58,6 +61,8 @@
 #define NM_DEFAULT_PID_FILE          NMRUNDIR "/NetworkManager.pid"
 #define NM_DEFAULT_SYSTEM_STATE_FILE NMSTATEDIR "/NetworkManager.state"
 
+#define CONFIG_ATOMIC_SECTION_PREFIXES ((char **) NULL)
+
 static GMainLoop *main_loop = NULL;
 static gboolean configure_and_quit = FALSE;
 
@@ -166,7 +171,7 @@ print_config (NMConfigCmdLineOptions *config_cli)
 
 	nm_logging_setup ("OFF", "ALL", NULL, NULL);
 
-	config = nm_config_new (config_cli, NULL, &error);
+	config = nm_config_new (config_cli, CONFIG_ATOMIC_SECTION_PREFIXES, &error);
 	if (config == NULL) {
 		fprintf (stderr, _("Failed to read configuration: %s\n"), error->message);
 		return 7;
@@ -234,7 +239,11 @@ main (int argc, char *argv[])
 
 	main_loop = g_main_loop_new (NULL, FALSE);
 
-	config_cli = nm_config_cmd_line_options_new ();
+	/* we determine a first-start (contrary to a restart during the same boot)
+	 * based on the existence of NM_CONFIG_DEVICE_STATE_DIR directory. */
+	config_cli = nm_config_cmd_line_options_new (!g_file_test (NM_CONFIG_DEVICE_STATE_DIR,
+	                                                           G_FILE_TEST_IS_DIR));
+
 	do_early_setup (&argc, &argv, config_cli);
 
 	if (global_opt.g_fatal_warnings)
@@ -299,7 +308,7 @@ main (int argc, char *argv[])
 	}
 
 	/* Read the config file and CLI overrides */
-	config = nm_config_setup (config_cli, NULL, &error);
+	config = nm_config_setup (config_cli, CONFIG_ATOMIC_SECTION_PREFIXES, &error);
 	nm_config_cmd_line_options_free (config_cli);
 	config_cli = NULL;
 	if (config == NULL) {
@@ -345,14 +354,14 @@ main (int argc, char *argv[])
 	/* Set up unix signal handling - before creating threads, but after daemonizing! */
 	nm_main_utils_setup_signals (main_loop);
 
-	nm_logging_syslog_openlog (nm_config_get_is_debug (config)
-	                           ? "debug"
-	                           : nm_config_data_get_value_cached (NM_CONFIG_GET_DATA_ORIG,
-	                                                              NM_CONFIG_KEYFILE_GROUP_LOGGING,
-	                                                              NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND,
-	                                                              NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY));
+	nm_logging_syslog_openlog (nm_config_data_get_value_cached (NM_CONFIG_GET_DATA_ORIG,
+	                                                            NM_CONFIG_KEYFILE_GROUP_LOGGING,
+	                                                            NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND,
+	                                                            NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY),
+	                           nm_config_get_is_debug (config));
 
-	nm_log_info (LOGD_CORE, "NetworkManager (version " NM_DIST_VERSION ") is starting...");
+	nm_log_info (LOGD_CORE, "NetworkManager (version " NM_DIST_VERSION ") is starting... (%s)",
+	             nm_config_get_first_start (config) ? "for the first time" : "after a restart");
 
 	nm_log_info (LOGD_CORE, "Read config: %s", nm_config_data_get_config_description (nm_config_get_data (config)));
 	nm_config_data_log (nm_config_get_data (config), "CONFIG: ", "  ", NULL);
@@ -368,6 +377,11 @@ main (int argc, char *argv[])
 #endif
 	             );
 
+	/* Set up platform interaction layer */
+	nm_linux_platform_setup ();
+
+	NM_UTILS_KEEP_ALIVE (config, nm_netns_get (), "NMConfig-depends-on-NMNetns");
+
 	nm_auth_manager_setup (nm_config_data_get_value_boolean (nm_config_get_data_orig (config),
 	                                                         NM_CONFIG_KEYFILE_GROUP_MAIN,
 	                                                         NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT,
@@ -385,10 +399,9 @@ main (int argc, char *argv[])
 		}
 	}
 
-	/* Set up platform interaction layer */
-	nm_linux_platform_setup ();
-
-	NM_UTILS_KEEP_ALIVE (config, NM_PLATFORM_GET, "NMConfig-depends-on-NMPlatform");
+#if WITH_CONCHECK
+	NM_UTILS_KEEP_ALIVE (nm_manager_get (), nm_connectivity_get (), "NMManager-depends-on-NMConnectivity");
+#endif
 
 	nm_dispatcher_init ();
 
@@ -399,6 +412,8 @@ main (int argc, char *argv[])
 		goto done;
 	}
 
+	nm_platform_process_events (NM_PLATFORM_GET);
+
 	/* Make sure the loopback interface is up. If interface is down, we bring
 	 * it up and kernel will assign it link-local IPv4 and IPv6 addresses. If
 	 * it was already up, we assume is in clean state.
@@ -434,6 +449,8 @@ done:
 
 	nm_config_state_set (config, TRUE, TRUE);
 
+	nm_dns_manager_stop (nm_dns_manager_get ());
+
 	if (global_opt.pidfile && wrote_pidfile)
 		unlink (global_opt.pidfile);
 
diff --git a/src/ndisc/nm-ndisc-private.h b/src/ndisc/nm-ndisc-private.h
index 2308675a..10bcc64f 100644
--- a/src/ndisc/nm-ndisc-private.h
+++ b/src/ndisc/nm-ndisc-private.h
@@ -58,16 +58,14 @@ gboolean nm_ndisc_add_dns_domain           (NMNDisc *ndisc, const NMNDiscDNSDoma
         if (nm_logging_enabled (__level, __domain)) { \
             NMNDisc *const __self = (self); \
             char __prefix[64]; \
+            const char *__ifname = __self ? nm_ndisc_get_ifname (__self) : NULL; \
             \
-            _nm_log (__level, __domain, 0, \
+            _nm_log (__level, __domain, 0, __ifname, NULL, \
                      "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      (__self \
-                        ? ({ \
-                            const char *__ifname = nm_ndisc_get_ifname (__self); \
-                            nm_sprintf_buf (__prefix, "%s[%p,%s%s%s]", \
-                                            _NMLOG_PREFIX_NAME, __self, \
-                                            NM_PRINT_FMT_QUOTE_STRING (__ifname)); \
-                            }) \
+                        ?  nm_sprintf_buf (__prefix, "%s[%p,%s%s%s]", \
+                                           _NMLOG_PREFIX_NAME, __self, \
+                                           NM_PRINT_FMT_QUOTE_STRING (__ifname)) \
                         : _NMLOG_PREFIX_NAME) \
                      _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
diff --git a/src/nm-act-request.c b/src/nm-act-request.c
index 03964896..6d8bb065 100644
--- a/src/nm-act-request.c
+++ b/src/nm-act-request.c
@@ -397,6 +397,7 @@ device_state_changed (NMActiveConnection *active,
 {
 	NMActiveConnectionState cur_ac_state = nm_active_connection_get_state (active);
 	NMActiveConnectionState ac_state = NM_ACTIVE_CONNECTION_STATE_UNKNOWN;
+	NMActiveConnectionStateReason ac_state_reason = NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN;
 
 	/* Decide which device state changes to handle when this active connection
 	 * is not the device's current request.  Two cases here: (a) the AC is
@@ -451,6 +452,7 @@ device_state_changed (NMActiveConnection *active,
 	case NM_DEVICE_STATE_UNMANAGED:
 	case NM_DEVICE_STATE_UNAVAILABLE:
 		ac_state = NM_ACTIVE_CONNECTION_STATE_DEACTIVATED;
+		ac_state_reason = NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED;
 
 		g_signal_handlers_disconnect_by_func (device, G_CALLBACK (device_notify), active);
 		break;
@@ -464,7 +466,7 @@ device_state_changed (NMActiveConnection *active,
 		nm_active_connection_set_default6 (active, FALSE);
 	}
 
-	nm_active_connection_set_state (active, ac_state);
+	nm_active_connection_set_state (active, ac_state, ac_state_reason);
 }
 
 static void
@@ -486,7 +488,9 @@ master_failed (NMActiveConnection *self)
 	}
 
 	/* If no device, or the device wasn't active, just move to deactivated state */
-	nm_active_connection_set_state (self, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
+	nm_active_connection_set_state (self,
+	                                NM_ACTIVE_CONNECTION_STATE_DEACTIVATED,
+	                                NM_ACTIVE_CONNECTION_STATE_REASON_DEPENDENCY_FAILED);
 }
 
 /*****************************************************************************/
@@ -543,6 +547,7 @@ nm_act_request_init (NMActRequest *req)
  * @specific_object: the object path of the specific object (ie, WiFi access point,
  *    etc) that will be used to activate @connection and @device
  * @subject: the #NMAuthSubject representing the requestor of the activation
+ * @activation_type: the #NMActivationType.
  * @device: the device/interface to configure according to @connection
  *
  * Creates a new device-based activation request. If an applied connection is
@@ -555,6 +560,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection,
                     NMConnection *applied_connection,
                     const char *specific_object,
                     NMAuthSubject *subject,
+                    NMActivationType activation_type,
                     NMDevice *device)
 {
 	g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL);
@@ -567,6 +573,7 @@ nm_act_request_new (NMSettingsConnection *settings_connection,
 	                                      NM_ACTIVE_CONNECTION_INT_DEVICE, device,
 	                                      NM_ACTIVE_CONNECTION_SPECIFIC_OBJECT, specific_object,
 	                                      NM_ACTIVE_CONNECTION_INT_SUBJECT, subject,
+	                                      NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE, (int) activation_type,
 	                                      NULL);
 }
 
diff --git a/src/nm-act-request.h b/src/nm-act-request.h
index 47247f89..b8549404 100644
--- a/src/nm-act-request.h
+++ b/src/nm-act-request.h
@@ -40,6 +40,7 @@ NMActRequest *nm_act_request_new          (NMSettingsConnection *settings_connec
                                            NMConnection *applied_connection,
                                            const char *specific_object,
                                            NMAuthSubject *subject,
+                                           NMActivationType activation_type,
                                            NMDevice *device);
 
 NMSettingsConnection *nm_act_request_get_settings_connection (NMActRequest *req);
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index 068bc85e..19c0343f 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -44,20 +44,20 @@ typedef struct _NMActiveConnectionPrivate {
 
 	char *pending_activation_id;
 
-	gboolean is_default;
-	gboolean is_default6;
 	NMActiveConnectionState state;
-	gboolean state_set;
-	gboolean vpn;
+	bool is_default:1;
+	bool is_default6:1;
+	bool state_set:1;
+	bool vpn:1;
+	bool master_ready:1;
+
+	NMActivationType activation_type:3;
 
 	NMAuthSubject *subject;
 	NMActiveConnection *master;
-	gboolean master_ready;
 
 	NMActiveConnection *parent;
 
-	gboolean assumed;
-
 	NMAuthChain *chain;
 	const char *wifi_shared_permission;
 	NMActiveConnectionAuthResultFunc result_func;
@@ -88,12 +88,14 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMActiveConnection,
 	PROP_INT_SUBJECT,
 	PROP_INT_MASTER,
 	PROP_INT_MASTER_READY,
+	PROP_INT_ACTIVATION_TYPE,
 );
 
 enum {
 	DEVICE_CHANGED,
 	DEVICE_METERED_CHANGED,
 	PARENT_ACTIVE,
+	STATE_CHANGED,
 	LAST_SIGNAL
 };
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -102,8 +104,15 @@ G_DEFINE_ABSTRACT_TYPE (NMActiveConnection, nm_active_connection, NM_TYPE_EXPORT
 
 #define NM_ACTIVE_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMActiveConnection, NM_IS_ACTIVE_CONNECTION)
 
+/*****************************************************************************/
+
 static void check_master_ready (NMActiveConnection *self);
 static void _device_cleanup (NMActiveConnection *self);
+static void _settings_connection_notify_flags (NMSettingsConnection *settings_connection,
+                                               GParamSpec *param,
+                                               NMActiveConnection *self);
+static void _set_activation_type (NMActiveConnection *self,
+                                  NMActivationType activation_type);
 
 /*****************************************************************************/
 
@@ -112,8 +121,12 @@ static void _device_cleanup (NMActiveConnection *self);
 #define _NMLOG(level, ...) \
     G_STMT_START { \
         char _sbuf[64]; \
+        NMDevice *_device = (self) ? NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->device : NULL; \
+        NMConnection *_applied_connection = _device ? NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->applied_connection : NULL; \
         \
         nm_log ((level), _NMLOG_DOMAIN, \
+                (_device) ? nm_device_get_iface (_device) : NULL, \
+                (_applied_connection) ? nm_connection_get_uuid (_applied_connection) : NULL, \
                 "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                 _NMLOG_PREFIX_NAME, \
                 self ? nm_sprintf_buf (_sbuf, "[%p]", self) : "" \
@@ -181,12 +194,15 @@ _set_settings_connection (NMActiveConnection *self, NMSettingsConnection *connec
 	if (priv->settings_connection) {
 		g_signal_handlers_disconnect_by_func (priv->settings_connection, _settings_connection_updated, self);
 		g_signal_handlers_disconnect_by_func (priv->settings_connection, _settings_connection_removed, self);
+		g_signal_handlers_disconnect_by_func (priv->settings_connection, _settings_connection_notify_flags, self);
 		g_clear_object (&priv->settings_connection);
 	}
 	if (connection) {
 		priv->settings_connection = g_object_ref (connection);
 		g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL, (GCallback) _settings_connection_updated, self);
 		g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED, (GCallback) _settings_connection_removed, self);
+		if (nm_active_connection_get_activation_type (self) == NM_ACTIVATION_TYPE_EXTERNAL)
+			g_signal_connect (connection, "notify::"NM_SETTINGS_CONNECTION_FLAGS, (GCallback) _settings_connection_notify_flags, self);
 	}
 }
 
@@ -198,7 +214,8 @@ nm_active_connection_get_state (NMActiveConnection *self)
 
 void
 nm_active_connection_set_state (NMActiveConnection *self,
-                                NMActiveConnectionState new_state)
+                                NMActiveConnectionState new_state,
+                                NMActiveConnectionStateReason reason)
 {
 	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
 	NMActiveConnectionState old_state;
@@ -214,9 +231,18 @@ nm_active_connection_set_state (NMActiveConnection *self,
 	       state_to_string (new_state),
 	       state_to_string (priv->state));
 
+	if (   new_state == NM_ACTIVE_CONNECTION_STATE_ACTIVATED
+	    && priv->activation_type == NM_ACTIVATION_TYPE_ASSUME) {
+		/* assuming connections mean to gracefully take over an externally
+		 * configured device. Once activation is complete, an assumed
+		 * activation *is* the same as a full activation. */
+		_set_activation_type (self, NM_ACTIVATION_TYPE_MANAGED);
+	}
+
 	old_state = priv->state;
 	priv->state = new_state;
 	priv->state_set = TRUE;
+	g_signal_emit (self, signals[STATE_CHANGED], 0, (guint) new_state, (guint) reason);
 	_notify (self, PROP_STATE);
 
 	check_master_ready (self);
@@ -566,7 +592,7 @@ nm_active_connection_set_device (NMActiveConnection *self, NMDevice *device)
 		g_signal_connect (device, "notify::" NM_DEVICE_METERED,
 		                  G_CALLBACK (device_metered_changed), self);
 
-		if (!priv->assumed) {
+		if (priv->activation_type != NM_ACTIVATION_TYPE_EXTERNAL) {
 			priv->pending_activation_id = g_strdup_printf (NM_PENDING_ACTIONPREFIX_ACTIVATION"%p", (void *)self);
 			nm_device_add_pending_action (device, priv->pending_activation_id, TRUE);
 		}
@@ -712,24 +738,55 @@ nm_active_connection_set_master (NMActiveConnection *self, NMActiveConnection *m
 	check_master_ready (self);
 }
 
-void
-nm_active_connection_set_assumed (NMActiveConnection *self, gboolean assumed)
+NMActivationType
+nm_active_connection_get_activation_type (NMActiveConnection *self)
+{
+	g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), NM_ACTIVATION_TYPE_MANAGED);
+
+	return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->activation_type;
+}
+
+static void
+_set_activation_type (NMActiveConnection *self,
+                      NMActivationType activation_type)
 {
 	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
 
-	g_return_if_fail (priv->assumed == FALSE);
-	priv->assumed = assumed;
+	if (priv->activation_type == activation_type)
+		return;
 
-	if (priv->pending_activation_id) {
-		nm_device_remove_pending_action (priv->device, priv->pending_activation_id, TRUE);
-		g_clear_pointer (&priv->pending_activation_id, g_free);
-	}
+	_LOGD ("update activation type from %s to %s",
+	       nm_activation_type_to_string (priv->activation_type),
+	       nm_activation_type_to_string (activation_type));
+	priv->activation_type = activation_type;
+
+	if (   priv->activation_type == NM_ACTIVATION_TYPE_MANAGED
+	    && priv->device
+	    && self == NM_ACTIVE_CONNECTION (nm_device_get_act_request (priv->device))
+	    && NM_IN_SET (nm_device_sys_iface_state_get (priv->device),
+	                  NM_DEVICE_SYS_IFACE_STATE_EXTERNAL,
+	                  NM_DEVICE_SYS_IFACE_STATE_ASSUME))
+		nm_device_sys_iface_state_set (priv->device, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
 }
 
-gboolean
-nm_active_connection_get_assumed (NMActiveConnection *self)
+/*****************************************************************************/
+
+static void
+_settings_connection_notify_flags (NMSettingsConnection *settings_connection,
+                                   GParamSpec *param,
+                                   NMActiveConnection *self)
 {
-	return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->assumed;
+	nm_assert (NM_IS_ACTIVE_CONNECTION (self));
+	nm_assert (NM_IS_SETTINGS_CONNECTION (settings_connection));
+	nm_assert (nm_active_connection_get_activation_type (self) == NM_ACTIVATION_TYPE_EXTERNAL);
+	nm_assert (NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->settings_connection == settings_connection);
+
+	if (nm_settings_connection_get_nm_generated (settings_connection))
+		return;
+
+	g_signal_handlers_disconnect_by_func (settings_connection, _settings_connection_notify_flags, self);
+	_set_activation_type (self, NM_ACTIVATION_TYPE_MANAGED);
+	nm_device_reapply_settings_immediately (nm_active_connection_get_device (self));
 }
 
 /*****************************************************************************/
@@ -942,7 +999,7 @@ nm_active_connection_version_id_bump (NMActiveConnection *self)
 
 	priv = NM_ACTIVE_CONNECTION_GET_PRIVATE  (self);
 	priv->version_id = _version_id_new ();
-	_LOGT ("new version-id %llu", (long long unsigned) priv->version_id);
+	_LOGT ("new version-id %llu", (unsigned long long) priv->version_id);
 	return priv->version_id;
 }
 
@@ -1058,6 +1115,7 @@ set_property (GObject *object, guint prop_id,
 	const char *tmp;
 	NMSettingsConnection *con;
 	NMConnection *acon;
+	int i;
 
 	switch (prop_id) {
 	case PROP_INT_SETTINGS_CONNECTION:
@@ -1082,6 +1140,15 @@ set_property (GObject *object, guint prop_id,
 	case PROP_INT_MASTER:
 		nm_active_connection_set_master (self, g_value_get_object (value));
 		break;
+	case PROP_INT_ACTIVATION_TYPE:
+		/* construct-only */
+		i = g_value_get_int (value);
+		if (!NM_IN_SET (i, NM_ACTIVATION_TYPE_MANAGED,
+		                   NM_ACTIVATION_TYPE_ASSUME,
+		                   NM_ACTIVATION_TYPE_EXTERNAL))
+			g_return_if_reached ();
+		priv->activation_type = (NMActivationType) i;
+		break;
 	case PROP_SPECIFIC_OBJECT:
 		tmp = g_value_get_string (value);
 		/* NM uses "/" to mean NULL */
@@ -1117,6 +1184,7 @@ nm_active_connection_init (NMActiveConnection *self)
 
 	_LOGT ("creating");
 
+	priv->activation_type = NM_ACTIVATION_TYPE_MANAGED;
 	priv->version_id = _version_id_new ();
 }
 
@@ -1128,15 +1196,16 @@ constructed (GObject *object)
 
 	G_OBJECT_CLASS (nm_active_connection_parent_class)->constructed (object);
 
-	if (!priv->applied_connection && priv->settings_connection) {
-		priv->applied_connection =
-			nm_simple_connection_new_clone ((NMConnection *) priv->settings_connection);
-	}
+	if (!priv->applied_connection && priv->settings_connection)
+		priv->applied_connection = nm_simple_connection_new_clone (NM_CONNECTION (priv->settings_connection));
 
 	if (priv->applied_connection)
 		nm_connection_clear_secrets (priv->applied_connection);
 
-	_LOGD ("constructed (%s, version-id %llu)", G_OBJECT_TYPE_NAME (self), (long long unsigned) priv->version_id);
+	_LOGD ("constructed (%s, version-id %llu, type %s)",
+	       G_OBJECT_TYPE_NAME (self),
+	       (unsigned long long) priv->version_id,
+	       nm_activation_type_to_string (priv->activation_type));
 
 	g_return_if_fail (priv->subject);
 }
@@ -1321,6 +1390,15 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class)
 	                           FALSE, G_PARAM_READABLE |
 	                           G_PARAM_STATIC_STRINGS);
 
+	obj_properties[PROP_INT_ACTIVATION_TYPE] =
+	     g_param_spec_int (NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE, "", "",
+	                       NM_ACTIVATION_TYPE_MANAGED,
+	                       NM_ACTIVATION_TYPE_EXTERNAL,
+	                       NM_ACTIVATION_TYPE_MANAGED,
+	                       G_PARAM_WRITABLE |
+	                       G_PARAM_CONSTRUCT_ONLY |
+	                       G_PARAM_STATIC_STRINGS);
+
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	signals[DEVICE_CHANGED] =
@@ -1347,6 +1425,13 @@ nm_active_connection_class_init (NMActiveConnectionClass *ac_class)
 	                  NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, NM_TYPE_ACTIVE_CONNECTION);
 
+	signals[STATE_CHANGED] =
+	    g_signal_new (NM_ACTIVE_CONNECTION_STATE_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
 	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (ac_class),
 	                                        NMDBUS_TYPE_ACTIVE_CONNECTION_SKELETON,
 	                                        NULL);
diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h
index d87a30b5..8d3478c7 100644
--- a/src/nm-active-connection.h
+++ b/src/nm-active-connection.h
@@ -55,6 +55,10 @@
 #define NM_ACTIVE_CONNECTION_INT_SUBJECT        "int-subject"
 #define NM_ACTIVE_CONNECTION_INT_MASTER         "int-master"
 #define NM_ACTIVE_CONNECTION_INT_MASTER_READY   "int-master-ready"
+#define NM_ACTIVE_CONNECTION_INT_ACTIVATION_TYPE "int-activation-type"
+
+/* Signals */
+#define NM_ACTIVE_CONNECTION_STATE_CHANGED           "state-changed"
 
 /* Internal signals*/
 #define NM_ACTIVE_CONNECTION_DEVICE_CHANGED          "device-changed"
@@ -138,7 +142,8 @@ gboolean      nm_active_connection_get_default6 (NMActiveConnection *self);
 NMActiveConnectionState nm_active_connection_get_state (NMActiveConnection *self);
 
 void          nm_active_connection_set_state (NMActiveConnection *self,
-                                              NMActiveConnectionState state);
+                                              NMActiveConnectionState state,
+                                              NMActiveConnectionStateReason reason);
 
 NMDevice *    nm_active_connection_get_device (NMActiveConnection *self);
 
@@ -158,10 +163,7 @@ void          nm_active_connection_set_master (NMActiveConnection *self,
 void          nm_active_connection_set_parent (NMActiveConnection *self,
                                                NMActiveConnection *parent);
 
-void          nm_active_connection_set_assumed (NMActiveConnection *self,
-                                                gboolean assumed);
-
-gboolean      nm_active_connection_get_assumed (NMActiveConnection *self);
+NMActivationType nm_active_connection_get_activation_type (NMActiveConnection *self);
 
 void          nm_active_connection_clear_secrets (NMActiveConnection *self);
 
diff --git a/src/nm-audit-manager.c b/src/nm-audit-manager.c
index eeb9d2ba..2d75c0e7 100644
--- a/src/nm-audit-manager.c
+++ b/src/nm-audit-manager.c
@@ -77,7 +77,7 @@ G_DEFINE_TYPE (NMAuditManager, nm_audit_manager, G_TYPE_OBJECT)
 #define _NMLOG_PREFIX_NAME    "audit"
 #define _NMLOG(level, domain, ...) \
     G_STMT_START { \
-        nm_log ((level), (domain), \
+        nm_log ((level), (domain), NULL, NULL, \
                 "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                 _NMLOG_PREFIX_NAME": " \
                 _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
@@ -302,11 +302,11 @@ _nm_audit_manager_log_generic_op (NMAuditManager *self, const char *file, guint
 void
 _nm_audit_manager_log_device_op (NMAuditManager *self, const char *file, guint line,
                                  const char *func, const char *op, NMDevice *device,
-                                 gboolean result, gpointer subject_context,
+                                 gboolean result, const char *args, gpointer subject_context,
                                  const char *reason)
 {
 	gs_unref_ptrarray GPtrArray *fields = NULL;
-	AuditField interface_field = { }, ifindex_field = { };
+	AuditField interface_field = { }, ifindex_field = { }, args_field = { };
 	int ifindex;
 
 	g_return_if_fail (op);
@@ -324,6 +324,11 @@ _nm_audit_manager_log_device_op (NMAuditManager *self, const char *file, guint l
 		g_ptr_array_add (fields, &ifindex_field);
 	}
 
+	if (args) {
+		_audit_field_init_string (&args_field, "args", args, FALSE, BACKEND_ALL);
+		g_ptr_array_add (fields, &args_field);
+	}
+
 	_audit_log_helper (self, fields, file, line, func, op, result, subject_context, reason);
 }
 
diff --git a/src/nm-audit-manager.h b/src/nm-audit-manager.h
index 29bde1a5..56e26584 100644
--- a/src/nm-audit-manager.h
+++ b/src/nm-audit-manager.h
@@ -83,13 +83,13 @@ gboolean nm_audit_manager_audit_enabled (NMAuditManager *self);
 		} \
 	} G_STMT_END
 
-#define nm_audit_log_device_op(op, device, result, subject_context, reason) \
+#define nm_audit_log_device_op(op, device, result, args, subject_context, reason) \
 	G_STMT_START { \
 		NMAuditManager *_audit = nm_audit_manager_get (); \
 		\
 		if (nm_audit_manager_audit_enabled (_audit)) { \
 			_nm_audit_manager_log_device_op (_audit, __FILE__, __LINE__, G_STRFUNC, \
-			                                 (op), (device), (result), (subject_context), (reason)); \
+			                                 (op), (device), (result), (args), (subject_context), (reason)); \
 		} \
 	} G_STMT_END
 
@@ -114,6 +114,7 @@ void _nm_audit_manager_log_generic_op    (NMAuditManager *self, const char *file
 
 void _nm_audit_manager_log_device_op     (NMAuditManager *self, const char *file, guint line,
                                           const char *func, const char *op, NMDevice *device,
-                                          gboolean result, gpointer subject_context, const char *reason);
+                                          gboolean result, const char *args, gpointer subject_context,
+                                          const char *reason);
 
 #endif /* __NM_AUDIT_MANAGER_H__ */
diff --git a/src/nm-auth-manager.c b/src/nm-auth-manager.c
index 4359d8f1..003d9975 100644
--- a/src/nm-auth-manager.c
+++ b/src/nm-auth-manager.c
@@ -79,7 +79,7 @@ NM_DEFINE_SINGLETON_REGISTER (NMAuthManager);
             \
             if ((self) != singleton_instance) \
                 g_snprintf (__prefix, sizeof (__prefix), ""_NMLOG_PREFIX_NAME"[%p]", (self)); \
-            _nm_log ((level), (_NMLOG_DOMAIN), 0, \
+            _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
                      "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
@@ -595,10 +595,7 @@ dispose (GObject *object)
 	/* since we take a reference for each queued call, we don't expect to have any queued calls in dispose() */
 	g_assert (!priv->queued_calls);
 
-	if (priv->new_proxy_cancellable) {
-		g_cancellable_cancel (priv->new_proxy_cancellable);
-		g_clear_object (&priv->new_proxy_cancellable);
-	}
+	nm_clear_g_cancellable (&priv->new_proxy_cancellable);
 
 	if (priv->proxy) {
 		g_signal_handlers_disconnect_by_data (priv->proxy, self);
diff --git a/src/nm-auth-subject.c b/src/nm-auth-subject.c
index 9819ca70..0f40ff7c 100644
--- a/src/nm-auth-subject.c
+++ b/src/nm-auth-subject.c
@@ -88,9 +88,9 @@ nm_auth_subject_to_string (NMAuthSubject *self, char *buf, gsize buf_len)
 	switch (priv->subject_type) {
 	case NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS:
 		g_snprintf (buf, buf_len, "unix-process[pid=%lu, uid=%lu, start=%llu]",
-		            (long unsigned) priv->unix_process.pid,
-		            (long unsigned) priv->unix_process.uid,
-		            (long long unsigned) priv->unix_process.start_time);
+		            (unsigned long) priv->unix_process.pid,
+		            (unsigned long) priv->unix_process.uid,
+		            (unsigned long long) priv->unix_process.start_time);
 		break;
 	case NM_AUTH_SUBJECT_TYPE_INTERNAL:
 		g_strlcat (buf, "internal", buf_len);
@@ -290,9 +290,9 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *p
 	const char *str;
 	gulong id;
 
-	/* all properties are construct-only */
 	switch (prop_id) {
 	case PROP_SUBJECT_TYPE:
+		/* construct-only */
 		i = g_value_get_int (value);
 		g_return_if_fail (NM_IN_SET (i, (int) NM_AUTH_SUBJECT_TYPE_INTERNAL, (int) NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS));
 		subject_type = i;
@@ -300,6 +300,7 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *p
 		g_return_if_fail (priv->subject_type == subject_type);
 		break;
 	case PROP_UNIX_PROCESS_DBUS_SENDER:
+		/* construct-only */
 		if ((str = g_value_get_string (value))) {
 			priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS;
 			g_return_if_fail (priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS);
@@ -307,6 +308,7 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *p
 		}
 		break;
 	case PROP_UNIX_PROCESS_PID:
+		/* construct-only */
 		if ((id = g_value_get_ulong (value)) != G_MAXULONG) {
 			priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS;
 			g_return_if_fail (priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS);
@@ -314,6 +316,7 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *p
 		}
 		break;
 	case PROP_UNIX_PROCESS_UID:
+		/* construct-only */
 		if ((id = g_value_get_ulong (value)) != G_MAXULONG) {
 			priv->subject_type |= NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS;
 			g_return_if_fail (priv->subject_type == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS);
@@ -407,7 +410,6 @@ nm_auth_subject_class_init (NMAuthSubjectClass *config_class)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (config_class);
 
-	/* virtual methods */
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
 	object_class->constructed = constructed;
diff --git a/src/nm-auth-utils.c b/src/nm-auth-utils.c
index 930f6d0f..b8e64ceb 100644
--- a/src/nm-auth-utils.c
+++ b/src/nm-auth-utils.c
@@ -301,12 +301,10 @@ auth_call_cancel (gpointer user_data)
 {
 	AuthCall *call = user_data;
 
-	if (call->cancellable) {
+	if (nm_clear_g_cancellable (&call->cancellable)) {
 		/* we don't free call immediately. Instead we cancel the async operation
 		 * and set cancellable to NULL. pk_call_cb() will check for this and
 		 * do the final cleanup. */
-		g_cancellable_cancel (call->cancellable);
-		g_clear_object (&call->cancellable);
 	} else {
 		g_source_remove (call->call_idle_id);
 		auth_call_free (call);
@@ -319,7 +317,7 @@ pk_call_cb (GObject *object, GAsyncResult *result, gpointer user_data)
 {
 	AuthCall *call = user_data;
 	GError *error = NULL;
-	gboolean is_authorized, is_challenge;
+	gboolean is_authorized = FALSE, is_challenge = FALSE;
 	guint call_result = NM_AUTH_CALL_RESULT_UNKNOWN;
 
 	nm_auth_manager_polkit_authority_check_authorization_finish (NM_AUTH_MANAGER (object),
diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c
index 7d89e61e..3f2f0eaa 100644
--- a/src/nm-checkpoint.c
+++ b/src/nm-checkpoint.c
@@ -24,6 +24,7 @@
 
 #include <string.h>
 
+#include "nm-active-connection.h"
 #include "nm-auth-subject.h"
 #include "nm-core-utils.h"
 #include "nm-dbus-interface.h"
@@ -42,6 +43,7 @@ typedef struct {
 	NMDevice *device;
 	NMConnection *applied_connection;
 	NMConnection *settings_connection;
+	guint64 ac_version_id;
 	NMDeviceState state;
 	bool realized:1;
 	bool unmanaged_explicit:1;
@@ -92,7 +94,7 @@ G_DEFINE_TYPE (NMCheckpoint, nm_checkpoint, NM_TYPE_EXPORTED_OBJECT)
 				g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", ""_NMLOG_PREFIX_NAME"", (self)); \
 			else \
 				g_strlcpy (__prefix, _NMLOG_PREFIX_NAME, sizeof (__prefix)); \
-			_nm_log ((level), (_NMLOG_DOMAIN), 0, \
+			_nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
 			          "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
 			          __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
 		} \
@@ -116,6 +118,62 @@ nm_checkpoint_includes_device (NMCheckpoint *self, NMDevice *device)
 	return g_hash_table_contains (priv->devices, device);
 }
 
+static NMSettingsConnection *
+find_settings_connection (NMCheckpoint *self,
+                          DeviceCheckpoint *dev_checkpoint,
+                          gboolean *need_update,
+                          gboolean *need_activation)
+{
+	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
+	const GSList *active_connections, *iter;
+	NMActiveConnection *active = NULL;
+	NMSettingsConnection *connection;
+	const char *uuid, *ac_uuid;
+
+	*need_activation = FALSE;
+	*need_update = FALSE;
+
+	uuid = nm_connection_get_uuid (dev_checkpoint->settings_connection);
+	connection = nm_settings_get_connection_by_uuid (nm_settings_get (), uuid);
+
+	if (!connection)
+		return NULL;
+
+	/* Now check if the connection changed, ... */
+	if (!nm_connection_compare (dev_checkpoint->settings_connection,
+	                            NM_CONNECTION (connection),
+	                            NM_SETTING_COMPARE_FLAG_EXACT)) {
+		_LOGT ("rollback: settings connection %s changed", uuid);
+		*need_update = TRUE;
+		*need_activation = TRUE;
+	}
+
+	/* ... is active, ... */
+	active_connections = nm_manager_get_active_connections (priv->manager);
+	for (iter = active_connections; iter; iter = g_slist_next (iter)) {
+		active = iter->data;
+		ac_uuid = nm_settings_connection_get_uuid (nm_active_connection_get_settings_connection (active));
+		if (nm_streq (uuid, ac_uuid)) {
+			_LOGT ("rollback: connection %s is active", uuid);
+			break;
+		}
+	}
+
+	if (!iter) {
+		_LOGT ("rollback: connection %s is not active", uuid);
+		*need_activation = TRUE;
+		return connection;
+	}
+
+	/* ... or if the connection was reactivated/reapplied */
+	if (nm_active_connection_version_id_get (active) != dev_checkpoint->ac_version_id) {
+		_LOGT ("rollback: active connection version id of %s changed", uuid);
+		*need_activation = TRUE;
+	}
+
+	return connection;
+}
+
 GVariant *
 nm_checkpoint_rollback (NMCheckpoint *self)
 {
@@ -135,7 +193,6 @@ nm_checkpoint_rollback (NMCheckpoint *self)
 	while (g_hash_table_iter_next (&iter, (gpointer *) &device, (gpointer *) &dev_checkpoint)) {
 		gs_unref_object NMAuthSubject *subject = NULL;
 		guint32 result = NM_ROLLBACK_RESULT_OK;
-		const char *con_uuid;
 
 		_LOGD ("rollback: restoring device %s (state %d, realized %d, explicitly unmanaged %d)",
 		       nm_device_get_iface (device),
@@ -180,27 +237,26 @@ activate:
 		}
 
 		if (dev_checkpoint->applied_connection) {
-			/* The device had an active connection, check if the
-			 * connection still exists
-			 * */
-			con_uuid = nm_connection_get_uuid (dev_checkpoint->settings_connection);
-			connection = nm_settings_get_connection_by_uuid (nm_settings_get (), con_uuid);
+			gboolean need_update, need_activation;
 
+			/* The device had an active connection: check if the
+			 * connection still exists, is active and was changed */
+			connection = find_settings_connection (self, dev_checkpoint, &need_update, &need_activation);
 			if (connection) {
-				/* If the connection is still there, restore its content
-				 * and save it
-				 * */
-				_LOGD ("rollback: connection %s still exists", con_uuid);
-
-				nm_connection_replace_settings_from_connection (NM_CONNECTION (connection),
-				                                                dev_checkpoint->settings_connection);
-				nm_settings_connection_commit_changes (connection,
-				                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
-				                                       NULL,
-				                                       NULL);
+				if (need_update) {
+					_LOGD ("rollback: updating connection %s",
+					        nm_settings_connection_get_uuid (connection));
+					nm_connection_replace_settings_from_connection (NM_CONNECTION (connection),
+					                                                dev_checkpoint->settings_connection);
+					nm_settings_connection_commit_changes (connection,
+					                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+					                                       NULL,
+					                                       NULL);
+				}
 			} else {
 				/* The connection was deleted, recreate it */
-				_LOGD ("rollback: adding connection %s again", con_uuid);
+				_LOGD ("rollback: adding connection %s again",
+				       nm_connection_get_uuid (dev_checkpoint->settings_connection));
 
 				connection = nm_settings_add_connection (nm_settings_get (),
 				                                         dev_checkpoint->settings_connection,
@@ -212,24 +268,29 @@ activate:
 					result = NM_ROLLBACK_RESULT_ERR_FAILED;
 					goto next_dev;
 				}
+				need_activation = TRUE;
 			}
 
-			/* Now re-activate the connection */
-			subject = nm_auth_subject_new_internal ();
-			if (!nm_manager_activate_connection (priv->manager,
-			                                     connection,
-			                                     dev_checkpoint->applied_connection,
-			                                     NULL,
-			                                     device,
-			                                     subject,
-			                                     &local_error)) {
-				_LOGW ("rollback: reactivation of connection %s/%s failed: %s",
-				       nm_connection_get_id ((NMConnection *) connection),
-				       nm_connection_get_uuid ((NMConnection *	) connection),
-				       local_error->message);
-				g_clear_error (&local_error);
-				result = NM_ROLLBACK_RESULT_ERR_FAILED;
-				goto next_dev;
+			if (need_activation) {
+				_LOGD ("rollback: reactivating connection %s",
+				       nm_settings_connection_get_uuid (connection));
+				subject = nm_auth_subject_new_internal ();
+				if (!nm_manager_activate_connection (priv->manager,
+				                                     connection,
+				                                     dev_checkpoint->applied_connection,
+				                                     NULL,
+				                                     device,
+				                                     subject,
+				                                     NM_ACTIVATION_TYPE_MANAGED,
+				                                     &local_error)) {
+					_LOGW ("rollback: reactivation of connection %s/%s failed: %s",
+					       nm_connection_get_id ((NMConnection *) connection),
+					       nm_connection_get_uuid ((NMConnection *	) connection),
+					       local_error->message);
+					g_clear_error (&local_error);
+					result = NM_ROLLBACK_RESULT_ERR_FAILED;
+					goto next_dev;
+				}
 			}
 		} else {
 			/* The device was initially disconnected, deactivate any existing connection */
@@ -249,26 +310,25 @@ next_dev:
 
 	if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS)) {
 		NMSettingsConnection *con;
-		gs_free_slist GSList *list = NULL;
-		GSList *item;
+		gs_free NMSettingsConnection **list = NULL;
+		guint i;
 
 		g_return_val_if_fail (priv->connection_uuids, NULL);
-		list = nm_settings_get_connections_sorted (nm_settings_get ());
+		list = nm_settings_get_connections_sorted (nm_settings_get (), NULL);
 
-		for (item = list; item; item = g_slist_next (item)) {
-			con = item->data;
+		for (i = 0; list[i]; i++) {
+			con = list[i];
 			if (!g_hash_table_contains (priv->connection_uuids,
 			                            nm_settings_connection_get_uuid (con))) {
-				_LOGD ("rollback: deleting new connection %s (%s)",
-				       nm_settings_connection_get_uuid (con),
-				       nm_settings_connection_get_id (con));
+				_LOGD ("rollback: deleting new connection %s",
+				       nm_settings_connection_get_uuid (con));
 				nm_settings_connection_delete (con, NULL, NULL);
 			}
 		}
 	}
 
 	if (NM_FLAGS_HAS (priv->flags, NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES)) {
-		const GSList *list = nm_manager_get_devices (priv->manager);
+		const GSList *list;
 		NMDeviceState state;
 		NMDevice *dev;
 
@@ -300,6 +360,7 @@ device_checkpoint_create (NMDevice *device,
 	NMSettingsConnection *settings_connection;
 	const char *path;
 	gboolean unmanaged_explicit;
+	NMActRequest *act_request;
 
 	path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (device));
 	unmanaged_explicit = !!nm_device_get_unmanaged_flags (device,
@@ -321,6 +382,11 @@ device_checkpoint_create (NMDevice *device,
 		g_return_val_if_fail (settings_connection, NULL);
 		dev_checkpoint->settings_connection =
 			nm_simple_connection_new_clone (NM_CONNECTION (settings_connection));
+
+		act_request = nm_device_get_act_request (device);
+		g_return_val_if_fail (act_request, NULL);
+		dev_checkpoint->ac_version_id =
+			nm_active_connection_version_id_get (NM_ACTIVE_CONNECTION (act_request));
 	}
 
 	return dev_checkpoint;
diff --git a/src/nm-config-data.c b/src/nm-config-data.c
index 68e66971..8f4d1121 100644
--- a/src/nm-config-data.c
+++ b/src/nm-config-data.c
@@ -561,7 +561,7 @@ _nm_config_data_log_sort (const char **pa, const char **pb, gpointer dummy)
 	return g_strcmp0 (a, b);
 }
 
-static struct {
+static const struct {
 	const char *group;
 	const char *key;
 	const char *value;
@@ -601,7 +601,7 @@ nm_config_data_log (const NMConfigData *self,
 #define _LOG(stream, prefix, ...) \
 	G_STMT_START { \
 		if (!stream) \
-			_nm_log (LOGL_DEBUG, LOGD_CORE, 0, "%s"_NM_UTILS_MACRO_FIRST(__VA_ARGS__)"%s", prefix _NM_UTILS_MACRO_REST (__VA_ARGS__), ""); \
+			_nm_log (LOGL_DEBUG, LOGD_CORE, 0, NULL, NULL, "%s"_NM_UTILS_MACRO_FIRST(__VA_ARGS__)"%s", prefix _NM_UTILS_MACRO_REST (__VA_ARGS__), ""); \
 		else \
 			fprintf (stream, "%s"_NM_UTILS_MACRO_FIRST(__VA_ARGS__)"%s", prefix _NM_UTILS_MACRO_REST (__VA_ARGS__), "\n"); \
 	} G_STMT_END
@@ -1437,15 +1437,17 @@ set_property (GObject *object,
 	NMConfigData *self = NM_CONFIG_DATA (object);
 	NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (self);
 
-	/* This type is immutable. All properties are construct only. */
 	switch (prop_id) {
 	case PROP_CONFIG_MAIN_FILE:
+		/* construct-only */
 		priv->config_main_file = g_value_dup_string (value);
 		break;
 	case PROP_CONFIG_DESCRIPTION:
+		/* construct-only */
 		priv->config_description = g_value_dup_string (value);
 		break;
 	case PROP_KEYFILE_USER:
+		/* construct-only */
 		priv->keyfile_user = g_value_dup_boxed (value);
 		if (   priv->keyfile_user
 		    && !_nm_keyfile_has_values (priv->keyfile_user)) {
@@ -1454,6 +1456,7 @@ set_property (GObject *object,
 		}
 		break;
 	case PROP_KEYFILE_INTERN:
+		/* construct-only */
 		priv->keyfile_intern = g_value_dup_boxed (value);
 		if (   priv->keyfile_intern
 		    && !_nm_keyfile_has_values (priv->keyfile_intern)) {
@@ -1462,6 +1465,7 @@ set_property (GObject *object,
 		}
 		break;
 	case PROP_NO_AUTO_DEFAULT:
+		/* construct-only */
 		{
 			char **value_arr = g_value_get_boxed (value);
 			guint i, j = 0;
@@ -1472,7 +1476,7 @@ set_property (GObject *object,
 			for (i = 0; value_arr && value_arr[i]; i++) {
 				if (   *value_arr[i]
 				    && nm_utils_hwaddr_valid (value_arr[i], -1)
-				    && _nm_utils_strv_find_first (value_arr, i, value_arr[i]) < 0) {
+				    && nm_utils_strv_find_first (value_arr, i, value_arr[i]) < 0) {
 					priv->no_auto_default.arr[j++] = g_strdup (value_arr[i]);
 					priv->no_auto_default.specs = g_slist_prepend (priv->no_auto_default.specs, g_strdup_printf ("mac:%s", value_arr[i]));
 				}
diff --git a/src/nm-config-data.h b/src/nm-config-data.h
index d7d14a61..98c66751 100644
--- a/src/nm-config-data.h
+++ b/src/nm-config-data.h
@@ -171,6 +171,8 @@ const char *nm_config_data_get_rc_manager (const NMConfigData *self);
 
 gboolean nm_config_data_get_ignore_carrier (const NMConfigData *self, NMDevice *device);
 gboolean nm_config_data_get_assume_ipv6ll_only (const NMConfigData *self, NMDevice *device);
+int      nm_config_data_get_sriov_num_vfs (const NMConfigData *self, NMDevice *device);
+
 NMGlobalDnsConfig *nm_config_data_get_global_dns_config (const NMConfigData *self);
 
 char *nm_config_data_get_connection_default (const NMConfigData *self,
diff --git a/src/nm-config.c b/src/nm-config.c
index c68a8429..2cdf8556 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -60,6 +60,15 @@ struct NMConfigCmdLineOptions {
 	 */
 	int connectivity_interval;
 	char *connectivity_response;
+
+	/* @first_start is not provided by command line. It is a convenient hack
+	 * to pass in an argument to NMConfig. This makes NMConfigCmdLineOptions a
+	 * misnomer.
+	 *
+	 * It is true, if NM is started the first time -- contrary to a restart
+	 * during the same boot up. That is determined by the content of the
+	 * /var/run/NetworManager state directory. */
+	bool first_start;
 };
 
 typedef struct {
@@ -291,6 +300,12 @@ nm_config_get_is_debug (NMConfig *config)
 	return NM_CONFIG_GET_PRIVATE (config)->cli.is_debug;
 }
 
+gboolean
+nm_config_get_first_start (NMConfig *config)
+{
+	return NM_CONFIG_GET_PRIVATE (config)->cli.first_start;
+}
+
 /*****************************************************************************/
 
 static char **
@@ -309,7 +324,7 @@ no_auto_default_from_file (const char *no_auto_default_file)
 		for (i = 0; list[i]; i++) {
 			if (   *list[i]
 			    && nm_utils_hwaddr_valid (list[i], -1)
-			    && _nm_utils_strv_find_first (list, i, list[i]) < 0)
+			    && nm_utils_strv_find_first (list, i, list[i]) < 0)
 				g_ptr_array_add (no_auto_default_new, list[i]);
 			else
 				g_free (list[i]);
@@ -369,7 +384,7 @@ nm_config_set_no_auto_default_for_device (NMConfig *self, NMDevice *device)
 
 	no_auto_default_current = nm_config_data_get_no_auto_default (priv->config_data);
 
-	if (_nm_utils_strv_find_first ((char **) no_auto_default_current, -1, hw_address) >= 0) {
+	if (nm_utils_strv_find_first ((char **) no_auto_default_current, -1, hw_address) >= 0) {
 		/* @hw_address is already blocked. We don't have to update our in-memory representation.
 		 * Maybe we should write to no_auto_default_file anew, but let's save that too. */
 		return;
@@ -412,6 +427,7 @@ _nm_config_cmd_line_options_clear (NMConfigCmdLineOptions *cli)
 	g_clear_pointer (&cli->connectivity_uri, g_free);
 	g_clear_pointer (&cli->connectivity_response, g_free);
 	cli->connectivity_interval = -1;
+	cli->first_start = FALSE;
 }
 
 static void
@@ -434,14 +450,18 @@ _nm_config_cmd_line_options_copy (const NMConfigCmdLineOptions *cli, NMConfigCmd
 	dst->connectivity_uri = g_strdup (cli->connectivity_uri);
 	dst->connectivity_response = g_strdup (cli->connectivity_response);
 	dst->connectivity_interval = cli->connectivity_interval;
+	dst->first_start = cli->first_start;
 }
 
 NMConfigCmdLineOptions *
-nm_config_cmd_line_options_new ()
+nm_config_cmd_line_options_new (gboolean first_start)
 {
 	NMConfigCmdLineOptions *cli = g_new0 (NMConfigCmdLineOptions, 1);
 
 	_nm_config_cmd_line_options_clear (cli);
+
+	cli->first_start = first_start;
+
 	return cli;
 }
 
@@ -551,14 +571,14 @@ _sort_groups_cmp (const char **pa, const char **pb, gpointer dummy)
 	b_is_connection = g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION);
 
 	if (a_is_connection != b_is_connection) {
-		/* one is a [connection*] entry, the other not. We sort [connection*] entires
+		/* one is a [connection*] entry, the other not. We sort [connection*] entries
 		 * after.  */
 		if (a_is_connection)
 			return 1;
 		return -1;
 	}
 	if (a_is_connection) {
-		/* both are [connection.\+] entires. Reverse their order.
+		/* both are [connection.\+] entries. Reverse their order.
 		 * One of the sections might be literally [connection]. That section
 		 * is special and it's order will be fixed later. It doesn't actually
 		 * matter here how it compares with [connection.\+] sections. */
@@ -569,14 +589,14 @@ _sort_groups_cmp (const char **pa, const char **pb, gpointer dummy)
 	b_is_device = g_str_has_prefix (b, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE);
 
 	if (a_is_device != b_is_device) {
-		/* one is a [device*] entry, the other not. We sort [device*] entires
+		/* one is a [device*] entry, the other not. We sort [device*] entries
 		 * after.  */
 		if (a_is_device)
 			return 1;
 		return -1;
 	}
 	if (a_is_device) {
-		/* both are [device.\+] entires. Reverse their order.
+		/* both are [device.\+] entries. Reverse their order.
 		 * One of the sections might be literally [device]. That section
 		 * is special and it's order will be fixed later. It doesn't actually
 		 * matter here how it compares with [device.\+] sections. */
@@ -659,7 +679,7 @@ read_config (GKeyFile *keyfile, gboolean is_base_config, const char *dirname, co
 	}
 
 	/* the config-group is internal to every configuration snippets. It doesn't make sense
-	 * to merge the into the global configuration, and it doesn't make sense to preserve the
+	 * to merge it into the global configuration, and it doesn't make sense to preserve the
 	 * group beyond this point. */
 	g_key_file_remove_group (kf, NM_CONFIG_KEYFILE_GROUP_CONFIG, NULL);
 
@@ -750,13 +770,13 @@ read_config (GKeyFile *keyfile, gboolean is_base_config, const char *dirname, co
 
 					for (iter_val = old_val; iter_val && *iter_val; iter_val++) {
 						if (   last_char != '-'
-						    || _nm_utils_strv_find_first (new_val, -1, *iter_val) < 0)
+						    || nm_utils_strv_find_first (new_val, -1, *iter_val) < 0)
 							g_ptr_array_add (new, g_strdup (*iter_val));
 					}
 					for (iter_val = new_val; iter_val && *iter_val; iter_val++) {
 						/* don't add duplicates. That means an "option=a,b"; "option+=a,c" results in "option=a,b,c" */
 						if (   last_char == '+'
-						    && _nm_utils_strv_find_first (old_val, -1, *iter_val) < 0)
+						    && nm_utils_strv_find_first (old_val, -1, *iter_val) < 0)
 							g_ptr_array_add (new, *iter_val);
 						else
 							g_free (*iter_val);
@@ -953,8 +973,8 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
 		const char *filename = system_confs->pdata[i];
 
 		/* if a same named file exists in config_dir or run_config_dir, skip it. */
-		if (_nm_utils_strv_find_first ((char **) confs->pdata, confs->len, filename) >= 0 ||
-		    _nm_utils_strv_find_first ((char **) run_confs->pdata, run_confs->len, filename) >= 0) {
+		if (nm_utils_strv_find_first ((char **) confs->pdata, confs->len, filename) >= 0 ||
+		    nm_utils_strv_find_first ((char **) run_confs->pdata, run_confs->len, filename) >= 0) {
 			g_ptr_array_remove_index (system_confs, i);
 			continue;
 		}
@@ -968,7 +988,7 @@ read_entire_config (const NMConfigCmdLineOptions *cli,
 		const char *filename = run_confs->pdata[i];
 
 		/* if a same named file exists in config_dir, skip it. */
-		if (_nm_utils_strv_find_first ((char **) confs->pdata, confs->len, filename) >= 0) {
+		if (nm_utils_strv_find_first ((char **) confs->pdata, confs->len, filename) >= 0) {
 			g_ptr_array_remove_index (run_confs, i);
 			continue;
 		}
@@ -1926,15 +1946,13 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
 
 /**
  * nm_config_device_state_load:
- * @self: the NMConfig instance
  * @ifindex: the ifindex for which the state is to load
  *
  * Returns: (transfer full): a run state object.
  *   Must be freed with g_free().
  */
 NMConfigDeviceStateData *
-nm_config_device_state_load (NMConfig *self,
-                             int ifindex)
+nm_config_device_state_load (int ifindex)
 {
 	NMConfigDeviceStateData *device_state;
 	char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR) + 60];
@@ -1964,10 +1982,16 @@ nm_config_device_state_load (NMConfig *self,
 	return device_state;
 }
 
+NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_device_state_managed_type_to_str, NMConfigDeviceStateManagedType,
+	NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN,   "unknown"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED, "unmanaged"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED,   "managed"),
+);
+
 gboolean
-nm_config_device_state_write (NMConfig *self,
-                              int ifindex,
-                              gboolean managed,
+nm_config_device_state_write (int ifindex,
+                              NMConfigDeviceStateManagedType managed,
                               const char *perm_hw_addr_fake,
                               const char *connection_uuid)
 {
@@ -1975,20 +1999,23 @@ nm_config_device_state_write (NMConfig *self,
 	GError *local = NULL;
 	gs_unref_keyfile GKeyFile *kf = NULL;
 
-	g_return_val_if_fail (NM_IS_CONFIG (self), FALSE);
 	g_return_val_if_fail (ifindex > 0, FALSE);
 	g_return_val_if_fail (!connection_uuid || *connection_uuid, FALSE);
-	g_return_val_if_fail (managed || !connection_uuid, FALSE);
+	g_return_val_if_fail (managed == NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED || !connection_uuid, FALSE);
 
 	nm_assert (!perm_hw_addr_fake || nm_utils_hwaddr_valid (perm_hw_addr_fake, -1));
 
 	nm_sprintf_buf (path, "%s/%d", NM_CONFIG_DEVICE_STATE_DIR, ifindex);
 
 	kf = nm_config_create_keyfile ();
-	g_key_file_set_boolean (kf,
-	                        DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
-	                        DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED,
-	                        !!managed);
+	if (NM_IN_SET (managed,
+	               NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED,
+	               NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED)) {
+		g_key_file_set_boolean (kf,
+		                        DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
+		                        DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED,
+		                        managed == NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED);
+	}
 	if (perm_hw_addr_fake) {
 		g_key_file_set_string (kf,
 		                       DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
@@ -2007,17 +2034,16 @@ nm_config_device_state_write (NMConfig *self,
 		g_error_free (local);
 		return FALSE;
 	}
-	_LOGT ("device-state: write #%d (%s); managed=%d%s%s%s%s%s%s",
+	_LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s",
 	       ifindex, path,
-	       (bool) managed,
+	       _device_state_managed_type_to_str (managed),
 	       NM_PRINT_FMT_QUOTED (connection_uuid, ", connection-uuid=", connection_uuid, "", ""),
 	       NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""));
 	return TRUE;
 }
 
 void
-nm_config_device_state_prune_unseen (NMConfig *self,
-                                     GHashTable *seen_ifindexes)
+nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes)
 {
 	GDir *dir;
 	const char *fn;
@@ -2230,6 +2256,7 @@ set_property (GObject *object, guint prop_id,
 	NMConfig *self = NM_CONFIG (object);
 	NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self);
 	NMConfigCmdLineOptions *cli;
+	char **strv;
 
 	switch (prop_id) {
 	case PROP_CMD_LINE_OPTIONS:
@@ -2242,7 +2269,10 @@ set_property (GObject *object, guint prop_id,
 		break;
 	case PROP_ATOMIC_SECTION_PREFIXES:
 		/* construct-only */
-		priv->atomic_section_prefixes = g_strdupv (g_value_get_boxed (value));
+		strv = g_value_get_boxed (value);
+		if (strv && !strv[0])
+			strv = NULL;
+		priv->atomic_section_prefixes = g_strdupv (strv);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
diff --git a/src/nm-config.h b/src/nm-config.h
index 9930df33..283d6a1b 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -62,6 +62,7 @@
 #define NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT              "auth-polkit"
 #define NM_CONFIG_KEYFILE_KEY_MAIN_DHCP                     "dhcp"
 #define NM_CONFIG_KEYFILE_KEY_MAIN_DEBUG                    "debug"
+#define NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE            "hostname-mode"
 #define NM_CONFIG_KEYFILE_KEY_LOGGING_BACKEND               "backend"
 #define NM_CONFIG_KEYFILE_KEY_CONFIG_ENABLE                 "enable"
 #define NM_CONFIG_KEYFILE_KEY_ATOMIC_SECTION_WAS            ".was"
@@ -74,6 +75,7 @@
 #define NM_CONFIG_KEYFILE_KEY_AUDIT                         "audit"
 
 #define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER         "ignore-carrier"
+#define NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS          "sriov-num-vfs"
 
 #define NM_CONFIG_KEYFILE_KEYPREFIX_WAS                     ".was."
 #define NM_CONFIG_KEYFILE_KEYPREFIX_SET                     ".set."
@@ -123,13 +125,15 @@ const char *nm_config_get_log_domains (NMConfig *config);
 gboolean nm_config_get_configure_and_quit (NMConfig *config);
 gboolean nm_config_get_is_debug (NMConfig *config);
 
+gboolean nm_config_get_first_start (NMConfig *config);
+
 void nm_config_set_values (NMConfig *self,
                            GKeyFile *keyfile_intern_new,
                            gboolean allow_write,
                            gboolean force_rewrite);
 
 /* for main.c only */
-NMConfigCmdLineOptions *nm_config_cmd_line_options_new (void);
+NMConfigCmdLineOptions *nm_config_cmd_line_options_new (gboolean first_start);
 void                    nm_config_cmd_line_options_free (NMConfigCmdLineOptions *cli);
 void                    nm_config_cmd_line_options_add_to_entries (NMConfigCmdLineOptions *cli,
                                                                    GOptionContext *opt_ctx);
@@ -202,14 +206,12 @@ struct _NMConfigDeviceStateData {
 	const char *perm_hw_addr_fake;
 };
 
-NMConfigDeviceStateData *nm_config_device_state_load (NMConfig *self,
-                                                      int ifindex);
-gboolean nm_config_device_state_write (NMConfig *self,
-                                       int ifindex,
-                                       gboolean managed,
+NMConfigDeviceStateData *nm_config_device_state_load (int ifindex);
+gboolean nm_config_device_state_write (int ifindex,
+                                       NMConfigDeviceStateManagedType managed,
                                        const char *perm_hw_addr_fake,
                                        const char *connection_uuid);
-void nm_config_device_state_prune_unseen (NMConfig *self, GHashTable *seen_ifindexes);
+void nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes);
 
 /*****************************************************************************/
 
diff --git a/src/nm-connectivity.c b/src/nm-connectivity.c
index a33ec680..75bb7b63 100644
--- a/src/nm-connectivity.c
+++ b/src/nm-connectivity.c
@@ -17,6 +17,7 @@
  *
  * Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de>
  * Copyright (C) 2011 Dan Williams <dcbw@redhat.com>
+ * Copyright (C) 2016,2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -24,36 +25,21 @@
 #include "nm-connectivity.h"
 
 #include <string.h>
-#if WITH_CONCHECK
-#include <libsoup/soup.h>
-#endif
+#include <curl/curl.h>
 
 #include "nm-config.h"
-#include "nm-dispatcher.h"
 #include "NetworkManagerUtils.h"
 
 /*****************************************************************************/
 
-NM_GOBJECT_PROPERTIES_DEFINE (NMConnectivity,
-	PROP_URI,
-	PROP_INTERVAL,
-	PROP_RESPONSE,
-	PROP_STATE,
-);
-
 typedef struct {
 	char *uri;
 	char *response;
 	guint interval;
-	gboolean online; /* whether periodic connectivity checking is enabled. */
-
-#if WITH_CONCHECK
-	SoupSession *soup_session;
-	gboolean initial_check_obsoleted;
-	guint check_id;
-#endif
-
-	NMConnectivityState state;
+	NMConfig *config;
+	guint periodic_check_id;
+	CURLM *curl_mhandle;
+	guint curl_timer;
 } NMConnectivityPrivate;
 
 struct _NMConnectivity {
@@ -69,20 +55,37 @@ G_DEFINE_TYPE (NMConnectivity, nm_connectivity, G_TYPE_OBJECT)
 
 #define NM_CONNECTIVITY_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMConnectivity, NM_IS_CONNECTIVITY)
 
+NM_DEFINE_SINGLETON_GETTER (NMConnectivity, nm_connectivity_get, NM_TYPE_CONNECTIVITY);
+
+enum {
+	PERIODIC_CHECK,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 /*****************************************************************************/
 
 #define _NMLOG_DOMAIN      LOGD_CONCHECK
 #define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "connectivity", __VA_ARGS__)
 
-/*****************************************************************************/
-
-NMConnectivityState
-nm_connectivity_get_state (NMConnectivity *connectivity)
-{
-	g_return_val_if_fail (NM_IS_CONNECTIVITY (connectivity), NM_CONNECTIVITY_UNKNOWN);
+#define _NMLOG2_DOMAIN     LOGD_CONCHECK
+#define _NMLOG2(level, ...) \
+    G_STMT_START { \
+        const NMLogLevel __level = (level); \
+        \
+        if (nm_logging_enabled (__level, _NMLOG2_DOMAIN)) { \
+            _nm_log (__level, _NMLOG2_DOMAIN, 0, \
+                        &cb_data->ifspec[3], NULL, \
+                        "connectivity: (%s) " \
+                        _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+                        &cb_data->ifspec[3] \
+                        _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+        } \
+    } G_STMT_END
 
-	return NM_CONNECTIVITY_GET_PRIVATE (connectivity)->state;
-}
+/*****************************************************************************/
 
 NM_UTILS_LOOKUP_STR_DEFINE (nm_connectivity_state_to_string, NMConnectivityState,
 	NM_UTILS_LOOKUP_DEFAULT_WARN ("???"),
@@ -93,190 +96,245 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_connectivity_state_to_string, NMConnectivityState
 	NM_UTILS_LOOKUP_STR_ITEM (NM_CONNECTIVITY_FULL,     "FULL"),
 );
 
-static void
-update_state (NMConnectivity *self, NMConnectivityState state)
-{
-	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
-
-	if (priv->state != state) {
-		_LOGD ("state changed from %s to %s",
-		       nm_connectivity_state_to_string (priv->state),
-		       nm_connectivity_state_to_string (state));
-		priv->state = state;
-		_notify (self, PROP_STATE);
-
-		/* Notify dispatcher scripts of a connectivity state change */
-		nm_dispatcher_call_connectivity (DISPATCHER_ACTION_CONNECTIVITY_CHANGE, state);
-	}
-}
+/*****************************************************************************/
 
-#if WITH_CONCHECK
 typedef struct {
 	GSimpleAsyncResult *simple;
-	char *uri;
 	char *response;
-	guint check_id_when_scheduled;
+	CURL *curl_ehandle;
+	size_t msg_size;
+	char *msg;
+	struct curl_slist *request_headers;
+	guint timeout_id;
+	char *ifspec;
 } ConCheckCbData;
 
 static void
-nm_connectivity_check_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
+finish_cb_data (ConCheckCbData *cb_data, NMConnectivityState new_state)
 {
-	NMConnectivity *self;
-	NMConnectivityPrivate *priv;
-	ConCheckCbData *cb_data = user_data;
-	GSimpleAsyncResult *simple = cb_data->simple;
-	NMConnectivityState new_state;
-	const char *nm_header;
-	const char *uri = cb_data->uri;
-	const char *response = cb_data->response ? cb_data->response : NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE;
-
-	self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
-	/* it is safe to unref @self here, @simple holds yet another reference. */
-	g_object_unref (self);
-	priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+	/* Contrary to what cURL manual claim it is *not* safe to remove
+	 * the easy handle "at any moment"; specifically not from the
+	 * write function. Thus here we just dissociate the cb_data from
+	 * the easy handle and the easy handle will be cleaned up when the
+	 * message goes to CURLMSG_DONE in curl_check_connectivity(). */
+	curl_easy_setopt (cb_data->curl_ehandle, CURLOPT_PRIVATE, NULL);
+
+	g_simple_async_result_set_op_res_gssize (cb_data->simple, new_state);
+	g_simple_async_result_complete (cb_data->simple);
+	g_object_unref (cb_data->simple);
+	curl_slist_free_all (cb_data->request_headers);
+	g_free (cb_data->response);
+	g_source_remove (cb_data->timeout_id);
+	g_slice_free (ConCheckCbData, cb_data);
+}
 
-	if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
-		_LOGI ("check for uri '%s' failed with '%s'", uri, msg->reason_phrase);
-		new_state = NM_CONNECTIVITY_LIMITED;
-		goto done;
-	}
+static void
+curl_check_connectivity (CURLM *mhandle, CURLMcode ret)
+{
+	ConCheckCbData *cb_data;
+	CURLMsg *msg;
+	CURLcode eret;
+	gint m_left;
+
+	if (ret != CURLM_OK)
+		_LOGW ("connectivity check failed");
+
+	while ((msg = curl_multi_info_read (mhandle, &m_left))) {
+		if (msg->msg != CURLMSG_DONE)
+			continue;
+
+		/* Here we have completed a session. Check easy session result. */
+		eret = curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, &cb_data);
+		if (eret != CURLE_OK) {
+			_LOG2E ("curl cannot extract cb_data for easy handle %p, skipping msg", msg->easy_handle);
+			continue;
+		}
 
-	if (msg->status_code == 511) {
-		_LOGD ("check for uri '%s' returned status '%d %s'; captive portal present.",
-		       uri, msg->status_code, msg->reason_phrase);
-		new_state = NM_CONNECTIVITY_PORTAL;
-	} else {
-		/* Check headers; if we find the NM-specific one we're done */
-		nm_header = soup_message_headers_get_one (msg->response_headers, "X-NetworkManager-Status");
-		if (g_strcmp0 (nm_header, "online") == 0) {
-			_LOGD ("check for uri '%s' with Status header successful.", uri);
-			new_state = NM_CONNECTIVITY_FULL;
-		} else if (msg->status_code == SOUP_STATUS_OK) {
-			/* check response */
-			if (msg->response_body->data && g_str_has_prefix (msg->response_body->data, response)) {
-				_LOGD ("check for uri '%s' successful.", uri);
-				new_state = NM_CONNECTIVITY_FULL;
+		if (cb_data) {
+			/* If cb_data is still there this message hasn't been
+			 * taken care of. Do so now. */
+			if (msg->data.result == CURLE_OK) {
+				/* If we get here, it means that easy_write_cb() didn't read enough
+				 * bytes to be able to do a match. */
+				_LOG2I ("response shorter than expected '%s'; assuming captive portal.",
+				        cb_data->response);
+				finish_cb_data (cb_data, NM_CONNECTIVITY_PORTAL);
 			} else {
-				_LOGI ("check for uri '%s' did not match expected response '%s'; assuming captive portal.",
-					   uri, response);
-				new_state = NM_CONNECTIVITY_PORTAL;
+				_LOG2D ("check failed (%d)", msg->data.result);
+				finish_cb_data (cb_data, NM_CONNECTIVITY_LIMITED);
 			}
-		} else {
-			_LOGI ("check for uri '%s' returned status '%d %s'; assuming captive portal.",
-			       uri, msg->status_code, msg->reason_phrase);
-			new_state = NM_CONNECTIVITY_PORTAL;
 		}
-	}
 
- done:
-	/* Only update the state, if the call was done from external, or if the periodic check
-	 * is still the one that called this async check. */
-	if (!cb_data->check_id_when_scheduled || cb_data->check_id_when_scheduled == priv->check_id) {
-		/* Only update the state, if the URI and response parameters did not change
-		 * since invocation.
-		 * The interval does not matter for exernal calls, and for internal calls
-		 * we don't reach this line if the interval changed. */
-		if (   !g_strcmp0 (cb_data->uri, priv->uri)
-		    && !g_strcmp0 (cb_data->response, priv->response))
-			update_state (self, new_state);
+		curl_multi_remove_handle (mhandle, msg->easy_handle);
+		curl_easy_cleanup (msg->easy_handle);
 	}
+}
 
-	g_simple_async_result_set_op_res_gssize (simple, new_state);
-	g_simple_async_result_complete (simple);
-	g_object_unref (simple);
+static gboolean
+curl_timeout_cb (gpointer user_data)
+{
+	NMConnectivity *self = NM_CONNECTIVITY (user_data);
+	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+	CURLMcode ret;
+	int pending_conn;
 
-	g_free (cb_data->uri);
-	g_free (cb_data->response);
-	g_slice_free (ConCheckCbData, cb_data);
-}
+	priv->curl_timer = 0;
 
-#define IS_PERIODIC_CHECK(callback)  (callback == run_check_complete)
+	ret = curl_multi_socket_action (priv->curl_mhandle, CURL_SOCKET_TIMEOUT, 0, &pending_conn);
+	_LOGT ("timeout elapsed - multi_socket_action (%d conn remaining)", pending_conn);
 
-static void
-run_check_complete (GObject      *object,
-                    GAsyncResult *result,
-                    gpointer      user_data)
-{
-	NMConnectivity *self = NM_CONNECTIVITY (object);
-	GError *error = NULL;
+	curl_check_connectivity (priv->curl_mhandle, ret);
 
-	nm_connectivity_check_finish (self, result, &error);
-	if (error) {
-		_LOGE ("check failed: %s", error->message);
-		g_error_free (error);
-	}
+	return G_SOURCE_REMOVE;
 }
 
-static gboolean
-run_check (gpointer user_data)
+static int
+multi_timer_cb (CURLM *multi, long timeout_ms, void *userdata)
 {
-	NMConnectivity *self = NM_CONNECTIVITY (user_data);
+	NMConnectivity *self = NM_CONNECTIVITY (userdata);
+	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
 
-	nm_connectivity_check_async (self, run_check_complete, NULL);
-	return TRUE;
+	nm_clear_g_source (&priv->curl_timer);
+	if (timeout_ms != -1)
+		priv->curl_timer = g_timeout_add (timeout_ms * 1000, curl_timeout_cb, self);
+
+	return 0;
 }
 
 static gboolean
-idle_start_periodic_checks (gpointer user_data)
+curl_socketevent_cb (GIOChannel *ch, GIOCondition condition, gpointer data)
 {
-	NMConnectivity *self = user_data;
+	NMConnectivity *self = NM_CONNECTIVITY (data);
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+	CURLMcode ret;
+	int pending_conn = 0;
+	gboolean bret = TRUE;
+	int fd = g_io_channel_unix_get_fd (ch);
+	int action = 0;
+
+	if (condition & G_IO_IN)
+		action |= CURL_CSELECT_IN;
+	if (condition & G_IO_OUT)
+		action |= CURL_CSELECT_OUT;
 
-	priv->check_id = g_timeout_add_seconds (priv->interval, run_check, self);
-	if (!priv->initial_check_obsoleted)
-		run_check (self);
+	ret = curl_multi_socket_action (priv->curl_mhandle, fd, 0, &pending_conn);
 
-	return FALSE;
+	curl_check_connectivity (priv->curl_mhandle, ret);
+
+	if (pending_conn == 0) {
+		nm_clear_g_source (&priv->curl_timer);
+		bret = FALSE;
+	}
+	return bret;
 }
-#endif
 
-static void
-_reschedule_periodic_checks (NMConnectivity *self, gboolean force_reschedule)
+typedef struct {
+	GIOChannel *ch;
+	guint ev;
+} CurlSockData;
+
+static int
+multi_socket_cb (CURL *e_handle, curl_socket_t s, int what, void *userdata, void *socketp)
 {
+	NMConnectivity *self = NM_CONNECTIVITY (userdata);
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
-
-#if WITH_CONCHECK
-	if (priv->online && priv->uri && priv->interval) {
-		if (force_reschedule || !priv->check_id) {
-			if (priv->check_id)
-				g_source_remove (priv->check_id);
-			priv->check_id = g_timeout_add (0, idle_start_periodic_checks, self);
-			priv->initial_check_obsoleted = FALSE;
+	CurlSockData *fdp = (CurlSockData *) socketp;
+	GIOCondition condition = 0;
+
+	if (what == CURL_POLL_REMOVE) {
+		if (fdp) {
+			nm_clear_g_source (&fdp->ev);
+			g_io_channel_unref (fdp->ch);
+			g_slice_free (CurlSockData, fdp);
 		}
 	} else {
-		nm_clear_g_source (&priv->check_id);
+		if (!fdp) {
+			fdp = g_slice_new0 (CurlSockData);
+			fdp->ch = g_io_channel_unix_new (s);
+		} else
+			nm_clear_g_source (&fdp->ev);
+
+		if (what & CURL_POLL_IN)
+			condition |= G_IO_IN;
+		if (what & CURL_POLL_OUT)
+			condition |= G_IO_OUT;
+
+		fdp->ev = g_io_add_watch (fdp->ch, condition, curl_socketevent_cb, self);
+		curl_multi_assign (priv->curl_mhandle, s, fdp);
 	}
-	if (priv->check_id)
-		return;
-#endif
 
-	/* Either @online is %TRUE but we aren't checking connectivity, or
-	 * @online is %FALSE. Either way we can update our status immediately.
-	 */
-	update_state (self, priv->online ? NM_CONNECTIVITY_FULL : NM_CONNECTIVITY_NONE);
+	return CURLM_OK;
 }
 
-void
-nm_connectivity_set_online (NMConnectivity *self,
-                            gboolean        online)
+#define HEADER_STATUS_ONLINE "X-NetworkManager-Status: online\r\n"
+
+static size_t
+easy_header_cb (char *buffer, size_t size, size_t nitems, void *userdata)
 {
-	NMConnectivityPrivate *priv= NM_CONNECTIVITY_GET_PRIVATE (self);
+	ConCheckCbData *cb_data = userdata;
+	size_t len = size * nitems;
+
+	if (   len >= sizeof (HEADER_STATUS_ONLINE) - 1
+	    && !g_ascii_strncasecmp (buffer, HEADER_STATUS_ONLINE, sizeof (HEADER_STATUS_ONLINE) - 1)) {
+		_LOG2D ("status header found, check successful");
+		finish_cb_data (cb_data, NM_CONNECTIVITY_FULL);
+		return 0;
+	}
+
+	return len;
+}
 
-	online = !!online;
-	if (priv->online != online) {
-		_LOGD ("set %s", online ? "online" : "offline");
-		priv->online = online;
-		_reschedule_periodic_checks (self, FALSE);
+static size_t
+easy_write_cb (void *buffer, size_t size, size_t nmemb, void *userdata)
+{
+	ConCheckCbData *cb_data = userdata;
+	size_t len = size * nmemb;
+
+	cb_data->msg = g_realloc (cb_data->msg, cb_data->msg_size + len);
+	memcpy (cb_data->msg + cb_data->msg_size, buffer, len);
+	cb_data->msg_size += len;
+
+	if (cb_data->msg_size >= strlen (cb_data->response)) {
+		/* We already have enough data -- check response */
+		if (g_str_has_prefix (cb_data->msg, cb_data->response)) {
+			_LOG2D ("check successful.");
+			finish_cb_data (cb_data, NM_CONNECTIVITY_FULL);
+		} else {
+			_LOG2I ("response did not match expected response '%s'; assuming captive portal.",
+			        cb_data->response);
+			finish_cb_data (cb_data, NM_CONNECTIVITY_PORTAL);
+		}
+		return 0;
 	}
+
+	return len;
+}
+
+static gboolean
+timeout_cb (gpointer user_data)
+{
+	ConCheckCbData *cb_data = user_data;
+	NMConnectivity *self = NM_CONNECTIVITY (g_async_result_get_source_object (G_ASYNC_RESULT (cb_data->simple)));
+	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+	CURL *ehandle = cb_data->curl_ehandle;
+
+	_LOG2I ("timed out");
+	finish_cb_data (cb_data, NM_CONNECTIVITY_LIMITED);
+	curl_multi_remove_handle (priv->curl_mhandle, ehandle);
+	curl_easy_cleanup (ehandle);
+
+	return G_SOURCE_REMOVE;
 }
 
 void
 nm_connectivity_check_async (NMConnectivity      *self,
+                             const char          *iface,
                              GAsyncReadyCallback  callback,
                              gpointer             user_data)
 {
 	NMConnectivityPrivate *priv;
 	GSimpleAsyncResult *simple;
+	CURL *ehandle = NULL;
 
 	g_return_if_fail (NM_IS_CONNECTIVITY (self));
 	priv = NM_CONNECTIVITY_GET_PRIVATE (self);
@@ -284,39 +342,40 @@ nm_connectivity_check_async (NMConnectivity      *self,
 	simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
 	                                    nm_connectivity_check_async);
 
-#if WITH_CONCHECK
-	if (priv->uri && priv->interval) {
-		SoupMessage *msg;
-		ConCheckCbData *cb_data = g_slice_new (ConCheckCbData);
+	if (priv->uri && priv->interval && priv->curl_mhandle)
+		ehandle = curl_easy_init ();
+
+	if (ehandle) {
+		ConCheckCbData *cb_data = g_slice_new0 (ConCheckCbData);
 
-		msg = soup_message_new ("GET", priv->uri);
-		soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
-		/* Disable HTTP/1.1 keepalive; the connection should not persist */
-		soup_message_headers_append (msg->request_headers, "Connection", "close");
+		cb_data->curl_ehandle = ehandle;
+		cb_data->request_headers = curl_slist_append (NULL, "Connection: close");
+		cb_data->ifspec = g_strdup_printf ("if!%s", iface);
 		cb_data->simple = simple;
-		cb_data->uri = g_strdup (priv->uri);
-		cb_data->response = g_strdup (priv->response);
+		if (priv->response)
+			cb_data->response = g_strdup (priv->response);
+		else
+			cb_data->response = g_strdup (NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE);
 
-		/* For internal calls (periodic), remember the check-id at time of scheduling. */
-		cb_data->check_id_when_scheduled = IS_PERIODIC_CHECK (callback) ? priv->check_id : 0;
+		curl_easy_setopt (ehandle, CURLOPT_URL, priv->uri);
+		curl_easy_setopt (ehandle, CURLOPT_WRITEFUNCTION, easy_write_cb);
+		curl_easy_setopt (ehandle, CURLOPT_WRITEDATA, cb_data);
+		curl_easy_setopt (ehandle, CURLOPT_HEADERFUNCTION, easy_header_cb);
+		curl_easy_setopt (ehandle, CURLOPT_HEADERDATA, cb_data);
+		curl_easy_setopt (ehandle, CURLOPT_PRIVATE, cb_data);
+		curl_easy_setopt (ehandle, CURLOPT_HTTPHEADER, cb_data->request_headers);
+		curl_easy_setopt (ehandle, CURLOPT_INTERFACE, cb_data->ifspec);
+		curl_multi_add_handle (priv->curl_mhandle, ehandle);
 
-		soup_session_queue_message (priv->soup_session,
-		                            msg,
-		                            nm_connectivity_check_cb,
-		                            cb_data);
-		priv->initial_check_obsoleted = TRUE;
+		cb_data->timeout_id = g_timeout_add_seconds (30, timeout_cb, cb_data);
 
-		_LOGD ("check: send %srequest to '%s'", IS_PERIODIC_CHECK (callback) ? "periodic " : "", priv->uri);
+		_LOG2D ("sending request to '%s'", priv->uri);
 		return;
 	} else {
-		g_warn_if_fail (!IS_PERIODIC_CHECK (callback));
-		_LOGD ("check: faking request. Connectivity check disabled");
+		_LOGD ("(%s) faking request. Connectivity check disabled", iface);
 	}
-#else
-	_LOGD ("check: faking request. Compiled without connectivity-check support");
-#endif
 
-	g_simple_async_result_set_op_res_gssize (simple, priv->state);
+	g_simple_async_result_set_op_res_gssize (simple, NM_CONNECTIVITY_UNKNOWN);
 	g_simple_async_result_complete_in_idle (simple);
 	g_object_unref (simple);
 }
@@ -336,120 +395,118 @@ nm_connectivity_check_finish (NMConnectivity  *self,
 	return (NMConnectivityState) g_simple_async_result_get_op_res_gssize (simple);
 }
 
-/*****************************************************************************/
-
-static void
-get_property (GObject *object, guint property_id,
-              GValue *value, GParamSpec *pspec)
+gboolean
+nm_connectivity_check_enabled (NMConnectivity *self)
 {
-	NMConnectivity *self = NM_CONNECTIVITY (object);
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
 
-	switch (property_id) {
-	case PROP_URI:
-		g_value_set_string (value, priv->uri);
-		break;
-	case PROP_INTERVAL:
-		g_value_set_uint (value, priv->interval);
-		break;
-	case PROP_RESPONSE:
-		if (priv->response)
-			g_value_set_string (value, priv->response);
-		else
-			g_value_set_static_string (value, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE);
-		break;
-	case PROP_STATE:
-		g_value_set_uint (value, priv->state);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-		break;
-	}
+	return (priv->uri && priv->interval && priv->curl_mhandle);
+}
+
+/*****************************************************************************/
+
+static gboolean
+periodic_check (gpointer user_data)
+{
+	g_signal_emit (NM_CONNECTIVITY (user_data), signals[PERIODIC_CHECK], 0);
+	return G_SOURCE_CONTINUE;
 }
 
 static void
-set_property (GObject *object, guint property_id,
-              const GValue *value, GParamSpec *pspec)
+update_config (NMConnectivity *self, NMConfigData *config_data)
 {
-	NMConnectivity *self = NM_CONNECTIVITY (object);
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
 	const char *uri, *response;
 	guint interval;
-	gboolean changed;
-
-	switch (property_id) {
-	case PROP_URI:
-		uri = g_value_get_string (value);
-		if (uri && !*uri)
+	gboolean changed = FALSE;
+
+	/* Set the URI. */
+	uri = nm_config_data_get_connectivity_uri (config_data);
+	if (uri && !*uri)
+		uri = NULL;
+	changed = g_strcmp0 (uri, priv->uri) != 0;
+	if (uri) {
+		char *scheme = g_uri_parse_scheme (uri);
+
+		if (!scheme) {
+			_LOGE ("invalid URI '%s' for connectivity check.", uri);
+			uri = NULL;
+		} else if (strcasecmp (scheme, "https") == 0) {
+			_LOGW ("use of HTTPS for connectivity checking is not reliable and is discouraged (URI: %s)", uri);
+		} else if (strcasecmp (scheme, "http") != 0) {
+			_LOGE ("scheme of '%s' uri does't use a scheme that is allowed for connectivity check.", uri);
 			uri = NULL;
-		changed = g_strcmp0 (uri, priv->uri) != 0;
-#if WITH_CONCHECK
-		if (uri) {
-			SoupURI *soup_uri = soup_uri_new (uri);
-
-			if (!soup_uri || !SOUP_URI_VALID_FOR_HTTP (soup_uri)) {
-				_LOGE ("invalid uri '%s' for connectivity check.", uri);
-				uri = NULL;
-			}
-			if (uri && soup_uri && changed &&
-			    soup_uri_get_scheme(soup_uri) == SOUP_URI_SCHEME_HTTPS)
-				_LOGW ("use of HTTPS for connectivity checking is not reliable and is discouraged (URI: %s)", uri);
-			if (soup_uri)
-				soup_uri_free (soup_uri);
-		}
-#endif
-		if (changed) {
-			g_free (priv->uri);
-			priv->uri = g_strdup (uri);
-			_reschedule_periodic_checks (self, TRUE);
-		}
-		break;
-	case PROP_INTERVAL:
-		interval = g_value_get_uint (value);
-		if (priv->interval != interval) {
-			priv->interval = interval;
-			_reschedule_periodic_checks (self, TRUE);
-		}
-		break;
-	case PROP_RESPONSE:
-		response = g_value_get_string (value);
-		if (g_strcmp0 (response, priv->response) != 0) {
-			/* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response
-			 * (including "") is accepted. */
-			g_free (priv->response);
-			priv->response = g_strdup (response);
-			_reschedule_periodic_checks (self, TRUE);
 		}
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-		break;
+
+		if (scheme)
+			g_free (scheme);
+	}
+	if (changed) {
+		g_free (priv->uri);
+		priv->uri = g_strdup (uri);
+	}
+
+	/* Set the interval. */
+	interval = nm_config_data_get_connectivity_interval (config_data);
+	if (priv->interval != interval) {
+		priv->interval = interval;
+		changed = TRUE;
+	}
+
+	/* Set the response. */
+	response = nm_config_data_get_connectivity_response (config_data);
+	if (g_strcmp0 (response, priv->response) != 0) {
+		/* a response %NULL means, NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE. Any other response
+		 * (including "") is accepted. */
+		g_free (priv->response);
+		priv->response = g_strdup (response);
+		changed = TRUE;
+	}
+
+	if (changed) {
+		nm_clear_g_source (&priv->periodic_check_id);
+		if (nm_connectivity_check_enabled (self))
+			priv->periodic_check_id = g_timeout_add_seconds (priv->interval, periodic_check, self);
 	}
 }
 
-/*****************************************************************************/
+static void
+config_changed_cb (NMConfig *config,
+                   NMConfigData *config_data,
+                   NMConfigChangeFlags changes,
+                   NMConfigData *old_data,
+                   NMConnectivity *self)
+{
+	update_config (self, config_data);
+}
 
 static void
 nm_connectivity_init (NMConnectivity *self)
 {
 	NMConnectivityPrivate *priv = NM_CONNECTIVITY_GET_PRIVATE (self);
+	CURLcode retv;
 
-#if WITH_CONCHECK
-	priv->soup_session = soup_session_async_new_with_options (SOUP_SESSION_TIMEOUT, 15, NULL);
-#endif
-	priv->state = NM_CONNECTIVITY_NONE;
-}
+	priv->config = g_object_ref (nm_config_get ());
+	update_config (self, nm_config_get_data (priv->config));
+	g_signal_connect (G_OBJECT (priv->config),
+	                  NM_CONFIG_SIGNAL_CONFIG_CHANGED,
+	                  G_CALLBACK (config_changed_cb),
+	                  self);
 
-NMConnectivity *
-nm_connectivity_new (const char *uri,
-                     guint interval,
-                     const char *response)
-{
-	return g_object_new (NM_TYPE_CONNECTIVITY,
-	                     NM_CONNECTIVITY_URI, uri,
-	                     NM_CONNECTIVITY_INTERVAL, interval,
-	                     NM_CONNECTIVITY_RESPONSE, response,
-	                     NULL);
+	retv = curl_global_init (CURL_GLOBAL_ALL);
+	if (retv == CURLE_OK)
+		priv->curl_mhandle = curl_multi_init ();
+
+	if (priv->curl_mhandle == NULL) {
+		 _LOGE ("cnable to init cURL, connectivity check will not work");
+		return;
+	}
+
+	curl_multi_setopt (priv->curl_mhandle, CURLMOPT_SOCKETFUNCTION, multi_socket_cb);
+	curl_multi_setopt (priv->curl_mhandle, CURLMOPT_SOCKETDATA, self);
+	curl_multi_setopt (priv->curl_mhandle, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
+	curl_multi_setopt (priv->curl_mhandle, CURLMOPT_TIMERDATA, self);
+	curl_multi_setopt (priv->curl_mhandle, CURLOPT_VERBOSE, 1);
 }
 
 static void
@@ -461,14 +518,14 @@ dispose (GObject *object)
 	g_clear_pointer (&priv->uri, g_free);
 	g_clear_pointer (&priv->response, g_free);
 
-#if WITH_CONCHECK
-	if (priv->soup_session) {
-		soup_session_abort (priv->soup_session);
-		g_clear_object (&priv->soup_session);
+	if (priv->config) {
+		g_signal_handlers_disconnect_by_func (priv->config, config_changed_cb, self);
+		g_clear_object (&priv->config);
 	}
 
-	nm_clear_g_source (&priv->check_id);
-#endif
+	curl_multi_cleanup (priv->curl_mhandle);
+	curl_global_cleanup ();
+	nm_clear_g_source (&priv->periodic_check_id);
 
 	G_OBJECT_CLASS (nm_connectivity_parent_class)->dispose (object);
 }
@@ -478,37 +535,12 @@ nm_connectivity_class_init (NMConnectivityClass *klass)
 {
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-	object_class->set_property = set_property;
-	object_class->get_property = get_property;
-	object_class->dispose = dispose;
+	signals[PERIODIC_CHECK] =
+	    g_signal_new (NM_CONNECTIVITY_PERIODIC_CHECK,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 0);
 
-	obj_properties[PROP_URI] =
-	     g_param_spec_string (NM_CONNECTIVITY_URI, "", "",
-	                          NULL,
-	                          G_PARAM_READWRITE |
-	                          G_PARAM_CONSTRUCT |
-	                          G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_INTERVAL] =
-	     g_param_spec_uint (NM_CONNECTIVITY_INTERVAL, "", "",
-	                        0, G_MAXUINT, NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL,
-	                        G_PARAM_READWRITE |
-	                        G_PARAM_CONSTRUCT |
-	                        G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_RESPONSE] =
-	     g_param_spec_string (NM_CONNECTIVITY_RESPONSE, "", "",
-	                          NM_CONFIG_DEFAULT_CONNECTIVITY_RESPONSE,
-	                          G_PARAM_READWRITE |
-	                          G_PARAM_CONSTRUCT |
-	                          G_PARAM_STATIC_STRINGS);
-
-	obj_properties[PROP_STATE] =
-	     g_param_spec_uint (NM_CONNECTIVITY_STATE, "", "",
-	                        NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN,
-	                        G_PARAM_READABLE |
-	                        G_PARAM_STATIC_STRINGS);
-
-	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+	object_class->dispose = dispose;
 }
-
diff --git a/src/nm-connectivity.h b/src/nm-connectivity.h
index 6900bd0f..d9a9f233 100644
--- a/src/nm-connectivity.h
+++ b/src/nm-connectivity.h
@@ -16,6 +16,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2011 Thomas Bechtold <thomasbechtold@jpberlin.de>
+ * Copyright (C) 2017 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_CONNECTIVITY_H__
@@ -30,31 +31,23 @@
 #define NM_IS_CONNECTIVITY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_CONNECTIVITY))
 #define NM_CONNECTIVITY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_CONNECTIVITY, NMConnectivityClass))
 
-#define NM_CONNECTIVITY_URI       "uri"
-#define NM_CONNECTIVITY_INTERVAL  "interval"
-#define NM_CONNECTIVITY_RESPONSE  "response"
-#define NM_CONNECTIVITY_STATE     "state"
+#define NM_CONNECTIVITY_PERIODIC_CHECK  "nm-connectivity-periodic-check"
 
 typedef struct _NMConnectivityClass NMConnectivityClass;
 
 GType nm_connectivity_get_type (void);
 
-const char *nm_connectivity_state_to_string (NMConnectivityState state);
-
-NMConnectivity      *nm_connectivity_new (const char *uri,
-                                          guint interval,
-                                          const char *response);
+NMConnectivity *nm_connectivity_get (void);
 
-void                 nm_connectivity_set_online   (NMConnectivity       *self,
-                                                   gboolean              online);
-
-NMConnectivityState  nm_connectivity_get_state    (NMConnectivity       *self);
+const char *nm_connectivity_state_to_string (NMConnectivityState state);
 
 void                 nm_connectivity_check_async  (NMConnectivity       *self,
+                                                   const char           *iface,
                                                    GAsyncReadyCallback   callback,
                                                    gpointer              user_data);
 NMConnectivityState  nm_connectivity_check_finish (NMConnectivity       *self,
                                                    GAsyncResult         *result,
                                                    GError              **error);
+gboolean nm_connectivity_check_enabled (NMConnectivity *self);
 
 #endif /* __NETWORKMANAGER_CONNECTIVITY_H__ */
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index c9bb220c..aaaf7b6c 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -110,6 +110,10 @@ _nm_utils_set_testing (NMUtilsTestFlags flags)
 
 /*****************************************************************************/
 
+const NMIPAddr nm_ip_addr_zero = NMIPAddrInit;
+
+/*****************************************************************************/
+
 static GSList *_singletons = NULL;
 static gboolean _singletons_shutdown = FALSE;
 
@@ -155,6 +159,37 @@ _nm_singleton_instance_register_destruction (GObject *instance)
 
 /*****************************************************************************/
 
+static double
+_exp10 (guint16 ex)
+{
+	double v;
+
+	if (ex == 0)
+		return 1.0;
+
+	v = _exp10 (ex / 2);
+	v = v * v;
+	if (ex % 2)
+		v *= 10;
+	return v;
+}
+
+/*
+ * nm_utils_exp10:
+ * @ex: the exponent
+ *
+ * Returns: 10^ex, or pow(10, ex), or exp10(ex).
+ */
+double
+nm_utils_exp10 (gint16 ex)
+{
+	if (ex >= 0)
+		return _exp10 (ex);
+	return 1.0 / _exp10 (- ((gint32) ex));
+}
+
+/*****************************************************************************/
+
 /*
  * nm_ethernet_address_is_valid:
  * @addr: pointer to a binary or ASCII Ethernet address
@@ -367,30 +402,6 @@ nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete,
 	g_array_set_size (array, res_length);
 }
 
-int
-nm_spawn_process (const char *args, GError **error)
-{
-	GError *local = NULL;
-	gint num_args;
-	char **argv = NULL;
-	int status = -1;
-
-	g_return_val_if_fail (args != NULL, -1);
-	g_return_val_if_fail (!error || !*error, -1);
-
-	if (g_shell_parse_argv (args, &num_args, &argv, &local)) {
-		g_spawn_sync ("/", argv, NULL, 0, NULL, NULL, NULL, NULL, &status, &local);
-		g_strfreev (argv);
-	}
-
-	if (local) {
-		nm_log_warn (LOGD_CORE, "could not spawn process '%s': %s", args, local->message);
-		g_propagate_error (error, local);
-	}
-
-	return status;
-}
-
 static const char *
 _trunk_first_line (char *str)
 {
@@ -431,11 +442,11 @@ nm_utils_modprobe (GError **error, gboolean suppress_error_logging, const char *
 
 	nm_log_dbg (LOGD_CORE, "modprobe: '%s'", ARGV_TO_STR (argv));
 	if (!g_spawn_sync (NULL, (char **) argv->pdata, NULL, 0, NULL, NULL, &std_out, &std_err, &exit_status, &local)) {
-		nm_log (llevel, LOGD_CORE, "modprobe: '%s' failed: %s", ARGV_TO_STR (argv), local->message);
+		nm_log (llevel, LOGD_CORE, NULL, NULL, "modprobe: '%s' failed: %s", ARGV_TO_STR (argv), local->message);
 		g_propagate_error (error, local);
 		return -1;
 	} else if (exit_status != 0) {
-		nm_log (llevel, LOGD_CORE, "modprobe: '%s' exited with error %d%s%s%s%s%s%s", ARGV_TO_STR (argv), exit_status,
+		nm_log (llevel, LOGD_CORE, NULL, NULL, "modprobe: '%s' exited with error %d%s%s%s%s%s%s", ARGV_TO_STR (argv), exit_status,
 		        std_out&&*std_out ? " (" : "", std_out&&*std_out ? _trunk_first_line (std_out) : "", std_out&&*std_out ? ")" : "",
 		        std_err&&*std_err ? " (" : "", std_err&&*std_err ? _trunk_first_line (std_err) : "", std_err&&*std_err ? ")" : "");
 	}
@@ -1000,7 +1011,7 @@ nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, NMLogDomain
 	}
 	if (start_time != 0 && start_time != start_time0) {
 		nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": don't kill process %ld because the start_time is unexpectedly %lu instead of %ld",
-		            LOG_NAME_ARGS, (long int) pid, (long unsigned) start_time0, (long unsigned) start_time);
+		            LOG_NAME_ARGS, (long int) pid, (unsigned long) start_time0, (unsigned long) start_time);
 		return;
 	}
 
@@ -1206,6 +1217,7 @@ nm_utils_read_link_absolute (const char *link_file, GError **error)
 #define MAC_TAG "mac:"
 #define INTERFACE_NAME_TAG "interface-name:"
 #define DEVICE_TYPE_TAG "type:"
+#define DRIVER_TAG "driver:"
 #define SUBCHAN_TAG "s390-subchannels:"
 #define EXCEPT_TAG "except:"
 #define MATCH_TAG_CONFIG_NM_VERSION             "nm-version:"
@@ -1216,6 +1228,8 @@ nm_utils_read_link_absolute (const char *link_file, GError **error)
 typedef struct {
 	const char *interface_name;
 	const char *device_type;
+	const char *driver;
+	const char *driver_version;
 	struct {
 		const char *value;
 		gboolean is_parsed;
@@ -1398,6 +1412,38 @@ match_device_eval (const char *spec_str,
 		return FALSE;
 	}
 
+	if (_MATCH_CHECK (spec_str, DRIVER_TAG)) {
+		const char *t;
+
+		if (!match_data->driver)
+			return FALSE;
+
+		/* support:
+		 * 1) "${DRIVER}"
+		 *   In this case, DRIVER may not contain a '/' character.
+		 *   It matches any driver version.
+		 * 2) "${DRIVER}/${DRIVER_VERSION}"
+		 *   In this case, DRIVER may contains '/' but DRIVER_VERSION
+		 *   may not. A '/' in DRIVER_VERSION may be replaced by '?'.
+		 *
+		 * It follows, that "${DRIVER}/""*" is like 1), but allows
+		 * '/' inside DRIVER.
+		 *
+		 * The fields match to what `nmcli -f GENERAL.DRIVER,GENERAL.DRIVER-VERSION device show`
+		 * gives. However, DRIVER matches literally, while DRIVER_VERSION is a glob
+		 * supporting ? and *.
+		 */
+
+		t = strrchr (spec_str, '/');
+
+		if (!t)
+			return nm_streq (spec_str, match_data->driver);
+
+		return    (strncmp (spec_str, match_data->driver, t - spec_str) == 0)
+		       && g_pattern_match_simple (&t[1],
+		                                  match_data->driver_version ?: "");
+	}
+
 	if (_MATCH_CHECK (spec_str, SUBCHAN_TAG))
 		return match_data_s390_subchannels_eval (spec_str, match_data);
 
@@ -1416,6 +1462,8 @@ NMMatchSpecMatchType
 nm_match_spec_device (const GSList *specs,
                       const char *interface_name,
                       const char *device_type,
+                      const char *driver,
+                      const char *driver_version,
                       const char *hwaddr,
                       const char *s390_subchannels)
 {
@@ -1426,6 +1474,8 @@ nm_match_spec_device (const GSList *specs,
 	MatchDeviceData match_data = {
 	    .interface_name = interface_name,
 	    .device_type = nm_str_not_empty (device_type),
+	    .driver = nm_str_not_empty (driver),
+	    .driver_version = nm_str_not_empty (driver_version),
 	    .hwaddr = {
 	        .value = hwaddr,
 	    },
@@ -1588,7 +1638,7 @@ nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env
  * @value: the string of device specs
  *
  * Splits the specs from the string and returns them as individual
- * entires in a #GSList.
+ * entries in a #GSList.
  *
  * It does not validate any specs, it basically just does a special
  * strsplit with ',' or ';' as separators and supporting '\\' as
@@ -1999,27 +2049,57 @@ nm_utils_read_resolv_conf_dns_options (const char *rc_contents)
 	return options;
 }
 
+/*****************************************************************************/
+
+/**
+ * nm_utils_cmp_connection_by_autoconnect_priority:
+ * @a:
+ * @b:
+ *
+ * compare connections @a and @b for their autoconnect property
+ * (with sorting the connection that has autoconnect enabled before
+ * the other)
+ * If they both have autoconnect enabled, sort them depending on their
+ * autoconnect-priority (with the higher priority first).
+ *
+ * If their autoconnect/autoconnect-priority is the same, 0 is returned.
+ * That is, they compare equal.
+ *
+ * Returns: -1, 0, or 1
+ */
 int
-nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnection **b)
+nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection *b)
 {
-	NMSettingConnection *a_s_con, *b_s_con;
-	gboolean a_ac, b_ac;
-	gint a_ap, b_ap;
-
-	a_s_con = nm_connection_get_setting_connection (*a);
-	b_s_con = nm_connection_get_setting_connection (*b);
-
-	a_ac = !!nm_setting_connection_get_autoconnect (a_s_con);
-	b_ac = !!nm_setting_connection_get_autoconnect (b_s_con);
-	if (a_ac != b_ac)
-		return ((int) b_ac) - ((int) a_ac);
-	if (!a_ac)
+	NMSettingConnection *a_s_con;
+	NMSettingConnection *b_s_con;
+	int a_ap, b_ap;
+	gboolean can_autoconnect;
+
+	if (a == b)
 		return 0;
+	if (!a)
+		return 1;
+	if (!b)
+		return -1;
+
+	a_s_con = nm_connection_get_setting_connection (a);
+	b_s_con = nm_connection_get_setting_connection (b);
 
-	a_ap = nm_setting_connection_get_autoconnect_priority (a_s_con);
-	b_ap = nm_setting_connection_get_autoconnect_priority (b_s_con);
-	if (a_ap != b_ap)
-		return (a_ap > b_ap) ? -1 : 1;
+	if (!a_s_con)
+		return !b_s_con ? 0 : 1;
+	if (!b_s_con)
+		return -1;
+
+	can_autoconnect = !!nm_setting_connection_get_autoconnect (a_s_con);
+	if (can_autoconnect != (!!nm_setting_connection_get_autoconnect (b_s_con)))
+		return can_autoconnect ? -1 : 1;
+
+	if (can_autoconnect) {
+		a_ap = nm_setting_connection_get_autoconnect_priority (a_s_con);
+		b_ap = nm_setting_connection_get_autoconnect_priority (b_s_con);
+		if (a_ap != b_ap)
+			return (a_ap > b_ap) ? -1 : 1;
+	}
 
 	return 0;
 }
@@ -2355,9 +2435,9 @@ nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base,
 	connection_diff_are_same = nm_connection_diff (connection, diff_base, NM_SETTING_COMPARE_FLAG_EXACT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT, &connection_diff);
 	if (connection_diff_are_same) {
 		if (diff_base)
-			nm_log (level, domain, "%sconnection '%s' (%p/%s and %p/%s): no difference", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), diff_base, G_OBJECT_TYPE_NAME (diff_base));
+			nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s and %p/%s): no difference", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), diff_base, G_OBJECT_TYPE_NAME (diff_base));
 		else
-			nm_log (level, domain, "%sconnection '%s' (%p/%s): no properties set", prefix, name, connection, G_OBJECT_TYPE_NAME (connection));
+			nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s): no properties set", prefix, name, connection, G_OBJECT_TYPE_NAME (connection));
 		g_assert (!connection_diff);
 		return;
 	}
@@ -2393,16 +2473,16 @@ nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base,
 				const char *path = nm_connection_get_path (connection);
 
 				if (diff_base) {
-					nm_log (level, domain, "%sconnection '%s' (%p/%s < %p/%s)%s%s%s:", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), diff_base, G_OBJECT_TYPE_NAME (diff_base),
+					nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s < %p/%s)%s%s%s:", prefix, name, connection, G_OBJECT_TYPE_NAME (connection), diff_base, G_OBJECT_TYPE_NAME (diff_base),
 					        NM_PRINT_FMT_QUOTED (path, " [", path, "]", ""));
 				} else {
-					nm_log (level, domain, "%sconnection '%s' (%p/%s):%s%s%s", prefix, name, connection, G_OBJECT_TYPE_NAME (connection),
+					nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s):%s%s%s", prefix, name, connection, G_OBJECT_TYPE_NAME (connection),
 					        NM_PRINT_FMT_QUOTED (path, " [", path, "]", ""));
 				}
 				print_header = FALSE;
 
 				if (!nm_connection_verify (connection, &err_verify)) {
-					nm_log (level, domain, "%sconnection %p does not verify: %s", prefix, connection, err_verify->message);
+					nm_log (level, domain, NULL, NULL, "%sconnection %p does not verify: %s", prefix, connection, err_verify->message);
 					g_clear_error (&err_verify);
 				}
 			}
@@ -2415,21 +2495,21 @@ nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base,
 						g_string_printf (str1, "*missing* < %p", setting_data->diff_base_setting);
 					else
 						g_string_printf (str1, "%p < *missing*", setting_data->setting);
-					nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s [ %s ]", prefix, setting_data->name, str1->str);
+					nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s [ %s ]", prefix, setting_data->name, str1->str);
 				} else
-					nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s [ %p ]", prefix, setting_data->name, setting_data->setting);
+					nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s [ %p ]", prefix, setting_data->name, setting_data->setting);
 				print_setting_header = FALSE;
 			}
 			g_string_printf (str1, "%s.%s", setting_data->name, item->item_name);
 			switch (item->diff_result & (NM_SETTING_DIFF_RESULT_IN_A | NM_SETTING_DIFF_RESULT_IN_B)) {
 				case NM_SETTING_DIFF_RESULT_IN_B:
-					nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s < %s", prefix, str1->str, str_diff ? str_diff : "NULL");
+					nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s < %s", prefix, str1->str, str_diff ? str_diff : "NULL");
 					break;
 				case NM_SETTING_DIFF_RESULT_IN_A:
-					nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s = %s", prefix, str1->str, str_conn ? str_conn : "NULL");
+					nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s = %s", prefix, str1->str, str_conn ? str_conn : "NULL");
 					break;
 				default:
-					nm_log (level, domain, "%s%"_NM_LOG_ALIGN"s = %s < %s", prefix, str1->str, str_conn ? str_conn : "NULL", str_diff ? str_diff : "NULL");
+					nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s = %s < %s", prefix, str1->str, str_conn ? str_conn : "NULL", str_diff ? str_diff : "NULL");
 					break;
 #undef _NM_LOG_ALIGN
 			}
@@ -3249,7 +3329,7 @@ nm_utils_ipv6_interface_identifier_get_from_addr (NMUtilsIPv6IfaceId *iid,
  */
 gboolean
 nm_utils_ipv6_interface_identifier_get_from_token (NMUtilsIPv6IfaceId *iid,
-                                                  const char *token)
+                                                   const char *token)
 {
 	struct in6_addr i6_token;
 
@@ -3469,11 +3549,36 @@ nm_utils_stable_id_parse (const char *stable_id,
 /*****************************************************************************/
 
 static gboolean
+_is_reserved_ipv6_iid (const guint8 *iid)
+{
+	/* https://tools.ietf.org/html/rfc5453 */
+	/* https://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xml */
+
+	/* 0000:0000:0000:0000 (Subnet-Router Anycast [RFC4291]) */
+	if (memcmp (iid, &nm_ip_addr_zero.addr6.s6_addr[8], 8) == 0)
+		return TRUE;
+
+	/* 0200:5EFF:FE00:0000 - 0200:5EFF:FE00:5212 (Reserved IPv6 Interface Identifiers corresponding to the IANA Ethernet Block [RFC4291])
+	 * 0200:5EFF:FE00:5213                       (Proxy Mobile IPv6 [RFC6543])
+	 * 0200:5EFF:FE00:5214 - 0200:5EFF:FEFF:FFFF (Reserved IPv6 Interface Identifiers corresponding to the IANA Ethernet Block [RFC4291]) */
+	if (memcmp (iid, (const guint8[]) { 0x02, 0x00, 0x5E, 0xFF, 0xFE }, 5) == 0)
+		return TRUE;
+
+	/* FDFF:FFFF:FFFF:FF80 - FDFF:FFFF:FFFF:FFFF (Reserved Subnet Anycast Addresses [RFC2526]) */
+	if (memcmp (iid, (const guint8[]) { 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 7) == 0) {
+		if (iid[7] & 0x80)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
 _set_stable_privacy (NMUtilsStableType stable_type,
                      struct in6_addr *addr,
                      const char *ifname,
                      const char *network_id,
-                     guint dad_counter,
+                     guint32 dad_counter,
                      guint8 *secret_key,
                      gsize key_len,
                      GError **error)
@@ -3524,9 +3629,19 @@ _set_stable_privacy (NMUtilsStableType stable_type,
 	g_checksum_update (sum, (const guchar *) secret_key, key_len);
 
 	g_checksum_get_digest (sum, digest, &len);
-	g_checksum_free (sum);
 
-	g_return_val_if_fail (len == 32, FALSE);
+	nm_assert (len == sizeof (digest));
+
+	while (_is_reserved_ipv6_iid (digest)) {
+		g_checksum_reset (sum);
+		tmp[0] = htonl (++dad_counter);
+		g_checksum_update (sum, digest, len);
+		g_checksum_update (sum, (const guchar *) &tmp[0], sizeof (tmp[0]));
+		g_checksum_get_digest (sum, digest, &len);
+		nm_assert (len == sizeof (digest));
+	}
+
+	g_checksum_free (sum);
 
 	memcpy (addr->s6_addr + 8, &digest[0], 8);
 
@@ -3538,7 +3653,7 @@ nm_utils_ipv6_addr_set_stable_privacy_impl (NMUtilsStableType stable_type,
                                             struct in6_addr *addr,
                                             const char *ifname,
                                             const char *network_id,
-                                            guint dad_counter,
+                                            guint32 dad_counter,
                                             guint8 *secret_key,
                                             gsize key_len,
                                             GError **error)
@@ -3560,7 +3675,7 @@ nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type,
                                        struct in6_addr *addr,
                                        const char *ifname,
                                        const char *network_id,
-                                       guint dad_counter,
+                                       guint32 dad_counter,
                                        GError **error)
 {
 	gs_free guint8 *secret_key = NULL;
@@ -3795,7 +3910,7 @@ debug_key_matches (const gchar *key,
  * nm_utils_parse_debug_string:
  * @string: the string to parse
  * @keys: the debug keys
- * @nkeys: number of entires in @keys
+ * @nkeys: number of entries in @keys
  *
  * Similar to g_parse_debug_string(), but does not special
  * case "help" or "all".
@@ -4305,3 +4420,40 @@ skip:
 	return result;
 }
 
+char *
+nm_utils_format_con_diff_for_audit (GHashTable *diff)
+{
+	GHashTable *setting_diff;
+	char *setting_name, *prop_name;
+	GHashTableIter iter, iter2;
+	GString *str;
+
+	str = g_string_sized_new (32);
+	g_hash_table_iter_init (&iter, diff);
+
+	while (g_hash_table_iter_next (&iter,
+	                               (gpointer *) &setting_name,
+	                               (gpointer *) &setting_diff)) {
+		if (!setting_diff)
+			continue;
+
+		g_hash_table_iter_init (&iter2, setting_diff);
+
+		while (g_hash_table_iter_next (&iter2, (gpointer *) &prop_name, NULL))
+			g_string_append_printf (str, "%s.%s,", setting_name, prop_name);
+	}
+
+	if (str->len)
+		str->str[str->len - 1] = '\0';
+
+	return g_string_free (str, FALSE);
+}
+
+/*****************************************************************************/
+
+NM_UTILS_LOOKUP_STR_DEFINE (nm_activation_type_to_string, NMActivationType,
+	NM_UTILS_LOOKUP_DEFAULT_WARN ("(unknown)"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_ACTIVATION_TYPE_MANAGED,  "managed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_ACTIVATION_TYPE_ASSUME,   "assume"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_ACTIVATION_TYPE_EXTERNAL, "external"),
+)
diff --git a/src/nm-core-utils.h b/src/nm-core-utils.h
index ada3f8ef..0f37bd20 100644
--- a/src/nm-core-utils.h
+++ b/src/nm-core-utils.h
@@ -90,6 +90,25 @@ GETTER (void) \
 
 /*****************************************************************************/
 
+typedef struct {
+	union {
+		guint8 addr_ptr[1];
+		in_addr_t addr4;
+		struct in6_addr addr6;
+
+		/* NMIPAddr is really a union for IP addresses.
+		 * However, as ethernet addresses fit in here nicely, use
+		 * it also for an ethernet MAC address. */
+		guint8 addr_eth[6 /*ETH_ALEN*/];
+	};
+} NMIPAddr;
+
+extern const NMIPAddr nm_ip_addr_zero;
+
+#define NMIPAddrInit { .addr6 = IN6ADDR_ANY_INIT }
+
+/*****************************************************************************/
+
 gboolean nm_ethernet_address_is_valid (gconstpointer addr, gssize len);
 
 gconstpointer nm_utils_ipx_address_clear_host_address (int family, gpointer dst, gconstpointer src, guint8 plen);
@@ -97,6 +116,8 @@ in_addr_t nm_utils_ip4_address_clear_host_address (in_addr_t addr, guint8 plen);
 const struct in6_addr *nm_utils_ip6_address_clear_host_address (struct in6_addr *dst, const struct in6_addr *src, guint8 plen);
 gboolean nm_utils_ip6_address_same_prefix (const struct in6_addr *addr_a, const struct in6_addr *addr_b, guint8 plen);
 
+double nm_utils_exp10 (gint16 e);
+
 /**
  * nm_utils_ip6_route_metric_normalize:
  * @metric: the route metric
@@ -112,8 +133,6 @@ nm_utils_ip6_route_metric_normalize (guint32 metric)
 	return metric ? metric : 1024 /*NM_PLATFORM_ROUTE_METRIC_DEFAULT_IP6*/;
 }
 
-int nm_spawn_process (const char *args, GError **error);
-
 int nm_utils_modprobe (GError **error, gboolean suppress_error_loggin, const char *arg1, ...) G_GNUC_NULL_TERMINATED;
 
 guint64 nm_utils_get_start_time_for_pid (pid_t pid, char *out_state, pid_t *out_ppid);
@@ -144,6 +163,8 @@ typedef enum {
 
 NMMatchSpecMatchType nm_match_spec_device (const GSList *specs,
                                            const char *interface_name,
+                                           const char *driver,
+                                           const char *driver_version,
                                            const char *device_type,
                                            const char *hwaddr,
                                            const char *s390_subchannels);
@@ -274,7 +295,7 @@ const char *nm_utils_new_infiniband_name (char *name, const char *parent_name, i
 GPtrArray *nm_utils_read_resolv_conf_nameservers (const char *rc_contents);
 GPtrArray *nm_utils_read_resolv_conf_dns_options (const char *rc_contents);
 
-int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection **a, NMConnection **b);
+int nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection *b);
 
 void nm_utils_log_connection_diff (NMConnection *connection, NMConnection *diff_base, guint32 level, guint64 domain, const char *name, const char *prefix);
 
@@ -390,7 +411,7 @@ gboolean nm_utils_ipv6_addr_set_stable_privacy_impl (NMUtilsStableType stable_ty
                                                      struct in6_addr *addr,
                                                      const char *ifname,
                                                      const char *network_id,
-                                                     guint dad_counter,
+                                                     guint32 dad_counter,
                                                      guint8 *secret_key,
                                                      gsize key_len,
                                                      GError **error);
@@ -399,7 +420,7 @@ gboolean nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType id_type,
                                                 struct in6_addr *addr,
                                                 const char *ifname,
                                                 const char *network_id,
-                                                guint dad_counter,
+                                                guint32 dad_counter,
                                                 GError **error);
 
 char *nm_utils_hw_addr_gen_random_eth (const char *current_mac_address,
@@ -471,5 +492,13 @@ struct stat;
 
 gboolean nm_utils_validate_plugin (const char *path, struct stat *stat, GError **error);
 char **nm_utils_read_plugin_paths (const char *dirname, const char *prefix);
+char *nm_utils_format_con_diff_for_audit (GHashTable *diff);
+
+
+/*****************************************************************************/
+
+const char *nm_activation_type_to_string (NMActivationType activation_type);
+
+/*****************************************************************************/
 
 #endif /* __NM_CORE_UTILS_H__ */
diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c
index 5944654c..9ac6d552 100644
--- a/src/nm-default-route-manager.c
+++ b/src/nm-default-route-manager.c
@@ -36,12 +36,16 @@
 /*****************************************************************************/
 
 NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_LOG_WITH_PTR,
 	PROP_PLATFORM,
 );
 
 typedef struct {
 	GPtrArray *entries_ip4;
 	GPtrArray *entries_ip6;
+
+	NMPlatform *platform;
+
 	struct {
 		guint guard;
 		guint backoff_wait_time_ms;
@@ -56,9 +60,9 @@ typedef struct {
 	 * pointers.
 	 * Guard every publicly accessible function to return early if the instance
 	 * is already disposing. */
-	gboolean disposed;
+	bool disposed;
 
-	NMPlatform *platform;
+	bool log_with_ptr;
 } NMDefaultRouteManagerPrivate;
 
 struct _NMDefaultRouteManager {
@@ -74,8 +78,6 @@ G_DEFINE_TYPE (NMDefaultRouteManager, nm_default_route_manager, G_TYPE_OBJECT)
 
 #define NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDefaultRouteManager, NM_IS_DEFAULT_ROUTE_MANAGER)
 
-NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get, NM_TYPE_DEFAULT_ROUTE_MANAGER);
-
 /*****************************************************************************/
 
 #define _NMLOG_PREFIX_NAME   "default-route"
@@ -97,9 +99,9 @@ NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get,
         if (nm_logging_enabled (__level, __domain)) { \
             char __prefix_buf[100]; \
             \
-            _nm_log (__level, __domain, 0, \
+            _nm_log (__level, __domain, 0, NULL, NULL, \
                      "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
-                     self != singleton_instance \
+                     NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self)->log_with_ptr \
                         ? nm_sprintf_buf (__prefix_buf, "%s%c[%p]", \
                                           _NMLOG2_PREFIX_NAME, \
                                           __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'), \
@@ -123,9 +125,9 @@ NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get,
             guint __entry_idx = (entry_idx); \
             const Entry *const __entry = (entry); \
             \
-            _nm_log (__level, __domain, 0, \
-                     "%s: entry[%u/%s:%p:%s:%c:%csync]: "_NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
-                     self != singleton_instance \
+            _nm_log (__level, __domain, 0, NULL, NULL, \
+                     "%s: entry[%u/%s:%p:%s:%chas:%csync]: "_NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                     NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self)->log_with_ptr \
                         ? nm_sprintf_buf (__prefix_buf, "%s%c[%p]", \
                                           _NMLOG2_PREFIX_NAME, \
                                           __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'), \
@@ -135,7 +137,7 @@ NM_DEFINE_SINGLETON_GETTER (NMDefaultRouteManager, nm_default_route_manager_get,
                      NM_IS_DEVICE (__entry->source.pointer) ? "dev" : "vpn", \
                      __entry->source.pointer, \
                      NM_IS_DEVICE (__entry->source.pointer) ? nm_device_get_iface (__entry->source.device) : nm_active_connection_get_settings_connection_id (NM_ACTIVE_CONNECTION (__entry->source.vpn)), \
-                     (__entry->never_default ? '0' : '1'), \
+                     (__entry->never_default ? '-' : '+'), \
                      (__entry->synced ? '+' : '-') \
                      _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
@@ -301,24 +303,21 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g
 		return FALSE;
 
 	if (vtable->vt->is_ip4) {
-		success = nm_platform_ip4_route_add (priv->platform,
-		                                     entry->route.rx.ifindex,
-		                                     entry->route.rx.rt_source,
-		                                     0,
-		                                     0,
-		                                     entry->route.r4.gateway,
-		                                     0,
-		                                     entry->effective_metric,
-		                                     entry->route.rx.mss);
+		NMPlatformIP4Route rt = entry->route.r4;
+
+		rt.network = 0;
+		rt.plen = 0;
+		rt.metric = entry->effective_metric;
+
+		success = nm_platform_ip4_route_add (priv->platform, &rt);
 	} else {
-		success = nm_platform_ip6_route_add (priv->platform,
-		                                     entry->route.rx.ifindex,
-		                                     entry->route.rx.rt_source,
-		                                     in6addr_any,
-		                                     0,
-		                                     entry->route.r6.gateway,
-		                                     entry->effective_metric,
-		                                     entry->route.rx.mss);
+		NMPlatformIP6Route rt = entry->route.r6;
+
+		rt.network = in6addr_any;
+		rt.plen = 0;
+		rt.metric = entry->effective_metric;
+
+		success = nm_platform_ip6_route_add (priv->platform, &rt);
 	}
 	if (!success) {
 		_LOGW (vtable->vt->addr_family, "failed to add default route %s with effective metric %u",
@@ -483,17 +482,6 @@ _get_assumed_interface_metrics (const VTableIP *vtable, NMDefaultRouteManager *s
 	return result;
 }
 
-static int
-_sort_metrics_ascending_fcn (gconstpointer a, gconstpointer b)
-{
-	guint32 m_a = *((guint32 *) a);
-	guint32 m_b = *((guint32 *) b);
-
-	if (m_a < m_b)
-		return -1;
-	return m_a == m_b ? 0 : 1;
-}
-
 static gboolean
 _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *changed_entry, const Entry *old_entry, gboolean external_change)
 {
@@ -536,8 +524,6 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c
 	for (i = 0; i < entries->len; i++) {
 		entry = g_ptr_array_index (entries, i);
 
-		g_assert (entry != old_entry);
-
 		if (entry->never_default)
 			continue;
 
@@ -592,12 +578,15 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c
 			/* for the changed entry, the previous metric was either old_entry->effective_metric,
 			 * or none. Hence, we only have to remember what is going to change. */
 			g_array_append_val (changed_metrics, expected_metric);
-			if (old_entry) {
+			if (!old_entry) {
+				_LOG2D (vtable, i, entry, "sync:add    %s (%u)",
+				        vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) expected_metric);
+			} else if (old_entry != changed_entry) {
 				_LOG2D (vtable, i, entry, "sync:update %s (%u -> %u)",
 				        vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) old_entry->effective_metric,
 				        (guint) expected_metric);
 			} else {
-				_LOG2D (vtable, i, entry, "sync:add    %s (%u)",
+				_LOG2D (vtable, i, entry, "sync:resync %s (%u)",
 				        vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) expected_metric);
 			}
 		} else if (entry->effective_metric != expected_metric) {
@@ -624,7 +613,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c
 
 	g_array_free (routes, TRUE);
 
-	g_array_sort (changed_metrics, _sort_metrics_ascending_fcn);
+	g_array_sort_with_data (changed_metrics, nm_cmp_uint32_p_with_data, NULL);
 	last_metric = -1;
 	for (j = 0; j < changed_metrics->len; j++) {
 		expected_metric = g_array_index (changed_metrics, guint32, j);
@@ -656,7 +645,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c
 	return changed;
 }
 
-static void
+static gboolean
 _entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx, const Entry *old_entry)
 {
 	NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
@@ -675,21 +664,26 @@ _entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint
 		entry->effective_metric = entry->route.rx.metric;
 
 	_LOG2D (vtable, entry_idx, entry, "%s %s (%"G_GUINT32_FORMAT")",
-	        old_entry ? "record:update" : "record:add   ",
+	        old_entry
+	            ? (entry != old_entry
+	                   ? "record:update"
+	                   : "record:resync")
+	            : "record:add   ",
 	        vtable->vt->route_to_string (&entry->route, NULL, 0),
 	        entry->effective_metric);
 
 	g_ptr_array_sort_with_data (entries, _sort_entries_cmp, NULL);
 
-	_resync_all (vtable, self, entry, old_entry, FALSE);
+	return _resync_all (vtable, self, entry, old_entry, FALSE);
 }
 
-static void
+static gboolean
 _entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry_idx)
 {
 	NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
 	Entry *entry;
 	GPtrArray *entries;
+	gboolean ret;
 
 	entries = vtable->get_entries (priv);
 
@@ -704,15 +698,18 @@ _entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint
 	g_ptr_array_index (entries, entry_idx) = NULL;
 	g_ptr_array_remove_index (entries, entry_idx);
 
-	_resync_all (vtable, self, NULL, entry, FALSE);
-
+	ret = _resync_all (vtable, self, NULL, entry, FALSE);
 	_entry_free (entry);
+
+	return ret;
 }
 
 /*****************************************************************************/
 
-static void
-_ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self, gpointer source)
+static gboolean
+_ipx_update_default_route (const VTableIP *vtable,
+                           NMDefaultRouteManager *self,
+                           gpointer source)
 {
 	NMDefaultRouteManagerPrivate *priv;
 	Entry *entry;
@@ -724,20 +721,20 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
 	NMDevice *device = NULL;
 	NMVpnConnection *vpn = NULL;
 	gboolean never_default = FALSE;
-	gboolean synced = FALSE;
+	gboolean synced = FALSE, ret;
 
-	g_return_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self));
+	g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), FALSE);
 
 	priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
 	if (priv->disposed)
-		return;
+		return FALSE;
 
 	if (NM_IS_DEVICE (source))
 		device = source;
 	else if (NM_IS_VPN_CONNECTION (source))
 		vpn = source;
 	else
-		g_return_if_reached ();
+		g_return_val_if_reached (FALSE);
 
 	if (device)
 		ip_ifindex = nm_device_get_ip_ifindex (device);
@@ -756,15 +753,15 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
 		g_object_freeze_notify (G_OBJECT (self));
 		_entry_at_idx_remove (vtable, self, entry_idx);
 		g_assert (!_entry_find_by_source (entries, source, NULL));
-		_ipx_update_default_route (vtable, self, source);
+		ret = _ipx_update_default_route (vtable, self, source);
 		g_object_thaw_notify (G_OBJECT (self));
-		return;
+		return ret;
 	}
 
 	/* get the @default_route from the device. */
 	if (ip_ifindex > 0) {
 		if (device) {
-			gboolean is_assumed;
+			gboolean is_assumed = FALSE;
 
 			if (vtable->vt->is_ip4)
 				default_route = (const NMPlatformIPRoute *) nm_device_get_ip4_default_route (device, &is_assumed);
@@ -784,9 +781,8 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
 				default_route = &rt.rx;
 
 				never_default = TRUE;
-				synced = TRUE;
-			} else
-				synced = default_route && !is_assumed;
+			}
+			synced = !is_assumed;
 		} else {
 			NMConnection *connection = nm_active_connection_get_applied_connection ((NMActiveConnection *) vpn);
 
@@ -842,9 +838,10 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
 		default_route = NULL;
 	}
 
-	if (!entry && !default_route)
-		/* nothing to do */;
-	else if (!entry) {
+	if (!entry && !default_route) {
+		/* nothing to do */
+		return FALSE;
+	} else if (!entry) {
 		/* add */
 		entry = g_slice_new0 (Entry);
 		entry->source.object = g_object_ref (source);
@@ -862,7 +859,7 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
 		entry->synced = synced;
 
 		g_ptr_array_add (entries, entry);
-		_entry_at_idx_update (vtable, self, entries->len - 1, NULL);
+		return _entry_at_idx_update (vtable, self, entries->len - 1, NULL);
 	} else if (default_route) {
 		/* update */
 		Entry old_entry, new_entry;
@@ -878,28 +875,36 @@ _ipx_update_default_route (const VTableIP *vtable, NMDefaultRouteManager *self,
 		new_entry.never_default = never_default;
 		new_entry.synced = synced;
 
-		if (memcmp (entry, &new_entry, sizeof (new_entry)) == 0)
-			return;
-
-		old_entry = *entry;
-		*entry = new_entry;
-		_entry_at_idx_update (vtable, self, entry_idx, &old_entry);
+		if (memcmp (entry, &new_entry, sizeof (new_entry)) == 0) {
+			if (!synced) {
+				/* the internal book-keeping doesn't change, so don't do a full
+				 * sync of the configured routes. */
+				return FALSE;
+			}
+			return _entry_at_idx_update (vtable, self, entry_idx, entry);
+		} else {
+			old_entry = *entry;
+			*entry = new_entry;
+			return _entry_at_idx_update (vtable, self, entry_idx, &old_entry);
+		}
 	} else {
 		/* delete */
-		_entry_at_idx_remove (vtable, self, entry_idx);
+		return _entry_at_idx_remove (vtable, self, entry_idx);
 	}
 }
 
-void
-nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *self, gpointer source)
+gboolean
+nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *self,
+                                                   gpointer source)
 {
-	_ipx_update_default_route (&vtable_ip4, self, source);
+	return _ipx_update_default_route (&vtable_ip4, self, source);
 }
 
-void
-nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *self, gpointer source)
+gboolean
+nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *self,
+                                                   gpointer source)
 {
-	_ipx_update_default_route (&vtable_ip6, self, source);
+	return _ipx_update_default_route (&vtable_ip6, self, source);
 }
 
 /*****************************************************************************/
@@ -1259,7 +1264,7 @@ static const VTableIP vtable_ip6 = {
 /*****************************************************************************/
 
 static gboolean
-_resync_idle_now (NMDefaultRouteManager *self)
+_resync_now (NMDefaultRouteManager *self)
 {
 	gboolean has_v4_changes, has_v6_changes;
 	gboolean changed = FALSE;
@@ -1274,7 +1279,7 @@ _resync_idle_now (NMDefaultRouteManager *self)
 
 	priv->resync.has_v4_changes = FALSE;
 	priv->resync.has_v6_changes = FALSE;
-	priv->resync.idle_handle = 0;
+	nm_clear_g_source (&priv->resync.idle_handle);
 	priv->resync.backoff_wait_time_ms =
 	    priv->resync.backoff_wait_time_ms == 0
 	    ? 100
@@ -1291,6 +1296,64 @@ _resync_idle_now (NMDefaultRouteManager *self)
 		_resync_idle_cancel (self);
 	}
 
+	return changed;
+}
+
+/**
+ * nm_default_route_manager_resync:
+ * @self: the #NMDefaultRouteManager instance
+ * @af_family: the address family to resync, can be
+ *   AF_INET, AF_INET6 or AF_UNSPEC to sync both.
+ *
+ * #NMDefaultRouteManager keeps an internal list of configured
+ * routes. Usually, it configures routes in the system only
+ *  - when that internal list changes due to
+ *    nm_default_route_manager_ip4_update_default_route() or
+ *    nm_default_route_manager_ip6_update_default_route().
+ *  - when platform notifies about changes, via _resync_idle_now().
+ * This forces a resync to update the internal bookkeeping
+ * with what is currently configured in the system, but also
+ * reconfigure the system with all non-assumed default routes.
+ *
+ * Returns: %TRUE if anything changed during resync.
+ */
+gboolean
+nm_default_route_manager_resync (NMDefaultRouteManager *self,
+                                 int af_family)
+{
+	NMDefaultRouteManagerPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_DEFAULT_ROUTE_MANAGER (self), FALSE);
+	g_return_val_if_fail (NM_IN_SET (af_family, AF_INET, AF_INET6, AF_UNSPEC), FALSE);
+
+	priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
+
+	if (priv->disposed)
+		return FALSE;
+
+	switch (af_family) {
+	case AF_INET:
+		priv->resync.has_v4_changes = TRUE;
+		break;
+	case AF_INET6:
+		priv->resync.has_v6_changes = TRUE;
+		break;
+	default:
+		priv->resync.has_v4_changes = TRUE;
+		priv->resync.has_v6_changes = TRUE;
+		break;
+	}
+
+	return _resync_now (self);
+}
+
+static gboolean
+_resync_idle_now (NMDefaultRouteManager *self)
+{
+	NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
+
+	priv->resync.idle_handle = 0;
+	_resync_now (self);
 	return G_SOURCE_REMOVE;
 }
 
@@ -1336,33 +1399,6 @@ _resync_idle_reschedule (NMDefaultRouteManager *self)
 }
 
 static void
-_platform_ipx_route_changed_cb (const VTableIP *vtable,
-                                NMDefaultRouteManager *self,
-                                const NMPlatformIPRoute *route)
-{
-	NMDefaultRouteManagerPrivate *priv;
-
-	if (route && !NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) {
-		/* we only care about address changes or changes of default route. */
-		return;
-	}
-
-	priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
-
-	if (priv->resync.guard) {
-		/* callbacks while executing _resync_all() are ignored. */
-		return;
-	}
-
-	if (vtable->vt->is_ip4)
-		priv->resync.has_v4_changes = TRUE;
-	else
-		priv->resync.has_v6_changes = TRUE;
-
-	_resync_idle_reschedule (self);
-}
-
-static void
 _platform_changed_cb (NMPlatform *platform,
                       int obj_type_i,
                       int ifindex,
@@ -1370,24 +1406,44 @@ _platform_changed_cb (NMPlatform *platform,
                       int change_type_i,
                       NMDefaultRouteManager *self)
 {
+	NMDefaultRouteManagerPrivate *priv;
 	const NMPObjectType obj_type = obj_type_i;
+	const VTableIP *vtable;
 
 	switch (obj_type) {
 	case NMP_OBJECT_TYPE_IP4_ADDRESS:
-		_platform_ipx_route_changed_cb (&vtable_ip4, self, NULL);
+		vtable = &vtable_ip4;
 		break;
 	case NMP_OBJECT_TYPE_IP6_ADDRESS:
-		_platform_ipx_route_changed_cb (&vtable_ip6, self, NULL);
+		vtable = &vtable_ip6;
 		break;
 	case NMP_OBJECT_TYPE_IP4_ROUTE:
-		_platform_ipx_route_changed_cb (&vtable_ip4, self, (const NMPlatformIPRoute *) platform_object);
+		if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (platform_object))
+			return;
+		vtable = &vtable_ip4;
 		break;
 	case NMP_OBJECT_TYPE_IP6_ROUTE:
-		_platform_ipx_route_changed_cb (&vtable_ip6, self, (const NMPlatformIPRoute *) platform_object);
+		if (!NM_PLATFORM_IP_ROUTE_IS_DEFAULT (platform_object))
+			return;
+		vtable = &vtable_ip6;
 		break;
 	default:
 		g_return_if_reached ();
 	}
+
+	priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
+
+	if (priv->resync.guard) {
+		/* callbacks while executing _resync_all() are ignored. */
+		return;
+	}
+
+	if (vtable->vt->is_ip4)
+		priv->resync.has_v4_changes = TRUE;
+	else
+		priv->resync.has_v6_changes = TRUE;
+
+	_resync_idle_reschedule (self);
 }
 
 /*****************************************************************************/
@@ -1400,6 +1456,10 @@ set_property (GObject *object, guint prop_id,
 	NMDefaultRouteManagerPrivate *priv = NM_DEFAULT_ROUTE_MANAGER_GET_PRIVATE (self);
 
 	switch (prop_id) {
+	case PROP_LOG_WITH_PTR:
+		/* construct-only */
+		priv->log_with_ptr = g_value_get_boolean (value);
+		break;
 	case PROP_PLATFORM:
 		/* construct-only */
 		priv->platform = g_value_get_object (value) ? : NM_PLATFORM_GET;
@@ -1436,9 +1496,10 @@ constructed (GObject *object)
 }
 
 NMDefaultRouteManager *
-nm_default_route_manager_new (NMPlatform *platform)
+nm_default_route_manager_new (gboolean log_with_ptr, NMPlatform *platform)
 {
 	return g_object_new (NM_TYPE_DEFAULT_ROUTE_MANAGER,
+	                     NM_DEFAULT_ROUTE_MANAGER_LOG_WITH_PTR, log_with_ptr,
 	                     NM_DEFAULT_ROUTE_MANAGER_PLATFORM, platform,
 	                     NULL);
 }
@@ -1486,6 +1547,13 @@ nm_default_route_manager_class_init (NMDefaultRouteManagerClass *klass)
 	object_class->dispose = dispose;
 	object_class->set_property = set_property;
 
+	obj_properties[PROP_LOG_WITH_PTR] =
+	    g_param_spec_boolean (NM_DEFAULT_ROUTE_MANAGER_LOG_WITH_PTR, "", "",
+	                          TRUE,
+	                          G_PARAM_WRITABLE |
+	                          G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_STATIC_STRINGS);
+
 	obj_properties[PROP_PLATFORM] =
 	    g_param_spec_object (NM_DEFAULT_ROUTE_MANAGER_PLATFORM, "", "",
 	                         NM_TYPE_PLATFORM,
diff --git a/src/nm-default-route-manager.h b/src/nm-default-route-manager.h
index bd5e8732..bac8a6eb 100644
--- a/src/nm-default-route-manager.h
+++ b/src/nm-default-route-manager.h
@@ -30,17 +30,17 @@
 #define NM_IS_DEFAULT_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEFAULT_ROUTE_MANAGER))
 #define NM_DEFAULT_ROUTE_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEFAULT_ROUTE_MANAGER, NMDefaultRouteManagerClass))
 
-#define NM_DEFAULT_ROUTE_MANAGER_PLATFORM "platform"
+#define NM_DEFAULT_ROUTE_MANAGER_LOG_WITH_PTR "log-with-ptr"
+#define NM_DEFAULT_ROUTE_MANAGER_PLATFORM     "platform"
 
 typedef struct _NMDefaultRouteManagerClass NMDefaultRouteManagerClass;
 
 GType nm_default_route_manager_get_type (void);
 
-NMDefaultRouteManager *nm_default_route_manager_get (void);
-NMDefaultRouteManager *nm_default_route_manager_new (NMPlatform *platform);
+NMDefaultRouteManager *nm_default_route_manager_new (gboolean log_with_ptr, NMPlatform *platform);
 
-void nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *manager, gpointer source);
-void nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *manager, gpointer source);
+gboolean nm_default_route_manager_ip4_update_default_route (NMDefaultRouteManager *manager, gpointer source);
+gboolean nm_default_route_manager_ip6_update_default_route (NMDefaultRouteManager *manager, gpointer source);
 
 gboolean nm_default_route_manager_ip4_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection, gboolean *out_is_never_default);
 gboolean nm_default_route_manager_ip6_connection_has_default_route (NMDefaultRouteManager *manager, NMConnection *connection, gboolean *out_is_never_default);
@@ -61,4 +61,7 @@ NMIP6Config *nm_default_route_manager_ip6_get_best_config (NMDefaultRouteManager
                                                            NMDevice **out_device,
                                                            NMVpnConnection **out_vpn);
 
+gboolean nm_default_route_manager_resync (NMDefaultRouteManager *self,
+                                          int af_family);
+
 #endif  /* NM_DEFAULT_ROUTE_MANAGER_H */
diff --git a/src/nm-dispatcher.c b/src/nm-dispatcher.c
index 6c0c4bb4..0d482e0c 100644
--- a/src/nm-dispatcher.c
+++ b/src/nm-dispatcher.c
@@ -15,20 +15,22 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2004 - 2012 Red Hat, Inc.
+ * Copyright (C) 2004 - 2017 Red Hat, Inc.
  * Copyright (C) 2005 - 2008 Novell, Inc.
  */
 
 #include "nm-default.h"
 
+#include "nm-dispatcher.h"
+
 #include <string.h>
 #include <errno.h>
 
-#include "nm-dispatcher.h"
 #include "nm-dispatcher-api.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
 #include "nm-connectivity.h"
+#include "nm-act-request.h"
 #include "devices/nm-device.h"
 #include "nm-dhcp4-config.h"
 #include "nm-dhcp6-config.h"
@@ -70,14 +72,14 @@ static Monitor monitors[3] = {
 };
 
 static const Monitor*
-_get_monitor_by_action (DispatcherAction action)
+_get_monitor_by_action (NMDispatcherAction action)
 {
 	switch (action) {
-	case DISPATCHER_ACTION_PRE_UP:
-	case DISPATCHER_ACTION_VPN_PRE_UP:
+	case NM_DISPATCHER_ACTION_PRE_UP:
+	case NM_DISPATCHER_ACTION_VPN_PRE_UP:
 		return &monitors[MONITOR_INDEX_PRE_UP];
-	case DISPATCHER_ACTION_PRE_DOWN:
-	case DISPATCHER_ACTION_VPN_PRE_DOWN:
+	case NM_DISPATCHER_ACTION_PRE_DOWN:
+	case NM_DISPATCHER_ACTION_VPN_PRE_DOWN:
 		return &monitors[MONITOR_INDEX_PRE_DOWN];
 	default:
 		return &monitors[MONITOR_INDEX_DEFAULT];
@@ -311,9 +313,9 @@ fill_vpn_props (NMProxyConfig *proxy_config,
 }
 
 typedef struct {
-	DispatcherAction action;
+	NMDispatcherAction action;
 	guint request_id;
-	DispatcherFunc callback;
+	NMDispatcherFunc callback;
 	gpointer user_data;
 	guint idle_id;
 } DispatchInfo;
@@ -362,7 +364,7 @@ dispatch_result_to_string (DispatchResult result)
 }
 
 static void
-dispatcher_results_process (guint request_id, DispatcherAction action, GVariantIter *results)
+dispatcher_results_process (guint request_id, NMDispatcherAction action, GVariantIter *results)
 {
 	const char *script, *err;
 	guint32 result;
@@ -440,22 +442,22 @@ dispatcher_done_cb (GObject *proxy, GAsyncResult *result, gpointer user_data)
 }
 
 static const char *action_table[] = {
-	[DISPATCHER_ACTION_HOSTNAME]     = NMD_ACTION_HOSTNAME,
-	[DISPATCHER_ACTION_PRE_UP]       = NMD_ACTION_PRE_UP,
-	[DISPATCHER_ACTION_UP]           = NMD_ACTION_UP,
-	[DISPATCHER_ACTION_PRE_DOWN]     = NMD_ACTION_PRE_DOWN,
-	[DISPATCHER_ACTION_DOWN]         = NMD_ACTION_DOWN,
-	[DISPATCHER_ACTION_VPN_PRE_UP]   = NMD_ACTION_VPN_PRE_UP,
-	[DISPATCHER_ACTION_VPN_UP]       = NMD_ACTION_VPN_UP,
-	[DISPATCHER_ACTION_VPN_PRE_DOWN] = NMD_ACTION_VPN_PRE_DOWN,
-	[DISPATCHER_ACTION_VPN_DOWN]     = NMD_ACTION_VPN_DOWN,
-	[DISPATCHER_ACTION_DHCP4_CHANGE] = NMD_ACTION_DHCP4_CHANGE,
-	[DISPATCHER_ACTION_DHCP6_CHANGE] = NMD_ACTION_DHCP6_CHANGE,
-	[DISPATCHER_ACTION_CONNECTIVITY_CHANGE] = NMD_ACTION_CONNECTIVITY_CHANGE
+	[NM_DISPATCHER_ACTION_HOSTNAME]     = NMD_ACTION_HOSTNAME,
+	[NM_DISPATCHER_ACTION_PRE_UP]       = NMD_ACTION_PRE_UP,
+	[NM_DISPATCHER_ACTION_UP]           = NMD_ACTION_UP,
+	[NM_DISPATCHER_ACTION_PRE_DOWN]     = NMD_ACTION_PRE_DOWN,
+	[NM_DISPATCHER_ACTION_DOWN]         = NMD_ACTION_DOWN,
+	[NM_DISPATCHER_ACTION_VPN_PRE_UP]   = NMD_ACTION_VPN_PRE_UP,
+	[NM_DISPATCHER_ACTION_VPN_UP]       = NMD_ACTION_VPN_UP,
+	[NM_DISPATCHER_ACTION_VPN_PRE_DOWN] = NMD_ACTION_VPN_PRE_DOWN,
+	[NM_DISPATCHER_ACTION_VPN_DOWN]     = NMD_ACTION_VPN_DOWN,
+	[NM_DISPATCHER_ACTION_DHCP4_CHANGE] = NMD_ACTION_DHCP4_CHANGE,
+	[NM_DISPATCHER_ACTION_DHCP6_CHANGE] = NMD_ACTION_DHCP6_CHANGE,
+	[NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE] = NMD_ACTION_CONNECTIVITY_CHANGE
 };
 
 static const char *
-action_to_string (DispatcherAction action)
+action_to_string (NMDispatcherAction action)
 {
 	g_assert ((gsize) action < G_N_ELEMENTS (action_table));
 	return action_table[action];
@@ -474,17 +476,18 @@ dispatcher_idle_cb (gpointer user_data)
 }
 
 static gboolean
-_dispatcher_call (DispatcherAction action,
+_dispatcher_call (NMDispatcherAction action,
                   gboolean blocking,
+                  NMDevice *device,
                   NMSettingsConnection *settings_connection,
                   NMConnection *applied_connection,
-                  NMDevice *device,
+                  gboolean activation_type_external,
                   NMConnectivityState connectivity_state,
                   const char *vpn_iface,
                   NMProxyConfig *vpn_proxy_config,
                   NMIP4Config *vpn_ip4_config,
                   NMIP6Config *vpn_ip6_config,
-                  DispatcherFunc callback,
+                  NMDispatcherFunc callback,
                   gpointer user_data,
                   guint *out_call_id)
 {
@@ -504,6 +507,7 @@ _dispatcher_call (DispatcherAction action,
 	GError *error = NULL;
 	static guint request_counter = 0;
 	guint reqid = ++request_counter;
+	const char *connectivity_state_string = "UNKNOWN";
 
 	if (!dispatcher_proxy)
 		return FALSE;
@@ -517,8 +521,8 @@ _dispatcher_call (DispatcherAction action,
 	_ensure_requests ();
 
 	/* All actions except 'hostname' and 'connectivity-change' require a device */
-	if (   action == DISPATCHER_ACTION_HOSTNAME
-	    || action == DISPATCHER_ACTION_CONNECTIVITY_CHANGE) {
+	if (   action == NM_DISPATCHER_ACTION_HOSTNAME
+	    || action == NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE) {
 		_LOGD ("(%u) dispatching action '%s'%s",
 		       reqid, action_to_string (action),
 		       blocking
@@ -573,7 +577,7 @@ _dispatcher_call (DispatcherAction action,
 			                       NMD_CONNECTION_PROPS_FILENAME,
 			                       g_variant_new_string (filename));
 		}
-		if (nm_settings_connection_get_nm_generated_assumed (settings_connection)) {
+		if (activation_type_external) {
 			g_variant_builder_add (&connection_props, "{sv}",
 			                       NMD_CONNECTION_PROPS_EXTERNAL,
 			                       g_variant_new_boolean (TRUE));
@@ -589,8 +593,8 @@ _dispatcher_call (DispatcherAction action,
 	g_variant_builder_init (&vpn_ip6_props, G_VARIANT_TYPE_VARDICT);
 
 	/* hostname and connectivity-change actions don't send device data */
-	if (   action != DISPATCHER_ACTION_HOSTNAME
-	    && action != DISPATCHER_ACTION_CONNECTIVITY_CHANGE) {
+	if (   action != NM_DISPATCHER_ACTION_HOSTNAME
+	    && action != NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE) {
 		fill_device_props (device,
 		                   &device_props,
 		                   &device_proxy_props,
@@ -613,6 +617,10 @@ _dispatcher_call (DispatcherAction action,
 	if (!device_dhcp6_props)
 		device_dhcp6_props = g_variant_ref_sink (g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0));
 
+#if WITH_CONCHECK
+	connectivity_state_string = nm_connectivity_state_to_string (connectivity_state);
+#endif
+
 	/* Send the action to the dispatcher */
 	if (blocking) {
 		GVariant *ret;
@@ -629,7 +637,7 @@ _dispatcher_call (DispatcherAction action,
 		                                               &device_ip6_props,
 		                                               device_dhcp4_props,
 		                                               device_dhcp6_props,
-		                                               nm_connectivity_state_to_string (connectivity_state),
+		                                               connectivity_state_string,
 		                                               vpn_iface ? vpn_iface : "",
 		                                               &vpn_proxy_props,
 		                                               &vpn_ip4_props,
@@ -667,7 +675,7 @@ _dispatcher_call (DispatcherAction action,
 		                                  &device_ip6_props,
 		                                  device_dhcp4_props,
 		                                  device_dhcp6_props,
-		                                  nm_connectivity_state_to_string (connectivity_state),
+		                                  connectivity_state_string,
 		                                  vpn_iface ? vpn_iface : "",
 		                                  &vpn_proxy_props,
 		                                  &vpn_ip4_props,
@@ -694,41 +702,75 @@ done:
 }
 
 /**
- * nm_dispatcher_call:
- * @action: the %DispatcherAction
- * @settings_connection: the #NMSettingsConnection the action applies to
- * @applied_connection: the currently applied connection
+ * nm_dispatcher_call_hostname:
+ * @callback: a caller-supplied callback to execute when done
+ * @user_data: caller-supplied pointer passed to @callback
+ * @out_call_id: on success, a call identifier which can be passed to
+ * nm_dispatcher_call_cancel()
+ *
+ * This method always invokes the dispatcher action asynchronously.
+ *
+ * Returns: %TRUE if the action was dispatched, %FALSE on failure
+ */
+gboolean
+nm_dispatcher_call_hostname (NMDispatcherFunc callback,
+                             gpointer user_data,
+                             guint *out_call_id)
+{
+	return _dispatcher_call (NM_DISPATCHER_ACTION_HOSTNAME, FALSE,
+	                         NULL, NULL, NULL, FALSE,
+	                         NM_CONNECTIVITY_UNKNOWN,
+	                         NULL, NULL, NULL, NULL,
+	                         callback, user_data, out_call_id);
+}
+
+/**
+ * nm_dispatcher_call_device:
+ * @action: the %NMDispatcherAction
  * @device: the #NMDevice the action applies to
+ * @act_request: the #NMActRequest for the action. If %NULL, use the
+ *   current request of the device.
  * @callback: a caller-supplied callback to execute when done
  * @user_data: caller-supplied pointer passed to @callback
  * @out_call_id: on success, a call identifier which can be passed to
  * nm_dispatcher_call_cancel()
  *
- * This method always invokes the dispatcher action asynchronously.  To ignore
+ * This method always invokes the device dispatcher action asynchronously.  To ignore
  * the result, pass %NULL to @callback.
  *
  * Returns: %TRUE if the action was dispatched, %FALSE on failure
  */
 gboolean
-nm_dispatcher_call (DispatcherAction action,
-                    NMSettingsConnection *settings_connection,
-                    NMConnection *applied_connection,
-                    NMDevice *device,
-                    DispatcherFunc callback,
-                    gpointer user_data,
-                    guint *out_call_id)
+nm_dispatcher_call_device (NMDispatcherAction action,
+                           NMDevice *device,
+                           NMActRequest *act_request,
+                           NMDispatcherFunc callback,
+                           gpointer user_data,
+                           guint *out_call_id)
 {
-	return _dispatcher_call (action, FALSE, settings_connection, applied_connection, device,
-	                         NM_CONNECTIVITY_UNKNOWN, NULL, NULL, NULL, NULL,
+	nm_assert (NM_IS_DEVICE (device));
+	if (!act_request) {
+		act_request = nm_device_get_act_request (device);
+		if (!act_request)
+			return FALSE;
+	}
+	nm_assert (NM_IN_SET (nm_active_connection_get_device (NM_ACTIVE_CONNECTION (act_request)), NULL, device));
+	return _dispatcher_call (action, FALSE,
+	                         device,
+	                         nm_act_request_get_settings_connection (act_request),
+	                         nm_act_request_get_applied_connection (act_request),
+	                         nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (act_request)) == NM_ACTIVATION_TYPE_EXTERNAL,
+	                         NM_CONNECTIVITY_UNKNOWN,
+	                         NULL, NULL, NULL, NULL,
 	                         callback, user_data, out_call_id);
 }
 
 /**
- * nm_dispatcher_call_sync():
- * @action: the %DispatcherAction
- * @settings_connection: the #NMSettingsConnection the action applies to
- * @applied_connection: the currently applied connection
+ * nm_dispatcher_call_device_sync():
+ * @action: the %NMDispatcherAction
  * @device: the #NMDevice the action applies to
+ * @act_request: the #NMActRequest for the action. If %NULL, use the
+ *   current request of the device.
  *
  * This method always invokes the dispatcher action synchronously and it may
  * take a long time to return.
@@ -736,18 +778,30 @@ nm_dispatcher_call (DispatcherAction action,
  * Returns: %TRUE if the action was dispatched, %FALSE on failure
  */
 gboolean
-nm_dispatcher_call_sync (DispatcherAction action,
-                         NMSettingsConnection *settings_connection,
-                         NMConnection *applied_connection,
-                         NMDevice *device)
+nm_dispatcher_call_device_sync (NMDispatcherAction action,
+                                NMDevice *device,
+                                NMActRequest *act_request)
 {
-	return _dispatcher_call (action, TRUE, settings_connection, applied_connection, device,
-	                         NM_CONNECTIVITY_UNKNOWN, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+	nm_assert (NM_IS_DEVICE (device));
+	if (!act_request) {
+		act_request = nm_device_get_act_request (device);
+		if (!act_request)
+			return FALSE;
+	}
+	nm_assert (NM_IN_SET (nm_active_connection_get_device (NM_ACTIVE_CONNECTION (act_request)), NULL, device));
+	return _dispatcher_call (action, TRUE,
+	                         device,
+	                         nm_act_request_get_settings_connection (act_request),
+	                         nm_act_request_get_applied_connection (act_request),
+	                         nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (act_request)) == NM_ACTIVATION_TYPE_EXTERNAL,
+	                         NM_CONNECTIVITY_UNKNOWN,
+	                         NULL, NULL, NULL, NULL,
+	                         NULL, NULL, NULL);
 }
 
 /**
  * nm_dispatcher_call_vpn():
- * @action: the %DispatcherAction
+ * @action: the %NMDispatcherAction
  * @settings_connection: the #NMSettingsConnection the action applies to
  * @applied_connection: the currently applied connection
  * @parent_device: the parent #NMDevice of the VPN connection
@@ -766,7 +820,7 @@ nm_dispatcher_call_sync (DispatcherAction action,
  * Returns: %TRUE if the action was dispatched, %FALSE on failure
  */
 gboolean
-nm_dispatcher_call_vpn (DispatcherAction action,
+nm_dispatcher_call_vpn (NMDispatcherAction action,
                         NMSettingsConnection *settings_connection,
                         NMConnection *applied_connection,
                         NMDevice *parent_device,
@@ -774,18 +828,23 @@ nm_dispatcher_call_vpn (DispatcherAction action,
                         NMProxyConfig *vpn_proxy_config,
                         NMIP4Config *vpn_ip4_config,
                         NMIP6Config *vpn_ip6_config,
-                        DispatcherFunc callback,
+                        NMDispatcherFunc callback,
                         gpointer user_data,
                         guint *out_call_id)
 {
-	return _dispatcher_call (action, FALSE, settings_connection, applied_connection,
-	                         parent_device, NM_CONNECTIVITY_UNKNOWN, vpn_iface, vpn_proxy_config,
-	                         vpn_ip4_config, vpn_ip6_config, callback, user_data, out_call_id);
+	return _dispatcher_call (action, FALSE,
+	                         parent_device,
+	                         settings_connection,
+	                         applied_connection,
+	                         FALSE,
+	                         NM_CONNECTIVITY_UNKNOWN,
+	                         vpn_iface, vpn_proxy_config, vpn_ip4_config, vpn_ip6_config,
+	                         callback, user_data, out_call_id);
 }
 
 /**
  * nm_dispatcher_call_vpn_sync():
- * @action: the %DispatcherAction
+ * @action: the %NMDispatcherAction
  * @settings_connection: the #NMSettingsConnection the action applies to
  * @applied_connection: the currently applied connection
  * @parent_device: the parent #NMDevice of the VPN connection
@@ -800,7 +859,7 @@ nm_dispatcher_call_vpn (DispatcherAction action,
  * Returns: %TRUE if the action was dispatched, %FALSE on failure
  */
 gboolean
-nm_dispatcher_call_vpn_sync (DispatcherAction action,
+nm_dispatcher_call_vpn_sync (NMDispatcherAction action,
                              NMSettingsConnection *settings_connection,
                              NMConnection *applied_connection,
                              NMDevice *parent_device,
@@ -809,26 +868,39 @@ nm_dispatcher_call_vpn_sync (DispatcherAction action,
                              NMIP4Config *vpn_ip4_config,
                              NMIP6Config *vpn_ip6_config)
 {
-	return _dispatcher_call (action, TRUE, settings_connection, applied_connection,
-	                         parent_device, NM_CONNECTIVITY_UNKNOWN, vpn_iface, vpn_proxy_config,
-	                         vpn_ip4_config, vpn_ip6_config, NULL, NULL, NULL);
+	return _dispatcher_call (action, TRUE,
+	                         parent_device,
+	                         settings_connection,
+	                         applied_connection,
+	                         FALSE,
+	                         NM_CONNECTIVITY_UNKNOWN,
+	                         vpn_iface, vpn_proxy_config, vpn_ip4_config, vpn_ip6_config,
+	                         NULL, NULL, NULL);
 }
 
 /**
  * nm_dispatcher_call_connectivity():
- * @action: the %DispatcherAction
  * @connectivity_state: the #NMConnectivityState value
+ * @callback: a caller-supplied callback to execute when done
+ * @user_data: caller-supplied pointer passed to @callback
+ * @out_call_id: on success, a call identifier which can be passed to
+ * nm_dispatcher_call_cancel()
  *
  * This method does not block the caller.
  *
  * Returns: %TRUE if the action was dispatched, %FALSE on failure
  */
 gboolean
-nm_dispatcher_call_connectivity (DispatcherAction action,
-                                 NMConnectivityState connectivity_state)
+nm_dispatcher_call_connectivity (NMConnectivityState connectivity_state,
+                                 NMDispatcherFunc callback,
+                                 gpointer user_data,
+                                 guint *out_call_id)
 {
-	return _dispatcher_call (action, FALSE, NULL, NULL, NULL, connectivity_state,
-	                         NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+	return _dispatcher_call (NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE, FALSE,
+	                         NULL, NULL, NULL, FALSE,
+	                         connectivity_state,
+	                         NULL, NULL, NULL, NULL,
+	                         callback, user_data, out_call_id);
 }
 
 void
diff --git a/src/nm-dispatcher.h b/src/nm-dispatcher.h
index 7ea1a6b8..4448e817 100644
--- a/src/nm-dispatcher.h
+++ b/src/nm-dispatcher.h
@@ -19,44 +19,44 @@
  * Copyright (C) 2005 - 2008 Novell, Inc.
  */
 
-#ifndef __NETWORKMANAGER_DISPATCHER_H__
-#define __NETWORKMANAGER_DISPATCHER_H__
-
-#include <stdio.h>
+#ifndef __NM_DISPATCHER_H__
+#define __NM_DISPATCHER_H__
 
 #include "nm-connection.h"
 
 typedef enum {
-	DISPATCHER_ACTION_HOSTNAME,
-	DISPATCHER_ACTION_PRE_UP,
-	DISPATCHER_ACTION_UP,
-	DISPATCHER_ACTION_PRE_DOWN,
-	DISPATCHER_ACTION_DOWN,
-	DISPATCHER_ACTION_VPN_PRE_UP,
-	DISPATCHER_ACTION_VPN_UP,
-	DISPATCHER_ACTION_VPN_PRE_DOWN,
-	DISPATCHER_ACTION_VPN_DOWN,
-	DISPATCHER_ACTION_DHCP4_CHANGE,
-	DISPATCHER_ACTION_DHCP6_CHANGE,
-	DISPATCHER_ACTION_CONNECTIVITY_CHANGE
-} DispatcherAction;
+	NM_DISPATCHER_ACTION_HOSTNAME,
+	NM_DISPATCHER_ACTION_PRE_UP,
+	NM_DISPATCHER_ACTION_UP,
+	NM_DISPATCHER_ACTION_PRE_DOWN,
+	NM_DISPATCHER_ACTION_DOWN,
+	NM_DISPATCHER_ACTION_VPN_PRE_UP,
+	NM_DISPATCHER_ACTION_VPN_UP,
+	NM_DISPATCHER_ACTION_VPN_PRE_DOWN,
+	NM_DISPATCHER_ACTION_VPN_DOWN,
+	NM_DISPATCHER_ACTION_DHCP4_CHANGE,
+	NM_DISPATCHER_ACTION_DHCP6_CHANGE,
+	NM_DISPATCHER_ACTION_CONNECTIVITY_CHANGE
+} NMDispatcherAction;
+
+typedef void (*NMDispatcherFunc) (guint call_id, gpointer user_data);
 
-typedef void (*DispatcherFunc) (guint call_id, gpointer user_data);
+gboolean nm_dispatcher_call_hostname (NMDispatcherFunc callback,
+                                      gpointer user_data,
+                                      guint *out_call_id);
 
-gboolean nm_dispatcher_call (DispatcherAction action,
-                             NMSettingsConnection *settings_connection,
-                             NMConnection *applied_connection,
-                             NMDevice *device,
-                             DispatcherFunc callback,
-                             gpointer user_data,
-                             guint *out_call_id);
+gboolean nm_dispatcher_call_device (NMDispatcherAction action,
+                                    NMDevice *device,
+                                    NMActRequest *act_request,
+                                    NMDispatcherFunc callback,
+                                    gpointer user_data,
+                                    guint *out_call_id);
 
-gboolean nm_dispatcher_call_sync (DispatcherAction action,
-                                  NMSettingsConnection *settings_connection,
-                                  NMConnection *applied_connection,
-                                  NMDevice *device);
+gboolean nm_dispatcher_call_device_sync (NMDispatcherAction action,
+                                         NMDevice *device,
+                                         NMActRequest *act_request);
 
-gboolean nm_dispatcher_call_vpn (DispatcherAction action,
+gboolean nm_dispatcher_call_vpn (NMDispatcherAction action,
                                  NMSettingsConnection *settings_connection,
                                  NMConnection *applied_connection,
                                  NMDevice *parent_device,
@@ -64,11 +64,11 @@ gboolean nm_dispatcher_call_vpn (DispatcherAction action,
                                  NMProxyConfig *vpn_proxy_config,
                                  NMIP4Config *vpn_ip4_config,
                                  NMIP6Config *vpn_ip6_config,
-                                 DispatcherFunc callback,
+                                 NMDispatcherFunc callback,
                                  gpointer user_data,
                                  guint *out_call_id);
 
-gboolean nm_dispatcher_call_vpn_sync (DispatcherAction action,
+gboolean nm_dispatcher_call_vpn_sync (NMDispatcherAction action,
                                       NMSettingsConnection *settings_connection,
                                       NMConnection *applied_connection,
                                       NMDevice *parent_device,
@@ -77,11 +77,14 @@ gboolean nm_dispatcher_call_vpn_sync (DispatcherAction action,
                                       NMIP4Config *vpn_ip4_config,
                                       NMIP6Config *vpn_ip6_config);
 
-gboolean nm_dispatcher_call_connectivity (DispatcherAction action,
-                                          NMConnectivityState state);
+gboolean nm_dispatcher_call_connectivity (NMConnectivityState state,
+                                          NMDispatcherFunc callback,
+                                          gpointer user_data,
+                                          guint *out_call_id);
+
 
 void nm_dispatcher_call_cancel (guint call_id);
 
 void nm_dispatcher_init (void);
 
-#endif /* __NETWORKMANAGER_DISPATCHER_H__ */
+#endif /* __NM_DISPATCHER_H__ */
diff --git a/src/nm-exported-object.c b/src/nm-exported-object.c
index cd0789ee..0e903b89 100644
--- a/src/nm-exported-object.c
+++ b/src/nm-exported-object.c
@@ -77,8 +77,7 @@ typedef struct {
 	GArray *methods;
 } NMExportedObjectClassInfo;
 
-GQuark nm_exported_object_class_info_quark (void);
-G_DEFINE_QUARK (NMExportedObjectClassInfo, nm_exported_object_class_info)
+static NM_CACHED_QUARK_FCN ("NMExportedObjectClassInfo", nm_exported_object_class_info_quark)
 
 /*****************************************************************************/
 
@@ -388,8 +387,7 @@ nm_exported_object_meta_marshal (GClosure *closure, GValue *return_value,
 	g_free (local_param_values);
 }
 
-GQuark _skeleton_data_quark (void);
-G_DEFINE_QUARK (skeleton-data, _skeleton_data);
+static NM_CACHED_QUARK_FCN ("skeleton-data", _skeleton_data_quark)
 
 typedef struct {
 	GBinding **prop_bindings;
@@ -595,7 +593,7 @@ _create_export_path (NMExportedObjectClass *klass)
 		}
 
 		NM_PRAGMA_WARNING_DISABLE("-Wformat-nonliteral")
-		return g_strdup_printf (class_export_path, (long long unsigned) (++(*counter)));
+		return g_strdup_printf (class_export_path, (unsigned long long) (++(*counter)));
 		NM_PRAGMA_WARNING_REENABLE
 	}
 
diff --git a/src/nm-firewall-manager.c b/src/nm-firewall-manager.c
index 5ba3d23e..4a887b79 100644
--- a/src/nm-firewall-manager.c
+++ b/src/nm-firewall-manager.c
@@ -28,22 +28,19 @@
 
 /*****************************************************************************/
 
-NM_GOBJECT_PROPERTIES_DEFINE (NMFirewallManager,
-	PROP_AVAILABLE,
-);
-
 enum {
-	STARTED,
+	STATE_CHANGED,
 	LAST_SIGNAL
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
-	GDBusProxy *    proxy;
-	gboolean        running;
+	GDBusProxy     *proxy;
+	GCancellable   *proxy_cancellable;
 
 	GHashTable     *pending_calls;
+	bool            running;
 } NMFirewallManagerPrivate;
 
 struct _NMFirewallManager {
@@ -73,6 +70,7 @@ typedef enum {
 
 typedef enum {
 	CB_INFO_MODE_IDLE = 1,
+	CB_INFO_MODE_DBUS_WAITING,
 	CB_INFO_MODE_DBUS,
 	CB_INFO_MODE_DBUS_COMPLETED,
 } CBInfoMode;
@@ -80,7 +78,10 @@ typedef enum {
 struct _NMFirewallManagerCallId {
 	NMFirewallManager *self;
 	CBInfoOpsType ops_type;
-	CBInfoMode mode;
+	union {
+		const CBInfoMode mode;
+		CBInfoMode mode_mutable;
+	};
 	char *iface;
 	NMFirewallManagerAddRemoveCallback callback;
 	gpointer user_data;
@@ -88,6 +89,7 @@ struct _NMFirewallManagerCallId {
 	union {
 		struct {
 			GCancellable *cancellable;
+			GVariant *arg;
 		} dbus;
 		struct {
 			guint id;
@@ -118,7 +120,7 @@ _ops_type_to_string (CBInfoOpsType ops_type)
             char __prefix_name[30]; \
             char __prefix_info[64]; \
             \
-            _nm_log ((level), (_NMLOG_DOMAIN), 0, \
+            _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
                      "%s: %s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      (self) != singleton_instance \
                         ? ({ \
@@ -129,7 +131,7 @@ _ops_type_to_string (CBInfoOpsType ops_type)
                      __info \
                         ? ({ \
                                 g_snprintf (__prefix_info, sizeof (__prefix_info), "[%p,%s%s:%s%s%s]: ", __info, \
-                                            _ops_type_to_string (__info->ops_type), _cb_info_is_idle (__info) ? "*" : "", \
+                                            _ops_type_to_string (__info->ops_type), __info->mode == CB_INFO_MODE_IDLE ? "*" : "", \
                                             NM_PRINT_FMT_QUOTE_STRING (__info->iface)); \
                                 __prefix_info; \
                            }) \
@@ -140,16 +142,21 @@ _ops_type_to_string (CBInfoOpsType ops_type)
 
 /*****************************************************************************/
 
-static gboolean
-_cb_info_is_idle (CBInfo *info)
+gboolean
+nm_firewall_manager_get_running (NMFirewallManager *self)
 {
-	return info->mode == CB_INFO_MODE_IDLE;
+	g_return_val_if_fail (NM_IS_FIREWALL_MANAGER (self), FALSE);
+
+	return NM_FIREWALL_MANAGER_GET_PRIVATE (self)->running;
 }
 
+/*****************************************************************************/
+
 static CBInfo *
 _cb_info_create (NMFirewallManager *self,
                  CBInfoOpsType ops_type,
                  const char *iface,
+                 const char *zone,
                  NMFirewallManagerAddRemoveCallback callback,
                  gpointer user_data)
 {
@@ -163,14 +170,14 @@ _cb_info_create (NMFirewallManager *self,
 	info->callback = callback;
 	info->user_data = user_data;
 
-	if (priv->running) {
-		info->mode = CB_INFO_MODE_DBUS;
-		info->dbus.cancellable = g_cancellable_new ();
+	if (priv->running || priv->proxy_cancellable) {
+		info->mode_mutable = CB_INFO_MODE_DBUS_WAITING;
+		info->dbus.arg = g_variant_new ("(ss)", zone ? zone : "", iface);
 	} else
-		info->mode = CB_INFO_MODE_IDLE;
+		info->mode_mutable = CB_INFO_MODE_IDLE;
 
 	if (!nm_g_hash_table_add (priv->pending_calls, info))
-		g_return_val_if_reached (NULL);
+		nm_assert_not_reached ();
 
 	return info;
 }
@@ -178,8 +185,11 @@ _cb_info_create (NMFirewallManager *self,
 static void
 _cb_info_free (CBInfo *info)
 {
-	if (!_cb_info_is_idle (info))
-		g_object_unref (info->dbus.cancellable);
+	if (info->mode != CB_INFO_MODE_IDLE) {
+		if (info->dbus.arg)
+			g_variant_unref (info->dbus.arg);
+		g_clear_object (&info->dbus.cancellable);
+	}
 	g_free (info->iface);
 	if (info->self)
 		g_object_unref (info->self);
@@ -253,14 +263,16 @@ _handle_dbus (GObject *proxy, GAsyncResult *result, gpointer user_data)
 			non_error = "UNKNOWN_INTERFACE";
 			break;
 		}
-		if (!g_strcmp0 (error->message, non_error)) {
+		if (   error->message
+		    && non_error
+		    && g_str_has_prefix (error->message, non_error)
+		    && NM_IN_SET (error->message[strlen (non_error)], '\0', ':')) {
 			_LOGD (info, "complete: request failed with a non-error (%s)", error->message);
 
 			/* The operation failed with an error reason that we don't want
 			 * to propagate. Instead, signal success. */
 			g_clear_error (&error);
-		}
-		else
+		} else
 			_LOGW (info, "complete: request failed (%s)", error->message);
 	} else
 		_LOGD (info, "complete: success");
@@ -268,6 +280,48 @@ _handle_dbus (GObject *proxy, GAsyncResult *result, gpointer user_data)
 	_cb_info_complete_normal (info, error);
 }
 
+static void
+_handle_dbus_start (NMFirewallManager *self,
+                    CBInfo *info)
+{
+	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
+	const char *dbus_method = NULL;
+	GVariant *arg;
+
+	nm_assert (info);
+	nm_assert (priv->running);
+	nm_assert (info->mode == CB_INFO_MODE_DBUS_WAITING);
+
+	switch (info->ops_type) {
+	case CB_INFO_OPS_ADD:
+		dbus_method = "addInterface";
+		break;
+	case CB_INFO_OPS_CHANGE:
+		dbus_method = "changeZone";
+		break;
+	case CB_INFO_OPS_REMOVE:
+		dbus_method = "removeInterface";
+		break;
+	}
+	nm_assert (dbus_method);
+
+	arg = info->dbus.arg;
+	info->dbus.arg = NULL;
+
+	nm_assert (arg && g_variant_is_floating (arg));
+
+	info->mode_mutable = CB_INFO_MODE_DBUS;
+	info->dbus.cancellable = g_cancellable_new ();
+
+	g_dbus_proxy_call (priv->proxy,
+	                   dbus_method,
+	                   arg,
+	                   G_DBUS_CALL_FLAGS_NONE, 10000,
+	                   info->dbus.cancellable,
+	                   _handle_dbus,
+	                   info);
+}
+
 static NMFirewallManagerCallId
 _start_request (NMFirewallManager *self,
                 CBInfoOpsType ops_type,
@@ -278,45 +332,27 @@ _start_request (NMFirewallManager *self,
 {
 	NMFirewallManagerPrivate *priv;
 	CBInfo *info;
-	const char *dbus_method;
 
 	g_return_val_if_fail (NM_IS_FIREWALL_MANAGER (self), NULL);
 	g_return_val_if_fail (iface && *iface, NULL);
 
 	priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
 
-	info = _cb_info_create (self, ops_type, iface, callback, user_data);
+	info = _cb_info_create (self, ops_type, iface, zone, callback, user_data);
 
 	_LOGD (info, "firewall zone %s %s:%s%s%s%s",
 	       _ops_type_to_string (info->ops_type),
 	       iface,
 	       NM_PRINT_FMT_QUOTED (zone, "\"", zone, "\"", "default"),
-	       _cb_info_is_idle (info) ? " (not running, simulate success)" : "");
-
-	if (!_cb_info_is_idle (info)) {
-
-		switch (ops_type) {
-		case CB_INFO_OPS_ADD:
-			dbus_method = "addInterface";
-			break;
-		case CB_INFO_OPS_CHANGE:
-			dbus_method = "changeZone";
-			break;
-		case CB_INFO_OPS_REMOVE:
-			dbus_method = "removeInterface";
-			break;
-		default:
-			g_assert_not_reached ();
-		}
-
-		g_dbus_proxy_call (priv->proxy,
-		                   dbus_method,
-		                   g_variant_new ("(ss)", zone ? zone : "", iface),
-		                   G_DBUS_CALL_FLAGS_NONE, 10000,
-		                   info->dbus.cancellable,
-		                   _handle_dbus,
-		                   info);
-
+	       info->mode == CB_INFO_MODE_IDLE
+	         ? " (not running, simulate success)"
+	         : (!priv->running
+	              ? " (waiting to initialize)"
+	              : ""));
+
+	if (info->mode == CB_INFO_MODE_DBUS_WAITING) {
+		if (priv->running)
+			_handle_dbus_start (self, info);
 		if (!info->callback) {
 			/* if the user did not provide a callback, the call_id is useless.
 			 * Especially, the user cannot use the call-id to cancel the request,
@@ -326,15 +362,18 @@ _start_request (NMFirewallManager *self,
 			 * (the request will always be started). */
 			return NULL;
 		}
-	} else if (!info->callback) {
-		/* if the user did not provide a callback and firewalld is not running,
-		 * there is no point in scheduling an idle-request to fake success. Just
-		 * return right away. */
-		_LOGD (info, "complete: drop request simulating success");
-		_cb_info_complete_normal (info, NULL);
-		return NULL;
+	} else if (info->mode == CB_INFO_MODE_IDLE) {
+		if (!info->callback) {
+			/* if the user did not provide a callback and firewalld is not running,
+			 * there is no point in scheduling an idle-request to fake success. Just
+			 * return right away. */
+			_LOGD (info, "complete: drop request simulating success");
+			_cb_info_complete_normal (info, NULL);
+			return NULL;
+		} else
+			info->idle.id = g_idle_add (_handle_idle, info);
 	} else
-		info->idle.id = g_idle_add (_handle_idle, info);
+		nm_assert_not_reached ();
 
 	return info;
 }
@@ -393,11 +432,13 @@ nm_firewall_manager_cancel_call (NMFirewallManagerCallId call)
 
 	_cb_info_callback (info, error);
 
-	if (_cb_info_is_idle (info)) {
+	if (info->mode == CB_INFO_MODE_DBUS_WAITING)
+		_cb_info_free (info);
+	else if (info->mode == CB_INFO_MODE_IDLE) {
 		g_source_remove (info->idle.id);
 		_cb_info_free (info);
 	} else {
-		info->mode = CB_INFO_MODE_DBUS_COMPLETED;
+		info->mode_mutable = CB_INFO_MODE_DBUS_COMPLETED;
 		g_cancellable_cancel (info->dbus.cancellable);
 		g_clear_object (&info->self);
 	}
@@ -405,49 +446,92 @@ nm_firewall_manager_cancel_call (NMFirewallManagerCallId call)
 
 /*****************************************************************************/
 
-static void
-set_running (NMFirewallManager *self, gboolean now_running)
+static gboolean
+name_owner_changed (NMFirewallManager *self)
 {
 	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
-	gboolean old_running = priv->running;
+	gs_free char *owner = NULL;
+	gboolean now_running;
+
+	owner = g_dbus_proxy_get_name_owner (priv->proxy);
+	now_running = !!owner;
+
+	if (now_running == priv->running)
+		return FALSE;
 
 	priv->running = now_running;
-	if (old_running != priv->running)
-		_notify (self, PROP_AVAILABLE);
+	_LOGD (NULL, "firewall %s", now_running ? "started" : "stopped");
+	return TRUE;
 }
 
 static void
-name_owner_changed (GObject    *object,
-                    GParamSpec *pspec,
-                    gpointer    user_data)
+name_owner_changed_cb (GObject    *object,
+                       GParamSpec *pspec,
+                       gpointer    user_data)
 {
-	NMFirewallManager *self = NM_FIREWALL_MANAGER (user_data);
-	gs_free char *owner = NULL;
+	NMFirewallManager *self = user_data;
 
-	owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object));
-	if (owner) {
-		_LOGD (NULL, "firewall started");
-		set_running (self, TRUE);
-		g_signal_emit (self, signals[STARTED], 0);
-	} else {
-		_LOGD (NULL, "firewall stopped");
-		set_running (self, FALSE);
-	}
-}
+	nm_assert (NM_IS_FIREWALL_MANAGER (self));
+	nm_assert (G_IS_DBUS_PROXY (object));
+	nm_assert (NM_FIREWALL_MANAGER_GET_PRIVATE (self)->proxy == G_DBUS_PROXY (object));
 
-/*****************************************************************************/
+	if (name_owner_changed (self))
+		g_signal_emit (self, signals[STATE_CHANGED], 0, FALSE);
+}
 
 static void
-get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+_proxy_new_cb (GObject *source_object,
+               GAsyncResult *result,
+               gpointer user_data)
 {
-	switch (prop_id) {
-	case PROP_AVAILABLE:
-		g_value_set_boolean (value, NM_FIREWALL_MANAGER_GET_PRIVATE ((NMFirewallManager *) object)->running);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
+	NMFirewallManager *self;
+	NMFirewallManagerPrivate *priv;
+	GDBusProxy *proxy;
+	gs_free_error GError *error = NULL;
+	GHashTableIter iter;
+	CBInfo *info;
+
+	proxy = g_dbus_proxy_new_for_bus_finish (result, &error);
+	if (   !proxy
+	    && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = user_data;
+	priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
+	g_clear_object (&priv->proxy_cancellable);
+
+	if (!proxy) {
+		_LOGW (NULL, "could not connect to system D-Bus (%s)", error->message);
+		return;
+	}
+
+	priv->proxy = proxy;
+	g_signal_connect (priv->proxy, "notify::g-name-owner",
+	                  G_CALLBACK (name_owner_changed_cb), self);
+
+	if (!name_owner_changed (self))
+		_LOGD (NULL, "firewall %s", "initialized (not running)");
+
+again:
+	g_hash_table_iter_init (&iter, priv->pending_calls);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &info, NULL)) {
+		if (info->mode != CB_INFO_MODE_DBUS_WAITING)
+			continue;
+		if (priv->running) {
+			_LOGD (info, "make D-Bus call");
+			_handle_dbus_start (self, info);
+		} else {
+			_LOGD (info, "complete: fake success");
+			g_hash_table_iter_remove (&iter);
+			_cb_info_callback (info, NULL);
+			_cb_info_free (info);
+			goto again;
+		}
 	}
+
+	/* we always emit a state-changed signal, even if the
+	 * "running" property is still false. */
+	g_signal_emit (self, signals[STATE_CHANGED], 0, TRUE);
 }
 
 /*****************************************************************************/
@@ -465,28 +549,21 @@ constructed (GObject *object)
 {
 	NMFirewallManager *self = (NMFirewallManager *) object;
 	NMFirewallManagerPrivate *priv = NM_FIREWALL_MANAGER_GET_PRIVATE (self);
-	gs_free char *owner = NULL;
-	gs_free_error GError *error = NULL;
 
-	G_OBJECT_CLASS (nm_firewall_manager_parent_class)->constructed (object);
+	priv->proxy_cancellable = g_cancellable_new ();
 
-	priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
-	                                             G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-	                                                 G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
-	                                             NULL,
-	                                             FIREWALL_DBUS_SERVICE,
-	                                             FIREWALL_DBUS_PATH,
-	                                             FIREWALL_DBUS_INTERFACE_ZONE,
-	                                             NULL, &error);
-	if (priv->proxy) {
-		g_signal_connect (priv->proxy, "notify::g-name-owner",
-		                  G_CALLBACK (name_owner_changed), self);
-		owner = g_dbus_proxy_get_name_owner (priv->proxy);
-		priv->running = (owner != NULL);
-	} else
-		_LOGW (NULL, "could not connect to system D-Bus (%s)", error->message);
+	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+	                            G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES
+	                          | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+	                          NULL,
+	                          FIREWALL_DBUS_SERVICE,
+	                          FIREWALL_DBUS_PATH,
+	                          FIREWALL_DBUS_INTERFACE_ZONE,
+	                          priv->proxy_cancellable,
+	                          _proxy_new_cb,
+	                          self);
 
-	_LOGD (NULL, "firewall constructed (%srunning)", priv->running ? "" : "not");
+	G_OBJECT_CLASS (nm_firewall_manager_parent_class)->constructed (object);
 }
 
 static void
@@ -503,6 +580,7 @@ dispose (GObject *object)
 		priv->pending_calls = NULL;
 	}
 
+	nm_clear_g_cancellable (&priv->proxy_cancellable);
 	g_clear_object (&priv->proxy);
 
 	G_OBJECT_CLASS (nm_firewall_manager_parent_class)->dispose (object);
@@ -514,23 +592,15 @@ nm_firewall_manager_class_init (NMFirewallManagerClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
 	object_class->constructed = constructed;
-	object_class->get_property = get_property;
 	object_class->dispose = dispose;
 
-	obj_properties[PROP_AVAILABLE] =
-	     g_param_spec_boolean (NM_FIREWALL_MANAGER_AVAILABLE, "", "",
-	                           FALSE,
-	                           G_PARAM_READABLE |
-	                           G_PARAM_STATIC_STRINGS);
-
-	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-
-	signals[STARTED] =
-	    g_signal_new (NM_FIREWALL_MANAGER_STARTED,
+	signals[STATE_CHANGED] =
+	    g_signal_new (NM_FIREWALL_MANAGER_STATE_CHANGED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0,
 	                  NULL, NULL,
-	                  g_cclosure_marshal_VOID__VOID,
-	                  G_TYPE_NONE, 0);
+	                  g_cclosure_marshal_VOID__BOOLEAN,
+	                  G_TYPE_NONE, 1,
+	                  G_TYPE_BOOLEAN /* initialized_now */);
 }
diff --git a/src/nm-firewall-manager.h b/src/nm-firewall-manager.h
index a00ee451..8bbf82a7 100644
--- a/src/nm-firewall-manager.h
+++ b/src/nm-firewall-manager.h
@@ -33,9 +33,7 @@
 #define NM_IS_FIREWALL_MANAGER_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass),  NM_TYPE_FIREWALL_MANAGER))
 #define NM_FIREWALL_MANAGER_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj),  NM_TYPE_FIREWALL_MANAGER, NMFirewallManagerClass))
 
-#define NM_FIREWALL_MANAGER_AVAILABLE "available"
-
-#define NM_FIREWALL_MANAGER_STARTED "started"
+#define NM_FIREWALL_MANAGER_STATE_CHANGED "state-changed"
 
 typedef struct _NMFirewallManagerCallId *NMFirewallManagerCallId;
 
@@ -46,6 +44,8 @@ GType nm_firewall_manager_get_type (void);
 
 NMFirewallManager *nm_firewall_manager_get (void);
 
+gboolean nm_firewall_manager_get_running (NMFirewallManager *self);
+
 typedef void (*NMFirewallManagerAddRemoveCallback) (NMFirewallManager *self,
                                                     NMFirewallManagerCallId call_id,
                                                     GError *error,
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
index 4405c325..f8df2b9a 100644
--- a/src/nm-iface-helper.c
+++ b/src/nm-iface-helper.c
@@ -42,6 +42,7 @@
 #include "nm-utils.h"
 #include "nm-setting-ip6-config.h"
 #include "systemd/nm-sd.h"
+#include "nm-route-manager.h"
 
 #if !defined(NM_DIST_VERSION)
 # define NM_DIST_VERSION VERSION
@@ -91,12 +92,18 @@ static struct {
 
 #define _NMLOG_PREFIX_NAME      "nm-iface-helper"
 #define _NMLOG(level, domain, ...) \
-    nm_log ((level), (domain), \
+    nm_log ((level), (domain), global_opt.ifname, NULL, \
             "iface-helper: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__) \
             _NM_UTILS_MACRO_REST (__VA_ARGS__))
 
 /*****************************************************************************/
 
+NMRouteManager *route_manager_get (void);
+
+NM_DEFINE_SINGLETON_GETTER (NMRouteManager, route_manager_get, NM_TYPE_ROUTE_MANAGER);
+
+/*****************************************************************************/
+
 static void
 dhcp4_state_changed (NMDhcpClient *client,
                      NMDhcpState state,
@@ -115,12 +122,12 @@ dhcp4_state_changed (NMDhcpClient *client,
 	switch (state) {
 	case NM_DHCP_STATE_BOUND:
 		g_assert (ip4_config);
-		existing = nm_ip4_config_capture (gl.ifindex, FALSE);
+		existing = nm_ip4_config_capture (NM_PLATFORM_GET, gl.ifindex, FALSE);
 		if (last_config)
 			nm_ip4_config_subtract (existing, last_config);
 
 		nm_ip4_config_merge (existing, ip4_config, NM_IP_CONFIG_MERGE_DEFAULT);
-		if (!nm_ip4_config_commit (existing, gl.ifindex, TRUE, global_opt.priority_v4))
+		if (!nm_ip4_config_commit (existing, NM_PLATFORM_GET, route_manager_get (), gl.ifindex, TRUE, global_opt.priority_v4))
 			_LOGW (LOGD_DHCP4, "failed to apply DHCPv4 config");
 
 		if (last_config)
@@ -170,7 +177,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 		ifa_flags |= IFA_F_MANAGETEMPADDR;
 	}
 
-	existing = nm_ip6_config_capture (gl.ifindex, FALSE, global_opt.tempaddr);
+	existing = nm_ip6_config_capture (NM_PLATFORM_GET, gl.ifindex, FALSE, global_opt.tempaddr);
 	if (ndisc_config)
 		nm_ip6_config_subtract (existing, ndisc_config);
 	else
@@ -245,7 +252,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in
 	}
 
 	nm_ip6_config_merge (existing, ndisc_config, NM_IP_CONFIG_MERGE_DEFAULT);
-	if (!nm_ip6_config_commit (existing, gl.ifindex, TRUE))
+	if (!nm_ip6_config_commit (existing, NM_PLATFORM_GET, route_manager_get (), gl.ifindex, TRUE))
 		_LOGW (LOGD_IP6, "failed to apply IPv6 config");
 }
 
@@ -432,9 +439,8 @@ main (int argc, char *argv[])
 	gl.main_loop = g_main_loop_new (NULL, FALSE);
 	setup_signals ();
 
-	nm_logging_syslog_openlog (global_opt.logging_backend
-	                           ? global_opt.logging_backend
-	                           : (global_opt.debug ? "debug" : NULL));
+	nm_logging_syslog_openlog (global_opt.logging_backend,
+	                           global_opt.debug);
 
 	_LOGI (LOGD_CORE, "nm-iface-helper (version " NM_DIST_VERSION ") is starting...");
 
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 1486edcc..20532e86 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -249,7 +249,7 @@ notify_addresses (NMIP4Config *self)
 }
 
 NMIP4Config *
-nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
+nm_ip4_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf)
 {
 	NMIP4Config *config;
 	NMIP4ConfigPrivate *priv;
@@ -259,7 +259,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 	gboolean old_has_gateway = FALSE;
 
 	/* Slaves have no IP configuration */
-	if (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex) > 0)
+	if (nm_platform_link_get_master (platform, ifindex) > 0)
 		return NULL;
 
 	config = nm_ip4_config_new (ifindex);
@@ -268,8 +268,8 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 	g_array_unref (priv->addresses);
 	g_array_unref (priv->routes);
 
-	priv->addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex);
-	priv->routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
+	priv->addresses = nm_platform_ip4_address_get_all (platform, ifindex);
+	priv->routes = nm_platform_ip4_route_get_all (platform, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
 
 	/* Extract gateway from default route */
 	old_gateway = priv->gateway;
@@ -331,7 +331,7 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
 }
 
 gboolean
-nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_full_sync, gint64 default_route_metric)
+nm_ip4_config_commit (const NMIP4Config *config, NMPlatform *platform, NMRouteManager *route_manager, int ifindex, gboolean routes_full_sync, gint64 default_route_metric)
 {
 	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
 	gs_unref_ptrarray GPtrArray *added_addresses = NULL;
@@ -340,7 +340,7 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_fu
 	g_return_val_if_fail (config != NULL, FALSE);
 
 	/* Addresses */
-	nm_platform_ip4_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses,
+	nm_platform_ip4_address_sync (platform, ifindex, priv->addresses,
 	                              default_route_metric >= 0 ? &added_addresses : NULL);
 
 	/* Routes */
@@ -401,9 +401,9 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_fu
 			g_array_append_vals (routes, route, 1);
 		}
 
-		nm_route_manager_ip4_route_register_device_route_purge_list (nm_route_manager_get (), device_route_purge_list);
+		nm_route_manager_ip4_route_register_device_route_purge_list (route_manager, device_route_purge_list);
 
-		success = nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, default_route_metric < 0, routes_full_sync);
+		success = nm_route_manager_ip4_route_sync (route_manager, ifindex, routes, default_route_metric < 0, routes_full_sync);
 		g_array_unref (routes);
 		if (!success)
 			return FALSE;
@@ -412,6 +412,38 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_fu
 	return TRUE;
 }
 
+static void
+merge_route_attributes (NMIPRoute *s_route, NMPlatformIP4Route *r)
+{
+	GVariant *variant;
+	in_addr_t addr;
+
+#define GET_ATTR(name, field, variant_type, type) \
+	variant = nm_ip_route_get_attribute (s_route, name); \
+	if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_ ## variant_type)) \
+		r->field = g_variant_get_ ## type (variant);
+
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS,            tos,            BYTE,     byte);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW,         window,         UINT32,   uint32);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND,           cwnd,           UINT32,   uint32);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND,       initcwnd,       UINT32,   uint32);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITRWND,       initrwnd,       UINT32,   uint32);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_MTU,            mtu,            UINT32,   uint32);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW,    lock_window,    BOOLEAN,  boolean);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND,      lock_cwnd,      BOOLEAN,  boolean);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND,  lock_initcwnd,  BOOLEAN,  boolean);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND,  lock_initrwnd,  BOOLEAN,  boolean);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU,       lock_mtu,       BOOLEAN,  boolean);
+
+	if (   (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_SRC))
+	    && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) {
+		if (inet_pton (AF_INET, g_variant_get_string (variant, NULL), &addr) == 1)
+			r->pref_src = addr;
+	}
+
+#undef GET_ATTR
+}
+
 void
 nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric)
 {
@@ -492,6 +524,7 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu
 			route.metric = nm_ip_route_get_metric (s_route);
 		route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 
+		merge_route_attributes (s_route, &route);
 		nm_ip4_config_add_route (config, &route);
 	}
 
@@ -1822,7 +1855,7 @@ nm_ip4_config_add_search (NMIP4Config *config, const char *new)
 		return;
 	}
 
-	if (_nm_utils_strv_find_first ((char **) priv->searches->pdata,
+	if (nm_utils_strv_find_first ((char **) priv->searches->pdata,
 	                               priv->searches->len, search) >= 0) {
 		g_free (search);
 		return;
diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
index 58ee4a7d..ceb52ac5 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -59,8 +59,8 @@ NMIP4Config * nm_ip4_config_new (int ifindex);
 int nm_ip4_config_get_ifindex (const NMIP4Config *config);
 
 
-NMIP4Config *nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf);
-gboolean nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_full_sync, gint64 default_route_metric);
+NMIP4Config *nm_ip4_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf);
+gboolean nm_ip4_config_commit (const NMIP4Config *config, NMPlatform *platform, NMRouteManager *route_manager, int ifindex, gboolean routes_full_sync, gint64 default_route_metric);
 void nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, guint32 default_route_metric);
 NMSetting *nm_ip4_config_create_setting (const NMIP4Config *config);
 
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index c90d04aa..af88b21c 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -301,7 +301,7 @@ nm_ip6_config_addresses_sort (NMIP6Config *self)
 }
 
 NMIP6Config *
-nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary)
+nm_ip6_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary)
 {
 	NMIP6Config *config;
 	NMIP6ConfigPrivate *priv;
@@ -312,7 +312,7 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6Co
 	gboolean notify_nameservers = FALSE;
 
 	/* Slaves have no IP configuration */
-	if (nm_platform_link_get_master (NM_PLATFORM_GET, ifindex) > 0)
+	if (nm_platform_link_get_master (platform, ifindex) > 0)
 		return NULL;
 
 	config = nm_ip6_config_new (ifindex);
@@ -321,8 +321,8 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6Co
 	g_array_unref (priv->addresses);
 	g_array_unref (priv->routes);
 
-	priv->addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex);
-	priv->routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
+	priv->addresses = nm_platform_ip6_address_get_all (platform, ifindex);
+	priv->routes = nm_platform_ip6_route_get_all (platform, ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
 
 	/* Extract gateway from default route */
 	old_gateway = priv->gateway;
@@ -386,7 +386,11 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6Co
 }
 
 gboolean
-nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_full_sync)
+nm_ip6_config_commit (const NMIP6Config *config,
+                      NMPlatform *platform,
+                      NMRouteManager *route_manager,
+                      int ifindex,
+                      gboolean routes_full_sync)
 {
 	const NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
 	gboolean success;
@@ -395,7 +399,7 @@ nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_fu
 	g_return_val_if_fail (config != NULL, FALSE);
 
 	/* Addresses */
-	nm_platform_ip6_address_sync (NM_PLATFORM_GET, ifindex, priv->addresses, TRUE);
+	nm_platform_ip6_address_sync (platform, ifindex, priv->addresses, TRUE);
 
 	/* Routes */
 	{
@@ -409,13 +413,64 @@ nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_fu
 			g_array_append_vals (routes, route, 1);
 		}
 
-		success = nm_route_manager_ip6_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, routes_full_sync);
+		success = nm_route_manager_ip6_route_sync (route_manager, ifindex, routes, TRUE, routes_full_sync);
 		g_array_unref (routes);
 	}
 
 	return success;
 }
 
+static void
+merge_route_attributes (NMIPRoute *s_route, NMPlatformIP6Route *r)
+{
+	GVariant *variant;
+	struct in6_addr addr;
+
+#define GET_ATTR(name, field, variant_type, type) \
+	variant = nm_ip_route_get_attribute (s_route, name); \
+	if (variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_ ## variant_type)) \
+		r->field = g_variant_get_ ## type (variant);
+
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS,            tos,            BYTE,     byte);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW,         window,         UINT32,   uint32);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND,           cwnd,           UINT32,   uint32);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND,       initcwnd,       UINT32,   uint32);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITRWND,       initrwnd,       UINT32,   uint32);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_MTU,            mtu,            UINT32,   uint32);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW,    lock_window,    BOOLEAN,  boolean);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND,      lock_cwnd,      BOOLEAN,  boolean);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND,  lock_initcwnd,  BOOLEAN,  boolean);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND,  lock_initrwnd,  BOOLEAN,  boolean);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU,       lock_mtu,       BOOLEAN,  boolean);
+
+
+	if (   (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_SRC))
+	    && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) {
+		if (inet_pton (AF_INET6, g_variant_get_string (variant, NULL), &addr) == 1)
+			r->pref_src = addr;
+	}
+
+	if (   (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_FROM))
+	    && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) {
+		gs_free char *string = NULL;
+		guint8 plen = 128;
+		char *sep;
+
+		string = g_variant_dup_string (variant, NULL);
+		sep = strchr (string, '/');
+		if (sep) {
+			*sep = 0;
+			plen = _nm_utils_ascii_str_to_int64 (sep + 1, 10, 1, 128, 255);
+		}
+		if (   plen <= 128
+		    && inet_pton (AF_INET6, string, &addr) == 1) {
+			r->src = addr;
+			r->src_plen = plen;
+		}
+	}
+#undef GET_ATTR
+}
+
 void
 nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, guint32 default_route_metric)
 {
@@ -492,6 +547,7 @@ nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, gu
 			route.metric = nm_ip_route_get_metric (s_route);
 		route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 
+		merge_route_attributes (s_route, &route);
 		nm_ip6_config_add_route (config, &route);
 	}
 
@@ -1742,7 +1798,7 @@ nm_ip6_config_add_search (NMIP6Config *config, const char *new)
 		return;
 	}
 
-	if (_nm_utils_strv_find_first ((char **) priv->searches->pdata,
+	if (nm_utils_strv_find_first ((char **) priv->searches->pdata,
 	                               priv->searches->len, search) >= 0) {
 		g_free (search);
 		return;
diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h
index c196421b..557041c9 100644
--- a/src/nm-ip6-config.h
+++ b/src/nm-ip6-config.h
@@ -61,8 +61,12 @@ NMIP6Config * nm_ip6_config_new_cloned (const NMIP6Config *src);
 int nm_ip6_config_get_ifindex (const NMIP6Config *config);
 
 
-NMIP6Config *nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary);
-gboolean nm_ip6_config_commit (const NMIP6Config *config, int ifindex, gboolean routes_full_sync);
+NMIP6Config *nm_ip6_config_capture (NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary);
+gboolean nm_ip6_config_commit (const NMIP6Config *config,
+                               NMPlatform *platform,
+                               NMRouteManager *route_manager,
+                               int ifindex,
+                               gboolean routes_full_sync);
 void nm_ip6_config_merge_setting (NMIP6Config *config, NMSettingIPConfig *setting, guint32 default_route_metric);
 NMSetting *nm_ip6_config_create_setting (const NMIP6Config *config);
 
diff --git a/src/nm-logging.c b/src/nm-logging.c
index c242d903..f19f0de1 100644
--- a/src/nm-logging.c
+++ b/src/nm-logging.c
@@ -94,6 +94,7 @@ static struct Global {
 	NMLogLevel log_level;
 	bool uses_syslog:1;
 	bool syslog_identifier_initialized:1;
+	bool debug_stderr:1;
 	const char *prefix;
 	const char *syslog_identifier;
 	enum {
@@ -593,6 +594,8 @@ _nm_log_impl (const char *file,
               NMLogLevel level,
               NMLogDomain domain,
               int error,
+              const char *ifname,
+              const char *conn_uuid,
               const char *fmt,
               ...)
 {
@@ -630,6 +633,9 @@ _nm_log_impl (const char *file,
 
 	g_get_current_time (&tv);
 
+	if (global.debug_stderr)
+		g_printerr (MESSAGE_FMT"\n", MESSAGE_ARG (global, tv, msg));
+
 	switch (global.log_backend) {
 #if SYSTEMD_JOURNAL
 	case LOG_BACKEND_JOURNAL:
@@ -638,7 +644,7 @@ _nm_log_impl (const char *file,
 #define _NUM_MAX_FIELDS_SYSLOG_FACILITY 10
 			struct iovec iov_data[12 + _NUM_MAX_FIELDS_SYSLOG_FACILITY];
 			struct iovec *iov = iov_data;
-			gpointer iov_free_data[3];
+			gpointer iov_free_data[5];
 			gpointer *iov_free = iov_free_data;
 			nm_auto_free_gstring GString *s_domain_all = NULL;
 
@@ -700,6 +706,10 @@ _nm_log_impl (const char *file,
 			_iovec_set_format_a (iov++, 60, "TIMESTAMP_BOOTTIME=%lld.%06lld", (long long) (boottime / NM_UTILS_NS_PER_SECOND), (long long) ((boottime % NM_UTILS_NS_PER_SECOND) / 1000));
 			if (error != 0)
 				_iovec_set_format_a (iov++, 30, "ERRNO=%d", error);
+			if (ifname)
+				_iovec_set_format (iov++, iov_free++, "NM_DEVICE=%s", ifname);
+			if (conn_uuid)
+				_iovec_set_format (iov++, iov_free++, "NM_CONNECTION=%s", conn_uuid);
 
 			nm_assert (iov <= &iov_data[G_N_ELEMENTS (iov_data)]);
 			nm_assert (iov_free <= &iov_free_data[G_N_ELEMENTS (iov_free_data)]);
@@ -817,7 +827,7 @@ nm_logging_set_prefix (const char *format, ...)
 }
 
 void
-nm_logging_syslog_openlog (const char *logging_backend)
+nm_logging_syslog_openlog (const char *logging_backend, gboolean debug)
 {
 	if (global.log_backend != LOG_BACKEND_GLIB)
 		g_return_if_reached ();
@@ -825,21 +835,21 @@ nm_logging_syslog_openlog (const char *logging_backend)
 	if (!logging_backend)
 		logging_backend = ""NM_CONFIG_DEFAULT_LOGGING_BACKEND;
 
-	if (strcmp (logging_backend, "debug") == 0) {
-		global.log_backend = LOG_BACKEND_SYSLOG;
-		openlog (syslog_identifier_domain (&global), LOG_CONS | LOG_PERROR | LOG_PID, LOG_USER);
 #if SYSTEMD_JOURNAL
-	} else if (strcmp (logging_backend, "syslog") != 0) {
+	if (strcmp (logging_backend, "syslog") != 0) {
 		global.log_backend = LOG_BACKEND_JOURNAL;
 		global.uses_syslog = TRUE;
+		global.debug_stderr = debug;
 
 		/* ensure we read a monotonic timestamp. Reading the timestamp the first
 		 * time causes a logging message. We don't want to do that during _nm_log_impl. */
 		nm_utils_get_monotonic_timestamp_ns ();
+	} else
 #endif
-	} else {
+	{
 		global.log_backend = LOG_BACKEND_SYSLOG;
 		global.uses_syslog = TRUE;
+		global.debug_stderr = debug;
 		openlog (syslog_identifier_domain (&global), LOG_PID, LOG_DAEMON);
 	}
 
diff --git a/src/nm-logging.h b/src/nm-logging.h
index 2c1a1059..ff1fac79 100644
--- a/src/nm-logging.h
+++ b/src/nm-logging.h
@@ -22,6 +22,8 @@
 #ifndef __NETWORKMANAGER_LOGGING_H__
 #define __NETWORKMANAGER_LOGGING_H__
 
+#include "nm-core-types.h"
+
 #ifdef __NM_TEST_UTILS_H__
 #error nm-test-utils.h must be included as last header
 #endif
@@ -97,11 +99,11 @@ typedef enum  { /*< skip >*/
 	_LOGL_N, /* the number of logging levels including "OFF" */
 } NMLogLevel;
 
-#define nm_log_err(domain, ...)     nm_log (LOGL_ERR,   (domain), __VA_ARGS__)
-#define nm_log_warn(domain, ...)    nm_log (LOGL_WARN,  (domain), __VA_ARGS__)
-#define nm_log_info(domain, ...)    nm_log (LOGL_INFO,  (domain), __VA_ARGS__)
-#define nm_log_dbg(domain, ...)     nm_log (LOGL_DEBUG, (domain), __VA_ARGS__)
-#define nm_log_trace(domain, ...)   nm_log (LOGL_TRACE, (domain), __VA_ARGS__)
+#define nm_log_err(domain, ...)     nm_log (LOGL_ERR,   (domain),  NULL, NULL, __VA_ARGS__)
+#define nm_log_warn(domain, ...)    nm_log (LOGL_WARN,  (domain),  NULL, NULL, __VA_ARGS__)
+#define nm_log_info(domain, ...)    nm_log (LOGL_INFO,  (domain),  NULL, NULL, __VA_ARGS__)
+#define nm_log_dbg(domain, ...)     nm_log (LOGL_DEBUG, (domain),  NULL, NULL, __VA_ARGS__)
+#define nm_log_trace(domain, ...)   nm_log (LOGL_TRACE, (domain),  NULL, NULL, __VA_ARGS__)
 
 //#define _NM_LOG_FUNC G_STRFUNC
 #define _NM_LOG_FUNC NULL
@@ -109,53 +111,84 @@ typedef enum  { /*< skip >*/
 /* A wrapper for the _nm_log_impl() function that adds call site information.
  * Contrary to nm_log(), it unconditionally calls the function without
  * checking whether logging for the given level and domain is enabled. */
-#define _nm_log(level, domain, error, ...) \
+#define _nm_log(level, domain, error, ifname, con_uuid, ...) \
     G_STMT_START { \
         _nm_log_impl (__FILE__, __LINE__, \
                       _NM_LOG_FUNC, \
                       (level), \
                       (domain), \
                       (error), \
+                      (ifname), \
+                      (con_uuid), \
                       ""__VA_ARGS__); \
     } G_STMT_END
 
 /* nm_log() only evaluates it's argument list after checking
  * whether logging for the given level/domain is enabled.  */
-#define nm_log(level, domain, ...) \
+#define nm_log(level, domain, ifname, con_uuid, ...) \
     G_STMT_START { \
         if (nm_logging_enabled ((level), (domain))) { \
-            _nm_log (level, domain, 0, __VA_ARGS__); \
+            _nm_log (level, domain, 0, ifname, con_uuid, __VA_ARGS__); \
         } \
     } G_STMT_END
 
 
-#define _nm_log_ptr(level, domain, self, prefix, ...) \
-   nm_log ((level), (domain), "%s[%p] " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), (prefix) ?: "", self _NM_UTILS_MACRO_REST(__VA_ARGS__))
+#define _nm_log_ptr(level, domain, ifname, con_uuid, self, prefix, ...) \
+   nm_log ((level), \
+           (domain), \
+           (ifname), \
+           (con_uuid), \
+           "%s[%p] " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+           (prefix) ?: "", \
+           self _NM_UTILS_MACRO_REST(__VA_ARGS__))
 
 /* log a message for an object (with providing a generic @self pointer) */
-#define nm_log_ptr(level, domain, self, prefix, ...) \
+#define nm_log_ptr(level, domain, ifname, con_uuid, self, prefix, ...) \
     G_STMT_START { \
         NM_PRAGMA_WARNING_DISABLE("-Wtautological-compare") \
         if ((level) <= LOGL_DEBUG) { \
-            _nm_log_ptr ((level), (domain), (self), (prefix), __VA_ARGS__); \
+            _nm_log_ptr ((level), \
+                         (domain), \
+                         (ifname), \
+                         (con_uuid), \
+                         (self), \
+                         (prefix), \
+                         __VA_ARGS__); \
         } else { \
             const char *__prefix = (prefix); \
             \
-            nm_log ((level), (domain), "%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), __prefix ?: "", __prefix ? " " : "" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+            nm_log ((level), \
+                    (domain), \
+                    (ifname), \
+                    (con_uuid), \
+                    "%s%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+                    __prefix ?: "", \
+                    __prefix ? " " : "" _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
         NM_PRAGMA_WARNING_REENABLE \
     } G_STMT_END
 
 
-#define _nm_log_obj(level, domain, self, prefix, ...) \
-    _nm_log_ptr ((level), (domain), (self), prefix, __VA_ARGS__)
+#define _nm_log_obj(level, domain, ifname, con_uuid, self, prefix, ...) \
+    _nm_log_ptr ((level), \
+                 (domain), \
+                 (ifname), \
+                 (con_uuid), \
+                 (self), \
+                 prefix, \
+                 __VA_ARGS__)
 
 /* log a message for an object (with providing a @self pointer to a GObject).
  * Contrary to nm_log_ptr(), @self must be a GObject type (or %NULL).
  * As of now, nm_log_obj() is identical to nm_log_ptr(), but we might change that */
-#define nm_log_obj(level, domain, self, prefix, ...) \
-    nm_log_ptr ((level), (domain), (self), prefix, __VA_ARGS__)
-
+#define nm_log_obj(level, domain, ifname, con_uuid, self, prefix, ...) \
+    nm_log_ptr ((level), \
+                (domain), \
+                (ifname), \
+                (con_uuid), \
+                (self), \
+                prefix, \
+                __VA_ARGS__)
 
 void _nm_log_impl (const char *file,
                    guint line,
@@ -163,8 +196,10 @@ void _nm_log_impl (const char *file,
                    NMLogLevel level,
                    NMLogDomain domain,
                    int error,
+                   const char *ifname,
+                   const char *con_uuid,
                    const char *fmt,
-                   ...) _nm_printf (7, 8);
+                   ...) _nm_printf (9, 10);
 
 const char *nm_logging_level_to_string (void);
 const char *nm_logging_domains_to_string (void);
@@ -191,7 +226,7 @@ gboolean nm_logging_setup (const char  *level,
 void nm_logging_set_syslog_identifier (const char *domain);
 void nm_logging_set_prefix (const char *format, ...) _nm_printf (1, 2);
 
-void     nm_logging_syslog_openlog (const char *logging_backend);
+void     nm_logging_syslog_openlog (const char *logging_backend, gboolean debug);
 gboolean nm_logging_syslog_enabled (void);
 
 /*****************************************************************************/
@@ -277,7 +312,7 @@ extern void (*_nm_logging_clear_platform_logging_cache) (void);
 
 #define __NMLOG_DEFAULT(level, domain, prefix, ...) \
 	G_STMT_START { \
-		nm_log ((level), (domain), \
+		nm_log ((level), (domain), NULL, NULL, \
 		        "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
 		        (prefix) \
 		        _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
@@ -285,7 +320,7 @@ extern void (*_nm_logging_clear_platform_logging_cache) (void);
 
 #define __NMLOG_DEFAULT_WITH_ADDR(level, domain, prefix, ...) \
 	G_STMT_START { \
-		nm_log ((level), (domain), \
+		nm_log ((level), (domain), NULL, NULL, \
 		        "%s[%p]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
 		        (prefix), \
 		        (self) \
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 7dfaa5ab..a7402195 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -16,7 +16,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2007 - 2009 Novell, Inc.
- * Copyright (C) 2007 - 2012 Red Hat, Inc.
+ * Copyright (C) 2007 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -54,6 +54,7 @@
 #include "nm-dbus-compat.h"
 #include "nm-checkpoint.h"
 #include "nm-checkpoint-manager.h"
+#include "nm-dispatcher.h"
 #include "NetworkManagerUtils.h"
 
 #include "introspection/org.freedesktop.NetworkManager.h"
@@ -67,6 +68,7 @@ static NMActiveConnection *_new_active_connection (NMManager *self,
                                                    const char *specific_object,
                                                    NMDevice *device,
                                                    NMAuthSubject *subject,
+                                                   NMActivationType activation_type,
                                                    GError **error);
 
 static void policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data);
@@ -86,7 +88,11 @@ static void device_sleep_cb (NMDevice *device,
                              GParamSpec *pspec,
                              NMManager *self);
 
-#define TAG_ACTIVE_CONNETION_ADD_AND_ACTIVATE "act-con-add-and-activate"
+static void settings_startup_complete_changed (NMSettings *settings,
+                                               GParamSpec *pspec,
+                                               NMManager *self);
+
+static NM_CACHED_QUARK_FCN ("active-connection-add-and-activate", active_connection_add_and_activate_quark)
 
 typedef struct {
 	gboolean user_enabled;
@@ -112,7 +118,7 @@ typedef struct {
 	GSList *devices;
 	NMState state;
 	NMConfig *config;
-	NMConnectivity *connectivity;
+	NMConnectivityState connectivity_state;
 
 	NMPolicy *policy;
 
@@ -129,9 +135,6 @@ typedef struct {
 	char *hostname;
 
 	RadioState radio_states[RFKILL_TYPE_MAX];
-	gboolean sleeping;
-	gboolean net_enabled;
-
 	NMVpnManager *vpn_manager;
 
 	NMSleepMonitor *sleep_monitor;
@@ -147,8 +150,13 @@ typedef struct {
 
 	guint timestamp_update_id;
 
-	gboolean startup;
-	gboolean devices_inited;
+	guint devices_inited_id;
+
+	bool startup:1;
+	bool devices_inited:1;
+
+	bool sleeping:1;
+	bool net_enabled:1;
 } NMManagerPrivate;
 
 struct _NMManager {
@@ -221,7 +229,7 @@ NM_DEFINE_SINGLETON_INSTANCE (NMManager);
             const NMManager *const __self = (self); \
             char __sbuf[32]; \
             \
-            _nm_log (__level, __domain, 0, \
+            _nm_log (__level, __domain, 0, NULL, NULL, \
                      "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      _NMLOG_PREFIX_NAME, \
                      (__self && __self != singleton_instance) \
@@ -233,8 +241,7 @@ NM_DEFINE_SINGLETON_INSTANCE (NMManager);
 
 /*****************************************************************************/
 
-GQuark autoconnect_root_quark (void);
-G_DEFINE_QUARK (autoconnect-root, autoconnect_root);
+static NM_CACHED_QUARK_FCN ("autoconnect-root", autoconnect_root_quark)
 
 static void active_connection_state_changed (NMActiveConnection *active,
                                              GParamSpec *pspec,
@@ -265,20 +272,20 @@ active_connection_remove (NMManager *self, NMActiveConnection *active)
 		g_signal_handlers_disconnect_by_func (active, active_connection_default_changed, self);
 		g_signal_handlers_disconnect_by_func (active, active_connection_parent_active, self);
 
-		if (   nm_active_connection_get_assumed (active)
-		    && (connection = nm_active_connection_get_settings_connection (active))
-		    && nm_settings_connection_get_nm_generated_assumed (connection))
+		if (   (connection = nm_active_connection_get_settings_connection (active))
+		    && nm_settings_connection_get_volatile (connection))
 			g_object_ref (connection);
 		else
 			connection = NULL;
 
 		nm_exported_object_clear_and_unexport (&active);
 
-		if (   connection
-		    && nm_settings_has_connection (priv->settings, connection)) {
-			_LOGD (LOGD_DEVICE, "assumed connection disconnected. Deleting generated connection '%s' (%s)",
-			       nm_settings_connection_get_id (connection), nm_settings_connection_get_uuid (connection));
-			nm_settings_connection_delete (NM_SETTINGS_CONNECTION (connection), NULL, NULL);
+		if (connection) {
+			if (nm_settings_has_connection (priv->settings, connection)) {
+				_LOGD (LOGD_DEVICE, "assumed connection disconnected. Deleting generated connection '%s' (%s)",
+				       nm_settings_connection_get_id (connection), nm_settings_connection_get_uuid (connection));
+				nm_settings_connection_delete (connection, NULL, NULL);
+			}
 			g_object_unref (connection);
 		}
 	}
@@ -390,63 +397,84 @@ nm_manager_get_active_connections (NMManager *manager)
 }
 
 static NMActiveConnection *
-find_ac_for_connection (NMManager *manager, NMConnection *connection)
+active_connection_find_first (NMManager *self,
+                              NMSettingsConnection *settings_connection,
+                              const char *uuid,
+                              NMActiveConnectionState max_state)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+	NMManagerPrivate *priv;
 	GSList *iter;
-	const char *uuid = NULL;
-	gboolean is_settings_connection;
 
-	is_settings_connection = NM_IS_SETTINGS_CONNECTION (connection);
+	g_return_val_if_fail (NM_IS_MANAGER (self), NULL);
+	g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL);
 
-	if (!is_settings_connection)
-		uuid = nm_connection_get_uuid (connection);
+	priv = NM_MANAGER_GET_PRIVATE (self);
 
 	for (iter = priv->active_connections; iter; iter = iter->next) {
 		NMActiveConnection *ac = iter->data;
 		NMSettingsConnection *con;
 
 		con = nm_active_connection_get_settings_connection (ac);
-
-		/* depending on whether we have a NMSettingsConnection or a NMConnection,
-		 * we lookup by UUID or by reference. */
-		if (is_settings_connection) {
-			if (con != (NMSettingsConnection *) connection)
-				continue;
-		} else {
-			if (strcmp (uuid, nm_connection_get_uuid (NM_CONNECTION (con))) != 0)
-				continue;
-		}
-		if (nm_active_connection_get_state (ac) < NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)
-			return ac;
+		if (settings_connection && con != settings_connection)
+			continue;
+		if (uuid && !nm_streq0 (uuid, nm_connection_get_uuid (NM_CONNECTION (con))))
+			continue;
+		if (nm_active_connection_get_state (ac) > max_state)
+			continue;
+		return ac;
 	}
 
 	return NULL;
 }
 
+static NMActiveConnection *
+active_connection_find_first_by_connection (NMManager *self,
+                                            NMConnection *connection)
+{
+	gboolean is_settings_connection;
+
+	nm_assert (NM_IS_MANAGER (self));
+	nm_assert (NM_IS_CONNECTION (connection));
+
+	is_settings_connection = NM_IS_SETTINGS_CONNECTION (connection);
+	/* Depending on whether connection is a settings connection,
+	 * either lookup by object-identity of @connection, or compare the UUID */
+	return active_connection_find_first (self,
+	                                     is_settings_connection ? NM_SETTINGS_CONNECTION (connection) : NULL,
+	                                     is_settings_connection ? NULL : nm_connection_get_uuid (connection),
+	                                     NM_ACTIVE_CONNECTION_STATE_DEACTIVATING);
+}
+
+static gboolean
+_get_activatable_connections_filter (NMSettings *settings,
+                                     NMSettingsConnection *connection,
+                                     gpointer user_data)
+{
+	if (nm_settings_connection_get_volatile (connection))
+		return FALSE;
+	return !active_connection_find_first (user_data, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING);
+}
+
 /* Filter out connections that are already active.
  * nm_settings_get_connections_sorted() returns sorted list. We need to preserve the
  * order so that we didn't change auto-activation order (recent timestamps
  * are first).
  * Caller is responsible for freeing the returned list with g_slist_free().
  */
-GSList *
-nm_manager_get_activatable_connections (NMManager *manager)
+NMSettingsConnection **
+nm_manager_get_activatable_connections (NMManager *manager, guint *out_len, gboolean sort)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	GSList *all_connections = nm_settings_get_connections_sorted (priv->settings);
-	GSList *connections = NULL, *iter;
-	NMSettingsConnection *connection;
-
-	for (iter = all_connections; iter; iter = iter->next) {
-		connection = iter->data;
-
-		if (!find_ac_for_connection (manager, NM_CONNECTION (connection)))
-			connections = g_slist_prepend (connections, connection);
-	}
+	NMSettingsConnection **connections;
+	guint len;
 
-	g_slist_free (all_connections);
-	return g_slist_reverse (connections);
+	connections = nm_settings_get_connections_clone (priv->settings, &len,
+	                                                 _get_activatable_connections_filter,
+	                                                 manager);
+	if (sort && len > 1)
+		g_qsort_with_data (connections, len, sizeof (connections[0]), nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
+	NM_SET_OUT (out_len, len);
+	return connections;
 }
 
 static NMActiveConnection *
@@ -472,12 +500,6 @@ active_connection_get_by_path (NMManager *manager, const char *path)
 static void
 _config_changed_cb (NMConfig *config, NMConfigData *config_data, NMConfigChangeFlags changes, NMConfigData *old_data, NMManager *self)
 {
-	g_object_set (NM_MANAGER_GET_PRIVATE (self)->connectivity,
-	              NM_CONNECTIVITY_URI, nm_config_data_get_connectivity_uri (config_data),
-	              NM_CONNECTIVITY_INTERVAL, nm_config_data_get_connectivity_interval (config_data),
-	              NM_CONNECTIVITY_RESPONSE, nm_config_data_get_connectivity_response (config_data),
-	              NULL);
-
 	if (NM_FLAGS_HAS (changes, NM_CONFIG_CHANGE_GLOBAL_DNS_CONFIG))
 		_notify (self, PROP_GLOBAL_DNS_CONFIGURATION);
 }
@@ -745,27 +767,8 @@ set_state (NMManager *self, NMState state)
 	g_signal_emit (self, signals[STATE_CHANGED], 0, priv->state);
 }
 
-static void
-checked_connectivity (GObject *object, GAsyncResult *result, gpointer user_data)
-{
-	NMManager *manager = user_data;
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	NMConnectivityState connectivity;
-
-	if (priv->state == NM_STATE_CONNECTING || priv->state == NM_STATE_CONNECTED_SITE) {
-		connectivity = nm_connectivity_check_finish (priv->connectivity, result, NULL);
-
-		if (connectivity == NM_CONNECTIVITY_FULL)
-			set_state (manager, NM_STATE_CONNECTED_GLOBAL);
-
-		_notify (manager, PROP_CONNECTIVITY);
-	}
-
-	g_object_unref (manager);
-}
-
 static NMState
-find_best_device_state (NMManager *manager, gboolean *force_connectivity_check)
+find_best_device_state (NMManager *manager)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
 	NMState best_state = NM_STATE_DISCONNECTED;
@@ -779,24 +782,27 @@ find_best_device_state (NMManager *manager, gboolean *force_connectivity_check)
 		case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
 			if (   nm_active_connection_get_default (ac)
 			    || nm_active_connection_get_default6 (ac)) {
-				if (nm_connectivity_get_state (priv->connectivity) == NM_CONNECTIVITY_FULL)
+				if (priv->connectivity_state)
 					return NM_STATE_CONNECTED_GLOBAL;
 
 				best_state = NM_STATE_CONNECTED_SITE;
-				NM_SET_OUT (force_connectivity_check, TRUE);
 			} else {
 				if (best_state < NM_STATE_CONNECTING)
 					best_state = NM_STATE_CONNECTED_LOCAL;
 			}
 			break;
 		case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
-			if (!nm_active_connection_get_assumed (ac)) {
+			if (!NM_IN_SET (nm_active_connection_get_activation_type (ac),
+			                NM_ACTIVATION_TYPE_EXTERNAL,
+			                NM_ACTIVATION_TYPE_ASSUME)) {
 				if (best_state != NM_STATE_CONNECTED_GLOBAL)
 					best_state = NM_STATE_CONNECTING;
 			}
 			break;
 		case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
-			if (!nm_active_connection_get_assumed (ac)) {
+			if (!NM_IN_SET (nm_active_connection_get_activation_type (ac),
+			                NM_ACTIVATION_TYPE_EXTERNAL,
+			                NM_ACTIVATION_TYPE_ASSUME)) {
 				if (best_state < NM_STATE_DISCONNECTING)
 					best_state = NM_STATE_DISCONNECTING;
 			}
@@ -837,7 +843,6 @@ nm_manager_update_state (NMManager *manager)
 {
 	NMManagerPrivate *priv;
 	NMState new_state = NM_STATE_DISCONNECTED;
-	gboolean force_connectivity_check = FALSE;
 
 	g_return_if_fail (NM_IS_MANAGER (manager));
 
@@ -846,14 +851,11 @@ nm_manager_update_state (NMManager *manager)
 	if (manager_sleeping (manager))
 		new_state = NM_STATE_ASLEEP;
 	else
-		new_state = find_best_device_state (manager, &force_connectivity_check);
-
-	nm_connectivity_set_online (priv->connectivity, new_state >= NM_STATE_CONNECTED_LOCAL);
+		new_state = find_best_device_state (manager);
 
-	if (new_state == NM_STATE_CONNECTED_SITE || force_connectivity_check) {
-		nm_connectivity_check_async (priv->connectivity,
-		                             checked_connectivity,
-		                             g_object_ref (manager));
+	if (   new_state >= NM_STATE_CONNECTED_LOCAL
+	    && priv->connectivity_state == NM_CONNECTIVITY_FULL) {
+		new_state = NM_STATE_CONNECTED_GLOBAL;
 	}
 
 	set_state (manager, new_state);
@@ -920,15 +922,18 @@ check_if_startup_complete (NMManager *self)
 	_LOGI (LOGD_CORE, "startup complete");
 
 	priv->startup = FALSE;
-	_notify (self, PROP_STARTUP);
 
-	/* We don't have to watch notify::has-pending-action any more. */
+	/* we no longer care about these signals. Startup-complete only
+	 * happens once. */
+	g_signal_handlers_disconnect_by_func (priv->settings, G_CALLBACK (settings_startup_complete_changed), self);
 	for (iter = priv->devices; iter; iter = iter->next) {
-		NMDevice *dev = iter->data;
-
-		g_signal_handlers_disconnect_by_func (dev, G_CALLBACK (device_has_pending_action_changed), self);
+		g_signal_handlers_disconnect_by_func (iter->data,
+		                                      G_CALLBACK (device_has_pending_action_changed),
+		                                      self);
 	}
 
+	_notify (self, PROP_STARTUP);
+
 	if (nm_config_get_configure_and_quit (priv->config))
 		g_signal_emit (self, signals[CONFIGURE_QUIT], 0);
 }
@@ -993,8 +998,10 @@ remove_device (NMManager *self,
 		if (unmanage) {
 			if (quitting)
 				nm_device_set_unmanaged_by_quitting (device);
-			else
+			else {
+				nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_REMOVED);
 				nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_PLATFORM_INIT, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
+			}
 		} else if (quitting && nm_config_get_configure_and_quit (priv->config)) {
 			nm_device_spawn_iface_helper (device);
 		}
@@ -1023,7 +1030,12 @@ remove_device (NMManager *self,
 
 		g_signal_emit (self, signals[DEVICE_REMOVED], 0, device);
 		_notify (self, PROP_DEVICES);
+	} else {
+		/* unrealize() does not release a slave device from master and
+		 * clear IP configurations, do it here */
+		nm_device_removed (device, TRUE);
 	}
+
 	g_signal_emit (self, signals[INTERNAL_DEVICE_REMOVED], 0, device);
 	_notify (self, PROP_ALL_DEVICES);
 
@@ -1132,6 +1144,11 @@ nm_manager_get_connection_iface (NMManager *self,
 
 	factory = nm_device_factory_manager_find_factory_for_connection (connection);
 	if (!factory) {
+		if (nm_streq0 (nm_connection_get_connection_type (connection), NM_SETTING_GENERIC_SETTING_NAME)) {
+			/* the generic type doesn't have a factory. */
+			goto return_ifname_fom_connection;
+		}
+
 		g_set_error (error,
 		             NM_MANAGER_ERROR,
 		             NM_MANAGER_ERROR_FAILED,
@@ -1143,15 +1160,7 @@ nm_manager_get_connection_iface (NMManager *self,
 	if (   !out_parent
 	    && !NM_DEVICE_FACTORY_GET_CLASS (factory)->get_connection_iface) {
 		/* optimization. Shortcut lookup of the partent device. */
-		iface = g_strdup (nm_connection_get_interface_name (connection));
-		if (!iface) {
-			g_set_error (error,
-			             NM_MANAGER_ERROR,
-			             NM_MANAGER_ERROR_FAILED,
-			             "failed to determine interface name: error determine name for %s",
-			             nm_connection_get_connection_type (connection));
-		}
-		return iface;
+		goto return_ifname_fom_connection;
 	}
 
 	parent = find_parent_device_for_connection (self, connection, factory);
@@ -1165,6 +1174,17 @@ nm_manager_get_connection_iface (NMManager *self,
 	if (out_parent)
 		*out_parent = parent;
 	return iface;
+
+return_ifname_fom_connection:
+	iface = g_strdup (nm_connection_get_interface_name (connection));
+	if (!iface) {
+		g_set_error (error,
+		             NM_MANAGER_ERROR,
+		             NM_MANAGER_ERROR_FAILED,
+		             "failed to determine interface name: error determine name for %s",
+		             nm_connection_get_connection_type (connection));
+	}
+	return iface;
 }
 
 /**
@@ -1206,7 +1226,8 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	NMDeviceFactory *factory;
-	gs_free_slist GSList *connections = NULL;
+	gs_free NMSettingsConnection **connections = NULL;
+	guint i;
 	GSList *iter;
 	gs_free char *iface = NULL;
 	NMDevice *device = NULL, *parent = NULL;
@@ -1277,9 +1298,9 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
 	}
 
 	/* Create backing resources if the device has any autoconnect connections */
-	connections = nm_settings_get_connections_sorted (priv->settings);
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMConnection *candidate = iter->data;
+	connections = nm_settings_get_connections_sorted (priv->settings, NULL);
+	for (i = 0; connections[i]; i++) {
+		NMConnection *candidate = NM_CONNECTION (connections[i]);
 		NMSettingConnection *s_con;
 
 		if (!nm_device_check_connection_compatible (device, candidate))
@@ -1308,13 +1329,14 @@ static void
 retry_connections_for_parent_device (NMManager *self, NMDevice *device)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *connections, *iter;
+	gs_free NMSettingsConnection **connections = NULL;
+	guint i;
 
 	g_return_if_fail (device);
 
-	connections = nm_settings_get_connections_sorted (priv->settings);
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMConnection *candidate = iter->data;
+	connections = nm_settings_get_connections_sorted (priv->settings, NULL);
+	for (i = 0; connections[i]; i++) {
+		NMConnection *candidate = NM_CONNECTION (connections[i]);
 		gs_free_error GError *error = NULL;
 		gs_free char *ifname = NULL;
 		NMDevice *parent;
@@ -1329,8 +1351,6 @@ retry_connections_for_parent_device (NMManager *self, NMDevice *device)
 			}
 		}
 	}
-
-	g_slist_free (connections);
 }
 
 static void
@@ -1667,35 +1687,35 @@ done:
 	g_clear_error (&error);
 }
 
-static gboolean
-match_connection_filter (NMConnection *connection, gpointer user_data)
-{
-	if (nm_settings_connection_get_nm_generated_assumed (NM_SETTINGS_CONNECTION (connection)))
-		return FALSE;
-
-	return nm_device_check_connection_compatible (NM_DEVICE (user_data), connection);
-}
-
 /**
  * get_existing_connection:
  * @manager: #NMManager instance
  * @device: #NMDevice instance
+ * @guess_assume: whether to employ a heuristic to search for a matching
+ *   connection to assume.
+ * @assume_connection_uuid: if present, try to assume a connection with this
+ *   UUID. If no uuid is given or no matching connection is found, we
+ *   only do external activation.
  * @out_generated: (allow-none): return TRUE, if the connection was generated.
  *
  * Returns: a #NMSettingsConnection to be assumed by the device, or %NULL if
  *   the device does not support assuming existing connections.
  */
 static NMSettingsConnection *
-get_existing_connection (NMManager *self, NMDevice *device, gboolean *out_generated)
+get_existing_connection (NMManager *self,
+                         NMDevice *device,
+                         gboolean guess_assume,
+                         const char *assume_connection_uuid,
+                         gboolean *out_generated)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	gs_free_slist GSList *connections = nm_manager_get_activatable_connections (self);
 	NMConnection *connection = NULL;
-	NMSettingsConnection *matched;
 	NMSettingsConnection *added = NULL;
 	GError *error = NULL;
 	NMDevice *master = NULL;
 	int ifindex = nm_device_get_ifindex (device);
+	NMSettingsConnection *matched;
+	NMSettingsConnection *connection_checked = NULL;
 
 	if (out_generated)
 		*out_generated = FALSE;
@@ -1739,18 +1759,61 @@ get_existing_connection (NMManager *self, NMDevice *device, gboolean *out_genera
 	 * When no configured connection matches the generated connection, we keep
 	 * the generated connection instead.
 	 */
-	connections = g_slist_reverse (g_slist_sort (connections, nm_settings_sort_connections));
-	matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections,
-	                                                             connection,
-	                                                             nm_device_has_carrier (device),
-	                                                             nm_device_get_ip4_route_metric (device),
-	                                                             nm_device_get_ip6_route_metric (device),
-	                                                             match_connection_filter,
-	                                                             device));
+	if (   assume_connection_uuid
+	    && (connection_checked = nm_settings_get_connection_by_uuid (priv->settings, assume_connection_uuid))
+	    && !active_connection_find_first (self, connection_checked, NULL,
+	                                      NM_ACTIVE_CONNECTION_STATE_DEACTIVATING)
+	    && nm_device_check_connection_compatible (device, NM_CONNECTION (connection_checked))) {
+		NMConnection *const connections[] = {
+			NM_CONNECTION (connection_checked),
+			NULL,
+		};
+
+		matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections,
+		                                                             connection,
+		                                                             nm_device_has_carrier (device),
+		                                                             nm_device_get_ip4_route_metric (device),
+		                                                             nm_device_get_ip6_route_metric (device),
+		                                                             NULL, NULL));
+	} else
+		matched = NULL;
+
+	if (!matched && guess_assume) {
+		gs_free NMSettingsConnection **connections = NULL;
+		guint len, i, j;
+
+		/* the state file doesn't indicate a connection UUID to assume. Search the
+		 * persistent connections for a matching candidate. */
+		connections = nm_manager_get_activatable_connections (self, &len, FALSE);
+		if (len > 0) {
+			for (i = 0, j = 0; i < len; i++) {
+				NMConnection *con = NM_CONNECTION (connections[i]);
+
+				if (   con != NM_CONNECTION (connection_checked)
+				    && nm_device_check_connection_compatible (device, con))
+					connections[j++] = connections[i];
+			}
+			connections[j] = NULL;
+			len = j;
+			g_qsort_with_data (connections, len, sizeof (connections[0]),
+			                   nm_settings_connection_cmp_timestamp_p_with_data, NULL);
+
+			matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection ((NMConnection *const*) connections,
+			                                                             connection,
+			                                                             nm_device_has_carrier (device),
+			                                                             nm_device_get_ip4_route_metric (device),
+			                                                             nm_device_get_ip6_route_metric (device),
+			                                                             NULL, NULL));
+		}
+	}
+
 	if (matched) {
-		_LOGI (LOGD_DEVICE, "(%s): found matching connection '%s'",
+		_LOGI (LOGD_DEVICE, "(%s): found matching connection '%s' (%s)%s",
 		       nm_device_get_iface (device),
-		       nm_settings_connection_get_id (matched));
+		       nm_settings_connection_get_id (matched),
+		       nm_settings_connection_get_uuid (matched),
+		       assume_connection_uuid && nm_streq (assume_connection_uuid, nm_settings_connection_get_uuid (matched))
+		           ? " (indicated)" : " (guessed)");
 		g_object_unref (connection);
 		return matched;
 	}
@@ -1763,7 +1826,7 @@ get_existing_connection (NMManager *self, NMDevice *device, gboolean *out_genera
 	if (added) {
 		nm_settings_connection_set_flags (NM_SETTINGS_CONNECTION (added),
 		                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED |
-		                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED,
+		                                  NM_SETTINGS_CONNECTION_FLAGS_VOLATILE,
 		                                  TRUE);
 		if (out_generated)
 			*out_generated = TRUE;
@@ -1780,55 +1843,17 @@ get_existing_connection (NMManager *self, NMDevice *device, gboolean *out_genera
 }
 
 static gboolean
-assume_connection (NMManager *self, NMDevice *device, NMSettingsConnection *connection)
-{
-	NMActiveConnection *active, *master_ac;
-	NMAuthSubject *subject;
-	GError *error = NULL;
-
-	_LOGD (LOGD_DEVICE, "(%s): will attempt to assume connection",
-	       nm_device_get_iface (device));
-
-	/* Move device to DISCONNECTED to activate the connection */
-	if (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE) {
-		nm_device_state_changed (device,
-		                         NM_DEVICE_STATE_DISCONNECTED,
-		                         NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
-	}
-	g_return_val_if_fail (nm_device_get_state (device) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
-
-	subject = nm_auth_subject_new_internal ();
-	active = _new_active_connection (self, NM_CONNECTION (connection), NULL, NULL, device, subject, &error);
-	g_object_unref (subject);
-
-	if (!active) {
-		_LOGW (LOGD_DEVICE, "assumed connection %s failed to activate: %s",
-		       nm_connection_get_path (NM_CONNECTION (connection)),
-		       error->message);
-		g_error_free (error);
-		return FALSE;
-	}
-
-	/* If the device is a slave or VLAN, find the master ActiveConnection */
-	master_ac = NULL;
-	if (find_master (self, NM_CONNECTION (connection), device, NULL, NULL, &master_ac, NULL) && master_ac)
-		nm_active_connection_set_master (active, master_ac);
-
-	nm_active_connection_set_assumed (active, TRUE);
-	nm_exported_object_export (NM_EXPORTED_OBJECT (active));
-	active_connection_add (self, active);
-	nm_device_queue_activation (device, NM_ACT_REQUEST (active));
-	g_object_unref (active);
-
-	return TRUE;
-}
-
-static gboolean
-recheck_assume_connection (NMManager *self, NMDevice *device)
+recheck_assume_connection (NMManager *self,
+                           NMDevice *device,
+                           gboolean guess_assume,
+                           const char *assume_connection_uuid)
 {
 	NMSettingsConnection *connection;
-	gboolean was_unmanaged = FALSE, success, generated = FALSE;
+	gboolean was_unmanaged = FALSE;
+	gboolean generated = FALSE;
 	NMDeviceState state;
+	NMDeviceSysIfaceState if_state;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 
 	g_return_val_if_fail (NM_IS_MANAGER (self), FALSE);
 	g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
@@ -1843,43 +1868,93 @@ recheck_assume_connection (NMManager *self, NMDevice *device)
 	if (state > NM_DEVICE_STATE_DISCONNECTED)
 		return FALSE;
 
-	connection = get_existing_connection (self, device, &generated);
+	if_state = nm_device_sys_iface_state_get (device);
+	if (!priv->startup  && (if_state == NM_DEVICE_SYS_IFACE_STATE_MANAGED))
+		nm_assert (!guess_assume && (assume_connection_uuid == NULL));
+	else if (if_state != NM_DEVICE_SYS_IFACE_STATE_EXTERNAL)
+		return FALSE;
+
+	connection = get_existing_connection (self, device, guess_assume, assume_connection_uuid, &generated);
 	if (!connection) {
 		_LOGD (LOGD_DEVICE, "(%s): can't assume; no connection",
 		       nm_device_get_iface (device));
 		return FALSE;
 	}
 
+	_LOGD (LOGD_DEVICE, "(%s): will attempt to assume connection",
+	       nm_device_get_iface (device));
+
+	if (!generated)
+		nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_ASSUME);
+
+	/* Move device to DISCONNECTED to activate the connection */
 	if (state == NM_DEVICE_STATE_UNMANAGED) {
 		was_unmanaged = TRUE;
 		nm_device_state_changed (device,
 		                         NM_DEVICE_STATE_UNAVAILABLE,
 		                         NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
 	}
+	if (nm_device_get_state (device) == NM_DEVICE_STATE_UNAVAILABLE) {
+		nm_device_state_changed (device,
+		                         NM_DEVICE_STATE_DISCONNECTED,
+		                         NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
+	}
 
-	success = assume_connection (self, device, connection);
-	if (!success) {
-		if (was_unmanaged) {
-			nm_device_state_changed (device,
-			                         NM_DEVICE_STATE_UNAVAILABLE,
-			                         NM_DEVICE_STATE_REASON_CONFIG_FAILED);
-		}
+	g_return_val_if_fail (nm_device_get_state (device) >= NM_DEVICE_STATE_DISCONNECTED, FALSE);
+
+	{
+		gs_unref_object NMActiveConnection *active = NULL;
+		gs_unref_object NMAuthSubject *subject = NULL;
+		NMActiveConnection *master_ac;
+		GError *error = NULL;
+
+		subject = nm_auth_subject_new_internal ();
+		active = _new_active_connection (self, NM_CONNECTION (connection), NULL, NULL,
+		                                 device, subject,
+		                                 generated ? NM_ACTIVATION_TYPE_EXTERNAL : NM_ACTIVATION_TYPE_ASSUME,
+		                                 &error);
+
+		if (!active) {
+			_LOGW (LOGD_DEVICE, "assumed connection %s failed to activate: %s",
+			       nm_connection_get_path (NM_CONNECTION (connection)),
+			       error->message);
+			g_error_free (error);
+
+			if (was_unmanaged) {
+				nm_device_state_changed (device,
+				                         NM_DEVICE_STATE_UNAVAILABLE,
+				                         NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+			}
 
-		if (generated) {
-			_LOGD (LOGD_DEVICE, "(%s): connection assumption failed. Deleting generated connection",
-			       nm_device_get_iface (device));
+			if (generated) {
+				_LOGD (LOGD_DEVICE, "(%s): connection assumption failed. Deleting generated connection",
+				       nm_device_get_iface (device));
 
-			nm_settings_connection_delete (connection, NULL, NULL);
+				nm_settings_connection_delete (connection, NULL, NULL);
+			} else {
+				if (nm_device_sys_iface_state_get (device) == NM_DEVICE_SYS_IFACE_STATE_ASSUME)
+					nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_EXTERNAL);
+			}
+			return FALSE;
 		}
+
+		/* If the device is a slave or VLAN, find the master ActiveConnection */
+		master_ac = NULL;
+		if (find_master (self, NM_CONNECTION (connection), device, NULL, NULL, &master_ac, NULL) && master_ac)
+			nm_active_connection_set_master (active, master_ac);
+
+		nm_exported_object_export (NM_EXPORTED_OBJECT (active));
+		active_connection_add (self, active);
+		nm_device_queue_activation (device, NM_ACT_REQUEST (active));
 	}
 
-	return success;
+	return TRUE;
 }
 
 static void
 recheck_assume_connection_cb (NMDevice *device, gpointer user_data)
 {
-	recheck_assume_connection (user_data, device);
+	recheck_assume_connection (user_data, device, FALSE, NULL);
 }
 
 static void
@@ -1939,8 +2014,42 @@ device_realized (NMDevice *device,
 	_notify (self, PROP_DEVICES);
 }
 
+#if WITH_CONCHECK
 static void
-_device_realize_finish (NMManager *self, NMDevice *device, const NMPlatformLink *plink)
+device_connectivity_changed (NMDevice *device,
+                             GParamSpec *pspec,
+                             NMManager *self)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMConnectivityState best_state = NM_CONNECTIVITY_UNKNOWN;
+	NMConnectivityState state;
+	const GSList *devices;
+
+	for (devices = priv->devices; devices; devices = devices->next) {
+		state = nm_device_get_connectivity_state (NM_DEVICE (devices->data));
+		if (state > best_state)
+			best_state = state;
+	}
+
+	if (best_state != priv->connectivity_state) {
+		priv->connectivity_state = best_state;
+
+		_LOGD (LOGD_CORE, "connectivity checking indicates %s",
+		       nm_connectivity_state_to_string (priv->connectivity_state));
+
+		nm_manager_update_state (self);
+		_notify (self, PROP_CONNECTIVITY);
+		nm_dispatcher_call_connectivity (priv->connectivity_state, NULL, NULL, NULL);
+	}
+}
+#endif
+
+static void
+_device_realize_finish (NMManager *self,
+                        NMDevice *device,
+                        const NMPlatformLink *plink,
+                        gboolean guess_assume,
+                        const char *connection_uuid_to_assume)
 {
 	g_return_if_fail (NM_IS_MANAGER (self));
 	g_return_if_fail (NM_IS_DEVICE (device));
@@ -1950,7 +2059,7 @@ _device_realize_finish (NMManager *self, NMDevice *device, const NMPlatformLink
 	if (!nm_device_get_managed (device, FALSE))
 		return;
 
-	if (recheck_assume_connection (self, device))
+	if (recheck_assume_connection (self, device, guess_assume, connection_uuid_to_assume))
 		return;
 
 	/* if we failed to assume a connection for the managed device, but the device
@@ -2040,6 +2149,12 @@ add_device (NMManager *self, NMDevice *device, GError **error)
 	                  G_CALLBACK (device_realized),
 	                  self);
 
+#if WITH_CONCHECK
+	g_signal_connect (device, "notify::" NM_DEVICE_CONNECTIVITY,
+	                  G_CALLBACK (device_connectivity_changed),
+	                  self);
+#endif
+
 	if (priv->startup) {
 		g_signal_connect (device, "notify::" NM_DEVICE_HAS_PENDING_ACTION,
 		                  G_CALLBACK (device_has_pending_action_changed),
@@ -2096,9 +2211,13 @@ factory_device_added_cb (NMDeviceFactory *factory,
 
 	g_return_if_fail (NM_IS_MANAGER (self));
 
-	if (nm_device_realize_start (device, NULL, NULL, &error)) {
+	if (nm_device_realize_start (device,
+	                             NULL,
+	                             NM_UNMAN_FLAG_OP_FORGET,
+	                             NULL,
+	                             &error)) {
 		add_device (self, device, NULL);
-		_device_realize_finish (self, device, NULL);
+		_device_realize_finish (self, device, NULL, FALSE, NULL);
 	} else {
 		_LOGW (LOGD_DEVICE, "(%s): failed to realize device: %s",
 		       nm_device_get_iface (device), error->message);
@@ -2144,6 +2263,7 @@ static void
 platform_link_added (NMManager *self,
                      int ifindex,
                      const NMPlatformLink *plink,
+                     gboolean guess_assume,
                      const NMConfigDeviceStateData *dev_state)
 {
 	NMDeviceFactory *factory;
@@ -2169,9 +2289,13 @@ platform_link_added (NMManager *self,
 			 * device with the link's name.
 			 */
 			return;
-		} else if (nm_device_realize_start (candidate, plink, &compatible, &error)) {
+		} else if (nm_device_realize_start (candidate,
+		                                    plink,
+		                                    NM_UNMAN_FLAG_OP_FORGET,
+		                                    &compatible,
+		                                    &error)) {
 			/* Success */
-			_device_realize_finish (self, candidate, plink);
+			_device_realize_finish (self, candidate, plink, FALSE, NULL);
 			return;
 		}
 
@@ -2221,10 +2345,30 @@ platform_link_added (NMManager *self,
 
 	if (device) {
 		gs_free_error GError *error = NULL;
+		NMUnmanFlagOp unmanaged_user_explicit = NM_UNMAN_FLAG_OP_FORGET;
+
+		if (dev_state) {
+			switch (dev_state->managed) {
+			case NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED:
+				unmanaged_user_explicit = NM_UNMAN_FLAG_OP_SET_MANAGED;
+				break;
+			case NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED:
+				unmanaged_user_explicit = NM_UNMAN_FLAG_OP_SET_UNMANAGED;
+				break;
+			case NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN:
+				break;
+			}
+		}
 
-		if (nm_device_realize_start (device, plink, NULL, &error)) {
+		if (nm_device_realize_start (device,
+		                             plink,
+		                             unmanaged_user_explicit,
+		                             NULL,
+		                             &error)) {
 			add_device (self, device, NULL);
-			_device_realize_finish (self, device, plink);
+			_device_realize_finish (self, device, plink,
+			                        guess_assume,
+			                        dev_state ? dev_state->connection_uuid : NULL);
 		} else {
 			_LOGW (LOGD_DEVICE, "%s: failed to realize device: %s",
 			       plink->name, error->message);
@@ -2254,7 +2398,7 @@ _platform_link_cb_idle (PlatformLinkCbData *data)
 		NMPlatformLink pllink;
 
 		pllink = *l; /* make a copy of the link instance */
-		platform_link_added (self, data->ifindex, &pllink, NULL);
+		platform_link_added (self, data->ifindex, &pllink, FALSE, NULL);
 	} else {
 		NMDevice *device;
 		GError *error = NULL;
@@ -2262,6 +2406,7 @@ _platform_link_cb_idle (PlatformLinkCbData *data)
 		device = nm_manager_get_device_by_ifindex (self, data->ifindex);
 		if (device) {
 			if (nm_device_is_software (device)) {
+				nm_device_sys_iface_state_set (device, NM_DEVICE_SYS_IFACE_STATE_REMOVED);
 				/* Our software devices stick around until their connection is removed */
 				if (!nm_device_unrealize (device, FALSE, &error)) {
 					_LOGW (LOGD_DEVICE, "(%s): failed to unrealize: %s",
@@ -2310,22 +2455,24 @@ platform_link_cb (NMPlatform *platform,
 static void
 platform_query_devices (NMManager *self)
 {
-	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GArray *links_array;
 	NMPlatformLink *links;
 	int i;
+	gboolean guess_assume;
+
+	guess_assume = nm_config_get_first_start (nm_config_get ());
 
 	links_array = nm_platform_link_get_all (NM_PLATFORM_GET);
 	links = (NMPlatformLink *) links_array->data;
 	for (i = 0; i < links_array->len; i++) {
 		gs_free NMConfigDeviceStateData *dev_state = NULL;
 
-		dev_state = nm_config_device_state_load (priv->config,
-		                                         links[i].ifindex);
+		dev_state = nm_config_device_state_load (links[i].ifindex);
 
 		platform_link_added (self,
 		                     links[i].ifindex,
 		                     &links[i],
+		                     guess_assume && (!dev_state || !dev_state->connection_uuid),
 		                     dev_state);
 	}
 
@@ -2372,27 +2519,22 @@ nm_manager_get_device_paths (NMManager *self)
 }
 
 static NMDevice *
-nm_manager_get_connection_device (NMManager *self,
-                                  NMConnection *connection)
-{
-	NMActiveConnection *ac = find_ac_for_connection (self, connection);
-	if (ac == NULL)
-		return NULL;
-
-	return nm_active_connection_get_device (ac);
-}
-
-static NMDevice *
 nm_manager_get_best_device_for_connection (NMManager *self,
                                            NMConnection *connection,
-                                           gboolean for_user_request)
+                                           gboolean for_user_request,
+                                           GHashTable *unavailable_devices)
 {
 	const GSList *devices, *iter;
-	NMDevice *act_device = nm_manager_get_connection_device (self, connection);
+	NMActiveConnection *ac;
+	NMDevice *act_device;
 	NMDeviceCheckConAvailableFlags flags;
 
-	if (act_device)
-		return act_device;
+	ac = active_connection_find_first_by_connection (self, connection);
+	if (ac) {
+		act_device = nm_active_connection_get_device (ac);
+		if (act_device)
+			return act_device;
+	}
 
 	flags = for_user_request ? NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST : NM_DEVICE_CHECK_CON_AVAILABLE_NONE;
 
@@ -2401,6 +2543,9 @@ nm_manager_get_best_device_for_connection (NMManager *self,
 	for (iter = devices; iter; iter = g_slist_next (iter)) {
 		NMDevice *device = NM_DEVICE (iter->data);
 
+		if (unavailable_devices && g_hash_table_contains (unavailable_devices, device))
+			continue;
+
 		if (nm_device_check_connection_available (device, connection, flags, NULL))
 			return device;
 	}
@@ -2590,8 +2735,10 @@ find_master (NMManager *self,
 		*out_master_connection = master_connection;
 	if (out_master_device)
 		*out_master_device = master_device;
-	if (out_master_ac && master_connection)
-		*out_master_ac = find_ac_for_connection (self, NM_CONNECTION (master_connection));
+	if (out_master_ac && master_connection) {
+		*out_master_ac = active_connection_find_first (self, master_connection, NULL,
+		                                               NM_ACTIVE_CONNECTION_STATE_DEACTIVATING);
+	}
 
 	if (master_device || master_connection)
 		return TRUE;
@@ -2676,14 +2823,15 @@ ensure_master_active_connection (NMManager *self,
 		 * activate it on the device.
 		 */
 		if (master_state == NM_DEVICE_STATE_DISCONNECTED || !nm_device_is_real (master_device)) {
-			GSList *connections;
+			gs_free NMSettingsConnection **connections = NULL;
+			guint i;
 
 			g_assert (master_connection == NULL);
 
 			/* Find a compatible connection and activate this device using it */
-			connections = nm_manager_get_activatable_connections (self);
-			for (iter = connections; iter; iter = g_slist_next (iter)) {
-				NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (iter->data);
+			connections = nm_manager_get_activatable_connections (self, NULL, TRUE);
+			for (i = 0; connections[i]; i++) {
+				NMSettingsConnection *candidate = connections[i];
 
 				/* Ensure eg bond/team slave and the candidate master is a
 				 * bond/team master
@@ -2698,12 +2846,11 @@ ensure_master_active_connection (NMManager *self,
 					                                            NULL,
 					                                            master_device,
 					                                            subject,
+					                                            NM_ACTIVATION_TYPE_MANAGED,
 					                                            error);
-					g_slist_free (connections);
 					return master_ac;
 				}
 			}
-			g_slist_free (connections);
 
 			g_set_error (error,
 			             NM_MANAGER_ERROR,
@@ -2745,6 +2892,7 @@ ensure_master_active_connection (NMManager *self,
 			                                            NULL,
 			                                            candidate,
 			                                            subject,
+			                                            NM_ACTIVATION_TYPE_MANAGED,
 			                                            error);
 			return master_ac;
 		}
@@ -2759,49 +2907,83 @@ ensure_master_active_connection (NMManager *self,
 	return NULL;
 }
 
+typedef struct {
+	NMSettingsConnection *connection;
+	NMDevice *device;
+} SlaveConnectionInfo;
+
 /**
  * find_slaves:
  * @manager: #NMManager object
  * @connection: the master #NMSettingsConnection to find slave connections for
  * @device: the master #NMDevice for the @connection
+ * @out_n_slaves: on return, the number of slaves found
  *
  * Given an #NMSettingsConnection, attempts to find its slaves. If @connection is not
  * master, or has not any slaves, this will return %NULL.
  *
- * Returns: list of slave connections for given master @connection, or %NULL
+ * Returns: an array of #SlaveConnectionInfo for given master @connection, or %NULL
  **/
-static GSList *
+static SlaveConnectionInfo *
 find_slaves (NMManager *manager,
              NMSettingsConnection *connection,
-             NMDevice *device)
+             NMDevice *device,
+             guint *out_n_slaves)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	GSList *all_connections, *iter;
-	GSList *slaves = NULL;
+	gs_free NMSettingsConnection **all_connections = NULL;
+	guint n_all_connections;
+	guint i;
+	SlaveConnectionInfo *slaves = NULL;
+	guint n_slaves = 0;
 	NMSettingConnection *s_con;
+	gs_unref_hashtable GHashTable *devices = NULL;
+
+	nm_assert (out_n_slaves);
 
 	s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
-	g_assert (s_con);
+	g_return_val_if_fail (s_con, NULL);
+
+	devices = g_hash_table_new (g_direct_hash, g_direct_equal);
 
 	/* Search through all connections, not only inactive ones, because
 	 * even if a slave was already active, it might be deactivated during
 	 * master reactivation.
 	 */
-	all_connections = nm_settings_get_connections_sorted (priv->settings);
-	for (iter = all_connections; iter; iter = iter->next) {
+	all_connections = nm_settings_get_connections_sorted (priv->settings, &n_all_connections);
+	for (i = 0; i < n_all_connections; i++) {
 		NMSettingsConnection *master_connection = NULL;
-		NMDevice *master_device = NULL;
-		NMConnection *candidate = iter->data;
+		NMDevice *master_device = NULL, *slave_device;
+		NMConnection *candidate = NM_CONNECTION (all_connections[i]);
 
 		find_master (manager, candidate, NULL, &master_connection, &master_device, NULL, NULL);
 		if (   (master_connection && master_connection == connection)
 		    || (master_device && master_device == device)) {
-			slaves = g_slist_prepend (slaves, candidate);
+			slave_device = nm_manager_get_best_device_for_connection (manager,
+			                                                          candidate,
+			                                                          FALSE,
+			                                                          devices);
+
+			if (!slaves) {
+				/* what we allocate is quite likely much too large. Don't bother, it is only
+				 * a temporary buffer. */
+				slaves = g_new (SlaveConnectionInfo, n_all_connections);
+			}
+
+			nm_assert (n_slaves < n_all_connections);
+			slaves[n_slaves].connection = NM_SETTINGS_CONNECTION (candidate),
+			slaves[n_slaves].device = slave_device,
+			n_slaves++;
+
+			if (slave_device)
+				g_hash_table_add (devices, slave_device);
 		}
 	}
-	g_slist_free (all_connections);
 
-	return g_slist_reverse (slaves);
+	*out_n_slaves = n_slaves;
+
+	/* Warning: returns NULL if n_slaves is zero. */
+	return slaves;
 }
 
 static gboolean
@@ -2833,39 +3015,56 @@ out:
 	return FALSE;
 }
 
-static gboolean
+static gint
+compare_slaves (gconstpointer a, gconstpointer b, gpointer _unused)
+{
+	const SlaveConnectionInfo *a_info = a;
+	const SlaveConnectionInfo *b_info = b;
+
+	/* Slaves without a device at the end */
+	if (!a_info->device)
+		return 1;
+	if (!b_info->device)
+		return -1;
+
+	return g_strcmp0 (nm_device_get_iface (a_info->device),
+	                  nm_device_get_iface (b_info->device));
+}
+
+static void
 autoconnect_slaves (NMManager *self,
                     NMSettingsConnection *master_connection,
                     NMDevice *master_device,
                     NMAuthSubject *subject)
 {
 	GError *local_err = NULL;
-	gboolean ret = FALSE;
 
 	if (should_connect_slaves (NM_CONNECTION (master_connection), master_device)) {
-		GSList *slaves, *iter;
+		gs_free SlaveConnectionInfo *slaves = NULL;
+		guint i, n_slaves = 0;
 
-		iter = slaves = find_slaves (self, master_connection, master_device);
-		ret = slaves != NULL;
+		slaves = find_slaves (self, master_connection, master_device, &n_slaves);
+		if (n_slaves > 1) {
+			g_qsort_with_data (slaves, n_slaves, sizeof (slaves[0]),
+			                   compare_slaves, NULL);
+		}
 
-		while (iter) {
-			NMSettingsConnection *slave_connection = iter->data;
+		for (i = 0; i < n_slaves; i++) {
+			SlaveConnectionInfo *slave = &slaves[i];
 			const char *uuid;
 
-			iter = iter->next;
-
 			/* To avoid loops when autoconnecting slaves, we propagate
 			 * the UUID of the initial connection down to slaves until
 			 * the same connection is found.
 			 */
 			uuid = g_object_get_qdata (G_OBJECT (master_connection),
 			                           autoconnect_root_quark ());
-			if (nm_streq0 (nm_settings_connection_get_uuid (slave_connection), uuid)) {
+			if (nm_streq0 (nm_settings_connection_get_uuid (slave->connection), uuid)) {
 				_LOGI (LOGD_CORE,
 				       "will NOT activate slave connection '%s' (%s) as a dependency for master '%s' (%s): "
 				       "circular dependency detected",
-				       nm_settings_connection_get_id (slave_connection),
-				       nm_settings_connection_get_uuid (slave_connection),
+				       nm_settings_connection_get_id (slave->connection),
+				       nm_settings_connection_get_uuid (slave->connection),
 				       nm_settings_connection_get_id (master_connection),
 				       nm_settings_connection_get_uuid (master_connection));
 				continue;
@@ -2873,44 +3072,60 @@ autoconnect_slaves (NMManager *self,
 
 			if (!uuid)
 				uuid = nm_settings_connection_get_uuid (master_connection);
-			g_object_set_qdata_full (G_OBJECT (slave_connection),
+			g_object_set_qdata_full (G_OBJECT (slave->connection),
 			                         autoconnect_root_quark (),
 			                         g_strdup (uuid),
 			                         g_free);
 
+			if (!slave->device) {
+				_LOGD (LOGD_CORE,
+				       "will NOT activate slave connection '%s' (%s) as a dependency for master '%s' (%s): "
+				       "no compatible device found",
+				       nm_settings_connection_get_id (slave->connection),
+				       nm_settings_connection_get_uuid (slave->connection),
+				       nm_settings_connection_get_id (master_connection),
+				       nm_settings_connection_get_uuid (master_connection));
+				continue;
+			}
+
 			_LOGD (LOGD_CORE, "will activate slave connection '%s' (%s) as a dependency for master '%s' (%s)",
-			       nm_settings_connection_get_id (slave_connection),
-			       nm_settings_connection_get_uuid (slave_connection),
+			       nm_settings_connection_get_id (slave->connection),
+			       nm_settings_connection_get_uuid (slave->connection),
 			       nm_settings_connection_get_id (master_connection),
 			       nm_settings_connection_get_uuid (master_connection));
 
 			/* Schedule slave activation */
 			nm_manager_activate_connection (self,
-			                                slave_connection,
+			                                slave->connection,
 			                                NULL,
 			                                NULL,
-			                                nm_manager_get_best_device_for_connection (self, NM_CONNECTION (slave_connection), FALSE),
+			                                slave->device,
 			                                subject,
+			                                NM_ACTIVATION_TYPE_MANAGED,
 			                                &local_err);
 			if (local_err) {
 				_LOGW (LOGD_CORE, "Slave connection activation failed: %s", local_err->message);
-				g_error_free (local_err);
+				g_clear_error (&local_err);
 			}
 		}
-		g_slist_free (slaves);
 	}
-	return ret;
 }
 
 static gboolean
 _internal_activate_vpn (NMManager *self, NMActiveConnection *active, GError **error)
 {
+	gboolean success;
+
 	g_assert (NM_IS_VPN_CONNECTION (active));
 
 	nm_exported_object_export (NM_EXPORTED_OBJECT (active));
-	return nm_vpn_manager_activate_connection (NM_MANAGER_GET_PRIVATE (self)->vpn_manager,
-	                                           NM_VPN_CONNECTION (active),
-	                                           error);
+	success = nm_vpn_manager_activate_connection (NM_MANAGER_GET_PRIVATE (self)->vpn_manager,
+	                                              NM_VPN_CONNECTION (active),
+	                                              error);
+	if (!success)
+		nm_exported_object_unexport (NM_EXPORTED_OBJECT (active));
+
+	return success;
 }
 
 /* Traverse the device to disconnected state. This means that the device is ready
@@ -2969,12 +3184,16 @@ active_connection_parent_active (NMActiveConnection *active,
 		} else {
 			_LOGW (LOGD_CORE, "Could not realize device '%s': %s",
 			       nm_device_get_iface (device), error->message);
-			nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
+			nm_active_connection_set_state (active,
+			                                NM_ACTIVE_CONNECTION_STATE_DEACTIVATED,
+			                                NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REALIZE_FAILED);
 		}
 	} else {
 		_LOGW (LOGD_CORE, "The parent connection device '%s' depended on disappeared.",
 		       nm_device_get_iface (device));
-		nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
+		nm_active_connection_set_state (active,
+		                                NM_ACTIVE_CONNECTION_STATE_DEACTIVATED,
+		                                NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_REMOVED);
 	}
 }
 
@@ -2982,6 +3201,7 @@ static gboolean
 _internal_activate_device (NMManager *self, NMActiveConnection *active, GError **error)
 {
 	NMDevice *device, *existing, *master_device = NULL;
+	NMActiveConnection *existing_ac;
 	NMConnection *applied;
 	NMSettingsConnection *connection;
 	NMSettingsConnection *master_connection = NULL;
@@ -3048,7 +3268,8 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 				return FALSE;
 			}
 
-			parent_ac = nm_manager_activate_connection (self, parent_con, NULL, NULL, parent, subject, error);
+			parent_ac = nm_manager_activate_connection (self, parent_con, NULL, NULL, parent,
+			                                            subject, NM_ACTIVATION_TYPE_MANAGED, error);
 			if (!parent_ac) {
 				g_prefix_error (error, "%s failed to activate parent: ", nm_device_get_iface (device));
 				return FALSE;
@@ -3142,9 +3363,12 @@ _internal_activate_device (NMManager *self, NMActiveConnection *active, GError *
 	autoconnect_slaves (self, connection, device, nm_active_connection_get_subject (active));
 
 	/* Disconnect the connection if connected or queued on another device */
-	existing = nm_manager_get_connection_device (self, NM_CONNECTION (connection));
-	if (existing)
-		nm_device_steal_connection (existing, connection);
+	existing_ac = active_connection_find_first (self, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING);
+	if (existing_ac) {
+		existing = nm_active_connection_get_device (existing_ac);
+		if (existing)
+			nm_device_steal_connection (existing, connection);
+	}
 
 	/* If the device is there, we can ready it for the activation. */
 	if (nm_device_is_real (device))
@@ -3243,6 +3467,7 @@ _new_active_connection (NMManager *self,
                         const char *specific_object,
                         NMDevice *device,
                         NMAuthSubject *subject,
+                        NMActivationType activation_type,
                         GError **error)
 {
 	NMSettingsConnection *settings_connection = NULL;
@@ -3253,7 +3478,7 @@ _new_active_connection (NMManager *self,
 	g_return_val_if_fail (NM_IS_AUTH_SUBJECT (subject), NULL);
 
 	/* Can't create new AC for already-active connection */
-	existing_ac = find_ac_for_connection (self, connection);
+	existing_ac = active_connection_find_first_by_connection (self, connection);
 	if (NM_IS_VPN_CONNECTION (existing_ac)) {
 		g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_CONNECTION_ALREADY_ACTIVE,
 		             "Connection '%s' is already active",
@@ -3271,6 +3496,8 @@ _new_active_connection (NMManager *self,
 		settings_connection = (NMSettingsConnection *) connection;
 
 	if (is_vpn) {
+		if (activation_type != NM_ACTIVATION_TYPE_MANAGED)
+			g_return_val_if_reached (NULL);
 		return _new_vpn_active_connection (self,
 		                                   settings_connection,
 		                                   specific_object,
@@ -3282,6 +3509,7 @@ _new_active_connection (NMManager *self,
 	                                                  applied,
 	                                                  specific_object,
 	                                                  subject,
+	                                                  activation_type,
 	                                                  device);
 }
 
@@ -3295,8 +3523,12 @@ _internal_activation_failed (NMManager *self,
 	       error_desc);
 
 	if (nm_active_connection_get_state (active) <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-		nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATING);
-		nm_active_connection_set_state (active, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED);
+		nm_active_connection_set_state (active,
+		                                NM_ACTIVE_CONNECTION_STATE_DEACTIVATING,
+		                                NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN);
+		nm_active_connection_set_state (active,
+		                                NM_ACTIVE_CONNECTION_STATE_DEACTIVATED,
+		                                NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN);
 	}
 }
 
@@ -3334,6 +3566,8 @@ _internal_activation_auth_done (NMActiveConnection *active,
  * @specific_object: the specific object path, if any, for the activation
  * @device: the #NMDevice to activate @connection on
  * @subject: the subject which requested activation
+ * @activation_type: whether to assume the connection. That is, take over gracefully,
+ *   non-destructible.
  * @error: return location for an error
  *
  * Begins a new internally-initiated activation of @connection on @device.
@@ -3353,6 +3587,7 @@ nm_manager_activate_connection (NMManager *self,
                                 const char *specific_object,
                                 NMDevice *device,
                                 NMAuthSubject *subject,
+                                NMActivationType activation_type,
                                 GError **error)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
@@ -3399,6 +3634,7 @@ nm_manager_activate_connection (NMManager *self,
 	                                 specific_object,
 	                                 device,
 	                                 subject,
+	                                 activation_type,
 	                                 error);
 	if (active) {
 		priv->authorizing_connections = g_slist_prepend (priv->authorizing_connections, active);
@@ -3484,7 +3720,7 @@ validate_activation_request (NMManager *self,
 			goto error;
 		}
 	} else
-		device = nm_manager_get_best_device_for_connection (self, connection, TRUE);
+		device = nm_manager_get_best_device_for_connection (self, connection, TRUE, NULL);
 
 	if (!device && !vpn) {
 		gboolean is_software = nm_connection_is_virtual (connection);
@@ -3642,6 +3878,7 @@ impl_manager_activate_connection (NMManager *self,
 	                                 specific_object_path,
 	                                 device,
 	                                 subject,
+	                                 NM_ACTIVATION_TYPE_MANAGED,
 	                                 &error);
 	if (!active)
 		goto error;
@@ -3739,8 +3976,8 @@ _add_and_activate_auth_done (NMActiveConnection *active,
 	if (success) {
 		NMConnection *connection;
 
-		connection = g_object_steal_data (G_OBJECT (active),
-		                                  TAG_ACTIVE_CONNETION_ADD_AND_ACTIVATE);
+		connection = g_object_steal_qdata (G_OBJECT (active),
+		                                   active_connection_add_and_activate_quark ());
 
 		info = g_slice_new (AddAndActivateInfo);
 		info->manager = self;
@@ -3814,7 +4051,17 @@ impl_manager_add_and_activate_connection (NMManager *self,
 	if (!subject)
 		goto error;
 
-	all_connections = nm_settings_get_connections_sorted (priv->settings);
+	{
+		gs_free NMSettingsConnection **connections = NULL;
+		guint i, len;
+
+		connections = nm_settings_get_connections_sorted (priv->settings, &len);
+		all_connections = NULL;
+		for (i = len; i > 0; ) {
+			i--;
+			all_connections = g_slist_prepend (all_connections, connections[i]);
+		}
+	}
 	if (vpn) {
 		/* Try to fill the VPN's connection setting and name at least */
 		if (!nm_connection_get_setting_vpn (connection)) {
@@ -3851,14 +4098,15 @@ impl_manager_add_and_activate_connection (NMManager *self,
 	                                 specific_object_path,
 	                                 device,
 	                                 subject,
+	                                 NM_ACTIVATION_TYPE_MANAGED,
 	                                 &error);
 	if (!active)
 		goto error;
 
-	g_object_set_data_full (G_OBJECT (active),
-	                        TAG_ACTIVE_CONNETION_ADD_AND_ACTIVATE,
-	                        connection,
-	                        g_object_unref);
+	g_object_set_qdata_full (G_OBJECT (active),
+	                         active_connection_add_and_activate_quark (),
+	                         connection,
+	                         g_object_unref);
 
 	nm_active_connection_authorize (active, connection, _add_and_activate_auth_done, self, context);
 	g_object_unref (subject);
@@ -3886,10 +4134,11 @@ nm_manager_deactivate_connection (NMManager *manager,
 	gboolean success = FALSE;
 
 	if (NM_IS_VPN_CONNECTION (active)) {
-		NMVpnConnectionStateReason vpn_reason = NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED;
+		NMActiveConnectionStateReason vpn_reason = NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED;
+
+		if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_CONNECTION_REMOVED)
+			vpn_reason = NM_ACTIVE_CONNECTION_STATE_REASON_CONNECTION_REMOVED;
 
-		if (reason == NM_DEVICE_STATE_REASON_CONNECTION_REMOVED)
-			vpn_reason = NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED;
 		if (nm_vpn_connection_deactivate (NM_VPN_CONNECTION (active), vpn_reason, FALSE))
 			success = TRUE;
 		else
@@ -4240,7 +4489,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 					nm_device_set_enabled (device, enabled);
 			}
 
-			nm_device_set_autoconnect (device, TRUE);
+			nm_device_set_autoconnect_intern (device, TRUE);
 
 			nm_device_set_unmanaged_by_flags (device, NM_UNMANAGED_SLEEPING, FALSE, NM_DEVICE_STATE_REASON_NOW_MANAGED);
 		}
@@ -4631,24 +4880,36 @@ impl_manager_get_logging (NMManager *manager,
 	                                                      nm_logging_domains_to_string ()));
 }
 
+typedef struct {
+	guint remaining;
+	GDBusMethodInvocation *context;
+	NMConnectivityState state;
+} ConnectivityCheckData;
+
 static void
-connectivity_check_done (GObject *object,
-                         GAsyncResult *result,
-                         gpointer user_data)
+device_connectivity_done (NMDevice *device, NMConnectivityState state, gpointer user_data)
 {
-	GDBusMethodInvocation *context = user_data;
-	NMConnectivityState state;
-	GError *error = NULL;
+	ConnectivityCheckData *data = user_data;
 
-	state = nm_connectivity_check_finish (NM_CONNECTIVITY (object), result, &error);
-	if (error)
-		g_dbus_method_invocation_take_error (context, error);
-	else {
-		g_dbus_method_invocation_return_value (context,
-		                                       g_variant_new ("(u)", state));
+	data->remaining--;
+
+	/* We check if the state is already FULL so that we can provide the
+	 * response without waiting for slower devices that are not going to
+	 * affect the overall state anyway. */
+
+	if (data->state != NM_CONNECTIVITY_FULL) {
+		if (state > data->state)
+			data->state = state;
+
+		if (data->state == NM_CONNECTIVITY_FULL || !data->remaining) {
+			g_dbus_method_invocation_return_value (data->context,
+			                                       g_variant_new ("(u)", data->state));
+		}
 	}
-}
 
+	if (!data->remaining)
+		g_slice_free (ConnectivityCheckData, data);
+}
 
 static void
 check_connectivity_auth_done_cb (NMAuthChain *chain,
@@ -4660,6 +4921,8 @@ check_connectivity_auth_done_cb (NMAuthChain *chain,
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	GError *error = NULL;
 	NMAuthCallResult result;
+	ConnectivityCheckData *data;
+	const GSList *devices;
 
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 
@@ -4677,9 +4940,15 @@ check_connectivity_auth_done_cb (NMAuthChain *chain,
 		                             "Not authorized to recheck connectivity");
 	} else {
 		/* it's allowed */
-		nm_connectivity_check_async (priv->connectivity,
-		                             connectivity_check_done,
-		                             context);
+		data = g_slice_new0 (ConnectivityCheckData);
+		data->context = context;
+
+		for (devices = priv->devices; devices; devices = devices->next) {
+			data->remaining++;
+			nm_device_check_connectivity (NM_DEVICE (devices->data),
+			                              device_connectivity_done,
+			                              data);
+		}
 	}
 
 	if (error)
@@ -4728,6 +4997,7 @@ nm_manager_write_device_state (NMManager *self)
 		NMDevice *device = NM_DEVICE (devices->data);
 		int ifindex;
 		gboolean managed;
+		NMConfigDeviceStateManagedType managed_type;
 		NMConnection *settings_connection;
 		const char *uuid = NULL;
 		const char *perm_hw_addr_fake = NULL;
@@ -4749,39 +5019,48 @@ nm_manager_write_device_state (NMManager *self)
 			settings_connection = NM_CONNECTION (nm_device_get_settings_connection (device));
 			if (settings_connection)
 				uuid = nm_connection_get_uuid (settings_connection);
-		}
+			managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED;
+		} else if (nm_device_get_unmanaged_flags (device, NM_UNMANAGED_USER_EXPLICIT))
+			managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED;
+		else
+			managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNKNOWN;
 
 		perm_hw_addr_fake = nm_device_get_permanent_hw_address_full (device, FALSE, &perm_hw_addr_is_fake);
 		if (perm_hw_addr_fake && !perm_hw_addr_is_fake)
 			perm_hw_addr_fake = NULL;
 
-		if (nm_config_device_state_write (priv->config,
-		                                  ifindex,
-		                                  managed,
+		if (nm_config_device_state_write (ifindex,
+		                                  managed_type,
 		                                  perm_hw_addr_fake,
 		                                  uuid))
 			g_hash_table_add (seen_ifindexes, GINT_TO_POINTER (ifindex));
 	}
 
-	nm_config_device_state_prune_unseen (priv->config,
-	                                     seen_ifindexes);
+	nm_config_device_state_prune_unseen (seen_ifindexes);
+}
+
+static gboolean
+devices_inited_cb (gpointer user_data)
+{
+	NMManager *self = user_data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	priv->devices_inited_id = 0;
+	priv->devices_inited = TRUE;
+	check_if_startup_complete (self);
+	return G_SOURCE_REMOVE;
 }
 
 gboolean
 nm_manager_start (NMManager *self, GError **error)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter, *connections;
+	gs_free NMSettingsConnection **connections = NULL;
 	guint i;
 
 	if (!nm_settings_start (priv->settings, error))
 		return FALSE;
 
-	g_signal_connect (NM_PLATFORM_GET,
-	                  NM_PLATFORM_SIGNAL_LINK_CHANGED,
-	                  G_CALLBACK (platform_link_cb),
-	                  self);
-
 	/* Set initial radio enabled/disabled state */
 	for (i = 0; i < RFKILL_TYPE_MAX; i++) {
 		RadioState *rstate = &priv->radio_states[i];
@@ -4814,6 +5093,13 @@ nm_manager_start (NMManager *self, GError **error)
 	nm_device_factory_manager_load_factories (_register_device_factory, self);
 	nm_device_factory_manager_for_each_factory (start_factory, NULL);
 
+	nm_platform_process_events (NM_PLATFORM_GET);
+
+	g_signal_connect (NM_PLATFORM_GET,
+	                  NM_PLATFORM_SIGNAL_LINK_CHANGED,
+	                  G_CALLBACK (platform_link_cb),
+	                  self);
+
 	platform_query_devices (self);
 
 	/* Load VPN plugins */
@@ -4823,14 +5109,12 @@ nm_manager_start (NMManager *self, GError **error)
 	 * connection-added signals thus devices have to be created manually.
 	 */
 	_LOGD (LOGD_CORE, "creating virtual devices...");
-	connections = nm_settings_get_connections_sorted (priv->settings);
-	for (iter = connections; iter; iter = iter->next)
-		connection_changed (self, NM_CONNECTION (iter->data));
-	g_slist_free (connections);
+	connections = nm_settings_get_connections_sorted (priv->settings, NULL);
+	for (i = 0; connections[i]; i++)
+		connection_changed (self, NM_CONNECTION (connections[i]));
 
-	priv->devices_inited = TRUE;
-
-	check_if_startup_complete (self);
+	nm_clear_g_source (&priv->devices_inited_id);
+	priv->devices_inited_id = g_idle_add_full (G_PRIORITY_LOW + 10, devices_inited_cb, self, NULL);
 
 	return TRUE;
 }
@@ -4845,6 +5129,8 @@ nm_manager_stop (NMManager *self)
 		remove_device (self, NM_DEVICE (priv->devices->data), TRUE, TRUE);
 
 	_active_connection_cleanup (self);
+
+	nm_clear_g_source (&priv->devices_inited_id);
 }
 
 static gboolean
@@ -4877,20 +5163,6 @@ handle_firmware_changed (gpointer user_data)
 }
 
 static void
-connectivity_changed (NMConnectivity *connectivity,
-                      GParamSpec *pspec,
-                      gpointer user_data)
-{
-	NMManager *self = NM_MANAGER (user_data);
-
-	_LOGD (LOGD_CORE, "connectivity checking indicates %s",
-	       nm_connectivity_state_to_string (nm_connectivity_get_state (connectivity)));
-
-	nm_manager_update_state (self);
-	_notify (self, PROP_CONNECTIVITY);
-}
-
-static void
 firmware_dir_changed (GFileMonitor *monitor,
                       GFile *file,
                       GFile *other_file,
@@ -5044,9 +5316,9 @@ prop_set_auth_done_cb (NMAuthChain *chain,
 	priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
 	result = nm_auth_chain_get_result (chain, pfd->permission);
 	if (error || (result != NM_AUTH_CALL_RESULT_YES)) {
-		reply = g_dbus_message_new_method_error (pfd->message,
-		                                         NM_PERM_DENIED_ERROR,
-		                                         (error_message = "Not authorized to perform this operation"));
+		reply = g_dbus_message_new_method_error_literal (pfd->message,
+		                                                 NM_PERM_DENIED_ERROR,
+		                                                 (error_message = "Not authorized to perform this operation"));
 		if (error)
 			error_message = error->message;
 		goto done;
@@ -5055,17 +5327,17 @@ prop_set_auth_done_cb (NMAuthChain *chain,
 	object = NM_EXPORTED_OBJECT (nm_bus_manager_get_registered_object (priv->dbus_mgr,
 	                                                                   g_dbus_message_get_path (pfd->message)));
 	if (!object) {
-		reply = g_dbus_message_new_method_error (pfd->message,
-		                                         "org.freedesktop.DBus.Error.UnknownObject",
-		                                         (error_message = "Object doesn't exist."));
+		reply = g_dbus_message_new_method_error_literal (pfd->message,
+		                                                 "org.freedesktop.DBus.Error.UnknownObject",
+		                                                 (error_message = "Object doesn't exist."));
 		goto done;
 	}
 
 	/* do some extra type checking... */
 	if (!nm_exported_object_get_interface_by_type (object, pfd->interface_type)) {
-		reply = g_dbus_message_new_method_error (pfd->message,
-		                                         "org.freedesktop.DBus.Error.InvalidArgs",
-		                                         (error_message = "Object is of unexpected type."));
+		reply = g_dbus_message_new_method_error_literal (pfd->message,
+		                                                 "org.freedesktop.DBus.Error.InvalidArgs",
+		                                                 (error_message = "Object is of unexpected type."));
 		goto done;
 	}
 
@@ -5078,9 +5350,9 @@ prop_set_auth_done_cb (NMAuthChain *chain,
 		global_dns = nm_config_data_get_global_dns_config (nm_config_get_data (priv->config));
 
 		if (global_dns && !nm_global_dns_config_is_internal (global_dns)) {
-			reply = g_dbus_message_new_method_error (pfd->message,
-			                                         NM_PERM_DENIED_ERROR,
-			                                         (error_message = "Global DNS configuration already set via configuration file"));
+			reply = g_dbus_message_new_method_error_literal (pfd->message,
+			                                                 NM_PERM_DENIED_ERROR,
+			                                                 (error_message = "Global DNS configuration already set via configuration file"));
 			goto done;
 		}
 		/* ... but set the property on the @object itself. It would be correct to set the property
@@ -5122,18 +5394,18 @@ do_set_property_check (gpointer user_data)
 
 	pfd->subject = nm_auth_subject_new_unix_process_from_message (pfd->connection, pfd->message);
 	if (!pfd->subject) {
-		reply = g_dbus_message_new_method_error (pfd->message,
-		                                         NM_PERM_DENIED_ERROR,
-		                                         (error_message = "Could not determine request UID."));
+		reply = g_dbus_message_new_method_error_literal (pfd->message,
+		                                                 NM_PERM_DENIED_ERROR,
+		                                                 (error_message = "Could not determine request UID."));
 		goto out;
 	}
 
 	/* Validate the user request */
 	chain = nm_auth_chain_new_subject (pfd->subject, NULL, prop_set_auth_done_cb, pfd);
 	if (!chain) {
-		reply = g_dbus_message_new_method_error (pfd->message,
-		                                         NM_PERM_DENIED_ERROR,
-		                                         (error_message = "Could not authenticate request."));
+		reply = g_dbus_message_new_method_error_literal (pfd->message,
+		                                                 NM_PERM_DENIED_ERROR,
+		                                                 (error_message = "Could not authenticate request."));
 		goto out;
 	}
 
@@ -5697,7 +5969,6 @@ constructed (GObject *object)
 {
 	NMManager *self = NM_MANAGER (object);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMConfigData *config_data;
 	const NMConfigState *state;
 
 	G_OBJECT_CLASS (nm_manager_parent_class)->constructed (object);
@@ -5740,13 +6011,6 @@ constructed (GObject *object)
 	                  G_CALLBACK (_config_changed_cb),
 	                  self);
 
-	config_data = nm_config_get_data (priv->config);
-	priv->connectivity = nm_connectivity_new (nm_config_data_get_connectivity_uri (config_data),
-	                                          nm_config_data_get_connectivity_interval (config_data),
-	                                          nm_config_data_get_connectivity_response (config_data));
-	g_signal_connect (priv->connectivity, "notify::" NM_CONNECTIVITY_STATE,
-	                  G_CALLBACK (connectivity_changed), self);
-
 	state = nm_config_state_get (priv->config);
 
 	priv->net_enabled = state->net_enabled;
@@ -5902,7 +6166,7 @@ get_property (GObject *object, guint prop_id,
 		nm_utils_g_value_set_object_path_array (value, priv->active_connections, NULL, NULL);
 		break;
 	case PROP_CONNECTIVITY:
-		g_value_set_uint (value, nm_connectivity_get_state (priv->connectivity));
+		g_value_set_uint (value, priv->connectivity_state);
 		break;
 	case PROP_PRIMARY_CONNECTION:
 		nm_utils_g_value_set_object_path (value, priv->primary_connection);
@@ -6003,6 +6267,8 @@ dispose (GObject *object)
 	g_slist_free_full (priv->auth_chains, (GDestroyNotify) nm_auth_chain_unref);
 	priv->auth_chains = NULL;
 
+	nm_clear_g_source (&priv->devices_inited_id);
+
 	if (priv->checkpoint_mgr) {
 		nm_checkpoint_manager_destroy_all (priv->checkpoint_mgr, NULL);
 		g_clear_pointer (&priv->checkpoint_mgr, nm_checkpoint_manager_unref);
@@ -6029,10 +6295,6 @@ dispose (GObject *object)
 		g_signal_handlers_disconnect_by_func (priv->config, _config_changed_cb, manager);
 		g_clear_object (&priv->config);
 	}
-	if (priv->connectivity) {
-		g_signal_handlers_disconnect_by_func (priv->connectivity, connectivity_changed, manager);
-		g_clear_object (&priv->connectivity);
-	}
 
 	g_free (priv->hostname);
 
diff --git a/src/nm-manager.h b/src/nm-manager.h
index c69fc9e1..676fa995 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -84,7 +84,10 @@ gboolean      nm_manager_start                         (NMManager *manager,
 void          nm_manager_stop                          (NMManager *manager);
 NMState       nm_manager_get_state                     (NMManager *manager);
 const GSList *nm_manager_get_active_connections        (NMManager *manager);
-GSList *      nm_manager_get_activatable_connections   (NMManager *manager);
+
+NMSettingsConnection **nm_manager_get_activatable_connections (NMManager *manager,
+                                                               guint *out_len,
+                                                               gboolean sort);
 
 void          nm_manager_write_device_state (NMManager *manager);
 
@@ -112,6 +115,7 @@ NMActiveConnection *nm_manager_activate_connection     (NMManager *manager,
                                                         const char *specific_object,
                                                         NMDevice *device,
                                                         NMAuthSubject *subject,
+                                                        NMActivationType activation_type,
                                                         GError **error);
 
 gboolean            nm_manager_deactivate_connection   (NMManager *manager,
diff --git a/src/nm-netns.c b/src/nm-netns.c
new file mode 100644
index 00000000..a81aa696
--- /dev/null
+++ b/src/nm-netns.c
@@ -0,0 +1,176 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-netns.h"
+
+#include "platform/nm-platform.h"
+#include "platform/nmp-netns.h"
+#include "nm-route-manager.h"
+#include "nm-default-route-manager.h"
+#include "nm-core-internal.h"
+#include "NetworkManagerUtils.h"
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PLATFORM,
+);
+
+typedef struct {
+	NMPlatform *platform;
+	NMPNetns *platform_netns;
+	NMRouteManager *route_manager;
+	NMDefaultRouteManager *default_route_manager;
+	bool log_with_ptr;
+} NMNetnsPrivate;
+
+struct _NMNetns {
+	GObject parent;
+	NMNetnsPrivate _priv;
+};
+
+struct _NMNetnsClass {
+	GObjectClass parent;
+};
+
+G_DEFINE_TYPE (NMNetns, nm_netns, G_TYPE_OBJECT);
+
+#define NM_NETNS_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMNetns, NM_IS_NETNS)
+
+/*****************************************************************************/
+
+NM_DEFINE_SINGLETON_GETTER (NMNetns, nm_netns_get, NM_TYPE_NETNS);
+
+/*****************************************************************************/
+
+NMPNetns *
+nm_netns_get_platform_netns (NMNetns *self)
+{
+	return NM_NETNS_GET_PRIVATE (self)->platform_netns;
+}
+
+NMPlatform *
+nm_netns_get_platform (NMNetns *self)
+{
+	return NM_NETNS_GET_PRIVATE (self)->platform;
+}
+
+NMDefaultRouteManager *
+nm_netns_get_default_route_manager (NMNetns *self)
+{
+	return NM_NETNS_GET_PRIVATE (self)->default_route_manager;
+}
+
+NMRouteManager *
+nm_netns_get_route_manager (NMNetns *self)
+{
+	return NM_NETNS_GET_PRIVATE (self)->route_manager;
+}
+
+/*****************************************************************************/
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMNetns *self = NM_NETNS (object);
+	NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self);
+
+	switch (prop_id) {
+	case PROP_PLATFORM:
+		/* construct-only */
+		priv->platform = g_value_get_object (value) ?: NM_PLATFORM_GET;
+		if (!priv->platform)
+			g_return_if_reached ();
+		g_object_ref (priv->platform);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_netns_init (NMNetns *self)
+{
+}
+
+static void
+constructed (GObject *object)
+{
+	NMNetns *self = NM_NETNS (object);
+	NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self);
+	gboolean log_with_ptr;
+
+	if (!priv->platform)
+		g_return_if_reached ();
+
+	log_with_ptr = nm_platform_get_log_with_ptr (priv->platform);
+
+	priv->platform_netns = nm_platform_netns_get (priv->platform);
+	priv->route_manager = nm_route_manager_new (log_with_ptr, priv->platform);
+	priv->default_route_manager = nm_default_route_manager_new (log_with_ptr, priv->platform);
+
+	G_OBJECT_CLASS (nm_netns_parent_class)->constructed (object);
+}
+
+NMNetns *
+nm_netns_new (NMPlatform *platform)
+{
+	return g_object_new (NM_TYPE_NETNS,
+	                     NM_NETNS_PLATFORM, platform,
+	                     NULL);
+}
+
+static void
+dispose (GObject *object)
+{
+	NMNetns *self = NM_NETNS (object);
+	NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self);
+
+	g_clear_object (&priv->route_manager);
+	g_clear_object (&priv->default_route_manager);
+	g_clear_object (&priv->platform);
+
+	G_OBJECT_CLASS (nm_netns_parent_class)->dispose (object);
+}
+
+static void
+nm_netns_class_init (NMNetnsClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	object_class->constructed = constructed;
+	object_class->set_property = set_property;
+	object_class->dispose = dispose;
+
+	obj_properties[PROP_PLATFORM] =
+	    g_param_spec_object (NM_NETNS_PLATFORM, "", "",
+	                         NM_TYPE_PLATFORM,
+	                         G_PARAM_WRITABLE |
+	                         G_PARAM_CONSTRUCT_ONLY |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+}
diff --git a/src/nm-netns.h b/src/nm-netns.h
new file mode 100644
index 00000000..fd5daf47
--- /dev/null
+++ b/src/nm-netns.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2017 Red Hat, Inc.
+ */
+
+#ifndef __NM_NETNS_H__
+#define __NM_NETNS_H__
+
+#define NM_TYPE_NETNS            (nm_netns_get_type ())
+#define NM_NETNS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_NETNS, NMNetns))
+#define NM_NETNS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_NETNS, NMNetnsClass))
+#define NM_IS_NETNS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_NETNS))
+#define NM_IS_NETNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_NETNS))
+#define NM_NETNS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_NETNS, NMNetnsClass))
+
+#define NM_NETNS_PLATFORM "platform"
+
+typedef struct _NMNetnsClass NMNetnsClass;
+
+GType nm_netns_get_type (void);
+
+NMNetns *nm_netns_get (void);
+NMNetns *nm_netns_new (NMPlatform *platform);
+
+NMPlatform *nm_netns_get_platform (NMNetns *self);
+NMPNetns *nm_netns_get_platform_netns (NMNetns *self);
+NMRouteManager *nm_netns_get_route_manager (NMNetns *self);
+NMDefaultRouteManager *nm_netns_get_default_route_manager (NMNetns *self);
+
+#define NM_NETNS_GET (nm_netns_get ())
+
+#endif /* __NM_NETNS_H__ */
diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c
index 450cf0e3..cfc028c2 100644
--- a/src/nm-pacrunner-manager.c
+++ b/src/nm-pacrunner-manager.c
@@ -28,24 +28,29 @@
 #include "nm-ip4-config.h"
 #include "nm-ip6-config.h"
 
+static void pacrunner_remove_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data);
+
 #define PACRUNNER_DBUS_SERVICE "org.pacrunner"
 #define PACRUNNER_DBUS_INTERFACE "org.pacrunner.Manager"
 #define PACRUNNER_DBUS_PATH "/org/pacrunner/manager"
 
 /*****************************************************************************/
 
-struct remove_data {
-	char *iface;
+struct _NMPacrunnerCallId {
+	NMPacrunnerManager *manager;
+	GVariant *args;
 	char *path;
+	guint refcount;
+	bool removed;
 };
 
+typedef struct _NMPacrunnerCallId Config;
+
 typedef struct {
 	char *iface;
-	GPtrArray *domains;
 	GDBusProxy *pacrunner;
 	GCancellable *pacrunner_cancellable;
-	GList *args;
-	GList *remove;
+	GList *configs;
 } NMPacrunnerManagerPrivate;
 
 struct _NMPacrunnerManager {
@@ -68,23 +73,58 @@ NM_DEFINE_SINGLETON_GETTER (NMPacrunnerManager, nm_pacrunner_manager_get, NM_TYP
 /*****************************************************************************/
 
 #define _NMLOG_DOMAIN      LOGD_PROXY
-#define _NMLOG(level, ...) __NMLOG_DEFAULT_WITH_ADDR (level, _NMLOG_DOMAIN, "pacrunner", __VA_ARGS__)
+#define _NMLOG(level, ...) __NMLOG_DEFAULT (level, _NMLOG_DOMAIN, "pacrunner", __VA_ARGS__)
+
+#define _NMLOG2_PREFIX_NAME "pacrunner"
+#define _NMLOG2(level, config, ...) \
+	G_STMT_START { \
+		nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
+		        "%s%p]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+		        "pacrunner: call[", \
+		        (config) \
+		        _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+	} G_STMT_END
 
 /*****************************************************************************/
 
+static Config *
+config_new (NMPacrunnerManager *manager, GVariant *args)
+{
+	Config *config;
+
+	config = g_slice_new0 (Config);
+	config->manager = manager;
+	config->args = g_variant_ref_sink (args);
+	config->refcount = 1;
+
+	return config;
+}
+
 static void
-remove_data_destroy (struct remove_data *data)
+config_ref (Config *config)
 {
-	g_return_if_fail (data != NULL);
+	g_assert (config);
+	g_assert (config->refcount > 0);
 
-	g_free (data->iface);
-	g_free (data->path);
-	memset (data, 0, sizeof (struct remove_data));
-	g_free (data);
+	config->refcount++;
 }
 
 static void
-add_proxy_config (NMPacrunnerManager *self, GVariantBuilder *proxy_data, const NMProxyConfig *proxy_config)
+config_unref (Config *config)
+{
+	g_assert (config);
+	g_assert (config->refcount > 0);
+
+	if (config->refcount == 1) {
+		g_variant_unref (config->args);
+		g_free (config->path);
+		g_slice_free (Config, config);
+	} else
+		config->refcount--;
+}
+
+static void
+add_proxy_config (GVariantBuilder *proxy_data, const NMProxyConfig *proxy_config)
 {
 	const char *pac_url, *pac_script;
 	NMProxyConfigMethod method;
@@ -113,19 +153,18 @@ add_proxy_config (NMPacrunnerManager *self, GVariantBuilder *proxy_data, const N
 }
 
 static void
-add_ip4_config (NMPacrunnerManager *self, GVariantBuilder *proxy_data, NMIP4Config *ip4)
+get_ip4_domains (GPtrArray *domains, NMIP4Config *ip4)
 {
-	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	char *cidr;
 	int i;
-	char *cidr = NULL;
 
 	/* Extract searches */
 	for (i = 0; i < nm_ip4_config_get_num_searches (ip4); i++)
-		g_ptr_array_add (priv->domains, g_strdup (nm_ip4_config_get_search (ip4, i)));
+		g_ptr_array_add (domains, g_strdup (nm_ip4_config_get_search (ip4, i)));
 
 	/* Extract domains */
 	for (i = 0; i < nm_ip4_config_get_num_domains (ip4); i++)
-		g_ptr_array_add (priv->domains, g_strdup (nm_ip4_config_get_domain (ip4, i)));
+		g_ptr_array_add (domains, g_strdup (nm_ip4_config_get_domain (ip4, i)));
 
 	/* Add addresses and routes in CIDR form */
 	for (i = 0; i < nm_ip4_config_get_num_addresses (ip4); i++) {
@@ -134,8 +173,7 @@ add_ip4_config (NMPacrunnerManager *self, GVariantBuilder *proxy_data, NMIP4Conf
 		cidr = g_strdup_printf ("%s/%u",
 		                        nm_utils_inet4_ntop (address->address, NULL),
 		                        address->plen);
-		g_ptr_array_add (priv->domains, g_strdup (cidr));
-		g_free (cidr);
+		g_ptr_array_add (domains, cidr);
 	}
 
 	for (i = 0; i < nm_ip4_config_get_num_routes (ip4); i++) {
@@ -144,25 +182,23 @@ add_ip4_config (NMPacrunnerManager *self, GVariantBuilder *proxy_data, NMIP4Conf
 		cidr = g_strdup_printf ("%s/%u",
 		                        nm_utils_inet4_ntop (routes->network, NULL),
 		                        routes->plen);
-		g_ptr_array_add (priv->domains, g_strdup (cidr));
-		g_free (cidr);
+		g_ptr_array_add (domains, cidr);
 	}
 }
 
 static void
-add_ip6_config (NMPacrunnerManager *self, GVariantBuilder *proxy_data, NMIP6Config *ip6)
+get_ip6_domains (GPtrArray *domains, NMIP6Config *ip6)
 {
-	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	char *cidr;
 	int i;
-	char *cidr = NULL;
 
 	/* Extract searches */
 	for (i = 0; i < nm_ip6_config_get_num_searches (ip6); i++)
-		g_ptr_array_add (priv->domains, g_strdup (nm_ip6_config_get_search (ip6, i)));
+		g_ptr_array_add (domains, g_strdup (nm_ip6_config_get_search (ip6, i)));
 
 	/* Extract domains */
 	for (i = 0; i < nm_ip6_config_get_num_domains (ip6); i++)
-		g_ptr_array_add (priv->domains, g_strdup (nm_ip6_config_get_domain (ip6, i)));
+		g_ptr_array_add (domains, g_strdup (nm_ip6_config_get_domain (ip6, i)));
 
 	/* Add addresses and routes in CIDR form */
 	for (i = 0; i < nm_ip6_config_get_num_addresses (ip6); i++) {
@@ -171,8 +207,7 @@ add_ip6_config (NMPacrunnerManager *self, GVariantBuilder *proxy_data, NMIP6Conf
 		cidr = g_strdup_printf ("%s/%u",
 		                        nm_utils_inet6_ntop (&address->address, NULL),
 		                        address->plen);
-		g_ptr_array_add (priv->domains, g_strdup (cidr));
-		g_free (cidr);
+		g_ptr_array_add (domains, cidr);
 	}
 
 	for (i = 0; i < nm_ip6_config_get_num_routes (ip6); i++) {
@@ -181,93 +216,101 @@ add_ip6_config (NMPacrunnerManager *self, GVariantBuilder *proxy_data, NMIP6Conf
 		cidr = g_strdup_printf ("%s/%u",
 		                        nm_utils_inet6_ntop (&routes->network, NULL),
 		                        routes->plen);
-		g_ptr_array_add (priv->domains, g_strdup (cidr));
-		g_free (cidr);
+		g_ptr_array_add (domains, cidr);
 	}
 }
 
 static void
-pacrunner_send_done (GObject *source, GAsyncResult *res, gpointer user_data)
+pacrunner_send_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
 {
-	NMPacrunnerManager *self = NM_PACRUNNER_MANAGER (user_data);
-	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	Config *config = user_data;
+	NMPacrunnerManager *self;
+	NMPacrunnerManagerPrivate *priv;
 	gs_free_error GError *error = NULL;
 	gs_unref_variant GVariant *variant = NULL;
 	const char *path = NULL;
-	GList *iter = NULL;
-	gboolean found = FALSE;
 
-	variant = g_dbus_proxy_call_finish (priv->pacrunner, res, &error);
-	if (!variant) {
-		_LOGD ("sending proxy config to pacrunner failed: %s", error->message);
-	} else {
-		struct remove_data *data;
-		g_variant_get (variant, "(&o)", &path);
+	g_return_if_fail (!config->path);
 
-		/* Replace the old path (if any) of proxy config with the new one returned
-		 * from CreateProxyConfiguration() DBus method on pacrunner.
-		 */
-		for (iter = g_list_first (priv->remove); iter; iter = g_list_next (iter)) {
-			struct remove_data *r = iter->data;
-			if (g_strcmp0 (priv->iface, r->iface) == 0) {
-				g_free (r->path);
-				r->path = g_strdup (path);
-				found = TRUE;
-				break;
-			}
-		}
+	variant = g_dbus_proxy_call_finish (proxy, res, &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		config_unref (config);
+		return;
+	}
+
+	self = NM_PACRUNNER_MANAGER (config->manager);
+	priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
 
-		if (!found) {
-			data = g_malloc0 (sizeof (struct remove_data));
-			data->iface = g_strdup (priv->iface);
-			data->path = g_strdup (path);
-			priv->remove = g_list_append (priv->remove, data);
-			_LOGD ("proxy config sent to pacrunner");
+	if (!variant)
+		_LOG2D (config, "sending failed: %s", error->message);
+	else {
+		g_variant_get (variant, "(&o)", &path);
+
+		config->path = g_strdup (path);
+		_LOG2D (config, "sent");
+
+		if (config->removed) {
+			g_dbus_proxy_call (priv->pacrunner,
+			                   "DestroyProxyConfiguration",
+			                   g_variant_new ("(o)", config->path),
+			                   G_DBUS_CALL_FLAGS_NO_AUTO_START,
+			                   -1,
+			                   priv->pacrunner_cancellable,
+			                   (GAsyncReadyCallback) pacrunner_remove_done,
+			                   config);
 		}
 	}
+	config_unref (config);
 }
 
 static void
-send_pacrunner_proxy_data (NMPacrunnerManager *self, GVariant *pacrunner_manager_args)
+pacrunner_send_config (NMPacrunnerManager *self, Config *config)
 {
 	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
 
-	if (!pacrunner_manager_args)
-		return;
+	if (priv->pacrunner) {
+		_LOG2T (config, "sending...");
+
+		config_ref (config);
+		g_clear_pointer (&config->path, g_free);
 
-	if (priv->pacrunner)
 		g_dbus_proxy_call (priv->pacrunner,
 		                   "CreateProxyConfiguration",
-		                   pacrunner_manager_args,
-		                   G_DBUS_CALL_FLAGS_NONE,
+		                   config->args,
+		                   G_DBUS_CALL_FLAGS_NO_AUTO_START,
 		                   -1,
-		                   NULL,
-		                  (GAsyncReadyCallback) pacrunner_send_done,
-		                   self);
+		                   priv->pacrunner_cancellable,
+		                   (GAsyncReadyCallback) pacrunner_send_done,
+		                   config);
+	}
 }
 
 static void
-name_owner_changed (GObject *object,
-                    GParamSpec *pspec,
-                    gpointer user_data)
+name_owner_changed (NMPacrunnerManager *self)
 {
-	NMPacrunnerManager *self = NM_PACRUNNER_MANAGER (user_data);
 	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
 	gs_free char *owner = NULL;
 	GList *iter = NULL;
 
-	owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (object));
+	owner = g_dbus_proxy_get_name_owner (priv->pacrunner);
 	if (owner) {
-		_LOGD ("pacrunner appeared as %s", owner);
-		for (iter = g_list_first(priv->args); iter; iter = g_list_next(iter)) {
-			send_pacrunner_proxy_data (self, iter->data);
-		}
+		_LOGD ("name owner appeared (%s)", owner);
+		for (iter = g_list_first (priv->configs); iter; iter = g_list_next (iter))
+			pacrunner_send_config (self, iter->data);
 	} else {
-		_LOGD ("pacrunner disappeared");
+		_LOGD ("name owner disappeared");
 	}
 }
 
 static void
+name_owner_changed_cb (GObject *object,
+                       GParamSpec *pspec,
+                       gpointer user_data)
+{
+	name_owner_changed (user_data);
+}
+
+static void
 pacrunner_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 {
 	NMPacrunnerManager *self = user_data;
@@ -289,7 +332,8 @@ pacrunner_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data)
 	nm_clear_g_cancellable (&priv->pacrunner_cancellable);
 
 	g_signal_connect (priv->pacrunner, "notify::g-name-owner",
-	                  G_CALLBACK (name_owner_changed), self);
+	                  G_CALLBACK (name_owner_changed_cb), self);
+	name_owner_changed (self);
 }
 
 /**
@@ -297,10 +341,18 @@ pacrunner_proxy_cb (GObject *source, GAsyncResult *res, gpointer user_data)
  * @self: the #NMPacrunnerManager
  * @iface: the iface for the connection or %NULL
  * @proxy_config: proxy config of the connection
- * @ip4_config: IP4 config of the connection
- * @ip6_config: IP6 config of the connection
+ * @ip4_config: IP4 config of the connection to extract domain info from
+ * @ip6_config: IP6 config of the connection to extract domain info from
+ *
+ * Returns: a #NMPacrunnerCallId call id. The function cannot
+ *  fail and always returns a non NULL pointer. The call-id may
+ *  be used to remove the configuration later via nm_pacrunner_manager_remove().
+ *  Note that the call-id does not keep the @self instance alive.
+ *  If you plan to remove the configuration later, you must keep
+ *  the instance alive long enough. You can remove the configuration
+ *  at most once using this call call-id.
  */
-void
+NMPacrunnerCallId *
 nm_pacrunner_manager_send (NMPacrunnerManager *self,
                            const char *iface,
                            NMProxyConfig *proxy_config,
@@ -311,10 +363,11 @@ nm_pacrunner_manager_send (NMPacrunnerManager *self,
 	NMProxyConfigMethod method;
 	NMPacrunnerManagerPrivate *priv;
 	GVariantBuilder proxy_data;
-	GVariant *pacrunner_manager_args;
+	GPtrArray *domains;
+	Config *config;
 
-	g_return_if_fail (NM_IS_PACRUNNER_MANAGER (self));
-	g_return_if_fail (proxy_config);
+	g_return_val_if_fail (NM_IS_PACRUNNER_MANAGER (self), NULL);
+	g_return_val_if_fail (proxy_config, NULL);
 
 	priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
 
@@ -343,80 +396,133 @@ nm_pacrunner_manager_send (NMPacrunnerManager *self,
 		                       g_variant_new_string ("direct"));
 	}
 
-	priv->domains = g_ptr_array_new_with_free_func (g_free);
 
 	/* Extract stuff from configs */
-	add_proxy_config (self, &proxy_data, proxy_config);
+	add_proxy_config (&proxy_data, proxy_config);
 
-	if (ip4_config)
-		add_ip4_config (self, &proxy_data, ip4_config);
-	if (ip6_config)
-		add_ip6_config (self, &proxy_data, ip6_config);
+	if (ip4_config || ip6_config) {
+		domains = g_ptr_array_new_with_free_func (g_free);
 
-	g_ptr_array_add (priv->domains, NULL);
-	strv = (char **) g_ptr_array_free (priv->domains, (priv->domains->len == 1));
+		if (ip4_config)
+			get_ip4_domains (domains, ip4_config);
+		if (ip6_config)
+			get_ip6_domains (domains, ip6_config);
 
-	if (strv) {
-		g_variant_builder_add (&proxy_data, "{sv}",
-		                       "Domains",
-		                       g_variant_new_strv ((const char *const *) strv, -1));
-		g_strfreev (strv);
+		g_ptr_array_add (domains, NULL);
+		strv = (char **) g_ptr_array_free (domains, (domains->len == 1));
+
+		if (strv) {
+			g_variant_builder_add (&proxy_data, "{sv}",
+			                       "Domains",
+			                       g_variant_new_strv ((const char *const *) strv, -1));
+			g_strfreev (strv);
+		}
 	}
 
-	pacrunner_manager_args = g_variant_ref_sink (g_variant_new ("(a{sv})", &proxy_data));
-	priv->args = g_list_append (priv->args, pacrunner_manager_args);
+	config = config_new (self, g_variant_new ("(a{sv})", &proxy_data));
+	priv->configs = g_list_append (priv->configs, config);
 
-	/* Send if pacrunner is available on Bus, otherwise
-	 * argument has already been appended above to be
+	{
+		gs_free char *args_str = NULL;
+
+		_LOG2D (config, "send: new config %s",
+		        (args_str = g_variant_print (config->args, FALSE)));
+	}
+
+	/* Send if pacrunner is available on bus, otherwise
+	 * config has already been appended above to be
 	 * sent when pacrunner appears.
 	 */
-	send_pacrunner_proxy_data (self, pacrunner_manager_args);
+	pacrunner_send_config (self, config);
+
+	return config;
 }
 
 static void
-pacrunner_remove_done (GObject *source, GAsyncResult *res, gpointer user_data)
+pacrunner_remove_done (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data)
 {
-	/* @self may be a dangling pointer. However, we don't use it as the
-	 * logging macro below does not dereference @self. */
-	NMPacrunnerManager *self = user_data;
+	Config *config = user_data;
+	NMPacrunnerManager *self;
 	gs_free_error GError *error = NULL;
 	gs_unref_variant GVariant *ret = NULL;
 
-	ret = g_dbus_proxy_call_finish ((GDBusProxy *) source, res, &error);
+	ret = g_dbus_proxy_call_finish (proxy, res, &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+		config_unref (config);
+		return;
+	}
+
+	self = NM_PACRUNNER_MANAGER (config->manager);
 
 	if (!ret)
-		_LOGD ("Couldn't remove proxy config from pacrunner: %s", error->message);
+		_LOG2D (config, "remove failed: %s", error->message);
 	else
-		_LOGD ("Successfully removed proxy config from pacrunner");
+		_LOG2D (config, "removed");
+
+	config_unref (config);
 }
 
 /**
  * nm_pacrunner_manager_remove:
  * @self: the #NMPacrunnerManager
- * @iface: the iface for the connection to be removed
- * from pacrunner
+ * @call_id: the call-id obtained from nm_pacrunner_manager_send()
  */
 void
-nm_pacrunner_manager_remove (NMPacrunnerManager *self, const char *iface)
+nm_pacrunner_manager_remove (NMPacrunnerManager *self, NMPacrunnerCallId *call_id)
 {
-	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+	NMPacrunnerManagerPrivate *priv;
+	Config *config;
 	GList *list;
 
-	for (list = g_list_first(priv->remove); list; list = g_list_next(list)) {
-		struct remove_data *data = list->data;
-		if (g_strcmp0 (data->iface, iface) == 0) {
-			if (priv->pacrunner && data->path)
-				g_dbus_proxy_call (priv->pacrunner,
-				                   "DestroyProxyConfiguration",
-				                   g_variant_new ("(o)", data->path),
-				                   G_DBUS_CALL_FLAGS_NONE,
-				                   -1,
-				                   NULL,
-				                  (GAsyncReadyCallback) pacrunner_remove_done,
-				                   self);
-			break;
+	g_return_if_fail (NM_IS_PACRUNNER_MANAGER (self));
+	g_return_if_fail (call_id);
+
+	config = call_id;
+	priv = NM_PACRUNNER_MANAGER_GET_PRIVATE (self);
+
+	_LOG2T (config, "removing...");
+
+	list = g_list_find (priv->configs, config);
+	if (!list)
+		g_return_if_reached ();
+
+	if (priv->pacrunner) {
+		if (!config->path) {
+			/* send() failed or is still pending. Mark the item as
+			 * removed, so that we ask pacrunner to drop it when the
+			 * send() completes.
+			 */
+			config->removed = TRUE;
+			config_unref (config);
+		} else {
+			g_dbus_proxy_call (priv->pacrunner,
+			                   "DestroyProxyConfiguration",
+			                   g_variant_new ("(o)", config->path),
+			                   G_DBUS_CALL_FLAGS_NO_AUTO_START,
+			                   -1,
+			                   priv->pacrunner_cancellable,
+			                   (GAsyncReadyCallback) pacrunner_remove_done,
+			                   config);
 		}
-	}
+	} else
+		config_unref (config);
+	priv->configs = g_list_delete_link (priv->configs, list);
+}
+
+gboolean
+nm_pacrunner_manager_remove_clear (NMPacrunnerManager *self,
+                                   NMPacrunnerCallId **p_call_id)
+{
+	g_return_val_if_fail (p_call_id, FALSE);
+
+	/* if we have no call-id, allow for %NULL */
+	g_return_val_if_fail ((!self && !*p_call_id) || NM_IS_PACRUNNER_MANAGER (self), FALSE);
+
+	if (!*p_call_id)
+		return FALSE;
+	nm_pacrunner_manager_remove (self,
+	                             g_steal_pointer (p_call_id));
+	return TRUE;
 }
 
 /*****************************************************************************/
@@ -445,16 +551,11 @@ dispose (GObject *object)
 	NMPacrunnerManagerPrivate *priv = NM_PACRUNNER_MANAGER_GET_PRIVATE ((NMPacrunnerManager *) object);
 
 	g_clear_pointer (&priv->iface, g_free);
-
 	nm_clear_g_cancellable (&priv->pacrunner_cancellable);
-
 	g_clear_object (&priv->pacrunner);
 
-	g_list_free_full (priv->args, (GDestroyNotify) g_variant_unref);
-	priv->args = NULL;
-
-	g_list_free_full (priv->remove, (GDestroyNotify) remove_data_destroy);
-	priv->remove = NULL;
+	g_list_free_full (priv->configs, (GDestroyNotify) config_unref);
+	priv->configs = NULL;
 
 	G_OBJECT_CLASS (nm_pacrunner_manager_parent_class)->dispose (object);
 }
diff --git a/src/nm-pacrunner-manager.h b/src/nm-pacrunner-manager.h
index 99e85115..3080c4f5 100644
--- a/src/nm-pacrunner-manager.h
+++ b/src/nm-pacrunner-manager.h
@@ -15,7 +15,8 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2016 Atul Anand <atulhjp@gmail.com>.
+ * Copyright 2016 Atul Anand <atulhjp@gmail.com>.
+ * Copyright 2016 - 2017 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_PACRUNNER_MANAGER_H__
@@ -30,16 +31,22 @@
 
 typedef struct _NMPacrunnerManagerClass NMPacrunnerManagerClass;
 
+typedef struct _NMPacrunnerCallId NMPacrunnerCallId;
+
 GType nm_pacrunner_manager_get_type (void);
 
 NMPacrunnerManager *nm_pacrunner_manager_get (void);
 
-void nm_pacrunner_manager_send (NMPacrunnerManager *self,
-                                const char *iface,
-                                NMProxyConfig *proxy_config,
-                                NMIP4Config *ip4_config,
-                                NMIP6Config *ip6_config);
+NMPacrunnerCallId *nm_pacrunner_manager_send (NMPacrunnerManager *self,
+                                              const char *iface,
+                                              NMProxyConfig *proxy_config,
+                                              NMIP4Config *ip4_config,
+                                              NMIP6Config *ip6_config);
+
+void nm_pacrunner_manager_remove (NMPacrunnerManager *self,
+                                  NMPacrunnerCallId *call_id);
 
-void nm_pacrunner_manager_remove (NMPacrunnerManager *self, const char *iface);
+gboolean nm_pacrunner_manager_remove_clear (NMPacrunnerManager *self,
+                                            NMPacrunnerCallId **p_call_id);
 
 #endif /* __NETWORKMANAGER_PACRUNNER_MANAGER_H__ */
diff --git a/src/nm-policy.c b/src/nm-policy.c
index a2ff2945..7c74a6b4 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -47,6 +47,8 @@
 #include "settings/nm-settings-connection.h"
 #include "nm-dhcp4-config.h"
 #include "nm-dhcp6-config.h"
+#include "nm-config.h"
+#include "nm-netns.h"
 
 /*****************************************************************************/
 
@@ -61,6 +63,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMPolicy,
 
 typedef struct {
 	NMManager *manager;
+	NMNetns *netns;
 	NMFirewallManager *firewall_manager;
 	GSList *pending_activation_checks;
 
@@ -68,8 +71,6 @@ typedef struct {
 
 	GSList *pending_secondaries;
 
-	gulong fw_started_id;
-
 	NMSettings *settings;
 
 	NMDevice *default_device4, *activating_device4;
@@ -85,9 +86,12 @@ typedef struct {
 
 	guint schedule_activate_all_id; /* idle handler for schedule_activate_all(). */
 
+	NMPolicyHostnameMode hostname_mode;
 	char *orig_hostname; /* hostname at NM start time */
 	char *cur_hostname;  /* hostname we want to assign */
-	gboolean hostname_changed;  /* TRUE if NM ever set the hostname */
+	char *last_hostname; /* last hostname NM set (to detect if someone else changed it in the meanwhile) */
+	gboolean changing_hostname; /* hostname set operation still in progress */
+	gboolean dhcp_hostname; /* current hostname was set from dhcp */
 
 	GArray *ip6_prefix_delegations; /* pool of ip6 prefixes delegated to all devices */
 } NMPolicyPrivate;
@@ -123,7 +127,7 @@ _PRIV_TO_SELF (NMPolicyPrivate *priv)
 #define _NMLOG_PREFIX_NAME    "policy"
 #define _NMLOG(level, domain, ...) \
     G_STMT_START { \
-        nm_log ((level), (domain), \
+        nm_log ((level), (domain), NULL, NULL, \
                 "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                 _NMLOG_PREFIX_NAME": " \
                 _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
@@ -369,7 +373,7 @@ get_best_ip4_device (NMPolicy *self, gboolean fully_activated)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 
-	return nm_default_route_manager_ip4_get_best_device (nm_default_route_manager_get (),
+	return nm_default_route_manager_ip4_get_best_device (nm_netns_get_default_route_manager (priv->netns),
 	                                                     nm_manager_get_devices (priv->manager),
 	                                                     fully_activated,
 	                                                     priv->default_device4);
@@ -380,12 +384,32 @@ get_best_ip6_device (NMPolicy *self, gboolean fully_activated)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 
-	return nm_default_route_manager_ip6_get_best_device (nm_default_route_manager_get (),
+	return nm_default_route_manager_ip6_get_best_device (nm_netns_get_default_route_manager (priv->netns),
 	                                                     nm_manager_get_devices (priv->manager),
 	                                                     fully_activated,
 	                                                     priv->default_device6);
 }
 
+static gboolean
+all_devices_not_active (NMPolicy *self)
+{
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
+	const GSList *iter = nm_manager_get_devices (priv->manager);
+
+	while (iter != NULL) {
+		NMDeviceState state;
+
+		state = nm_device_get_state (NM_DEVICE (iter->data));
+		if (   state <= NM_DEVICE_STATE_DISCONNECTED
+		    || state >= NM_DEVICE_STATE_DEACTIVATING) {
+			iter = g_slist_next (iter);
+			continue;
+		}
+		return FALSE;
+	}
+	return TRUE;
+}
+
 #define FALLBACK_HOSTNAME4 "localhost.localdomain"
 
 static void
@@ -393,22 +417,76 @@ settings_set_hostname_cb (const char *hostname,
                           gboolean result,
                           gpointer user_data)
 {
+	NMPolicy *self = NM_POLICY (user_data);
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	int ret = 0;
 
 	if (!result) {
+		_LOGT (LOGD_DNS, "set-hostname: hostname set via dbus failed, fallback to \"sethostname\"");
 		ret = sethostname (hostname, strlen (hostname));
 		if (ret != 0) {
 			int errsv = errno;
 
-			_LOGW (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s",
+			_LOGW (LOGD_DNS, "set-hostname: couldn't set the system hostname to '%s': (%d) %s",
 			       hostname, errsv, strerror (errsv));
 			if (errsv == EPERM)
-				_LOGW (LOGD_DNS, "you should use hostnamed when systemd hardening is in effect!");
+				_LOGW (LOGD_DNS, "set-hostname: you should use hostnamed when systemd hardening is in effect!");
 		}
 	}
 
+	priv->changing_hostname = FALSE;
 	if (!ret)
-		nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL, NULL, NULL);
+		nm_dispatcher_call_hostname (NULL, NULL, NULL);
+	g_object_unref (self);
+}
+
+#define HOST_NAME_BUFSIZE (HOST_NAME_MAX + 2)
+
+static char *
+_get_hostname (NMPolicy *self, char **hostname)
+{
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
+	char *buf;
+
+	g_assert (hostname && *hostname == NULL);
+
+	/* If there is an in-progress hostname change, return
+	 * the last hostname set as would be set soon...
+	 */
+	if (priv->changing_hostname) {
+		_LOGT (LOGD_DNS, "get-hostname: \"%s\" (last on set)", priv->last_hostname);
+		*hostname = g_strdup (priv->last_hostname);
+		return *hostname;
+	}
+
+	/* try to get the hostname via dbus... */
+	if (nm_settings_get_transient_hostname (priv->settings, hostname)) {
+		_LOGT (LOGD_DNS, "get-hostname: \"%s\" (from dbus)", *hostname);
+		return *hostname;
+	}
+
+	/* ...or retrieve it by yourself */
+	buf = g_malloc (HOST_NAME_BUFSIZE);
+	if (gethostname (buf, HOST_NAME_BUFSIZE -1) != 0) {
+		int errsv = errno;
+
+		_LOGT (LOGD_DNS, "get-hostname: couldn't get the system hostname: (%d) %s",
+		       errsv, g_strerror (errsv));
+		g_free (buf);
+		return NULL;
+	}
+
+	/* the name may be truncated... */
+	buf[HOST_NAME_BUFSIZE - 1] = '\0';
+	if (strlen (buf) >= HOST_NAME_BUFSIZE -1) {
+		_LOGT (LOGD_DNS, "get-hostname: system hostname too long: \"%s\"", buf);
+		g_free (buf);
+		return NULL;
+	}
+
+	_LOGT (LOGD_DNS, "get-hostname: \"%s\"", buf);
+	*hostname = buf;
+	return *hostname;
 }
 
 static void
@@ -417,9 +495,8 @@ _set_hostname (NMPolicy *self,
                const char *msg)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	char old_hostname[HOST_NAME_MAX + 1];
+	gs_free char *old_hostname = NULL;
 	const char *name;
-	int ret;
 
 	/* The incoming hostname *can* be NULL, which will get translated to
 	 * 'localhost.localdomain' or such in the hostname policy code, but we
@@ -433,25 +510,18 @@ _set_hostname (NMPolicy *self,
 	if (new_hostname)
 		g_clear_object (&priv->lookup_addr);
 
-	if (   priv->orig_hostname
-	    && (priv->hostname_changed == FALSE)
-	    && g_strcmp0 (priv->orig_hostname, new_hostname) == 0) {
-		/* Don't change the hostname or update DNS this is the first time we're
-		 * trying to change the hostname, and it's not actually changing.
-		 */
-	} else if (g_strcmp0 (priv->cur_hostname, new_hostname) == 0) {
-		/* Don't change the hostname or update DNS if the hostname isn't actually
-		 * going to change.
-		 */
-	} else {
+	/* Update the DNS only if the hostname is actually
+	 * going to change.
+	 */
+	if (!nm_streq0 (priv->cur_hostname, new_hostname)) {
 		g_free (priv->cur_hostname);
 		priv->cur_hostname = g_strdup (new_hostname);
-		priv->hostname_changed = TRUE;
 
 		/* Notify the DNS manager of the hostname change so that the domain part, if
 		 * present, can be added to the search list.
 		 */
-		nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname);
+		nm_dns_manager_set_hostname (priv->dns_manager, priv->cur_hostname,
+		                             all_devices_not_active (self));
 	}
 
 	 /* Finally, set kernel hostname */
@@ -463,26 +533,26 @@ _set_hostname (NMPolicy *self,
 	} else
 		name = new_hostname;
 
-	old_hostname[HOST_NAME_MAX] = '\0';
-	errno = 0;
-	ret = gethostname (old_hostname, HOST_NAME_MAX);
-	if (ret != 0) {
-		_LOGW (LOGD_DNS, "couldn't get the system hostname: (%d) %s",
-		       errno, strerror (errno));
-	} else {
-		/* Don't set the hostname if it isn't actually changing */
-		if (nm_streq (name, old_hostname))
-			return;
+	/* Don't set the hostname if it isn't actually changing */
+	if (   _get_hostname (self, &old_hostname)
+	    && (nm_streq (name, old_hostname))) {
+		_LOGT (LOGD_DNS, "set-hostname: hostname already set to '%s' (%s)", name, msg);
+		return;
 	}
 
-	_LOGI (LOGD_DNS, "setting system hostname to '%s' (%s)", name, msg);
+	/* Keep track of the last set hostname */
+	g_free (priv->last_hostname);
+	priv->last_hostname = g_strdup (name);
+	priv->changing_hostname = TRUE;
+
+	_LOGI (LOGD_DNS, "set-hostname: set hostname to '%s' (%s)", name, msg);
 
 	/* Ask NMSettings to update the transient hostname using its
 	 * systemd-hostnamed proxy */
 	nm_settings_set_transient_hostname (priv->settings,
 	                                    name,
 	                                    settings_set_hostname_cb,
-	                                    NULL);
+	                                    g_object_ref (self));
 }
 
 static void
@@ -490,49 +560,76 @@ lookup_callback (GObject *source,
                  GAsyncResult *result,
                  gpointer user_data)
 {
-	NMPolicy *self = (NMPolicy *) user_data;
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	const char *hostname;
-	GError *error = NULL;
+	NMPolicy *self;
+	NMPolicyPrivate *priv;
+	gs_free char *hostname = NULL;
+	gs_free_error GError *error = NULL;
 
 	hostname = g_resolver_lookup_by_address_finish (G_RESOLVER (source), result, &error);
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-		/* Don't touch policy; it may have been freed already */
-		g_error_free (error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
-	}
+
+	self = user_data;
+	priv = NM_POLICY_GET_PRIVATE (self);
+
+	g_clear_object (&priv->lookup_cancellable);
 
 	if (hostname)
 		_set_hostname (self, hostname, "from address lookup");
-	else {
+	else
 		_set_hostname (self, NULL, error->message);
-		g_error_free (error);
-	}
-
-	g_clear_object (&priv->lookup_cancellable);
 }
 
 static void
-update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6)
+update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6, const char *msg)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	char *configured_hostname = NULL;
+	gs_free char *temp_hostname = NULL;
 	const char *dhcp_hostname, *p;
 	NMIP4Config *ip4_config;
 	NMIP6Config *ip6_config;
+	gboolean external_hostname = FALSE;
 
 	g_return_if_fail (self != NULL);
 
-	if (priv->lookup_cancellable) {
-		g_cancellable_cancel (priv->lookup_cancellable);
-		g_clear_object (&priv->lookup_cancellable);
+	if (priv->hostname_mode == NM_POLICY_HOSTNAME_MODE_NONE) {
+		_LOGT (LOGD_DNS, "set-hostname: hostname is unmanaged");
+		return;
+	}
+
+	_LOGT (LOGD_DNS, "set-hostname: updating hostname (%s)", msg);
+
+	nm_clear_g_cancellable (&priv->lookup_cancellable);
+
+	/* Check if the hostname was set externally to NM, so that in that case
+	 * we can avoid to fallback to the one we got when we started.
+	 * Consider "not specific" hostnames as equal. */
+	if (   _get_hostname (self, &temp_hostname)
+	    && !nm_streq0 (temp_hostname, priv->last_hostname)
+	    && (   nm_utils_is_specific_hostname (temp_hostname)
+	        || nm_utils_is_specific_hostname (priv->last_hostname))) {
+		external_hostname = TRUE;
+		_LOGI (LOGD_DNS, "set-hostname: current hostname was changed outside NetworkManager: '%s'",
+		       temp_hostname);
+		priv->dhcp_hostname = FALSE;
+
+		if (!nm_streq0 (temp_hostname, priv->orig_hostname)) {
+			/* Update original (fallback) hostname */
+			g_free (priv->orig_hostname);
+			if (nm_utils_is_specific_hostname (temp_hostname)) {
+				priv->orig_hostname = temp_hostname;
+				temp_hostname = NULL;
+			} else
+				priv->orig_hostname = NULL;
+		}
 	}
 
 	/* Hostname precedence order:
 	 *
 	 * 1) a configured hostname (from settings)
 	 * 2) automatic hostname from the default device's config (DHCP, VPN, etc)
-	 * 3) the original hostname when NM started
+	 * 3) the last hostname set outside NM
 	 * 4) reverse-DNS of the best device's IPv4 address
 	 *
 	 */
@@ -541,6 +638,7 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6)
 	g_object_get (G_OBJECT (priv->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL);
 	if (configured_hostname && nm_utils_is_specific_hostname (configured_hostname)) {
 		_set_hostname (self, configured_hostname, "from system configuration");
+		priv->dhcp_hostname = FALSE;
 		g_free (configured_hostname);
 		return;
 	}
@@ -552,14 +650,6 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6)
 	if (!best6)
 		best6 = get_best_ip6_device (self, TRUE);
 
-	if (!best4 && !best6) {
-		/* No best device; fall back to original hostname or if there wasn't
-		 * one, 'localhost.localdomain'
-		 */
-		_set_hostname (self, priv->orig_hostname, "no default device");
-		return;
-	}
-
 	if (best4) {
 		NMDhcp4Config *dhcp4_config;
 
@@ -572,10 +662,11 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6)
 				while (*p) {
 					if (!g_ascii_isspace (*p++)) {
 						_set_hostname (self, p-1, "from DHCPv4");
+						priv->dhcp_hostname = TRUE;
 						return;
 					}
 				}
-				_LOGW (LOGD_DNS, "DHCPv4-provided hostname '%s' looks invalid; ignoring it",
+				_LOGW (LOGD_DNS, "set-hostname: DHCPv4-provided hostname '%s' looks invalid; ignoring it",
 				       dhcp_hostname);
 			}
 		}
@@ -591,17 +682,45 @@ update_system_hostname (NMPolicy *self, NMDevice *best4, NMDevice *best6)
 				while (*p) {
 					if (!g_ascii_isspace (*p++)) {
 						_set_hostname (self, p-1, "from DHCPv6");
+						priv->dhcp_hostname = TRUE;
 						return;
 					}
 				}
-				_LOGW (LOGD_DNS, "DHCPv6-provided hostname '%s' looks invalid; ignoring it",
+				_LOGW (LOGD_DNS, "set-hostname: DHCPv6-provided hostname '%s' looks invalid; ignoring it",
 				       dhcp_hostname);
 			}
 		}
 	}
 
-	/* If no automatically-configured hostname, try using the hostname from
-	 * when NM started up.
+	/* If an hostname was set outside NetworkManager keep it */
+	if (external_hostname)
+		return;
+
+	if (priv->hostname_mode == NM_POLICY_HOSTNAME_MODE_DHCP) {
+		/* In dhcp hostname-mode, the hostname is updated only if it comes from
+		 * a DHCP host-name option: if last set was from a host-name option and
+		 * we are here than that connection is gone (with its host-name option),
+		 * so reset the hostname to the previous value
+		 */
+		if (priv->dhcp_hostname) {
+			_set_hostname (self, priv->orig_hostname, "reset dhcp hostname");
+			priv->dhcp_hostname = FALSE;
+		}
+		return;
+	}
+
+	priv->dhcp_hostname = FALSE;
+
+	if (!best4 && !best6) {
+		/* No best device; fall back to the last hostname set externally
+		 * to NM or if there wasn't one, 'localhost.localdomain'
+		 */
+		_set_hostname (self, priv->orig_hostname, "no default device");
+		return;
+	}
+
+	/* If no automatically-configured hostname, try using the last hostname
+	 * set externally to NM
 	 */
 	if (priv->orig_hostname) {
 		_set_hostname (self, priv->orig_hostname, "from system startup");
@@ -672,7 +791,7 @@ get_best_ip4_config (NMPolicy *self,
                      NMDevice **out_device,
                      NMVpnConnection **out_vpn)
 {
-	return nm_default_route_manager_ip4_get_best_config (nm_default_route_manager_get (),
+	return nm_default_route_manager_ip4_get_best_config (nm_netns_get_default_route_manager (NM_POLICY_GET_PRIVATE (self)->netns),
 	                                                     ignore_never_default,
 	                                                     out_ip_iface,
 	                                                     out_ac,
@@ -767,7 +886,7 @@ get_best_ip6_config (NMPolicy *self,
                      NMDevice **out_device,
                      NMVpnConnection **out_vpn)
 {
-	return nm_default_route_manager_ip6_get_best_config (nm_default_route_manager_get (),
+	return nm_default_route_manager_ip6_get_best_config (nm_netns_get_default_route_manager (NM_POLICY_GET_PRIVATE (self)->netns),
 	                                                     ignore_never_default,
 	                                                     out_ip_iface,
 	                                                     out_ac,
@@ -903,7 +1022,7 @@ update_routing_and_dns (NMPolicy *self, gboolean force_update)
 	update_ip6_routing (self, force_update);
 
 	/* Update the system hostname */
-	update_system_hostname (self, priv->default_device4, priv->default_device6);
+	update_system_hostname (self, priv->default_device4, priv->default_device6, "routing and dns");
 
 	nm_dns_manager_end_updates (priv->dns_manager, __func__);
 }
@@ -960,9 +1079,8 @@ auto_activate_device (NMPolicy *self,
 	NMPolicyPrivate *priv;
 	NMSettingsConnection *best_connection;
 	gs_free char *specific_object = NULL;
-	GPtrArray *connections;
-	GSList *connection_list;
-	guint i;
+	gs_free NMSettingsConnection **connections = NULL;
+	guint i, len;
 
 	nm_assert (NM_IS_POLICY (self));
 	nm_assert (NM_IS_DEVICE (device));
@@ -976,21 +1094,14 @@ auto_activate_device (NMPolicy *self,
 	if (nm_device_get_act_request (device))
 		return;
 
-	connection_list = nm_manager_get_activatable_connections (priv->manager);
-	if (!connection_list)
+	connections = nm_manager_get_activatable_connections (priv->manager, &len, TRUE);
+	if (!connections[0])
 		return;
 
-	connections = _nm_utils_copy_slist_to_array (connection_list, NULL, NULL);
-	g_slist_free (connection_list);
-
-	/* sort is stable (which is important at this point) so that connections
-	 * with same priority are still sorted by last-connected-timestamp. */
-	g_ptr_array_sort (connections, (GCompareFunc) nm_utils_cmp_connection_by_autoconnect_priority);
-
 	/* Find the first connection that should be auto-activated */
 	best_connection = NULL;
-	for (i = 0; i < connections->len; i++) {
-		NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (connections->pdata[i]);
+	for (i = 0; i < len; i++) {
+		NMSettingsConnection *candidate = NM_SETTINGS_CONNECTION (connections[i]);
 
 		if (!nm_settings_connection_can_autoconnect (candidate))
 			continue;
@@ -999,7 +1110,6 @@ auto_activate_device (NMPolicy *self,
 			break;
 		}
 	}
-	g_ptr_array_free (connections, TRUE);
 
 	if (best_connection) {
 		GError *error = NULL;
@@ -1014,6 +1124,7 @@ auto_activate_device (NMPolicy *self,
 		                                     specific_object,
 		                                     device,
 		                                     subject,
+		                                     NM_ACTIVATION_TYPE_MANAGED,
 		                                     &error)) {
 			_LOGI (LOGD_DEVICE, "connection '%s' auto-activation failed: (%d) %s",
 			       nm_settings_connection_get_id (best_connection),
@@ -1142,14 +1253,15 @@ hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
 	NMPolicyPrivate *priv = user_data;
 	NMPolicy *self = _PRIV_TO_SELF (priv);
 
-	update_system_hostname (self, NULL, NULL);
+	update_system_hostname (self, NULL, NULL, "hostname changed");
 }
 
 static void
 reset_autoconnect_all (NMPolicy *self, NMDevice *device)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	GSList *connections, *iter;
+	gs_free NMSettingsConnection **connections = NULL;
+	guint i;
 
 	if (device) {
 		_LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections on %s",
@@ -1157,41 +1269,43 @@ reset_autoconnect_all (NMPolicy *self, NMDevice *device)
 	} else
 		_LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections");
 
-	connections = nm_settings_get_connections_sorted (priv->settings);
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		if (!device || nm_device_check_connection_compatible (device, iter->data)) {
-			nm_settings_connection_reset_autoconnect_retries (iter->data);
-			nm_settings_connection_set_autoconnect_blocked_reason (iter->data, NM_DEVICE_STATE_REASON_NONE);
+	connections = nm_settings_get_connections_sorted (priv->settings, NULL);
+	for (i = 0; connections[i]; i++) {
+		NMSettingsConnection *connection = connections[i];
+
+		if (!device || nm_device_check_connection_compatible (device, NM_CONNECTION (connection))) {
+			nm_settings_connection_reset_autoconnect_retries (connection);
+			nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED);
 		}
 	}
-	g_slist_free (connections);
 }
 
 static void
 reset_autoconnect_for_failed_secrets (NMPolicy *self)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	GSList *connections, *iter;
+	gs_free NMSettingsConnection **connections = NULL;
+	guint i;
 
 	_LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections with failed secrets");
 
-	connections = nm_settings_get_connections_sorted (priv->settings);
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMSettingsConnection *connection = NM_SETTINGS_CONNECTION (iter->data);
+	connections = nm_settings_get_connections_sorted (priv->settings, NULL);
+	for (i = 0; connections[i]; i++) {
+		NMSettingsConnection *connection = connections[i];
 
-		if (nm_settings_connection_get_autoconnect_blocked_reason (connection) == NM_DEVICE_STATE_REASON_NO_SECRETS) {
+		if (nm_settings_connection_get_autoconnect_blocked_reason (connection) == NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS) {
 			nm_settings_connection_reset_autoconnect_retries (connection);
-			nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_DEVICE_STATE_REASON_NONE);
+			nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED);
 		}
 	}
-	g_slist_free (connections);
 }
 
 static void
 block_autoconnect_for_device (NMPolicy *self, NMDevice *device)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	GSList *connections, *iter;
+	gs_free NMSettingsConnection **connections = NULL;
+	guint i;
 
 	_LOGD (LOGD_DEVICE, "blocking autoconnect for all connections on %s",
 	       nm_device_get_iface (device));
@@ -1203,14 +1317,15 @@ block_autoconnect_for_device (NMPolicy *self, NMDevice *device)
 	if (!nm_device_is_software (device))
 		return;
 
-	connections = nm_settings_get_connections_sorted (priv->settings);
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		if (nm_device_check_connection_compatible (device, iter->data)) {
-			nm_settings_connection_set_autoconnect_blocked_reason (NM_SETTINGS_CONNECTION (iter->data),
-			                                                       NM_DEVICE_STATE_REASON_USER_REQUESTED);
+	connections = nm_settings_get_connections_sorted (priv->settings, NULL);
+	for (i = 0; connections[i]; i++) {
+		NMSettingsConnection *connection = connections[i];
+
+		if (nm_device_check_connection_compatible (device, NM_CONNECTION (connection))) {
+			nm_settings_connection_set_autoconnect_blocked_reason (connection,
+			                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED);
 		}
 	}
-	g_slist_free (connections);
 }
 
 static void
@@ -1278,7 +1393,8 @@ reset_connections_retries (gpointer user_data)
 {
 	NMPolicy *self = (NMPolicy *) user_data;
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	GSList *connections, *iter;
+	gs_free NMSettingsConnection **connections = NULL;
+	guint i;
 	gint32 con_stamp, min_stamp, now;
 	gboolean changed = FALSE;
 
@@ -1286,9 +1402,9 @@ reset_connections_retries (gpointer user_data)
 
 	min_stamp = 0;
 	now = nm_utils_get_monotonic_timestamp_s ();
-	connections = nm_settings_get_connections_sorted (priv->settings);
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMSettingsConnection *connection = NM_SETTINGS_CONNECTION (iter->data);
+	connections = nm_settings_get_connections_sorted (priv->settings, NULL);
+	for (i = 0; connections[i]; i++) {
+		NMSettingsConnection *connection = connections[i];
 
 		con_stamp = nm_settings_connection_get_autoconnect_retry_time (connection);
 		if (con_stamp == 0)
@@ -1300,7 +1416,6 @@ reset_connections_retries (gpointer user_data)
 		} else if (min_stamp == 0 || min_stamp > con_stamp)
 			min_stamp = con_stamp;
 	}
-	g_slist_free (connections);
 
 	/* Schedule the handler again if there are some stamps left */
 	if (min_stamp != 0)
@@ -1318,8 +1433,7 @@ activate_slave_connections (NMPolicy *self, NMDevice *device)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const char *master_device, *master_uuid_settings = NULL, *master_uuid_applied = NULL;
-	gs_free_slist GSList *connections = NULL;
-	GSList *iter;
+	guint i;
 	NMActRequest *req;
 	gboolean internal_activation = FALSE;
 
@@ -1345,27 +1459,29 @@ activate_slave_connections (NMPolicy *self, NMDevice *device)
 		internal_activation = subject && nm_auth_subject_is_internal (subject);
 	}
 
-	if (!internal_activation)
-		connections = nm_settings_get_connections_sorted (priv->settings);
+	if (!internal_activation) {
+		gs_free NMSettingsConnection **connections = NULL;
 
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMConnection *slave;
-		NMSettingConnection *s_slave_con;
-		const char *slave_master;
+		connections = nm_settings_get_connections_sorted (priv->settings, NULL);
 
-		slave = NM_CONNECTION (iter->data);
-		g_assert (slave);
+		for (i = 0; connections[i]; i++) {
+			NMConnection *slave;
+			NMSettingConnection *s_slave_con;
+			const char *slave_master;
 
-		s_slave_con = nm_connection_get_setting_connection (slave);
-		g_assert (s_slave_con);
-		slave_master = nm_setting_connection_get_master (s_slave_con);
-		if (!slave_master)
-			continue;
+			slave = NM_CONNECTION (connections[i]);
+
+			s_slave_con = nm_connection_get_setting_connection (slave);
+			g_assert (s_slave_con);
+			slave_master = nm_setting_connection_get_master (s_slave_con);
+			if (!slave_master)
+				continue;
 
-		if (   !g_strcmp0 (slave_master, master_device)
-		    || !g_strcmp0 (slave_master, master_uuid_applied)
-		    || !g_strcmp0 (slave_master, master_uuid_settings))
-			nm_settings_connection_reset_autoconnect_retries (NM_SETTINGS_CONNECTION (slave));
+			if (   !g_strcmp0 (slave_master, master_device)
+			    || !g_strcmp0 (slave_master, master_uuid_applied)
+			    || !g_strcmp0 (slave_master, master_uuid_settings))
+				nm_settings_connection_reset_autoconnect_retries (NM_SETTINGS_CONNECTION (slave));
+		}
 	}
 
 	schedule_activate_all (self);
@@ -1419,6 +1535,7 @@ activate_secondary_connections (NMPolicy *self,
 		                                     nm_exported_object_get_path (NM_EXPORTED_OBJECT (req)),
 		                                     device,
 		                                     nm_active_connection_get_subject (NM_ACTIVE_CONNECTION (req)),
+		                                     NM_ACTIVATION_TYPE_MANAGED,
 		                                     &error);
 		if (ac)
 			secondary_ac_list = g_slist_append (secondary_ac_list, g_object_ref (ac));
@@ -1469,11 +1586,11 @@ device_state_changed (NMDevice *device,
 		    && old_state <= NM_DEVICE_STATE_ACTIVATED) {
 			int tries = nm_settings_connection_get_autoconnect_retries (connection);
 
-			if (reason == NM_DEVICE_STATE_REASON_NO_SECRETS) {
+			if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_NO_SECRETS) {
 				_LOGD (LOGD_DEVICE, "connection '%s' now blocked from autoconnect due to no secrets",
 				       nm_settings_connection_get_id (connection));
 
-				nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_DEVICE_STATE_REASON_NO_SECRETS);
+				nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS);
 			} else if (tries != 0) {
 				_LOGD (LOGD_DEVICE, "connection '%s' failed to autoconnect; %d tries left",
 				       nm_settings_connection_get_id (connection), tries);
@@ -1528,7 +1645,7 @@ device_state_changed (NMDevice *device,
 			update_routing_and_dns (self, FALSE);
 		break;
 	case NM_DEVICE_STATE_DEACTIVATING:
-		if (reason == NM_DEVICE_STATE_REASON_USER_REQUESTED) {
+		if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_USER_REQUESTED) {
 			if (!nm_device_get_autoconnect (device)) {
 				/* The device was disconnected; block all connections on it */
 				block_autoconnect_for_device (self, device);
@@ -1538,7 +1655,7 @@ device_state_changed (NMDevice *device,
 					_LOGD (LOGD_DEVICE, "blocking autoconnect of connection '%s' by user request",
 					       nm_settings_connection_get_id (connection));
 					nm_settings_connection_set_autoconnect_blocked_reason (connection,
-					                                                       NM_DEVICE_STATE_REASON_USER_REQUESTED);
+					                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED);
 				}
 			}
 		}
@@ -1548,7 +1665,8 @@ device_state_changed (NMDevice *device,
 		/* Reset retry counts for a device's connections when carrier on; if cable
 		 * was unplugged and plugged in again, we should try to reconnect.
 		 */
-		if (reason == NM_DEVICE_STATE_REASON_CARRIER && old_state == NM_DEVICE_STATE_UNAVAILABLE)
+		if (   nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_CARRIER
+		    && old_state == NM_DEVICE_STATE_UNAVAILABLE)
 			reset_autoconnect_all (self, device);
 
 		if (old_state > NM_DEVICE_STATE_DISCONNECTED)
@@ -1566,7 +1684,7 @@ device_state_changed (NMDevice *device,
 	case NM_DEVICE_STATE_IP_CONFIG:
 		/* We must have secrets if we got here. */
 		if (connection)
-			nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_DEVICE_STATE_REASON_NONE);
+			nm_settings_connection_set_autoconnect_blocked_reason (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED);
 		break;
 	case NM_DEVICE_STATE_SECONDARIES:
 		if (connection)
@@ -1603,8 +1721,9 @@ device_ip4_config_changed (NMDevice *device,
 
 	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
 
-	/* Ignore IP config changes while the device is activating, because we'll
-	 * catch all the changes when the device moves to ACTIVATED state.
+	/* We catch already all the IP events registering on the device state changes but
+	 * the ones where the IP changes but the device state keep stable (i.e., activated):
+	 * ignore IP config changes but when the device is in activated state.
 	 * Prevents unecessary changes to DNS information.
 	 */
 	if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) {
@@ -1616,7 +1735,7 @@ device_ip4_config_changed (NMDevice *device,
 		}
 		update_ip4_dns (self, priv->dns_manager);
 		update_ip4_routing (self, TRUE);
-		update_system_hostname (self, priv->default_device4, priv->default_device6);
+		update_system_hostname (self, priv->default_device4, priv->default_device6, "ip4 conf");
 	} else {
 		/* Old configs get removed immediately */
 		if (old_config)
@@ -1638,11 +1757,12 @@ device_ip6_config_changed (NMDevice *device,
 
 	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
 
-	/* Ignore IP config changes while the device is activating, because we'll
-	 * catch all the changes when the device moves to ACTIVATED state.
+	/* We catch already all the IP events registering on the device state changes but
+	 * the ones where the IP changes but the device state keep stable (i.e., activated):
+	 * ignore IP config changes but when the device is in activated state.
 	 * Prevents unecessary changes to DNS information.
 	 */
-	if (!nm_device_is_activating (device)) {
+	if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) {
 		if (old_config != new_config) {
 			if (old_config)
 				nm_dns_manager_remove_ip6_config (priv->dns_manager, old_config);
@@ -1651,7 +1771,7 @@ device_ip6_config_changed (NMDevice *device,
 		}
 		update_ip6_dns (self, priv->dns_manager);
 		update_ip6_routing (self, TRUE);
-		update_system_hostname (self, priv->default_device4, priv->default_device6);
+		update_system_hostname (self, priv->default_device4, priv->default_device6, "ip6 conf");
 	} else {
 		/* Old configs get removed immediately */
 		if (old_config)
@@ -1803,7 +1923,7 @@ static void
 vpn_connection_state_changed (NMVpnConnection *vpn,
                               NMVpnConnectionState new_state,
                               NMVpnConnectionState old_state,
-                              NMVpnConnectionStateReason reason,
+                              NMActiveConnectionStateReason reason,
                               NMPolicy *self)
 {
 	if (new_state == NM_VPN_CONNECTION_STATE_ACTIVATED)
@@ -1831,6 +1951,7 @@ vpn_connection_retry_after_failure (NMVpnConnection *vpn, NMPolicy *self)
 	                                     NULL,
 	                                     NULL,
 	                                     nm_active_connection_get_subject (ac),
+	                                     NM_ACTIVATION_TYPE_MANAGED,
 	                                     &error)) {
 		_LOGW (LOGD_DEVICE, "VPN '%s' reconnect failed: %s",
 		       nm_settings_connection_get_id (connection),
@@ -1933,13 +2054,24 @@ connection_added (NMSettings *settings,
 }
 
 static void
-firewall_started (NMFirewallManager *manager,
-                  gpointer user_data)
+firewall_state_changed (NMFirewallManager *manager,
+                        gboolean initialized_now,
+                        gpointer user_data)
 {
 	NMPolicy *self = (NMPolicy *) user_data;
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const GSList *iter;
 
+	if (initialized_now) {
+		/* the firewall manager was initializing, but all requests
+		 * so fare were queued and are already sent. No need to
+		 * re-update the firewall zone of the devices. */
+		return;
+	}
+
+	if (!nm_firewall_manager_get_running (manager))
+		return;
+
 	/* add interface of each device to correct zone */
 	for (iter = nm_manager_get_devices (priv->manager); iter; iter = g_slist_next (iter))
 		nm_device_update_firewall_zone (iter->data);
@@ -1956,15 +2088,20 @@ dns_config_changed (NMDnsManager *dns_manager, gpointer user_data)
 	 * (race in updating DNS and doing the reverse lookup).
 	 */
 
-	/* Stop a lookup thread if any. */
-	if (priv->lookup_cancellable) {
-		g_cancellable_cancel (priv->lookup_cancellable);
-		g_clear_object (&priv->lookup_cancellable);
-	}
+	nm_clear_g_cancellable (&priv->lookup_cancellable);
 
 	/* Re-start the hostname lookup thread if we don't have hostname yet. */
 	if (priv->lookup_addr) {
 		char *str = NULL;
+		gs_free char *hostname = NULL;
+
+		/* Check if the hostname was externally set */
+		if (   _get_hostname (self, &hostname)
+		    && nm_utils_is_specific_hostname (hostname)
+		    && !nm_streq0 (hostname, priv->last_hostname)) {
+			g_clear_object (&priv->lookup_addr);
+			return;
+		}
 
 		_LOGD (LOGD_DNS, "restarting reverse-lookup thread for address %s",
 		       (str = g_inet_address_to_string (priv->lookup_addr)));
@@ -2159,7 +2296,22 @@ static void
 nm_policy_init (NMPolicy *self)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-
+	const char *hostname_mode;
+
+	priv->netns = g_object_ref (nm_netns_get ());
+
+	hostname_mode = nm_config_data_get_value (NM_CONFIG_GET_DATA_ORIG,
+	                                          NM_CONFIG_KEYFILE_GROUP_MAIN,
+	                                          NM_CONFIG_KEYFILE_KEY_MAIN_HOSTNAME_MODE,
+	                                          NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
+	if (nm_streq0 (hostname_mode, "none"))
+		priv->hostname_mode = NM_POLICY_HOSTNAME_MODE_NONE;
+	else if (nm_streq0 (hostname_mode, "dhcp"))
+		priv->hostname_mode = NM_POLICY_HOSTNAME_MODE_DHCP;
+	else /* default - full mode */
+		priv->hostname_mode = NM_POLICY_HOSTNAME_MODE_FULL;
+
+	_LOGI (LOGD_DNS, "hostname management mode: %s", hostname_mode ? hostname_mode : "default");
 	priv->devices = g_hash_table_new (NULL, NULL);
 	priv->ip6_prefix_delegations = g_array_new (FALSE, FALSE, sizeof (IP6PrefixDelegation));
 	g_array_set_clear_func (priv->ip6_prefix_delegations, clear_ip6_prefix_delegation);
@@ -2170,20 +2322,21 @@ constructed (GObject *object)
 {
 	NMPolicy *self = NM_POLICY (object);
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	char hostname[HOST_NAME_MAX + 2];
+	char *hostname = NULL;
 
 	/* Grab hostname on startup and use that if nothing provides one */
-	memset (hostname, 0, sizeof (hostname));
-	if (gethostname (&hostname[0], HOST_NAME_MAX) == 0) {
+	if (_get_hostname (self, &hostname)) {
+		/* init last_hostname */
+		priv->last_hostname = hostname;
+
 		/* only cache it if it's a valid hostname */
-		if (*hostname && nm_utils_is_specific_hostname (hostname))
+		if (nm_utils_is_specific_hostname (hostname))
 			priv->orig_hostname = g_strdup (hostname);
 	}
 
 	priv->firewall_manager = g_object_ref (nm_firewall_manager_get ());
-
-	priv->fw_started_id = g_signal_connect (priv->firewall_manager, NM_FIREWALL_MANAGER_STARTED,
-	                                        G_CALLBACK (firewall_started), self);
+	g_signal_connect (priv->firewall_manager, NM_FIREWALL_MANAGER_STATE_CHANGED,
+	                  G_CALLBACK (firewall_state_changed), self);
 
 	priv->dns_manager = g_object_ref (nm_dns_manager_get ());
 	nm_dns_manager_set_initial_hostname (priv->dns_manager, priv->orig_hostname);
@@ -2242,8 +2395,7 @@ dispose (GObject *object)
 	priv->pending_secondaries = NULL;
 
 	if (priv->firewall_manager) {
-		g_assert (priv->fw_started_id);
-		nm_clear_g_signal_handler (priv->firewall_manager, &priv->fw_started_id);
+		g_signal_handlers_disconnect_by_func (priv->firewall_manager, firewall_state_changed, self);
 		g_clear_object (&priv->firewall_manager);
 	}
 
@@ -2270,6 +2422,7 @@ dispose (GObject *object)
 
 	g_clear_pointer (&priv->orig_hostname, g_free);
 	g_clear_pointer (&priv->cur_hostname, g_free);
+	g_clear_pointer (&priv->last_hostname, g_free);
 
 	if (priv->settings) {
 		g_signal_handlers_disconnect_by_data (priv->settings, priv);
@@ -2302,6 +2455,8 @@ finalize (GObject *object)
 	g_hash_table_unref (priv->devices);
 
 	G_OBJECT_CLASS (nm_policy_parent_class)->finalize (object);
+
+	g_object_unref (priv->netns);
 }
 
 static void
diff --git a/src/nm-policy.h b/src/nm-policy.h
index 64cac4fe..2c96e6d0 100644
--- a/src/nm-policy.h
+++ b/src/nm-policy.h
@@ -47,4 +47,21 @@ NMDevice *nm_policy_get_default_ip6_device (NMPolicy *policy);
 NMDevice *nm_policy_get_activating_ip4_device (NMPolicy *policy);
 NMDevice *nm_policy_get_activating_ip6_device (NMPolicy *policy);
 
+/**
+ * NMPolicyHostnameMode
+ * @NM_POLICY_HOSTNAME_MODE_NONE: never update the transient hostname.
+ * @NM_POLICY_HOSTNAME_MODE_DHCP: only hostname from DHCP hostname
+ *   options are eligible to be set as transient hostname.
+ * @NM_POLICY_HOSTNAME_MODE_FULL: NM will try to update the hostname looking
+ *   to current static hostname, DHCP options, reverse IP lookup and externally
+ *   set hostnames.
+ *
+ * NMPolicy's hostname update policy
+ */
+typedef enum {
+	NM_POLICY_HOSTNAME_MODE_NONE,
+	NM_POLICY_HOSTNAME_MODE_DHCP,
+	NM_POLICY_HOSTNAME_MODE_FULL,
+} NMPolicyHostnameMode;
+
 #endif /* __NETWORKMANAGER_POLICY_H__ */
diff --git a/src/nm-rfkill-manager.c b/src/nm-rfkill-manager.c
index 09a32965..e655e67c 100644
--- a/src/nm-rfkill-manager.c
+++ b/src/nm-rfkill-manager.c
@@ -23,7 +23,9 @@
 #include "nm-rfkill-manager.h"
 
 #include <string.h>
-#include <gudev/gudev.h>
+#include <libudev.h>
+
+#include "nm-utils/nm-udev-utils.h"
 
 /*****************************************************************************/
 
@@ -35,7 +37,7 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct {
-	GUdevClient *client;
+	NMUdevClient *udev_client;
 
 	/* Authoritative rfkill state (RFKILL_* enum) */
 	RfKillState rfkill_states[RFKILL_TYPE_MAX];
@@ -101,32 +103,32 @@ rfkill_state_to_desc (RfKillState rstate)
 }
 
 static Killswitch *
-killswitch_new (GUdevDevice *device, RfKillType rtype)
+killswitch_new (struct udev_device *device, RfKillType rtype)
 {
 	Killswitch *ks;
-	GUdevDevice *parent = NULL, *grandparent = NULL;
+	struct udev_device *parent = NULL, *grandparent = NULL;
 	const char *driver, *subsys, *parent_subsys = NULL;
 
 	ks = g_malloc0 (sizeof (Killswitch));
-	ks->name = g_strdup (g_udev_device_get_name (device));
-	ks->seqnum = g_udev_device_get_seqnum (device);
-	ks->path = g_strdup (g_udev_device_get_sysfs_path (device));
+	ks->name = g_strdup (udev_device_get_sysname (device));
+	ks->seqnum = udev_device_get_seqnum (device);
+	ks->path = g_strdup (udev_device_get_syspath (device));
 	ks->rtype = rtype;
 
-	driver = g_udev_device_get_property (device, "DRIVER");
-	subsys = g_udev_device_get_subsystem (device);
+	driver = udev_device_get_property_value (device, "DRIVER");
+	subsys = udev_device_get_subsystem (device);
 
 	/* Check parent for various attributes */
-	parent = g_udev_device_get_parent (device);
+	parent = udev_device_get_parent (device);
 	if (parent) {
-		parent_subsys = g_udev_device_get_subsystem (parent);
+		parent_subsys = udev_device_get_subsystem (parent);
 		if (!driver)
-			driver = g_udev_device_get_property (parent, "DRIVER");
+			driver = udev_device_get_property_value (parent, "DRIVER");
 		if (!driver) {
 			/* Sigh; try the grandparent */
-			grandparent = g_udev_device_get_parent (parent);
+			grandparent = udev_device_get_parent (parent);
 			if (grandparent)
-				driver = g_udev_device_get_property (grandparent, "DRIVER");
+				driver = udev_device_get_property_value (grandparent, "DRIVER");
 		}
 	}
 
@@ -140,10 +142,6 @@ killswitch_new (GUdevDevice *device, RfKillType rtype)
 	    || g_strcmp0 (parent_subsys, "acpi") == 0)
 		ks->platform = TRUE;
 
-	if (grandparent)
-		g_object_unref (grandparent);
-	if (parent)
-		g_object_unref (parent);
 	return ks;
 }
 
@@ -196,32 +194,34 @@ recheck_killswitches (NMRfkillManager *self)
 	/* Poll the states of all killswitches */
 	for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) {
 		Killswitch *ks = iter->data;
-		GUdevDevice *device;
+		struct udev_device *device;
 		RfKillState dev_state;
 		int sysfs_state;
 
-		device = g_udev_client_query_by_subsystem_and_name (priv->client, "rfkill", ks->name);
-		if (device) {
-			sysfs_state = g_udev_device_get_property_as_int (device, "RFKILL_STATE");
-			dev_state = sysfs_state_to_nm_state (sysfs_state);
-
-			nm_log_dbg (LOGD_RFKILL, "%s rfkill%s switch %s state now %d/%u",
-			            rfkill_type_to_desc (ks->rtype),
-			            ks->platform ? " platform" : "",
-			            ks->name,
-			            sysfs_state,
-			            dev_state);
-
-			if (ks->platform == FALSE) {
-				if (dev_state > poll_states[ks->rtype])
-					poll_states[ks->rtype] = dev_state;
-			} else {
-				platform_checked[ks->rtype] = TRUE;
-				if (dev_state > platform_states[ks->rtype])
-					platform_states[ks->rtype] = dev_state;
-			}
-			g_object_unref (device);
+		device = udev_device_new_from_subsystem_sysname (nm_udev_client_get_udev (priv->udev_client),
+		                                                 "rfkill", ks->name);
+		if (!device)
+			continue;
+		sysfs_state = _nm_utils_ascii_str_to_int64 (udev_device_get_property_value (device, "RFKILL_STATE"),
+		                                            10, G_MININT, G_MAXINT, -1);
+		dev_state = sysfs_state_to_nm_state (sysfs_state);
+
+		nm_log_dbg (LOGD_RFKILL, "%s rfkill%s switch %s state now %d/%u",
+		            rfkill_type_to_desc (ks->rtype),
+		            ks->platform ? " platform" : "",
+		            ks->name,
+		            sysfs_state,
+		            dev_state);
+
+		if (ks->platform == FALSE) {
+			if (dev_state > poll_states[ks->rtype])
+				poll_states[ks->rtype] = dev_state;
+		} else {
+			platform_checked[ks->rtype] = TRUE;
+			if (dev_state > platform_states[ks->rtype])
+				platform_states[ks->rtype] = dev_state;
 		}
+		udev_device_unref (device);
 	}
 
 	/* Log and emit change signal for final rfkill states */
@@ -276,14 +276,14 @@ rfkill_type_to_enum (const char *str)
 }
 
 static void
-add_one_killswitch (NMRfkillManager *self, GUdevDevice *device)
+add_one_killswitch (NMRfkillManager *self, struct udev_device *device)
 {
 	NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE (self);
 	const char *str_type;
 	RfKillType rtype;
 	Killswitch *ks;
 
-	str_type = g_udev_device_get_property (device, "RFKILL_TYPE");
+	str_type = udev_device_get_property_value (device, "RFKILL_TYPE");
 	rtype = rfkill_type_to_enum (str_type);
 	if (rtype == RFKILL_TYPE_UNKNOWN)
 		return;
@@ -300,12 +300,12 @@ add_one_killswitch (NMRfkillManager *self, GUdevDevice *device)
 }
 
 static void
-rfkill_add (NMRfkillManager *self, GUdevDevice *device)
+rfkill_add (NMRfkillManager *self, struct udev_device *device)
 {
 	const char *name;
 
 	g_return_if_fail (device != NULL);
-	name = g_udev_device_get_name (device);
+	name = udev_device_get_sysname (device);
 	g_return_if_fail (name != NULL);
 
 	if (!killswitch_find_by_name (self, name))
@@ -314,14 +314,14 @@ rfkill_add (NMRfkillManager *self, GUdevDevice *device)
 
 static void
 rfkill_remove (NMRfkillManager *self,
-               GUdevDevice *device)
+               struct udev_device *device)
 {
 	NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE (self);
 	GSList *iter;
 	const char *name;
 
 	g_return_if_fail (device != NULL);
-	name = g_udev_device_get_name (device);
+	name = udev_device_get_sysname (device);
 	g_return_if_fail (name != NULL);
 
 	for (iter = priv->killswitches; iter; iter = g_slist_next (iter)) {
@@ -337,22 +337,24 @@ rfkill_remove (NMRfkillManager *self,
 }
 
 static void
-handle_uevent (GUdevClient *client,
-               const char *action,
-               GUdevDevice *device,
+handle_uevent (NMUdevClient *client,
+               struct udev_device *device,
                gpointer user_data)
 {
 	NMRfkillManager *self = NM_RFKILL_MANAGER (user_data);
 	const char *subsys;
+	const char *action;
+
+	action = udev_device_get_action (device);
 
 	g_return_if_fail (action != NULL);
 
 	/* A bit paranoid */
-	subsys = g_udev_device_get_subsystem (device);
+	subsys = udev_device_get_subsystem (device);
 	g_return_if_fail (!g_strcmp0 (subsys, "rfkill"));
 
 	nm_log_dbg (LOGD_PLATFORM, "udev rfkill event: action '%s' device '%s'",
-	            action, g_udev_device_get_name (device));
+	            action, udev_device_get_sysname (device));
 
 	if (!strcmp (action, "add"))
 		rfkill_add (self, device);
@@ -368,22 +370,31 @@ static void
 nm_rfkill_manager_init (NMRfkillManager *self)
 {
 	NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE (self);
-	const char *subsys[] = { "rfkill", NULL };
-	GList *switches, *iter;
-	guint32 i;
+	struct udev_enumerate *enumerate;
+	struct udev_list_entry *iter;
+	guint i;
 
 	for (i = 0; i < RFKILL_TYPE_MAX; i++)
 		priv->rfkill_states[i] = RFKILL_UNBLOCKED;
 
-	priv->client = g_udev_client_new (subsys);
-	g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self);
+	priv->udev_client = nm_udev_client_new ((const char *[]) { "rfkill", NULL },
+	                                        handle_uevent, self);
+
+	enumerate = nm_udev_client_enumerate_new (priv->udev_client);
+	udev_enumerate_scan_devices (enumerate);
+	iter = udev_enumerate_get_list_entry (enumerate);
+	for (; iter; iter = udev_list_entry_get_next (iter)) {
+		struct udev_device *udevice;
 
-	switches = g_udev_client_query_by_subsystem (priv->client, "rfkill");
-	for (iter = switches; iter; iter = g_list_next (iter)) {
-		add_one_killswitch (self, G_UDEV_DEVICE (iter->data));
-		g_object_unref (G_UDEV_DEVICE (iter->data));
+		udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate),
+		                                        udev_list_entry_get_name (iter));
+		if (!udevice)
+			continue;
+
+		add_one_killswitch (self, udevice);
+		udev_device_unref (udevice);
 	}
-	g_list_free (switches);
+	udev_enumerate_unref (enumerate);
 
 	recheck_killswitches (self);
 }
@@ -400,13 +411,13 @@ dispose (GObject *object)
 	NMRfkillManager *self = NM_RFKILL_MANAGER (object);
 	NMRfkillManagerPrivate *priv = NM_RFKILL_MANAGER_GET_PRIVATE (self);
 
-	g_clear_object (&priv->client);
-
 	if (priv->killswitches) {
 		g_slist_free_full (priv->killswitches, (GDestroyNotify) killswitch_destroy);
 		priv->killswitches = NULL;
 	}
 
+	priv->udev_client = nm_udev_client_unref (priv->udev_client);
+
 	G_OBJECT_CLASS (nm_rfkill_manager_parent_class)->dispose (object);
 }
 
diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c
index 344ebc46..b58cdeb0 100644
--- a/src/nm-route-manager.c
+++ b/src/nm-route-manager.c
@@ -63,7 +63,14 @@ typedef struct {
 
 /*****************************************************************************/
 
+enum {
+	IP4_ROUTES_CHANGED,
+	LAST_SIGNAL,
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
 NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_LOG_WITH_PTR,
 	PROP_PLATFORM,
 );
 
@@ -76,6 +83,8 @@ typedef struct {
 		GHashTable *entries;
 		guint gc_id;
 	} ip4_device_routes;
+
+	bool log_with_ptr;
 } NMRouteManagerPrivate;
 
 struct _NMRouteManager {
@@ -93,10 +102,6 @@ G_DEFINE_TYPE (NMRouteManager, nm_route_manager, G_TYPE_OBJECT);
 
 /*****************************************************************************/
 
-NM_DEFINE_SINGLETON_GETTER (NMRouteManager, nm_route_manager_get, NM_TYPE_ROUTE_MANAGER);
-
-/*****************************************************************************/
-
 typedef struct {
 	const NMPlatformVTableRoute *vt;
 
@@ -150,11 +155,11 @@ static const VTableIP vtable_v4, vtable_v6;
             char __ch = __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'); \
             char __prefix[30] = _NMLOG_PREFIX_NAME; \
             \
-            if ((self) != singleton_instance) \
+            if (NM_ROUTE_MANAGER_GET_PRIVATE (self)->log_with_ptr) \
                 g_snprintf (__prefix, sizeof (__prefix), "%s%c[%p]", _NMLOG_PREFIX_NAME, __ch, (self)); \
             else \
                 __prefix[NM_STRLEN (_NMLOG_PREFIX_NAME)] = __ch; \
-            _nm_log ((level), (__domain), 0, \
+            _nm_log ((level), (__domain), 0, NULL, NULL, \
                      "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
@@ -167,7 +172,7 @@ static gboolean _ip4_device_routes_cancel (NMRouteManager *self);
 /*****************************************************************************/
 
 #if NM_MORE_ASSERTS && !defined (G_DISABLE_ASSERT)
-inline static void
+static inline void
 ASSERT_route_index_valid (const VTableIP *vtable, const GArray *entries, const RouteIndex *index, gboolean unique_ifindexes)
 {
 	guint i, j;
@@ -371,7 +376,7 @@ _route_equals_ignoring_ifindex (const VTableIP *vtable, const NMPlatformIPXRoute
 			r2_backup.rx.metric = (guint32) r2_metric;
 		r2 = &r2_backup;
 	}
-	return vtable->vt->route_cmp (r1, r2) == 0;
+	return vtable->vt->route_cmp (r1, r2, FALSE) == 0;
 }
 
 static NMPlatformIPXRoute *
@@ -530,6 +535,8 @@ _vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const
 				memcpy (cur_ipx_route, cur_known_route, vtable->vt->sizeof_route);
 				cur_ipx_route->rx.ifindex = ifindex;
 				cur_ipx_route->rx.metric = vtable->vt->metric_normalize (cur_ipx_route->rx.metric);
+				nm_utils_ipx_address_clear_host_address (vtable->vt->addr_family, cur_ipx_route->rx.network_ptr,
+				                                         cur_ipx_route->rx.network_ptr, cur_ipx_route->rx.plen);
 				ipx_routes_changed = TRUE;
 				_LOGt (vtable->vt->addr_family, "%3d: STATE: update  #%u - %s", ifindex, i_ipx_routes,
 				       vtable->vt->route_to_string (cur_ipx_route, NULL, 0));
@@ -625,6 +632,8 @@ _vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const
 				ipx_route = VTABLE_ROUTE_INDEX (vtable, ipx_routes->entries, ipx_routes->entries->len - 1);
 				ipx_route->rx.ifindex = ifindex;
 				ipx_route->rx.metric = vtable->vt->metric_normalize (ipx_route->rx.metric);
+				nm_utils_ipx_address_clear_host_address (vtable->vt->addr_family, ipx_route->rx.network_ptr,
+				                                         ipx_route->rx.network_ptr, ipx_route->rx.plen);
 
 				g_array_index (ipx_routes->effective_metrics_reverse, gint64, j++) = -1;
 
@@ -904,6 +913,9 @@ next:
 		}
 	}
 
+	if (vtable->vt->is_ip4 && ipx_routes_changed)
+		g_signal_emit (self, signals[IP4_ROUTES_CHANGED], 0);
+
 	g_free (known_routes_idx);
 	g_free (plat_routes_idx);
 	g_array_unref (plat_routes);
@@ -967,6 +979,33 @@ nm_route_manager_route_flush (NMRouteManager *self, int ifindex)
 	return success;
 }
 
+/**
+ * nm_route_manager_ip4_routes_shadowed:
+ * @ifindex: Interface index
+ *
+ * Returns: %TRUE if some other link has a route to the same destination
+ *   with a lower metric.
+ */
+gboolean
+nm_route_manager_ip4_routes_shadowed (NMRouteManager *self, int ifindex)
+{
+	NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self);
+	RouteIndex *index = priv->ip4_routes.index;
+	const NMPlatformIP4Route *route;
+	guint i;
+
+	for (i = 1; i < index->len; i++) {
+		route = (const NMPlatformIP4Route *) index->entries[i];
+
+		if (route->ifindex != ifindex)
+			continue;
+		if (_v4_route_dest_cmp (route, (const NMPlatformIP4Route *) index->entries[i - 1]) == 0)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
 /*****************************************************************************/
 
 static gboolean
@@ -1167,6 +1206,10 @@ set_property (GObject *object, guint prop_id,
 	NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self);
 
 	switch (prop_id) {
+	case PROP_LOG_WITH_PTR:
+		/* construct-only */
+		priv->log_with_ptr = g_value_get_boolean (value);
+		break;
 	case PROP_PLATFORM:
 		/* construct-only */
 		priv->platform = g_value_get_object (value) ? : NM_PLATFORM_GET;
@@ -1202,9 +1245,10 @@ nm_route_manager_init (NMRouteManager *self)
 }
 
 NMRouteManager *
-nm_route_manager_new (NMPlatform *platform)
+nm_route_manager_new (gboolean log_with_ptr, NMPlatform *platform)
 {
 	return g_object_new (NM_TYPE_ROUTE_MANAGER,
+	                     NM_ROUTE_MANAGER_LOG_WITH_PTR, log_with_ptr,
 	                     NM_ROUTE_MANAGER_PLATFORM, platform,
 	                     NULL);
 }
@@ -1251,6 +1295,13 @@ nm_route_manager_class_init (NMRouteManagerClass *klass)
 	object_class->dispose = dispose;
 	object_class->finalize = finalize;
 
+	obj_properties[PROP_LOG_WITH_PTR] =
+	    g_param_spec_boolean (NM_ROUTE_MANAGER_LOG_WITH_PTR, "", "",
+	                          TRUE,
+	                          G_PARAM_WRITABLE |
+	                          G_PARAM_CONSTRUCT_ONLY |
+	                          G_PARAM_STATIC_STRINGS);
+
 	obj_properties[PROP_PLATFORM] =
 	    g_param_spec_object (NM_ROUTE_MANAGER_PLATFORM, "", "",
 	                         NM_TYPE_PLATFORM,
@@ -1258,4 +1309,11 @@ nm_route_manager_class_init (NMRouteManagerClass *klass)
 	                         G_PARAM_CONSTRUCT_ONLY |
 	                         G_PARAM_STATIC_STRINGS);
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	signals[IP4_ROUTES_CHANGED] =
+	    g_signal_new (NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL, NULL,
+	                  G_TYPE_NONE, 0);
 }
diff --git a/src/nm-route-manager.h b/src/nm-route-manager.h
index d12f0256..bdf79a09 100644
--- a/src/nm-route-manager.h
+++ b/src/nm-route-manager.h
@@ -28,7 +28,10 @@
 #define NM_IS_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ROUTE_MANAGER))
 #define NM_ROUTE_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ROUTE_MANAGER, NMRouteManagerClass))
 
-#define NM_ROUTE_MANAGER_PLATFORM "platform"
+#define NM_ROUTE_MANAGER_LOG_WITH_PTR "log-with-ptr"
+#define NM_ROUTE_MANAGER_PLATFORM     "platform"
+
+#define NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED "ip4-routes-changed"
 
 typedef struct _NMRouteManagerClass NMRouteManagerClass;
 
@@ -38,9 +41,9 @@ gboolean nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, con
 gboolean nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync);
 gboolean nm_route_manager_route_flush (NMRouteManager *self, int ifindex);
 
+gboolean nm_route_manager_ip4_routes_shadowed (NMRouteManager *self, int ifindex);
 void nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list);
 
-NMRouteManager *nm_route_manager_get (void);
-NMRouteManager *nm_route_manager_new (NMPlatform *platform);
+NMRouteManager *nm_route_manager_new (gboolean log_with_ptr, NMPlatform *platform);
 
 #endif /* __NM_ROUTE_MANAGER_H__ */
diff --git a/src/nm-session-monitor.c b/src/nm-session-monitor.c
index b37db362..151deec8 100644
--- a/src/nm-session-monitor.c
+++ b/src/nm-session-monitor.c
@@ -28,8 +28,20 @@
 #include <string.h>
 #include <sys/stat.h>
 
+#if defined (SESSION_TRACKING_SYSTEMD) && defined (SESSION_TRACKING_ELOGIND)
+#error Cannot build both systemd-logind and elogind support
+#endif
+
 #ifdef SESSION_TRACKING_SYSTEMD
 #include <systemd/sd-login.h>
+#define LOGIND_NAME "systemd-logind"
+#endif
+
+#ifdef SESSION_TRACKING_ELOGIND
+#include <elogind/sd-login.h>
+#define LOGIND_NAME "elogind"
+/* Re-Use SESSION_TRACKING_SYSTEMD as elogind substitutes systemd-login */
+#define SESSION_TRACKING_SYSTEMD 1
 #endif
 
 #include "NetworkManagerUtils.h"
@@ -85,7 +97,7 @@ st_sd_session_exists (NMSessionMonitor *monitor, uid_t uid, gboolean active)
 	status = sd_uid_get_sessions (uid, active, NULL);
 
 	if (status < 0)
-		_LOGE ("failed to get systemd sessions for uid %d: %d", uid, status);
+		_LOGE ("failed to get "LOGIND_NAME" sessions for uid %d: %d", uid, status);
 
 	return status > 0;
 }
@@ -112,7 +124,7 @@ st_sd_init (NMSessionMonitor *monitor)
 		return;
 
 	if ((status = sd_login_monitor_new (NULL, &monitor->sd.monitor)) < 0) {
-		_LOGE ("failed to create systemd login monitor: %d", status);
+		_LOGE ("failed to create "LOGIND_NAME" monitor: %d", status);
 		return;
 	}
 
@@ -357,7 +369,7 @@ nm_session_monitor_init (NMSessionMonitor *monitor)
 {
 #ifdef SESSION_TRACKING_SYSTEMD
 	st_sd_init (monitor);
-	_LOGD ("using systemd-logind session tracking");
+	_LOGD ("using "LOGIND_NAME" session tracking");
 #endif
 
 #ifdef SESSION_TRACKING_CONSOLEKIT
diff --git a/src/nm-sleep-monitor.c b/src/nm-sleep-monitor.c
index 3be542c7..037db11b 100644
--- a/src/nm-sleep-monitor.c
+++ b/src/nm-sleep-monitor.c
@@ -38,13 +38,17 @@
 #define USE_UPOWER                      1
 #define _NMLOG_PREFIX_NAME              "sleep-monitor-up"
 
-#elif defined (SUSPEND_RESUME_SYSTEMD)
+#elif defined (SUSPEND_RESUME_SYSTEMD) || defined (SUSPEND_RESUME_ELOGIND)
 
 #define SUSPEND_DBUS_NAME               "org.freedesktop.login1"
 #define SUSPEND_DBUS_PATH               "/org/freedesktop/login1"
 #define SUSPEND_DBUS_INTERFACE          "org.freedesktop.login1.Manager"
 #define USE_UPOWER                      0
+#if defined (SUSPEND_RESUME_SYSTEMD)
 #define _NMLOG_PREFIX_NAME              "sleep-monitor-sd"
+#else
+#define _NMLOG_PREFIX_NAME              "sleep-monitor-el"
+#endif
 
 #elif defined(SUSPEND_RESUME_CONSOLEKIT)
 
@@ -60,7 +64,7 @@
 
 #else
 
-#error define one of SUSPEND_RESUME_SYSTEMD, SUSPEND_RESUME_CONSOLEKIT, or SUSPEND_RESUME_UPOWER
+#error define one of SUSPEND_RESUME_SYSTEMD, SUSPEND_RESUME_ELOGIND, SUSPEND_RESUME_CONSOLEKIT, or SUSPEND_RESUME_UPOWER
 
 #endif
 
diff --git a/src/nm-test-utils-core.h b/src/nm-test-utils-core.h
index f5118398..4e8e2f98 100644
--- a/src/nm-test-utils-core.h
+++ b/src/nm-test-utils-core.h
@@ -30,12 +30,12 @@
 
 /*****************************************************************************/
 
-inline static void
+static inline void
 nmtst_init_with_logging (int *argc, char ***argv, const char *log_level, const char *log_domains)
 {
 	__nmtst_init (argc, argv, FALSE, log_level, log_domains, NULL);
 }
-inline static void
+static inline void
 nmtst_init_assert_logging (int *argc, char ***argv, const char *log_level, const char *log_domains)
 {
 	gboolean set_logging;
@@ -54,7 +54,7 @@ nmtst_init_assert_logging (int *argc, char ***argv, const char *log_level, const
 
 #ifdef __NETWORKMANAGER_PLATFORM_H__
 
-inline static NMPlatformIP4Address *
+static inline NMPlatformIP4Address *
 nmtst_platform_ip4_address (const char *address, const char *peer_address, guint plen)
 {
 	static NMPlatformIP4Address addr;
@@ -72,7 +72,7 @@ nmtst_platform_ip4_address (const char *address, const char *peer_address, guint
 	return &addr;
 }
 
-inline static NMPlatformIP4Address *
+static inline NMPlatformIP4Address *
 nmtst_platform_ip4_address_full (const char *address, const char *peer_address, guint plen,
                                  int ifindex, NMIPConfigSource source, guint32 timestamp,
                                  guint32 lifetime, guint32 preferred, guint32 flags,
@@ -95,7 +95,7 @@ nmtst_platform_ip4_address_full (const char *address, const char *peer_address,
 	return addr;
 }
 
-inline static NMPlatformIP6Address *
+static inline NMPlatformIP6Address *
 nmtst_platform_ip6_address (const char *address, const char *peer_address, guint plen)
 {
 	static NMPlatformIP6Address addr;
@@ -110,7 +110,7 @@ nmtst_platform_ip6_address (const char *address, const char *peer_address, guint
 	return &addr;
 }
 
-inline static NMPlatformIP6Address *
+static inline NMPlatformIP6Address *
 nmtst_platform_ip6_address_full (const char *address, const char *peer_address, guint plen,
                                  int ifindex, NMIPConfigSource source, guint32 timestamp,
                                  guint32 lifetime, guint32 preferred, guint32 flags)
@@ -127,7 +127,7 @@ nmtst_platform_ip6_address_full (const char *address, const char *peer_address,
 	return addr;
 }
 
-inline static NMPlatformIP4Route *
+static inline NMPlatformIP4Route *
 nmtst_platform_ip4_route (const char *network, guint plen, const char *gateway)
 {
 	static NMPlatformIP4Route route;
@@ -142,7 +142,7 @@ nmtst_platform_ip4_route (const char *network, guint plen, const char *gateway)
 	return &route;
 }
 
-inline static NMPlatformIP4Route *
+static inline NMPlatformIP4Route *
 nmtst_platform_ip4_route_full (const char *network, guint plen, const char *gateway,
                                int ifindex, NMIPConfigSource source,
                                guint metric, guint mss,
@@ -161,8 +161,8 @@ nmtst_platform_ip4_route_full (const char *network, guint plen, const char *gate
 	return route;
 }
 
-inline static NMPlatformIP6Route *
-nmtst_platform_ip6_route (const char *network, guint plen, const char *gateway)
+static inline NMPlatformIP6Route *
+nmtst_platform_ip6_route (const char *network, guint plen, const char *gateway, const char *pref_src)
 {
 	static NMPlatformIP6Route route;
 
@@ -172,16 +172,17 @@ nmtst_platform_ip6_route (const char *network, guint plen, const char *gateway)
 	route.network = *nmtst_inet6_from_string (network);
 	route.plen = plen;
 	route.gateway = *nmtst_inet6_from_string (gateway);
+	route.pref_src = *nmtst_inet6_from_string (pref_src);
 
 	return &route;
 }
 
-inline static NMPlatformIP6Route *
+static inline NMPlatformIP6Route *
 nmtst_platform_ip6_route_full (const char *network, guint plen, const char *gateway,
                                int ifindex, NMIPConfigSource source,
                                guint metric, guint mss)
 {
-	NMPlatformIP6Route *route = nmtst_platform_ip6_route (network, plen, gateway);
+	NMPlatformIP6Route *route = nmtst_platform_ip6_route (network, plen, gateway, NULL);
 
 	route->ifindex = ifindex;
 	route->rt_source = source;
@@ -191,13 +192,13 @@ nmtst_platform_ip6_route_full (const char *network, guint plen, const char *gate
 	return route;
 }
 
-inline static int
+static inline int
 _nmtst_platform_ip4_routes_equal_sort (gconstpointer a, gconstpointer b, gpointer user_data)
 {
 	return nm_platform_ip4_route_cmp ((const NMPlatformIP4Route *) a, (const NMPlatformIP4Route *) b);
 }
 
-inline static void
+static inline void
 nmtst_platform_ip4_routes_equal (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gsize len, gboolean ignore_order)
 {
 	gsize i;
@@ -217,7 +218,7 @@ nmtst_platform_ip4_routes_equal (const NMPlatformIP4Route *a, const NMPlatformIP
 		if (nm_platform_ip4_route_cmp (&a[i], &b[i]) != 0) {
 			char buf[sizeof (_nm_utils_to_string_buffer)];
 
-			g_error ("Error comparing IPv4 route[%lu]: %s vs %s", (long unsigned) i,
+			g_error ("Error comparing IPv4 route[%lu]: %s vs %s", (unsigned long) i,
 			         nm_platform_ip4_route_to_string (&a[i], NULL, 0),
 			         nm_platform_ip4_route_to_string (&b[i], buf, sizeof (buf)));
 			g_assert_not_reached ();
@@ -225,13 +226,13 @@ nmtst_platform_ip4_routes_equal (const NMPlatformIP4Route *a, const NMPlatformIP
 	}
 }
 
-inline static int
+static inline int
 _nmtst_platform_ip6_routes_equal_sort (gconstpointer a, gconstpointer b, gpointer user_data)
 {
 	return nm_platform_ip6_route_cmp ((const NMPlatformIP6Route *) a, (const NMPlatformIP6Route *) b);
 }
 
-inline static void
+static inline void
 nmtst_platform_ip6_routes_equal (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gsize len, gboolean ignore_order)
 {
 	gsize i;
@@ -251,7 +252,7 @@ nmtst_platform_ip6_routes_equal (const NMPlatformIP6Route *a, const NMPlatformIP
 		if (nm_platform_ip6_route_cmp (&a[i], &b[i]) != 0) {
 			char buf[sizeof (_nm_utils_to_string_buffer)];
 
-			g_error ("Error comparing IPv6 route[%lu]: %s vs %s", (long unsigned) i,
+			g_error ("Error comparing IPv6 route[%lu]: %s vs %s", (unsigned long) i,
 			         nm_platform_ip6_route_to_string (&a[i], NULL, 0),
 			         nm_platform_ip6_route_to_string (&b[i], buf, sizeof (buf)));
 			g_assert_not_reached ();
@@ -264,7 +265,7 @@ nmtst_platform_ip6_routes_equal (const NMPlatformIP6Route *a, const NMPlatformIP
 
 #ifdef __NETWORKMANAGER_IP4_CONFIG_H__
 
-inline static NMIP4Config *
+static inline NMIP4Config *
 nmtst_ip4_config_clone (NMIP4Config *config)
 {
 	NMIP4Config *copy = nm_ip4_config_new (-1);
@@ -280,7 +281,7 @@ nmtst_ip4_config_clone (NMIP4Config *config)
 
 #ifdef __NETWORKMANAGER_IP6_CONFIG_H__
 
-inline static NMIP6Config *
+static inline NMIP6Config *
 nmtst_ip6_config_clone (NMIP6Config *config)
 {
 	NMIP6Config *copy = nm_ip6_config_new (-1);
diff --git a/src/nm-types.h b/src/nm-types.h
index c323c9a7..44b4fecb 100644
--- a/src/nm-types.h
+++ b/src/nm-types.h
@@ -46,6 +46,7 @@ typedef struct _NMProxyConfig        NMProxyConfig;
 typedef struct _NMIP4Config          NMIP4Config;
 typedef struct _NMIP6Config          NMIP6Config;
 typedef struct _NMManager            NMManager;
+typedef struct _NMNetns              NMNetns;
 typedef struct _NMPolicy             NMPolicy;
 typedef struct _NMRfkillManager      NMRfkillManager;
 typedef struct _NMPacrunnerManager   NMPacrunnerManager;
@@ -55,6 +56,22 @@ typedef struct _NMSleepMonitor       NMSleepMonitor;
 typedef struct _NMLldpListener       NMLldpListener;
 typedef struct _NMConfigDeviceStateData NMConfigDeviceStateData;
 
+/*****************************************************************************/
+
+typedef enum {
+	/* Do a full activation. */
+	NM_ACTIVATION_TYPE_MANAGED = 0,
+
+	/* gracefully/seamlessly take over the device. This leaves additional
+	 * IP addresses and does not restore missing manual addresses. */
+	NM_ACTIVATION_TYPE_ASSUME = 1,
+
+	/* external activation. This device is not managed by NM, instead
+	 * a in-memory connection is generated and NM pretends the device
+	 * to be active, but it doesn't do anything really. */
+	NM_ACTIVATION_TYPE_EXTERNAL = 2,
+} NMActivationType;
+
 typedef enum {
 	/* In priority order; higher number == higher priority */
 
@@ -83,7 +100,7 @@ typedef enum {
 	NM_IP_CONFIG_SOURCE_USER,
 } NMIPConfigSource;
 
-inline static gboolean
+static inline gboolean
 NM_IS_IP_CONFIG_SOURCE_RTPROT (NMIPConfigSource source)
 {
 	return source > NM_IP_CONFIG_SOURCE_UNKNOWN && source <= _NM_IP_CONFIG_SOURCE_RTPROT_LAST;
diff --git a/src/org.freedesktop.NetworkManager.conf b/src/org.freedesktop.NetworkManager.conf
index d130f7e2..6be1feb6 100644
--- a/src/org.freedesktop.NetworkManager.conf
+++ b/src/org.freedesktop.NetworkManager.conf
@@ -11,8 +11,8 @@
 
                 <allow send_interface="org.freedesktop.NetworkManager.SecretAgent"/>
                 <!-- These are there because some broken policies do
-		     <deny send_interface="..." /> (see dbus-daemon(8) for details).
-		     This seems to override that for the known VPN plugins.
+                     <deny send_interface="..." /> (see dbus-daemon(8) for details).
+                     This seems to override that for the known VPN plugins.
                   -->
                 <allow send_destination="org.freedesktop.NetworkManager.openconnect"/>
                 <allow send_destination="org.freedesktop.NetworkManager.openswan"/>
@@ -27,6 +27,8 @@
                 <allow send_destination="org.freedesktop.NetworkManager.strongswan"/>
                 <allow send_interface="org.freedesktop.NetworkManager.VPN.Plugin"/>
 
+                <allow send_destination="org.fedoraproject.FirewallD1"/>
+
                 <!-- Allow the custom name for the dnsmasq instance spawned by NM
                      from the dns dnsmasq plugin to own it's dbus name, and for
                      messages to be sent to it.
@@ -39,7 +41,7 @@
 
                 <deny send_destination="org.freedesktop.NetworkManager"/>
 
-		<!-- Basic D-Bus API stuff -->
+                <!-- Basic D-Bus API stuff -->
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.DBus.Introspectable"/>
                 <allow send_destination="org.freedesktop.NetworkManager"
@@ -47,7 +49,7 @@
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.DBus.ObjectManager"/>
 
-		<!-- Devices (read-only properties, no methods) -->
+                <!-- Devices (read-only properties, no methods) -->
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.NetworkManager.Device.Adsl"/>
                 <allow send_destination="org.freedesktop.NetworkManager"
@@ -83,17 +85,17 @@
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.NetworkManager.AccessPoint"/>
 
-		<!-- Devices (read-only, no security required) -->
+                <!-- Devices (read-only, no security required) -->
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.NetworkManager.Device.WiMax"/>
 
-		<!-- Devices (read/write, secured with PolicyKit) -->
+                <!-- Devices (read/write, secured with PolicyKit) -->
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.NetworkManager.Device.Wireless"/>
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.NetworkManager.Device"/>
 
-		<!-- Core stuff (read-only properties, no methods) -->
+                <!-- Core stuff (read-only properties, no methods) -->
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.NetworkManager.Connection.Active"/>
                 <allow send_destination="org.freedesktop.NetworkManager"
@@ -107,7 +109,7 @@
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.NetworkManager.VPN.Connection"/>
 
-		<!-- Core stuff (read/write, secured with PolicyKit) -->
+                <!-- Core stuff (read/write, secured with PolicyKit) -->
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.NetworkManager"/>
                 <allow send_destination="org.freedesktop.NetworkManager"
@@ -115,13 +117,13 @@
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.NetworkManager.Settings.Connection"/>
 
-		<!-- Agents; secured with PolicyKit.  Any process can talk to
-		     the AgentManager API, but only NetworkManager can talk
-		     to the agents themselves. -->
+                <!-- Agents; secured with PolicyKit.  Any process can talk to
+                     the AgentManager API, but only NetworkManager can talk
+                     to the agents themselves. -->
                 <allow send_destination="org.freedesktop.NetworkManager"
                        send_interface="org.freedesktop.NetworkManager.AgentManager"/>
 
-		<!-- Root-only functions -->
+                <!-- Root-only functions -->
                 <deny send_destination="org.freedesktop.NetworkManager"
                       send_interface="org.freedesktop.NetworkManager"
                       send_member="SetLogging"/>
@@ -139,7 +141,7 @@
                 <deny send_destination="org.freedesktop.NetworkManager.dnsmasq"/>
         </policy>
 
-	<limit name="max_replies_per_connection">1024</limit>
-	<limit name="max_match_rules_per_connection">2048</limit>
+        <limit name="max_replies_per_connection">1024</limit>
+        <limit name="max_match_rules_per_connection">2048</limit>
 </busconfig>
 
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index b92f56ce..38706f37 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.c
@@ -82,13 +82,13 @@ G_DEFINE_TYPE (NMFakePlatform, nm_fake_platform, NM_TYPE_PLATFORM)
         if (nm_logging_enabled (__level, __domain)) { \
             char __prefix[32]; \
             const char *__p_prefix = _NMLOG_PREFIX_NAME; \
-            const void *const __self = (self); \
+            NMPlatform *const __self = (self); \
             \
-            if (__self && __self != nm_platform_try_get ()) { \
+            if (__self && nm_platform_get_log_with_ptr (self)) { \
                 g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
                 __p_prefix = __prefix; \
             } \
-            _nm_log (__level, __domain, 0, \
+            _nm_log (__level, __domain, 0, NULL, NULL, \
                      "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
@@ -573,6 +573,12 @@ link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu)
 	return !!device;
 }
 
+static gboolean
+link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs)
+{
+	return TRUE;
+}
+
 static const char *
 link_get_udi (NMPlatform *platform, int ifindex)
 {
@@ -633,6 +639,22 @@ link_supports_vlans (NMPlatform *platform, int ifindex)
 }
 
 static gboolean
+link_supports_sriov (NMPlatform *platform, int ifindex)
+{
+	NMFakePlatformLink *device = link_get (platform, ifindex);
+
+	if (!device)
+		return FALSE;
+
+	switch (device->link.type) {
+	case NM_LINK_TYPE_LOOPBACK:
+		return FALSE;
+	default:
+		return TRUE;
+	}
+}
+
+static gboolean
 link_enslave (NMPlatform *platform, int master, int slave)
 {
 	NMFakePlatformLink *device = link_get (platform, slave);
@@ -1225,42 +1247,29 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, gu
 }
 
 static gboolean
-ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
-               in_addr_t network, guint8 plen, in_addr_t gateway,
-               in_addr_t pref_src, guint32 metric, guint32 mss)
+ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route)
 {
 	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform);
-	NMPlatformIP4Route route;
+	NMPlatformIP4Route rt = *route;
 	guint i;
-	guint8 scope;
-
-	g_assert (plen <= 32);
 
-	scope = gateway == 0 ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
+	rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source);
+	rt.network = nm_utils_ip4_address_clear_host_address (rt.network, rt.plen);
+	rt.scope_inv = nm_platform_route_scope_inv (rt.gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK);
 
-	memset (&route, 0, sizeof (route));
-	route.ifindex = ifindex;
-	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (source);
-	route.network = nm_utils_ip4_address_clear_host_address (network, plen);
-	route.plen = plen;
-	route.gateway = gateway;
-	route.metric = metric;
-	route.mss = mss;
-	route.scope_inv = nm_platform_route_scope_inv (scope);
-
-	if (gateway) {
+	if (rt.gateway) {
 		for (i = 0; i < priv->ip4_routes->len; i++) {
 			NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes,
 			                                           NMPlatformIP4Route, i);
 			guint32 gate = ntohl (item->network) >> (32 - item->plen);
-			guint32 host = ntohl (gateway) >> (32 - item->plen);
+			guint32 host = ntohl (rt.gateway) >> (32 - item->plen);
 
-			if (ifindex == item->ifindex && gate == host)
+			if (rt.ifindex == item->ifindex && gate == host)
 				break;
 		}
 		if (i == priv->ip4_routes->len) {
 			nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable",
-			             route.ifindex, nm_utils_inet4_ntop (route.network, NULL), route.plen, route.metric);
+			             rt.ifindex, nm_utils_inet4_ntop (rt.network, NULL), rt.plen, rt.metric);
 			return FALSE;
 		}
 	}
@@ -1268,65 +1277,58 @@ ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
 	for (i = 0; i < priv->ip4_routes->len; i++) {
 		NMPlatformIP4Route *item = &g_array_index (priv->ip4_routes, NMPlatformIP4Route, i);
 
-		if (item->network != route.network)
+		if (item->network != rt.network)
 			continue;
-		if (item->plen != route.plen)
+		if (item->plen != rt.plen)
 			continue;
-		if (item->metric != metric)
+		if (item->metric != rt.metric)
 			continue;
 
-		if (item->ifindex != route.ifindex) {
+		if (item->ifindex != rt.ifindex) {
 			ip4_route_delete (platform, item->ifindex, item->network, item->plen, item->metric);
 			i--;
 			continue;
 		}
 
-		memcpy (item, &route, sizeof (route));
-		g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, (int) NM_PLATFORM_SIGNAL_CHANGED);
+		memcpy (item, &rt, sizeof (rt));
+		g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE,
+		                       rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_CHANGED);
 		return TRUE;
 	}
 
-	g_array_append_val (priv->ip4_routes, route);
-	g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, &route, (int) NM_PLATFORM_SIGNAL_ADDED);
+	g_array_append_val (priv->ip4_routes, rt);
+	g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP4_ROUTE,
+	                       rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_ADDED);
 
 	return TRUE;
 }
 
 static gboolean
-ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
-               struct in6_addr network, guint8 plen, struct in6_addr gateway,
-               guint32 metric, guint32 mss)
+ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route)
 {
 	NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE ((NMFakePlatform *) platform);
-	NMPlatformIP6Route route;
+	NMPlatformIP6Route rt = *route;
 	guint i;
 
-	metric = nm_utils_ip6_route_metric_normalize (metric);
-
-	memset (&route, 0, sizeof (route));
-	route.ifindex = ifindex;
-	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (source);
-	nm_utils_ip6_address_clear_host_address (&route.network, &network, plen);
-	route.plen = plen;
-	route.gateway = gateway;
-	route.metric = metric;
-	route.mss = mss;
+	rt.metric = nm_utils_ip6_route_metric_normalize (rt.metric);
+	rt.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt.rt_source);
+	nm_utils_ip6_address_clear_host_address (&rt.network, &rt.network, rt.plen);
 
-	if (!IN6_IS_ADDR_UNSPECIFIED(&gateway)) {
+	if (!IN6_IS_ADDR_UNSPECIFIED (&rt.gateway)) {
 		for (i = 0; i < priv->ip6_routes->len; i++) {
 			NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes,
 			                                           NMPlatformIP6Route, i);
-			guint8 gate_bits = gateway.s6_addr[item->plen / 8] >> (8 - item->plen % 8);
+			guint8 gate_bits = rt.gateway.s6_addr[item->plen / 8] >> (8 - item->plen % 8);
 			guint8 host_bits = item->network.s6_addr[item->plen / 8] >> (8 - item->plen % 8);
 
-			if (   ifindex == item->ifindex
-			    && memcmp (&gateway, &item->network, item->plen / 8) == 0
+			if (   rt.ifindex == item->ifindex
+			    && memcmp (&rt.gateway, &item->network, item->plen / 8) == 0
 			    && gate_bits == host_bits)
 				break;
 		}
 		if (i == priv->ip6_routes->len) {
 			nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable",
-			             route.ifindex, nm_utils_inet6_ntop (&route.network, NULL), route.plen, route.metric);
+			             rt.ifindex, nm_utils_inet6_ntop (&rt.network, NULL), rt.plen, rt.metric);
 			return FALSE;
 		}
 	}
@@ -1334,26 +1336,28 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
 	for (i = 0; i < priv->ip6_routes->len; i++) {
 		NMPlatformIP6Route *item = &g_array_index (priv->ip6_routes, NMPlatformIP6Route, i);
 
-		if (!IN6_ARE_ADDR_EQUAL (&item->network, &route.network))
+		if (!IN6_ARE_ADDR_EQUAL (&item->network, &rt.network))
 			continue;
-		if (item->plen != route.plen)
+		if (item->plen != rt.plen)
 			continue;
-		if (item->metric != metric)
+		if (item->metric != rt.metric)
 			continue;
 
-		if (item->ifindex != route.ifindex) {
+		if (item->ifindex != rt.ifindex) {
 			ip6_route_delete (platform, item->ifindex, item->network, item->plen, item->metric);
 			i--;
 			continue;
 		}
 
-		memcpy (item, &route, sizeof (route));
-		g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, (int) NM_PLATFORM_SIGNAL_CHANGED);
+		memcpy (item, &rt, sizeof (rt));
+		g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE,
+		                       rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_CHANGED);
 		return TRUE;
 	}
 
-	g_array_append_val (priv->ip6_routes, route);
-	g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, &route, (int) NM_PLATFORM_SIGNAL_ADDED);
+	g_array_append_val (priv->ip6_routes, rt);
+	g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, (int) NMP_OBJECT_TYPE_IP6_ROUTE,
+	                       rt.ifindex, &rt, (int) NM_PLATFORM_SIGNAL_ADDED);
 
 	return TRUE;
 }
@@ -1418,7 +1422,9 @@ nm_fake_platform_setup (void)
 {
 	NMPlatform *platform;
 
-	platform = g_object_new (NM_TYPE_FAKE_PLATFORM, NULL);
+	platform = g_object_new (NM_TYPE_FAKE_PLATFORM,
+	                         NM_PLATFORM_LOG_WITH_PTR, FALSE,
+	                         NULL);
 
 	nm_platform_setup (platform);
 
@@ -1486,11 +1492,13 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass)
 
 	platform_class->link_set_address = link_set_address;
 	platform_class->link_set_mtu = link_set_mtu;
+	platform_class->link_set_sriov_num_vfs = link_set_sriov_num_vfs;
 
 	platform_class->link_get_driver_info = link_get_driver_info;
 
 	platform_class->link_supports_carrier_detect = link_supports_carrier_detect;
 	platform_class->link_supports_vlans = link_supports_vlans;
+	platform_class->link_supports_sriov = link_supports_sriov;
 
 	platform_class->link_enslave = link_enslave;
 	platform_class->link_release = link_release;
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index 2c5f0897..252f054d 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -38,7 +38,7 @@
 #include <linux/if_tunnel.h>
 #include <netlink/netlink.h>
 #include <netlink/msg.h>
-#include <gudev/gudev.h>
+#include <libudev.h>
 
 #include "nm-utils.h"
 #include "nm-core-internal.h"
@@ -51,6 +51,7 @@
 #include "wifi/wifi-utils.h"
 #include "wifi/wifi-utils-wext.h"
 #include "nm-utils/unaligned.h"
+#include "nm-utils/nm-udev-utils.h"
 
 #define VLAN_FLAG_MVRP 0x8
 
@@ -144,13 +145,13 @@
     G_STMT_START { \
         char __prefix[32]; \
         const char *__p_prefix = _NMLOG_PREFIX_NAME; \
-        const void *const __self = (self); \
+        NMPlatform *const __self = (self); \
         \
-        if (__self && __self != nm_platform_try_get ()) { \
+        if (__self && nm_platform_get_log_with_ptr (__self)) { \
             g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
             __p_prefix = __prefix; \
         } \
-        _nm_log (__level, __domain, __errsv, \
+        _nm_log (__level, __domain, __errsv, NULL, NULL, \
                  "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                  __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
     } G_STMT_END
@@ -298,7 +299,7 @@ _support_user_ipv6ll_get (void)
 {
 	if (_support_user_ipv6ll_still_undecided ()) {
 		_support_user_ipv6ll = -1;
-		_LOG2W ("kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "failed to detect; assume no support");
+		_LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", "failed to detect; assume no support");
 		return FALSE;
 	}
 	return _support_user_ipv6ll > 0;
@@ -309,13 +310,11 @@ static void
 _support_user_ipv6ll_detect (struct nlattr **tb)
 {
 	if (_support_user_ipv6ll_still_undecided ()) {
-		if (tb[IFLA_INET6_ADDR_GEN_MODE]) {
-			_support_user_ipv6ll = 1;
-			_LOG2D ("kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "detected");
-		} else {
-			_support_user_ipv6ll = -1;
-			_LOG2D ("kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "not detected");
-		}
+		gboolean supported = !!tb[IFLA_INET6_ADDR_GEN_MODE];
+
+		_support_user_ipv6ll = supported ? 1 : -1;
+		_LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s",
+		        supported ? "detected" : "not detected");
 	}
 }
 
@@ -1835,6 +1834,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 		NMIPAddr gateway;
 	} nh;
 	guint32 mss;
+	guint32 window = 0, cwnd = 0, initcwnd = 0, initrwnd = 0, mtu = 0, lock = 0;
 	guint32 table;
 
 	if (!nlmsg_valid_hdr (nlh, sizeof (*rtm)))
@@ -1848,8 +1848,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 	if (!NM_IN_SET (rtm->rtm_family, AF_INET, AF_INET6))
 		goto errout;
 
-	if (   rtm->rtm_type != RTN_UNICAST
-	    || rtm->rtm_tos != 0)
+	if (rtm->rtm_type != RTN_UNICAST)
 		goto errout;
 
 	err = nlmsg_parse (nlh, sizeof (struct rtmsg), tb, RTA_MAX, policy);
@@ -1943,21 +1942,34 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 	mss = 0;
 	if (tb[RTA_METRICS]) {
 		struct nlattr *mtb[RTAX_MAX + 1];
-		int i;
+		static struct nla_policy rtax_policy[RTAX_MAX + 1] = {
+			[RTAX_LOCK]        = { .type = NLA_U32 },
+			[RTAX_ADVMSS]      = { .type = NLA_U32 },
+			[RTAX_WINDOW]      = { .type = NLA_U32 },
+			[RTAX_CWND]        = { .type = NLA_U32 },
+			[RTAX_INITCWND]    = { .type = NLA_U32 },
+			[RTAX_INITRWND]    = { .type = NLA_U32 },
+			[RTAX_MTU]         = { .type = NLA_U32 },
+		};
 
-		err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL);
+		err = nla_parse_nested (mtb, RTAX_MAX, tb[RTA_METRICS], rtax_policy);
 		if (err < 0)
 			goto errout;
 
-		for (i = 1; i <= RTAX_MAX; i++) {
-			if (mtb[i]) {
-				if (i == RTAX_ADVMSS) {
-					if (nla_len (mtb[i]) >= sizeof (uint32_t))
-						mss = nla_get_u32(mtb[i]);
-					break;
-				}
-			}
-		}
+		if (mtb[RTAX_LOCK])
+			lock = nla_get_u32 (mtb[RTAX_LOCK]);
+		if (mtb[RTAX_ADVMSS])
+			mss = nla_get_u32 (mtb[RTAX_ADVMSS]);
+		if (mtb[RTAX_WINDOW])
+			window = nla_get_u32 (mtb[RTAX_WINDOW]);
+		if (mtb[RTAX_CWND])
+			cwnd = nla_get_u32 (mtb[RTAX_CWND]);
+		if (mtb[RTAX_INITCWND])
+			initcwnd = nla_get_u32 (mtb[RTAX_INITCWND]);
+		if (mtb[RTAX_INITRWND])
+			initrwnd = nla_get_u32 (mtb[RTAX_INITRWND]);
+		if (mtb[RTAX_MTU])
+			mtu = nla_get_u32 (mtb[RTAX_MTU]);
 	}
 
 	/*****************************************************************/
@@ -1982,12 +1994,31 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 	if (is_v4)
 		obj->ip4_route.scope_inv = nm_platform_route_scope_inv (rtm->rtm_scope);
 
-	if (is_v4) {
-		if (_check_addr_or_errout (tb, RTA_PREFSRC, addr_len))
+	if (_check_addr_or_errout (tb, RTA_PREFSRC, addr_len)) {
+		if (is_v4)
 			memcpy (&obj->ip4_route.pref_src, nla_data (tb[RTA_PREFSRC]), addr_len);
+		else
+			memcpy (&obj->ip6_route.pref_src, nla_data (tb[RTA_PREFSRC]), addr_len);
+	}
+
+	if (!is_v4 && tb[RTA_SRC]) {
+		_check_addr_or_errout (tb, RTA_SRC, addr_len);
+		memcpy (&obj->ip6_route.src, nla_data (tb[RTA_SRC]), addr_len);
+		obj->ip6_route.src_plen = rtm->rtm_src_len;
 	}
 
 	obj->ip_route.mss = mss;
+	obj->ip_route.window = window;
+	obj->ip_route.cwnd = cwnd;
+	obj->ip_route.initcwnd = initcwnd;
+	obj->ip_route.initrwnd = initrwnd;
+	obj->ip_route.mtu = mtu;
+	obj->ip_route.tos = rtm->rtm_tos;
+	obj->ip_route.lock_window = NM_FLAGS_HAS (lock, 1 << RTAX_WINDOW);
+	obj->ip_route.lock_cwnd = NM_FLAGS_HAS (lock, 1 << RTAX_CWND);
+	obj->ip_route.lock_initcwnd = NM_FLAGS_HAS (lock, 1 << RTAX_INITCWND);
+	obj->ip_route.lock_initrwnd = NM_FLAGS_HAS (lock, 1 << RTAX_INITRWND);
+	obj->ip_route.lock_mtu  = NM_FLAGS_HAS (lock, 1 << RTAX_MTU);
 
 	if (NM_FLAGS_HAS (rtm->rtm_flags, RTM_F_CLONED)) {
 		/* we must not straight way reject cloned routes, because we might have cached
@@ -2362,21 +2393,29 @@ _nl_msg_new_route (int nlmsg_type,
                    gconstpointer gateway,
                    guint32 metric,
                    guint32 mss,
-                   gconstpointer pref_src)
+                   gconstpointer pref_src,
+                   gconstpointer src,
+                   guint8 src_plen,
+                   guint8 tos,
+                   guint32 window,
+                   guint32 cwnd,
+                   guint32 initcwnd,
+                   guint32 initrwnd,
+                   guint32 mtu,
+                   guint32 lock)
 {
 	struct nl_msg *msg;
 	struct rtmsg rtmsg = {
 		.rtm_family = family,
-		.rtm_tos = 0,
+		.rtm_tos = tos,
 		.rtm_table = RT_TABLE_MAIN, /* omit setting RTA_TABLE attribute */
 		.rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (source),
 		.rtm_scope = scope,
 		.rtm_type = RTN_UNICAST,
 		.rtm_flags = 0,
 		.rtm_dst_len = plen,
-		.rtm_src_len = 0,
+		.rtm_src_len = src ? src_plen : 0,
 	};
-	NMIPAddr network_clean;
 
 	gsize addr_len;
 
@@ -2393,22 +2432,37 @@ _nl_msg_new_route (int nlmsg_type,
 
 	addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr);
 
-	nm_utils_ipx_address_clear_host_address (family, &network_clean, network, plen);
-	NLA_PUT (msg, RTA_DST, addr_len, &network_clean);
+	NLA_PUT (msg, RTA_DST, addr_len, network);
+
+	if (src)
+		NLA_PUT (msg, RTA_SRC, addr_len, src);
 
 	NLA_PUT_U32 (msg, RTA_PRIORITY, metric);
 
 	if (pref_src)
 		NLA_PUT (msg, RTA_PREFSRC, addr_len, pref_src);
 
-	if (mss > 0) {
+	if (mss || window || cwnd || initcwnd || initrwnd || mtu || lock) {
 		struct nlattr *metrics;
 
 		metrics = nla_nest_start (msg, RTA_METRICS);
 		if (!metrics)
 			goto nla_put_failure;
 
-		NLA_PUT_U32 (msg, RTAX_ADVMSS, mss);
+		if (mss)
+			NLA_PUT_U32 (msg, RTAX_ADVMSS, mss);
+		if (window)
+			NLA_PUT_U32 (msg, RTAX_WINDOW, window);
+		if (cwnd)
+			NLA_PUT_U32 (msg, RTAX_CWND, cwnd);
+		if (initcwnd)
+			NLA_PUT_U32 (msg, RTAX_INITCWND, initcwnd);
+		if (initrwnd)
+			NLA_PUT_U32 (msg, RTAX_INITRWND, initrwnd);
+		if (mtu)
+			NLA_PUT_U32 (msg, RTAX_MTU, mtu);
+		if (lock)
+			NLA_PUT_U32 (msg, RTAX_LOCK, lock);
 
 		nla_nest_end(msg, metrics);
 	}
@@ -2452,15 +2506,15 @@ _support_kernel_extended_ifa_flags_detect (struct nl_msg *msg)
 	 * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR
 	 * and IFA_F_NOPREFIXROUTE (they were added together).
 	 **/
-	_support_kernel_extended_ifa_flags = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */);
-	_LOG2D ("support: kernel-extended-ifa-flags: %ssupported", _support_kernel_extended_ifa_flags ? "" : "not ");
+	_support_kernel_extended_ifa_flags = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), IFA_FLAGS);
+	_LOG2D ("kernel-support: extended-ifa-flags: %s", _support_kernel_extended_ifa_flags ? "detected" : "not detected");
 }
 
 static gboolean
 _support_kernel_extended_ifa_flags_get (void)
 {
 	if (_support_kernel_extended_ifa_flags_still_undecided ()) {
-		_LOG2W ("support: kernel-extended-ifa-flags: unable to detect kernel support for handling IPv6 temporary addresses. Assume support");
+		_LOG2D ("kernel-support: extended-ifa-flags: %s", "unable to detect kernel support for handling IPv6 temporary addresses. Assume support");
 		_support_kernel_extended_ifa_flags = 1;
 	}
 	return _support_kernel_extended_ifa_flags;
@@ -2492,7 +2546,7 @@ typedef struct {
 	gboolean sysctl_get_warned;
 	GHashTable *sysctl_get_prev_values;
 
-	GUdevClient *udev_client;
+	NMUdevClient *udev_client;
 
 	struct {
 		/* which delayed actions are scheduled, as marked in @flags.
@@ -2526,19 +2580,13 @@ struct _NMLinuxPlatformClass {
 
 G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM)
 
-static inline NMLinuxPlatformPrivate *
-NM_LINUX_PLATFORM_GET_PRIVATE (const void *self)
-{
-	nm_assert (NM_IS_LINUX_PLATFORM (self));
-
-	return &(((NMLinuxPlatform *) self)->_priv);
-}
+#define NM_LINUX_PLATFORM_GET_PRIVATE(self) _NM_GET_PRIVATE_VOID(self, NMLinuxPlatform, NM_IS_LINUX_PLATFORM)
 
 NMPlatform *
-nm_linux_platform_new (gboolean netns_support)
+nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support)
 {
 	return g_object_new (NM_TYPE_LINUX_PLATFORM,
-	                     NM_PLATFORM_REGISTER_SINGLETON, FALSE,
+	                     NM_PLATFORM_LOG_WITH_PTR, log_with_ptr,
 	                     NM_PLATFORM_NETNS_SUPPORT, netns_support,
 	                     NULL);
 }
@@ -2546,10 +2594,7 @@ nm_linux_platform_new (gboolean netns_support)
 void
 nm_linux_platform_setup (void)
 {
-	g_object_new (NM_TYPE_LINUX_PLATFORM,
-	              NM_PLATFORM_REGISTER_SINGLETON, TRUE,
-	              NM_PLATFORM_NETNS_SUPPORT, FALSE,
-	              NULL);
+	nm_platform_setup (nm_linux_platform_new (FALSE, FALSE));
 }
 
 static void
@@ -2584,23 +2629,24 @@ static void
 _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value)
 {
 	GError *error = NULL;
-	char *contents, *contents_escaped;
-	char *value_escaped = g_strescape (value, NULL);
+	char *contents;
+	gs_free char *value_escaped = g_strescape (value, NULL);
 
 	if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, &contents, NULL, &error) < 0) {
 		_LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid, value_escaped, error->message);
 		g_clear_error (&error);
-	} else {
-		g_strstrip (contents);
-		contents_escaped = g_strescape (contents, NULL);
-		if (strcmp (contents, value) == 0)
-			_LOGD ("sysctl: setting '%s' to '%s' (current value is identical)", pathid, value_escaped);
-		else
-			_LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", pathid, value_escaped, contents_escaped);
-		g_free (contents);
-		g_free (contents_escaped);
+		return;
 	}
-	g_free (value_escaped);
+
+	g_strstrip (contents);
+	if (nm_streq (contents, value))
+		_LOGD ("sysctl: setting '%s' to '%s' (current value is identical)", pathid, value_escaped);
+	else {
+		gs_free char *contents_escaped = g_strescape (contents, NULL);
+
+		_LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", pathid, value_escaped, contents_escaped);
+	}
+	g_free (contents);
 }
 
 #define _log_dbg_sysctl_set(platform, pathid, dirfd, path, value) \
@@ -2750,26 +2796,23 @@ _log_dbg_sysctl_get_impl (NMPlatform *platform, const char *pathid, const char *
 
 	if (prev_value) {
 		if (strcmp (prev_value, contents) != 0) {
-			char *contents_escaped = g_strescape (contents, NULL);
-			char *prev_value_escaped = g_strescape (prev_value, NULL);
+			gs_free char *contents_escaped = g_strescape (contents, NULL);
+			gs_free char *prev_value_escaped = g_strescape (prev_value, NULL);
 
 			_LOGD ("sysctl: reading '%s': '%s' (changed from '%s' on last read)", pathid, contents_escaped, prev_value_escaped);
-			g_free (contents_escaped);
-			g_free (prev_value_escaped);
 			g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (pathid), g_strdup (contents));
 		}
 	} else {
-		char *contents_escaped = g_strescape (contents, NULL);
+		gs_free char *contents_escaped = g_strescape (contents, NULL);
 
 		_LOGD ("sysctl: reading '%s': '%s'", pathid, contents_escaped);
-		g_free (contents_escaped);
 		g_hash_table_insert (priv->sysctl_get_prev_values, g_strdup (pathid), g_strdup (contents));
-	}
 
-	if (   !priv->sysctl_get_warned
-	    && g_hash_table_size (priv->sysctl_get_prev_values) > 50000) {
-		_LOGW ("sysctl: the internal cache for debug-logging of sysctl values grew pretty large. You can clear it by disabling debug-logging: `nmcli general logging level KEEP domains PLATFORM:INFO`.");
-		priv->sysctl_get_warned = TRUE;
+		if (   !priv->sysctl_get_warned
+		    && g_hash_table_size (priv->sysctl_get_prev_values) > 50000) {
+			_LOGW ("sysctl: the internal cache for debug-logging of sysctl values grew pretty large. You can clear it by disabling debug-logging: `nmcli general logging level KEEP domains PLATFORM:INFO`.");
+			priv->sysctl_get_warned = TRUE;
+		}
 	}
 }
 
@@ -4352,18 +4395,23 @@ link_get_unmanaged (NMPlatform *platform, int ifindex, gboolean *unmanaged)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	const NMPObject *link;
-	GUdevDevice *udev_device = NULL;
+	struct udev_device *udevice = NULL;
+	const char *uproperty;
 
 	link = nmp_cache_lookup_link (priv->cache, ifindex);
-	if (link)
-		udev_device = link->_link.udev.device;
+	if (!link)
+		return FALSE;
 
-	if (udev_device && g_udev_device_get_property (udev_device, "NM_UNMANAGED")) {
-		*unmanaged = g_udev_device_get_property_as_boolean (udev_device, "NM_UNMANAGED");
-		return TRUE;
-	}
+	udevice = link->_link.udev.device;
+	if (!udevice)
+		return FALSE;
 
-	return FALSE;
+	uproperty = udev_device_get_property_value (udevice, "NM_UNMANAGED");
+	if (!uproperty)
+		return FALSE;
+
+	*unmanaged = nm_udev_utils_property_as_boolean (uproperty);
+	return TRUE;
 }
 
 static gboolean
@@ -4463,10 +4511,10 @@ link_get_udi (NMPlatform *platform, int ifindex)
 	    || !obj->_link.netlink.is_in_netlink
 	    || !obj->_link.udev.device)
 		return NULL;
-	return g_udev_device_get_sysfs_path (obj->_link.udev.device);
+	return udev_device_get_syspath (obj->_link.udev.device);
 }
 
-static GObject *
+static struct udev_device *
 link_get_udev_device (NMPlatform *platform, int ifindex)
 {
 	const NMPObject *obj_cache;
@@ -4477,7 +4525,7 @@ link_get_udev_device (NMPlatform *platform, int ifindex)
 	 * appears invisible via other platform functions. */
 
 	obj_cache = nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex);
-	return obj_cache ? (GObject *) obj_cache->_link.udev.device : NULL;
+	return obj_cache ? obj_cache->_link.udev.device : NULL;
 }
 
 static NMPlatformError
@@ -4556,6 +4604,30 @@ link_supports_vlans (NMPlatform *platform, int ifindex)
 	return nmp_utils_ethtool_supports_vlans (ifindex);
 }
 
+static gboolean
+link_supports_sriov (NMPlatform *platform, int ifindex)
+{
+	nm_auto_pop_netns NMPNetns *netns = NULL;
+	nm_auto_close int dirfd = -1;
+	char ifname[IFNAMSIZ];
+	int total = -1;
+
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
+	dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname);
+	if (dirfd < 0)
+		return FALSE;
+
+	total = nm_platform_sysctl_get_int32 (platform,
+	                                      NMP_SYSCTL_PATHID_NETDIR (dirfd,
+	                                                                ifname,
+	                                                                "device/sriov_totalvfs"),
+	                                      -1);
+
+	return total > 0;
+}
+
 static NMPlatformError
 link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size_t length)
 {
@@ -4647,6 +4719,71 @@ nla_put_failure:
 	g_return_val_if_reached (FALSE);
 }
 
+static gboolean
+link_set_sriov_num_vfs (NMPlatform *platform, int ifindex, guint num_vfs)
+{
+	nm_auto_pop_netns NMPNetns *netns = NULL;
+	nm_auto_close int dirfd = -1;
+	int total, current;
+	char ifname[IFNAMSIZ];
+	char buf[64];
+
+	_LOGD ("link: change %d: num VFs: %u", ifindex, num_vfs);
+
+	if (!nm_platform_netns_push (platform, &netns))
+		return FALSE;
+
+	dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname);
+	if (!dirfd)
+		return FALSE;
+
+	total = nm_platform_sysctl_get_int32 (platform,
+	                                      NMP_SYSCTL_PATHID_NETDIR (dirfd,
+	                                                                ifname,
+	                                                                "device/sriov_totalvfs"),
+	                                      -1);
+	if (total < 1)
+		return FALSE;
+	if (num_vfs > total) {
+		_LOGW ("link: %d only supports %u VFs (requested %u)", ifindex, total, num_vfs);
+		num_vfs = total;
+	}
+
+	current = nm_platform_sysctl_get_int32 (platform,
+	                                        NMP_SYSCTL_PATHID_NETDIR (dirfd,
+	                                                                  ifname,
+	                                                                  "device/sriov_numvfs"),
+	                                        -1);
+	if (current == num_vfs)
+		return TRUE;
+
+	if (current != 0) {
+		/* We need to destroy all other VFs before changing the value */
+		if (!nm_platform_sysctl_set (NM_PLATFORM_GET,
+		                             NMP_SYSCTL_PATHID_NETDIR (dirfd,
+		                                                       ifname,
+		                                                      "device/sriov_numvfs"),
+		                             "0")) {
+			_LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", 0, strerror (errno));
+			return FALSE;
+		}
+		if (num_vfs == 0)
+			return TRUE;
+	}
+
+	/* Finally, set the desired value */
+	if (!nm_platform_sysctl_set (NM_PLATFORM_GET,
+	                             NMP_SYSCTL_PATHID_NETDIR (dirfd,
+	                                                       ifname,
+	                                                       "device/sriov_numvfs"),
+	                             nm_sprintf_buf (buf, "%d", num_vfs))) {
+		_LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", num_vfs, strerror (errno));
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static char *
 link_get_physical_port_id (NMPlatform *platform, int ifindex)
 {
@@ -4691,7 +4828,7 @@ vlan_add (NMPlatform *platform,
 	vlan_flags &= (guint32) NM_VLAN_FLAGS_ALL;
 
 	_LOGD ("link: add vlan '%s', parent %d, vlan id %d, flags %X",
-	       name, parent, vlan_id, (unsigned int) vlan_flags);
+	       name, parent, vlan_id, (unsigned) vlan_flags);
 
 	nlmsg = _nl_msg_new_link (RTM_NEWLINK,
 	                          NLM_F_CREATE | NLM_F_EXCL,
@@ -5153,7 +5290,7 @@ _vlan_change_vlan_qos_mapping_create (gboolean is_ingress_map,
 	if (current_n_map) {
 		if (is_ingress_map) {
 			/* For the ingress-map, there are only 8 entries (0 to 7).
-			 * When the user requests to reset all entires, we don't actually
+			 * When the user requests to reset all entries, we don't actually
 			 * need the cached entries, we can just explicitly clear all possible
 			 * ones.
 			 *
@@ -5456,34 +5593,17 @@ wifi_get_wifi_data (NMPlatform *platform, int ifindex)
 	wifi_data = g_hash_table_lookup (priv->wifi_data, GINT_TO_POINTER (ifindex));
 	pllink = nm_platform_link_get (platform, ifindex);
 
-	/* @wifi_data contains an interface name which is used for WEXT queries. If
-	 * the interface name changes we should at least replace the name in the
-	 * existing structure; but probably a complete reinitialization is better
-	 * because during the initial creation there can be race conditions while
-	 * the interface is renamed by udev.
-	 */
-	if (wifi_data && pllink) {
-		if (!nm_streq (wifi_utils_get_iface (wifi_data), pllink->name)) {
-			_LOGD ("wifi: interface %s renamed to %s, dropping old data for ifindex %d",
-			       wifi_utils_get_iface (wifi_data),
-			       pllink->name,
-			       ifindex);
-			g_hash_table_remove (priv->wifi_data, GINT_TO_POINTER (ifindex));
-			wifi_data = NULL;
-		}
-	}
-
 	if (!wifi_data) {
 		if (pllink) {
 			if (pllink->type == NM_LINK_TYPE_WIFI)
-				wifi_data = wifi_utils_init (pllink->name, ifindex, TRUE);
+				wifi_data = wifi_utils_init (ifindex, TRUE);
 			else if (pllink->type == NM_LINK_TYPE_OLPC_MESH) {
 				/* The kernel driver now uses nl80211, but we force use of WEXT because
 				 * the cfg80211 interactions are not quite ready to support access to
 				 * mesh control through nl80211 just yet.
 				 */
 #if HAVE_WEXT
-				wifi_data = wifi_wext_init (pllink->name, ifindex, FALSE);
+				wifi_data = wifi_wext_init (ifindex, FALSE);
 #endif
 			}
 
@@ -5905,53 +6025,85 @@ ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags fl
 	return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ROUTE, flags);
 }
 
+static guint32
+ip_route_get_lock_flag (NMPlatformIPRoute *route)
+{
+	return   (((guint32) route->lock_window) << RTAX_WINDOW)
+	       | (((guint32) route->lock_cwnd) << RTAX_CWND)
+	       | (((guint32) route->lock_initcwnd) << RTAX_INITCWND)
+	       | (((guint32) route->lock_initrwnd) << RTAX_INITRWND)
+	       | (((guint32) route->lock_mtu) << RTAX_MTU);
+}
+
 static gboolean
-ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
-               in_addr_t network, guint8 plen, in_addr_t gateway,
-               in_addr_t pref_src, guint32 metric, guint32 mss)
+ip4_route_add (NMPlatform *platform, const NMPlatformIP4Route *route)
 {
 	NMPObject obj_id;
 	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
+	in_addr_t network;
+
+	network = nm_utils_ip4_address_clear_host_address (route->network, route->plen);
 
+	/* FIXME: take the scope from route into account */
 	nlmsg = _nl_msg_new_route (RTM_NEWROUTE,
 	                           NLM_F_CREATE | NLM_F_REPLACE,
 	                           AF_INET,
-	                           ifindex,
-	                           source,
-	                           gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK,
+	                           route->ifindex,
+	                           route->rt_source,
+	                           route->gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK,
 	                           &network,
-	                           plen,
-	                           &gateway,
-	                           metric,
-	                           mss,
-	                           pref_src ? &pref_src : NULL);
-
-	nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric);
+	                           route->plen,
+	                           &route->gateway,
+	                           route->metric,
+	                           route->mss,
+	                           route->pref_src ? &route->pref_src : NULL,
+	                           NULL,
+	                           0,
+	                           route->tos,
+	                           route->window,
+	                           route->cwnd,
+	                           route->initcwnd,
+	                           route->initrwnd,
+	                           route->mtu,
+	                           ip_route_get_lock_flag ((NMPlatformIPRoute *) route));
+
+	nmp_object_stackinit_id_ip4_route (&obj_id, route->ifindex, network, route->plen, route->metric);
 	return do_add_addrroute (platform, &obj_id, nlmsg);
 }
 
 static gboolean
-ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source,
-               struct in6_addr network, guint8 plen, struct in6_addr gateway,
-               guint32 metric, guint32 mss)
+ip6_route_add (NMPlatform *platform, const NMPlatformIP6Route *route)
 {
 	NMPObject obj_id;
 	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
+	struct in6_addr network;
 
+	nm_utils_ip6_address_clear_host_address (&network, &route->network, route->plen);
+
+	/* FIXME: take the scope from route into account */
 	nlmsg = _nl_msg_new_route (RTM_NEWROUTE,
 	                           NLM_F_CREATE | NLM_F_REPLACE,
 	                           AF_INET6,
-	                           ifindex,
-	                           source,
-	                           !IN6_IS_ADDR_UNSPECIFIED (&gateway) ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK,
+	                           route->ifindex,
+	                           route->rt_source,
+	                           IN6_IS_ADDR_UNSPECIFIED (&route->gateway) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE,
 	                           &network,
-	                           plen,
-	                           &gateway,
-	                           metric,
-	                           mss,
-	                           NULL);
-
-	nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric);
+	                           route->plen,
+	                           &route->gateway,
+	                           route->metric,
+	                           route->mss,
+	                           !IN6_IS_ADDR_UNSPECIFIED (&route->pref_src) ? &route->pref_src : NULL,
+	                           !IN6_IS_ADDR_UNSPECIFIED (&route->src) ? &route->src : NULL,
+	                           route->src_plen,
+	                           route->tos,
+	                           route->window,
+	                           route->cwnd,
+	                           route->initcwnd,
+	                           route->initrwnd,
+	                           route->mtu,
+	                           ip_route_get_lock_flag ((NMPlatformIPRoute *) route));
+
+	nmp_object_stackinit_id_ip6_route (&obj_id, route->ifindex, &network, route->plen, route->metric);
 	return do_add_addrroute (platform, &obj_id, nlmsg);
 }
 
@@ -5962,6 +6114,8 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 p
 	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
 	NMPObject obj_id;
 
+	network = nm_utils_ip4_address_clear_host_address (network, plen);
+
 	nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric);
 
 	if (metric == 0) {
@@ -6004,7 +6158,16 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, guint8 p
 	                           NULL,
 	                           metric,
 	                           0,
-	                           NULL);
+	                           NULL,
+	                           NULL,
+	                           0,
+	                           0,
+	                           0,
+	                           0,
+	                           0,
+	                           0,
+	                           0,
+	                           0);
 	if (!nlmsg)
 		return FALSE;
 
@@ -6019,6 +6182,8 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, gu
 
 	metric = nm_utils_ip6_route_metric_normalize (metric);
 
+	nm_utils_ip6_address_clear_host_address (&network, &network, plen);
+
 	nlmsg = _nl_msg_new_route (RTM_DELROUTE,
 	                           0,
 	                           AF_INET6,
@@ -6030,7 +6195,16 @@ ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, gu
 	                           NULL,
 	                           metric,
 	                           0,
-	                           NULL);
+	                           NULL,
+	                           NULL,
+	                           0,
+	                           0,
+	                           0,
+	                           0,
+	                           0,
+	                           0,
+	                           0,
+	                           0);
 	if (!nlmsg)
 		return FALSE;
 
@@ -6423,14 +6597,16 @@ after_read:
 /*****************************************************************************/
 
 static void
-cache_update_link_udev (NMPlatform *platform, int ifindex, GUdevDevice *udev_device)
+cache_update_link_udev (NMPlatform *platform,
+                        int ifindex,
+                        struct udev_device *udevice)
 {
 	NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
 	nm_auto_nmpobj NMPObject *obj_cache = NULL;
 	gboolean was_visible;
 	NMPCacheOpsType cache_op;
 
-	cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udev_device, &obj_cache, &was_visible, cache_pre_hook, platform);
+	cache_op = nmp_cache_update_link_udev (priv->cache, ifindex, udevice, &obj_cache, &was_visible, cache_pre_hook, platform);
 
 	if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
 		nm_auto_pop_netns NMPNetns *netns = NULL;
@@ -6443,55 +6619,58 @@ cache_update_link_udev (NMPlatform *platform, int ifindex, GUdevDevice *udev_dev
 
 static void
 udev_device_added (NMPlatform *platform,
-                   GUdevDevice *udev_device)
+                   struct udev_device *udevice)
 {
 	const char *ifname;
+	const char *ifindex_s;
 	int ifindex;
 
-	ifname = g_udev_device_get_name (udev_device);
+	ifname = udev_device_get_sysname (udevice);
 	if (!ifname) {
 		_LOGD ("udev-add: failed to get device's interface");
 		return;
 	}
 
-	if (!g_udev_device_get_property (udev_device, "IFINDEX")) {
+	ifindex_s = udev_device_get_property_value (udevice, "IFINDEX");
+	if (!ifindex_s) {
 		_LOGW ("udev-add[%s]failed to get device's ifindex", ifname);
 		return;
 	}
-	ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
+	ifindex = _nm_utils_ascii_str_to_int64 (ifindex_s, 10, 1, G_MAXINT, 0);
 	if (ifindex <= 0) {
 		_LOGW ("udev-add[%s]: retrieved invalid IFINDEX=%d", ifname, ifindex);
 		return;
 	}
 
-	if (!g_udev_device_get_sysfs_path (udev_device)) {
+	if (!udev_device_get_syspath (udevice)) {
 		_LOGD ("udev-add[%s,%d]: couldn't determine device path; ignoring...", ifname, ifindex);
 		return;
 	}
 
 	_LOGT ("udev-add[%s,%d]: device added", ifname, ifindex);
-	cache_update_link_udev (platform, ifindex, udev_device);
+	cache_update_link_udev (platform, ifindex, udevice);
 }
 
 static gboolean
-_udev_device_removed_match_link (const NMPObject *obj, gpointer udev_device)
+_udev_device_removed_match_link (const NMPObject *obj, gpointer udevice)
 {
-	return obj->_link.udev.device == udev_device;
+	return obj->_link.udev.device == udevice;
 }
 
 static void
 udev_device_removed (NMPlatform *platform,
-                     GUdevDevice *udev_device)
+                     struct udev_device *udevice)
 {
+	const char *ifindex_s;
 	int ifindex = 0;
 
-	if (g_udev_device_get_property (udev_device, "IFINDEX"))
-		ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
-	else {
+	ifindex_s = udev_device_get_property_value (udevice, "IFINDEX");
+	ifindex = _nm_utils_ascii_str_to_int64 (ifindex_s, 10, 1, G_MAXINT, 0);
+	if (ifindex <= 0) {
 		const NMPObject *obj;
 
 		obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache,
-		                                  0, NULL, FALSE, NM_LINK_TYPE_NONE, _udev_device_removed_match_link, udev_device);
+		                                  0, NULL, FALSE, NM_LINK_TYPE_NONE, _udev_device_removed_match_link, udevice);
 		if (obj)
 			ifindex = obj->link.ifindex;
 	}
@@ -6504,9 +6683,8 @@ udev_device_removed (NMPlatform *platform,
 }
 
 static void
-handle_udev_event (GUdevClient *client,
-                   const char *action,
-                   GUdevDevice *udev_device,
+handle_udev_event (NMUdevClient *udev_client,
+                   struct udev_device *udevice,
                    gpointer user_data)
 {
 	nm_auto_pop_netns NMPNetns *netns = NULL;
@@ -6514,26 +6692,27 @@ handle_udev_event (GUdevClient *client,
 	const char *subsys;
 	const char *ifindex;
 	guint64 seqnum;
+	const char *action;
+
+	action = udev_device_get_action (udevice);
+	g_return_if_fail (action);
 
-	g_return_if_fail (action != NULL);
+	subsys = udev_device_get_subsystem (udevice);
+	g_return_if_fail (nm_streq0 (subsys, "net"));
 
 	if (!nm_platform_netns_push (platform, &netns))
 		return;
 
-	/* A bit paranoid */
-	subsys = g_udev_device_get_subsystem (udev_device);
-	g_return_if_fail (!g_strcmp0 (subsys, "net"));
-
-	ifindex = g_udev_device_get_property (udev_device, "IFINDEX");
-	seqnum = g_udev_device_get_seqnum (udev_device);
+	ifindex = udev_device_get_property_value (udevice, "IFINDEX");
+	seqnum = udev_device_get_seqnum (udevice);
 	_LOGD ("UDEV event: action '%s' subsys '%s' device '%s' (%s); seqnum=%" G_GUINT64_FORMAT,
-	        action, subsys, g_udev_device_get_name (udev_device),
+	        action, subsys, udev_device_get_sysname (udevice),
 	        ifindex ? ifindex : "unknown", seqnum);
 
-	if (!strcmp (action, "add") || !strcmp (action, "move"))
-		udev_device_added (platform, udev_device);
-	if (!strcmp (action, "remove"))
-		udev_device_removed (platform, udev_device);
+	if (NM_IN_STRSET (action, "add", "move"))
+		udev_device_added (platform, udevice);
+	else if (NM_IN_STRSET (action, "remove"))
+		udev_device_removed (platform, udevice);
 }
 
 /*****************************************************************************/
@@ -6554,8 +6733,10 @@ nm_linux_platform_init (NMLinuxPlatform *self)
 	priv->delayed_action.list_wait_for_nl_response = g_array_new (FALSE, TRUE, sizeof (DelayedActionWaitForNlResponseData));
 	priv->wifi_data = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) wifi_utils_deinit);
 
-	if (use_udev)
-		priv->udev_client = g_udev_client_new ((const char *[]) { "net", NULL });
+	if (use_udev) {
+		priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL },
+		                                        handle_udev_event, self);
+	}
 }
 
 static void
@@ -6638,24 +6819,30 @@ constructed (GObject *_object)
 
 	/* Set up udev monitoring */
 	if (priv->udev_client) {
-		GUdevEnumerator *enumerator;
-		GList *devices, *iter;
-
-		g_signal_connect (priv->udev_client, "uevent", G_CALLBACK (handle_udev_event), platform);
+		struct udev_enumerate *enumerator;
+		struct udev_list_entry *devices, *l;
 
 		/* And read initial device list */
-		enumerator = g_udev_enumerator_new (priv->udev_client);
-		g_udev_enumerator_add_match_subsystem (enumerator, "net");
+		enumerator = nm_udev_client_enumerate_new (priv->udev_client);
+
+		udev_enumerate_add_match_is_initialized (enumerator);
 
-		g_udev_enumerator_add_match_is_initialized (enumerator);
+		udev_enumerate_scan_devices (enumerator);
 
-		devices = g_udev_enumerator_execute (enumerator);
-		for (iter = devices; iter; iter = g_list_next (iter)) {
-			udev_device_added (platform, G_UDEV_DEVICE (iter->data));
-			g_object_unref (G_UDEV_DEVICE (iter->data));
+		devices = udev_enumerate_get_list_entry (enumerator);
+		for (l = devices; l; l = udev_list_entry_get_next (l)) {
+			struct udev_device *udevice;
+
+			udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerator),
+			                                        udev_list_entry_get_name (l));
+			if (!udevice)
+				continue;
+
+			udev_device_added (platform, udevice);
+			udev_device_unref (udevice);
 		}
-		g_list_free (devices);
-		g_object_unref (enumerator);
+
+		udev_enumerate_unref (enumerator);
 	}
 }
 
@@ -6675,11 +6862,6 @@ dispose (GObject *object)
 
 	g_clear_pointer (&priv->prune_candidates, g_hash_table_unref);
 
-	if (priv->udev_client) {
-		g_signal_handlers_disconnect_by_func (priv->udev_client, G_CALLBACK (handle_udev_event), platform);
-		g_clear_object (&priv->udev_client);
-	}
-
 	G_OBJECT_CLASS (nm_linux_platform_parent_class)->dispose (object);
 }
 
@@ -6705,6 +6887,8 @@ finalize (GObject *object)
 		g_hash_table_destroy (priv->sysctl_get_prev_values);
 	}
 
+	priv->udev_client = nm_udev_client_unref (priv->udev_client);
+
 	G_OBJECT_CLASS (nm_linux_platform_parent_class)->finalize (object);
 }
 
@@ -6750,6 +6934,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
 	platform_class->link_set_address = link_set_address;
 	platform_class->link_get_permanent_address = link_get_permanent_address;
 	platform_class->link_set_mtu = link_set_mtu;
+	platform_class->link_set_sriov_num_vfs = link_set_sriov_num_vfs;
 
 	platform_class->link_get_physical_port_id = link_get_physical_port_id;
 	platform_class->link_get_dev_id = link_get_dev_id;
@@ -6758,6 +6943,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
 
 	platform_class->link_supports_carrier_detect = link_supports_carrier_detect;
 	platform_class->link_supports_vlans = link_supports_vlans;
+	platform_class->link_supports_sriov = link_supports_sriov;
 
 	platform_class->link_enslave = link_enslave;
 	platform_class->link_release = link_release;
diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h
index b3272aae..6b66ea69 100644
--- a/src/platform/nm-linux-platform.h
+++ b/src/platform/nm-linux-platform.h
@@ -35,7 +35,7 @@ typedef struct _NMLinuxPlatformClass NMLinuxPlatformClass;
 
 GType nm_linux_platform_get_type (void);
 
-NMPlatform *nm_linux_platform_new (gboolean netns_support);
+NMPlatform *nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support);
 
 void nm_linux_platform_setup (void);
 
diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c
index 65fb01bc..b664e8a9 100644
--- a/src/platform/nm-platform-utils.c
+++ b/src/platform/nm-platform-utils.c
@@ -32,6 +32,7 @@
 #include <linux/version.h>
 #include <linux/rtnetlink.h>
 #include <fcntl.h>
+#include <libudev.h>
 
 #include "nm-utils.h"
 #include "nm-setting-wired.h"
@@ -42,8 +43,8 @@
  * utils
  ******************************************************************/
 
-extern char *if_indextoname (unsigned int __ifindex, char *__ifname);
-unsigned int if_nametoindex (const char *__ifname);
+extern char *if_indextoname (unsigned __ifindex, char *__ifname);
+unsigned if_nametoindex (const char *__ifname);
 
 const char *
 nmp_utils_if_indextoname (int ifindex, char *out_ifname/*IFNAMSIZ*/)
@@ -440,7 +441,7 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex,
 		return TRUE;
 
 	nm_log_dbg (LOGD_PLATFORM, "setting Wake-on-LAN options 0x%x, password '%s'",
-	            (unsigned int) wol, wol_password);
+	            (unsigned) wol, wol_password);
 
 	wol_info.cmd = ETHTOOL_SWOL;
 	wol_info.wolopts = 0;
@@ -520,35 +521,33 @@ nmp_utils_mii_supports_carrier_detect (int ifindex)
  ******************************************************************/
 
 const char *
-nmp_utils_udev_get_driver (GUdevDevice *device)
+nmp_utils_udev_get_driver (struct udev_device *udevice)
 {
-	GUdevDevice *parent = NULL, *grandparent = NULL;
+	struct udev_device *parent = NULL, *grandparent = NULL;
 	const char *driver, *subsys;
 
-	driver = g_udev_device_get_driver (device);
+	driver = udev_device_get_driver (udevice);
 	if (driver)
 		goto out;
 
 	/* Try the parent */
-	parent = g_udev_device_get_parent (device);
+	parent = udev_device_get_parent (udevice);
 	if (parent) {
-		driver = g_udev_device_get_driver (parent);
+		driver = udev_device_get_driver (parent);
 		if (!driver) {
 			/* Try the grandparent if it's an ibmebus device or if the
 			 * subsys is NULL which usually indicates some sort of
 			 * platform device like a 'gadget' net interface.
 			 */
-			subsys = g_udev_device_get_subsystem (parent);
+			subsys = udev_device_get_subsystem (parent);
 			if (   (g_strcmp0 (subsys, "ibmebus") == 0)
 			    || (subsys == NULL)) {
-				grandparent = g_udev_device_get_parent (parent);
+				grandparent = udev_device_get_parent (parent);
 				if (grandparent)
-					driver = g_udev_device_get_driver (grandparent);
+					driver = udev_device_get_driver (grandparent);
 			}
 		}
 	}
-	g_clear_object (&parent);
-	g_clear_object (&grandparent);
 
 out:
 	/* Intern the string so we don't have to worry about memory
diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h
index 699e80c6..ea25470e 100644
--- a/src/platform/nm-platform-utils.h
+++ b/src/platform/nm-platform-utils.h
@@ -21,8 +21,6 @@
 #ifndef __NM_PLATFORM_UTILS_H__
 #define __NM_PLATFORM_UTILS_H__
 
-#include <gudev/gudev.h>
-
 #include "nm-platform.h"
 #include "nm-setting-wired.h"
 
@@ -66,7 +64,9 @@ gboolean  nmp_utils_ethtool_get_permanent_address (int ifindex,
 gboolean nmp_utils_mii_supports_carrier_detect (int ifindex);
 
 
-const char *nmp_utils_udev_get_driver (GUdevDevice *device);
+struct udev_device;
+
+const char *nmp_utils_udev_get_driver (struct udev_device *udevice);
 
 NMIPConfigSource nmp_utils_ip_config_source_from_rtprot (guint8 rtprot) _nm_const;
 guint8           nmp_utils_ip_config_source_coerce_to_rtprot   (NMIPConfigSource source) _nm_const;
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index a9014b36..767187d9 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -43,10 +43,6 @@
 
 /*****************************************************************************/
 
-const NMIPAddr nm_ip_addr_zero = NMIPAddrInit;
-
-/*****************************************************************************/
-
 G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->addr.data ) == NM_UTILS_HWADDR_LEN_MAX);
 G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Address, address));
 G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Address, address));
@@ -62,13 +58,13 @@ G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OF
         if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
             char __prefix[32]; \
             const char *__p_prefix = _NMLOG_PREFIX_NAME; \
-            const void *const __self = (self); \
+            const NMPlatform *const __self = (self); \
             \
-            if (__self && __self != nm_platform_try_get ()) { \
+            if (__self && NM_PLATFORM_GET_PRIVATE (__self)->log_with_ptr) { \
                 g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
                 __p_prefix = __prefix; \
             } \
-            _nm_log (__level, _NMLOG_DOMAIN, 0, \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \
                      "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
@@ -83,12 +79,12 @@ static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = { 0 };
 enum {
 	PROP_0,
 	PROP_NETNS_SUPPORT,
-	PROP_REGISTER_SINGLETON,
+	PROP_LOG_WITH_PTR,
 	LAST_PROP,
 };
 
 typedef struct _NMPlatformPrivate {
-	bool register_singleton:1;
+	bool log_with_ptr:1;
 } NMPlatformPrivate;
 
 G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT)
@@ -97,6 +93,14 @@ G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT)
 
 /*****************************************************************************/
 
+gboolean
+nm_platform_get_log_with_ptr (NMPlatform *self)
+{
+	return NM_PLATFORM_GET_PRIVATE (self)->log_with_ptr;
+}
+
+/*****************************************************************************/
+
 guint
 _nm_platform_signal_id_get (NMPlatformSignalIdType signal_type)
 {
@@ -187,12 +191,6 @@ nm_platform_get ()
 	return singleton_instance;
 }
 
-NMPlatform *
-nm_platform_try_get (void)
-{
-	return singleton_instance;
-}
-
 /*****************************************************************************/
 
 /**
@@ -444,11 +442,19 @@ _link_get_all_presort (gconstpointer  p_a,
 	const NMPlatformLink *a = p_a;
 	const NMPlatformLink *b = p_b;
 
-	if (a->ifindex < b->ifindex)
+	/* Loopback always first */
+	if (a->ifindex == 1)
 		return -1;
-	if (a->ifindex > b->ifindex)
+	if (b->ifindex == 1)
 		return 1;
-	return 0;
+
+	/* Initialized links first */
+	if (a->initialized > b->initialized)
+		return -1;
+	if (a->initialized < b->initialized)
+		return 1;
+
+	return strcmp (a->name, b->name);
 }
 
 /**
@@ -1000,7 +1006,7 @@ nm_platform_link_get_udi (NMPlatform *self, int ifindex)
 	return NULL;
 }
 
-GObject *
+struct udev_device *
 nm_platform_link_get_udev_device (NMPlatform *self, int ifindex)
 {
 	_CHECK_SELF (self, klass, FALSE);
@@ -1169,6 +1175,30 @@ nm_platform_link_supports_vlans (NMPlatform *self, int ifindex)
 	return klass->link_supports_vlans (self, ifindex);
 }
 
+gboolean
+nm_platform_link_supports_sriov (NMPlatform *self, int ifindex)
+{
+	_CHECK_SELF (self, klass, FALSE);
+
+	g_return_val_if_fail (ifindex >= 0, FALSE);
+
+	return klass->link_supports_sriov (self, ifindex);
+}
+
+gboolean
+nm_platform_link_set_sriov_num_vfs (NMPlatform *self, int ifindex, guint num_vfs)
+{
+	_CHECK_SELF (self, klass, FALSE);
+
+	g_return_val_if_fail (ifindex > 0, FALSE);
+
+	_LOGD ("link: setting %u VFs for %s (%d)",
+	       num_vfs,
+	       nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)),
+	       ifindex);
+	return klass->link_set_sriov_num_vfs (self, ifindex, num_vfs);
+}
+
 /**
  * nm_platform_link_set_up:
  * @self: platform instance
@@ -1184,7 +1214,7 @@ nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmwar
 
 	g_return_val_if_fail (ifindex > 0, FALSE);
 
-	_LOGD ("link: setting up '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex);
+	_LOGD ("link: setting up %s (%d)", nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex);
 	return klass->link_set_up (self, ifindex, out_no_firmware);
 }
 
@@ -1202,7 +1232,7 @@ nm_platform_link_set_down (NMPlatform *self, int ifindex)
 
 	g_return_val_if_fail (ifindex > 0, FALSE);
 
-	_LOGD ("link: setting down '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex);
+	_LOGD ("link: setting down %s (%d)", nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex);
 	return klass->link_set_down (self, ifindex);
 }
 
@@ -1220,7 +1250,7 @@ nm_platform_link_set_arp (NMPlatform *self, int ifindex)
 
 	g_return_val_if_fail (ifindex >= 0, FALSE);
 
-	_LOGD ("link: setting arp '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex);
+	_LOGD ("link: setting arp %s (%d)", nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex);
 	return klass->link_set_arp (self, ifindex);
 }
 
@@ -3109,14 +3139,7 @@ nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRoute
 /**
  * nm_platform_ip4_route_add:
  * @self:
- * @ifindex:
- * @source:
- * network:
- * plen:
- * gateway:
- * pref_src:
- * metric:
- * mss:
+ * @route:
  *
  * For kernel, a gateway can be either explicitly set or left
  * at zero (0.0.0.0). In addition, there is the scope of the IPv4
@@ -3137,57 +3160,29 @@ nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRoute
  * Returns: %TRUE in case of success.
  */
 gboolean
-nm_platform_ip4_route_add (NMPlatform *self,
-                           int ifindex, NMIPConfigSource source,
-                           in_addr_t network, guint8 plen,
-                           in_addr_t gateway, in_addr_t pref_src,
-                           guint32 metric, guint32 mss)
+nm_platform_ip4_route_add (NMPlatform *self, const NMPlatformIP4Route *route)
 {
 	_CHECK_SELF (self, klass, FALSE);
 
-	g_return_val_if_fail (plen <= 32, FALSE);
+	g_return_val_if_fail (route, FALSE);
+	g_return_val_if_fail (route->plen <= 32, FALSE);
 
-	if (_LOGD_ENABLED ()) {
-		NMPlatformIP4Route route = { 0 };
-
-		route.ifindex = ifindex;
-		route.rt_source = source;
-		route.network = network;
-		route.plen = plen;
-		route.gateway = gateway;
-		route.metric = metric;
-		route.mss = mss;
-		route.pref_src = pref_src;
-
-		_LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (&route, NULL, 0));
-	}
-	return klass->ip4_route_add (self, ifindex, source, network, plen, gateway, pref_src, metric, mss);
+	_LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (route, NULL, 0));
+
+	return klass->ip4_route_add (self, route);
 }
 
 gboolean
-nm_platform_ip6_route_add (NMPlatform *self,
-                           int ifindex, NMIPConfigSource source,
-                           struct in6_addr network, guint8 plen, struct in6_addr gateway,
-                           guint32 metric, guint32 mss)
+nm_platform_ip6_route_add (NMPlatform *self, const NMPlatformIP6Route *route)
 {
 	_CHECK_SELF (self, klass, FALSE);
 
-	g_return_val_if_fail (plen <= 128, FALSE);
+	g_return_val_if_fail (route, FALSE);
+	g_return_val_if_fail (route->plen <= 128, FALSE);
 
-	if (_LOGD_ENABLED ()) {
-		NMPlatformIP6Route route = { 0 };
+	_LOGD ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (route, NULL, 0));
 
-		route.ifindex = ifindex;
-		route.rt_source = source;
-		route.network = network;
-		route.plen = plen;
-		route.gateway = gateway;
-		route.metric = metric;
-		route.mss = mss;
-
-		_LOGD ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (&route, NULL, 0));
-	}
-	return klass->ip6_route_add (self, ifindex, source, network, plen, gateway, metric, mss);
+	return klass->ip6_route_add (self, route);
 }
 
 gboolean
@@ -3900,6 +3895,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi
 	char s_pref_src[INET_ADDRSTRLEN];
 	char str_dev[TO_STRING_DEV_BUF_SIZE];
 	char str_scope[30], s_source[50];
+	char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32];
 
 	if (!nm_utils_to_string_buffer_init_null (route, &buf, &len))
 		return buf;
@@ -3909,16 +3905,35 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi
 
 	_to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev));
 
+	if (route->tos)
+		nm_sprintf_buf (str_tos, " tos 0x%x", (unsigned) route->tos);
+	if (route->window)
+		nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window);
+	if (route->cwnd)
+		nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd);
+	if (route->initcwnd)
+		nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd);
+	if (route->initrwnd)
+		nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd);
+	if (route->mtu)
+		nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu);
+
 	g_snprintf (buf, len,
 	            "%s/%d"
 	            " via %s"
 	            "%s"
 	            " metric %"G_GUINT32_FORMAT
 	            " mss %"G_GUINT32_FORMAT
-	            " src %s" /* source */
+	            " rt-src %s" /* protocol */
 	            "%s" /* cloned */
 	            "%s%s" /* scope */
 	            "%s%s" /* pref-src */
+	            "%s" /* tos */
+	            "%s" /* window */
+	            "%s" /* cwnd */
+	            "%s" /* initcwnd */
+	            "%s" /* initrwnd */
+	            "%s" /* mtu */
 	            "",
 	            s_network,
 	            route->plen,
@@ -3931,7 +3946,13 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi
 	            route->scope_inv ? " scope " : "",
 	            route->scope_inv ? (nm_platform_route_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_scope))) : "",
 	            route->pref_src ? " pref-src " : "",
-	            route->pref_src ? inet_ntop (AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : "");
+	            route->pref_src ? inet_ntop (AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : "",
+	            route->tos ? str_tos : "",
+	            route->window ? str_window : "",
+	            route->cwnd ? str_cwnd : "",
+	            route->initcwnd ? str_initcwnd : "",
+	            route->initrwnd ? str_initrwnd : "",
+	            route->mtu ? str_mtu : "");
 	return buf;
 }
 
@@ -3950,25 +3971,54 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi
 const char *
 nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len)
 {
-	char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN];
+	char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN], s_pref_src[INET6_ADDRSTRLEN];
+	char s_src[INET6_ADDRSTRLEN];
 	char str_dev[TO_STRING_DEV_BUF_SIZE], s_source[50];
+	char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32];
 
 	if (!nm_utils_to_string_buffer_init_null (route, &buf, &len))
 		return buf;
 
-	inet_ntop (AF_INET6, &route->network, s_network, sizeof(s_network));
-	inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof(s_gateway));
+	inet_ntop (AF_INET6, &route->network, s_network, sizeof (s_network));
+	inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof (s_gateway));
+	inet_ntop (AF_INET6, &route->src, s_src, sizeof (s_src));
+
+	if (IN6_IS_ADDR_UNSPECIFIED (&route->pref_src))
+		s_pref_src[0] = 0;
+	else
+		inet_ntop (AF_INET6, &route->pref_src, s_pref_src, sizeof (s_pref_src));
 
 	_to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev));
 
+	if (route->tos)
+		nm_sprintf_buf (str_tos, " tos 0x%x", (unsigned) route->tos);
+	if (route->window)
+		nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window);
+	if (route->cwnd)
+		nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd);
+	if (route->initcwnd)
+		nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd);
+	if (route->initrwnd)
+		nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd);
+	if (route->mtu)
+		nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu);
+
 	g_snprintf (buf, len,
 	            "%s/%d"
 	            " via %s"
 	            "%s"
 	            " metric %"G_GUINT32_FORMAT
 	            " mss %"G_GUINT32_FORMAT
-	            " src %s" /* source */
+	            " rt-src %s" /* protocol */
+	            " src %s/%u" /* source */
 	            "%s" /* cloned */
+	            "%s%s" /* pref-src */
+	            "%s" /* tos */
+	            "%s" /* window */
+	            "%s" /* cwnd */
+	            "%s" /* initcwnd */
+	            "%s" /* initrwnd */
+	            "%s" /* mtu */
 	            "",
 	            s_network,
 	            route->plen,
@@ -3977,7 +4027,17 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi
 	            route->metric,
 	            route->mss,
 	            nmp_utils_ip_config_source_to_string (route->rt_source, s_source, sizeof (s_source)),
-	            route->rt_cloned ? " cloned" : "");
+	            s_src, route->src_plen,
+	            route->rt_cloned ? " cloned" : "",
+	            s_pref_src[0] ? " pref-src " : "",
+	            s_pref_src[0] ? s_pref_src : "",
+	            route->tos ? str_tos : "",
+	            route->window ? str_window : "",
+	            route->cwnd ? str_cwnd : "",
+	            route->initcwnd ? str_initcwnd : "",
+	            route->initrwnd ? str_initrwnd : "",
+	            route->mtu ? str_mtu : "");
+
 	return buf;
 }
 
@@ -4253,11 +4313,16 @@ nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6A
 }
 
 int
-nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b)
+nm_platform_ip4_route_cmp_full (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_host_part)
 {
 	_CMP_SELF (a, b);
 	_CMP_FIELD (a, b, ifindex);
-	_CMP_FIELD (a, b, network);
+	if (consider_host_part)
+		_CMP_FIELD (a, b, network);
+	else {
+		_CMP_DIRECT (nm_utils_ip4_address_clear_host_address (a->network, a->plen),
+		             nm_utils_ip4_address_clear_host_address (b->network, b->plen));
+	}
 	_CMP_FIELD (a, b, plen);
 	_CMP_FIELD (a, b, metric);
 	_CMP_FIELD (a, b, gateway);
@@ -4266,21 +4331,54 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route
 	_CMP_FIELD (a, b, scope_inv);
 	_CMP_FIELD (a, b, pref_src);
 	_CMP_FIELD (a, b, rt_cloned);
+	_CMP_FIELD (a, b, tos);
+	_CMP_FIELD (a, b, lock_window);
+	_CMP_FIELD (a, b, lock_cwnd);
+	_CMP_FIELD (a, b, lock_initcwnd);
+	_CMP_FIELD (a, b, lock_initrwnd);
+	_CMP_FIELD (a, b, lock_mtu);
+	_CMP_FIELD (a, b, window);
+	_CMP_FIELD (a, b, cwnd);
+	_CMP_FIELD (a, b, initcwnd);
+	_CMP_FIELD (a, b, initrwnd);
+	_CMP_FIELD (a, b, mtu);
 	return 0;
 }
 
 int
-nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b)
+nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gboolean consider_host_part)
 {
 	_CMP_SELF (a, b);
 	_CMP_FIELD (a, b, ifindex);
-	_CMP_FIELD_MEMCMP (a, b, network);
+	if (consider_host_part)
+		_CMP_FIELD_MEMCMP (a, b, network);
+	else {
+		struct in6_addr n1, n2;
+
+		nm_utils_ip6_address_clear_host_address (&n1, &a->network, a->plen);
+		nm_utils_ip6_address_clear_host_address (&n2, &b->network, b->plen);
+		_CMP_DIRECT_MEMCMP (&n1, &n2, sizeof (struct in6_addr));
+	}
 	_CMP_FIELD (a, b, plen);
 	_CMP_FIELD (a, b, metric);
 	_CMP_FIELD_MEMCMP (a, b, gateway);
+	_CMP_FIELD_MEMCMP (a, b, pref_src);
+	_CMP_FIELD_MEMCMP (a, b, src);
+	_CMP_FIELD (a, b, src_plen);
 	_CMP_FIELD (a, b, rt_source);
 	_CMP_FIELD (a, b, mss);
 	_CMP_FIELD (a, b, rt_cloned);
+	_CMP_FIELD (a, b, tos);
+	_CMP_FIELD (a, b, lock_window);
+	_CMP_FIELD (a, b, lock_cwnd);
+	_CMP_FIELD (a, b, lock_initcwnd);
+	_CMP_FIELD (a, b, lock_initrwnd);
+	_CMP_FIELD (a, b, lock_mtu);
+	_CMP_FIELD (a, b, window);
+	_CMP_FIELD (a, b, cwnd);
+	_CMP_FIELD (a, b, initcwnd);
+	_CMP_FIELD (a, b, initrwnd);
+	_CMP_FIELD (a, b, mtu);
 	return 0;
 }
 
@@ -4411,28 +4509,27 @@ nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns)
 static gboolean
 _vtr_v4_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric)
 {
-	return nm_platform_ip4_route_add (self,
-	                                  ifindex > 0 ? ifindex : route->rx.ifindex,
-	                                  route->rx.rt_source,
-	                                  route->r4.network,
-	                                  route->rx.plen,
-	                                  route->r4.gateway,
-	                                  route->r4.pref_src,
-	                                  metric >= 0 ? (guint32) metric : route->rx.metric,
-	                                  route->rx.mss);
+	NMPlatformIP4Route rt = route->r4;
+
+	if (ifindex > 0)
+		rt.ifindex = ifindex;
+	if (metric >= 0)
+		rt.metric = metric;
+
+	return nm_platform_ip4_route_add (self, &rt);
 }
 
 static gboolean
 _vtr_v6_route_add (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric)
 {
-	return nm_platform_ip6_route_add (self,
-	                                  ifindex > 0 ? ifindex : route->rx.ifindex,
-	                                  route->rx.rt_source,
-	                                  route->r6.network,
-	                                  route->rx.plen,
-	                                  route->r6.gateway,
-	                                  metric >= 0 ? (guint32) metric : route->rx.metric,
-	                                  route->rx.mss);
+	NMPlatformIP6Route rt = route->r6;
+
+	if (ifindex > 0)
+		rt.ifindex = ifindex;
+	if (metric >= 0)
+		rt.metric = metric;
+
+	return nm_platform_ip6_route_add (self, &rt);
 }
 
 static gboolean
@@ -4479,7 +4576,7 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v4 = {
 	.is_ip4                         = TRUE,
 	.addr_family                    = AF_INET,
 	.sizeof_route                   = sizeof (NMPlatformIP4Route),
-	.route_cmp                      = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b)) nm_platform_ip4_route_cmp,
+	.route_cmp                      = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part)) nm_platform_ip4_route_cmp_full,
 	.route_to_string                = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string,
 	.route_get_all                  = nm_platform_ip4_route_get_all,
 	.route_add                      = _vtr_v4_route_add,
@@ -4492,7 +4589,7 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v6 = {
 	.is_ip4                         = FALSE,
 	.addr_family                    = AF_INET6,
 	.sizeof_route                   = sizeof (NMPlatformIP6Route),
-	.route_cmp                      = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b)) nm_platform_ip6_route_cmp,
+	.route_cmp                      = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part)) nm_platform_ip6_route_cmp_full,
 	.route_to_string                = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string,
 	.route_get_all                  = nm_platform_ip6_route_get_all,
 	.route_add                      = _vtr_v6_route_add,
@@ -4521,9 +4618,9 @@ set_property (GObject *object, guint prop_id,
 				self->_netns = g_object_ref (netns);
 		}
 		break;
-	case PROP_REGISTER_SINGLETON:
+	case PROP_LOG_WITH_PTR:
 		/* construct-only */
-		priv->register_singleton = g_value_get_boolean (value);
+		priv->log_with_ptr = g_value_get_boolean (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -4532,18 +4629,6 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-constructed (GObject *object)
-{
-	NMPlatform *self = NM_PLATFORM (object);
-	NMPlatformPrivate *priv =  NM_PLATFORM_GET_PRIVATE (self);
-
-	G_OBJECT_CLASS (nm_platform_parent_class)->constructed (object);
-
-	if (priv->register_singleton)
-		nm_platform_setup (self);
-}
-
-static void
 nm_platform_init (NMPlatform *self)
 {
 	self->_priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_PLATFORM, NMPlatformPrivate);
@@ -4565,7 +4650,6 @@ nm_platform_class_init (NMPlatformClass *platform_class)
 	g_type_class_add_private (object_class, sizeof (NMPlatformPrivate));
 
 	object_class->set_property = set_property;
-	object_class->constructed = constructed;
 	object_class->finalize = finalize;
 
 	platform_class->wifi_set_powersave = wifi_set_powersave;
@@ -4579,9 +4663,9 @@ nm_platform_class_init (NMPlatformClass *platform_class)
 	                           G_PARAM_STATIC_STRINGS));
 
 	g_object_class_install_property
-	 (object_class, PROP_REGISTER_SINGLETON,
-	     g_param_spec_boolean (NM_PLATFORM_REGISTER_SINGLETON, "", "",
-	                           FALSE,
+	 (object_class, PROP_LOG_WITH_PTR,
+	     g_param_spec_boolean (NM_PLATFORM_LOG_WITH_PTR, "", "",
+	                           TRUE,
 	                           G_PARAM_WRITABLE |
 	                           G_PARAM_CONSTRUCT_ONLY |
 	                           G_PARAM_STATIC_STRINGS));
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 63dbe5a0..43be17fa 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -45,10 +45,12 @@
 /*****************************************************************************/
 
 #define NM_PLATFORM_NETNS_SUPPORT      "netns-support"
-#define NM_PLATFORM_REGISTER_SINGLETON "register-singleton"
+#define NM_PLATFORM_LOG_WITH_PTR       "log-with-ptr"
 
 /*****************************************************************************/
 
+struct udev_device;
+
 /* workaround for older libnl version, that does not define these flags. */
 #ifndef IFA_F_MANAGETEMPADDR
 #define IFA_F_MANAGETEMPADDR 0x100
@@ -91,24 +93,6 @@ typedef enum { /*< skip >*/
 	NM_PLATFORM_ERROR_OPNOTSUPP,
 } NMPlatformError;
 
-
-typedef struct {
-	union {
-		guint8 addr_ptr[1];
-		in_addr_t addr4;
-		struct in6_addr addr6;
-
-		/* NMIPAddr is really a union for IP addresses.
-		 * However, as ethernet addresses fit in here nicely, ruse
-		 * it also for an ethernet MAC address. */
-		guint8 addr_eth[6 /*ETH_ALEN*/];
-	};
-} NMIPAddr;
-
-extern const NMIPAddr nm_ip_addr_zero;
-
-#define NMIPAddrInit { .addr6 = IN6ADDR_ANY_INIT }
-
 #define NM_PLATFORM_LINK_OTHER_NETNS    (-1)
 
 #define __NMPlatformObject_COMMON \
@@ -324,9 +308,20 @@ typedef union {
 	 * of platform users. This flag is internal to track those hidden
 	 * routes. Such a route is not alive, according to nmp_object_is_alive(). */ \
 	bool rt_cloned:1; \
+	bool lock_window:1; \
+	bool lock_cwnd:1; \
+	bool lock_initcwnd:1; \
+	bool lock_initrwnd:1; \
+	bool lock_mtu:1; \
 	\
 	guint32 metric; \
 	guint32 mss; \
+	guint32 tos; \
+	guint32 window; \
+	guint32 cwnd; \
+	guint32 initcwnd; \
+	guint32 initrwnd; \
+	guint32 mtu; \
 	;
 
 typedef struct {
@@ -358,6 +353,9 @@ struct _NMPlatformIP6Route {
 	__NMPlatformIPRoute_COMMON;
 	struct in6_addr network;
 	struct in6_addr gateway;
+	struct in6_addr pref_src;
+	struct in6_addr src;
+	guint8 src_plen;
 };
 
 typedef union {
@@ -376,7 +374,7 @@ typedef struct {
 	gboolean is_ip4;
 	int addr_family;
 	gsize sizeof_route;
-	int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b);
+	int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, gboolean consider_host_part);
 	const char *(*route_to_string) (const NMPlatformIPXRoute *route, char *buf, gsize len);
 	GArray *(*route_get_all) (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags);
 	gboolean (*route_add) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric);
@@ -548,7 +546,7 @@ typedef struct {
 	gboolean (*link_set_noarp) (NMPlatform *, int ifindex);
 
 	const char *(*link_get_udi) (NMPlatform *self, int ifindex);
-	GObject *(*link_get_udev_device) (NMPlatform *self, int ifindex);
+	struct udev_device *(*link_get_udev_device) (NMPlatform *self, int ifindex);
 
 	NMPlatformError (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled);
 	gboolean (*link_set_token) (NMPlatform *, int ifindex, NMUtilsIPv6IfaceId iid);
@@ -559,6 +557,7 @@ typedef struct {
 	                                        size_t *length);
 	NMPlatformError (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length);
 	gboolean (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu);
+	gboolean (*link_set_sriov_num_vfs) (NMPlatform *, int ifindex, guint num_vfs);
 
 	char *   (*link_get_physical_port_id) (NMPlatform *, int ifindex);
 	guint    (*link_get_dev_id) (NMPlatform *, int ifindex);
@@ -571,6 +570,7 @@ typedef struct {
 
 	gboolean (*link_supports_carrier_detect) (NMPlatform *, int ifindex);
 	gboolean (*link_supports_vlans) (NMPlatform *, int ifindex);
+	gboolean (*link_supports_sriov) (NMPlatform *, int ifindex);
 
 	gboolean (*link_enslave) (NMPlatform *, int master, int slave);
 	gboolean (*link_release) (NMPlatform *, int master, int slave);
@@ -667,12 +667,8 @@ typedef struct {
 
 	GArray * (*ip4_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags);
 	GArray * (*ip6_route_get_all) (NMPlatform *, int ifindex, NMPlatformGetRouteFlags flags);
-	gboolean (*ip4_route_add) (NMPlatform *, int ifindex, NMIPConfigSource source,
-	                           in_addr_t network, guint8 plen, in_addr_t gateway,
-	                           in_addr_t pref_src, guint32 metric, guint32 mss);
-	gboolean (*ip6_route_add) (NMPlatform *, int ifindex, NMIPConfigSource source,
-	                           struct in6_addr network, guint8 plen, struct in6_addr gateway,
-	                           guint32 metric, guint32 mss);
+	gboolean (*ip4_route_add) (NMPlatform *, const NMPlatformIP4Route *route);
+	gboolean (*ip6_route_add) (NMPlatform *, const NMPlatformIP6Route *route);
 	gboolean (*ip4_route_delete) (NMPlatform *, int ifindex, in_addr_t network, guint8 plen, guint32 metric);
 	gboolean (*ip6_route_delete) (NMPlatform *, int ifindex, struct in6_addr network, guint8 plen, guint32 metric);
 	const NMPlatformIP4Route *(*ip4_route_get) (NMPlatform *, int ifindex, in_addr_t network, guint8 plen, guint32 metric);
@@ -707,7 +703,6 @@ GType nm_platform_get_type (void);
 
 void nm_platform_setup (NMPlatform *instance);
 NMPlatform *nm_platform_get (void);
-NMPlatform *nm_platform_try_get (void);
 
 #define NM_PLATFORM_GET (nm_platform_get ())
 
@@ -730,6 +725,8 @@ _nm_platform_uint8_inv (guint8 scope)
 	return (guint8) ~scope;
 }
 
+gboolean nm_platform_get_log_with_ptr (NMPlatform *self);
+
 NMPNetns *nm_platform_netns_get (NMPlatform *self);
 gboolean nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns);
 
@@ -804,7 +801,7 @@ gboolean nm_platform_link_set_noarp (NMPlatform *self, int ifindex);
 
 const char *nm_platform_link_get_udi (NMPlatform *self, int ifindex);
 
-GObject *nm_platform_link_get_udev_device (NMPlatform *self, int ifindex);
+struct udev_device *nm_platform_link_get_udev_device (NMPlatform *self, int ifindex);
 
 NMPlatformError nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled);
 gboolean nm_platform_link_set_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid);
@@ -812,6 +809,7 @@ gboolean nm_platform_link_set_ipv6_token (NMPlatform *self, int ifindex, NMUtils
 gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, guint8 *buf, size_t *length);
 NMPlatformError nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length);
 gboolean nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu);
+gboolean nm_platform_link_set_sriov_num_vfs (NMPlatform *self, int ifindex, guint num_vfs);
 
 char    *nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex);
 guint    nm_platform_link_get_dev_id (NMPlatform *self, int ifindex);
@@ -824,6 +822,7 @@ gboolean nm_platform_link_get_driver_info (NMPlatform *self,
 
 gboolean nm_platform_link_supports_carrier_detect (NMPlatform *self, int ifindex);
 gboolean nm_platform_link_supports_vlans (NMPlatform *self, int ifindex);
+gboolean nm_platform_link_supports_sriov (NMPlatform *self, int ifindex);
 
 gboolean nm_platform_link_enslave (NMPlatform *self, int master, int slave);
 gboolean nm_platform_link_release (NMPlatform *self, int master, int slave);
@@ -969,12 +968,8 @@ const NMPlatformIP4Route *nm_platform_ip4_route_get (NMPlatform *self, int ifind
 const NMPlatformIP6Route *nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric);
 GArray *nm_platform_ip4_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags);
 GArray *nm_platform_ip6_route_get_all (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags);
-gboolean nm_platform_ip4_route_add (NMPlatform *self, int ifindex, NMIPConfigSource source,
-                                    in_addr_t network, guint8 plen, in_addr_t gateway,
-                                    in_addr_t pref_src, guint32 metric, guint32 mss);
-gboolean nm_platform_ip6_route_add (NMPlatform *self, int ifindex, NMIPConfigSource source,
-                                    struct in6_addr network, guint8 plen, struct in6_addr gateway,
-                                    guint32 metric, guint32 mss);
+gboolean nm_platform_ip4_route_add (NMPlatform *self, const NMPlatformIP4Route *route);
+gboolean nm_platform_ip6_route_add (NMPlatform *self, const NMPlatformIP6Route *route);
 gboolean nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric);
 gboolean nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric);
 
@@ -1011,8 +1006,20 @@ int nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVla
 int nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b);
 int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b);
 int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b);
-int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b);
-int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b);
+int nm_platform_ip4_route_cmp_full (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, gboolean consider_host_part);
+int nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, gboolean consider_host_part);
+
+static inline int
+nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b)
+{
+	return nm_platform_ip4_route_cmp_full (a, b, TRUE);
+}
+
+static inline int
+nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b)
+{
+	return nm_platform_ip6_route_cmp_full (a, b, TRUE);
+}
 
 gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self);
 gboolean nm_platform_check_support_user_ipv6ll (NMPlatform *self);
diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c
index c9c6850d..4acd4761 100644
--- a/src/platform/nmp-netns.c
+++ b/src/platform/nmp-netns.c
@@ -75,7 +75,7 @@ __ns_types_to_str (int ns_types, int ns_types_already_set, char *buf, gsize len)
             NMPNetns *_netns = (netns); \
             char _sbuf[20]; \
             \
-            _nm_log (_level, _NMLOG_DOMAIN, 0, \
+            _nm_log (_level, _NMLOG_DOMAIN, 0, NULL, NULL, \
                      "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      _NMLOG_PREFIX_NAME, \
                      (_netns ? nm_sprintf_buf (_sbuf, "[%p]", _netns) : "") \
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index 1503ca9a..ecec8f0f 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.c
@@ -24,6 +24,7 @@
 
 #include <unistd.h>
 #include <linux/rtnetlink.h>
+#include <libudev.h>
 
 #include "nm-utils.h"
 
@@ -40,7 +41,7 @@
         if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
             const NMPObject *const __obj = (obj); \
             \
-            _nm_log (__level, _NMLOG_DOMAIN, 0, \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \
                      "nmp-object[%p/%s]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      __obj, \
                      (__obj ? NMP_OBJECT_GET_CLASS (__obj)->obj_type_name : "???") \
@@ -48,10 +49,6 @@
         } \
     } G_STMT_END
 
-/* logging to trace object lifetime and references.
- * Disabled by default. */
-#define _LOGr(...) G_STMT_START { if (FALSE) { _LOGt (__VA_ARGS__); } } G_STMT_END
-
 /*****************************************************************************/
 
 struct _NMPCache {
@@ -125,14 +122,14 @@ _vlan_xgress_qos_mappings_cpy (guint *dst_n_map,
 /*****************************************************************************/
 
 static const char *
-_link_get_driver (GUdevDevice *udev_device, const char *kind, int ifindex)
+_link_get_driver (struct udev_device *udevice, const char *kind, int ifindex)
 {
 	const char *driver = NULL;
 
 	nm_assert (kind == g_intern_string (kind));
 
-	if (udev_device) {
-		driver = nmp_utils_udev_get_driver (udev_device);
+	if (udevice) {
+		driver = nmp_utils_udev_get_driver (udevice);
 		if (driver)
 			return driver;
 	}
@@ -214,8 +211,6 @@ nmp_object_ref (NMPObject *obj)
 	g_return_val_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT, NULL);
 	obj->_ref_count++;
 
-	_LOGr (obj, "ref: %d", obj->_ref_count);
-
 	return obj;
 }
 
@@ -225,9 +220,6 @@ nmp_object_unref (NMPObject *obj)
 	if (obj) {
 		g_return_if_fail (obj->_ref_count > 0);
 		g_return_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT);
-		_LOGr (obj, "%s: %d",
-		       obj->_ref_count <= 1 ? "destroy" : "unref",
-		       obj->_ref_count - 1);
 		if (--obj->_ref_count <= 0) {
 			const NMPClass *klass = obj->_class;
 
@@ -242,7 +234,10 @@ nmp_object_unref (NMPObject *obj)
 static void
 _vt_cmd_obj_dispose_link (NMPObject *obj)
 {
-	g_clear_object (&obj->_link.udev.device);
+	if (obj->_link.udev.device) {
+		udev_device_unref (obj->_link.udev.device);
+		obj->_link.udev.device = NULL;
+	}
 	nmp_object_unref (obj->_link.netlink.lnk);
 }
 
@@ -265,7 +260,6 @@ _nmp_object_new_from_class (const NMPClass *klass)
 	obj = g_slice_alloc0 (klass->sizeof_data + G_STRUCT_OFFSET (NMPObject, object));
 	obj->_class = klass;
 	obj->_ref_count = 1;
-	_LOGr (obj, "new");
 	return obj;
 }
 
@@ -495,7 +489,7 @@ _vt_cmd_obj_to_string_link (const NMPObject *obj, NMPObjectToStringMode to_strin
 static const char *
 _vt_cmd_obj_to_string_lnk_vlan (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size)
 {
-	const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj);
+	const NMPClass *klass;
 	char buf2[sizeof (_nm_utils_to_string_buffer)];
 	char *b;
 	gsize l;
@@ -619,8 +613,7 @@ _vt_cmd_obj_cmp_link (const NMPObject *obj1, const NMPObject *obj2)
 			return 1;
 
 		/* Only compare based on pointer values. That is ugly because it's not a
-		 * stable sort order, but probably udev gives us always the same GUdevDevice
-		 * instance.
+		 * stable sort order.
 		 *
 		 * Have this check as very last. */
 		return (obj1->_link.udev.device < obj2->_link.udev.device) ? -1 : 1;
@@ -687,15 +680,17 @@ _vt_cmd_obj_copy_link (NMPObject *dst, const NMPObject *src)
 {
 	if (dst->_link.udev.device != src->_link.udev.device) {
 		if (src->_link.udev.device)
-			g_object_ref (src->_link.udev.device);
+			udev_device_ref (src->_link.udev.device);
 		if (dst->_link.udev.device)
-			g_object_unref (dst->_link.udev.device);
+			udev_device_unref (dst->_link.udev.device);
+		dst->_link.udev.device = src->_link.udev.device;
 	}
 	if (dst->_link.netlink.lnk != src->_link.netlink.lnk) {
 		if (src->_link.netlink.lnk)
 			nmp_object_ref (src->_link.netlink.lnk);
 		if (dst->_link.netlink.lnk)
 			nmp_object_unref (dst->_link.netlink.lnk);
+		dst->_link.netlink.lnk = src->_link.netlink.lnk;
 	}
 	dst->_link = src->_link;
 }
@@ -810,12 +805,17 @@ _vt_cmd_plobj_id_equal (ip4_route, NMPlatformIP4Route,
                            obj1->ifindex == obj2->ifindex
                         && obj1->plen == obj2->plen
                         && obj1->metric == obj2->metric
-                        && obj1->network == obj2->network);
+                        && nm_utils_ip4_address_clear_host_address (obj1->network, obj1->plen) == nm_utils_ip4_address_clear_host_address (obj2->network, obj2->plen));
 _vt_cmd_plobj_id_equal (ip6_route, NMPlatformIP6Route,
                            obj1->ifindex == obj2->ifindex
                         && obj1->plen == obj2->plen
                         && obj1->metric == obj2->metric
-                        && IN6_ARE_ADDR_EQUAL( &obj1->network, &obj2->network));
+                        && ({
+                                struct in6_addr n1, n2;
+
+                                IN6_ARE_ADDR_EQUAL(nm_utils_ip6_address_clear_host_address (&n1, &obj1->network, obj1->plen),
+                                                   nm_utils_ip6_address_clear_host_address (&n2, &obj2->network, obj2->plen));
+                            }));
 
 guint
 nmp_object_id_hash (const NMPObject *obj)
@@ -869,14 +869,17 @@ _vt_cmd_plobj_id_hash (ip4_route, NMPlatformIP4Route, {
 	hash = hash      + ((guint) obj->ifindex);
 	hash = hash * 33 + ((guint) obj->plen);
 	hash = hash * 33 + ((guint) obj->metric);
-	hash = hash * 33 + ((guint) obj->network);
+	hash = hash * 33 + ((guint) nm_utils_ip4_address_clear_host_address (obj->network, obj->plen));
 })
 _vt_cmd_plobj_id_hash (ip6_route, NMPlatformIP6Route, {
 	hash = (guint) 3999787007u;
 	hash = hash      + ((guint) obj->ifindex);
 	hash = hash * 33 + ((guint) obj->plen);
 	hash = hash * 33 + ((guint) obj->metric);
-	hash = hash * 33 + _id_hash_ip6_addr (&obj->network);
+	hash = hash * 33 + ({
+	                        struct in6_addr n1;
+	                        _id_hash_ip6_addr (nm_utils_ip6_address_clear_host_address (&n1, &obj->network, obj->plen));
+	                    });
 })
 
 gboolean
@@ -985,11 +988,6 @@ nmp_cache_id_hash (const NMPCacheId *id)
 	guint hash = 5381;
 	guint i, n;
 
-	/* for hashing we only iterate over the actually set bytes and skip the
-	 * zero padding at the end (which depends on the type of the id).
-	 *
-	 * For the equal implementation, we don't care about that and compare the
-	 * entire NMPCacheId sized struct. */
 	n = _nmp_cache_id_size_by_type (id->_id_type);
 	for (i = 0; i < n; i++)
 		hash = ((hash << 5) + hash) + ((char *) id)[i]; /* hash * 33 + c */
@@ -1033,6 +1031,20 @@ _nmp_cache_id_init (NMPCacheId *id, NMPCacheIdType id_type)
 	 * all structs have the packed attribute, there are no holes
 	 * due to alignment, and it becomes simple for nmp_cache_id_init_*()
 	 * to ensure that all fields are set. */
+
+#if NM_MORE_ASSERTS
+	nm_assert (id);
+	{
+		guint i;
+
+		/* initialized with some bogus canary to hopefully detect when we miss
+		 * to initialize a field of the cache-id. */
+		for (i = 0; i < sizeof (*id); i++) {
+			((char *) id)[i] = GPOINTER_TO_UINT (id) ^ i;
+		}
+	}
+#endif
+
 	id->_id_type = id_type;
 }
 
@@ -1533,13 +1545,17 @@ nmp_cache_lookup_link_full (const NMPCache *cache,
 		    && strlen (ifname) <= sizeof (cache_id.link_by_ifname.ifname_short)) {
 			p_cache_id = nmp_cache_id_init_link_by_ifname (&cache_id, ifname);
 			ifname = NULL;
-		} else
+		} else {
 			p_cache_id = nmp_cache_id_init_object_type (&cache_id, NMP_OBJECT_TYPE_LINK, visible_only);
+			visible_only = FALSE;
+		}
 
 		list = nmp_cache_lookup_multi (cache, p_cache_id, &len);
 		for (i = 0; i < len; i++) {
 			obj = NMP_OBJECT_UP_CAST (list[i]);
 
+			if (visible_only && !nmp_object_is_visible (obj))
+				continue;
 			if (link_type != NM_LINK_TYPE_NONE && obj->link.type != link_type)
 				continue;
 			if (ifname && strcmp (ifname, obj->link.name))
@@ -1857,8 +1873,8 @@ nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj,
 				_nmp_object_fixup_link_master_connected (obj, cache);
 
 				/* Merge the netlink parts with what we have from udev. */
-				g_clear_object (&obj->_link.udev.device);
-				obj->_link.udev.device = old->_link.udev.device ? g_object_ref (old->_link.udev.device) : NULL;
+				udev_device_unref (obj->_link.udev.device);
+				obj->_link.udev.device = old->_link.udev.device ? udev_device_ref (old->_link.udev.device) : NULL;
 				_nmp_object_fixup_link_udev_fields (obj, cache->use_udev);
 			}
 		} else
@@ -1883,7 +1899,7 @@ nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj,
 }
 
 NMPCacheOpsType
-nmp_cache_update_link_udev (NMPCache *cache, int ifindex, GUdevDevice *udev_device, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data)
+nmp_cache_update_link_udev (NMPCache *cache, int ifindex, struct udev_device *udevice, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data)
 {
 	NMPObject *old;
 	nm_auto_nmpobj NMPObject *obj = NULL;
@@ -1896,12 +1912,12 @@ nmp_cache_update_link_udev (NMPCache *cache, int ifindex, GUdevDevice *udev_devi
 		*out_was_visible = FALSE;
 
 	if (!old) {
-		if (!udev_device)
+		if (!udevice)
 			return NMP_CACHE_OPS_UNCHANGED;
 
 		obj = nmp_object_new (NMP_OBJECT_TYPE_LINK, NULL);
 		obj->link.ifindex = ifindex;
-		obj->_link.udev.device = g_object_ref (udev_device);
+		obj->_link.udev.device = udev_device_ref (udevice);
 
 		_nmp_object_fixup_link_udev_fields (obj, cache->use_udev);
 
@@ -1922,10 +1938,10 @@ nmp_cache_update_link_udev (NMPCache *cache, int ifindex, GUdevDevice *udev_devi
 		if (out_was_visible)
 			*out_was_visible = nmp_object_is_visible (old);
 
-		if (old->_link.udev.device == udev_device)
+		if (old->_link.udev.device == udevice)
 			return NMP_CACHE_OPS_UNCHANGED;
 
-		if (!udev_device && !old->_link.netlink.is_in_netlink) {
+		if (!udevice && !old->_link.netlink.is_in_netlink) {
 			/* the update would make @old invalid. Remove it. */
 			if (pre_hook)
 				pre_hook (cache, old, NULL, NMP_CACHE_OPS_REMOVED, user_data);
@@ -1935,8 +1951,8 @@ nmp_cache_update_link_udev (NMPCache *cache, int ifindex, GUdevDevice *udev_devi
 
 		obj = nmp_object_clone (old, FALSE);
 
-		g_clear_object (&obj->_link.udev.device);
-		obj->_link.udev.device = udev_device ? g_object_ref (udev_device) : NULL;
+		udev_device_unref (obj->_link.udev.device);
+		obj->_link.udev.device = udevice ? udev_device_ref (udevice) : NULL;
 
 		_nmp_object_fixup_link_udev_fields (obj, cache->use_udev);
 
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index dd11b985..b69680f6 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.h
@@ -21,11 +21,11 @@
 #ifndef __NMP_OBJECT_H__
 #define __NMP_OBJECT_H__
 
-#include <gudev/gudev.h>
-
 #include "nm-platform.h"
 #include "nm-multi-index.h"
 
+struct udev_device;
+
 typedef enum { /*< skip >*/
 	NMP_OBJECT_TO_STRING_ID,
 	NMP_OBJECT_TO_STRING_PUBLIC,
@@ -186,7 +186,23 @@ typedef struct {
 	} netlink;
 
 	struct {
-		GUdevDevice *device;
+		/* note that "struct udev_device" references the library context
+		 * "struct udev", but doesn't own it.
+		 *
+		 * Hence, the udev.device shall not be used after the library
+		 * context is is destroyed.
+		 *
+		 * In case of NMPObjectLink instances that you obtained from the
+		 * platform cache, that means that you shall no keep references
+		 * to those instances that outlife the NMPlatform instance.
+		 *
+		 * In practice, the requirement is less strict and you'll be even
+		 * fine if the platform instance (and the "struct udev" instance)
+		 * are already destroyed while you still hold onto a reference to
+		 * the NMPObjectLink instance. Just don't make use of udev functions
+		 * that cause access to the udev library context.
+		 */
+		struct udev_device *device;
 	} udev;
 } NMPObjectLink;
 
@@ -307,7 +323,7 @@ NMP_CLASS_IS_VALID (const NMPClass *klass)
 {
 	return klass >= &_nmp_classes[0]
 	    && klass <= &_nmp_classes[G_N_ELEMENTS (_nmp_classes)]
-	    && ((((char *) klass) - ((char *) NULL)) % (&_nmp_classes[1] - &_nmp_classes[0])) == 0;
+	    && ((((char *) klass) - ((char *) _nmp_classes)) % (sizeof (_nmp_classes[0]))) == 0;
 }
 
 #define NMP_REF_COUNT_STACKINIT (G_MAXINT)
@@ -442,7 +458,7 @@ void ASSERT_nmp_cache_is_consistent (const NMPCache *cache);
 NMPCacheOpsType nmp_cache_remove (NMPCache *cache, const NMPObject *obj, gboolean equals_by_ptr, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
 NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
 NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
-NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, GUdevDevice *udev_device, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
+NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, struct udev_device *udevice, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
 NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifindex, NMPObject **out_obj, gboolean *out_was_visible, NMPCachePreHook pre_hook, gpointer user_data);
 
 NMPCache *nmp_cache_new (gboolean use_udev);
diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c
index 4ac49298..71a92cbf 100644
--- a/src/platform/tests/test-cleanup.c
+++ b/src/platform/tests/test-cleanup.c
@@ -65,12 +65,12 @@ test_cleanup_internal (void)
 	/* Add routes and addresses */
 	g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, plen4, addr4, lifetime, preferred, 0, NULL));
 	g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr6, plen6, in6addr_any, lifetime, preferred, flags));
-	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss));
-	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss));
-	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway4, 0, metric, mss));
-	g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway6, 128, in6addr_any, metric, mss));
-	g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network6, plen6, gateway6, metric, mss));
-	g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway6, metric, mss));
+	nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss);
+	nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss);
+	nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway4, 0, metric, mss);
+	nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway6, 128, in6addr_any, in6addr_any, metric, mss);
+	nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network6, plen6, gateway6, in6addr_any, metric, mss);
+	nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway6, in6addr_any, metric, mss);
 
 	addresses4 = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex);
 	addresses6 = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, ifindex);
diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c
index 0e42a727..04db862d 100644
--- a/src/platform/tests/test-common.c
+++ b/src/platform/tests/test-common.c
@@ -783,6 +783,54 @@ nmtstp_ip6_address_add (NMPlatform *platform,
 	                 NULL);
 }
 
+void nmtstp_ip4_route_add (NMPlatform *platform,
+                           int ifindex,
+                           NMIPConfigSource source,
+                           in_addr_t network,
+                           guint8 plen,
+                           in_addr_t gateway,
+                           in_addr_t pref_src,
+                           guint32 metric,
+                           guint32 mss)
+{
+	NMPlatformIP4Route route = { };
+
+	route.ifindex = ifindex;
+	route.rt_source = source;
+	route.network = network;
+	route.plen = plen;
+	route.gateway = gateway;
+	route.pref_src = pref_src;
+	route.metric = metric;
+	route.mss = mss;
+
+	g_assert (nm_platform_ip4_route_add (platform, &route));
+}
+
+void nmtstp_ip6_route_add (NMPlatform *platform,
+                           int ifindex,
+                           NMIPConfigSource source,
+                           struct in6_addr network,
+                           guint8 plen,
+                           struct in6_addr gateway,
+                           struct in6_addr pref_src,
+                           guint32 metric,
+                           guint32 mss)
+{
+	NMPlatformIP6Route route = { };
+
+	route.ifindex = ifindex;
+	route.rt_source = source;
+	route.network = network;
+	route.plen = plen;
+	route.gateway = gateway;
+	route.pref_src = pref_src;
+	route.metric = metric;
+	route.mss = mss;
+
+	g_assert (nm_platform_ip6_route_add (platform, &route));
+}
+
 /*****************************************************************************/
 
 static void
@@ -1547,7 +1595,7 @@ nmtstp_namespace_get_fd_for_process (pid_t pid, const char *ns_name)
 	g_return_val_if_fail (pid > 0, 0);
 	g_return_val_if_fail (ns_name && ns_name[0] && strlen (ns_name) < 50, 0);
 
-	nm_sprintf_buf (p, "/proc/%lu/ns/%s", (long unsigned) pid, ns_name);
+	nm_sprintf_buf (p, "/proc/%lu/ns/%s", (unsigned long) pid, ns_name);
 
 	return open(p, O_RDONLY | O_CLOEXEC);
 }
diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h
index 48a41de6..a52a5db5 100644
--- a/src/platform/tests/test-common.h
+++ b/src/platform/tests/test-common.h
@@ -26,7 +26,7 @@
         if (nm_logging_enabled (__level, __domain)) { \
             gint64 _ts = nm_utils_get_monotonic_timestamp_ns (); \
             \
-            _nm_log (__level, __domain, 0, \
+            _nm_log (__level, __domain, 0, NULL, NULL, \
                      "%s[%ld.%09ld]: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      _NMLOG_PREFIX_NAME, \
                      (long) (_ts / NM_UTILS_NS_PER_SECOND), \
@@ -167,6 +167,26 @@ void nmtstp_ip6_address_del (NMPlatform *platform,
                              struct in6_addr address,
                              int plen);
 
+void nmtstp_ip4_route_add (NMPlatform *platform,
+                           int ifindex,
+                           NMIPConfigSource source,
+                           in_addr_t network,
+                           guint8 plen,
+                           in_addr_t gateway,
+                           in_addr_t pref_src,
+                           guint32 metric,
+                           guint32 mss);
+
+void nmtstp_ip6_route_add (NMPlatform *platform,
+                           int ifindex,
+                           NMIPConfigSource source,
+                           struct in6_addr network,
+                           guint8 plen,
+                           struct in6_addr gateway,
+                           struct in6_addr pref_src,
+                           guint32 metric,
+                           guint32 mss);
+
 /*****************************************************************************/
 
 const NMPlatformLink *nmtstp_link_get_typed (NMPlatform *platform, int ifindex, const char *name, NMLinkType link_type);
diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c
index 658aad26..2ccfac7d 100644
--- a/src/platform/tests/test-general.c
+++ b/src/platform/tests/test-general.c
@@ -35,7 +35,7 @@ test_init_linux_platform (void)
 {
 	gs_unref_object NMPlatform *platform = NULL;
 
-	platform = nm_linux_platform_new (NM_PLATFORM_NETNS_SUPPORT_DEFAULT);
+	platform = nm_linux_platform_new (TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT);
 }
 
 /*****************************************************************************/
@@ -46,7 +46,7 @@ test_link_get_all (void)
 	gs_unref_object NMPlatform *platform = NULL;
 	gs_unref_array GArray *links = NULL;
 
-	platform = nm_linux_platform_new (NM_PLATFORM_NETNS_SUPPORT_DEFAULT);
+	platform = nm_linux_platform_new (TRUE, NM_PLATFORM_NETNS_SUPPORT_DEFAULT);
 
 	links = nm_platform_link_get_all (platform);
 }
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index c2b3de13..ed435567 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -1900,7 +1900,7 @@ _test_netns_create_platform (void)
 	netns = nmp_netns_new ();
 	g_assert (NMP_IS_NETNS (netns));
 
-	platform = nm_linux_platform_new (TRUE);
+	platform = nm_linux_platform_new (TRUE, TRUE);
 	g_assert (NM_IS_LINUX_PLATFORM (platform));
 
 	nmp_netns_pop (netns);
@@ -1961,7 +1961,7 @@ test_netns_general (gpointer fixture, gconstpointer test_data)
 	if (_test_netns_check_skip ())
 		return;
 
-	platform_1 = nm_linux_platform_new (TRUE);
+	platform_1 = nm_linux_platform_new (TRUE, TRUE);
 	platform_2 = _test_netns_create_platform ();
 
 	/* add some dummy devices. The "other-*" devices are there to bump the ifindex */
@@ -2061,7 +2061,7 @@ test_netns_set_netns (gpointer fixture, gconstpointer test_data)
 	if (_test_netns_check_skip ())
 		return;
 
-	platforms[0] = platform_0 = nm_linux_platform_new (TRUE);
+	platforms[0] = platform_0 = nm_linux_platform_new (TRUE, TRUE);
 	platforms[1] = platform_1 = _test_netns_create_platform ();
 	platforms[2] = platform_2 = _test_netns_create_platform ();
 
@@ -2156,7 +2156,7 @@ test_netns_push (gpointer fixture, gconstpointer test_data)
 	if (_test_netns_check_skip ())
 		return;
 
-	pl[0].platform = platform_0 = nm_linux_platform_new (TRUE);
+	pl[0].platform = platform_0 = nm_linux_platform_new (TRUE, TRUE);
 	pl[1].platform = platform_1 = _test_netns_create_platform ();
 	pl[2].platform = platform_2 = _test_netns_create_platform ();
 
@@ -2288,7 +2288,7 @@ test_netns_bind_to_path (gpointer fixture, gconstpointer test_data)
 	if (_test_netns_check_skip ())
 		return;
 
-	platforms[0] = platform_0 = nm_linux_platform_new (TRUE);
+	platforms[0] = platform_0 = nm_linux_platform_new (TRUE, TRUE);
 	platforms[1] = platform_1 = _test_netns_create_platform ();
 	platforms[2] = platform_2 = _test_netns_create_platform ();
 
@@ -2404,8 +2404,6 @@ test_sysctl_rename (void)
 		g_assert_cmpint (ifindex[0], ==, (gint32) nm_platform_sysctl_get_int32 (PL, NMP_SYSCTL_PATHID_NETDIR (dirfd, s ?: "<unknown>", "ifindex"), -1));
 		break;
 	}
-	default:
-		g_assert_not_reached ();
 	}
 
 	nm_platform_process_events (PL);
@@ -2435,7 +2433,7 @@ test_sysctl_netns_switch (void)
 	if (_test_netns_check_skip ())
 		return;
 
-	platforms[0] = platform_0 = nm_linux_platform_new (TRUE);
+	platforms[0] = platform_0 = nm_linux_platform_new (TRUE, TRUE);
 	platforms[1] = platform_1 = _test_netns_create_platform ();
 	platforms[2] = platform_2 = _test_netns_create_platform ();
 	PL = platforms[nmtst_get_rand_int () % 3];
diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c
index f7b209de..42dfc572 100644
--- a/src/platform/tests/test-nmp-object.c
+++ b/src/platform/tests/test-nmp-object.c
@@ -20,7 +20,10 @@
 
 #include "nm-default.h"
 
+#include <libudev.h>
+
 #include "platform/nmp-object.h"
+#include "nm-utils/nm-udev-utils.h"
 
 #include "nm-test-utils-core.h"
 
@@ -159,7 +162,7 @@ _nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj,
 
 	obj_old = nmp_cache_lookup_link (cache, obj->object.ifindex);
 	if (obj_old && obj_old->_link.udev.device)
-		obj_clone->_link.udev.device = g_object_ref (obj_old->_link.udev.device);
+		obj_clone->_link.udev.device = udev_device_ref (obj_old->_link.udev.device);
 	_nmp_object_fixup_link_udev_fields (obj_clone, nmp_cache_use_udev_get (cache));
 
 	g_assert (cache);
@@ -219,8 +222,8 @@ test_cache_link (void)
 	NMPObject objs1;
 	gboolean was_visible;
 	NMPCacheId cache_id_storage;
-	GUdevDevice *udev_device_2 = g_list_nth_data (global.udev_devices, 0);
-	GUdevDevice *udev_device_3 = g_list_nth_data (global.udev_devices, 0);
+	struct udev_device *udev_device_2 = g_list_nth_data (global.udev_devices, 0);
+	struct udev_device *udev_device_3 = g_list_nth_data (global.udev_devices, 0);
 	NMPCacheOpsType ops_type;
 
 	cache = nmp_cache_new (nmtst_get_rand_int () % 2);
@@ -390,23 +393,40 @@ int
 main (int argc, char **argv)
 {
 	int result;
-	gs_unref_object GUdevClient *udev_client = NULL;
+	NMUdevClient *udev_client;
 
 	nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT");
 
-	udev_client = g_udev_client_new ((const char *[]) { "net", NULL });
+	udev_client = nm_udev_client_new ((const char *[]) { "net", NULL },
+	                                  NULL, NULL);
 	{
-		gs_unref_object GUdevEnumerator *udev_enumerator = g_udev_enumerator_new (udev_client);
+		struct udev_enumerate *enumerator;
+		struct udev_list_entry *devices, *l;
 
-		g_udev_enumerator_add_match_subsystem (udev_enumerator, "net");
+		enumerator = nm_udev_client_enumerate_new (udev_client);
 
 		/* Demand that the device is initialized (udev rules ran,
 		 * device has a stable name now) in case udev is running
 		 * (not in a container). */
 		if (access ("/sys", W_OK) == 0)
-			g_udev_enumerator_add_match_is_initialized (udev_enumerator);
+			udev_enumerate_add_match_is_initialized (enumerator);
+
+		udev_enumerate_scan_devices (enumerator);
+
+		devices = udev_enumerate_get_list_entry (enumerator);
+		for (l = devices; l != NULL; l = udev_list_entry_get_next (l)) {
+			struct udev_device *udevice;
+
+			udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerator),
+			                                        udev_list_entry_get_name (l));
+			if (udevice == NULL)
+				continue;
 
-		global.udev_devices = g_udev_enumerator_execute (udev_enumerator);
+			global.udev_devices = g_list_prepend (global.udev_devices, udevice);
+		}
+		global.udev_devices = g_list_reverse (global.udev_devices);
+
+		udev_enumerate_unref (enumerator);
 	}
 
 	g_test_add_func ("/nmp-object/cache_link", test_cache_link);
@@ -414,10 +434,12 @@ main (int argc, char **argv)
 	result = g_test_run ();
 
 	while (global.udev_devices) {
-		g_object_unref (global.udev_devices->data);
+		udev_device_unref (global.udev_devices->data);
 		global.udev_devices = g_list_remove (global.udev_devices, global.udev_devices->data);
 	}
 
+	nm_udev_client_unref (udev_client);
+
 	return result;
 }
 
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
index 59a05bd4..6862f13e 100644
--- a/src/platform/tests/test-route.c
+++ b/src/platform/tests/test-route.c
@@ -94,7 +94,7 @@ test_ip4_route_metric0 (void)
 	nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric);
 
 	/* add the first route */
-	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss));
+	nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss);
 	accept_signal (route_added);
 
 	nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0);
@@ -108,7 +108,7 @@ test_ip4_route_metric0 (void)
 	nmtstp_assert_ip4_route_exists (NULL, TRUE,  DEVICE_NAME, network, plen, metric);
 
 	/* add the second route */
-	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss));
+	nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss);
 	accept_signal (route_added);
 
 	nmtstp_assert_ip4_route_exists (NULL, TRUE,  DEVICE_NAME, network, plen, 0);
@@ -160,27 +160,27 @@ test_ip4_route (void)
 	inet_pton (AF_INET, "198.51.100.1", &gateway);
 
 	/* Add route to gateway */
-	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 32, INADDR_ANY, 0, metric, mss));
+	nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 32, INADDR_ANY, 0, metric, mss);
 	accept_signal (route_added);
 
 	/* Add route */
 	nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric);
-	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss));
+	nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss);
 	nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric);
 	accept_signal (route_added);
 
 	/* Add route again */
-	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss));
+	nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss);
 	accept_signals (route_changed, 0, 1);
 
 	/* Add default route */
 	nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, 0, 0, metric);
-	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss));
+	nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss);
 	nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, 0, 0, metric);
 	accept_signal (route_added);
 
 	/* Add default route again */
-	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss));
+	nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss);
 	accept_signals (route_changed, 0, 1);
 
 	/* Test route listing */
@@ -222,6 +222,14 @@ test_ip4_route (void)
 	/* Remove route again */
 	g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric));
 
+	/* Remove default route */
+	g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, 0, 0, metric));
+	accept_signal (route_removed);
+
+	/* Remove route to gateway */
+	g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, gateway, 32, metric));
+	accept_signal (route_removed);
+
 	free_signal (route_added);
 	free_signal (route_changed);
 	free_signal (route_removed);
@@ -238,36 +246,55 @@ test_ip6_route (void)
 	NMPlatformIP6Route rts[3];
 	struct in6_addr network;
 	guint8 plen = 64;
-	struct in6_addr gateway;
+	struct in6_addr gateway, pref_src;
 	/* Choose a high metric so that we hopefully don't conflict. */
 	int metric = 22987;
 	int mss = 1000;
 
 	inet_pton (AF_INET6, "2001:db8:a:b:0:0:0:0", &network);
 	inet_pton (AF_INET6, "2001:db8:c:d:1:2:3:4", &gateway);
+	inet_pton (AF_INET6, "::42", &pref_src);
+
+	g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, pref_src, 128, in6addr_any,
+	                                       NM_PLATFORM_LIFETIME_PERMANENT, NM_PLATFORM_LIFETIME_PERMANENT, 0));
+	accept_signals (route_added, 0, 1);
+
+	/* Wait that the address becomes non-tentative.  Dummy interfaces are NOARP
+	 * and thus don't do DAD, but the kernel sets the address as tentative for a
+	 * small amount of time, which prevents the immediate addition of the route
+	 * with RTA_PREFSRC */
+	NMTST_WAIT_ASSERT (200, {
+		const NMPlatformIP6Address *plt_addr;
+
+		nmtstp_wait_for_signal (NM_PLATFORM_GET, 50);
+		nm_platform_process_events (NM_PLATFORM_GET);
+		plt_addr = nm_platform_ip6_address_get (NM_PLATFORM_GET, ifindex, pref_src, 128);
+		if (plt_addr && !NM_FLAGS_HAS (plt_addr->n_ifa_flags, IFA_F_TENTATIVE))
+			break;
+	});
 
 	/* Add route to gateway */
-	g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 128, in6addr_any, metric, mss));
+	nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway, 128, in6addr_any, in6addr_any, metric, mss);
 	accept_signal (route_added);
 
 	/* Add route */
 	g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric));
-	g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss));
+	nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, pref_src, metric, mss);
 	g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric));
 	accept_signal (route_added);
 
 	/* Add route again */
-	g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, metric, mss));
+	nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, pref_src, metric, mss);
 	accept_signals (route_changed, 0, 1);
 
 	/* Add default route */
 	g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric));
-	g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss));
+	nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, in6addr_any, metric, mss);
 	g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric));
 	accept_signal (route_added);
 
 	/* Add default route again */
-	g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, metric, mss));
+	nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, in6addr_any, metric, mss);
 	accept_signals (route_changed, 0, 1);
 
 	/* Test route listing */
@@ -278,6 +305,7 @@ test_ip6_route (void)
 	rts[0].plen = 128;
 	rts[0].ifindex = ifindex;
 	rts[0].gateway = in6addr_any;
+	rts[0].pref_src = in6addr_any;
 	rts[0].metric = nm_utils_ip6_route_metric_normalize (metric);
 	rts[0].mss = mss;
 	rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
@@ -285,6 +313,7 @@ test_ip6_route (void)
 	rts[1].plen = plen;
 	rts[1].ifindex = ifindex;
 	rts[1].gateway = gateway;
+	rts[1].pref_src = pref_src;
 	rts[1].metric = nm_utils_ip6_route_metric_normalize (metric);
 	rts[1].mss = mss;
 	rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
@@ -292,6 +321,7 @@ test_ip6_route (void)
 	rts[2].plen = 0;
 	rts[2].ifindex = ifindex;
 	rts[2].gateway = gateway;
+	rts[2].pref_src = in6addr_any;
 	rts[2].metric = nm_utils_ip6_route_metric_normalize (metric);
 	rts[2].mss = mss;
 	g_assert_cmpint (routes->len, ==, 3);
@@ -306,6 +336,14 @@ test_ip6_route (void)
 	/* Remove route again */
 	g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric));
 
+	/* Remove default route */
+	g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric));
+	accept_signal (route_removed);
+
+	/* Remove route to gateway */
+	g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, gateway, 128, metric));
+	accept_signal (route_removed);
+
 	free_signal (route_added);
 	free_signal (route_changed);
 	free_signal (route_removed);
@@ -334,6 +372,114 @@ test_ip4_zero_gateway (void)
 	nm_platform_process_events (NM_PLATFORM_GET);
 }
 
+static void
+test_ip4_route_options (void)
+{
+	int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME);
+	NMPlatformIP4Route route = { };
+	in_addr_t network;
+	GArray *routes;
+	NMPlatformIP4Route rts[1];
+
+	inet_pton (AF_INET, "172.16.1.0", &network);
+
+	route.ifindex = ifindex;
+	route.rt_source = NM_IP_CONFIG_SOURCE_USER;
+	route.network = network;
+	route.plen = 24;
+	route.metric = 20;
+	route.tos = 0x28;
+	route.window = 10000;
+	route.cwnd = 16;
+	route.initcwnd = 30;
+	route.initrwnd = 50;
+	route.mtu = 1350;
+	route.lock_cwnd = TRUE;
+
+	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, &route));
+
+	/* Test route listing */
+	routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, ifindex,
+	                                        NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT |
+	                                        NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
+	memset (rts, 0, sizeof (rts));
+	rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
+	rts[0].scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK);
+	rts[0].network = network;
+	rts[0].plen = 24;
+	rts[0].ifindex = ifindex;
+	rts[0].metric = 20;
+	rts[0].tos = 0x28;
+	rts[0].window = 10000;
+	rts[0].cwnd = 16;
+	rts[0].initcwnd = 30;
+	rts[0].initrwnd = 50;
+	rts[0].mtu = 1350;
+	rts[0].lock_cwnd = TRUE;
+
+	g_assert_cmpint (routes->len, ==, 1);
+	nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, rts, routes->len, TRUE);
+
+	/* Remove route */
+	g_assert (nm_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, 24, 20));
+
+	g_array_unref (routes);
+}
+
+
+static void
+test_ip6_route_options (void)
+{
+	int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME);
+	NMPlatformIP6Route route = { };
+	struct in6_addr network;
+	GArray *routes;
+	NMPlatformIP6Route rts[3];
+
+	inet_pton (AF_INET6, "2001:db8:a:b:0:0:0:0", &network);
+
+	route.ifindex = ifindex;
+	route.rt_source = NM_IP_CONFIG_SOURCE_USER;
+	route.network = network;
+	route.plen = 64;
+	route.gateway = in6addr_any;
+	route.metric = 1024;
+	route.window = 20000;
+	route.cwnd = 8;
+	route.initcwnd = 22;
+	route.initrwnd = 33;
+	route.mtu = 1300;
+	route.lock_mtu = TRUE;
+
+	g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, &route));
+
+	/* Test route listing */
+	routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, ifindex,
+	                                        NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT |
+	                                        NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT);
+	memset (rts, 0, sizeof (rts));
+	rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
+	rts[0].network = network;
+	rts[0].plen = 64;
+	rts[0].ifindex = ifindex;
+	rts[0].gateway = in6addr_any;
+	rts[0].metric = 1024;
+	rts[0].window = 20000;
+	rts[0].cwnd = 8;
+	rts[0].initcwnd = 22;
+	rts[0].initrwnd = 33;
+	rts[0].mtu = 1300;
+	rts[0].lock_mtu = TRUE;
+
+	g_assert_cmpint (routes->len, ==, 1);
+	nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, rts, routes->len, TRUE);
+
+	/* Remove route */
+	g_assert (nm_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, 64, 1024));
+
+	g_array_unref (routes);
+}
+
 /*****************************************************************************/
 
 NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP;
@@ -360,6 +506,8 @@ _nmtstp_setup_tests (void)
 	g_test_add_func ("/route/ip4", test_ip4_route);
 	g_test_add_func ("/route/ip6", test_ip6_route);
 	g_test_add_func ("/route/ip4_metric0", test_ip4_route_metric0);
+	g_test_add_func ("/route/ip4_options", test_ip4_route_options);
+	g_test_add_func ("/route/ip6_options", test_ip6_route_options);
 
 	if (nmtstp_is_root_test ())
 		g_test_add_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway);
diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c
index ac51678f..06eb7cb9 100644
--- a/src/platform/wifi/wifi-utils-nl80211.c
+++ b/src/platform/wifi/wifi-utils-nl80211.c
@@ -27,7 +27,6 @@
 #include <sys/ioctl.h>
 #include <net/ethernet.h>
 #include <unistd.h>
-#include <math.h>
 #include <netlink/netlink.h>
 #include <netlink/msg.h>
 #include <linux/nl80211.h>
@@ -35,8 +34,17 @@
 #include "wifi-utils-private.h"
 #include "wifi-utils-nl80211.h"
 #include "platform/nm-platform.h"
+#include "platform/nm-platform-utils.h"
 #include "nm-utils.h"
 
+#define _NMLOG_PREFIX_NAME      "wifi-nl80211"
+#define _NMLOG(level, domain, ...) \
+	G_STMT_START { \
+		nm_log ((level), (domain), NULL, NULL, \
+		        "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+		        _NMLOG_PREFIX_NAME \
+		        _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+	} G_STMT_END
 
 /*****************************************************************************
  * Copied from libnl3/genl:
@@ -219,9 +227,9 @@ out_cb_free:
 	nl_cb_put (cb);
 out:
 	if (result >= 0)
-		nm_log_dbg (LOGD_WIFI, "genl_ctrl_resolve: resolved \"%s\" as 0x%x", name, result);
+		_LOGD (LOGD_WIFI, "genl_ctrl_resolve: resolved \"%s\" as 0x%x", name, result);
 	else
-		nm_log_err (LOGD_WIFI, "genl_ctrl_resolve: failed resolve \"%s\"", name);
+		_LOGE (LOGD_WIFI, "genl_ctrl_resolve: failed resolve \"%s\"", name);
 	return result;
 }
 
@@ -333,8 +341,8 @@ _nl80211_send_and_recv (struct nl_sock *nl_sock,
 			    genlmsg_hdr (nlmsg_hdr (msg))->cmd == NL80211_CMD_GET_SCAN)
 				break;
 
-			nm_log_warn (LOGD_WIFI, "nl_recvmsgs() error: (%d) %s",
-			             err, nl_geterror (err));
+			_LOGW (LOGD_WIFI, "nl_recvmsgs() error: (%d) %s",
+			       err, nl_geterror (err));
 			break;
 		}
 	}
@@ -934,8 +942,9 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
 
 		nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS],
 		                     rem_freq) {
-			nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX,
-			                  nl_freq, freq_policy);
+			if (nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+			                      nl_freq, freq_policy) < 0)
+				continue;
 
 			if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
 				continue;
@@ -955,8 +964,9 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
 
 		nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS],
 		                    rem_freq) {
-			nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX,
-			                  nl_freq, freq_policy);
+			if (nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+			                      nl_freq, freq_policy) < 0)
+				continue;
 
 			if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
 				continue;
@@ -1003,7 +1013,9 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
 			case WLAN_CIPHER_SUITE_SMS4:
 				break;
 			default:
-				nm_log_dbg (LOGD_PLATFORM | LOGD_WIFI, "Don't know the meaning of NL80211_ATTR_CIPHER_SUITE %#8.8x.", ciphers[i]);
+				_LOGD (LOGD_PLATFORM | LOGD_WIFI,
+				       "don't know the meaning of NL80211_ATTR_CIPHER_SUITE %#8.8x.",
+				       ciphers[i]);
 				break;
 			}
 		}
@@ -1030,13 +1042,20 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg)
 }
 
 WifiData *
-wifi_nl80211_init (const char *iface, int ifindex)
+wifi_nl80211_init (int ifindex)
 {
 	WifiDataNl80211 *nl80211;
 	struct nl_msg *msg;
 	struct nl80211_device_info device_info = {};
+	char ifname[IFNAMSIZ];
+
+	if (!nmp_utils_if_indextoname (ifindex, ifname)) {
+		_LOGW (LOGD_PLATFORM | LOGD_WIFI,
+		       "can't determine interface name for ifindex %d", ifindex);
+		nm_sprintf_buf (ifname, "if %d", ifindex);
+	}
 
-	nl80211 = wifi_data_new (iface, ifindex, sizeof (*nl80211));
+	nl80211 = wifi_data_new (ifindex, sizeof (*nl80211));
 	nl80211->parent.get_mode = wifi_nl80211_get_mode;
 	nl80211->parent.set_mode = wifi_nl80211_set_mode;
 	nl80211->parent.set_powersave = wifi_nl80211_set_powersave;
@@ -1071,44 +1090,44 @@ wifi_nl80211_init (const char *iface, int ifindex)
 
 	if (nl80211_send_and_recv (nl80211, msg, nl80211_wiphy_info_handler,
 	                           &device_info) < 0) {
-		nm_log_dbg (LOGD_PLATFORM | LOGD_WIFI,
-		            "(%s): NL80211_CMD_GET_WIPHY request failed",
-		            nl80211->parent.iface);
+		_LOGD (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): NL80211_CMD_GET_WIPHY request failed",
+		       ifname);
 		goto error;
 	}
 
 	if (!device_info.success) {
-		nm_log_dbg (LOGD_PLATFORM | LOGD_WIFI,
-		            "(%s): NL80211_CMD_GET_WIPHY request indicated failure",
-		            nl80211->parent.iface);
+		_LOGD (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): NL80211_CMD_GET_WIPHY request indicated failure",
+		       ifname);
 		goto error;
 	}
 
 	if (!device_info.supported) {
-		nm_log_dbg (LOGD_PLATFORM | LOGD_WIFI,
-		            "(%s): driver does not fully support nl80211, falling back to WEXT",
-		            nl80211->parent.iface);
+		_LOGD (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): driver does not fully support nl80211, falling back to WEXT",
+		       ifname);
 		goto error;
 	}
 
 	if (!device_info.can_scan_ssid) {
-		nm_log_err (LOGD_PLATFORM | LOGD_WIFI,
-		            "(%s): driver does not support SSID scans",
-		            nl80211->parent.iface);
+		_LOGE (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): driver does not support SSID scans",
+		       ifname);
 		goto error;
 	}
 
 	if (device_info.num_freqs == 0 || device_info.freqs == NULL) {
 		nm_log_err (LOGD_PLATFORM | LOGD_WIFI,
 		            "(%s): driver reports no supported frequencies",
-		            nl80211->parent.iface);
+		            ifname);
 		goto error;
 	}
 
 	if (device_info.caps == 0) {
-		nm_log_err (LOGD_PLATFORM | LOGD_WIFI,
-		            "(%s): driver doesn't report support of any encryption",
-		            nl80211->parent.iface);
+		_LOGE (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): driver doesn't report support of any encryption",
+		       ifname);
 		goto error;
 	}
 
@@ -1120,9 +1139,9 @@ wifi_nl80211_init (const char *iface, int ifindex)
 	if (device_info.can_wowlan)
 		nl80211->parent.get_wowlan = wifi_nl80211_get_wowlan;
 
-	nm_log_info (LOGD_PLATFORM | LOGD_WIFI,
-	             "(%s): using nl80211 for WiFi device control",
-	             nl80211->parent.iface);
+	_LOGI (LOGD_PLATFORM | LOGD_WIFI,
+	       "(%s): using nl80211 for WiFi device control",
+	       ifname);
 
 	return (WifiData *) nl80211;
 
diff --git a/src/platform/wifi/wifi-utils-nl80211.h b/src/platform/wifi/wifi-utils-nl80211.h
index b3e8c897..aff24555 100644
--- a/src/platform/wifi/wifi-utils-nl80211.h
+++ b/src/platform/wifi/wifi-utils-nl80211.h
@@ -23,6 +23,6 @@
 
 #include "wifi-utils.h"
 
-WifiData *wifi_nl80211_init (const char *iface, int ifindex);
+WifiData *wifi_nl80211_init (int ifindex);
 
 #endif  /* __WIFI_UTILS_NL80211_H__ */
diff --git a/src/platform/wifi/wifi-utils-private.h b/src/platform/wifi/wifi-utils-private.h
index ebe76f1b..11a0f060 100644
--- a/src/platform/wifi/wifi-utils-private.h
+++ b/src/platform/wifi/wifi-utils-private.h
@@ -25,7 +25,6 @@
 #include "wifi-utils.h"
 
 struct WifiData {
-	char *iface;
 	int ifindex;
 	NMDeviceWifiCapabilities caps;
 
@@ -69,7 +68,7 @@ struct WifiData {
 	gboolean (*indicate_addressing_running) (WifiData *data, gboolean running);
 };
 
-gpointer wifi_data_new (const char *iface, int ifindex, gsize len);
+gpointer wifi_data_new (int ifindex, gsize len);
 void wifi_data_free (WifiData *data);
 
 #endif  /* __WIFI_UTILS_PRIVATE_H__ */
diff --git a/src/platform/wifi/wifi-utils-wext.c b/src/platform/wifi/wifi-utils-wext.c
index af8cf2de..1bc29ae8 100644
--- a/src/platform/wifi/wifi-utils-wext.c
+++ b/src/platform/wifi/wifi-utils-wext.c
@@ -26,7 +26,6 @@
 #include <sys/ioctl.h>
 #include <net/ethernet.h>
 #include <unistd.h>
-#include <math.h>
 
 #include "wifi-utils-private.h"
 #include "wifi-utils-wext.h"
@@ -69,8 +68,17 @@ struct iw_range_with_scan_capa
 	/* don't need the rest... */
 };
 
+#define _NMLOG_PREFIX_NAME      "wifi-wext"
+#define _NMLOG(level, domain, ...) \
+	G_STMT_START { \
+		nm_log ((level), (domain), NULL, NULL, \
+		        "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
+		        _NMLOG_PREFIX_NAME \
+		        _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
+	} G_STMT_END
+
 static guint32
-iw_freq_to_uint32 (struct iw_freq *freq)
+iw_freq_to_uint32 (const struct iw_freq *freq)
 {
 	if (freq->e == 0) {
 		/* Some drivers report channel not frequency.  Convert to a
@@ -81,8 +89,7 @@ iw_freq_to_uint32 (struct iw_freq *freq)
 		else if (freq->m == 14)
 			return 2484;
 	}
-
-	return (guint32) (((double) freq->m) * pow (10, freq->e) / 1000000);
+	return (guint32) ((((double) freq->m) * nm_utils_exp10 (freq->e)) / 1000000.0);
 }
 
 static void
@@ -94,20 +101,36 @@ wifi_wext_deinit (WifiData *parent)
 		close (wext->fd);
 }
 
+static gboolean
+get_ifname (int ifindex, char *buffer, const char *op)
+{
+	int errsv;
+
+	if (!nmp_utils_if_indextoname (ifindex, buffer)) {
+		errsv = errno;
+		_LOGW (LOGD_PLATFORM | LOGD_WIFI,
+		       "error getting interface name for ifindex %d, operation '%s': %s (%d)",
+		       ifindex, op, g_strerror (errsv), errsv);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 static NM80211Mode
-wifi_wext_get_mode (WifiData *data)
+wifi_wext_get_mode_ifname (WifiData *data, const char *ifname)
 {
 	WifiDataWext *wext = (WifiDataWext *) data;
 	struct iwreq wrq;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
-	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
+	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 
 	if (ioctl (wext->fd, SIOCGIWMODE, &wrq) < 0) {
 		if (errno != ENODEV) {
-			nm_log_warn (LOGD_PLATFORM | LOGD_WIFI,
-			             "(%s): error %d getting card mode",
-			             wext->parent.iface, errno);
+			_LOGW (LOGD_PLATFORM | LOGD_WIFI,
+			       "(%s): error %d getting card mode",
+			       ifname, errno);
 		}
 		return NM_802_11_MODE_UNKNOWN;
 	}
@@ -126,13 +149,28 @@ wifi_wext_get_mode (WifiData *data)
 	return NM_802_11_MODE_UNKNOWN;
 }
 
+static NM80211Mode
+wifi_wext_get_mode (WifiData *data)
+{
+	char ifname[IFNAMSIZ];
+
+	if (!get_ifname (data->ifindex, ifname, "get-mode"))
+		return FALSE;
+
+	return wifi_wext_get_mode_ifname (data, ifname);
+}
+
 static gboolean
 wifi_wext_set_mode (WifiData *data, const NM80211Mode mode)
 {
 	WifiDataWext *wext = (WifiDataWext *) data;
 	struct iwreq wrq;
+	char ifname[IFNAMSIZ];
+
+	if (!get_ifname (data->ifindex, ifname, "set-mode"))
+		return FALSE;
 
-	if (wifi_wext_get_mode (data) == mode)
+	if (wifi_wext_get_mode_ifname (data, ifname) == mode)
 		return TRUE;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
@@ -151,11 +189,12 @@ wifi_wext_set_mode (WifiData *data, const NM80211Mode mode)
 		return FALSE;
 	}
 
-	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
+	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 	if (ioctl (wext->fd, SIOCSIWMODE, &wrq) < 0) {
 		if (errno != ENODEV) {
-			nm_log_err (LOGD_PLATFORM | LOGD_WIFI, "(%s): error setting mode %d",
-			            wext->parent.iface, mode);
+			_LOGE (LOGD_PLATFORM | LOGD_WIFI,
+			       "(%s): error setting mode %d",
+			       ifname, mode);
 		}
 		return FALSE;
 	}
@@ -168,6 +207,10 @@ wifi_wext_set_powersave (WifiData *data, guint32 powersave)
 {
 	WifiDataWext *wext = (WifiDataWext *) data;
 	struct iwreq wrq;
+	char ifname[IFNAMSIZ];
+
+	if (!get_ifname (data->ifindex, ifname, "set-powersave"))
+		return FALSE;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
 	if (powersave == 1) {
@@ -175,11 +218,12 @@ wifi_wext_set_powersave (WifiData *data, guint32 powersave)
 	} else
 		wrq.u.power.disabled = 1;
 
-	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
+	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 	if (ioctl (wext->fd, SIOCSIWPOWER, &wrq) < 0) {
 		if (errno != ENODEV) {
-			nm_log_err (LOGD_PLATFORM | LOGD_WIFI, "(%s): error setting powersave %" G_GUINT32_FORMAT,
-			            wext->parent.iface, powersave);
+			_LOGE (LOGD_PLATFORM | LOGD_WIFI,
+			       "(%s): error setting powersave %" G_GUINT32_FORMAT,
+			       ifname, powersave);
 		}
 		return FALSE;
 	}
@@ -192,13 +236,17 @@ wifi_wext_get_freq (WifiData *data)
 {
 	WifiDataWext *wext = (WifiDataWext *) data;
 	struct iwreq wrq;
+	char ifname[IFNAMSIZ];
+
+	if (!get_ifname (data->ifindex, ifname, "get-freq"))
+		return FALSE;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
-	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
+	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 	if (ioctl (wext->fd, SIOCGIWFREQ, &wrq) < 0) {
-		nm_log_warn (LOGD_PLATFORM | LOGD_WIFI,
-		             "(%s): error getting frequency: %s",
-		             wext->parent.iface, strerror (errno));
+		_LOGW (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): error getting frequency: %s",
+		       ifname, strerror (errno));
 		return 0;
 	}
 
@@ -226,13 +274,17 @@ wifi_wext_get_bssid (WifiData *data, guint8 *out_bssid)
 {
 	WifiDataWext *wext = (WifiDataWext *) data;
 	struct iwreq wrq;
+	char ifname[IFNAMSIZ];
+
+	if (!get_ifname (data->ifindex, ifname, "get-bssid"))
+		return FALSE;
 
 	memset (&wrq, 0, sizeof (wrq));
-	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
+	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 	if (ioctl (wext->fd, SIOCGIWAP, &wrq) < 0) {
-		nm_log_warn (LOGD_PLATFORM | LOGD_WIFI,
-		             "(%s): error getting associated BSSID: %s",
-		             wext->parent.iface, strerror (errno));
+		_LOGW (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): error getting associated BSSID: %s",
+		       ifname, strerror (errno));
 		return FALSE;
 	}
 	memcpy (out_bssid, &(wrq.u.ap_addr.sa_data), ETH_ALEN);
@@ -245,9 +297,13 @@ wifi_wext_get_rate (WifiData *data)
 	WifiDataWext *wext = (WifiDataWext *) data;
 	struct iwreq wrq;
 	int err;
+	char ifname[IFNAMSIZ];
+
+	if (!get_ifname (data->ifindex, ifname, "get-rate"))
+		return FALSE;
 
 	memset (&wrq, 0, sizeof (wrq));
-	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
+	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 	err = ioctl (wext->fd, SIOCGIWRATE, &wrq);
 	return ((err == 0) ? wrq.u.bitrate.value / 1000 : 0);
 }
@@ -264,16 +320,16 @@ wext_qual_to_percent (const struct iw_quality *qual,
 
 	/* Magically convert the many different WEXT quality representations to a percentage */
 
-	nm_log_dbg (LOGD_WIFI,
-	            "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X  ** MAX: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X",
-	            (__s8) qual->qual, qual->qual, qual->qual,
-	            (__s8) qual->level, qual->level, qual->level,
-	            (__s8) qual->noise, qual->noise, qual->noise,
-	            qual->updated,
-	            (__s8) max_qual->qual, max_qual->qual, max_qual->qual,
-	            (__s8) max_qual->level, max_qual->level, max_qual->level,
-	            (__s8) max_qual->noise, max_qual->noise, max_qual->noise,
-	            max_qual->updated);
+	_LOGD (LOGD_WIFI,
+	       "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X  ** MAX: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X",
+	       (__s8) qual->qual, qual->qual, qual->qual,
+	       (__s8) qual->level, qual->level, qual->level,
+	       (__s8) qual->noise, qual->noise, qual->noise,
+	       qual->updated,
+	       (__s8) max_qual->qual, max_qual->qual, max_qual->qual,
+	       (__s8) max_qual->level, max_qual->level, max_qual->level,
+	       (__s8) max_qual->noise, max_qual->noise, max_qual->noise,
+	       max_qual->updated);
 
 	/* Try using the card's idea of the signal quality first as long as it tells us what the max quality is.
 	 * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be
@@ -319,8 +375,8 @@ wext_qual_to_percent (const struct iw_quality *qual,
 		/* A sort of signal-to-noise ratio calculation */
 		level_percent = (int) (100 - 70 * (((double)max_level - (double)level) /
 		                                   ((double)max_level - (double)noise)));
-		nm_log_dbg (LOGD_WIFI, "QL1: level_percent is %d.  max_level %d, level %d, noise_floor %d.",
-		            level_percent, max_level, level, noise);
+		_LOGD (LOGD_WIFI, "QL1: level_percent is %d.  max_level %d, level %d, noise_floor %d.",
+		       level_percent, max_level, level, noise);
 	} else if (   (max_qual->level != 0)
 	           && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */
 	           && !(qual->updated & IW_QUAL_LEVEL_INVALID)) {
@@ -331,18 +387,18 @@ wext_qual_to_percent (const struct iw_quality *qual,
 		/* Signal level is relavtive (0 -> max_qual->level) */
 		level = CLAMP (level, 0, max_qual->level);
 		level_percent = (int)(100 * ((double)level / (double)max_qual->level));
-		nm_log_dbg (LOGD_WIFI, "QL2: level_percent is %d.  max_level %d, level %d.",
-		            level_percent, max_qual->level, level);
+		_LOGD (LOGD_WIFI, "QL2: level_percent is %d.  max_level %d, level %d.",
+		       level_percent, max_qual->level, level);
 	} else if (percent == -1) {
-		nm_log_dbg (LOGD_WIFI, "QL: Could not get quality %% value from driver.  Driver is probably buggy.");
+		_LOGD (LOGD_WIFI, "QL: Could not get quality %% value from driver.  Driver is probably buggy.");
 	}
 
 	/* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */
 	if ((percent < 1) && (level_percent >= 0))
 		percent = level_percent;
 
-	nm_log_dbg (LOGD_WIFI, "QL: Final quality percent is %d (%d).",
-	            percent, CLAMP (percent, 0, 100));
+	_LOGD (LOGD_WIFI, "QL: Final quality percent is %d (%d).",
+	       percent, CLAMP (percent, 0, 100));
 	return (CLAMP (percent, 0, 100));
 }
 
@@ -352,17 +408,21 @@ wifi_wext_get_qual (WifiData *data)
 	WifiDataWext *wext = (WifiDataWext *) data;
 	struct iwreq wrq;
 	struct iw_statistics stats;
+	char ifname[IFNAMSIZ];
+
+	if (!get_ifname (data->ifindex, ifname, "get-qual"))
+		return FALSE;
 
 	memset (&stats, 0, sizeof (stats));
 	wrq.u.data.pointer = &stats;
 	wrq.u.data.length = sizeof (stats);
 	wrq.u.data.flags = 1;  /* Clear updated flag */
-	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
+	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 
 	if (ioctl (wext->fd, SIOCGIWSTATS, &wrq) < 0) {
-		nm_log_warn (LOGD_PLATFORM | LOGD_WIFI,
-		             "(%s): error getting signal strength: %s",
-		             wext->parent.iface, strerror (errno));
+		_LOGW (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): error getting signal strength: %s",
+		       ifname, strerror (errno));
 		return -1;
 	}
 
@@ -392,9 +452,13 @@ wifi_wext_set_mesh_channel (WifiData *data, guint32 channel)
 {
 	WifiDataWext *wext = (WifiDataWext *) data;
 	struct iwreq wrq;
+	char ifname[IFNAMSIZ];
+
+	if (!get_ifname (data->ifindex, ifname, "set-mesh-channel"))
+		return FALSE;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
-	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
+	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 
 	if (channel > 0) {
 		wrq.u.freq.flags = IW_FREQ_FIXED;
@@ -403,9 +467,9 @@ wifi_wext_set_mesh_channel (WifiData *data, guint32 channel)
 	}
 
 	if (ioctl (wext->fd, SIOCSIWFREQ, &wrq) < 0) {
-		nm_log_err (LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC,
-		            "(%s): error setting channel to %d: %s",
-		            wext->parent.iface, channel, strerror (errno));
+		_LOGE (LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC,
+		       "(%s): error setting channel to %d: %s",
+		       ifname, channel, strerror (errno));
 		return FALSE;
 	}
 
@@ -418,6 +482,11 @@ wifi_wext_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len)
 	WifiDataWext *wext = (WifiDataWext *) data;
 	struct iwreq wrq;
 	char buf[IW_ESSID_MAX_SIZE + 1];
+	char ifname[IFNAMSIZ];
+	int errsv;
+
+	if (!get_ifname (data->ifindex, ifname, "set-mesh-ssid"))
+		return FALSE;
 
 	memset (buf, 0, sizeof (buf));
 	memcpy (buf, ssid, MIN (sizeof (buf) - 1, len));
@@ -426,16 +495,17 @@ wifi_wext_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len)
 	wrq.u.essid.length = len;
 	wrq.u.essid.flags = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */
 
-	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
+	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 	if (ioctl (wext->fd, SIOCSIWESSID, &wrq) == 0)
 		return TRUE;
 
 	if (errno != ENODEV) {
-		nm_log_err (LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC,
-		            "(%s): error setting SSID to '%s': %s",
-		            wext->parent.iface,
-		            ssid ? nm_utils_escape_ssid (ssid, len) : "(null)",
-		            strerror (errno));
+		errsv = errno;
+		_LOGE (LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC,
+		       "(%s): error setting SSID to '%s': %s",
+		       ifname,
+		       ssid ? nm_utils_escape_ssid (ssid, len) : "(null)",
+		       strerror (errsv));
 	}
 
 	return FALSE;
@@ -444,12 +514,12 @@ wifi_wext_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len)
 /*****************************************************************************/
 
 static gboolean
-wext_can_scan (WifiDataWext *wext)
+wext_can_scan_ifname (WifiDataWext *wext, const char *ifname)
 {
 	struct iwreq wrq;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
-	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
+	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 	if (ioctl (wext->fd, SIOCSIWSCAN, &wrq) < 0) {
 		if (errno == EOPNOTSUPP)
 			return FALSE;
@@ -458,16 +528,17 @@ wext_can_scan (WifiDataWext *wext)
 }
 
 static gboolean
-wext_get_range (WifiDataWext *wext,
-                struct iw_range *range,
-                guint32 *response_len)
+wext_get_range_ifname (WifiDataWext *wext,
+                       const char *ifname,
+                       struct iw_range *range,
+                       guint32 *response_len)
 {
 	int i = 26;
 	gboolean success = FALSE;
 	struct iwreq wrq;
 
 	memset (&wrq, 0, sizeof (struct iwreq));
-	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
+	nm_utils_ifname_cpy (wrq.ifr_name, ifname);
 	wrq.u.data.pointer = (caddr_t) range;
 	wrq.u.data.length = sizeof (struct iw_range);
 
@@ -482,9 +553,9 @@ wext_get_range (WifiDataWext *wext,
 			success = TRUE;
 			break;
 		} else if (errno != EAGAIN) {
-			nm_log_err (LOGD_PLATFORM | LOGD_WIFI,
-			            "(%s): couldn't get driver range information (%d).",
-			            wext->parent.iface, errno);
+			_LOGE (LOGD_PLATFORM | LOGD_WIFI,
+			       "(%s): couldn't get driver range information (%d).",
+			       ifname, errno);
 			break;
 		}
 
@@ -492,9 +563,9 @@ wext_get_range (WifiDataWext *wext,
 	}
 
 	if (i <= 0) {
-		nm_log_warn (LOGD_PLATFORM | LOGD_WIFI,
-		             "(%s): driver took too long to respond to IWRANGE query.",
-		             wext->parent.iface);
+		_LOGW (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): driver took too long to respond to IWRANGE query.",
+		       ifname);
 	}
 
 	return success;
@@ -506,7 +577,7 @@ wext_get_range (WifiDataWext *wext,
                   NM_WIFI_DEVICE_CAP_RSN)
 
 static guint32
-wext_get_caps (WifiDataWext *wext, struct iw_range *range)
+wext_get_caps (WifiDataWext *wext, const char *ifname, struct iw_range *range)
 {
 	guint32 caps = NM_WIFI_DEVICE_CAP_NONE;
 
@@ -531,16 +602,18 @@ wext_get_caps (WifiDataWext *wext, struct iw_range *range)
 	/* Check for cipher support but not WPA support */
 	if (    (caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))
 	    && !(caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))) {
-		nm_log_warn (LOGD_WIFI, "%s: device supports WPA ciphers but not WPA protocol; "
-		             "WPA unavailable.", wext->parent.iface);
+		_LOGW (LOGD_WIFI,
+		       "%s: device supports WPA ciphers but not WPA protocol; WPA unavailable.",
+		       ifname);
 		caps &= ~WPA_CAPS;
 	}
 
 	/* Check for WPA support but not cipher support */
 	if (    (caps & (NM_WIFI_DEVICE_CAP_WPA | NM_WIFI_DEVICE_CAP_RSN))
 	    && !(caps & (NM_WIFI_DEVICE_CAP_CIPHER_TKIP | NM_WIFI_DEVICE_CAP_CIPHER_CCMP))) {
-		nm_log_warn (LOGD_WIFI, "%s: device supports WPA protocol but not WPA ciphers; "
-		             "WPA unavailable.", wext->parent.iface);
+		_LOGW (LOGD_WIFI,
+		       "%s: device supports WPA protocol but not WPA ciphers; WPA unavailable.",
+		       ifname);
 		caps &= ~WPA_CAPS;
 	}
 
@@ -554,7 +627,7 @@ wext_get_caps (WifiDataWext *wext, struct iw_range *range)
 }
 
 WifiData *
-wifi_wext_init (const char *iface, int ifindex, gboolean check_scan)
+wifi_wext_init (int ifindex, gboolean check_scan)
 {
 	WifiDataWext *wext;
 	struct iw_range range;
@@ -562,8 +635,15 @@ wifi_wext_init (const char *iface, int ifindex, gboolean check_scan)
 	struct iw_range_with_scan_capa *scan_capa_range;
 	int i;
 	gboolean freq_valid = FALSE, has_5ghz = FALSE, has_2ghz = FALSE;
+	char ifname[IFNAMSIZ];
+
+	if (!nmp_utils_if_indextoname (ifindex, ifname)) {
+		_LOGW (LOGD_PLATFORM | LOGD_WIFI,
+		       "can't determine interface name for ifindex %d", ifindex);
+		return NULL;
+	}
 
-	wext = wifi_data_new (iface, ifindex, sizeof (*wext));
+	wext = wifi_data_new (ifindex, sizeof (*wext));
 	wext->parent.get_mode = wifi_wext_get_mode;
 	wext->parent.set_mode = wifi_wext_set_mode;
 	wext->parent.set_powersave = wifi_wext_set_powersave;
@@ -582,17 +662,17 @@ wifi_wext_init (const char *iface, int ifindex, gboolean check_scan)
 		goto error;
 
 	memset (&range, 0, sizeof (struct iw_range));
-	if (wext_get_range (wext, &range, &response_len) == FALSE) {
-		nm_log_info (LOGD_PLATFORM | LOGD_WIFI, "(%s): driver WEXT range request failed",
-		             wext->parent.iface);
+	if (wext_get_range_ifname (wext, ifname, &range, &response_len) == FALSE) {
+		_LOGI (LOGD_PLATFORM | LOGD_WIFI, "(%s): driver WEXT range request failed",
+		       ifname);
 		goto error;
 	}
 
 	if ((response_len < 300) || (range.we_version_compiled < 21)) {
-		nm_log_info (LOGD_PLATFORM | LOGD_WIFI,
-		             "(%s): driver WEXT version too old (got %d, expected >= 21)",
-		             wext->parent.iface,
-		             range.we_version_compiled);
+		_LOGI (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): driver WEXT version too old (got %d, expected >= 21)",
+		       ifname,
+		       range.we_version_compiled);
 		goto error;
 	}
 
@@ -612,10 +692,10 @@ wifi_wext_init (const char *iface, int ifindex, gboolean check_scan)
 	}
 
 	/* Check for scanning capability; cards that can't scan are not supported */
-	if (check_scan && (wext_can_scan (wext) == FALSE)) {
-		nm_log_info (LOGD_PLATFORM | LOGD_WIFI,
-		             "(%s): drivers that cannot scan are unsupported",
-		             wext->parent.iface);
+	if (check_scan && (wext_can_scan_ifname (wext, ifname) == FALSE)) {
+		_LOGI (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): drivers that cannot scan are unsupported",
+		       ifname);
 		goto error;
 	}
 
@@ -625,18 +705,18 @@ wifi_wext_init (const char *iface, int ifindex, gboolean check_scan)
 	 */
 	scan_capa_range = (struct iw_range_with_scan_capa *) &range;
 	if (scan_capa_range->scan_capa & NM_IW_SCAN_CAPA_ESSID) {
-		nm_log_info (LOGD_PLATFORM | LOGD_WIFI,
-		             "(%s): driver supports SSID scans (scan_capa 0x%02X).",
-		             wext->parent.iface,
-		             scan_capa_range->scan_capa);
+		_LOGI (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): driver supports SSID scans (scan_capa 0x%02X).",
+		       ifname,
+		       scan_capa_range->scan_capa);
 	} else {
-		nm_log_info (LOGD_PLATFORM | LOGD_WIFI,
-		             "(%s): driver does not support SSID scans (scan_capa 0x%02X).",
-		             wext->parent.iface,
-		             scan_capa_range->scan_capa);
+		_LOGI (LOGD_PLATFORM | LOGD_WIFI,
+		       "(%s): driver does not support SSID scans (scan_capa 0x%02X).",
+		       ifname,
+		       scan_capa_range->scan_capa);
 	}
 
-	wext->parent.caps = wext_get_caps (wext, &range);
+	wext->parent.caps = wext_get_caps (wext, ifname, &range);
 	if (freq_valid)
 		wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_VALID;
 	if (has_2ghz)
@@ -644,9 +724,9 @@ wifi_wext_init (const char *iface, int ifindex, gboolean check_scan)
 	if (has_5ghz)
 		wext->parent.caps |= NM_WIFI_DEVICE_CAP_FREQ_5GHZ;
 
-	nm_log_info (LOGD_PLATFORM | LOGD_WIFI,
-	             "(%s): using WEXT for WiFi device control",
-	             wext->parent.iface);
+	_LOGI (LOGD_PLATFORM | LOGD_WIFI,
+	       "(%s): using WEXT for WiFi device control",
+	       ifname);
 
 	return (WifiData *) wext;
 
diff --git a/src/platform/wifi/wifi-utils-wext.h b/src/platform/wifi/wifi-utils-wext.h
index e168fe28..3ef5a173 100644
--- a/src/platform/wifi/wifi-utils-wext.h
+++ b/src/platform/wifi/wifi-utils-wext.h
@@ -23,7 +23,7 @@
 
 #include "wifi-utils.h"
 
-WifiData *wifi_wext_init (const char *iface, int ifindex, gboolean check_scan);
+WifiData *wifi_wext_init (int ifindex, gboolean check_scan);
 
 gboolean wifi_wext_is_wifi (const char *iface);
 
diff --git a/src/platform/wifi/wifi-utils.c b/src/platform/wifi/wifi-utils.c
index b8da02c1..d0052121 100644
--- a/src/platform/wifi/wifi-utils.c
+++ b/src/platform/wifi/wifi-utils.c
@@ -38,12 +38,11 @@
 #include "platform/nm-platform-utils.h"
 
 gpointer
-wifi_data_new (const char *iface, int ifindex, gsize len)
+wifi_data_new (int ifindex, gsize len)
 {
 	WifiData *data;
 
 	data = g_malloc0 (len);
-	data->iface = g_strdup (iface);
 	data->ifindex = ifindex;
 	return data;
 }
@@ -51,7 +50,6 @@ wifi_data_new (const char *iface, int ifindex, gsize len)
 void
 wifi_data_free (WifiData *data)
 {
-	g_free (data->iface);
 	memset (data, 0, sizeof (*data));
 	g_free (data);
 }
@@ -59,17 +57,16 @@ wifi_data_free (WifiData *data)
 /*****************************************************************************/
 
 WifiData *
-wifi_utils_init (const char *iface, int ifindex, gboolean check_scan)
+wifi_utils_init (int ifindex, gboolean check_scan)
 {
 	WifiData *ret;
 
-	g_return_val_if_fail (iface != NULL, NULL);
 	g_return_val_if_fail (ifindex > 0, NULL);
 
-	ret = wifi_nl80211_init (iface, ifindex);
+	ret = wifi_nl80211_init (ifindex);
 	if (ret == NULL) {
 #if HAVE_WEXT
-		ret = wifi_wext_init (iface, ifindex, check_scan);
+		ret = wifi_wext_init (ifindex, check_scan);
 #endif
 	}
 	return ret;
@@ -83,14 +80,6 @@ wifi_utils_get_ifindex (WifiData *data)
 	return data->ifindex;
 }
 
-const char *
-wifi_utils_get_iface (WifiData *data)
-{
-	g_return_val_if_fail (data != NULL, NULL);
-
-	return data->iface;
-}
-
 NMDeviceWifiCapabilities
 wifi_utils_get_caps (WifiData *data)
 {
diff --git a/src/platform/wifi/wifi-utils.h b/src/platform/wifi/wifi-utils.h
index 4fd5a80b..705717b0 100644
--- a/src/platform/wifi/wifi-utils.h
+++ b/src/platform/wifi/wifi-utils.h
@@ -30,12 +30,10 @@ typedef struct WifiData WifiData;
 
 gboolean wifi_utils_is_wifi (int dirfd, const char *ifname);
 
-WifiData *wifi_utils_init (const char *iface, int ifindex, gboolean check_scan);
+WifiData *wifi_utils_init (int ifindex, gboolean check_scan);
 
 int wifi_utils_get_ifindex (WifiData *data);
 
-const char *wifi_utils_get_iface (WifiData *data);
-
 void wifi_utils_deinit (WifiData *data);
 
 NMDeviceWifiCapabilities wifi_utils_get_caps (WifiData *data);
diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c
index 1bbe29d4..c7836a8f 100644
--- a/src/ppp/nm-ppp-manager.c
+++ b/src/ppp/nm-ppp-manager.c
@@ -57,7 +57,8 @@
 #include "introspection/org.freedesktop.NetworkManager.PPP.h"
 
 #define NM_PPPD_PLUGIN PPPD_PLUGIN_DIR "/nm-pppd-plugin.so"
-#define PPP_MANAGER_SECRET_TRIES "ppp-manager-secret-tries"
+
+static NM_CACHED_QUARK_FCN ("ppp-manager-secret-tries", ppp_manager_secret_tries_quark)
 
 /*****************************************************************************/
 
@@ -149,8 +150,8 @@ monitor_cb (gpointer user_data)
 			_LOGW ("could not read ppp stats: %s", strerror (errno));
 	} else {
 		g_signal_emit (manager, signals[STATS], 0,
-		               stats.p.ppp_ibytes,
-		               stats.p.ppp_obytes);
+		               (guint) stats.p.ppp_ibytes,
+		               (guint) stats.p.ppp_obytes);
 	}
 
 	return TRUE;
@@ -341,7 +342,7 @@ impl_ppp_manager_need_secrets (NMPPPManager *manager,
 	 * appear to ask a few times when they actually don't even care what you
 	 * pass back.
 	 */
-	tries = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (applied_connection), PPP_MANAGER_SECRET_TRIES));
+	tries = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (applied_connection), ppp_manager_secret_tries_quark()));
 	if (tries > 1)
 		flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW;
 
@@ -352,7 +353,7 @@ impl_ppp_manager_need_secrets (NMPPPManager *manager,
 	                                               hints ? g_ptr_array_index (hints, 0) : NULL,
 	                                               ppp_secrets_cb,
 	                                               manager);
-	g_object_set_data (G_OBJECT (applied_connection), PPP_MANAGER_SECRET_TRIES, GUINT_TO_POINTER (++tries));
+	g_object_set_qdata (G_OBJECT (applied_connection), ppp_manager_secret_tries_quark (), GUINT_TO_POINTER (++tries));
 	priv->pending_secrets_context = context;
 
 	if (hints)
@@ -389,7 +390,7 @@ set_ip_config_common (NMPPPManager *self,
 
 	/* Got successful IP config; obviously the secrets worked */
 	applied_connection = nm_act_request_get_applied_connection (priv->act_req);
-	g_object_set_data (G_OBJECT (applied_connection), PPP_MANAGER_SECRET_TRIES, NULL);
+	g_object_set_qdata (G_OBJECT (applied_connection), ppp_manager_secret_tries_quark (), NULL);
 
 	if (out_mtu) {
 		/* Get any custom MTU */
@@ -715,6 +716,8 @@ create_pppd_cmd_line (NMPPPManager *self,
                       NMSettingAdsl  *adsl,
                       const char *ppp_name,
                       guint baud_override,
+                      gboolean ip4_enabled,
+                      gboolean ip6_enabled,
                       GError **err)
 {
 	NMPPPManagerPrivate *priv = NM_PPP_MANAGER_GET_PRIVATE (self);
@@ -728,6 +731,14 @@ create_pppd_cmd_line (NMPPPManager *self,
 	if (!pppd_binary)
 		return NULL;
 
+	if (!ip4_enabled && !ip6_enabled) {
+		g_set_error_literal (err,
+		                     NM_MANAGER_ERROR,
+		                     NM_MANAGER_ERROR_FAILED,
+		                     "Neither IPv4 or IPv6 allowed.");
+		return NULL;
+	}
+
 	/* Create pppd command line */
 	cmd = nm_cmd_line_new ();
 	nm_cmd_line_add_string (cmd, pppd_binary);
@@ -738,9 +749,15 @@ create_pppd_cmd_line (NMPPPManager *self,
 	/* NM handles setting the default route */
 	nm_cmd_line_add_string (cmd, "nodefaultroute");
 
-	/* Allow IPv6 to be configured by IPV6CP */
-	nm_cmd_line_add_string (cmd, "ipv6");
-	nm_cmd_line_add_string (cmd, ",");
+	if (!ip4_enabled)
+		nm_cmd_line_add_string (cmd, "noip");
+
+	if (ip6_enabled) {
+		/* Allow IPv6 to be configured by IPV6CP */
+		nm_cmd_line_add_string (cmd, "ipv6");
+		nm_cmd_line_add_string (cmd, ",");
+	} else
+		nm_cmd_line_add_string (cmd, "noipv6");
 
 	ppp_debug = !!getenv ("NM_PPP_DEBUG");
 	if (nm_logging_enabled (LOGL_DEBUG, LOGD_PPP))
@@ -918,6 +935,9 @@ _ppp_manager_start (NMPPPManager *manager,
 	NMCmdLine *ppp_cmd;
 	char *cmd_str;
 	struct stat st;
+	const char *ip6_method, *ip4_method;
+	gboolean ip6_enabled = FALSE;
+	gboolean ip4_enabled = FALSE;
 
 	g_return_val_if_fail (NM_IS_PPP_MANAGER (manager), FALSE);
 	g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
@@ -962,7 +982,21 @@ _ppp_manager_start (NMPPPManager *manager,
 
 	adsl_setting = (NMSettingAdsl *) nm_connection_get_setting (connection, NM_TYPE_SETTING_ADSL);
 
-	ppp_cmd = create_pppd_cmd_line (manager, s_ppp, pppoe_setting, adsl_setting, ppp_name, baud_override, err);
+	/* Figure out what address methods should be enabled */
+	ip4_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+	ip4_enabled = g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0;
+	ip6_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
+	ip6_enabled = g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0;
+
+	ppp_cmd = create_pppd_cmd_line (manager,
+	                                s_ppp,
+	                                pppoe_setting,
+	                                adsl_setting,
+	                                ppp_name,
+	                                baud_override,
+	                                ip4_enabled,
+	                                ip6_enabled,
+	                                err);
 	if (!ppp_cmd)
 		goto out;
 
@@ -1276,7 +1310,8 @@ nm_ppp_manager_class_init (NMPPPManagerClass *manager_class)
 	                  0,
 	                  NULL, NULL, NULL,
 	                  G_TYPE_NONE, 2,
-	                  G_TYPE_UINT, G_TYPE_UINT);
+	                  G_TYPE_UINT /*guint32 in_bytes*/,
+	                  G_TYPE_UINT /*guint32 out_bytes*/);
 
 	nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (manager_class),
 	                                        NMDBUS_TYPE_PPP_MANAGER_SKELETON,
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c
index 0db20979..3d6b1cfb 100644
--- a/src/settings/nm-agent-manager.c
+++ b/src/settings/nm-agent-manager.c
@@ -103,7 +103,7 @@ NM_DEFINE_SINGLETON_GETTER (NMAgentManager, nm_agent_manager_get, NM_TYPE_AGENT_
                             nm_secret_agent_get_description (__agent)); \
             } else \
                 __prefix2[0] = '\0'; \
-            _nm_log ((level), (_NMLOG_DOMAIN), 0, \
+            _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
                      "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      __prefix1, __prefix2 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
@@ -178,7 +178,7 @@ remove_agent (NMAgentManager *self, const char *owner)
 	while (g_hash_table_iter_next (&iter, &data, NULL))
 		request_remove_agent ((Request *) data, agent, &pending_reqs);
 
-	/* We cannot call request_next_agent() from from within hash iterating loop,
+	/* We cannot call request_next_agent() from within hash iterating loop,
 	 * because it may remove the request from the hash table, which invalidates
 	 * the iterator. So, only remove the agent from requests. And store the requests
 	 * that should be sent to other agent to a temporary list to proceed afterwards.
diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c
index 6ec96a32..b9ca34d0 100644
--- a/src/settings/nm-secret-agent.c
+++ b/src/settings/nm-secret-agent.c
@@ -87,7 +87,7 @@ G_DEFINE_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT)
                 g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", ""_NMLOG_PREFIX_NAME"", (self)); \
             else \
                 g_strlcpy (__prefix, _NMLOG_PREFIX_NAME, sizeof (__prefix)); \
-            _nm_log ((level), (_NMLOG_DOMAIN), 0, \
+            _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
                      "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
diff --git a/src/settings/nm-secret-agent.h b/src/settings/nm-secret-agent.h
index 0b6da136..54c5b398 100644
--- a/src/settings/nm-secret-agent.h
+++ b/src/settings/nm-secret-agent.h
@@ -21,7 +21,7 @@
 #ifndef __NETWORKMANAGER_SECRET_AGENT_H__
 #define __NETWORKMANAGER_SECRET_AGENT_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 #define NM_TYPE_SECRET_AGENT            (nm_secret_agent_get_type ())
 #define NM_SECRET_AGENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRET_AGENT, NMSecretAgent))
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 81dcb698..0cb2920c 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -69,17 +69,24 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct _NMSettingsConnectionPrivate {
-	gboolean removed;
 
 	NMAgentManager *agent_mgr;
 	NMSessionMonitor *session_monitor;
 	gulong session_changed_id;
 
 	NMSettingsConnectionFlags flags;
-	gboolean ready;
+
+	bool removed:1;
+	bool ready:1;
+
+	/* Is this connection visible by some session? */
+	bool visible:1;
+
+	bool timestamp_set:1;
+
+	NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:3;
 
 	GSList *pending_auths; /* List of pending authentication requests */
-	gboolean visible; /* Is this connection is visible by some session? */
 
 	GSList *get_secret_requests;  /* in-progress secrets requests */
 
@@ -99,12 +106,10 @@ typedef struct _NMSettingsConnectionPrivate {
 	NMConnection *agent_secrets;
 
 	guint64 timestamp;   /* Up-to-date timestamp of connection use */
-	gboolean timestamp_set;
 	GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
 
 	int autoconnect_retries;
 	gint32 autoconnect_retry_time;
-	NMDeviceStateReason autoconnect_blocked_reason;
 
 	char *filename;
 } NMSettingsConnectionPrivate;
@@ -126,14 +131,13 @@ G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_E
         if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
             char __prefix[128]; \
             const char *__p_prefix = _NMLOG_PREFIX_NAME; \
+            const char *__uuid = (self) ? nm_settings_connection_get_uuid (self) : NULL; \
             \
             if (self) { \
-                const char *__uuid = nm_settings_connection_get_uuid (self); \
-                \
                 g_snprintf (__prefix, sizeof (__prefix), "%s[%p%s%s]", _NMLOG_PREFIX_NAME, self, __uuid ? "," : "", __uuid ? __uuid : ""); \
                 __p_prefix = __prefix; \
             } \
-            _nm_log (__level, _NMLOG_DOMAIN, 0, \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, __uuid, \
                      "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
@@ -493,7 +497,7 @@ set_unsaved (NMSettingsConnection *self, gboolean now_unsaved)
 		else {
 			flags &= ~(NM_SETTINGS_CONNECTION_FLAGS_UNSAVED |
 			           NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED |
-			           NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED);
+			           NM_SETTINGS_CONNECTION_FLAGS_VOLATILE);
 		}
 		nm_settings_connection_set_flags_all (self, flags);
 	}
@@ -556,7 +560,7 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
 	_LOGD ("replace settings from connection %p (%s)", new_connection, nm_connection_get_id (NM_CONNECTION (self)));
 
 	nm_settings_connection_set_flags (self,
-	                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED,
+	                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_FLAGS_VOLATILE,
 	                                  FALSE);
 
 	/* Cache the just-updated system secrets in case something calls
@@ -1648,49 +1652,6 @@ con_update_cb (NMSettingsConnection *self,
 	update_complete (self, info, error);
 }
 
-static char *
-con_list_changed_props (NMConnection *old, NMConnection *new)
-{
-	gs_unref_hashtable GHashTable *diff = NULL;
-	GHashTable *setting_diff;
-	char *setting_name, *prop_name;
-	GHashTableIter iter, iter2;
-	gboolean same;
-	GString *str;
-
-	same = nm_connection_diff (old, new,
-	                           NM_SETTING_COMPARE_FLAG_EXACT |
-	                           NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT,
-	                           &diff);
-
-	if (same || !diff)
-		return NULL;
-
-	str = g_string_sized_new (32);
-	g_hash_table_iter_init (&iter, diff);
-
-	while (g_hash_table_iter_next (&iter,
-	                               (gpointer *) &setting_name,
-	                               (gpointer *) &setting_diff)) {
-		if (!setting_diff)
-			continue;
-
-		g_hash_table_iter_init (&iter2, setting_diff);
-
-		while (g_hash_table_iter_next (&iter2, (gpointer *) &prop_name, NULL)) {
-			g_string_append (str, setting_name);
-			g_string_append_c (str, '.');
-			g_string_append (str, prop_name);
-			g_string_append_c (str, ',');
-		}
-	}
-
-	if (str->len)
-		str->str[str->len - 1] = '\0';
-
-	return g_string_free (str, FALSE);
-}
-
 static void
 update_auth_cb (NMSettingsConnection *self,
                 GDBusMethodInvocation *context,
@@ -1706,6 +1667,17 @@ update_auth_cb (NMSettingsConnection *self,
 		return;
 	}
 
+	if (!info->new_settings) {
+		/* We're just calling Save(). Just commit the existing connection. */
+		if (info->save_to_disk) {
+			nm_settings_connection_commit_changes (self,
+			                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION,
+			                                       con_update_cb,
+			                                       info);
+		}
+		return;
+	}
+
 	if (!any_secrets_present (info->new_settings)) {
 		/* If the new connection has no secrets, we do not want to remove all
 		 * secrets, rather we keep all the existing ones. Do that by merging
@@ -1720,8 +1692,17 @@ update_auth_cb (NMSettingsConnection *self,
 		update_agent_secrets_cache (self, info->new_settings);
 	}
 
-	if (nm_audit_manager_audit_enabled (nm_audit_manager_get ()))
-		info->audit_args = con_list_changed_props (NM_CONNECTION (self), info->new_settings);
+	if (nm_audit_manager_audit_enabled (nm_audit_manager_get ())) {
+		gs_unref_hashtable GHashTable *diff = NULL;
+		gboolean same;
+
+		same = nm_connection_diff (NM_CONNECTION (self), info->new_settings,
+		                           NM_SETTING_COMPARE_FLAG_EXACT |
+		                           NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT,
+		                           &diff);
+		if (!same && diff)
+			info->audit_args = nm_utils_format_con_diff_for_audit (diff);
+	}
 
 	if (info->save_to_disk) {
 		nm_settings_connection_replace_and_commit (self,
@@ -1855,11 +1836,7 @@ static void
 impl_settings_connection_save (NMSettingsConnection *self,
                                GDBusMethodInvocation *context)
 {
-	/* Do nothing if the connection is already synced with disk */
-	if (nm_settings_connection_get_unsaved (self))
-		settings_connection_update_helper (self, context, NULL, TRUE);
-	else
-		g_dbus_method_invocation_return_value (context, NULL);
+	settings_connection_update_helper (self, context, NULL, TRUE);
 }
 
 static void
@@ -2162,6 +2139,96 @@ nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConn
 
 /*****************************************************************************/
 
+static int
+_cmp_timestamp (NMSettingsConnection *a, NMSettingsConnection *b)
+{
+	gboolean a_has_ts, b_has_ts;
+	guint64 ats = 0, bts = 0;
+
+	nm_assert (NM_IS_SETTINGS_CONNECTION (a));
+	nm_assert (NM_IS_SETTINGS_CONNECTION (b));
+
+	a_has_ts = !!nm_settings_connection_get_timestamp (a, &ats);
+	b_has_ts = !!nm_settings_connection_get_timestamp (b, &bts);
+	if (a_has_ts != b_has_ts)
+		return a_has_ts ? -1 : 1;
+	if (a_has_ts && ats != bts)
+		return (ats > bts) ? -1 : 1;
+	return 0;
+}
+
+static int
+_cmp_last_resort (NMSettingsConnection *a, NMSettingsConnection *b)
+{
+	int c;
+
+	nm_assert (NM_IS_SETTINGS_CONNECTION (a));
+	nm_assert (NM_IS_SETTINGS_CONNECTION (b));
+
+	c = g_strcmp0 (nm_connection_get_uuid (NM_CONNECTION (a)),
+	               nm_connection_get_uuid (NM_CONNECTION (b)));
+	if (c)
+		return c;
+
+	/* hm, same UUID. Use their pointer value to give them a stable
+	 * order. */
+	return (a > b) ? -1 : 1;
+}
+
+/* sorting for "best" connections.
+ * The function sorts connections in descending timestamp order.
+ * That means an older connection (lower timestamp) goes after
+ * a newer one.
+ */
+int
+nm_settings_connection_cmp_timestamp (NMSettingsConnection *a, NMSettingsConnection *b)
+{
+	int c;
+
+	if (a == b)
+		return 0;
+	if (!a)
+		return 1;
+	if (!b)
+		return -1;
+
+	if ((c = _cmp_timestamp (a, b)))
+		return c;
+	if ((c = nm_utils_cmp_connection_by_autoconnect_priority (NM_CONNECTION (a), NM_CONNECTION (b))))
+		return c;
+	return _cmp_last_resort (a, b);
+}
+
+int
+nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data)
+{
+	return nm_settings_connection_cmp_timestamp (*((NMSettingsConnection **) pa),
+	                                             *((NMSettingsConnection **) pb));
+}
+
+int
+nm_settings_connection_cmp_autoconnect_priority (NMSettingsConnection *a, NMSettingsConnection *b)
+{
+	int c;
+
+	if (a == b)
+		return 0;
+	if ((c = nm_utils_cmp_connection_by_autoconnect_priority (NM_CONNECTION (a), NM_CONNECTION (b))))
+		return c;
+	if ((c = _cmp_timestamp (a, b)))
+		return c;
+	return _cmp_last_resort (a, b);
+}
+
+int
+nm_settings_connection_cmp_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data)
+{
+	return nm_settings_connection_cmp_autoconnect_priority (*((NMSettingsConnection **) pa),
+	                                                        *((NMSettingsConnection **) pb));
+}
+
+/*****************************************************************************/
+
 /**
  * nm_settings_connection_get_timestamp:
  * @self: the #NMSettingsConnection
@@ -2483,7 +2550,7 @@ nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self)
 		priv->autoconnect_retries = retries;
 	}
 
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retries;
+	return priv->autoconnect_retries;
 }
 
 void
@@ -2492,7 +2559,10 @@ nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *self,
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
-	priv->autoconnect_retries = retries;
+	if (priv->autoconnect_retries != retries) {
+		_LOGT ("autoconnect-retries: set %d", retries);
+		priv->autoconnect_retries = retries;
+	}
 	if (retries)
 		priv->autoconnect_retry_time = 0;
 	else
@@ -2511,7 +2581,7 @@ nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self)
 	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retry_time;
 }
 
-NMDeviceStateReason
+NMSettingsAutoconnectBlockedReason
 nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self)
 {
 	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason;
@@ -2519,8 +2589,12 @@ nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *sel
 
 void
 nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self,
-                                                       NMDeviceStateReason reason)
+                                                       NMSettingsAutoconnectBlockedReason reason)
 {
+	g_return_if_fail (NM_IN_SET (reason,
+	                             NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED,
+	                             NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED,
+	                             NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS));
 	NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason = reason;
 }
 
@@ -2533,7 +2607,7 @@ nm_settings_connection_can_autoconnect (NMSettingsConnection *self)
 
 	if (   !priv->visible
 	    || priv->autoconnect_retries == 0
-	    || priv->autoconnect_blocked_reason != NM_DEVICE_STATE_REASON_NONE)
+	    || priv->autoconnect_blocked_reason != NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED)
 		return FALSE;
 
 	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
@@ -2566,18 +2640,18 @@ nm_settings_connection_get_nm_generated (NMSettingsConnection *self)
 }
 
 /**
- * nm_settings_connection_get_nm_generated_assumed:
+ * nm_settings_connection_get_volatile:
  * @self: an #NMSettingsConnection
  *
- * Gets the "nm-generated-assumed" flag on @self.
+ * Gets the "volatile" flag on @self.
  *
- * The connection is a generated connection especially
- * generated for connection assumption.
+ * The connection is marked as volatile and will be removed when
+ * it disconnects.
  */
 gboolean
-nm_settings_connection_get_nm_generated_assumed (NMSettingsConnection *self)
+nm_settings_connection_get_volatile (NMSettingsConnection *self)
 {
-	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED);
+	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_VOLATILE);
 }
 
 gboolean
@@ -2673,7 +2747,6 @@ nm_settings_connection_init (NMSettingsConnection *self)
 	priv->seen_bssids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
 	priv->autoconnect_retries = AUTOCONNECT_RETRIES_UNSET;
-	priv->autoconnect_blocked_reason = NM_DEVICE_STATE_REASON_NONE;
 
 	g_signal_connect (self, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), NULL);
 	g_signal_connect (self, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed_cb), NULL);
@@ -2772,13 +2845,8 @@ set_property (GObject *object, guint prop_id,
 	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
 
 	switch (prop_id) {
-	case PROP_READY:
-		nm_settings_connection_set_ready (self, g_value_get_boolean (value));
-		break;
-	case PROP_FLAGS:
-		nm_settings_connection_set_flags_all (self, g_value_get_uint (value));
-		break;
 	case PROP_FILENAME:
+		/* construct-only */
 		nm_settings_connection_set_filename (self, g_value_get_string (value));
 		break;
 	default:
@@ -2822,7 +2890,7 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
 	obj_properties[PROP_READY] =
 	     g_param_spec_boolean (NM_SETTINGS_CONNECTION_READY, "", "",
 	                           TRUE,
-	                           G_PARAM_READWRITE |
+	                           G_PARAM_READABLE |
 	                           G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_FLAGS] =
@@ -2830,13 +2898,14 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
 	                        NM_SETTINGS_CONNECTION_FLAGS_NONE,
 	                        NM_SETTINGS_CONNECTION_FLAGS_ALL,
 	                        NM_SETTINGS_CONNECTION_FLAGS_NONE,
-	                        G_PARAM_READWRITE |
+	                        G_PARAM_READABLE |
 	                        G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_FILENAME] =
 	     g_param_spec_string (NM_SETTINGS_CONNECTION_FILENAME, "", "",
 	                          NULL,
 	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT_ONLY |
 	                          G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index c5ddd817..b449e2bd 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -25,7 +25,7 @@
 #include <net/ethernet.h>
 
 #include "nm-exported-object.h"
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 #define NM_TYPE_SETTINGS_CONNECTION            (nm_settings_connection_get_type ())
 #define NM_SETTINGS_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnection))
@@ -58,9 +58,9 @@
  * @NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED: A connection is "nm-generated" if
  *  it was generated by NetworkManger. If the connection gets modified or saved
  *  by the user, the flag gets cleared. A nm-generated is implicitly unsaved.
- * @NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED: A special kind of "nm-generated"
- *  connection that was specifically created for connection assumption. "nm-generated-assumed"
- *  implies "nm-generated".
+ * @NM_SETTINGS_CONNECTION_FLAGS_VOLATILE: The connection will be deleted
+ *  when it disconnects. That is for in-memory connections (unsaved), which are
+ *  currently active but cleanup on disconnect.
  * @NM_SETTINGS_CONNECTION_FLAGS_ALL: special mask, for all known flags
  *
  * #NMSettingsConnection flags.
@@ -70,7 +70,7 @@ typedef enum
 	NM_SETTINGS_CONNECTION_FLAGS_NONE                               = 0x00,
 	NM_SETTINGS_CONNECTION_FLAGS_UNSAVED                            = 0x01,
 	NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED                       = 0x02,
-	NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED               = 0x04,
+	NM_SETTINGS_CONNECTION_FLAGS_VOLATILE                           = 0x04,
 
 	__NM_SETTINGS_CONNECTION_FLAGS_LAST,
 	NM_SETTINGS_CONNECTION_FLAGS_ALL = ((__NM_SETTINGS_CONNECTION_FLAGS_LAST - 1) << 1) - 1,
@@ -82,6 +82,12 @@ typedef enum { /*< skip >*/
 	NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED                 = (1LL << 1),
 } NMSettingsConnectionCommitReason;
 
+typedef enum {
+	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED               = 0,
+	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED                 = 1,
+	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS              = 2,
+} NMSettingsAutoconnectBlockedReason;
+
 struct _NMSettingsConnectionCallId;
 typedef struct _NMSettingsConnectionCallId *NMSettingsConnectionCallId;
 
@@ -184,6 +190,11 @@ NMSettingsConnectionFlags nm_settings_connection_get_flags (NMSettingsConnection
 NMSettingsConnectionFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set);
 NMSettingsConnectionFlags nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConnectionFlags flags);
 
+int nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *ab);
+int nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data);
+int nm_settings_connection_cmp_autoconnect_priority (NMSettingsConnection *a, NMSettingsConnection *b);
+int nm_settings_connection_cmp_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data);
+
 gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self,
                                                guint64 *out_timestamp);
 
@@ -210,14 +221,14 @@ void nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *sel
 
 gint32 nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self);
 
-NMDeviceStateReason nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self);
-void nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self,
-                                                            NMDeviceStateReason reason);
+NMSettingsAutoconnectBlockedReason nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self);
+void                               nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self,
+                                                                                          NMSettingsAutoconnectBlockedReason reason);
 
 gboolean nm_settings_connection_can_autoconnect (NMSettingsConnection *self);
 
 gboolean nm_settings_connection_get_nm_generated (NMSettingsConnection *self);
-gboolean nm_settings_connection_get_nm_generated_assumed (NMSettingsConnection *self);
+gboolean nm_settings_connection_get_volatile (NMSettingsConnection *self);
 
 gboolean nm_settings_connection_get_ready (NMSettingsConnection *self);
 void     nm_settings_connection_set_ready (NMSettingsConnection *self,
diff --git a/src/settings/nm-settings-plugin.h b/src/settings/nm-settings-plugin.h
index 8abd72ea..97bb7b65 100644
--- a/src/settings/nm-settings-plugin.h
+++ b/src/settings/nm-settings-plugin.h
@@ -22,7 +22,7 @@
 #ifndef __NETWORKMANAGER_SETTINGS_PLUGIN_H__
 #define __NETWORKMANAGER_SETTINGS_PLUGIN_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 /* Plugin's factory function that returns a GObject that implements
  * NMSettingsPlugin.
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 35790a12..afd1b084 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -104,7 +104,7 @@ EXPORT(nm_settings_connection_replace_and_commit)
 #define IFCFG_DIR                    SYSCONFDIR "/sysconfig/network"
 #define CONF_DHCP                    IFCFG_DIR "/dhcp"
 
-#define PLUGIN_MODULE_PATH      "plugin-module-path"
+static NM_CACHED_QUARK_FCN ("plugin-module-path", plugin_module_path_quark)
 
 #if (defined(HOSTNAME_PERSIST_SUSE) + defined(HOSTNAME_PERSIST_SLACKWARE) + defined(HOSTNAME_PERSIST_GENTOO)) > 1
 #error "Can only define one of HOSTNAME_PERSIST_*"
@@ -120,6 +120,9 @@ EXPORT(nm_settings_connection_replace_and_commit)
 #define HOSTNAME_FILE           HOSTNAME_FILE_DEFAULT
 #endif
 
+static NM_CACHED_QUARK_FCN ("default-wired-connection", _default_wired_connection_quark)
+static NM_CACHED_QUARK_FCN ("default-wired-device", _default_wired_device_quark)
+
 /*****************************************************************************/
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMSettings,
@@ -390,35 +393,6 @@ error:
 	g_clear_object (&subject);
 }
 
-static int
-connection_sort (gconstpointer pa, gconstpointer pb)
-{
-	NMConnection *a = NM_CONNECTION (pa);
-	NMSettingConnection *con_a;
-	NMConnection *b = NM_CONNECTION (pb);
-	NMSettingConnection *con_b;
-	guint64 ts_a = 0, ts_b = 0;
-	gboolean can_ac_a, can_ac_b;
-
-	con_a = nm_connection_get_setting_connection (a);
-	g_assert (con_a);
-	con_b = nm_connection_get_setting_connection (b);
-	g_assert (con_b);
-
-	can_ac_a = !!nm_setting_connection_get_autoconnect (con_a);
-	can_ac_b = !!nm_setting_connection_get_autoconnect (con_b);
-	if (can_ac_a != can_ac_b)
-		return can_ac_a ? -1 : 1;
-
-	nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pa), &ts_a);
-	nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pb), &ts_b);
-	if (ts_a > ts_b)
-		return -1;
-	else if (ts_a == ts_b)
-		return 0;
-	return 1;
-}
-
 /**
  * nm_settings_get_connections:
  * @self: the #NMSettings
@@ -444,46 +418,98 @@ nm_settings_get_connections (NMSettings *self, guint *out_len)
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	if (priv->connections_cached_list) {
+	if (G_LIKELY (priv->connections_cached_list)) {
 		NM_SET_OUT (out_len, g_hash_table_size (priv->connections));
 		return priv->connections_cached_list;
 	}
 
 	l = g_hash_table_size (priv->connections);
 
-	v = g_new (NMSettingsConnection *, l + 1);
+	v = g_new (NMSettingsConnection *, (gsize) l + 1);
 
 	i = 0;
 	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &con))
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &con)) {
+		nm_assert (i < l);
 		v[i++] = con;
-	v[i] = NULL;
-
+	}
 	nm_assert (i == l);
+	v[i] = NULL;
 
 	NM_SET_OUT (out_len, l);
 	priv->connections_cached_list = v;
 	return v;
 }
 
+/**
+ * nm_settings_get_connections_clone:
+ * @self: the #NMSetting
+ * @out_len: (allow-none): optional output argument
+ * @func: caller-supplied function for filtering connections
+ * @func_data: caller-supplied data passed to @func
+ *
+ * Returns: (transfer container) (element-type NMSettingsConnection):
+ *   an NULL terminated array of #NMSettingsConnection objects that were
+ *   filtered by @func (or all connections if no filter was specified).
+ *   The order is arbitrary.
+ *   Caller is responsible for freeing the returned array with free(),
+ *   the contained values do not need to be unrefed.
+ */
+NMSettingsConnection **
+nm_settings_get_connections_clone (NMSettings *self,
+                                   guint *out_len,
+                                   NMSettingsConnectionFilterFunc func,
+                                   gpointer func_data)
+{
+	NMSettingsConnection *const*list_cached;
+	NMSettingsConnection **list;
+	guint len, i, j;
+
+	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
+
+	list_cached = nm_settings_get_connections (self, &len);
+
+#if NM_MORE_ASSERTS
+	nm_assert (list_cached);
+	for (i = 0; i < len; i++)
+		nm_assert (NM_IS_SETTINGS_CONNECTION (list_cached[i]));
+	nm_assert (!list_cached[i]);
+#endif
+
+	list = g_new (NMSettingsConnection *, ((gsize) len + 1));
+	if (func) {
+		for (i = 0, j = 0; i < len; i++) {
+			if (func (self, list_cached[i], func_data))
+				list[j++] = list_cached[i];
+		}
+		list[j] = NULL;
+		len = j;
+	} else
+		memcpy (list, list_cached, sizeof (list[0]) * ((gsize) len + 1));
+
+	NM_SET_OUT (out_len, len);
+	return list;
+}
+
 /* Returns a list of NMSettingsConnections.
  * The list is sorted in the order suitable for auto-connecting, i.e.
  * first go connections with autoconnect=yes and most recent timestamp.
- * Caller must free the list with g_slist_free().
+ * Caller must free the list with g_free(), but not the list items.
  */
-GSList *
-nm_settings_get_connections_sorted (NMSettings *self)
+NMSettingsConnection **
+nm_settings_get_connections_sorted (NMSettings *self, guint *out_len)
 {
-	GHashTableIter iter;
-	gpointer data = NULL;
-	GSList *list = NULL;
+	NMSettingsConnection **connections;
+	guint len;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
 
-	g_hash_table_iter_init (&iter, NM_SETTINGS_GET_PRIVATE (self)->connections);
-	while (g_hash_table_iter_next (&iter, NULL, &data))
-		list = g_slist_insert_sorted (list, data, connection_sort);
-	return list;
+	connections = nm_settings_get_connections_clone (self, &len, NULL, NULL);
+	if (len > 1)
+		g_qsort_with_data (connections, len, sizeof (NMSettingsConnection *), nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
+
+	NM_SET_OUT (out_len, len);
+	return connections;
 }
 
 NMSettingsConnection *
@@ -753,7 +779,7 @@ add_plugin (NMSettings *self, NMSettingsPlugin *plugin)
 	              NM_SETTINGS_PLUGIN_INFO, &pinfo,
 	              NULL);
 
-	path = g_object_get_data (G_OBJECT (plugin), PLUGIN_MODULE_PATH);
+	path = g_object_get_qdata (G_OBJECT (plugin), plugin_module_path_quark ());
 
 	_LOGI ("loaded plugin %s: %s%s%s%s", pname, pinfo,
 	       NM_PRINT_FMT_QUOTED (path, " (", path, ")", ""));
@@ -809,8 +835,8 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 	gboolean has_no_ibft;
 	gssize idx_no_ibft, idx_ibft;
 
-	idx_ibft    = _nm_utils_strv_find_first ((char **) plugins, -1, "ibft");
-	idx_no_ibft = _nm_utils_strv_find_first ((char **) plugins, -1, "no-ibft");
+	idx_ibft    = nm_utils_strv_find_first ((char **) plugins, -1, "ibft");
+	idx_no_ibft = nm_utils_strv_find_first ((char **) plugins, -1, "no-ibft");
 	has_no_ibft = idx_no_ibft >= 0 && idx_no_ibft > idx_ibft;
 #if WITH_SETTINGS_PLUGIN_IBFT
 	add_ibft = idx_no_ibft < 0 && idx_ibft < 0;
@@ -844,9 +870,9 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 			continue;
 		}
 
-		if (_nm_utils_strv_find_first ((char **) plugins,
-		                               iter - plugins,
-		                               pname) >= 0) {
+		if (nm_utils_strv_find_first ((char **) plugins,
+		                              iter - plugins,
+		                              pname) >= 0) {
 			/* the plugin is already mentioned in the list previously.
 			 * Don't load a duplicate. */
 			continue;
@@ -916,7 +942,7 @@ load_plugin:
 				break;
 			}
 
-			g_object_set_data_full (obj, PLUGIN_MODULE_PATH, path, g_free);
+			g_object_set_qdata_full (obj, plugin_module_path_quark (), path, g_free);
 			path = NULL;
 			if (add_plugin (self, NM_SETTINGS_PLUGIN (obj)))
 				list = g_slist_append (list, obj);
@@ -1659,6 +1685,28 @@ nm_settings_set_transient_hostname (NMSettings *self,
 	                   info);
 }
 
+gboolean
+nm_settings_get_transient_hostname (NMSettings *self, char **hostname)
+{
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	GVariant *v_hostname;
+
+	if (!priv->hostname.hostnamed_proxy)
+		return FALSE;
+
+	v_hostname = g_dbus_proxy_get_cached_property (priv->hostname.hostnamed_proxy,
+	                                               "Hostname");
+	if (!v_hostname) {
+		_LOGT ("transient hostname retrieval failed");
+		return FALSE;
+	}
+
+	*hostname = g_variant_dup_string (v_hostname, NULL);
+	g_variant_unref (v_hostname);
+
+	return TRUE;
+}
+
 static gboolean
 write_hostname (NMSettingsPrivate *priv, const char *hostname)
 {
@@ -1935,9 +1983,6 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 	return FALSE;
 }
 
-#define DEFAULT_WIRED_CONNECTION_TAG "default-wired-connection"
-#define DEFAULT_WIRED_DEVICE_TAG     "default-wired-device"
-
 static void default_wired_clear_tag (NMSettings *self,
                                      NMDevice *device,
                                      NMSettingsConnection *connection,
@@ -1953,7 +1998,7 @@ default_wired_connection_removed_cb (NMSettingsConnection *connection, NMSetting
 	 * wired device to the config file and don't create a new default wired
 	 * connection for that device again.
 	 */
-	device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG);
+	device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
 	if (device)
 		default_wired_clear_tag (self, device, connection, TRUE);
 }
@@ -1970,7 +2015,7 @@ default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, g
 	 * considered a default wired connection, and should no longer affect
 	 * the no-auto-default configuration option.
 	 */
-	device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG);
+	device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
 	if (device)
 		default_wired_clear_tag (self, device, connection, FALSE);
 }
@@ -1984,11 +2029,11 @@ default_wired_clear_tag (NMSettings *self,
 	g_return_if_fail (NM_IS_SETTINGS (self));
 	g_return_if_fail (NM_IS_DEVICE (device));
 	g_return_if_fail (NM_IS_CONNECTION (connection));
-	g_return_if_fail (device == g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG));
-	g_return_if_fail (connection == g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG));
+	g_return_if_fail (device == g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ()));
+	g_return_if_fail (connection == g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ()));
 
-	g_object_set_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG, NULL);
-	g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, NULL);
+	g_object_set_qdata (G_OBJECT (connection), _default_wired_device_quark (), NULL);
+	g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL);
 
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_removed_cb), self);
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
@@ -2015,7 +2060,7 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
 	 * ignore it.
 	 */
 	if (   !nm_device_get_managed (device, FALSE)
-	    || g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG)
+	    || g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ())
 	    || have_connection_for_device (self, device))
 		return;
 
@@ -2037,8 +2082,8 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
 		return;
 	}
 
-	g_object_set_data (G_OBJECT (added), DEFAULT_WIRED_DEVICE_TAG, device);
-	g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, added);
+	g_object_set_qdata (G_OBJECT (added), _default_wired_device_quark (), device);
+	g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), added);
 
 	g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
 	                  G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
@@ -2071,7 +2116,7 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin
 	                                      G_CALLBACK (device_realized),
 	                                      self);
 
-	connection = g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG);
+	connection = g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ());
 	if (connection) {
 		default_wired_clear_tag (self, device, connection, FALSE);
 
@@ -2085,107 +2130,6 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin
 
 /*****************************************************************************/
 
-/* GCompareFunc helper for sorting "best" connections.
- * The function sorts connections in ascending timestamp order.
- * That means an older connection (lower timestamp) goes before
- * a newer one.
- */
-gint
-nm_settings_sort_connections (gconstpointer a, gconstpointer b)
-{
-	NMSettingsConnection *ac = (NMSettingsConnection *) a;
-	NMSettingsConnection *bc = (NMSettingsConnection *) b;
-	guint64 ats = 0, bts = 0;
-
-	if (ac == bc)
-		return 0;
-	if (!ac)
-		return -1;
-	if (!bc)
-		return 1;
-
-	/* In the future we may use connection priorities in addition to timestamps */
-	nm_settings_connection_get_timestamp (ac, &ats);
-	nm_settings_connection_get_timestamp (bc, &bts);
-
-	if (ats < bts)
-		return -1;
-	else if (ats > bts)
-		return 1;
-	return 0;
-}
-
-/**
- * nm_settings_get_best_connections:
- * @self: the #NMSetting
- * @max_requested: if non-zero, the maximum number of connections to return
- * @ctype1: an #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME) to
- *   filter connections against
- * @ctype2: a second #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME)
- *   to filter connections against
- * @func: caller-supplied function for filtering connections
- * @func_data: caller-supplied data passed to @func
- *
- * Returns: a #GSList of #NMConnection objects in sorted order representing the
- *   "best" or highest-priority connections filtered by @ctype1 and/or @ctype2,
- *   and/or @func.  Caller is responsible for freeing the returned #GSList, but
- *   the contained values do not need to be unreffed.
- */
-GSList *
-nm_settings_get_best_connections (NMSettings *self,
-                                  guint max_requested,
-                                  const char *ctype1,
-                                  const char *ctype2,
-                                  NMConnectionFilterFunc func,
-                                  gpointer func_data)
-{
-	NMSettingsPrivate *priv;
-	GSList *sorted = NULL;
-	GHashTableIter iter;
-	NMSettingsConnection *connection;
-	guint added = 0;
-	guint64 oldest = 0;
-
-	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
-
-	priv = NM_SETTINGS_GET_PRIVATE (self);
-
-	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) {
-		guint64 cur_ts = 0;
-
-		if (ctype1 && !nm_connection_is_type (NM_CONNECTION (connection), ctype1))
-			continue;
-		if (ctype2 && !nm_connection_is_type (NM_CONNECTION (connection), ctype2))
-			continue;
-		if (func && !func (self, NM_CONNECTION (connection), func_data))
-			continue;
-
-		/* Don't bother with a connection that's older than the oldest one in the list */
-		if (max_requested && added >= max_requested) {
-		    nm_settings_connection_get_timestamp (connection, &cur_ts);
-		    if (cur_ts <= oldest)
-				continue;
-		}
-
-		/* List is sorted with oldest first */
-		sorted = g_slist_insert_sorted (sorted, connection, nm_settings_sort_connections);
-		added++;
-
-		if (max_requested && added > max_requested) {
-			/* Over the limit, remove the oldest one */
-			sorted = g_slist_delete_link (sorted, sorted);
-			added--;
-		}
-
-		nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (sorted->data), &oldest);
-	}
-
-	return g_slist_reverse (sorted);
-}
-
-/*****************************************************************************/
-
 gboolean
 nm_settings_get_startup_complete (NMSettings *self)
 {
@@ -2221,7 +2165,7 @@ hostnamed_properties_changed (GDBusProxy *proxy,
 		g_free (priv->hostname.value);
 		priv->hostname.value = g_strdup (hostname);
 		_notify (self, PROP_HOSTNAME);
-		nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL, NULL, NULL);
+		nm_dispatcher_call_hostname (NULL, NULL, NULL);
 	}
 
 	g_variant_unref (v_hostname);
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index c8a7ebae..7110a12b 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -26,7 +26,7 @@
 #ifndef __NM_SETTINGS_H__
 #define __NM_SETTINGS_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 #include "nm-exported-object.h"
 
@@ -57,9 +57,9 @@
  *
  * Returns: %TRUE to allow the connection, %FALSE to ignore it
  */
-typedef gboolean (*NMConnectionFilterFunc) (NMSettings *settings,
-                                            NMConnection *connection,
-                                            gpointer func_data);
+typedef gboolean (*NMSettingsConnectionFilterFunc) (NMSettings *settings,
+                                                    NMSettingsConnection *connection,
+                                                    gpointer func_data);
 
 typedef struct _NMSettingsClass NMSettingsClass;
 
@@ -97,14 +97,13 @@ void nm_settings_add_connection_dbus (NMSettings *self,
 
 NMSettingsConnection *const* nm_settings_get_connections (NMSettings *settings, guint *out_len);
 
-GSList *nm_settings_get_connections_sorted (NMSettings *settings);
+NMSettingsConnection **nm_settings_get_connections_clone (NMSettings *self,
+                                                          guint *out_len,
+                                                          NMSettingsConnectionFilterFunc func,
+                                                          gpointer func_data);
 
-GSList *nm_settings_get_best_connections (NMSettings *self,
-                                          guint max_requested,
-                                          const char *ctype1,
-                                          const char *ctype2,
-                                          NMConnectionFilterFunc func,
-                                          gpointer func_data);
+NMSettingsConnection **nm_settings_get_connections_sorted (NMSettings *self,
+                                                           guint *out_len);
 
 NMSettingsConnection *nm_settings_add_connection (NMSettings *settings,
                                                   NMConnection *connection,
@@ -126,8 +125,6 @@ void nm_settings_device_added (NMSettings *self, NMDevice *device);
 
 void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting);
 
-gint nm_settings_sort_connections (gconstpointer a, gconstpointer b);
-
 gboolean nm_settings_get_startup_complete (NMSettings *self);
 
 void nm_settings_set_transient_hostname (NMSettings *self,
@@ -135,4 +132,7 @@ void nm_settings_set_transient_hostname (NMSettings *self,
                                          NMSettingsSetHostnameCb cb,
                                          gpointer user_data);
 
+gboolean nm_settings_get_transient_hostname (NMSettings *self,
+                                             char **hostname);
+
 #endif  /* __NM_SETTINGS_H__ */
diff --git a/src/settings/plugins/ibft/nms-ibft-reader.h b/src/settings/plugins/ibft/nms-ibft-reader.h
index e2be7b02..27500cc5 100644
--- a/src/settings/plugins/ibft/nms-ibft-reader.h
+++ b/src/settings/plugins/ibft/nms-ibft-reader.h
@@ -21,7 +21,7 @@
 #ifndef __NMS_IBFT_READER_H__
 #define __NMS_IBFT_READER_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 gboolean nms_ibft_reader_load_blocks (const char *iscsiadm_path,
                                       GSList **out_blocks,
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
index 33f4847e..b54f9549 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
@@ -336,43 +336,24 @@ commit_changes (NMSettingsConnection *connection,
                 gpointer user_data)
 {
 	GError *error = NULL;
-	NMConnection *reread;
-	gboolean same = FALSE, success = FALSE;
+	gboolean success = FALSE;
 	char *ifcfg_path = NULL;
 	const char *filename;
 
-	/* To ensure we don't rewrite files that are only changed from other
-	 * processes on-disk, read the existing connection back in and only rewrite
-	 * it if it's really changed.
-	 */
 	filename = nm_settings_connection_get_filename (connection);
 	if (filename) {
-		gs_free char *unhandled = NULL;
-
-		reread = connection_from_file (filename, &unhandled, NULL, NULL);
-		if (reread) {
-			same = nm_connection_compare (NM_CONNECTION (connection),
-			                              reread,
-			                              NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
-			                                 NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS);
-			g_object_unref (reread);
-
-			/* Don't bother writing anything out if in-memory and on-disk data are the same */
-			if (same) {
-				/* But chain up to parent to handle success - emits updated signal */
-				NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->commit_changes (connection, commit_reason, callback, user_data);
-				return;
-			}
-		}
-
 		success = writer_update_connection (NM_CONNECTION (connection),
 		                                    IFCFG_DIR,
 		                                    filename,
+		                                    NULL,
+		                                    NULL,
 		                                    &error);
 	} else {
 		success = writer_new_connection (NM_CONNECTION (connection),
 		                                 IFCFG_DIR,
 		                                 &ifcfg_path,
+		                                 NULL,
+		                                 NULL,
 		                                 &error);
 		if (success) {
 			nm_settings_connection_set_filename (connection, ifcfg_path);
@@ -478,6 +459,11 @@ nm_ifcfg_connection_new (NMConnection *source,
 	if (out_ignore_error)
 		*out_ignore_error = FALSE;
 
+	if (full_path) {
+		/* The connection already is on the disk */
+		update_unsaved = FALSE;
+	}
+
 	/* If we're given a connection already, prefer that instead of re-reading */
 	if (source)
 		tmp = g_object_ref (source);
@@ -488,9 +474,6 @@ nm_ifcfg_connection_new (NMConnection *source,
 		                            out_ignore_error);
 		if (!tmp)
 			return NULL;
-
-		/* If we just read the connection from disk, it's clearly not Unsaved */
-		update_unsaved = FALSE;
 	}
 
 	if (unhandled_spec && g_str_has_prefix (unhandled_spec, "unmanaged:"))
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
index 414f593b..a3092e71 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -98,7 +98,7 @@ NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfcfg, settings_plugin_ifcfg_get, SETT
 #define _NMLOG_DOMAIN  LOGD_SETTINGS
 #define _NMLOG(level, ...) \
     G_STMT_START { \
-        nm_log ((level), (_NMLOG_DOMAIN), \
+        nm_log ((level), (_NMLOG_DOMAIN), NULL, NULL, \
                 "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                 "ifcfg-rh: " \
                 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
@@ -687,7 +687,7 @@ add_connection (NMSettingsPlugin *config,
 		return NULL;
 
 	if (save_to_disk) {
-		if (!writer_new_connection (connection, IFCFG_DIR, &path, error))
+		if (!writer_new_connection (connection, IFCFG_DIR, &path, NULL, NULL, error))
 			return NULL;
 	}
 	return NM_SETTINGS_CONNECTION (update_connection (self, connection, path, NULL, FALSE, NULL, error));
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index 46bd2de1..164f6844 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -48,6 +48,7 @@
 #include "nm-setting-bridge.h"
 #include "nm-setting-bridge-port.h"
 #include "nm-setting-dcb.h"
+#include "nm-setting-user.h"
 #include "nm-setting-proxy.h"
 #include "nm-setting-generic.h"
 #include "nm-core-internal.h"
@@ -66,7 +67,7 @@
 #define _NMLOG_PREFIX_NAME "ifcfg-rh"
 #define _NMLOG(level, ...) \
     G_STMT_START { \
-        nm_log ((level), (_NMLOG_DOMAIN), \
+        nm_log ((level), (_NMLOG_DOMAIN), NULL, NULL, \
                 "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                 _NMLOG_PREFIX_NAME": " \
                 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
@@ -97,14 +98,13 @@ make_connection_name (shvarFile *ifcfg,
 	char *full_name = NULL, *name;
 
 	/* If the ifcfg file already has a NAME, always use that */
-	name = svGetValueString (ifcfg, "NAME");
-	if (name && strlen (name))
+	name = svGetValueStr_cp (ifcfg, "NAME");
+	if (name)
 		return name;
 
 	/* Otherwise construct a new NAME */
-	g_free (name);
 	if (!prefix)
-		prefix = _("System");
+		prefix = "System";
 
 	/* For cosmetic reasons, if the suggested name is the same as
 	 * the ifcfg files name, don't use it.  Mainly for wifi so that
@@ -142,22 +142,18 @@ make_connection_setting (const char *file,
 	g_free (new_id);
 
 	/* Try for a UUID key before falling back to hashing the file name */
-	uuid = svGetValueString (ifcfg, "UUID");
-	if (!uuid || !strlen (uuid)) {
-		g_free (uuid);
+	uuid = svGetValueStr_cp (ifcfg, "UUID");
+	if (!uuid)
 		uuid = nm_utils_uuid_generate_from_string (svFileGetName (ifcfg), -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
-	}
-
-	stable_id = svGetValueString (ifcfg, "STABLE_ID");
 
 	g_object_set (s_con,
 	              NM_SETTING_CONNECTION_TYPE, type,
 	              NM_SETTING_CONNECTION_UUID, uuid,
-	              NM_SETTING_CONNECTION_STABLE_ID, stable_id,
+	              NM_SETTING_CONNECTION_STABLE_ID, svGetValue (ifcfg, "STABLE_ID", &stable_id),
 	              NULL);
 	g_free (uuid);
 
-	value = svGetValueString (ifcfg, "DEVICE");
+	value = svGetValueStr_cp (ifcfg, "DEVICE");
 	if (value) {
 		GError *error = NULL;
 
@@ -172,7 +168,7 @@ make_connection_setting (const char *file,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "LLDP");
+	value = svGetValueStr_cp (ifcfg, "LLDP");
 	if (!g_strcmp0 (value, "rx"))
 		lldp = NM_SETTING_CONNECTION_LLDP_ENABLE_RX;
 	else
@@ -196,7 +192,7 @@ make_connection_setting (const char *file,
 	              NM_SETTING_CONNECTION_LLDP, lldp,
 	              NULL);
 
-	value = svGetValueString (ifcfg, "USERS");
+	value = svGetValueStr_cp (ifcfg, "USERS");
 	if (value) {
 		char **items, **iter;
 
@@ -212,15 +208,11 @@ make_connection_setting (const char *file,
 	}
 
 
-	zone = svGetValueString (ifcfg, "ZONE");
-	if (!zone || !strlen (zone)) {
-		g_free (zone);
-		zone = NULL;
-	}
+	zone = svGetValueStr_cp (ifcfg, "ZONE");
 	g_object_set (s_con, NM_SETTING_CONNECTION_ZONE, zone, NULL);
 	g_free (zone);
 
-	value = svGetValueString (ifcfg, "SECONDARY_UUIDS");
+	value = svGetValueStr_cp (ifcfg, "SECONDARY_UUIDS");
 	if (value) {
 		char **items, **iter;
 
@@ -235,9 +227,9 @@ make_connection_setting (const char *file,
 		g_strfreev (items);
 	}
 
-	value = svGetValueString (ifcfg, "BRIDGE_UUID");
+	value = svGetValueStr_cp (ifcfg, "BRIDGE_UUID");
 	if (!value)
-		value = svGetValueString (ifcfg, "BRIDGE");
+		value = svGetValueStr_cp (ifcfg, "BRIDGE");
 	if (value) {
 		const char *old_value;
 
@@ -252,7 +244,7 @@ make_connection_setting (const char *file,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "GATEWAY_PING_TIMEOUT");
+	value = svGetValueStr_cp (ifcfg, "GATEWAY_PING_TIMEOUT");
 	if (value) {
 		gint64 tmp;
 
@@ -280,75 +272,82 @@ make_connection_setting (const char *file,
 static gboolean
 read_ip4_address (shvarFile *ifcfg,
                   const char *tag,
-                  char **out_addr,
+                  gboolean *out_has_key,
+                  guint32 *out_addr,
                   GError **error)
 {
-	char *value = NULL;
-
-	g_return_val_if_fail (ifcfg != NULL, FALSE);
-	g_return_val_if_fail (tag != NULL, FALSE);
-	g_return_val_if_fail (out_addr != NULL, FALSE);
-	g_return_val_if_fail (!error || !*error, FALSE);
+	gs_free char *value_to_free = NULL;
+	const char *value;
+	guint32 a;
 
-	*out_addr = NULL;
+	nm_assert (ifcfg);
+	nm_assert (tag);
+	nm_assert (!error || !*error);
 
-	value = svGetValueString (ifcfg, tag);
-	if (!value)
+	value = svGetValueStr (ifcfg, tag, &value_to_free);
+	if (!value) {
+		NM_SET_OUT (out_has_key, FALSE);
+		NM_SET_OUT (out_addr, 0);
 		return TRUE;
+	}
 
-	if (nm_utils_ipaddr_valid (AF_INET, value)) {
-		*out_addr = value;
-		return TRUE;
-	} else {
+	if (inet_pton (AF_INET, value, &a) != 1) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Invalid %s IP4 address '%s'", tag, value);
-		g_free (value);
 		return FALSE;
 	}
+
+	NM_SET_OUT (out_has_key, TRUE);
+	NM_SET_OUT (out_addr, a);
+	return TRUE;
 }
 
-static char *
-get_numbered_tag (char *tag_name, int which)
+static void
+_numbered_tag (char *buf, gsize buf_len, const char *tag_name, int which)
 {
-	if (which == -1)
-		return g_strdup (tag_name);
-	return g_strdup_printf ("%s%u", tag_name, which);
+	gsize l;
+
+	l = g_strlcpy (buf, tag_name, buf_len);
+	nm_assert (l < buf_len);
+	if (which != -1) {
+		buf_len -= l;
+		l = g_snprintf (&buf[l], buf_len, "%d", which);
+		nm_assert (l < buf_len);
+	}
 }
+#define numbered_tag(buf, tag_name, which) \
+	({ \
+		_nm_unused char *const _buf = (buf); \
+		\
+		/* some static assert trying to ensure that the buffer is statically allocated.
+		 * It disallows a buffer size of sizeof(gpointer) to catch that. */ \
+		G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) != sizeof (char *) && sizeof (buf) < G_MAXINT); \
+		_numbered_tag (buf, sizeof (buf), ""tag_name"", (which)); \
+		buf; \
+	})
 
 static gboolean
 is_any_ip4_address_defined (shvarFile *ifcfg, int *idx)
 {
-	int i, ignore, *ret_idx;;
+	int i, ignore, *ret_idx;
 
 	ret_idx = idx ? idx : &ignore;
 
 	for (i = -1; i <= 2; i++) {
-		char *tag;
-		char *value;
+		gs_free char *value = NULL;
+		char tag[256];
 
-		tag = get_numbered_tag ("IPADDR", i);
-		value = svGetValueString (ifcfg, tag);
-		g_free (tag);
-		if (value) {
-			g_free (value);
+		if (svGetValueStr (ifcfg, numbered_tag (tag, "IPADDR", i), &value)) {
 			*ret_idx = i;
 			return TRUE;
 		}
 
-		tag = get_numbered_tag ("PREFIX", i);
-		value = svGetValueString (ifcfg, tag);
-		g_free(tag);
-		if (value) {
-			g_free (value);
+		if (svGetValueStr (ifcfg, numbered_tag (tag, "PREFIX", i), &value)) {
 			*ret_idx = i;
 			return TRUE;
 		}
 
-		tag = get_numbered_tag ("NETMASK", i);
-		value = svGetValueString (ifcfg, tag);
-		g_free(tag);
-		if (value) {
-			g_free (value);
+		if (svGetValueStr (ifcfg, numbered_tag (tag, "NETMASK", i), &value)) {
 			*ret_idx = i;
 			return TRUE;
 		}
@@ -365,12 +364,14 @@ read_full_ip4_address (shvarFile *ifcfg,
                        char **out_gateway,
                        GError **error)
 {
-	char *ip_tag, *prefix_tag, *netmask_tag, *gw_tag;
-	char *ip = NULL;
+	char tag[256];
+	char prefix_tag[256];
+	guint32 ipaddr;
+	gs_free char *value = NULL;
 	int prefix = 0;
-	gboolean success = FALSE;
-	char *value;
-	guint32 tmp;
+	gboolean has_key;
+	guint32 a;
+	char inet_buf[NM_UTILS_INET_ADDRSTRLEN];
 
 	g_return_val_if_fail (which >= -1, FALSE);
 	g_return_val_if_fail (ifcfg != NULL, FALSE);
@@ -378,76 +379,186 @@ read_full_ip4_address (shvarFile *ifcfg,
 	g_return_val_if_fail (*out_address == NULL, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
-	ip_tag = get_numbered_tag ("IPADDR", which);
-	prefix_tag = get_numbered_tag ("PREFIX", which);
-	netmask_tag = get_numbered_tag ("NETMASK", which);
-	gw_tag = get_numbered_tag ("GATEWAY", which);
-
 	/* IP address */
-	if (!read_ip4_address (ifcfg, ip_tag, &ip, error))
-		goto done;
-	if (!ip) {
-		if (base_addr)
-			ip = g_strdup (nm_ip_address_get_address (base_addr));
-		else {
-			success = TRUE;
-			goto done;
-		}
+	if (!read_ip4_address (ifcfg,
+	                       numbered_tag (tag, "IPADDR", which),
+	                       &has_key, &ipaddr, error))
+		return FALSE;
+	if (!has_key) {
+		if (!base_addr)
+			return TRUE;
+		nm_ip_address_get_address_binary (base_addr, &ipaddr);
 	}
 
 	/* Gateway */
 	if (out_gateway && !*out_gateway) {
-		if (!read_ip4_address (ifcfg, gw_tag, out_gateway, error))
-			goto done;
+		if (!read_ip4_address (ifcfg,
+		                       numbered_tag (tag, "GATEWAY", which),
+		                       &has_key, &a, error))
+			return FALSE;
+		if (has_key)
+			*out_gateway = g_strdup (nm_utils_inet4_ntop (a, inet_buf));
 	}
 
 	/* Prefix */
-	value = svGetValueString (ifcfg, prefix_tag);
+	numbered_tag (prefix_tag, "PREFIX", which);
+	value = svGetValueStr_cp (ifcfg, prefix_tag);
 	if (value) {
 		prefix = _nm_utils_ascii_str_to_int64 (value, 10, 0, 32, -1);
 		if (prefix < 0) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid IP4 prefix '%s'", value);
-			g_free (value);
-			goto done;
+			return FALSE;
 		}
-		g_free (value);
 	} else {
 		/* Fall back to NETMASK if no PREFIX was specified */
-		if (!read_ip4_address (ifcfg, netmask_tag, &value, error))
-			goto done;
-		if (value) {
-			inet_pton (AF_INET, value, &tmp);
-			prefix = nm_utils_ip4_netmask_to_prefix (tmp);
-			g_free (value);
-		} else {
+		if (!read_ip4_address (ifcfg,
+		                       numbered_tag (tag, "NETMASK", which),
+		                       &has_key, &a, error))
+			return FALSE;
+		if (has_key)
+			prefix = nm_utils_ip4_netmask_to_prefix (a);
+		else {
 			if (base_addr)
 				prefix = nm_ip_address_get_prefix (base_addr);
 			else {
 				/* Try to autodetermine the prefix for the address' class */
-				if (inet_pton (AF_INET, ip, &tmp) == 1) {
-					prefix = nm_utils_ip4_get_default_prefix (tmp);
-
-					PARSE_WARNING ("missing %s, assuming %s/%d", prefix_tag, ip, prefix);
-				} else {
-					g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-					             "Missing IP4 prefix");
-					goto done;
-				}
+				prefix = nm_utils_ip4_get_default_prefix (ipaddr);
+				PARSE_WARNING ("missing %s, assuming %s/%d", prefix_tag, nm_utils_inet4_ntop (ipaddr, inet_buf), prefix);
 			}
 		}
 	}
 
-	*out_address = nm_ip_address_new (AF_INET, ip, prefix, error);
+	*out_address = nm_ip_address_new_binary (AF_INET, &ipaddr, prefix, error);
 	if (*out_address)
-		success = TRUE;
+		return TRUE;
 
-done:
-	g_free (ip);
-	g_free (ip_tag);
-	g_free (prefix_tag);
-	g_free (netmask_tag);
-	g_free (gw_tag);
+	return FALSE;
+}
+
+/*
+ * Use looser syntax to comprise all the possibilities.
+ * The validity must be checked after the match.
+ */
+#define IPV4_ADDR_REGEX "(?:[0-9]{1,3}\\.){3}[0-9]{1,3}"
+#define IPV6_ADDR_REGEX "[0-9A-Fa-f:.]+"
+
+/*
+ * NOTE: The regexes below don't describe all variants allowed by 'ip route add',
+ * namely destination IP without 'to' keyword is recognized just at line start.
+ */
+
+static gboolean
+parse_route_options (NMIPRoute *route, int family, const char *line, GError **error)
+{
+	GRegex *regex = NULL;
+	GMatchInfo *match_info = NULL;
+	gboolean success = FALSE;
+	static const char *metrics[] = { NM_IP_ROUTE_ATTRIBUTE_WINDOW, NM_IP_ROUTE_ATTRIBUTE_CWND,
+	                                 NM_IP_ROUTE_ATTRIBUTE_INITCWND, NM_IP_ROUTE_ATTRIBUTE_INITRWND,
+	                                 NM_IP_ROUTE_ATTRIBUTE_MTU, NULL };
+	char buffer[1024];
+	int i;
+
+	g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE);
+
+	for (i = 0; metrics[i]; i++) {
+		nm_sprintf_buf (buffer, "(?:\\s|^)%s\\s+(lock\\s+)?(\\d+)(?:$|\\s)", metrics[i]);
+		regex = g_regex_new (buffer, 0, 0, NULL);
+		g_regex_match (regex, line, 0, &match_info);
+		if (g_match_info_matches (match_info)) {
+			gs_free char *lock = g_match_info_fetch (match_info, 1);
+			gs_free char *str = g_match_info_fetch (match_info, 2);
+			gint64 num = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXUINT32, -1);
+
+			if (num == -1) {
+				g_match_info_free (match_info);
+				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				             "Invalid route %s '%s'", metrics[i], str);
+				goto out;
+			}
+
+			nm_ip_route_set_attribute (route, metrics[i],
+			                           g_variant_new_uint32 (num));
+			if (lock && lock[0]) {
+				nm_sprintf_buf (buffer, "lock-%s", metrics[i]);
+				nm_ip_route_set_attribute (route, buffer,
+				                           g_variant_new_boolean (TRUE));
+			}
+		}
+		g_clear_pointer (&regex, g_regex_unref);
+		g_clear_pointer (&match_info, g_match_info_free);
+	}
+
+	/* tos */
+	regex = g_regex_new ("(?:\\s|^)tos\\s+(\\S+)(?:$|\\s)", 0, 0, NULL);
+	g_regex_match (regex, line, 0, &match_info);
+	if (g_match_info_matches (match_info)) {
+		gs_free char *str = g_match_info_fetch (match_info, 1);
+		gint64 num = _nm_utils_ascii_str_to_int64 (str, 0, 0, G_MAXUINT8, -1);
+
+		if (num == -1) {
+			g_match_info_free (match_info);
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Invalid route %s '%s'", "tos", str);
+			goto out;
+		}
+		nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_TOS,
+		                           g_variant_new_byte ((guchar) num));
+	}
+	g_clear_pointer (&regex, g_regex_unref);
+	g_clear_pointer (&match_info, g_match_info_free);
+
+	/* from */
+	if (family == AF_INET6) {
+		regex = g_regex_new ("(?:\\s|^)from\\s+(" IPV6_ADDR_REGEX "(?:/\\d{1,3})?)(?:$|\\s)", 0, 0, NULL);
+		g_regex_match (regex, line, 0, &match_info);
+		if (g_match_info_matches (match_info)) {
+			gs_free char *str = g_match_info_fetch (match_info, 1);
+			gs_free_error GError *local_error = NULL;
+			GVariant *variant = g_variant_new_string (str);
+
+			if (!nm_ip_route_attribute_validate (NM_IP_ROUTE_ATTRIBUTE_FROM, variant, family, NULL, &local_error)) {
+				g_match_info_free (match_info);
+				g_variant_unref (variant);
+				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				             "Invalid route from '%s': %s", str, local_error->message);
+				goto out;
+			}
+			nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_FROM, variant);
+		}
+		g_clear_pointer (&regex, g_regex_unref);
+		g_clear_pointer (&match_info, g_match_info_free);
+	}
+
+	if (family == AF_INET)
+		regex = g_regex_new ("(?:\\s|^)src\\s+(" IPV4_ADDR_REGEX ")(?:$|\\s)", 0, 0, NULL);
+	else
+		regex = g_regex_new ("(?:\\s|^)src\\s+(" IPV6_ADDR_REGEX ")(?:$|\\s)", 0, 0, NULL);
+	g_regex_match (regex, line, 0, &match_info);
+	if (g_match_info_matches (match_info)) {
+		gs_free char *str = g_match_info_fetch (match_info, 1);
+		gs_free_error GError *local_error = NULL;
+		GVariant *variant = g_variant_new_string (str);
+
+		if (!nm_ip_route_attribute_validate (NM_IP_ROUTE_ATTRIBUTE_SRC, variant, family,
+		                                     NULL, &local_error)) {
+			g_match_info_free (match_info);
+			g_variant_unref (variant);
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Invalid route src '%s': %s", str, local_error->message);
+			goto out;
+		}
+
+		nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_SRC, variant);
+	}
+	success = TRUE;
+
+out:
+	if (regex)
+		g_regex_unref (regex);
+	if (match_info)
+		g_match_info_free (match_info);
 
 	return success;
 }
@@ -459,87 +570,85 @@ read_one_ip4_route (shvarFile *ifcfg,
                     NMIPRoute **out_route,
                     GError **error)
 {
-	char *ip_tag, *netmask_tag, *gw_tag, *metric_tag, *value;
-	char *dest = NULL, *next_hop = NULL;
+	char tag[256];
+	char netmask_tag[256];
+	guint32 dest;
+	guint32 next_hop;
+	guint32 netmask;
+	gboolean has_key;
+	gs_free char *value = NULL;
 	gint64 prefix, metric;
-	gboolean success = FALSE;
+	char inet_buf[NM_UTILS_INET_ADDRSTRLEN];
 
 	g_return_val_if_fail (ifcfg != NULL, FALSE);
 	g_return_val_if_fail (out_route != NULL, FALSE);
 	g_return_val_if_fail (*out_route == NULL, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
-	ip_tag = g_strdup_printf ("ADDRESS%u", which);
-	netmask_tag = g_strdup_printf ("NETMASK%u", which);
-	gw_tag = g_strdup_printf ("GATEWAY%u", which);
-	metric_tag = g_strdup_printf ("METRIC%u", which);
-
 	/* Destination */
-	if (!read_ip4_address (ifcfg, ip_tag, &dest, error))
-		goto out;
-	if (!dest) {
-		/* Check whether IP is missing or 0.0.0.0 */
-		char *val;
-		val = svGetValueString (ifcfg, ip_tag);
-		if (!val) {
-			*out_route = NULL;
-			success = TRUE;  /* missing route = success */
-			goto out;
-		}
-		g_free (val);
+	if (!read_ip4_address (ifcfg,
+	                       numbered_tag (tag, "ADDRESS", which),
+	                       &has_key, &dest, error))
+		return FALSE;
+	if (!has_key) {
+		/* missing route = success */
+		*out_route = NULL;
+		return TRUE;
 	}
 
 	/* Next hop */
-	if (!read_ip4_address (ifcfg, gw_tag, &next_hop, error))
-		goto out;
+	if (!read_ip4_address (ifcfg,
+	                       numbered_tag (tag, "GATEWAY", which),
+	                       NULL, &next_hop, error))
+		return FALSE;
 	/* We don't make distinction between missing GATEWAY IP and 0.0.0.0 */
 
 	/* Prefix */
-	if (!read_ip4_address (ifcfg, netmask_tag, &value, error))
-		goto out;
-	if (value) {
-		guint32 netmask;
-
-		inet_pton (AF_INET, value, &netmask);
+	if (!read_ip4_address (ifcfg,
+	                       numbered_tag (netmask_tag, "NETMASK", which),
+	                       &has_key, &netmask, error))
+		return FALSE;
+	if (has_key) {
 		prefix = nm_utils_ip4_netmask_to_prefix (netmask);
-		g_free (value);
 		if (prefix == 0 || netmask != nm_utils_ip4_prefix_to_netmask (prefix)) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, nm_utils_inet4_ntop (netmask, NULL));
-			goto out;
+			             "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, nm_utils_inet4_ntop (netmask, inet_buf));
+			return FALSE;
 		}
 	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IP4 route element '%s'", netmask_tag);
-		goto out;
+		return FALSE;
 	}
 
 	/* Metric */
-	value = svGetValueString (ifcfg, metric_tag);
+	nm_clear_g_free (&value);
+	value = svGetValueStr_cp (ifcfg, numbered_tag (tag, "METRIC", which));
 	if (value) {
 		metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
 		if (metric < 0) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid IP4 route metric '%s'", value);
-			g_free (value);
-			goto out;
+			return FALSE;
 		}
-		g_free (value);
 	} else
 		metric = -1;
 
-	*out_route = nm_ip_route_new (AF_INET, dest, prefix, next_hop, metric, error);
-	if (*out_route)
-		success = TRUE;
+	*out_route = nm_ip_route_new_binary (AF_INET, &dest, prefix, &next_hop, metric, error);
+	if (!*out_route)
+		return FALSE;
 
-out:
-	g_free (dest);
-	g_free (next_hop);
-	g_free (ip_tag);
-	g_free (netmask_tag);
-	g_free (gw_tag);
-	g_free (metric_tag);
-	return success;
+	/* Options */
+	nm_clear_g_free (&value);
+	value = svGetValueStr_cp (ifcfg, numbered_tag (tag, "OPTIONS", which));
+	if (value) {
+		if (!parse_route_options (*out_route, AF_INET, value, error)) {
+			g_clear_pointer (out_route, nm_ip_route_unref);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
 }
 
 static gboolean
@@ -665,6 +774,12 @@ read_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError *
 		route = nm_ip_route_new (AF_INET, dest, prefix_int, next_hop, metric_int, error);
 		if (!route)
 			goto error;
+
+		if (!parse_route_options (route, AF_INET, *iter, error)) {
+			nm_ip_route_unref (route);
+			goto error;
+		}
+
 		if (!nm_setting_ip_config_add_route (s_ip4, route))
 			PARSE_WARNING ("duplicate IP4 route");
 		nm_ip_route_unref (route);
@@ -758,13 +873,6 @@ error:
 	return success;
 }
 
-/* IPv6 address is very complex to describe completely by a regular expression,
- * so don't try to, rather use looser syntax to comprise all possibilities
- * NOTE: The regexes below don't describe all variants allowed by 'ip route add',
- * namely destination IP without 'to' keyword is recognized just at line start.
- */
-#define IPV6_ADDR_REGEX "[0-9A-Fa-f:.]+"
-
 static gboolean
 read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error)
 {
@@ -786,6 +894,7 @@ read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error
 	const char *pattern_via = "via\\s+(" IPV6_ADDR_REGEX ")";       /* IPv6 of gateway */
 	const char *pattern_metric = "metric\\s+(\\d+)";                /* metric */
 
+
 	g_return_val_if_fail (filename != NULL, FALSE);
 	g_return_val_if_fail (s_ip6 != NULL, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
@@ -891,6 +1000,12 @@ read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error
 		g_free (next_hop);
 		if (!route)
 			goto error;
+
+		if (!parse_route_options (route, AF_INET6, *iter, error)) {
+			nm_ip_route_unref (route);
+			goto error;
+		}
+
 		if (!nm_setting_ip_config_add_route (s_ip6, route))
 			PARSE_WARNING ("duplicate IP6 route");
 		nm_ip_route_unref (route);
@@ -910,13 +1025,61 @@ error:
 }
 
 static NMSetting *
+make_user_setting (shvarFile *ifcfg, GError **error)
+{
+	gboolean has_user_data = FALSE;
+	gs_unref_object NMSettingUser *s_user = NULL;
+	gs_unref_hashtable GHashTable *keys = NULL;
+	GHashTableIter iter;
+	const char *key;
+	nm_auto_free_gstring GString *str = NULL;
+
+	keys = svGetKeys (ifcfg);
+	if (!keys)
+		return NULL;
+
+	g_hash_table_iter_init (&iter, keys);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
+		const char *value;
+		gs_free char *value_to_free = NULL;
+
+		if (!g_str_has_prefix (key, "NM_USER_"))
+			continue;
+
+		value = svGetValue (ifcfg, key, &value_to_free);
+
+		if (!value)
+			continue;
+
+		if (!str)
+			str = g_string_sized_new (100);
+		else
+			g_string_set_size (str, 0);
+
+		if (!nms_ifcfg_rh_utils_user_key_decode (key + NM_STRLEN ("NM_USER_"), str))
+			continue;
+
+		if (!s_user)
+			s_user = NM_SETTING_USER (nm_setting_user_new ());
+
+		if (nm_setting_user_set_data (s_user, str->str,
+		                              value, NULL))
+			has_user_data = TRUE;
+	}
+
+	return has_user_data
+	       ? g_steal_pointer (&s_user)
+	       : NULL;
+}
+
+static NMSetting *
 make_proxy_setting (shvarFile *ifcfg, GError **error)
 {
 	NMSettingProxy *s_proxy = NULL;
 	char *value = NULL;
 	NMSettingProxyMethod method;
 
-	value = svGetValueString (ifcfg, "PROXY_METHOD");
+	value = svGetValueStr_cp (ifcfg, "PROXY_METHOD");
 	if (!value)
 		return NULL;
 
@@ -934,14 +1097,14 @@ make_proxy_setting (shvarFile *ifcfg, GError **error)
 		              NM_SETTING_PROXY_METHOD, (int) NM_SETTING_PROXY_METHOD_AUTO,
 		              NULL);
 
-		value = svGetValueString (ifcfg, "PAC_URL");
+		value = svGetValueStr_cp (ifcfg, "PAC_URL");
 		if (value) {
 			value = g_strstrip (value);
 			g_object_set (s_proxy, NM_SETTING_PROXY_PAC_URL, value, NULL);
 			g_free (value);
 		}
 
-		value = svGetValueString (ifcfg, "PAC_SCRIPT");
+		value = svGetValueStr_cp (ifcfg, "PAC_SCRIPT");
 		if (value) {
 			value = g_strstrip (value);
 			g_object_set (s_proxy, NM_SETTING_PROXY_PAC_SCRIPT, value, NULL);
@@ -956,7 +1119,7 @@ make_proxy_setting (shvarFile *ifcfg, GError **error)
 		break;
 	}
 
-	value = svGetValueString (ifcfg, "BROWSER_ONLY");
+	value = svGetValueStr_cp (ifcfg, "BROWSER_ONLY");
 	if (value) {
 		if (!g_ascii_strcasecmp (value, "yes"))
 			g_object_set (s_proxy, NM_SETTING_PROXY_BROWSER_ONLY, TRUE, NULL);
@@ -969,21 +1132,27 @@ make_proxy_setting (shvarFile *ifcfg, GError **error)
 static NMSetting *
 make_ip4_setting (shvarFile *ifcfg,
                   const char *network_file,
+                  gboolean *out_has_defroute,
                   GError **error)
 {
-	NMSettingIPConfig *s_ip4 = NULL;
+	gs_unref_object NMSettingIPConfig *s_ip4 = NULL;
+	gs_free char *route_path = NULL;
 	char *value = NULL;
-	char *route_path = NULL;
 	char *method;
 	gs_free char *dns_options_free = NULL;
 	const char *dns_options = NULL;
 	gs_free char *gateway = NULL;
-	gint32 i;
+	int i;
+	guint32 a;
+	gboolean has_key;
 	shvarFile *network_ifcfg;
 	shvarFile *route_ifcfg;
-	gboolean never_default = FALSE;
+	gboolean never_default;
 	gint64 timeout;
 	gint priority;
+	char inet_buf[NM_UTILS_INET_ADDRSTRLEN];
+
+	nm_assert (out_has_defroute && !*out_has_defroute);
 
 	s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
 
@@ -992,7 +1161,13 @@ make_ip4_setting (shvarFile *ifcfg,
 	 * specified is DEFROUTE=yes which means that this connection can be used
 	 * as a default route
 	 */
-	never_default = !svGetValueBoolean (ifcfg, "DEFROUTE", TRUE);
+	i = svGetValueBoolean (ifcfg, "DEFROUTE", -1);
+	if (i == -1)
+		never_default = FALSE;
+	else {
+		never_default = !i;
+		*out_has_defroute = TRUE;
+	}
 
 	/* Then check if GATEWAYDEV; it's global and overrides DEFROUTE */
 	network_ifcfg = svOpenFile (network_file, NULL);
@@ -1000,8 +1175,8 @@ make_ip4_setting (shvarFile *ifcfg,
 		char *gatewaydev;
 
 		/* Get the connection ifcfg device name and the global gateway device */
-		value = svGetValueString (ifcfg, "DEVICE");
-		gatewaydev = svGetValueString (network_ifcfg, "GATEWAYDEV");
+		value = svGetValueStr_cp (ifcfg, "DEVICE");
+		gatewaydev = svGetValueStr_cp (network_ifcfg, "GATEWAYDEV");
 		dns_options = svGetValue (network_ifcfg, "RES_OPTIONS", &dns_options_free);
 
 		/* If there was a global gateway device specified, then only connections
@@ -1015,7 +1190,7 @@ make_ip4_setting (shvarFile *ifcfg,
 		svCloseFile (network_ifcfg);
 	}
 
-	value = svGetValueString (ifcfg, "BOOTPROTO");
+	value = svGetValueStr_cp (ifcfg, "BOOTPROTO");
 
 	if (!value || !*value || !g_ascii_strcasecmp (value, "none")) {
 		if (is_any_ip4_address_defined (ifcfg, NULL))
@@ -1041,24 +1216,26 @@ make_ip4_setting (shvarFile *ifcfg,
 		              NULL);
 		/* 1 IP address is allowed for shared connections. Read it. */
 		if (is_any_ip4_address_defined (ifcfg, &idx)) {
+			guint32 gw;
 			NMIPAddress *addr = NULL;
 
 			if (!read_full_ip4_address (ifcfg, idx, NULL, &addr, NULL, error))
-				goto done;
-			if (!read_ip4_address (ifcfg, "GATEWAY", &gateway, error))
-				goto done;
+				return NULL;
+			if (!read_ip4_address (ifcfg, "GATEWAY", NULL, &gw, error))
+				return NULL;
 			(void) nm_setting_ip_config_add_address (s_ip4, addr);
 			nm_ip_address_unref (addr);
 			if (never_default)
 				PARSE_WARNING ("GATEWAY will be ignored when DEFROUTE is disabled");
+			gateway = g_strdup (nm_utils_inet4_ntop (gw, inet_buf));
 			g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL);
 		}
-		return NM_SETTING (s_ip4);
+		return g_steal_pointer (&s_ip4);
 	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Unknown BOOTPROTO '%s'", value);
 		g_free (value);
-		goto done;
+		return NULL;
 	}
 	g_free (value);
 
@@ -1073,32 +1250,34 @@ make_ip4_setting (shvarFile *ifcfg,
 	              NULL);
 
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
-		return NM_SETTING (s_ip4);
+		return g_steal_pointer (&s_ip4);
 
 	/* Handle DHCP settings */
-	value = svGetValueString (ifcfg, "DHCP_HOSTNAME");
-	if (value && *value)
+	value = svGetValueStr_cp (ifcfg, "DHCP_HOSTNAME");
+	if (value) {
 		g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, value, NULL);
-	g_free (value);
+		g_free (value);
+	}
 
-	value = svGetValueString (ifcfg, "DHCP_FQDN");
-	if (value && *value) {
+	value = svGetValueStr_cp (ifcfg, "DHCP_FQDN");
+	if (value) {
 		g_object_set (s_ip4,
-			      NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, NULL,
-			      NM_SETTING_IP4_CONFIG_DHCP_FQDN, value,
-			      NULL);
+		              NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, NULL,
+		              NM_SETTING_IP4_CONFIG_DHCP_FQDN, value,
+		              NULL);
+		g_free (value);
 	}
-	g_free (value);
 
 	g_object_set (s_ip4,
 		      NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCP_SEND_HOSTNAME", TRUE),
 		      NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, svGetValueInt64 (ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0),
 		      NULL);
 
-	value = svGetValueString (ifcfg, "DHCP_CLIENT_ID");
-	if (value && strlen (value))
+	value = svGetValueStr_cp (ifcfg, "DHCP_CLIENT_ID");
+	if (value) {
 		g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, value, NULL);
-	g_free (value);
+		g_free (value);
+	}
 
 	/* Read static IP addresses.
 	 * Read them even for AUTO method - in this case the addresses are
@@ -1111,7 +1290,7 @@ make_ip4_setting (shvarFile *ifcfg,
 		/* gateway will only be set if still unset. Hence, we don't leak gateway
 		 * here by calling read_full_ip4_address() repeatedly */
 		if (!read_full_ip4_address (ifcfg, i, NULL, &addr, &gateway, error))
-			goto done;
+			return NULL;
 
 		if (!addr) {
 			/* The first mandatory variable is 2-indexed (IPADDR2)
@@ -1132,16 +1311,17 @@ make_ip4_setting (shvarFile *ifcfg,
 		if (network_ifcfg) {
 			gboolean read_success;
 
-			read_success = read_ip4_address (network_ifcfg, "GATEWAY", &gateway, error);
+			read_success = read_ip4_address (network_ifcfg, "GATEWAY", &has_key, &a, error);
 			svCloseFile (network_ifcfg);
 			if (!read_success)
-				goto done;
-
-			if (gateway && nm_setting_ip_config_get_num_addresses (s_ip4) == 0) {
-				gs_free char *f = g_path_get_basename (svFileGetName (ifcfg));
-				PARSE_WARNING ("ignoring GATEWAY (/etc/sysconfig/network) for %s "
-				               "because the connection has no static addresses", f);
-				g_clear_pointer (&gateway, g_free);
+				return NULL;
+			if (has_key) {
+				if (nm_setting_ip_config_get_num_addresses (s_ip4) == 0) {
+					gs_free char *f = g_path_get_basename (svFileGetName (ifcfg));
+					PARSE_WARNING ("ignoring GATEWAY (/etc/sysconfig/network) for %s "
+					               "because the connection has no static addresses", f);
+				} else
+					gateway = g_strdup (nm_utils_inet4_ntop (a, inet_buf));
 			}
 		}
 	}
@@ -1154,10 +1334,10 @@ make_ip4_setting (shvarFile *ifcfg,
 	 * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting())
 	 */
 	for (i = 1; i <= 10; i++) {
-		char *tag;
+		char tag[256];
 
-		tag = g_strdup_printf ("DNS%u", i);
-		value = svGetValueString (ifcfg, tag);
+		numbered_tag (tag, "DNS", i);
+		value = svGetValueStr_cp (ifcfg, tag);
 		if (value) {
 			if (nm_utils_ipaddr_valid (AF_INET, value)) {
 				if (!nm_setting_ip_config_add_dns (s_ip4, value))
@@ -1166,19 +1346,16 @@ make_ip4_setting (shvarFile *ifcfg,
 				/* Ignore IPv6 addresses */
 			} else {
 				PARSE_WARNING ("invalid DNS server address %s", value);
-				g_free (tag);
 				g_free (value);
-				goto done;
+				return NULL;
 			}
 
 			g_free (value);
 		}
-
-		g_free (tag);
 	}
 
 	/* DNS searches */
-	value = svGetValueString (ifcfg, "DOMAIN");
+	value = svGetValueStr_cp (ifcfg, "DOMAIN");
 	if (value) {
 		char **searches = NULL;
 
@@ -1222,7 +1399,7 @@ make_ip4_setting (shvarFile *ifcfg,
 
 				if (!read_one_ip4_route (route_ifcfg, i, &route, error)) {
 					svCloseFile (route_ifcfg);
-					goto done;
+					return NULL;
 				}
 
 				if (!route)
@@ -1236,13 +1413,12 @@ make_ip4_setting (shvarFile *ifcfg,
 		}
 	} else {
 		if (!read_route_file_legacy (route_path, s_ip4, error))
-			goto done;
+			return NULL;
 	}
-	g_free (route_path);
 
 	/* Legacy value NM used for a while but is incorrect (rh #459370) */
 	if (!nm_setting_ip_config_get_num_dns_searches (s_ip4)) {
-		value = svGetValueString (ifcfg, "SEARCH");
+		value = svGetValueStr_cp (ifcfg, "SEARCH");
 		if (value) {
 			char **searches = NULL;
 
@@ -1266,16 +1442,11 @@ make_ip4_setting (shvarFile *ifcfg,
 	g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT,
 	              (gint) (timeout <= 0 ? timeout : timeout * 1000), NULL);
 
-	return NM_SETTING (s_ip4);
-
-done:
-	g_free (route_path);
-	g_object_unref (s_ip4);
-	return NULL;
+	return g_steal_pointer (&s_ip4);
 }
 
 static void
-read_aliases (NMSettingIPConfig *s_ip4, const char *filename)
+read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *filename)
 {
 	GDir *dir;
 	char *dirname, *base;
@@ -1301,6 +1472,7 @@ read_aliases (NMSettingIPConfig *s_ip4, const char *filename)
 		gboolean ok;
 
 		while ((item = g_dir_read_name (dir))) {
+			gs_free char *gateway = NULL;
 			char *full_path, *device;
 			const char *p;
 
@@ -1329,7 +1501,7 @@ read_aliases (NMSettingIPConfig *s_ip4, const char *filename)
 				continue;
 			}
 
-			device = svGetValueString (parsed, "DEVICE");
+			device = svGetValueStr_cp (parsed, "DEVICE");
 			if (!device) {
 				PARSE_WARNING ("alias file '%s' has no DEVICE", full_path);
 				svCloseFile (parsed);
@@ -1347,12 +1519,30 @@ read_aliases (NMSettingIPConfig *s_ip4, const char *filename)
 			}
 
 			addr = NULL;
-			ok = read_full_ip4_address (parsed, -1, base_addr, &addr, NULL, &err);
-			svCloseFile (parsed);
+			ok = read_full_ip4_address (parsed, -1, base_addr, &addr,
+			                            read_defroute ? &gateway : NULL,
+			                            &err);
 			if (ok) {
 				nm_ip_address_set_attribute (addr, "label", g_variant_new_string (device));
 				if (!nm_setting_ip_config_add_address (s_ip4, addr))
 					PARSE_WARNING ("duplicate IP4 address in alias file %s", item);
+				if (nm_streq0 (nm_setting_ip_config_get_method (s_ip4), NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
+					g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
+				if (read_defroute) {
+					int i;
+
+					if (gateway) {
+						g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL);
+						read_defroute = FALSE;
+					}
+					i = svGetValueBoolean (parsed, "DEFROUTE", -1);
+					if (i != -1) {
+						g_object_set (s_ip4,
+						              NM_SETTING_IP_CONFIG_NEVER_DEFAULT, (gboolean) !i,
+						              NULL);
+						read_defroute = FALSE;
+					}
+				}
 			} else {
 				PARSE_WARNING ("error reading IP4 address from alias file '%s': %s",
 				               full_path, err ? err->message : "no address");
@@ -1360,6 +1550,8 @@ read_aliases (NMSettingIPConfig *s_ip4, const char *filename)
 			}
 			nm_ip_address_unref (addr);
 
+			svCloseFile (parsed);
+
 			g_free (device);
 			g_free (full_path);
 		}
@@ -1416,9 +1608,9 @@ make_ip6_setting (shvarFile *ifcfg,
 		char *default_dev = NULL;
 
 		/* Get the connection ifcfg device name and the global default route device */
-		value = svGetValueString (ifcfg, "DEVICE");
-		ipv6_defaultgw = svGetValueString (network_ifcfg, "IPV6_DEFAULTGW");
-		ipv6_defaultdev = svGetValueString (network_ifcfg, "IPV6_DEFAULTDEV");
+		value = svGetValueStr_cp (ifcfg, "DEVICE");
+		ipv6_defaultgw = svGetValueStr_cp (network_ifcfg, "IPV6_DEFAULTGW");
+		ipv6_defaultdev = svGetValueStr_cp (network_ifcfg, "IPV6_DEFAULTDEV");
 		dns_options = svGetValue (network_ifcfg, "RES_OPTIONS", &dns_options_free);
 
 		if (ipv6_defaultgw) {
@@ -1443,7 +1635,7 @@ make_ip6_setting (shvarFile *ifcfg,
 
 	/* Find out method property */
 	/* Is IPV6 enabled? Set method to "ignored", when not enabled */
-	str_value = svGetValueString (ifcfg, "IPV6INIT");
+	str_value = svGetValueStr_cp (ifcfg, "IPV6INIT");
 	ipv6init = svGetValueBoolean (ifcfg, "IPV6INIT", FALSE);
 	if (!str_value) {
 		network_ifcfg = svOpenFile (network_file, NULL);
@@ -1458,7 +1650,7 @@ make_ip6_setting (shvarFile *ifcfg,
 		method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;  /* IPv6 is disabled */
 	else {
 		ipv6forwarding = svGetValueBoolean (ifcfg, "IPV6FORWARDING", FALSE);
-		str_value = svGetValueString (ifcfg, "IPV6_AUTOCONF");
+		str_value = svGetValueStr_cp (ifcfg, "IPV6_AUTOCONF");
 		dhcp6 = svGetValueBoolean (ifcfg, "DHCPV6C", FALSE);
 
 		if (!g_strcmp0 (str_value, "shared"))
@@ -1470,9 +1662,9 @@ make_ip6_setting (shvarFile *ifcfg,
 		else {
 			/* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */
 			g_free (str_value);
-			str_value = svGetValueString (ifcfg, "IPV6ADDR");
+			str_value = svGetValueStr_cp (ifcfg, "IPV6ADDR");
 			if (!str_value)
-				str_value = svGetValueString (ifcfg, "IPV6ADDR_SECONDARIES");
+				str_value = svGetValueStr_cp (ifcfg, "IPV6ADDR_SECONDARIES");
 
 			if (!str_value)
 				method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
@@ -1482,7 +1674,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	/* TODO - handle other methods */
 
 	/* Read IPv6 Privacy Extensions configuration */
-	str_value = svGetValueString (ifcfg, "IPV6_PRIVACY");
+	str_value = svGetValueStr_cp (ifcfg, "IPV6_PRIVACY");
 	if (str_value) {
 		ip6_privacy = svParseBoolean (str_value, FALSE);
 		if (!ip6_privacy)
@@ -1512,13 +1704,13 @@ make_ip6_setting (shvarFile *ifcfg,
 	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0)
 		return NM_SETTING (s_ip6);
 
-	value = svGetValueString (ifcfg, "DHCPV6_HOSTNAME");
+	value = svGetValueStr_cp (ifcfg, "DHCPV6_HOSTNAME");
 	/* Use DHCP_HOSTNAME as fallback if it is in FQDN format and ipv6.method is
 	 * auto or dhcp: this is required to support old ifcfg files
 	 */
 	if (!value && (   !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
 		       || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP))) {
-		value = svGetValueString (ifcfg, "DHCP_HOSTNAME");
+		value = svGetValueStr_cp (ifcfg, "DHCP_HOSTNAME");
 		if (value && !strchr (value, '.'))
 			g_clear_pointer (&value, g_free);
 	}
@@ -1534,8 +1726,8 @@ make_ip6_setting (shvarFile *ifcfg,
 	 * added to the automatic ones. Note that this is not currently supported by
 	 * the legacy 'network' service (ifup-eth).
 	 */
-	ipv6addr = svGetValueString (ifcfg, "IPV6ADDR");
-	ipv6addr_secondaries = svGetValueString (ifcfg, "IPV6ADDR_SECONDARIES");
+	ipv6addr = svGetValueStr_cp (ifcfg, "IPV6ADDR");
+	ipv6addr_secondaries = svGetValueStr_cp (ifcfg, "IPV6ADDR_SECONDARIES");
 
 	value = g_strjoin (ipv6addr && ipv6addr_secondaries ? " " : NULL,
 	                   ipv6addr ? ipv6addr : "",
@@ -1562,12 +1754,12 @@ make_ip6_setting (shvarFile *ifcfg,
 
 	/* Gateway */
 	if (nm_setting_ip_config_get_num_addresses (s_ip6)) {
-		value = svGetValueString (ifcfg, "IPV6_DEFAULTGW");
+		value = svGetValueStr_cp (ifcfg, "IPV6_DEFAULTGW");
 		if (!value) {
 			/* If no gateway in the ifcfg, try global /etc/sysconfig/network instead */
 			network_ifcfg = svOpenFile (network_file, NULL);
 			if (network_ifcfg) {
-				value = svGetValueString (network_ifcfg, "IPV6_DEFAULTGW");
+				value = svGetValueStr_cp (network_ifcfg, "IPV6_DEFAULTGW");
 				svCloseFile (network_ifcfg);
 			}
 		}
@@ -1588,7 +1780,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	}
 
 	/* IPv6 addressing mode configuration */
-	str_value = svGetValueString (ifcfg, "IPV6_ADDR_GEN_MODE");
+	str_value = svGetValueStr_cp (ifcfg, "IPV6_ADDR_GEN_MODE");
 	if (str_value) {
 		if (nm_utils_enum_from_str (nm_setting_ip6_config_addr_gen_mode_get_type (), str_value,
 		                            (int *) &addr_gen_mode, NULL))
@@ -1604,7 +1796,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	}
 
 	/* IPv6 tokenized interface identifier */
-	str_value = svGetValueString (ifcfg, "IPV6_TOKEN");
+	str_value = svGetValueStr_cp (ifcfg, "IPV6_TOKEN");
 	if (str_value) {
 		g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, str_value, NULL);
 		g_free (str_value);
@@ -1614,13 +1806,13 @@ make_ip6_setting (shvarFile *ifcfg,
 	 * Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting())
 	 */
 	for (i = 1; i <= 10; i++) {
-		char *tag;
+		char tag[256];
 
-		tag = g_strdup_printf ("DNS%u", i);
-		value = svGetValueString (ifcfg, tag);
+		numbered_tag (tag, "DNS", i);
+		value = svGetValueStr_cp (ifcfg, tag);
 		if (!value) {
-			g_free (tag);
-			break; /* all done */
+			/* all done */
+			break;
 		}
 
 		if (nm_utils_ipaddr_valid (AF_INET6, value)) {
@@ -1630,12 +1822,10 @@ make_ip6_setting (shvarFile *ifcfg,
 			/* Ignore IPv4 addresses */
 		} else {
 			PARSE_WARNING ("invalid DNS server address %s", value);
-			g_free (tag);
 			g_free (value);
 			goto error;
 		}
 
-		g_free (tag);
 		g_free (value);
 	}
 
@@ -1676,9 +1866,9 @@ check_if_bond_slave (shvarFile *ifcfg,
 {
 	char *value;
 
-	value = svGetValueString (ifcfg, "MASTER_UUID");
+	value = svGetValueStr_cp (ifcfg, "MASTER_UUID");
 	if (!value)
-		value = svGetValueString (ifcfg, "MASTER");
+		value = svGetValueStr_cp (ifcfg, "MASTER");
 
 	if (value) {
 		g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, value, NULL);
@@ -1699,9 +1889,9 @@ check_if_team_slave (shvarFile *ifcfg,
 {
 	gs_free char *value = NULL;
 
-	value = svGetValueString (ifcfg, "TEAM_MASTER_UUID");
+	value = svGetValueStr_cp (ifcfg, "TEAM_MASTER_UUID");
 	if (!value)
-		value = svGetValueString (ifcfg, "TEAM_MASTER");
+		value = svGetValueStr_cp (ifcfg, "TEAM_MASTER");
 	if (!value)
 		return FALSE;
 
@@ -1777,7 +1967,7 @@ read_dcb_app (shvarFile *ifcfg,
 
 	/* Priority */
 	tmp = g_strdup_printf ("DCB_APP_%s_PRIORITY", app);
-	val = svGetValueString (ifcfg, tmp);
+	val = svGetValueStr_cp (ifcfg, tmp);
 	if (val) {
 		priority = _nm_utils_ascii_str_to_int64 (val, 0, 0, 7, -1);
 		if (priority < 0) {
@@ -1814,26 +2004,23 @@ read_dcb_bool_array (shvarFile *ifcfg,
                      DcbSetBoolFunc set_func,
                      GError **error)
 {
-	char *val;
-	gboolean success = FALSE;
+	gs_free char *val = NULL;
 	guint i;
 
-	val = svGetValueString (ifcfg, prop);
+	val = svGetValueStr_cp (ifcfg, prop);
 	if (!val)
 		return TRUE;
 
 	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
 		PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
-		success = TRUE;
-		goto out;
+		return TRUE;
 	}
 
-	val = g_strstrip (val);
 	if (strlen (val) != 8) {
 		PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, val);
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "boolean array must be 8 characters");
-		goto out;
+		return FALSE;
 	}
 
 	/* All characters must be either 0 or 1 */
@@ -1842,15 +2029,11 @@ read_dcb_bool_array (shvarFile *ifcfg,
 			PARSE_WARNING ("invalid %s value '%s': not all 0s and 1s", prop, val);
 			g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			                     "invalid boolean digit");
-			goto out;
+			return FALSE;
 		}
 		set_func (s_dcb, i, (val[i] == '1'));
 	}
-	success = TRUE;
-
-out:
-	g_free (val);
-	return success;
+	return TRUE;
 }
 
 typedef void (*DcbSetUintFunc) (NMSettingDcb *, guint, guint);
@@ -1865,26 +2048,23 @@ read_dcb_uint_array (shvarFile *ifcfg,
                      DcbSetUintFunc set_func,
                      GError **error)
 {
-	char *val;
-	gboolean success = FALSE;
+	gs_free char *val = NULL;
 	guint i;
 
-	val = svGetValueString (ifcfg, prop);
+	val = svGetValueStr_cp (ifcfg, prop);
 	if (!val)
 		return TRUE;
 
 	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
 		PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
-		success = TRUE;
-		goto out;
+		return TRUE;
 	}
 
-	val = g_strstrip (val);
 	if (strlen (val) != 8) {
 		PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, val);
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "uint array must be 8 characters");
-		goto out;
+		return FALSE;
 	}
 
 	/* All characters must be either 0 - 7 or (optionally) f */
@@ -1898,14 +2078,11 @@ read_dcb_uint_array (shvarFile *ifcfg,
 			               prop, val, f_allowed ? " or 'f'" : "");
 			g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			                     "invalid uint digit");
-			goto out;
+			return FALSE;
 		}
 	}
-	success = TRUE;
 
-out:
-	g_free (val);
-	return success;
+	return TRUE;
 }
 
 static gboolean
@@ -1918,28 +2095,26 @@ read_dcb_percent_array (shvarFile *ifcfg,
                         DcbSetUintFunc set_func,
                         GError **error)
 {
-	char *val;
-	gboolean success = FALSE;
-	char **split = NULL, **iter;
+	gs_free char *val = NULL;
+	gs_strfreev char **split = NULL;
+	char **iter;
 	guint i, sum = 0;
 
-	val = svGetValueString (ifcfg, prop);
+	val = svGetValueStr_cp (ifcfg, prop);
 	if (!val)
 		return TRUE;
 
 	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
 		PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
-		success = TRUE;
-		goto out;
+		return TRUE;
 	}
 
-	val = g_strstrip (val);
 	split = g_strsplit_set (val, ",", 0);
 	if (!split || (g_strv_length (split) != 8)) {
 		PARSE_WARNING ("invalid %s percentage list value '%s'", prop, val);
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "percent array must be 8 elements");
-		goto out;
+		return FALSE;
 	}
 
 	for (iter = split, i = 0; iter && *iter; iter++, i++) {
@@ -1950,7 +2125,7 @@ read_dcb_percent_array (shvarFile *ifcfg,
 			PARSE_WARNING ("invalid %s percentage value '%s'", prop, *iter);
 			g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			                     "invalid percent element");
-			goto out;
+			return FALSE;
 		}
 		set_func (s_dcb, i, (guint) tmp);
 		sum += (guint) tmp;
@@ -1960,16 +2135,10 @@ read_dcb_percent_array (shvarFile *ifcfg,
 		PARSE_WARNING ("%s percentages do not equal 100%%", prop);
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "invalid percentage sum");
-		goto out;
+		return FALSE;
 	}
 
-	success = TRUE;
-
-out:
-	if (split)
-		g_strfreev (split);
-	g_free (val);
-	return success;
+	return TRUE;
 }
 
 static gboolean
@@ -2001,7 +2170,7 @@ make_dcb_setting (shvarFile *ifcfg,
 		return FALSE;
 	}
 	if (nm_setting_dcb_get_app_fcoe_flags (s_dcb) & NM_SETTING_DCB_FLAG_ENABLE) {
-		val = svGetValueString (ifcfg, KEY_DCB_APP_FCOE_MODE);
+		val = svGetValueStr_cp (ifcfg, KEY_DCB_APP_FCOE_MODE);
 		if (val) {
 			if (strcmp (val, NM_SETTING_DCB_FCOE_MODE_FABRIC) == 0 ||
 			    strcmp (val, NM_SETTING_DCB_FCOE_MODE_VN2VN) == 0)
@@ -2138,11 +2307,9 @@ add_one_wep_key (shvarFile *ifcfg,
 	g_return_val_if_fail (key_idx <= 3, FALSE);
 	g_return_val_if_fail (s_wsec != NULL, FALSE);
 
-	value = svGetValueString (ifcfg, shvar_key);
-	if (!value || !strlen (value)) {
-		g_free (value);
+	value = svGetValueStr_cp (ifcfg, shvar_key);
+	if (!value)
 		return TRUE;
-	}
 
 	/* Validate keys */
 	if (passphrase) {
@@ -2195,9 +2362,10 @@ add_one_wep_key (shvarFile *ifcfg,
 		nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, key);
 		g_free (key);
 		success = TRUE;
-	} else
+	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Invalid WEP key length.");
+	}
 
 out:
 	g_free (value);
@@ -2245,7 +2413,7 @@ read_secret_flags (shvarFile *ifcfg, const char *flags_key)
 	g_return_val_if_fail (flags_key[0] != '\0', NM_SETTING_SECRET_FLAG_NONE);
 	g_return_val_if_fail (g_str_has_suffix (flags_key, "_FLAGS"), NM_SETTING_SECRET_FLAG_NONE);
 
-	val = svGetValueString (ifcfg, flags_key);
+	val = svGetValueStr_cp (ifcfg, flags_key);
 	if (val) {
 		if (strstr (val, SECRET_FLAG_AGENT))
 			flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;
@@ -2264,7 +2432,7 @@ make_wep_setting (shvarFile *ifcfg,
                   const char *file,
                   GError **error)
 {
-	NMSettingWirelessSecurity *s_wsec;
+	gs_unref_object NMSettingWirelessSecurity *s_wsec = NULL;
 	char *value;
 	shvarFile *keys_ifcfg = NULL;
 	int default_key_idx = 0;
@@ -2274,14 +2442,14 @@ make_wep_setting (shvarFile *ifcfg,
 	s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
 	g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NULL);
 
-	value = svGetValueString (ifcfg, "DEFAULTKEY");
+	value = svGetValueStr_cp (ifcfg, "DEFAULTKEY");
 	if (value) {
 		default_key_idx = _nm_utils_ascii_str_to_int64 (value, 0, 1, 4, 0);
 		if (default_key_idx == 0) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid default WEP key '%s'", value);
 			g_free (value);
-			goto error;
+			return NULL;
 		}
 		has_default_key = TRUE;
 		default_key_idx--;  /* convert to [0...3] */
@@ -2296,21 +2464,21 @@ make_wep_setting (shvarFile *ifcfg,
 	/* Read keys in the ifcfg file if they are system-owned */
 	if (key_flags == NM_SETTING_SECRET_FLAG_NONE) {
 		if (!read_wep_keys (ifcfg, default_key_idx, s_wsec, error))
-			goto error;
+			return NULL;
 
 		/* Try to get keys from the "shadow" key file */
 		keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
 		if (keys_ifcfg) {
 			if (!read_wep_keys (keys_ifcfg, default_key_idx, s_wsec, error)) {
 				svCloseFile (keys_ifcfg);
-				goto error;
+				return NULL;
 			}
 			svCloseFile (keys_ifcfg);
 			g_assert (error == NULL || *error == NULL);
 		}
 	}
 
-	value = svGetValueString (ifcfg, "SECURITYMODE");
+	value = svGetValueStr_cp (ifcfg, "SECURITYMODE");
 	if (value) {
 		char *lcase;
 
@@ -2326,7 +2494,7 @@ make_wep_setting (shvarFile *ifcfg,
 			             "Invalid WEP authentication algorithm '%s'",
 			             lcase);
 			g_free (lcase);
-			goto error;
+			return NULL;
 		}
 		g_free (lcase);
 	}
@@ -2347,20 +2515,14 @@ make_wep_setting (shvarFile *ifcfg,
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "WEP Shared Key authentication is invalid for "
 			             "unencrypted connections.");
-			goto error;
+			return NULL;
 		}
 
 		/* Unencrypted */
-		g_object_unref (s_wsec);
-		s_wsec = NULL;
+		return NULL;
 	}
 
-	return (NMSetting *) s_wsec;
-
-error:
-	if (s_wsec)
-		g_object_unref (s_wsec);
-	return NULL;
+	return g_steal_pointer (&s_wsec);
 }
 
 static gboolean
@@ -2373,7 +2535,7 @@ fill_wpa_ciphers (shvarFile *ifcfg,
 	char **list = NULL, **iter;
 	int i = 0;
 
-	p = value = svGetValueString (ifcfg, group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE");
+	p = value = svGetValueStr_cp (ifcfg, group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE");
 	if (!value)
 		return TRUE;
 
@@ -2443,13 +2605,13 @@ parse_wpa_psk (shvarFile *ifcfg,
 	/* Try to get keys from the "shadow" key file */
 	keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
 	if (keys_ifcfg) {
-		psk = svGetValueString (keys_ifcfg, "WPA_PSK");
+		psk = svGetValueStr_cp (keys_ifcfg, "WPA_PSK");
 		svCloseFile (keys_ifcfg);
 	}
 
 	/* Fall back to the original ifcfg */
 	if (!psk)
-		psk = svGetValueString (ifcfg, "WPA_PSK");
+		psk = svGetValueStr_cp (ifcfg, "WPA_PSK");
 
 	if (!psk)
 		return NULL;
@@ -2486,7 +2648,7 @@ eap_simple_reader (const char *eap_method,
 	NMSettingSecretFlags flags;
 	char *value;
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_IDENTITY");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY");
 	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IEEE_8021X_IDENTITY for EAP method '%s'.",
@@ -2501,10 +2663,10 @@ eap_simple_reader (const char *eap_method,
 
 	/* Only read the password if it's system-owned */
 	if (flags == NM_SETTING_SECRET_FLAG_NONE) {
-		value = svGetValueString (ifcfg, "IEEE_8021X_PASSWORD");
+		value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PASSWORD");
 		if (!value && keys) {
 			/* Try the lookaside keys file */
-			value = svGetValueString (keys, "IEEE_8021X_PASSWORD");
+			value = svGetValueStr_cp (keys, "IEEE_8021X_PASSWORD");
 		}
 
 		if (!value) {
@@ -2543,6 +2705,19 @@ get_full_file_path (const char *ifcfg_path, const char *file_path)
 	return ret;
 }
 
+static char *
+get_cert_value (const char *ifcfg_path, const char *value,
+                NMSetting8021xCKScheme *out_scheme)
+{
+	if (strncmp (value, "pkcs11:", 7) == 0) {
+		*out_scheme = NM_SETTING_802_1X_CK_SCHEME_PKCS11;
+		return g_strdup (value);
+	}
+
+	*out_scheme = NM_SETTING_802_1X_CK_SCHEME_PATH;
+	return get_full_file_path (ifcfg_path, value);
+}
+
 static gboolean
 eap_tls_reader (const char *eap_method,
                 shvarFile *ifcfg,
@@ -2551,48 +2726,58 @@ eap_tls_reader (const char *eap_method,
                 gboolean phase2,
                 GError **error)
 {
+	gs_free char *ca_cert = NULL;
+	gs_free char *privkey = NULL;
+	gs_free char *privkey_password = NULL;
 	char *value;
-	char *ca_cert = NULL;
-	char *real_path = NULL;
-	char *client_cert = NULL;
-	char *privkey = NULL;
-	char *privkey_password = NULL;
-	gboolean success = FALSE;
+	char *ca_cert_password = NULL;
+	char *client_cert_password = NULL;
 	NMSetting8021xCKFormat privkey_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
 	const char *ca_cert_key = phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT";
-	const char *pk_pw_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD": "IEEE_8021X_PRIVATE_KEY_PASSWORD";
-	const char *pk_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY";
+	const char *ca_cert_pw_key = phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD";
+	const char *ca_cert_pw_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD;
+	const char *ca_cert_pw_flags_key = phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD_FLAGS" : "IEEE_8021X_CA_CERT_PASSWORD_FLAGS";
+	const char *ca_cert_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS : NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS;
 	const char *cli_cert_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT";
-	const char *pk_pw_flags_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS": "IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS";
+	const char *cli_cert_pw_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" : "IEEE_8021X_CLIENT_CERT_PASSWORD";
+	const char *cli_cert_pw_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD;
+	const char *cli_cert_pw_flags_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD_FLAGS" : "IEEE_8021X_CLIENT_CERT_PASSWORD_FLAGS";
+	const char *cli_cert_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS;
+	const char *pk_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY";
+	const char *pk_pw_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD": "IEEE_8021X_PRIVATE_KEY_PASSWORD";
+	const char *pk_pw_flags_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS" : "IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS";
 	const char *pk_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS;
 	NMSettingSecretFlags flags;
+	NMSetting8021xCKScheme scheme;
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_IDENTITY");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY");
 	if (value) {
 		g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL);
 		g_free (value);
 	}
 
-	ca_cert = svGetValueString (ifcfg, ca_cert_key);
+	ca_cert = svGetValueStr_cp (ifcfg, ca_cert_key);
 	if (ca_cert) {
-		real_path = get_full_file_path (svFileGetName (ifcfg), ca_cert);
+		gs_free char *real_cert_value = NULL;
+
+		real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme);
 		if (phase2) {
-			if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x,
-			                                           real_path,
-			                                           NM_SETTING_802_1X_CK_SCHEME_PATH,
-			                                           NULL,
-			                                           error))
-				goto done;
+			if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
+				return FALSE;
 		} else {
-			if (!nm_setting_802_1x_set_ca_cert (s_8021x,
-			                                    real_path,
-			                                    NM_SETTING_802_1X_CK_SCHEME_PATH,
-			                                    NULL,
-			                                    error))
-				goto done;
+			if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
+				return FALSE;
+		}
+
+		if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
+			flags = read_secret_flags (ifcfg, ca_cert_pw_flags_key);
+			g_object_set (s_8021x, ca_cert_pw_flags_prop, flags, NULL);
+
+			if (flags == NM_SETTING_SECRET_FLAG_NONE) {
+				ca_cert_password = svGetValueStr_cp (ifcfg, ca_cert_pw_key);
+				g_object_set (s_8021x, ca_cert_pw_prop, ca_cert_password, NULL);
+			}
 		}
-		g_free (real_path);
-		real_path = NULL;
 	} else {
 		PARSE_WARNING ("missing %s for EAP method '%s'; this is insecure!",
 		               ca_cert_key, eap_method);
@@ -2605,10 +2790,10 @@ eap_tls_reader (const char *eap_method,
 	/* Read the private key password if it's system-owned */
 	if (flags == NM_SETTING_SECRET_FLAG_NONE) {
 		/* Private key password */
-		privkey_password = svGetValueString (ifcfg, pk_pw_key);
+		privkey_password = svGetValueStr_cp (ifcfg, pk_pw_key);
 		if (!privkey_password && keys) {
 			/* Try the lookaside keys file */
-			privkey_password = svGetValueString (keys, pk_pw_key);
+			privkey_password = svGetValueStr_cp (keys, pk_pw_key);
 		}
 
 		if (!privkey_password) {
@@ -2616,40 +2801,42 @@ eap_tls_reader (const char *eap_method,
 			             "Missing %s for EAP method '%s'.",
 			             pk_pw_key,
 			             eap_method);
-			goto done;
+			return FALSE;
 		}
 	}
 
 	/* The private key itself */
-	privkey = svGetValueString (ifcfg, pk_key);
+	privkey = svGetValueStr_cp (ifcfg, pk_key);
 	if (!privkey) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing %s for EAP method '%s'.",
 		             pk_key,
 		             eap_method);
-		goto done;
+		return FALSE;
 	}
 
-	real_path = get_full_file_path (svFileGetName (ifcfg), privkey);
-	if (phase2) {
-		if (!nm_setting_802_1x_set_phase2_private_key (s_8021x,
-		                                               real_path,
-		                                               privkey_password,
-		                                               NM_SETTING_802_1X_CK_SCHEME_PATH,
-		                                               &privkey_format,
-		                                               error))
-			goto done;
-	} else {
-		if (!nm_setting_802_1x_set_private_key (s_8021x,
-		                                        real_path,
-		                                        privkey_password,
-		                                        NM_SETTING_802_1X_CK_SCHEME_PATH,
-		                                        &privkey_format,
-		                                        error))
-			goto done;
+	{
+		gs_free char *real_cert_value = NULL;
+
+		real_cert_value = get_cert_value (svFileGetName (ifcfg), privkey, &scheme);
+		if (phase2) {
+			if (!nm_setting_802_1x_set_phase2_private_key (s_8021x,
+			                                               real_cert_value,
+			                                               privkey_password,
+			                                               scheme,
+			                                               &privkey_format,
+			                                               error))
+				return FALSE;
+		} else {
+			if (!nm_setting_802_1x_set_private_key (s_8021x,
+			                                        real_cert_value,
+			                                        privkey_password,
+			                                        scheme,
+			                                        &privkey_format,
+			                                        error))
+				return FALSE;
+		}
 	}
-	g_free (real_path);
-	real_path = NULL;
 
 	/* Only set the client certificate if the private key is not PKCS#12 format,
 	 * as NM (due to supplicant restrictions) requires.  If the key was PKCS#12,
@@ -2658,44 +2845,39 @@ eap_tls_reader (const char *eap_method,
 	 */
 	if (   privkey_format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY
 	    || privkey_format == NM_SETTING_802_1X_CK_FORMAT_X509) {
-		client_cert = svGetValueString (ifcfg, cli_cert_key);
+		gs_free char *real_cert_value = NULL;
+		gs_free char *client_cert = NULL;
+
+		client_cert = svGetValueStr_cp (ifcfg, cli_cert_key);
 		if (!client_cert) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Missing %s for EAP method '%s'.",
 			             cli_cert_key,
 			             eap_method);
-			goto done;
+			return FALSE;
 		}
 
-		real_path = get_full_file_path (svFileGetName (ifcfg), client_cert);
+		real_cert_value = get_cert_value (svFileGetName (ifcfg), client_cert, &scheme);
 		if (phase2) {
-			if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x,
-			                                               real_path,
-			                                               NM_SETTING_802_1X_CK_SCHEME_PATH,
-			                                               NULL,
-			                                               error))
-				goto done;
+			if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x, real_cert_value, scheme, NULL, error))
+				return FALSE;
 		} else {
-			if (!nm_setting_802_1x_set_client_cert (s_8021x,
-			                                        real_path,
-			                                        NM_SETTING_802_1X_CK_SCHEME_PATH,
-			                                        NULL,
-			                                        error))
-				goto done;
+			if (!nm_setting_802_1x_set_client_cert (s_8021x, real_cert_value, scheme, NULL, error))
+				return FALSE;
 		}
-		g_free (real_path);
-		real_path = NULL;
-	}
 
-	success = TRUE;
+		if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
+			flags = read_secret_flags (ifcfg, cli_cert_pw_flags_key);
+			g_object_set (s_8021x, cli_cert_pw_flags_prop, flags, NULL);
 
-done:
-	g_free (real_path);
-	g_free (ca_cert);
-	g_free (client_cert);
-	g_free (privkey);
-	g_free (privkey_password);
-	return success;
+			if (flags == NM_SETTING_SECRET_FLAG_NONE) {
+				client_cert_password = svGetValueStr_cp (ifcfg, cli_cert_pw_key);
+				g_object_set (s_8021x, cli_cert_pw_prop, client_cert_password, NULL);
+			}
+		}
+	}
+
+	return TRUE;
 }
 
 static gboolean
@@ -2708,28 +2890,25 @@ eap_peap_reader (const char *eap_method,
 {
 	char *anon_ident = NULL;
 	char *ca_cert = NULL;
-	char *real_cert_path = NULL;
+	char *real_cert_value = NULL;
 	char *inner_auth = NULL;
 	char *peapver = NULL;
 	char *lower;
 	char **list = NULL, **iter;
 	gboolean success = FALSE;
+	NMSetting8021xCKScheme scheme;
 
-	ca_cert = svGetValueString (ifcfg, "IEEE_8021X_CA_CERT");
+	ca_cert = svGetValueStr_cp (ifcfg, "IEEE_8021X_CA_CERT");
 	if (ca_cert) {
-		real_cert_path = get_full_file_path (svFileGetName (ifcfg), ca_cert);
-		if (!nm_setting_802_1x_set_ca_cert (s_8021x,
-		                                    real_cert_path,
-		                                    NM_SETTING_802_1X_CK_SCHEME_PATH,
-		                                    NULL,
-		                                    error))
+		real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme);
+		if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
 			goto done;
 	} else {
 		PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
 		               eap_method);
 	}
 
-	peapver = svGetValueString (ifcfg, "IEEE_8021X_PEAP_VERSION");
+	peapver = svGetValueStr_cp (ifcfg, "IEEE_8021X_PEAP_VERSION");
 	if (peapver) {
 		if (!strcmp (peapver, "0"))
 			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "0", NULL);
@@ -2746,11 +2925,11 @@ eap_peap_reader (const char *eap_method,
 	if (svGetValueBoolean (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", FALSE))
 		g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", NULL);
 
-	anon_ident = svGetValueString (ifcfg, "IEEE_8021X_ANON_IDENTITY");
-	if (anon_ident && strlen (anon_ident))
+	anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY");
+	if (anon_ident)
 		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
 
-	inner_auth = svGetValueString (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
+	inner_auth = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
 	if (!inner_auth) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IEEE_8021X_INNER_AUTH_METHODS.");
@@ -2797,7 +2976,7 @@ done:
 		g_strfreev (list);
 	g_free (inner_auth);
 	g_free (peapver);
-	g_free (real_cert_path);
+	g_free (real_cert_value);
 	g_free (ca_cert);
 	g_free (anon_ident);
 	return success;
@@ -2814,30 +2993,27 @@ eap_ttls_reader (const char *eap_method,
 	gboolean success = FALSE;
 	char *anon_ident = NULL;
 	char *ca_cert = NULL;
-	char *real_cert_path = NULL;
+	char *real_cert_value = NULL;
 	char *inner_auth = NULL;
 	char *tmp;
 	char **list = NULL, **iter;
+	NMSetting8021xCKScheme scheme;
 
-	ca_cert = svGetValueString (ifcfg, "IEEE_8021X_CA_CERT");
+	ca_cert = svGetValueStr_cp (ifcfg, "IEEE_8021X_CA_CERT");
 	if (ca_cert) {
-		real_cert_path = get_full_file_path (svFileGetName (ifcfg), ca_cert);
-		if (!nm_setting_802_1x_set_ca_cert (s_8021x,
-		                                    real_cert_path,
-		                                    NM_SETTING_802_1X_CK_SCHEME_PATH,
-		                                    NULL,
-		                                    error))
+		real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme);
+		if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
 			goto done;
 	} else {
 		PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
 		               eap_method);
 	}
 
-	anon_ident = svGetValueString (ifcfg, "IEEE_8021X_ANON_IDENTITY");
-	if (anon_ident && strlen (anon_ident))
+	anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY");
+	if (anon_ident)
 		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
 
-	tmp = svGetValueString (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
+	tmp = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
 	if (!tmp) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IEEE_8021X_INNER_AUTH_METHODS.");
@@ -2885,7 +3061,7 @@ done:
 	if (list)
 		g_strfreev (list);
 	g_free (inner_auth);
-	g_free (real_cert_path);
+	g_free (real_cert_value);
 	g_free (ca_cert);
 	g_free (anon_ident);
 	return success;
@@ -2906,17 +3082,17 @@ eap_fast_reader (const char *eap_method,
 	char *fast_provisioning = NULL;
 	char *lower;
 	char **list = NULL, **iter;
-	const char* pac_prov_str;
+	const char *pac_prov_str;
 	gboolean allow_unauth = FALSE, allow_auth = FALSE;
 	gboolean success = FALSE;
 
-	pac_file = svGetValueString (ifcfg, "IEEE_8021X_PAC_FILE");
+	pac_file = svGetValueStr_cp (ifcfg, "IEEE_8021X_PAC_FILE");
 	if (pac_file) {
 		real_pac_path = get_full_file_path (svFileGetName (ifcfg), pac_file);
 		g_object_set (s_8021x, NM_SETTING_802_1X_PAC_FILE, real_pac_path, NULL);
 	}
 
-	fast_provisioning = svGetValueString (ifcfg, "IEEE_8021X_FAST_PROVISIONING");
+	fast_provisioning = svGetValueStr_cp (ifcfg, "IEEE_8021X_FAST_PROVISIONING");
 	if (fast_provisioning) {
 		list = g_strsplit_set (fast_provisioning, " \t", 0);
 		for (iter = list; iter && *iter; iter++) {
@@ -2944,11 +3120,11 @@ eap_fast_reader (const char *eap_method,
 		goto done;
 	}
 
-	anon_ident = svGetValueString (ifcfg, "IEEE_8021X_ANON_IDENTITY");
-	if (anon_ident && strlen (anon_ident))
+	anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY");
+	if (anon_ident)
 		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
 
-	inner_auth = svGetValueString (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
+	inner_auth = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
 	if (!inner_auth) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IEEE_8021X_INNER_AUTH_METHODS.");
@@ -2998,12 +3174,12 @@ done:
 
 typedef struct {
 	const char *method;
-	gboolean (*reader)(const char *eap_method,
-	                   shvarFile *ifcfg,
-	                   shvarFile *keys,
-	                   NMSetting8021x *s_8021x,
-	                   gboolean phase2,
-	                   GError **error);
+	gboolean (*reader) (const char *eap_method,
+	                    shvarFile *ifcfg,
+	                    shvarFile *keys,
+	                    NMSetting8021x *s_8021x,
+	                    gboolean phase2,
+	                    GError **error);
 	gboolean wifi_phase2_only;
 } EAPReader;
 
@@ -3035,7 +3211,7 @@ read_8021x_list_value (shvarFile *ifcfg,
 	g_return_if_fail (ifcfg_var_name != NULL);
 	g_return_if_fail (prop_name != NULL);
 
-	value = svGetValueString (ifcfg, ifcfg_var_name);
+	value = svGetValueStr_cp (ifcfg, ifcfg_var_name);
 	if (!value)
 		return;
 
@@ -3053,12 +3229,13 @@ fill_8021x (shvarFile *ifcfg,
             gboolean wifi,
             GError **error)
 {
+	nm_auto_shvar_file_close shvarFile *keys = NULL;
 	NMSetting8021x *s_8021x;
-	shvarFile *keys = NULL;
 	char *value;
 	char **list = NULL, **iter;
+	gint64 timeout;
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_EAP_METHODS");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_EAP_METHODS");
 	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IEEE_8021X_EAP_METHODS for key management '%s'",
@@ -3104,7 +3281,7 @@ fill_8021x (shvarFile *ifcfg,
 			found = TRUE;
 			break;
 
-		next:
+next:
 			eap++;
 		}
 
@@ -3119,37 +3296,54 @@ fill_8021x (shvarFile *ifcfg,
 		goto error;
 	}
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_SUBJECT_MATCH");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_SUBJECT_MATCH");
 	g_object_set (s_8021x, NM_SETTING_802_1X_SUBJECT_MATCH, value, NULL);
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH");
 	g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, value, NULL);
 	g_free (value);
 
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS");
+	if (value) {
+		NMSetting8021xAuthFlags flags;
+		char *token;
+
+		if (nm_utils_enum_from_str (nm_setting_802_1x_auth_flags_get_type (), value,
+		                            (int *) &flags, &token)) {
+			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, flags, NULL);
+		} else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Invalid IEEE_8021X_PHASE1_AUTH_FLAGS flag '%s'", token);
+			g_free (token);
+			g_free (value);
+			goto error;
+		}
+		g_free (value);
+	}
+
 	read_8021x_list_value (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES",
 	                       s_8021x, NM_SETTING_802_1X_ALTSUBJECT_MATCHES);
 	read_8021x_list_value (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES",
 	                       s_8021x, NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH");
 	g_object_set (s_8021x, NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, value, NULL);
 	g_free (value);
-	value = svGetValueString (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH");
 	g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, value, NULL);
 	g_free (value);
 
+	timeout = svGetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", 10, 0, G_MAXINT32, 0);
+	g_object_set (s_8021x, NM_SETTING_802_1X_AUTH_TIMEOUT, (gint32) timeout, NULL);
+
 	if (list)
 		g_strfreev (list);
-	if (keys)
-		svCloseFile (keys);
 	return s_8021x;
 
 error:
 	if (list)
 		g_strfreev (list);
-	if (keys)
-		svCloseFile (keys);
 	g_object_unref (s_8021x);
 	return NULL;
 }
@@ -3168,7 +3362,7 @@ make_wpa_setting (shvarFile *ifcfg,
 
 	wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
 
-	value = svGetValueString (ifcfg, "KEY_MGMT");
+	value = svGetValueStr_cp (ifcfg, "KEY_MGMT");
 	wpa_psk = !g_strcmp0 (value, "WPA-PSK");
 	wpa_eap = !g_strcmp0 (value, "WPA-EAP");
 	ieee8021x = !g_strcmp0 (value, "IEEE8021X");
@@ -3188,8 +3382,8 @@ make_wpa_setting (shvarFile *ifcfg,
 	} else {
 		char *allow_wpa, *allow_rsn;
 
-		allow_wpa = svGetValueString (ifcfg, "WPA_ALLOW_WPA");
-		allow_rsn = svGetValueString (ifcfg, "WPA_ALLOW_WPA2");
+		allow_wpa = svGetValueStr_cp (ifcfg, "WPA_ALLOW_WPA");
+		allow_rsn = svGetValueStr_cp (ifcfg, "WPA_ALLOW_WPA2");
 
 		if (allow_wpa && svGetValueBoolean (ifcfg, "WPA_ALLOW_WPA", TRUE))
 			nm_setting_wireless_security_add_proto (wsec, "wpa");
@@ -3243,7 +3437,7 @@ make_wpa_setting (shvarFile *ifcfg,
 
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "SECURITYMODE");
+	value = svGetValueStr_cp (ifcfg, "SECURITYMODE");
 	if (NM_IN_STRSET (value, NULL, "open"))
 		g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, value, NULL);
 
@@ -3269,12 +3463,12 @@ make_leap_setting (shvarFile *ifcfg,
 
 	wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
 
-	value = svGetValueString (ifcfg, "KEY_MGMT");
+	value = svGetValueStr_cp (ifcfg, "KEY_MGMT");
 	if (!value || strcmp (value, "IEEE8021X"))
 		goto error; /* Not LEAP */
 
 	g_free (value);
-	value = svGetValueString (ifcfg, "SECURITYMODE");
+	value = svGetValueStr_cp (ifcfg, "SECURITYMODE");
 	if (!value || strcasecmp (value, "leap"))
 		goto error; /* Not LEAP */
 
@@ -3285,12 +3479,12 @@ make_leap_setting (shvarFile *ifcfg,
 
 	/* Read LEAP password if it's system-owned */
 	if (flags == NM_SETTING_SECRET_FLAG_NONE) {
-		value = svGetValueString (ifcfg, "IEEE_8021X_PASSWORD");
+		value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PASSWORD");
 		if (!value) {
 			/* Try to get keys from the "shadow" key file */
 			keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
 			if (keys_ifcfg) {
-				value = svGetValueString (keys_ifcfg, "IEEE_8021X_PASSWORD");
+				value = svGetValueStr_cp (keys_ifcfg, "IEEE_8021X_PASSWORD");
 				svCloseFile (keys_ifcfg);
 			}
 		}
@@ -3299,8 +3493,8 @@ make_leap_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_IDENTITY");
-	if (!value || !strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY");
+	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing LEAP identity");
 		goto error;
@@ -3392,25 +3586,25 @@ make_wireless_setting (shvarFile *ifcfg,
 
 	s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
 
-	value = svGetValueString (ifcfg, "HWADDR");
+	value = svGetValueStr_cp (ifcfg, "HWADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "MACADDR");
+	value = svGetValueStr_cp (ifcfg, "MACADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "GENERATE_MAC_ADDRESS_MASK");
+	value = svGetValueStr_cp (ifcfg, "GENERATE_MAC_ADDRESS_MASK");
 	g_object_set (s_wireless, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK, value, NULL);
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "HWADDR_BLACKLIST");
+	value = svGetValueStr_cp (ifcfg, "HWADDR_BLACKLIST");
 	if (value) {
 		char **strv;
 
@@ -3420,7 +3614,7 @@ make_wireless_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "ESSID");
+	value = svGetValueStr_cp (ifcfg, "ESSID");
 	if (value) {
 		gs_unref_bytes GBytes *bytes = NULL;
 		gsize ssid_len = 0;
@@ -3449,7 +3643,7 @@ make_wireless_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "MODE");
+	value = svGetValueStr_cp (ifcfg, "MODE");
 	if (value) {
 		char *lcase;
 		const char *mode = NULL;
@@ -3475,14 +3669,14 @@ make_wireless_setting (shvarFile *ifcfg,
 		g_object_set (s_wireless, NM_SETTING_WIRELESS_MODE, mode, NULL);
 	}
 
-	value = svGetValueString (ifcfg, "BSSID");
+	value = svGetValueStr_cp (ifcfg, "BSSID");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_wireless, NM_SETTING_WIRELESS_BSSID, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "CHANNEL");
+	value = svGetValueStr_cp (ifcfg, "CHANNEL");
 	if (value) {
 		errno = 0;
 		chan = _nm_utils_ascii_str_to_int64 (value, 10, 1, 196, 0);
@@ -3496,7 +3690,7 @@ make_wireless_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "BAND");
+	value = svGetValueStr_cp (ifcfg, "BAND");
 	if (value) {
 		if (!strcmp (value, "a")) {
 			if (chan && chan <= 14) {
@@ -3527,7 +3721,7 @@ make_wireless_setting (shvarFile *ifcfg,
 			g_object_set (s_wireless, NM_SETTING_WIRELESS_BAND, "bg", NULL);
 	}
 
-	value = svGetValueString (ifcfg, "MTU");
+	value = svGetValueStr_cp (ifcfg, "MTU");
 	if (value) {
 		int mtu;
 
@@ -3637,7 +3831,7 @@ wireless_connection_from_ifcfg (const char *file,
 		printable_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
 		                                        g_bytes_get_size (ssid));
 	} else
-		printable_ssid = g_strdup_printf ("unmanaged");
+		printable_ssid = g_strdup ("unmanaged");
 
 	mode = nm_setting_wireless_get_mode (NM_SETTING_WIRELESS (wireless_setting));
 	if (mode && !strcmp (mode, "adhoc"))
@@ -3856,7 +4050,7 @@ parse_ethtool_options (shvarFile *ifcfg, NMSettingWired *s_wired, const char *va
 	}
 
 	/* ETHTOOL_WAKE_ON_LAN = ignore overrides WoL settings in ETHTOOL_OPTS */
-	wol_value = svGetValueString (ifcfg, "ETHTOOL_WAKE_ON_LAN");
+	wol_value = svGetValueStr_cp (ifcfg, "ETHTOOL_WAKE_ON_LAN");
 	if (wol_value) {
 		if (strcmp (wol_value, "ignore") == 0)
 			wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE;
@@ -3891,7 +4085,7 @@ make_wired_setting (shvarFile *ifcfg,
 
 	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
 
-	value = svGetValueString (ifcfg, "MTU");
+	value = svGetValueStr_cp (ifcfg, "MTU");
 	if (value) {
 		int mtu;
 
@@ -3903,14 +4097,14 @@ make_wired_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "HWADDR");
+	value = svGetValueStr_cp (ifcfg, "HWADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "SUBCHANNELS");
+	value = svGetValueStr_cp (ifcfg, "SUBCHANNELS");
 	if (value) {
 		const char *p = value;
 		gboolean success = TRUE;
@@ -3941,28 +4135,29 @@ make_wired_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "PORTNAME");
-	if (value && strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "PORTNAME");
+	if (value) {
 		nm_setting_wired_add_s390_option (s_wired, "portname", value);
+		g_free (value);
 	}
-	g_free (value);
 
-	value = svGetValueString (ifcfg, "CTCPROT");
-	if (value && strlen (value))
+	value = svGetValueStr_cp (ifcfg, "CTCPROT");
+	if (value) {
 		nm_setting_wired_add_s390_option (s_wired, "ctcprot", value);
-	g_free (value);
+		g_free (value);
+	}
 
-	nettype = svGetValueString (ifcfg, "NETTYPE");
-	if (nettype && strlen (nettype)) {
+	nettype = svGetValueStr_cp (ifcfg, "NETTYPE");
+	if (nettype) {
 		if (!strcmp (nettype, "qeth") || !strcmp (nettype, "lcs") || !strcmp (nettype, "ctc"))
 			g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, nettype, NULL);
 		else
 			PARSE_WARNING ("unknown s390 NETTYPE '%s'", nettype);
+		g_free (nettype);
 	}
-	g_free (nettype);
 
-	value = svGetValueString (ifcfg, "OPTIONS");
-	if (value && strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "OPTIONS");
+	if (value) {
 		char **options, **iter;
 
 		iter = options = g_strsplit_set (value, " ", 0);
@@ -3979,21 +4174,21 @@ make_wired_setting (shvarFile *ifcfg,
 			iter++;
 		}
 		g_strfreev (options);
+		g_free (value);
 	}
-	g_free (value);
 
-	value = svGetValueString (ifcfg, "MACADDR");
+	value = svGetValueStr_cp (ifcfg, "MACADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "GENERATE_MAC_ADDRESS_MASK");
+	value = svGetValueStr_cp (ifcfg, "GENERATE_MAC_ADDRESS_MASK");
 	g_object_set (s_wired, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, value, NULL);
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "HWADDR_BLACKLIST");
+	value = svGetValueStr_cp (ifcfg, "HWADDR_BLACKLIST");
 	if (value) {
 		char **strv;
 
@@ -4003,7 +4198,7 @@ make_wired_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "KEY_MGMT");
+	value = svGetValueStr_cp (ifcfg, "KEY_MGMT");
 	if (value) {
 		if (!strcmp (value, "IEEE8021X")) {
 			*s_8021x = fill_8021x (ifcfg, file, value, FALSE, error);
@@ -4078,19 +4273,19 @@ parse_infiniband_p_key (shvarFile *ifcfg,
 	int id;
 	gboolean ret = FALSE;
 
-	device = svGetValueString (ifcfg, "DEVICE");
+	device = svGetValueStr_cp (ifcfg, "DEVICE");
 	if (!device) {
 		PARSE_WARNING ("InfiniBand connection specified PKEY but not DEVICE");
 		goto done;
 	}
 
-	physdev = svGetValueString (ifcfg, "PHYSDEV");
+	physdev = svGetValueStr_cp (ifcfg, "PHYSDEV");
 	if (!physdev) {
 		PARSE_WARNING ("InfiniBand connection specified PKEY but not PHYSDEV");
 		goto done;
 	}
 
-	pkey_id = svGetValueString (ifcfg, "PKEY_ID");
+	pkey_id = svGetValueStr_cp (ifcfg, "PKEY_ID");
 	if (!pkey_id) {
 		PARSE_WARNING ("InfiniBand connection specified PKEY but not PKEY_ID");
 		goto done;
@@ -4138,7 +4333,7 @@ make_infiniband_setting (shvarFile *ifcfg,
 
 	s_infiniband = NM_SETTING_INFINIBAND (nm_setting_infiniband_new ());
 
-	value = svGetValueString (ifcfg, "MTU");
+	value = svGetValueStr_cp (ifcfg, "MTU");
 	if (value) {
 		int mtu;
 
@@ -4150,7 +4345,7 @@ make_infiniband_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "HWADDR");
+	value = svGetValueStr_cp (ifcfg, "HWADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, value, NULL);
@@ -4248,17 +4443,17 @@ make_bond_setting (shvarFile *ifcfg,
 	NMSettingBond *s_bond;
 	char *value;
 
-	s_bond = NM_SETTING_BOND (nm_setting_bond_new ());
-
-	value = svGetValueString (ifcfg, "DEVICE");
-	if (!value || !strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "DEVICE");
+	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "mandatory DEVICE keyword missing");
-		goto error;
+		return NULL;
 	}
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "BONDING_OPTS");
+	s_bond = NM_SETTING_BOND (nm_setting_bond_new ());
+
+	value = svGetValueStr_cp (ifcfg, "BONDING_OPTS");
 	if (value) {
 		char **items, **iter;
 
@@ -4271,7 +4466,7 @@ make_bond_setting (shvarFile *ifcfg,
 				if (keys && *keys) {
 					key = *keys;
 					val = *(keys + 1);
-					if (val && strlen(key) && strlen(val))
+					if (val && key[0] && val[0])
 						handle_bond_option (s_bond, key, val);
 				}
 
@@ -4283,10 +4478,6 @@ make_bond_setting (shvarFile *ifcfg,
 	}
 
 	return (NMSetting *) s_bond;
-
-error:
-	g_object_unref (s_bond);
-	return NULL;
 }
 
 static NMConnection *
@@ -4342,7 +4533,7 @@ read_team_config (shvarFile *ifcfg, const char *key, GError **error)
 	gs_free char *value = NULL;
 	size_t l;
 
-	value = svGetValueString (ifcfg, key);
+	value = svGetValueStr_cp (ifcfg, key);
 	if (!value)
 		return NULL;
 
@@ -4370,29 +4561,26 @@ make_team_setting (shvarFile *ifcfg,
 	char *value;
 	GError *local_err = NULL;
 
-	s_team = NM_SETTING_TEAM (nm_setting_team_new ());
-
-	value = svGetValueString (ifcfg, "DEVICE");
-	if (!value || !strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "DEVICE");
+	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "mandatory DEVICE keyword missing");
-		goto error;
+		return NULL;
 	}
 	g_free (value);
 
 	value = read_team_config (ifcfg, "TEAM_CONFIG", &local_err);
 	if (local_err) {
 		g_propagate_error (error, local_err);
-		goto error;
+		return NULL;
 	}
+
+	s_team = NM_SETTING_TEAM (nm_setting_team_new ());
+
 	g_object_set (s_team, NM_SETTING_TEAM_CONFIG, value, NULL);
 	g_free (value);
 
 	return (NMSetting *) s_team;
-
-error:
-	g_object_unref (s_team);
-	return NULL;
 }
 
 static NMConnection *
@@ -4454,23 +4642,23 @@ handle_bridge_option (NMSetting *setting,
 	guint32 u = 0;
 
 	if (!strcmp (key, "priority")) {
-		if (stp == FALSE) {
+		if (stp == FALSE)
 			PARSE_WARNING ("'priority' invalid when STP is disabled");
-		} else if (get_uint (value, &u))
+		else if (get_uint (value, &u))
 			g_object_set (setting, NM_SETTING_BRIDGE_PRIORITY, u, NULL);
 		else
 			PARSE_WARNING ("invalid priority value '%s'", value);
 	} else if (!strcmp (key, "hello_time")) {
-		if (stp == FALSE) {
+		if (stp == FALSE)
 			PARSE_WARNING ("'hello_time' invalid when STP is disabled");
-		} else if (get_uint (value, &u))
+		else if (get_uint (value, &u))
 			g_object_set (setting, NM_SETTING_BRIDGE_HELLO_TIME, u, NULL);
 		else
 			PARSE_WARNING ("invalid hello_time value '%s'", value);
 	} else if (!strcmp (key, "max_age")) {
-		if (stp == FALSE) {
+		if (stp == FALSE)
 			PARSE_WARNING ("'max_age' invalid when STP is disabled");
-		} else if (get_uint (value, &u))
+		else if (get_uint (value, &u))
 			g_object_set (setting, NM_SETTING_BRIDGE_MAX_AGE, u, NULL);
 		else
 			PARSE_WARNING ("invalid max_age value '%s'", value);
@@ -4506,7 +4694,7 @@ handle_bridging_opts (NMSetting *setting,
 			if (keys && *keys) {
 				key = *keys;
 				val = *(keys + 1);
-				if (val && strlen(key) && strlen(val))
+				if (val && strlen (key) && strlen (val))
 					func (setting, stp, key, val);
 			}
 
@@ -4527,24 +4715,24 @@ make_bridge_setting (shvarFile *ifcfg,
 	gboolean stp = FALSE;
 	gboolean stp_set = FALSE;
 
-	s_bridge = NM_SETTING_BRIDGE (nm_setting_bridge_new ());
-
-	value = svGetValueString (ifcfg, "DEVICE");
-	if (!value || !strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "DEVICE");
+	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "mandatory DEVICE keyword missing");
-		goto error;
+		return NULL;
 	}
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "MACADDR");
+	s_bridge = NM_SETTING_BRIDGE (nm_setting_bridge_new ());
+
+	value = svGetValueStr_cp (ifcfg, "MACADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "STP");
+	value = svGetValueStr_cp (ifcfg, "STP");
 	if (value) {
 		if (!strcasecmp (value, "on") || !strcasecmp (value, "yes")) {
 			g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, TRUE, NULL);
@@ -4563,7 +4751,7 @@ make_bridge_setting (shvarFile *ifcfg,
 		g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL);
 	}
 
-	value = svGetValueString (ifcfg, "DELAY");
+	value = svGetValueStr_cp (ifcfg, "DELAY");
 	if (value) {
 		if (stp) {
 			if (get_uint (value, &u))
@@ -4575,17 +4763,13 @@ make_bridge_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "BRIDGING_OPTS");
+	value = svGetValueStr_cp (ifcfg, "BRIDGING_OPTS");
 	if (value) {
 		handle_bridging_opts (NM_SETTING (s_bridge), stp, value, handle_bridge_option);
 		g_free (value);
 	}
 
 	return (NMSetting *) s_bridge;
-
-error:
-	g_object_unref (s_bridge);
-	return NULL;
 }
 
 static NMConnection *
@@ -4616,7 +4800,7 @@ bridge_connection_from_ifcfg (const char *file,
 		g_object_unref (connection);
 		return NULL;
 	}
-	nm_connection_add_setting (connection, bridge_setting);	
+	nm_connection_add_setting (connection, bridge_setting);
 
 	return connection;
 }
@@ -4658,14 +4842,14 @@ make_bridge_port_setting (shvarFile *ifcfg)
 
 	g_return_val_if_fail (ifcfg != NULL, FALSE);
 
-	value = svGetValueString (ifcfg, "BRIDGE_UUID");
+	value = svGetValueStr_cp (ifcfg, "BRIDGE_UUID");
 	if (!value)
-		value = svGetValueString (ifcfg, "BRIDGE");
+		value = svGetValueStr_cp (ifcfg, "BRIDGE");
 	if (value) {
 		g_free (value);
 
 		s_port = nm_setting_bridge_port_new ();
-		value = svGetValueString (ifcfg, "BRIDGING_OPTS");
+		value = svGetValueStr_cp (ifcfg, "BRIDGING_OPTS");
 		if (value)
 			handle_bridging_opts (s_port, FALSE, value, handle_bridge_port_option);
 		g_free (value);
@@ -4742,7 +4926,7 @@ parse_prio_map_list (NMSettingVlan *s_vlan,
 	char *value;
 	gchar **list = NULL, **iter;
 
-	value = svGetValueString (ifcfg, key);
+	value = svGetValueStr_cp (ifcfg, key);
 	if (!value)
 		return;
 
@@ -4764,16 +4948,16 @@ make_vlan_setting (shvarFile *ifcfg,
                    const char *file,
                    GError **error)
 {
-	NMSettingVlan *s_vlan = NULL;
+	gs_unref_object NMSettingVlan *s_vlan = NULL;
+	gs_free char *parent = NULL;
+	gs_free char *iface_name = NULL;
 	char *value = NULL;
-	char *iface_name = NULL;
-	char *parent = NULL;
 	const char *p = NULL;
 	int vlan_id = -1;
 	guint32 vlan_flags = 0;
 	gint gvrp, reorder_hdr;
 
-	value = svGetValueString (ifcfg, "VLAN_ID");
+	value = svGetValueStr_cp (ifcfg, "VLAN_ID");
 	if (value) {
 		vlan_id = _nm_utils_ascii_str_to_int64 (value, 10, 0, 4095, -1);
 		if (vlan_id == -1) {
@@ -4786,7 +4970,7 @@ make_vlan_setting (shvarFile *ifcfg,
 	}
 
 	/* Need DEVICE if we don't have a separate VLAN_ID property */
-	iface_name = svGetValueString (ifcfg, "DEVICE");
+	iface_name = svGetValueStr_cp (ifcfg, "DEVICE");
 	if (!iface_name && vlan_id < 0) {
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "Missing DEVICE property; cannot determine VLAN ID.");
@@ -4796,7 +4980,7 @@ make_vlan_setting (shvarFile *ifcfg,
 	s_vlan = NM_SETTING_VLAN (nm_setting_vlan_new ());
 
 	/* Parent interface from PHYSDEV takes precedence if it exists */
-	parent = svGetValueString (ifcfg, "PHYSDEV");
+	parent = svGetValueStr_cp (ifcfg, "PHYSDEV");
 
 	if (iface_name) {
 		p = strchr (iface_name, '.');
@@ -4808,8 +4992,7 @@ make_vlan_setting (shvarFile *ifcfg,
 					/* Like initscripts, if no PHYSDEV and we get an obviously
 					 * invalid parent interface from DEVICE, fail.
 					 */
-					g_free (parent);
-					parent = NULL;
+					nm_clear_g_free (&parent);
 				}
 			}
 			p++;
@@ -4834,17 +5017,16 @@ make_vlan_setting (shvarFile *ifcfg,
 	if (vlan_id < 0) {
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "Failed to determine VLAN ID from DEVICE or VLAN_ID.");
-		goto error;
+		return NULL;
 	}
 	g_object_set (s_vlan, NM_SETTING_VLAN_ID, vlan_id, NULL);
 
 	if (parent == NULL) {
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "Failed to determine VLAN parent from DEVICE or PHYSDEV");
-		goto error;
+		return NULL;
 	}
 	g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL);
-	g_clear_pointer (&parent, g_free);
 
 	vlan_flags |= NM_VLAN_FLAG_REORDER_HEADERS;
 
@@ -4852,7 +5034,7 @@ make_vlan_setting (shvarFile *ifcfg,
 	if (gvrp > 0)
 		vlan_flags |= NM_VLAN_FLAG_GVRP;
 
-	value = svGetValueString (ifcfg, "VLAN_FLAGS");
+	value = svGetValueStr_cp (ifcfg, "VLAN_FLAGS");
 	if (value) {
 		gs_strfreev char **strv = NULL;
 		char **ptr;
@@ -4883,15 +5065,7 @@ make_vlan_setting (shvarFile *ifcfg,
 	parse_prio_map_list (s_vlan, ifcfg, "VLAN_INGRESS_PRIORITY_MAP", NM_VLAN_INGRESS_MAP);
 	parse_prio_map_list (s_vlan, ifcfg, "VLAN_EGRESS_PRIORITY_MAP", NM_VLAN_EGRESS_MAP);
 
-	g_free (iface_name);
-
-	return (NMSetting *) s_vlan;
-
-error:
-	g_free (parent);
-	g_free (iface_name);
-	g_object_unref (s_vlan);
-	return NULL;
+	return g_steal_pointer (&s_vlan);
 }
 
 static NMConnection *
@@ -4963,7 +5137,7 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg,
 	nm_connection_add_setting (connection, nm_setting_generic_new ());
 
 	/* Get a spec */
-	value = svGetValueString (ifcfg, "HWADDR");
+	value = svGetValueStr_cp (ifcfg, "HWADDR");
 	if (value) {
 		char *lower = g_ascii_strdown (value, -1);
 		*out_spec = g_strdup_printf ("%s:mac:%s", type, lower);
@@ -4972,14 +5146,14 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg,
 		return connection;
 	}
 
-	value = svGetValueString (ifcfg, "SUBCHANNELS");
+	value = svGetValueStr_cp (ifcfg, "SUBCHANNELS");
 	if (value) {
 		*out_spec = g_strdup_printf ("%s:s390-subchannels:%s", type, value);
 		g_free (value);
 		return connection;
 	}
 
-	value = svGetValueString (ifcfg, "DEVICE");
+	value = svGetValueStr_cp (ifcfg, "DEVICE");
 	if (value) {
 		*out_spec = g_strdup_printf ("%s:interface-name:%s", type, value);
 		g_free (value);
@@ -5008,11 +5182,9 @@ uuid_from_file (const char *filename)
 		return NULL;
 
 	/* Try for a UUID key before falling back to hashing the file name */
-	uuid = svGetValueString (ifcfg, "UUID");
-	if (!uuid || !strlen (uuid)) {
-		g_free (uuid);
+	uuid = svGetValueStr_cp (ifcfg, "UUID");
+	if (!uuid)
 		uuid = nm_utils_uuid_generate_from_string (svFileGetName (ifcfg), -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
-	}
 
 	svCloseFile (ifcfg);
 	return uuid;
@@ -5029,7 +5201,8 @@ check_dns_search_domains (shvarFile *ifcfg, NMSetting *s_ip4, NMSetting *s_ip6)
 	 */
 	if (!s_ip4 || nm_setting_ip_config_get_num_dns_searches (NM_SETTING_IP_CONFIG (s_ip4)) == 0) {
 		/* DNS searches */
-		char *value = svGetValueString (ifcfg, "DOMAIN");
+		char *value = svGetValueStr_cp (ifcfg, "DOMAIN");
+
 		if (value) {
 			char **searches = g_strsplit (value, " ", 0);
 			if (searches) {
@@ -5055,12 +5228,13 @@ connection_from_file_full (const char *filename,
                            GError **error,
                            gboolean *out_ignore_error)
 {
-	NMConnection *connection = NULL;
-	shvarFile *parsed;
+	nm_auto_shvar_file_close shvarFile *parsed = NULL;
+	gs_unref_object NMConnection *connection = NULL;
 	gs_free char *type = NULL;
 	char *devtype, *bootproto;
-	NMSetting *s_ip4, *s_ip6, *s_proxy, *s_port, *s_dcb = NULL;
+	NMSetting *s_ip4, *s_ip6, *s_proxy, *s_port, *s_dcb = NULL, *s_user;
 	const char *ifcfg_name = NULL;
+	gboolean has_ip4_defroute = FALSE;
 
 	g_return_val_if_fail (filename != NULL, NULL);
 	g_return_val_if_fail (out_unhandled && !*out_unhandled, NULL);
@@ -5082,30 +5256,38 @@ connection_from_file_full (const char *filename,
 
 	if (!svGetValueBoolean (parsed, "NM_CONTROLLED", TRUE)) {
 		connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled);
-		if (!connection)
+		if (!connection) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 			             "NM_CONTROLLED was false but device was not uniquely identified; device will be managed");
-		goto done;
+		}
+		return g_steal_pointer (&connection);
 	}
 
 	/* iBFT is handled by the iBFT settings plugin */
-	bootproto = svGetValueString (parsed, "BOOTPROTO");
+	bootproto = svGetValueStr_cp (parsed, "BOOTPROTO");
 	if (bootproto && !g_ascii_strcasecmp (bootproto, "ibft")) {
 		if (out_ignore_error)
 			*out_ignore_error = TRUE;
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Ignoring iBFT configuration");
 		g_free (bootproto);
-		goto done;
+		return NULL;
 	}
 	g_free (bootproto);
 
-	devtype = svGetValueString (parsed, "DEVICETYPE");
+	devtype = svGetValueStr_cp (parsed, "DEVICETYPE");
 	if (devtype) {
 		if (!strcasecmp (devtype, TYPE_TEAM))
 			type = g_strdup (TYPE_TEAM);
-		else if (!strcasecmp (devtype, TYPE_TEAM_PORT))
-			type = g_strdup (TYPE_ETHERNET);
+		else if (!strcasecmp (devtype, TYPE_TEAM_PORT)) {
+			gs_free char *device = NULL;
+
+			device = svGetValueStr_cp (parsed, "DEVICE");
+			if (device && is_vlan_device (device, parsed))
+				type = g_strdup (TYPE_VLAN);
+			else
+				type = g_strdup (TYPE_ETHERNET);
+		}
 		g_free (devtype);
 	}
 	if (!type) {
@@ -5114,33 +5296,32 @@ connection_from_file_full (const char *filename,
 		/* Team and TeamPort types are also accepted by the mere
 		 * presense of TEAM_CONFIG/TEAM_MASTER. They don't require
 		 * DEVICETYPE. */
-		t = svGetValueString (parsed, "TEAM_CONFIG");
+		t = svGetValueStr_cp (parsed, "TEAM_CONFIG");
 		if (t)
 			type = g_strdup (TYPE_TEAM);
 	}
 
 	if (!type)
-		type = svGetValueString (parsed, "TYPE");
+		type = svGetValueStr_cp (parsed, "TYPE");
 
 	if (!type) {
 		gs_free char *tmp = NULL;
 		char *device;
 
-		if ((tmp = svGetValueString (parsed, "IPV6TUNNELIPV4"))) {
+		if ((tmp = svGetValueStr_cp (parsed, "IPV6TUNNELIPV4"))) {
 			if (out_ignore_error)
 				*out_ignore_error = TRUE;
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Ignoring unsupported connection due to IPV6TUNNELIPV4");
-			goto done;
+			return NULL;
 		}
 
-		device = svGetValueString (parsed, "DEVICE");
+		device = svGetValueStr_cp (parsed, "DEVICE");
 		if (!device) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "File '%s' had neither TYPE nor DEVICE keys.", filename);
-			goto done;
+			return NULL;
 		}
-		g_assert (device[0]);
 
 		if (!strcmp (device, "lo")) {
 			if (out_ignore_error)
@@ -5148,7 +5329,7 @@ connection_from_file_full (const char *filename,
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Ignoring loopback device config.");
 			g_free (device);
-			goto done;
+			return NULL;
 		}
 
 		if (!test_type) {
@@ -5196,7 +5377,7 @@ connection_from_file_full (const char *filename,
 						g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 						             "Ignore script for unknown device type which has a matching %s script",
 						             p_path);
-						goto done;
+						return NULL;
 					}
 				}
 
@@ -5220,11 +5401,21 @@ connection_from_file_full (const char *filename,
 		}
 	}
 
+	if (nm_streq0 (type, TYPE_ETHERNET)) {
+		gs_free char *bond_options = NULL;
+
+		if (svGetValueStr (parsed, "BONDING_OPTS", &bond_options)) {
+			/* initscripts consider these as bond masters */
+			g_free (type);
+			type = g_strdup (TYPE_BOND);
+		}
+	}
+
 	if (svGetValueBoolean (parsed, "BONDING_MASTER", FALSE) &&
 	    strcasecmp (type, TYPE_BOND)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "BONDING_MASTER=yes key only allowed in TYPE=bond connections");
-		goto done;
+		return NULL;
 	}
 
 	/* Construct the connection */
@@ -5244,29 +5435,30 @@ connection_from_file_full (const char *filename,
 		connection = bridge_connection_from_ifcfg (filename, parsed, error);
 	else {
 		connection = create_unhandled_connection (filename, parsed, "unrecognized", out_unhandled);
-		if (!connection)
+		if (!connection) {
 			PARSE_WARNING ("connection type was unrecognized but device was not uniquely identified; device may be managed");
-		goto done;
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Failed to read unrecognized connection");
+		}
+		return g_steal_pointer (&connection);
 	}
 
 	if (!connection)
-		goto done;
+		return NULL;
 
 	s_ip6 = make_ip6_setting (parsed, network_file, error);
-	if (!s_ip6) {
-		g_object_unref (connection);
-		connection = NULL;
-		goto done;
-	} else
+	if (!s_ip6)
+		return NULL;
+	else
 		nm_connection_add_setting (connection, s_ip6);
 
-	s_ip4 = make_ip4_setting (parsed, network_file, error);
-	if (!s_ip4) {
-		g_object_unref (connection);
-		connection = NULL;
-		goto done;
-	} else {
-		read_aliases (NM_SETTING_IP_CONFIG (s_ip4), filename);
+	s_ip4 = make_ip4_setting (parsed, network_file, &has_ip4_defroute, error);
+	if (!s_ip4)
+		return NULL;
+	else {
+		read_aliases (NM_SETTING_IP_CONFIG (s_ip4),
+		              !has_ip4_defroute && !nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (s_ip4)),
+		              filename);
 		nm_connection_add_setting (connection, s_ip4);
 	}
 
@@ -5280,6 +5472,10 @@ connection_from_file_full (const char *filename,
 	if (s_proxy)
 		nm_connection_add_setting (connection, s_proxy);
 
+	s_user = make_user_setting (parsed, error);
+	if (s_user)
+		nm_connection_add_setting (connection, s_user);
+
 	/* Bridge port? */
 	s_port = make_bridge_port_setting (parsed);
 	if (s_port)
@@ -5290,22 +5486,15 @@ connection_from_file_full (const char *filename,
 	if (s_port)
 		nm_connection_add_setting (connection, s_port);
 
-	if (!make_dcb_setting (parsed, network_file, &s_dcb, error)) {
-		g_object_unref (connection);
-		connection = NULL;
-		goto done;
-	}
+	if (!make_dcb_setting (parsed, network_file, &s_dcb, error))
+		return NULL;
 	if (s_dcb)
 		nm_connection_add_setting (connection, s_dcb);
 
-	if (!nm_connection_normalize (connection, NULL, NULL, error)) {
-		g_object_unref (connection);
-		connection = NULL;
-	}
+	if (!nm_connection_normalize (connection, NULL, NULL, error))
+		return NULL;
 
-done:
-	svCloseFile (parsed);
-	return connection;
+	return g_steal_pointer (&connection);
 }
 
 NMConnection *
@@ -5348,7 +5537,7 @@ devtimeout_from_file (const char *filename)
 	if (!ifcfg)
 		return 0;
 
-	devtimeout_str = svGetValueString (ifcfg, "DEVTIMEOUT");
+	devtimeout_str = svGetValueStr_cp (ifcfg, "DEVTIMEOUT");
 	if (devtimeout_str) {
 		devtimeout = _nm_utils_ascii_str_to_int64 (devtimeout_str, 10, 0, G_MAXUINT, 0);
 		g_free (devtimeout_str);
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
index d1c00976..e82ef60c 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
@@ -45,8 +45,9 @@ check_rpm_temp_suffix (const char *path)
 
 	/* Matches *;[a-fA-F0-9]{8}; used by rpm */
 	ptr = strrchr (path, ';');
-	if (ptr && (strspn (ptr + 1, "abcdefABCDEF0123456789") == 8)
-	    && (! ptr[9]))
+	if (   ptr
+	    && strspn (ptr + 1, "abcdefABCDEF0123456789") == 8
+	    && !ptr[9])
 		return TRUE;
 	return FALSE;
 }
@@ -100,19 +101,20 @@ utils_should_ignore_file (const char *filename, gboolean only_ifcfg)
 }
 
 char *
-utils_cert_path (const char *parent, const char *suffix)
+utils_cert_path (const char *parent, const char *suffix, const char *extension)
 {
+	gs_free char *dir = NULL;
 	const char *name;
-	char *dir, *path;
 
-	g_return_val_if_fail (parent != NULL, NULL);
-	g_return_val_if_fail (suffix != NULL, NULL);
+	g_return_val_if_fail (parent, NULL);
+	g_return_val_if_fail (suffix, NULL);
+	g_return_val_if_fail (extension, NULL);
 
 	name = utils_get_ifcfg_name (parent, FALSE);
+	g_return_val_if_fail (name, NULL);
+
 	dir = g_path_get_dirname (parent);
-	path = g_strdup_printf ("%s/%s-%s", dir, name, suffix);
-	g_free (dir);
-	return path;
+	return g_strdup_printf ("%s/%s-%s.%s", dir, name, suffix, extension);
 }
 
 const char *
@@ -363,3 +365,117 @@ utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg)
 		return NULL;
 	return utils_get_ifcfg_path (path);
 }
+
+void
+nms_ifcfg_rh_utils_user_key_encode (const char *key, GString *str_buffer)
+{
+	gsize i;
+
+	nm_assert (key);
+	nm_assert (str_buffer);
+
+	for (i = 0; key[i]; i++) {
+		char ch = key[i];
+
+		/* we encode the key in only upper case letters, digits, and underscore.
+		 * As we expect lower-case letters to be more common, we encode lower-case
+		 * letters as upper case, and upper-case letters with a leading underscore. */
+
+		if (ch >= '0' && ch <= '9') {
+			g_string_append_c (str_buffer, ch);
+			continue;
+		}
+		if (ch >= 'a' && ch <= 'z') {
+			g_string_append_c (str_buffer, ch - 'a' + 'A');
+			continue;
+		}
+		if (ch == '.') {
+			g_string_append (str_buffer, "__");
+			continue;
+		}
+		if (ch >= 'A' && ch <= 'Z') {
+			g_string_append_c (str_buffer, '_');
+			g_string_append_c (str_buffer, ch);
+			continue;
+		}
+		g_string_append_printf (str_buffer, "_%03o", (unsigned) ch);
+	}
+}
+
+gboolean
+nms_ifcfg_rh_utils_user_key_decode (const char *name, GString *str_buffer)
+{
+	gsize i;
+
+	nm_assert (name);
+	nm_assert (str_buffer);
+
+	if (!name[0])
+		return FALSE;
+
+	for (i = 0; name[i]; ) {
+		char ch = name[i];
+
+		if (ch >= '0' && ch <= '9') {
+			g_string_append_c (str_buffer, ch);
+			i++;
+			continue;
+		}
+		if (ch >= 'A' && ch <= 'Z') {
+			g_string_append_c (str_buffer, ch - 'A' + 'a');
+			i++;
+			continue;
+		}
+
+		if (ch == '_') {
+			ch = name[i + 1];
+			if (ch == '_') {
+				g_string_append_c (str_buffer, '.');
+				i += 2;
+				continue;
+			}
+			if (ch >= 'A' && ch <= 'Z') {
+				g_string_append_c (str_buffer, ch);
+				i += 2;
+				continue;
+			}
+			if (ch >= '0' && ch <= '7') {
+				char ch2, ch3;
+				unsigned v;
+
+				ch2 = name[i + 2];
+				if (!(ch2 >= '0' && ch2 <= '7'))
+					return FALSE;
+
+				ch3 = name[i + 3];
+				if (!(ch3 >= '0' && ch3 <= '7'))
+					return FALSE;
+
+#define OCTAL_VALUE(ch) ((unsigned) ((ch) - '0'))
+				v = (OCTAL_VALUE (ch)  << 6) +
+				    (OCTAL_VALUE (ch2) << 3) +
+				     OCTAL_VALUE (ch3);
+				if (   v > 0xFF
+				    || v == 0)
+					return FALSE;
+				ch = (char) v;
+				if (   (ch >= 'A' && ch <= 'Z')
+				    || (ch >= '0' && ch <= '9')
+				    || (ch == '.')
+				    || (ch >= 'a' && ch <= 'z')) {
+					/* such characters are not expected to be encoded via
+					 * octal representation. The encoding is invalid. */
+					return FALSE;
+				}
+				g_string_append_c (str_buffer, ch);
+				i += 4;
+				continue;
+			}
+			return FALSE;
+		}
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
index af0469e6..2b2c2755 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
@@ -31,7 +31,7 @@
 #define NM_IFCFG_CONNECTION_LOG_FMTD        "%s (%s,\"%s\",%p)"
 #define NM_IFCFG_CONNECTION_LOG_ARGD(con)   NM_IFCFG_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_connection_get_uuid ((NMConnection *) (con)), nm_connection_get_id ((NMConnection *) (con)), (con)
 
-char *utils_cert_path (const char *parent, const char *suffix);
+char *utils_cert_path (const char *parent, const char *suffix, const char *extension);
 
 const char *utils_get_ifcfg_name (const char *file, gboolean only_ifcfg);
 
@@ -54,5 +54,7 @@ gboolean utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg);
 
 char *utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg);
 
-#endif  /* _UTILS_H_ */
+void nms_ifcfg_rh_utils_user_key_encode (const char *key, GString *str_buffer);
+gboolean nms_ifcfg_rh_utils_user_key_decode (const char *name, GString *str_buffer);
 
+#endif  /* _UTILS_H_ */
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index fa8013b0..d6f33c49 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -42,11 +42,13 @@
 #include "nm-setting-ip6-config.h"
 #include "nm-setting-pppoe.h"
 #include "nm-setting-vlan.h"
+#include "nm-setting-user.h"
 #include "nm-setting-team.h"
 #include "nm-setting-team-port.h"
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
+#include "nm-setting-metadata.h"
 
 #include "nms-ifcfg-rh-common.h"
 #include "nms-ifcfg-rh-reader.h"
@@ -59,7 +61,7 @@
 #define _NMLOG_PREFIX_NAME "ifcfg-rh"
 #define _NMLOG(level, ...) \
     G_STMT_START { \
-        nm_log ((level), (_NMLOG_DOMAIN), \
+        nm_log ((level), (_NMLOG_DOMAIN), NULL, NULL, \
                 "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                 _NMLOG_PREFIX_NAME": " \
                 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
@@ -99,7 +101,7 @@ save_secret_flags (shvarFile *ifcfg,
 		g_string_append (str, SECRET_FLAG_NOT_REQUIRED);
 	}
 
-	svSetValueString (ifcfg, key, str->len ? str->str : NULL);
+	svSetValueStr (ifcfg, key, str->len ? str->str : NULL);
 	g_string_free (str, TRUE);
 }
 
@@ -127,7 +129,7 @@ set_secret (shvarFile *ifcfg,
 
 	/* Only write the secret if it's system owned and supposed to be saved */
 	if (flags == NM_SETTING_SECRET_FLAG_NONE)
-		svSetValueString (keyfile, key, value);
+		svSetValueStr (keyfile, key, value);
 	else
 		svUnsetValue (keyfile, key);
 
@@ -143,112 +145,70 @@ set_secret (shvarFile *ifcfg,
 
 error:
 	/* Try setting the secret in the actual ifcfg */
-	svSetValueString (ifcfg, key, value);
+	svSetValueStr (ifcfg, key, value);
 }
 
-typedef struct ObjectType {
-	const char *setting_key;
-	NMSetting8021xCKScheme (*scheme_func)(NMSetting8021x *setting);
-	const char *           (*path_func)  (NMSetting8021x *setting);
-	GBytes *               (*blob_func)  (NMSetting8021x *setting);
-	const char *ifcfg_key;
-	const char *suffix;
-} ObjectType;
-
-static const ObjectType ca_type = {
-	NM_SETTING_802_1X_CA_CERT,
-	nm_setting_802_1x_get_ca_cert_scheme,
-	nm_setting_802_1x_get_ca_cert_path,
-	nm_setting_802_1x_get_ca_cert_blob,
-	"IEEE_8021X_CA_CERT",
-	"ca-cert.der"
-};
-
-static const ObjectType phase2_ca_type = {
-	NM_SETTING_802_1X_PHASE2_CA_CERT,
-	nm_setting_802_1x_get_phase2_ca_cert_scheme,
-	nm_setting_802_1x_get_phase2_ca_cert_path,
-	nm_setting_802_1x_get_phase2_ca_cert_blob,
-	"IEEE_8021X_INNER_CA_CERT",
-	"inner-ca-cert.der"
-};
-
-static const ObjectType client_type = {
-	NM_SETTING_802_1X_CLIENT_CERT,
-	nm_setting_802_1x_get_client_cert_scheme,
-	nm_setting_802_1x_get_client_cert_path,
-	nm_setting_802_1x_get_client_cert_blob,
-	"IEEE_8021X_CLIENT_CERT",
-	"client-cert.der"
-};
-
-static const ObjectType phase2_client_type = {
-	NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
-	nm_setting_802_1x_get_phase2_client_cert_scheme,
-	nm_setting_802_1x_get_phase2_client_cert_path,
-	nm_setting_802_1x_get_phase2_client_cert_blob,
-	"IEEE_8021X_INNER_CLIENT_CERT",
-	"inner-client-cert.der"
-};
-
-static const ObjectType pk_type = {
-	NM_SETTING_802_1X_PRIVATE_KEY,
-	nm_setting_802_1x_get_private_key_scheme,
-	nm_setting_802_1x_get_private_key_path,
-	nm_setting_802_1x_get_private_key_blob,
-	"IEEE_8021X_PRIVATE_KEY",
-	"private-key.pem"
-};
-
-static const ObjectType phase2_pk_type = {
-	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-	nm_setting_802_1x_get_phase2_private_key_scheme,
-	nm_setting_802_1x_get_phase2_private_key_path,
-	nm_setting_802_1x_get_phase2_private_key_blob,
-	"IEEE_8021X_INNER_PRIVATE_KEY",
-	"inner-private-key.pem"
-};
-
-static const ObjectType p12_type = {
-	NM_SETTING_802_1X_PRIVATE_KEY,
-	nm_setting_802_1x_get_private_key_scheme,
-	nm_setting_802_1x_get_private_key_path,
-	nm_setting_802_1x_get_private_key_blob,
-	"IEEE_8021X_PRIVATE_KEY",
-	"private-key.p12"
-};
-
-static const ObjectType phase2_p12_type = {
-	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-	nm_setting_802_1x_get_phase2_private_key_scheme,
-	nm_setting_802_1x_get_phase2_private_key_path,
-	nm_setting_802_1x_get_phase2_private_key_blob,
-	"IEEE_8021X_INNER_PRIVATE_KEY",
-	"inner-private-key.p12"
+typedef struct {
+	const NMSetting8021xSchemeVtable *vtable;
+	const char *ifcfg_rh_key;
+} Setting8021xSchemeVtable;
+
+static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = {
+	[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT],
+		.ifcfg_rh_key           = "IEEE_8021X_CA_CERT",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT],
+		.ifcfg_rh_key           = "IEEE_8021X_INNER_CA_CERT",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT],
+		.ifcfg_rh_key           = "IEEE_8021X_CLIENT_CERT",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT],
+		.ifcfg_rh_key           = "IEEE_8021X_INNER_CLIENT_CERT",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY],
+		.ifcfg_rh_key           = "IEEE_8021X_PRIVATE_KEY",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY],
+		.ifcfg_rh_key           = "IEEE_8021X_INNER_PRIVATE_KEY",
+	},
 };
 
 static gboolean
 write_object (NMSetting8021x *s_8021x,
               shvarFile *ifcfg,
-              const ObjectType *objtype,
+              const Setting8021xSchemeVtable *objtype,
               GError **error)
 {
 	NMSetting8021xCKScheme scheme;
-	const char *path = NULL;
+	const char *value = NULL;
 	GBytes *blob = NULL;
+	const char *password = NULL;
+	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+	char *secret_name, *secret_flags;
+	const char *extension;
 
 	g_return_val_if_fail (ifcfg != NULL, FALSE);
 	g_return_val_if_fail (objtype != NULL, FALSE);
 
-	scheme = (*(objtype->scheme_func))(s_8021x);
+	scheme = (*(objtype->vtable->scheme_func))(s_8021x);
 	switch (scheme) {
 	case NM_SETTING_802_1X_CK_SCHEME_UNKNOWN:
 		break;
 	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
-		blob = (*(objtype->blob_func))(s_8021x);
+		blob = (*(objtype->vtable->blob_func))(s_8021x);
 		break;
 	case NM_SETTING_802_1X_CK_SCHEME_PATH:
-		path = (*(objtype->path_func))(s_8021x);
+		value = (*(objtype->vtable->path_func))(s_8021x);
+		break;
+	case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
+		value = (*(objtype->vtable->uri_func))(s_8021x);
 		break;
 	default:
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
@@ -256,10 +216,26 @@ write_object (NMSetting8021x *s_8021x,
 		return FALSE;
 	}
 
+	/* Set the password for certificate/private key. */
+	secret_name = g_strdup_printf ("%s_PASSWORD", objtype->ifcfg_rh_key);
+	secret_flags = g_strdup_printf ("%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key);
+	password = (*(objtype->vtable->passwd_func))(s_8021x);
+	flags = (*(objtype->vtable->pwflag_func))(s_8021x);
+	set_secret (ifcfg, secret_name, password, secret_flags, flags);
+	g_free (secret_name);
+	g_free (secret_flags);
+
+	if (!objtype->vtable->format_func)
+		extension = "der";
+	else if (objtype->vtable->format_func (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
+		extension = "p12";
+	else
+		extension = "pem";
+
 	/* If certificate/private key wasn't sent, the connection may no longer be
 	 * 802.1x and thus we clear out the paths and certs.
 	 */
-	if (!path && !blob) {
+	if (!value && !blob) {
 		char *standard_file;
 		int ignored;
 
@@ -269,20 +245,20 @@ write_object (NMSetting8021x *s_8021x,
 		 * /etc/sysconfig/network-scripts/ca-cert-Test_Write_Wifi_WPA_EAP-TLS.der
 		 * will be deleted, but /etc/pki/tls/cert.pem will not.
 		 */
-		standard_file = utils_cert_path (svFileGetName (ifcfg), objtype->suffix);
+		standard_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension);
 		if (g_file_test (standard_file, G_FILE_TEST_EXISTS))
 			ignored = unlink (standard_file);
 		g_free (standard_file);
 
-		svUnsetValue (ifcfg, objtype->ifcfg_key);
+		svUnsetValue (ifcfg, objtype->ifcfg_rh_key);
 		return TRUE;
 	}
 
 	/* If the object path was specified, prefer that over any raw cert data that
 	 * may have been sent.
 	 */
-	if (path) {
-		svSetValueString (ifcfg, objtype->ifcfg_key, path);
+	if (value) {
+		svSetValueStr (ifcfg, objtype->ifcfg_rh_key, value);
 		return TRUE;
 	}
 
@@ -292,11 +268,11 @@ write_object (NMSetting8021x *s_8021x,
 		char *new_file;
 		GError *write_error = NULL;
 
-		new_file = utils_cert_path (svFileGetName (ifcfg), objtype->suffix);
+		new_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension);
 		if (!new_file) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 			             "Could not create file path for %s / %s",
-			             NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key);
+			             NM_SETTING_802_1X_SETTING_NAME, objtype->vtable->setting_key);
 			return FALSE;
 		}
 
@@ -310,13 +286,13 @@ write_object (NMSetting8021x *s_8021x,
 		                                      0600,
 		                                      &write_error);
 		if (success) {
-			svSetValueString (ifcfg, objtype->ifcfg_key, new_file);
+			svSetValueStr (ifcfg, objtype->ifcfg_rh_key, new_file);
 			g_free (new_file);
 			return TRUE;
 		} else {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 			             "Could not write certificate/key for %s / %s: %s",
-			             NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key,
+			             NM_SETTING_802_1X_SETTING_NAME, objtype->vtable->setting_key,
 			             (write_error && write_error->message) ? write_error->message : "(unknown)");
 			g_clear_error (&write_error);
 		}
@@ -332,71 +308,45 @@ write_8021x_certs (NMSetting8021x *s_8021x,
                    shvarFile *ifcfg,
                    GError **error)
 {
-	const char *password = NULL;
-	gboolean success = FALSE, is_pkcs12 = FALSE;
-	const ObjectType *otype = NULL;
-	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+	const Setting8021xSchemeVtable *otype = NULL;
 
 	/* CA certificate */
-	if (!write_object (s_8021x, ifcfg, phase2 ? &phase2_ca_type : &ca_type, error))
+	if (!write_object (s_8021x, ifcfg,
+	                   phase2
+	                       ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT]
+	                       : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT],
+	                   error))
 		return FALSE;
 
 	/* Private key */
-	if (phase2) {
-		otype = &phase2_pk_type;
-		if (nm_setting_802_1x_get_phase2_private_key_format (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
-			otype = &phase2_p12_type;
-			is_pkcs12 = TRUE;
-		}
-		password = nm_setting_802_1x_get_phase2_private_key_password (s_8021x);
-		flags = nm_setting_802_1x_get_phase2_private_key_password_flags (s_8021x);
-	} else {
-		otype = &pk_type;
-		if (nm_setting_802_1x_get_private_key_format (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
-			otype = &p12_type;
-			is_pkcs12 = TRUE;
-		}
-		password = nm_setting_802_1x_get_private_key_password (s_8021x);
-		flags = nm_setting_802_1x_get_private_key_password_flags (s_8021x);
-	}
+	if (phase2)
+		otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY];
+	else
+		otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY];
 
 	/* Save the private key */
 	if (!write_object (s_8021x, ifcfg, otype, error))
-		goto out;
-
-	/* Private key password */
-	if (phase2) {
-		set_secret (ifcfg,
-		            "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD",
-		            password,
-		            "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS",
-		            flags);
-	} else {
-		set_secret (ifcfg,
-		            "IEEE_8021X_PRIVATE_KEY_PASSWORD",
-		            password,
-		            "IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS",
-		            flags);
-	}
+		return FALSE;
 
 	/* Client certificate */
-	if (is_pkcs12) {
+	if (otype->vtable->format_func (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
 		/* Don't need a client certificate with PKCS#12 since the file is both
 		 * the client certificate and the private key in one file.
 		 */
-		svSetValueString (ifcfg,
-		                  phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT",
-		                  NULL);
+		svSetValueStr (ifcfg,
+		               phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT",
+		               NULL);
 	} else {
 		/* Save the client certificate */
-		if (!write_object (s_8021x, ifcfg, phase2 ? &phase2_client_type : &client_type, error))
-			goto out;
+		if (!write_object (s_8021x, ifcfg,
+		                   phase2
+		                       ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT]
+		                       : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT],
+		                   error))
+			return FALSE;
 	}
 
-	success = TRUE;
-
-out:
-	return success;
+	return TRUE;
 }
 
 static gboolean
@@ -406,12 +356,13 @@ write_8021x_setting (NMConnection *connection,
                      GError **error)
 {
 	NMSetting8021x *s_8021x;
+	NMSetting8021xAuthFlags auth_flags;
 	const char *value, *match;
 	char *tmp = NULL;
-	gboolean success = FALSE;
 	GString *phase2_auth;
 	GString *str;
 	guint32 i, num;
+	gint timeout;
 
 	s_8021x = nm_connection_get_setting_802_1x (connection);
 	if (!s_8021x) {
@@ -423,7 +374,7 @@ write_8021x_setting (NMConnection *connection,
 
 	/* If wired, write KEY_MGMT */
 	if (wired)
-		svSetValueString (ifcfg, "KEY_MGMT", "IEEE8021X");
+		svSetValueStr (ifcfg, "KEY_MGMT", "IEEE8021X");
 
 	/* EAP method */
 	if (nm_setting_802_1x_get_num_eap_methods (s_8021x)) {
@@ -431,14 +382,14 @@ write_8021x_setting (NMConnection *connection,
 		if (value)
 			tmp = g_ascii_strup (value, -1);
 	}
-	svSetValueString (ifcfg, "IEEE_8021X_EAP_METHODS", tmp);
+	svSetValueStr (ifcfg, "IEEE_8021X_EAP_METHODS", tmp);
 	g_free (tmp);
 
-	svSetValueString (ifcfg, "IEEE_8021X_IDENTITY",
-	                  nm_setting_802_1x_get_identity (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_IDENTITY",
+	               nm_setting_802_1x_get_identity (s_8021x));
 
-	svSetValueString (ifcfg, "IEEE_8021X_ANON_IDENTITY",
-	                  nm_setting_802_1x_get_anonymous_identity (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_ANON_IDENTITY",
+	               nm_setting_802_1x_get_anonymous_identity (s_8021x));
 
 	set_secret (ifcfg,
 	            "IEEE_8021X_PASSWORD",
@@ -450,30 +401,30 @@ write_8021x_setting (NMConnection *connection,
 	value = nm_setting_802_1x_get_phase1_peapver (s_8021x);
 	svUnsetValue (ifcfg, "IEEE_8021X_PEAP_VERSION");
 	if (value && (!strcmp (value, "0") || !strcmp (value, "1")))
-		svSetValueString (ifcfg, "IEEE_8021X_PEAP_VERSION", value);
+		svSetValueStr (ifcfg, "IEEE_8021X_PEAP_VERSION", value);
 
 	/* Force new PEAP label */
 	value = nm_setting_802_1x_get_phase1_peaplabel (s_8021x);
 	svUnsetValue (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL");
 	if (value && !strcmp (value, "1"))
-		svSetValueString (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", "yes");
+		svSetValueStr (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", "yes");
 
 	/* PAC file */
 	value = nm_setting_802_1x_get_pac_file (s_8021x);
 	svUnsetValue (ifcfg, "IEEE_8021X_PAC_FILE");
 	if (value)
-		svSetValueString (ifcfg, "IEEE_8021X_PAC_FILE", value);
+		svSetValueStr (ifcfg, "IEEE_8021X_PAC_FILE", value);
 
 	/* FAST PAC provisioning */
 	value = nm_setting_802_1x_get_phase1_fast_provisioning (s_8021x);
 	svUnsetValue (ifcfg, "IEEE_8021X_FAST_PROVISIONING");
 	if (value) {
 		if (strcmp (value, "1") == 0)
-			svSetValueString (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth");
+			svSetValueStr (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth");
 		else if (strcmp (value, "2") == 0)
-			svSetValueString (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-auth");
+			svSetValueStr (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-auth");
 		else if (strcmp (value, "3") == 0)
-			svSetValueString (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth allow-auth");
+			svSetValueStr (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth allow-auth");
 	}
 
 	/* Phase2 auth methods */
@@ -497,16 +448,27 @@ write_8021x_setting (NMConnection *connection,
 		g_free (tmp);
 	}
 
-	svSetValueString (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS",
-	                  phase2_auth->len ? phase2_auth->str : NULL);
+	auth_flags = nm_setting_802_1x_get_phase1_auth_flags (s_8021x);
+	if (auth_flags == NM_SETTING_802_1X_AUTH_FLAGS_NONE) {
+		svUnsetValue (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS");
+	} else {
+		gs_free char *flags_str = NULL;
+
+		flags_str = _nm_utils_enum_to_str_full (nm_setting_802_1x_auth_flags_get_type (),
+		                                        auth_flags, " ");
+		svSetValueStr (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS", flags_str);
+	}
+
+	svSetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS",
+	               phase2_auth->len ? phase2_auth->str : NULL);
 
 	g_string_free (phase2_auth, TRUE);
 
-	svSetValueString (ifcfg, "IEEE_8021X_SUBJECT_MATCH",
-	                  nm_setting_802_1x_get_subject_match (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_SUBJECT_MATCH",
+	               nm_setting_802_1x_get_subject_match (s_8021x));
 
-	svSetValueString (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH",
-	                  nm_setting_802_1x_get_phase2_subject_match (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH",
+	               nm_setting_802_1x_get_phase2_subject_match (s_8021x));
 
 	svUnsetValue (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES");
 	str = g_string_new (NULL);
@@ -518,7 +480,7 @@ write_8021x_setting (NMConnection *connection,
 		g_string_append (str, match);
 	}
 	if (str->len > 0)
-		svSetValueString (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES", str->str);
+		svSetValueStr (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES", str->str);
 	g_string_free (str, TRUE);
 
 	svUnsetValue (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES");
@@ -531,21 +493,28 @@ write_8021x_setting (NMConnection *connection,
 		g_string_append (str, match);
 	}
 	if (str->len > 0)
-		svSetValueString (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", str->str);
+		svSetValueStr (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", str->str);
 	g_string_free (str, TRUE);
 
-	svSetValueString (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH",
-	                  nm_setting_802_1x_get_domain_suffix_match (s_8021x));
-	svSetValueString (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH",
-	                  nm_setting_802_1x_get_phase2_domain_suffix_match (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH",
+	               nm_setting_802_1x_get_domain_suffix_match (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH",
+	               nm_setting_802_1x_get_phase2_domain_suffix_match (s_8021x));
 
-	success = write_8021x_certs (s_8021x, FALSE, ifcfg, error);
-	if (success) {
-		/* phase2/inner certs */
-		success = write_8021x_certs (s_8021x, TRUE, ifcfg, error);
-	}
+	timeout = nm_setting_802_1x_get_auth_timeout (s_8021x);
+	if (timeout > 0)
+		svSetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", timeout);
+	else
+		svUnsetValue (ifcfg, "IEEE_8021X_AUTH_TIMEOUT");
+
+	if (!write_8021x_certs (s_8021x, FALSE, ifcfg, error))
+		return FALSE;
 
-	return success;
+	/* phase2/inner certs */
+	if (!write_8021x_certs (s_8021x, TRUE, ifcfg, error))
+		return FALSE;
+
+	return TRUE;
 }
 
 static gboolean
@@ -582,27 +551,27 @@ write_wireless_security_setting (NMConnection *connection,
 		wep = TRUE;
 		*no_8021x = TRUE;
 	} else if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) {
-		svSetValueString (ifcfg, "KEY_MGMT", "WPA-PSK");
+		svSetValueStr (ifcfg, "KEY_MGMT", "WPA-PSK");
 		wpa = TRUE;
 		*no_8021x = TRUE;
 	} else if (!strcmp (key_mgmt, "ieee8021x")) {
-		svSetValueString (ifcfg, "KEY_MGMT", "IEEE8021X");
+		svSetValueStr (ifcfg, "KEY_MGMT", "IEEE8021X");
 		dynamic_wep = TRUE;
 	} else if (!strcmp (key_mgmt, "wpa-eap")) {
-		svSetValueString (ifcfg, "KEY_MGMT", "WPA-EAP");
+		svSetValueStr (ifcfg, "KEY_MGMT", "WPA-EAP");
 		wpa = TRUE;
 	}
 
 	svUnsetValue (ifcfg, "SECURITYMODE");
 	if (auth_alg) {
 		if (!strcmp (auth_alg, "shared"))
-			svSetValueString (ifcfg, "SECURITYMODE", "restricted");
+			svSetValueStr (ifcfg, "SECURITYMODE", "restricted");
 		else if (!strcmp (auth_alg, "open"))
-			svSetValueString (ifcfg, "SECURITYMODE", "open");
+			svSetValueStr (ifcfg, "SECURITYMODE", "open");
 		else if (!strcmp (auth_alg, "leap")) {
-			svSetValueString (ifcfg, "SECURITYMODE", "leap");
-			svSetValueString (ifcfg, "IEEE_8021X_IDENTITY",
-			                  nm_setting_wireless_security_get_leap_username (s_wsec));
+			svSetValueStr (ifcfg, "SECURITYMODE", "leap");
+			svSetValueStr (ifcfg, "IEEE_8021X_IDENTITY",
+			               nm_setting_wireless_security_get_leap_username (s_wsec));
 			set_secret (ifcfg,
 			            "IEEE_8021X_PASSWORD",
 			            nm_setting_wireless_security_get_leap_password (s_wsec),
@@ -632,7 +601,7 @@ write_wireless_security_setting (NMConnection *connection,
 	if (wep) {
 		/* Default WEP TX key index */
 		tmp = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) + 1);
-		svSetValueString (ifcfg, "DEFAULTKEY", tmp);
+		svSetValueStr (ifcfg, "DEFAULTKEY", tmp);
 		g_free (tmp);
 
 		for (i = 0; i < 4; i++) {
@@ -688,9 +657,9 @@ write_wireless_security_setting (NMConnection *connection,
 	for (i = 0; i < num; i++) {
 		proto = nm_setting_wireless_security_get_proto (s_wsec, i);
 		if (proto && !strcmp (proto, "wpa"))
-			svSetValueString (ifcfg, "WPA_ALLOW_WPA", "yes");
+			svSetValueStr (ifcfg, "WPA_ALLOW_WPA", "yes");
 		else if (proto && !strcmp (proto, "rsn"))
-			svSetValueString (ifcfg, "WPA_ALLOW_WPA2", "yes");
+			svSetValueStr (ifcfg, "WPA_ALLOW_WPA2", "yes");
 	}
 
 	/* WPA Pairwise ciphers */
@@ -712,7 +681,7 @@ write_wireless_security_setting (NMConnection *connection,
 		}
 	}
 	if (strlen (str->str) && (dynamic_wep == FALSE))
-		svSetValueString (ifcfg, "CIPHER_PAIRWISE", str->str);
+		svSetValueStr (ifcfg, "CIPHER_PAIRWISE", str->str);
 	g_string_free (str, TRUE);
 
 	/* WPA Group ciphers */
@@ -728,7 +697,7 @@ write_wireless_security_setting (NMConnection *connection,
 		g_free (tmp);
 	}
 	if (strlen (str->str) && (dynamic_wep == FALSE))
-		svSetValueString (ifcfg, "CIPHER_GROUP", str->str);
+		svSetValueStr (ifcfg, "CIPHER_GROUP", str->str);
 	g_string_free (str, TRUE);
 
 	if (wpa)
@@ -768,13 +737,13 @@ write_wireless_setting (NMConnection *connection,
 	}
 
 	device_mac = nm_setting_wireless_get_mac_address (s_wireless);
-	svSetValueString (ifcfg, "HWADDR", device_mac);
+	svSetValueStr (ifcfg, "HWADDR", device_mac);
 
 	cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless);
-	svSetValueString (ifcfg, "MACADDR", cloned_mac);
+	svSetValueStr (ifcfg, "MACADDR", cloned_mac);
 
-	svSetValueString (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
-	                  nm_setting_wireless_get_generate_mac_address_mask (s_wireless));
+	svSetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
+	               nm_setting_wireless_get_generate_mac_address_mask (s_wireless));
 
 	svUnsetValue (ifcfg, "HWADDR_BLACKLIST");
 	macaddr_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
@@ -782,7 +751,7 @@ write_wireless_setting (NMConnection *connection,
 		char *blacklist_str;
 
 		blacklist_str = g_strjoinv (" ", (char **) macaddr_blacklist);
-		svSetValueString (ifcfg, "HWADDR_BLACKLIST", blacklist_str);
+		svSetValueStr (ifcfg, "HWADDR_BLACKLIST", blacklist_str);
 		g_free (blacklist_str);
 	}
 
@@ -790,7 +759,7 @@ write_wireless_setting (NMConnection *connection,
 	mtu = nm_setting_wireless_get_mtu (s_wireless);
 	if (mtu) {
 		tmp = g_strdup_printf ("%u", mtu);
-		svSetValueString (ifcfg, "MTU", tmp);
+		svSetValueStr (ifcfg, "MTU", tmp);
 		g_free (tmp);
 	}
 
@@ -838,7 +807,7 @@ write_wireless_setting (NMConnection *connection,
 		g_string_append (str, "0x");
 		for (i = 0; i < ssid_len; i++)
 			g_string_append_printf (str, "%02X", ssid_data[i]);
-		svSetValueString (ifcfg, "ESSID", str->str);
+		svSetValueStr (ifcfg, "ESSID", str->str);
 		g_string_free (str, TRUE);
 	} else {
 		char buf[33];
@@ -846,17 +815,17 @@ write_wireless_setting (NMConnection *connection,
 		nm_assert (ssid_len <= 32);
 		memcpy (buf, ssid_data, ssid_len);
 		buf[ssid_len] = '\0';
-		svSetValueString (ifcfg, "ESSID", buf);
+		svSetValueStr (ifcfg, "ESSID", buf);
 	}
 
 	mode = nm_setting_wireless_get_mode (s_wireless);
 	if (!mode || !strcmp (mode, "infrastructure")) {
-		svSetValueString (ifcfg, "MODE", "Managed");
+		svSetValueStr (ifcfg, "MODE", "Managed");
 	} else if (!strcmp (mode, "adhoc")) {
-		svSetValueString (ifcfg, "MODE", "Ad-Hoc");
+		svSetValueStr (ifcfg, "MODE", "Ad-Hoc");
 		adhoc = TRUE;
 	} else if (!strcmp (mode, "ap")) {
-		svSetValueString (ifcfg, "MODE", "Ap");
+		svSetValueStr (ifcfg, "MODE", "Ap");
 	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Invalid mode '%s' in '%s' setting",
@@ -869,15 +838,15 @@ write_wireless_setting (NMConnection *connection,
 	chan = nm_setting_wireless_get_channel (s_wireless);
 	if (chan) {
 		tmp = g_strdup_printf ("%u", chan);
-		svSetValueString (ifcfg, "CHANNEL", tmp);
+		svSetValueStr (ifcfg, "CHANNEL", tmp);
 		g_free (tmp);
 	} else {
 		/* Band only set if channel is not, since channel implies band */
-		svSetValueString (ifcfg, "BAND", nm_setting_wireless_get_band (s_wireless));
+		svSetValueStr (ifcfg, "BAND", nm_setting_wireless_get_band (s_wireless));
 	}
 
 	bssid = nm_setting_wireless_get_bssid (s_wireless);
-	svSetValueString (ifcfg, "BSSID", bssid);
+	svSetValueStr (ifcfg, "BSSID", bssid);
 
 	/* Ensure DEFAULTKEY and SECURITYMODE are cleared unless there's security;
 	 * otherwise there's no way to detect WEP vs. open when WEP keys aren't
@@ -923,17 +892,17 @@ write_wireless_setting (NMConnection *connection,
 		g_free (keys_path);
 	}
 
-	svSetValueString (ifcfg, "SSID_HIDDEN", nm_setting_wireless_get_hidden (s_wireless) ? "yes" : NULL);
+	svSetValueStr (ifcfg, "SSID_HIDDEN", nm_setting_wireless_get_hidden (s_wireless) ? "yes" : NULL);
 
 	switch (nm_setting_wireless_get_powersave (s_wireless)) {
 	case NM_SETTING_WIRELESS_POWERSAVE_IGNORE:
-		svSetValueString (ifcfg, "POWERSAVE", "ignore");
+		svSetValueStr (ifcfg, "POWERSAVE", "ignore");
 		break;
 	case NM_SETTING_WIRELESS_POWERSAVE_DISABLE:
-		svSetValueString (ifcfg, "POWERSAVE", "disable");
+		svSetValueStr (ifcfg, "POWERSAVE", "disable");
 		break;
 	case NM_SETTING_WIRELESS_POWERSAVE_ENABLE:
-		svSetValueString (ifcfg, "POWERSAVE", "enable");
+		svSetValueStr (ifcfg, "POWERSAVE", "enable");
 		break;
 	default:
 	case NM_SETTING_WIRELESS_POWERSAVE_DEFAULT:
@@ -943,18 +912,18 @@ write_wireless_setting (NMConnection *connection,
 
 	switch (nm_setting_wireless_get_mac_address_randomization (s_wireless)) {
 	case NM_SETTING_MAC_RANDOMIZATION_NEVER:
-		svSetValueString (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "never");
+		svSetValueStr (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "never");
 		break;
 	case NM_SETTING_MAC_RANDOMIZATION_ALWAYS:
-		svSetValueString (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "always");
+		svSetValueStr (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "always");
 		break;
 	case NM_SETTING_MAC_RANDOMIZATION_DEFAULT:
 	default:
-		svSetValueString (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "default");
+		svSetValueStr (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "default");
 		break;
 	}
 
-	svSetValueString (ifcfg, "TYPE", TYPE_WIRELESS);
+	svSetValueStr (ifcfg, "TYPE", TYPE_WIRELESS);
 
 	return TRUE;
 }
@@ -976,13 +945,13 @@ write_infiniband_setting (NMConnection *connection, shvarFile *ifcfg, GError **e
 	}
 
 	mac = nm_setting_infiniband_get_mac_address (s_infiniband);
-	svSetValueString (ifcfg, "HWADDR", mac);
+	svSetValueStr (ifcfg, "HWADDR", mac);
 
 	svUnsetValue (ifcfg, "MTU");
 	mtu = nm_setting_infiniband_get_mtu (s_infiniband);
 	if (mtu) {
 		tmp = g_strdup_printf ("%u", mtu);
-		svSetValueString (ifcfg, "MTU", tmp);
+		svSetValueStr (ifcfg, "MTU", tmp);
 		g_free (tmp);
 	}
 
@@ -991,17 +960,17 @@ write_infiniband_setting (NMConnection *connection, shvarFile *ifcfg, GError **e
 
 	p_key = nm_setting_infiniband_get_p_key (s_infiniband);
 	if (p_key != -1) {
-		svSetValueString (ifcfg, "PKEY", "yes");
+		svSetValueStr (ifcfg, "PKEY", "yes");
 		tmp = g_strdup_printf ("%u", p_key);
-		svSetValueString (ifcfg, "PKEY_ID", tmp);
+		svSetValueStr (ifcfg, "PKEY_ID", tmp);
 		g_free (tmp);
 
 		parent = nm_setting_infiniband_get_parent (s_infiniband);
 		if (parent)
-			svSetValueString (ifcfg, "PHYSDEV", parent);
+			svSetValueStr (ifcfg, "PHYSDEV", parent);
 	}
 
-	svSetValueString (ifcfg, "TYPE", TYPE_INFINIBAND);
+	svSetValueStr (ifcfg, "TYPE", TYPE_INFINIBAND);
 
 	return TRUE;
 }
@@ -1029,13 +998,13 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	}
 
 	device_mac = nm_setting_wired_get_mac_address (s_wired);
-	svSetValueString (ifcfg, "HWADDR", device_mac);
+	svSetValueStr (ifcfg, "HWADDR", device_mac);
 
 	cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-	svSetValueString (ifcfg, "MACADDR", cloned_mac);
+	svSetValueStr (ifcfg, "MACADDR", cloned_mac);
 
-	svSetValueString (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
-	                  nm_setting_wired_get_generate_mac_address_mask (s_wired));
+	svSetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
+	               nm_setting_wired_get_generate_mac_address_mask (s_wired));
 
 	svUnsetValue (ifcfg, "HWADDR_BLACKLIST");
 	macaddr_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
@@ -1043,7 +1012,7 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		char *blacklist_str;
 
 		blacklist_str = g_strjoinv (" ", (char **) macaddr_blacklist);
-		svSetValueString (ifcfg, "HWADDR_BLACKLIST", blacklist_str);
+		svSetValueStr (ifcfg, "HWADDR_BLACKLIST", blacklist_str);
 		g_free (blacklist_str);
 	}
 
@@ -1051,7 +1020,7 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	mtu = nm_setting_wired_get_mtu (s_wired);
 	if (mtu) {
 		tmp = g_strdup_printf ("%u", mtu);
-		svSetValueString (ifcfg, "MTU", tmp);
+		svSetValueStr (ifcfg, "MTU", tmp);
 		g_free (tmp);
 	}
 
@@ -1061,30 +1030,30 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		int len = g_strv_length ((char **)s390_subchannels);
 
 		tmp = NULL;
-	    if (len == 2) {
-		    tmp = g_strdup_printf ("%s,%s", s390_subchannels[0], s390_subchannels[1]);
-	    } else if (len == 3) {
-		    tmp = g_strdup_printf ("%s,%s,%s", s390_subchannels[0], s390_subchannels[1],
-		                           s390_subchannels[2]);
+		if (len == 2) {
+			tmp = g_strdup_printf ("%s,%s", s390_subchannels[0], s390_subchannels[1]);
+		} else if (len == 3) {
+			tmp = g_strdup_printf ("%s,%s,%s", s390_subchannels[0], s390_subchannels[1],
+			                       s390_subchannels[2]);
 		}
-		svSetValueString (ifcfg, "SUBCHANNELS", tmp);
+		svSetValueStr (ifcfg, "SUBCHANNELS", tmp);
 		g_free (tmp);
 	}
 
 	svUnsetValue (ifcfg, "NETTYPE");
 	nettype = nm_setting_wired_get_s390_nettype (s_wired);
 	if (nettype)
-		svSetValueString (ifcfg, "NETTYPE", nettype);
+		svSetValueStr (ifcfg, "NETTYPE", nettype);
 
 	svUnsetValue (ifcfg, "PORTNAME");
 	portname = nm_setting_wired_get_s390_option_by_key (s_wired, "portname");
 	if (portname)
-		svSetValueString (ifcfg, "PORTNAME", portname);
+		svSetValueStr (ifcfg, "PORTNAME", portname);
 
 	svUnsetValue (ifcfg, "CTCPROT");
 	ctcprot = nm_setting_wired_get_s390_option_by_key (s_wired, "ctcprot");
 	if (ctcprot)
-		svSetValueString (ifcfg, "CTCPROT", ctcprot);
+		svSetValueStr (ifcfg, "CTCPROT", ctcprot);
 
 	svUnsetValue (ifcfg, "OPTIONS");
 	num_opts = nm_setting_wired_get_num_s390_options (s_wired);
@@ -1102,7 +1071,7 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 			g_string_append_printf (str, "%s=%s", s390_key, s390_val);
 		}
 		if (str->len)
-			svSetValueString (ifcfg, "OPTIONS", str->str);
+			svSetValueStr (ifcfg, "OPTIONS", str->str);
 		g_string_free (str, TRUE);
 	}
 
@@ -1166,12 +1135,12 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 			g_string_append_printf (str, "s sopass %s", wol_password);
 	}
 	if (str) {
-		svSetValueString (ifcfg, "ETHTOOL_OPTS", str->str);
+		svSetValueStr (ifcfg, "ETHTOOL_OPTS", str->str);
 		g_string_free (str, TRUE);
 	}
 	/* End ETHTOOL_OPT stuffing */
 
-	svSetValueString (ifcfg, "TYPE", TYPE_ETHERNET);
+	svSetValueStr (ifcfg, "TYPE", TYPE_ETHERNET);
 
 	return TRUE;
 }
@@ -1213,18 +1182,18 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg)
 		has_wired = TRUE;
 
 		device_mac = nm_setting_wired_get_mac_address (s_wired);
-		svSetValueString (ifcfg, "HWADDR", device_mac);
+		svSetValueStr (ifcfg, "HWADDR", device_mac);
 
 		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-		svSetValueString (ifcfg, "MACADDR", cloned_mac);
+		svSetValueStr (ifcfg, "MACADDR", cloned_mac);
 
-		svSetValueString (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
-		                  nm_setting_wired_get_generate_mac_address_mask (s_wired));
+		svSetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
+		               nm_setting_wired_get_generate_mac_address_mask (s_wired));
 
 		mtu = nm_setting_wired_get_mtu (s_wired);
 		if (mtu) {
 			tmp = g_strdup_printf ("%u", mtu);
-			svSetValueString (ifcfg, "MTU", tmp);
+			svSetValueStr (ifcfg, "MTU", tmp);
 			g_free (tmp);
 		} else
 			svUnsetValue (ifcfg, "MTU");
@@ -1256,13 +1225,13 @@ write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 		return FALSE;
 	}
 
-	svSetValueString (ifcfg, "VLAN", "yes");
-	svSetValueString (ifcfg, "TYPE", TYPE_VLAN);
-	svSetValueString (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con));
-	svSetValueString (ifcfg, "PHYSDEV", nm_setting_vlan_get_parent (s_vlan));
+	svSetValueStr (ifcfg, "VLAN", "yes");
+	svSetValueStr (ifcfg, "TYPE", TYPE_VLAN);
+	svSetValueStr (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con));
+	svSetValueStr (ifcfg, "PHYSDEV", nm_setting_vlan_get_parent (s_vlan));
 
 	tmp = g_strdup_printf ("%d", nm_setting_vlan_get_id (s_vlan));
-	svSetValueString (ifcfg, "VLAN_ID", tmp);
+	svSetValueStr (ifcfg, "VLAN_ID", tmp);
 	g_free (tmp);
 
 	vlan_flags = nm_setting_vlan_get_flags (s_vlan);
@@ -1276,16 +1245,16 @@ write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 	if (!NM_FLAGS_HAS (vlan_flags, NM_VLAN_FLAG_REORDER_HEADERS))
 		nm_utils_strbuf_append (&s_buf_ptr, &s_buf_len, "%sNO_REORDER_HDR", s_buf[0] ? "," : "");
 
-	svSetValueString (ifcfg, "VLAN_FLAGS", s_buf);
+	svSetValueStr (ifcfg, "VLAN_FLAGS", s_buf);
 
 	svSetValueBoolean (ifcfg, "MVRP", NM_FLAGS_HAS (vlan_flags, NM_VLAN_FLAG_MVRP));
 
 	tmp = vlan_priority_maplist_to_stringlist (s_vlan, NM_VLAN_INGRESS_MAP);
-	svSetValueString (ifcfg, "VLAN_INGRESS_PRIORITY_MAP", tmp);
+	svSetValueStr (ifcfg, "VLAN_INGRESS_PRIORITY_MAP", tmp);
 	g_free (tmp);
 
 	tmp = vlan_priority_maplist_to_stringlist (s_vlan, NM_VLAN_EGRESS_MAP);
-	svSetValueString (ifcfg, "VLAN_EGRESS_PRIORITY_MAP", tmp);
+	svSetValueStr (ifcfg, "VLAN_EGRESS_PRIORITY_MAP", tmp);
 	g_free (tmp);
 
 	svUnsetValue (ifcfg, "HWADDR");
@@ -1318,7 +1287,7 @@ write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wir
 		return FALSE;
 	}
 
-	svSetValueString (ifcfg, "DEVICE", iface);
+	svSetValueStr (ifcfg, "DEVICE", iface);
 	svUnsetValue (ifcfg, "BONDING_OPTS");
 
 	num_opts = nm_setting_bond_get_num_options (s_bond);
@@ -1338,13 +1307,13 @@ write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wir
 		}
 
 		if (str->len)
-			svSetValueString (ifcfg, "BONDING_OPTS", str->str);
+			svSetValueStr (ifcfg, "BONDING_OPTS", str->str);
 
 		g_string_free (str, TRUE);
 	}
 
-	svSetValueString (ifcfg, "TYPE", TYPE_BOND);
-	svSetValueString (ifcfg, "BONDING_MASTER", "yes");
+	svSetValueStr (ifcfg, "TYPE", TYPE_BOND);
+	svSetValueStr (ifcfg, "BONDING_MASTER", "yes");
 
 	*wired = write_wired_for_virtual (connection, ifcfg);
 
@@ -1372,9 +1341,9 @@ write_team_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 		return FALSE;
 	}
 
-	svSetValueString (ifcfg, "DEVICE", iface);
+	svSetValueStr (ifcfg, "DEVICE", iface);
 	config = nm_setting_team_get_config (s_team);
-	svSetValueString (ifcfg, "TEAM_CONFIG", config);
+	svSetValueStr (ifcfg, "TEAM_CONFIG", config);
 
 	*wired = write_wired_for_virtual (connection, ifcfg);
 
@@ -1440,24 +1409,24 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error
 		return FALSE;
 	}
 
-	svSetValueString (ifcfg, "DEVICE", iface);
+	svSetValueStr (ifcfg, "DEVICE", iface);
 	svUnsetValue (ifcfg, "BRIDGING_OPTS");
 	svSetValueBoolean (ifcfg, "STP", FALSE);
 	svUnsetValue (ifcfg, "DELAY");
 
 	mac = nm_setting_bridge_get_mac_address (s_bridge);
-	svSetValueString (ifcfg, "MACADDR", mac);
+	svSetValueStr (ifcfg, "MACADDR", mac);
 
 	/* Bridge options */
 	opts = g_string_sized_new (32);
 
 	if (nm_setting_bridge_get_stp (s_bridge)) {
-		svSetValueString (ifcfg, "STP", "yes");
+		svSetValueStr (ifcfg, "STP", "yes");
 
 		i = nm_setting_bridge_get_forward_delay (s_bridge);
 		if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_FORWARD_DELAY)) {
 			s = g_strdup_printf ("%u", i);
-			svSetValueString (ifcfg, "DELAY", s);
+			svSetValueStr (ifcfg, "DELAY", s);
 			g_free (s);
 		}
 
@@ -1493,10 +1462,10 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error
 	}
 
 	if (opts->len)
-		svSetValueString (ifcfg, "BRIDGING_OPTS", opts->str);
+		svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str);
 	g_string_free (opts, TRUE);
 
-	svSetValueString (ifcfg, "TYPE", TYPE_BRIDGE);
+	svSetValueStr (ifcfg, "TYPE", TYPE_BRIDGE);
 
 	return TRUE;
 }
@@ -1535,7 +1504,7 @@ write_bridge_port_setting (NMConnection *connection, shvarFile *ifcfg, GError **
 	}
 
 	if (opts->len)
-		svSetValueString (ifcfg, "BRIDGING_OPTS", opts->str);
+		svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str);
 	g_string_free (opts, TRUE);
 
 	return TRUE;
@@ -1552,7 +1521,7 @@ write_team_port_setting (NMConnection *connection, shvarFile *ifcfg, GError **er
 		return TRUE;
 
 	config = nm_setting_team_port_get_config (s_port);
-	svSetValueString (ifcfg, "TEAM_PORT_CONFIG", config);
+	svSetValueStr (ifcfg, "TEAM_PORT_CONFIG", config);
 
 	return TRUE;
 }
@@ -1563,13 +1532,13 @@ write_dcb_flags (shvarFile *ifcfg, const char *tag, NMSettingDcbFlags flags)
 	char prop[NM_STRLEN ("DCB_xxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyy")];
 
 	nm_sprintf_buf (prop, "DCB_%s_ENABLE", tag);
-	svSetValueString (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ENABLE) ? "yes" : NULL);
+	svSetValueStr (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ENABLE) ? "yes" : NULL);
 
 	nm_sprintf_buf (prop, "DCB_%s_ADVERTISE", tag);
-	svSetValueString (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ADVERTISE) ? "yes" : NULL);
+	svSetValueStr (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ADVERTISE) ? "yes" : NULL);
 
 	nm_sprintf_buf (prop, "DCB_%s_WILLING", tag);
-	svSetValueString (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_WILLING) ? "yes" : NULL);
+	svSetValueStr (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_WILLING) ? "yes" : NULL);
 }
 
 static void
@@ -1609,7 +1578,7 @@ write_dcb_bool_array (shvarFile *ifcfg,
 	str[8] = 0;
 	for (i = 0; i < 8; i++)
 		str[i] = get_func (s_dcb, i) ? '1' : '0';
-	svSetValueString (ifcfg, key, str);
+	svSetValueStr (ifcfg, key, str);
 }
 
 typedef guint (*DcbGetUintFunc) (NMSettingDcb *, guint);
@@ -1639,7 +1608,7 @@ write_dcb_uint_array (shvarFile *ifcfg,
 		else
 			g_assert_not_reached ();
 	}
-	svSetValueString (ifcfg, key, str);
+	svSetValueStr (ifcfg, key, str);
 }
 
 static void
@@ -1663,7 +1632,7 @@ write_dcb_percent_array (shvarFile *ifcfg,
 			g_string_append_c (str, ',');
 		g_string_append_printf (str, "%d", get_func (s_dcb, i));
 	}
-	svSetValueString (ifcfg, key, str->str);
+	svSetValueStr (ifcfg, key, str->str);
 	g_string_free (str, TRUE);
 }
 
@@ -1707,13 +1676,13 @@ write_dcb_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		return TRUE;
 	}
 
-	svSetValueString (ifcfg, "DCB", "yes");
+	svSetValueStr (ifcfg, "DCB", "yes");
 
 	write_dcb_app (ifcfg, "APP_FCOE",
 	               nm_setting_dcb_get_app_fcoe_flags (s_dcb),
 	               nm_setting_dcb_get_app_fcoe_priority (s_dcb));
 	if (nm_setting_dcb_get_app_fcoe_flags (s_dcb) & NM_SETTING_DCB_FLAG_ENABLE)
-		svSetValueString (ifcfg, KEY_DCB_APP_FCOE_MODE, nm_setting_dcb_get_app_fcoe_mode (s_dcb));
+		svSetValueStr (ifcfg, KEY_DCB_APP_FCOE_MODE, nm_setting_dcb_get_app_fcoe_mode (s_dcb));
 	else
 		svUnsetValue (ifcfg, KEY_DCB_APP_FCOE_MODE);
 
@@ -1749,21 +1718,21 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	char *tmp;
 	gint i_int;
 
-	svSetValueString (ifcfg, "NAME", nm_setting_connection_get_id (s_con));
-	svSetValueString (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con));
-	svSetValueString (ifcfg, "STABLE_ID", nm_setting_connection_get_stable_id (s_con));
-	svSetValueString (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con));
+	svSetValueStr (ifcfg, "NAME", nm_setting_connection_get_id (s_con));
+	svSetValueStr (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con));
+	svSetValueStr (ifcfg, "STABLE_ID", nm_setting_connection_get_stable_id (s_con));
+	svSetValueStr (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con));
 	svSetValueBoolean (ifcfg, "ONBOOT", nm_setting_connection_get_autoconnect (s_con));
 
 	i_int = nm_setting_connection_get_autoconnect_priority (s_con);
 	tmp = i_int != NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT
 	      ? g_strdup_printf ("%d", i_int) : NULL;
-	svSetValueString (ifcfg, "AUTOCONNECT_PRIORITY", tmp);
+	svSetValueStr (ifcfg, "AUTOCONNECT_PRIORITY", tmp);
 	g_free (tmp);
 
 	i_int = nm_setting_connection_get_autoconnect_retries (s_con);
 	tmp = i_int != -1 ? g_strdup_printf ("%d", i_int) : NULL;
-	svSetValueString (ifcfg, "AUTOCONNECT_RETRIES", tmp);
+	svSetValueStr (ifcfg, "AUTOCONNECT_RETRIES", tmp);
 	g_free (tmp);
 
 	/* Only save the value for master connections */
@@ -1773,9 +1742,9 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	    || !g_strcmp0 (type, NM_SETTING_BRIDGE_SETTING_NAME)) {
 		NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
 		autoconnect_slaves = nm_setting_connection_get_autoconnect_slaves (s_con);
-		svSetValueString (ifcfg, "AUTOCONNECT_SLAVES",
-		                  autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES ? "yes" :
-		                  autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO ? "no" : NULL);
+		svSetValueStr (ifcfg, "AUTOCONNECT_SLAVES",
+		               autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES ? "yes" :
+		               autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO ? "no" : NULL);
 	} else
 		svUnsetValue (ifcfg, "AUTOCONNECT_SLAVES");
 
@@ -1789,7 +1758,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	default:
 		tmp = NULL;
 	}
-	svSetValueString (ifcfg, "LLDP", tmp);
+	svSetValueStr (ifcfg, "LLDP", tmp);
 
 	/* Permissions */
 	svUnsetValue (ifcfg, "USERS");
@@ -1809,19 +1778,19 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 			if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL))
 				g_string_append (str, puser);
 		}
-		svSetValueString (ifcfg, "USERS", str->str);
+		svSetValueStr (ifcfg, "USERS", str->str);
 		g_string_free (str, TRUE);
 	}
 
-	svSetValueString (ifcfg, "ZONE", nm_setting_connection_get_zone(s_con));
+	svSetValueStr (ifcfg, "ZONE", nm_setting_connection_get_zone (s_con));
 
-	svSetValueString (ifcfg, "MASTER_UUID", NULL);
-	svSetValueString (ifcfg, "MASTER", NULL);
-	svSetValueString (ifcfg, "SLAVE", NULL);
-	svSetValueString (ifcfg, "BRIDGE_UUID", NULL);
-	svSetValueString (ifcfg, "BRIDGE", NULL);
-	svSetValueString (ifcfg, "TEAM_MASTER_UUID", NULL);
-	svSetValueString (ifcfg, "TEAM_MASTER", NULL);
+	svSetValueStr (ifcfg, "MASTER_UUID", NULL);
+	svSetValueStr (ifcfg, "MASTER", NULL);
+	svSetValueStr (ifcfg, "SLAVE", NULL);
+	svSetValueStr (ifcfg, "BRIDGE_UUID", NULL);
+	svSetValueStr (ifcfg, "BRIDGE", NULL);
+	svSetValueStr (ifcfg, "TEAM_MASTER_UUID", NULL);
+	svSetValueStr (ifcfg, "TEAM_MASTER", NULL);
 
 	master = nm_setting_connection_get_master (s_con);
 	if (master) {
@@ -1838,23 +1807,23 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 		}
 
 		if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BOND_SETTING_NAME)) {
-			svSetValueString (ifcfg, "MASTER_UUID", master);
-			svSetValueString (ifcfg, "MASTER", master_iface);
-			svSetValueString (ifcfg, "SLAVE", "yes");
+			svSetValueStr (ifcfg, "MASTER_UUID", master);
+			svSetValueStr (ifcfg, "MASTER", master_iface);
+			svSetValueStr (ifcfg, "SLAVE", "yes");
 		} else if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BRIDGE_SETTING_NAME)) {
-			svSetValueString (ifcfg, "BRIDGE_UUID", master);
-			svSetValueString (ifcfg, "BRIDGE", master_iface);
+			svSetValueStr (ifcfg, "BRIDGE_UUID", master);
+			svSetValueStr (ifcfg, "BRIDGE", master_iface);
 		} else if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_TEAM_SETTING_NAME)) {
-			svSetValueString (ifcfg, "TEAM_MASTER_UUID", master);
-			svSetValueString (ifcfg, "TEAM_MASTER", master_iface);
+			svSetValueStr (ifcfg, "TEAM_MASTER_UUID", master);
+			svSetValueStr (ifcfg, "TEAM_MASTER", master_iface);
 			svUnsetValue (ifcfg, "TYPE");
 		}
 	}
 
 	if (nm_streq0 (type, NM_SETTING_TEAM_SETTING_NAME))
-		svSetValueString (ifcfg, "DEVICETYPE", TYPE_TEAM);
+		svSetValueStr (ifcfg, "DEVICETYPE", TYPE_TEAM);
 	else if (master_iface && nm_setting_connection_is_slave_type (s_con, NM_SETTING_TEAM_SETTING_NAME))
-		svSetValueString (ifcfg, "DEVICETYPE", TYPE_TEAM_PORT);
+		svSetValueStr (ifcfg, "DEVICETYPE", TYPE_TEAM_PORT);
 	else
 		svUnsetValue (ifcfg, "DEVICETYPE");
 
@@ -1876,40 +1845,94 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 			if ((uuid = nm_setting_connection_get_secondary (s_con, i)) != NULL)
 				g_string_append (str, uuid);
 		}
-		svSetValueString (ifcfg, "SECONDARY_UUIDS", str->str);
+		svSetValueStr (ifcfg, "SECONDARY_UUIDS", str->str);
 		g_string_free (str, TRUE);
 	}
 
 	svUnsetValue (ifcfg, "GATEWAY_PING_TIMEOUT");
 	if (nm_setting_connection_get_gateway_ping_timeout (s_con)) {
 		tmp = g_strdup_printf ("%" G_GUINT32_FORMAT, nm_setting_connection_get_gateway_ping_timeout (s_con));
-		svSetValueString (ifcfg, "GATEWAY_PING_TIMEOUT", tmp);
+		svSetValueStr (ifcfg, "GATEWAY_PING_TIMEOUT", tmp);
 		g_free (tmp);
 	}
 
 	switch (nm_setting_connection_get_metered (s_con)) {
 	case NM_METERED_YES:
-		svSetValueString (ifcfg, "CONNECTION_METERED", "yes");
+		svSetValueStr (ifcfg, "CONNECTION_METERED", "yes");
 		break;
 	case NM_METERED_NO:
-		svSetValueString (ifcfg, "CONNECTION_METERED", "no");
+		svSetValueStr (ifcfg, "CONNECTION_METERED", "no");
 		break;
 	default:
 		svUnsetValue (ifcfg, "CONNECTION_METERED");
 	}
 }
 
+static char *
+get_route_attributes_string (NMIPRoute *route, int family)
+{
+	gs_strfreev char **names = NULL;
+	GVariant *attr, *lock;
+	GString *str;
+	int i;
+
+	names = nm_ip_route_get_attribute_names (route);
+	if (!names || !names[0])
+		return NULL;
+
+	str = g_string_new ("");
+
+	for (i = 0; names[i]; i++) {
+		attr = nm_ip_route_get_attribute (route, names[i]);
+
+		if (!nm_ip_route_attribute_validate (names[i], attr, family, NULL, NULL))
+			continue;
+
+		if (NM_IN_STRSET (names[i], NM_IP_ROUTE_ATTRIBUTE_WINDOW,
+		                            NM_IP_ROUTE_ATTRIBUTE_CWND,
+		                            NM_IP_ROUTE_ATTRIBUTE_INITCWND,
+		                            NM_IP_ROUTE_ATTRIBUTE_INITRWND,
+		                            NM_IP_ROUTE_ATTRIBUTE_MTU)) {
+			char lock_name[256];
+
+			nm_sprintf_buf (lock_name, "lock-%s", names[i]);
+			lock = nm_ip_route_get_attribute (route, lock_name);
+
+			g_string_append_printf (str,
+			                        "%s %s%u",
+			                        names[i],
+			                        (lock && g_variant_get_boolean (lock)) ? "lock " : "",
+			                        g_variant_get_uint32 (attr));
+		} else if (strstr (names[i], "lock-")) {
+			/* handled above */
+		} else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_TOS)) {
+			g_string_append_printf (str, "%s %u", names[i], (unsigned) g_variant_get_byte (attr));
+		} else if (   nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC)
+		           || nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_FROM)) {
+			char *arg = nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC) ? "src" : "from";
+
+			g_string_append_printf (str, "%s %s", arg, g_variant_get_string (attr, NULL));
+		} else {
+			_LOGW ("unknown route option '%s'", names[i]);
+			continue;
+		}
+		if (names[i + 1])
+			g_string_append_c (str, ' ');
+	}
+
+	return g_string_free (str, FALSE);
+}
+
 static gboolean
 write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError **error)
 {
 	const char *dest, *next_hop;
 	char **route_items;
-	char *route_contents;
+	gs_free char *route_contents = NULL;
 	NMIPRoute *route;
 	guint32 prefix;
 	gint64 metric;
 	guint32 i, num;
-	gboolean success = FALSE;
 
 	g_return_val_if_fail (filename != NULL, FALSE);
 	g_return_val_if_fail (s_ip4 != NULL, FALSE);
@@ -1922,8 +1945,10 @@ write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError
 		return TRUE;
 	}
 
-	route_items = g_malloc0 (sizeof (char*) * (num + 1));
+	route_items = g_malloc0 (sizeof (char *) * (num + 1));
 	for (i = 0; i < num; i++) {
+		gs_free char *options = NULL;
+
 		route = nm_setting_ip_config_get_route (s_ip4, i);
 
 		dest = nm_ip_route_get_dest (route);
@@ -1931,10 +1956,19 @@ write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError
 		next_hop = nm_ip_route_get_next_hop (route);
 		metric = nm_ip_route_get_metric (route);
 
-		if (metric == -1)
-			route_items[i] = g_strdup_printf ("%s/%u via %s\n", dest, prefix, next_hop);
-		else
-			route_items[i] = g_strdup_printf ("%s/%u via %s metric %u\n", dest, prefix, next_hop, (guint32) metric);
+		options = get_route_attributes_string (route, AF_INET);
+
+		if (metric == -1) {
+			route_items[i] = g_strdup_printf ("%s/%u via %s%s%s\n",
+			                                  dest, prefix, next_hop,
+			                                  options ? " " : "",
+			                                  options ?: "");
+		} else {
+			route_items[i] = g_strdup_printf ("%s/%u via %s metric %u%s%s\n",
+			                                  dest, prefix, next_hop, (guint32) metric,
+			                                  options ? " " : "",
+			                                  options ?: "");
+		}
 	}
 	route_items[num] = NULL;
 	route_contents = g_strjoinv (NULL, route_items);
@@ -1943,15 +1977,10 @@ write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError
 	if (!g_file_set_contents (filename, route_contents, -1, NULL)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Writing route file '%s' failed", filename);
-		goto error;
+		return FALSE;
 	}
 
-	success = TRUE;
-
-error:
-	g_free (route_contents);
-
-	return success;
+	return TRUE;
 }
 
 static gboolean
@@ -1972,19 +2001,19 @@ write_proxy_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	method = nm_setting_proxy_get_method (s_proxy);
 	switch (method) {
 	case NM_SETTING_PROXY_METHOD_AUTO:
-		svSetValueString (ifcfg, "PROXY_METHOD", "auto");
+		svSetValueStr (ifcfg, "PROXY_METHOD", "auto");
 
 		pac_url = nm_setting_proxy_get_pac_url (s_proxy);
 		if (pac_url)
-			svSetValueString (ifcfg, "PAC_URL", pac_url);
+			svSetValueStr (ifcfg, "PAC_URL", pac_url);
 
 		pac_script = nm_setting_proxy_get_pac_script (s_proxy);
 		if (pac_script)
-			svSetValueString (ifcfg, "PAC_SCRIPT", pac_script);
+			svSetValueStr (ifcfg, "PAC_SCRIPT", pac_script);
 
 		break;
 	case NM_SETTING_PROXY_METHOD_NONE:
-		svSetValueString (ifcfg, "PROXY_METHOD", "none");
+		svSetValueStr (ifcfg, "PROXY_METHOD", "none");
 		break;
 	}
 
@@ -1994,14 +2023,51 @@ write_proxy_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 }
 
 static gboolean
+write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
+{
+	NMSettingUser *s_user;
+	guint i, len;
+	const char *const*keys;
+
+	s_user = NM_SETTING_USER (nm_connection_get_setting (connection, NM_TYPE_SETTING_USER));
+
+	svUnsetValuesWithPrefix (ifcfg, "NM_USER_");
+
+	if (!s_user)
+		return TRUE;
+
+	keys = nm_setting_user_get_keys (s_user, &len);
+	if (len) {
+		nm_auto_free_gstring GString *str = g_string_sized_new (100);
+
+		for (i = 0; i < len; i++) {
+			const char *key = keys[i];
+
+			g_string_set_size (str, 0);
+			g_string_append (str, "NM_USER_");
+			nms_ifcfg_rh_utils_user_key_encode (key, str);
+			svSetValue (ifcfg,
+			            str->str,
+			            nm_setting_user_get_data (s_user, key));
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
 write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 {
 	NMSettingIPConfig *s_ip4;
 	const char *value;
-	char *addr_key, *prefix_key, *netmask_key, *gw_key, *metric_key, *tmp;
+	char *tmp;
+	char addr_key[64];
+	char prefix_key[64];
+	char netmask_key[64];
+	char gw_key[64];
 	char *route_path = NULL;
-	gint32 j;
-	guint32 i, n, num;
+	gint j;
+	guint i, num, n;
 	gint64 route_metric;
 	gint priority;
 	int timeout;
@@ -2041,26 +2107,21 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		svUnsetValue (ifcfg, "BOOTPROTO");
 		for (j = -1; j < 256; j++) {
 			if (j == -1) {
-				addr_key = g_strdup ("IPADDR");
-				prefix_key = g_strdup ("PREFIX");
-				netmask_key = g_strdup ("NETMASK");
-				gw_key = g_strdup ("GATEWAY");
+				nm_sprintf_buf (addr_key, "IPADDR");
+				nm_sprintf_buf (prefix_key, "PREFIX");
+				nm_sprintf_buf (netmask_key, "NETMASK");
+				nm_sprintf_buf (gw_key, "GATEWAY");
 			} else {
-				addr_key = g_strdup_printf ("IPADDR%d", j);
-				prefix_key = g_strdup_printf ("PREFIX%d", j);
-				netmask_key = g_strdup_printf ("NETMASK%d", j);
-				gw_key = g_strdup_printf ("GATEWAY%d", j);
+				nm_sprintf_buf (addr_key, "IPADDR%d", (guint) j);
+				nm_sprintf_buf (prefix_key, "PREFIX%u",  (guint) j);
+				nm_sprintf_buf (netmask_key, "NETMASK%u",  (guint) j);
+				nm_sprintf_buf (gw_key, "GATEWAY%u",  (guint) j);
 			}
 
 			svUnsetValue (ifcfg, addr_key);
 			svUnsetValue (ifcfg, prefix_key);
 			svUnsetValue (ifcfg, netmask_key);
 			svUnsetValue (ifcfg, gw_key);
-
-			g_free (addr_key);
-			g_free (prefix_key);
-			g_free (netmask_key);
-			g_free (gw_key);
 		}
 
 		route_path = utils_get_route_path (svFileGetName (ifcfg));
@@ -2069,14 +2130,20 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		return TRUE;
 	}
 
+	num = nm_setting_ip_config_get_num_addresses (s_ip4);
+
 	if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO))
-		svSetValueString (ifcfg, "BOOTPROTO", "dhcp");
-	else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
-		svSetValueString (ifcfg, "BOOTPROTO", "none");
-	else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
-		svSetValueString (ifcfg, "BOOTPROTO", "autoip");
+		svSetValueStr (ifcfg, "BOOTPROTO", "dhcp");
+	else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+		/* Preserve the archaic form of "static" if there actually
+		 * is static configuration. */
+		if (g_strcmp0 (svGetValue (ifcfg, "BOOTPROTO", &tmp), "static") || !num)
+			svSetValueStr (ifcfg, "BOOTPROTO", "none");
+		g_free (tmp);
+	} else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
+		svSetValueStr (ifcfg, "BOOTPROTO", "autoip");
 	else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
-		svSetValueString (ifcfg, "BOOTPROTO", "shared");
+		svSetValueStr (ifcfg, "BOOTPROTO", "shared");
 
 	/* Clear out un-numbered IP address fields */
 	svUnsetValue (ifcfg, "IPADDR");
@@ -2092,9 +2159,9 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	/* Write out IPADDR<n>, PREFIX<n>, GATEWAY<n> for current IP addresses
 	 * without labels. Unset obsolete NETMASK<n>.
 	 */
-	num = nm_setting_ip_config_get_num_addresses (s_ip4);
 	for (i = n = 0; i < num; i++) {
 		NMIPAddress *addr;
+		guint prefix;
 
 		addr = nm_setting_ip_config_get_address (s_ip4, i);
 
@@ -2112,66 +2179,62 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 			 * See https://bugzilla.redhat.com/show_bug.cgi?id=771673
 			 * and https://bugzilla.redhat.com/show_bug.cgi?id=1105770
 			 */
-			addr_key = g_strdup ("IPADDR");
-			prefix_key = g_strdup ("PREFIX");
-			netmask_key = g_strdup ("NETMASK");
-			gw_key = g_strdup ("GATEWAY");
+			nm_sprintf_buf (addr_key, "IPADDR");
+			nm_sprintf_buf (prefix_key, "PREFIX");
+			nm_sprintf_buf (netmask_key, "NETMASK");
+			nm_sprintf_buf (gw_key, "GATEWAY");
 		} else {
-			addr_key = g_strdup_printf ("IPADDR%d", n);
-			prefix_key = g_strdup_printf ("PREFIX%d", n);
-			netmask_key = g_strdup_printf ("NETMASK%d", n);
-			gw_key = g_strdup_printf ("GATEWAY%d", n);
+			nm_sprintf_buf (addr_key, "IPADDR%u", n);
+			nm_sprintf_buf (prefix_key, "PREFIX%u", n);
+			nm_sprintf_buf (netmask_key, "NETMASK%u", n);
+			nm_sprintf_buf (gw_key, "GATEWAY%u", n);
 		}
 
-		svSetValueString (ifcfg, addr_key, nm_ip_address_get_address (addr));
+		svSetValueStr (ifcfg, addr_key, nm_ip_address_get_address (addr));
 
-		tmp = g_strdup_printf ("%u", nm_ip_address_get_prefix (addr));
-		svSetValueString (ifcfg, prefix_key, tmp);
+		prefix = nm_ip_address_get_prefix (addr);
+		tmp = g_strdup_printf ("%u", prefix);
+		svSetValueStr (ifcfg, prefix_key, tmp);
 		g_free (tmp);
 
-		svUnsetValue (ifcfg, netmask_key);
-		svUnsetValue (ifcfg, gw_key);
+		/* If the legacy "NETMASK" is present, keep it. */
+		if (svGetValue (ifcfg, netmask_key, &tmp)) {
+			char buf[INET_ADDRSTRLEN];
 
-		g_free (addr_key);
-		g_free (prefix_key);
-		g_free (netmask_key);
-		g_free (gw_key);
+			g_free (tmp);
+			svSetValueStr (ifcfg, netmask_key, nm_utils_inet4_ntop (prefix, buf));
+		}
+
+		svUnsetValue (ifcfg, gw_key);
 		n++;
 	}
 
 	/* Clear remaining IPADDR<n..255>, etc */
-	for (; n < 256; n++) {
-		addr_key = g_strdup_printf ("IPADDR%d", n);
-		prefix_key = g_strdup_printf ("PREFIX%d", n);
-		netmask_key = g_strdup_printf ("NETMASK%d", n);
-		gw_key = g_strdup_printf ("GATEWAY%d", n);
+	for (i = n; i < 256; i++) {
+		nm_sprintf_buf (addr_key, "IPADDR%u", i);
+		nm_sprintf_buf (prefix_key, "PREFIX%u", i);
+		nm_sprintf_buf (netmask_key, "NETMASK%u", i);
+		nm_sprintf_buf (gw_key, "GATEWAY%u", i);
 
 		svUnsetValue (ifcfg, addr_key);
 		svUnsetValue (ifcfg, prefix_key);
 		svUnsetValue (ifcfg, netmask_key);
 		svUnsetValue (ifcfg, gw_key);
-
-		g_free (addr_key);
-		g_free (prefix_key);
-		g_free (netmask_key);
-		g_free (gw_key);
 	}
 
-	svSetValueString (ifcfg, "GATEWAY", nm_setting_ip_config_get_gateway (s_ip4));
+	svSetValueStr (ifcfg, "GATEWAY", nm_setting_ip_config_get_gateway (s_ip4));
 
 	num = nm_setting_ip_config_get_num_dns (s_ip4);
 	for (i = 0; i < 254; i++) {
 		const char *dns;
 
-		addr_key = g_strdup_printf ("DNS%d", i + 1);
-
+		nm_sprintf_buf (addr_key, "DNS%u", i + 1);
 		if (i >= num)
 			svUnsetValue (ifcfg, addr_key);
 		else {
 			dns = nm_setting_ip_config_get_dns (s_ip4, i);
-			svSetValueString (ifcfg, addr_key, dns);
+			svSetValueStr (ifcfg, addr_key, dns);
 		}
-		g_free (addr_key);
 	}
 
 	num = nm_setting_ip_config_get_num_dns_searches (s_ip4);
@@ -2182,7 +2245,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 				g_string_append_c (searches, ' ');
 			g_string_append (searches, nm_setting_ip_config_get_dns_search (s_ip4, i));
 		}
-		svSetValueString (ifcfg, "DOMAIN", searches->str);
+		svSetValueStr (ifcfg, "DOMAIN", searches->str);
 		g_string_free (searches, TRUE);
 	} else
 		svUnsetValue (ifcfg, "DOMAIN");
@@ -2191,37 +2254,37 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	svSetValueBoolean (ifcfg, "DEFROUTE", !nm_setting_ip_config_get_never_default (s_ip4));
 
 	/* Missing PEERDNS means TRUE, so write it only when is FALSE */
-	svSetValueString (ifcfg, "PEERDNS",
-		nm_setting_ip_config_get_ignore_auto_dns (s_ip4) ? "no" : NULL);
+	svSetValueStr (ifcfg, "PEERDNS",
+	               nm_setting_ip_config_get_ignore_auto_dns (s_ip4) ? "no" : NULL);
 	/* Missing PEERROUTES means TRUE, so write it only when is FALSE */
-	svSetValueString (ifcfg, "PEERROUTES",
-		nm_setting_ip_config_get_ignore_auto_routes (s_ip4) ? "no" : NULL);
+	svSetValueStr (ifcfg, "PEERROUTES",
+	               nm_setting_ip_config_get_ignore_auto_routes (s_ip4) ? "no" : NULL);
 
 	value = nm_setting_ip_config_get_dhcp_hostname (s_ip4);
-	svSetValueString (ifcfg, "DHCP_HOSTNAME", value);
+	svSetValueStr (ifcfg, "DHCP_HOSTNAME", value);
 
 	value = nm_setting_ip4_config_get_dhcp_fqdn (NM_SETTING_IP4_CONFIG (s_ip4));
-	svSetValueString (ifcfg, "DHCP_FQDN", value);
+	svSetValueStr (ifcfg, "DHCP_FQDN", value);
 
 	/* Missing DHCP_SEND_HOSTNAME means TRUE, and we prefer not write it explicitly
 	 * in that case, because it is NM-specific variable
 	 */
-	svSetValueString (ifcfg, "DHCP_SEND_HOSTNAME",
-			  nm_setting_ip_config_get_dhcp_send_hostname (s_ip4) ? NULL : "no");
+	svSetValueStr (ifcfg, "DHCP_SEND_HOSTNAME",
+	               nm_setting_ip_config_get_dhcp_send_hostname (s_ip4) ? NULL : "no");
 
 	value = nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4));
-	svSetValueString (ifcfg, "DHCP_CLIENT_ID", value);
+	svSetValueStr (ifcfg, "DHCP_CLIENT_ID", value);
 
 	timeout = nm_setting_ip_config_get_dhcp_timeout (s_ip4);
 	tmp = timeout ? g_strdup_printf ("%d", timeout) : NULL;
-	svSetValueString (ifcfg, "IPV4_DHCP_TIMEOUT", tmp);
+	svSetValueStr (ifcfg, "IPV4_DHCP_TIMEOUT", tmp);
 	g_free (tmp);
 
 	svSetValueBoolean (ifcfg, "IPV4_FAILURE_FATAL", !nm_setting_ip_config_get_may_fail (s_ip4));
 
 	route_metric = nm_setting_ip_config_get_route_metric (s_ip4);
 	tmp = route_metric != -1 ? g_strdup_printf ("%"G_GINT64_FORMAT, route_metric) : NULL;
-	svSetValueString (ifcfg, "IPV4_ROUTE_METRIC", tmp);
+	svSetValueStr (ifcfg, "IPV4_ROUTE_METRIC", tmp);
 	g_free (tmp);
 
 	/* Static routes - route-<name> file */
@@ -2250,28 +2313,34 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 			NMIPRoute *route;
 			guint32 netmask;
 			gint64 metric;
+			char metric_key[64];
+			char options_key[64];
 
-			addr_key = g_strdup_printf ("ADDRESS%d", i);
-			netmask_key = g_strdup_printf ("NETMASK%d", i);
-			gw_key = g_strdup_printf ("GATEWAY%d", i);
-			metric_key = g_strdup_printf ("METRIC%d", i);
+			nm_sprintf_buf (addr_key, "ADDRESS%u", i);
+			nm_sprintf_buf (netmask_key, "NETMASK%u", i);
+			nm_sprintf_buf (gw_key, "GATEWAY%u", i);
+			nm_sprintf_buf (metric_key, "METRIC%u", i);
+			nm_sprintf_buf (options_key, "OPTIONS%u", i);
 
 			if (i >= num) {
 				svUnsetValue (routefile, addr_key);
 				svUnsetValue (routefile, netmask_key);
 				svUnsetValue (routefile, gw_key);
 				svUnsetValue (routefile, metric_key);
+				svUnsetValue (routefile, options_key);
 			} else {
+				gs_free char *options = NULL;
+
 				route = nm_setting_ip_config_get_route (s_ip4, i);
 
-				svSetValueString (routefile, addr_key, nm_ip_route_get_dest (route));
+				svSetValueStr (routefile, addr_key, nm_ip_route_get_dest (route));
 
 				memset (buf, 0, sizeof (buf));
 				netmask = nm_utils_ip4_prefix_to_netmask (nm_ip_route_get_prefix (route));
 				inet_ntop (AF_INET, (const void *) &netmask, &buf[0], sizeof (buf));
-				svSetValueString (routefile, netmask_key, &buf[0]);
+				svSetValueStr (routefile, netmask_key, &buf[0]);
 
-				svSetValueString (routefile, gw_key, nm_ip_route_get_next_hop (route));
+				svSetValueStr (routefile, gw_key, nm_ip_route_get_next_hop (route));
 
 				memset (buf, 0, sizeof (buf));
 				metric = nm_ip_route_get_metric (route);
@@ -2279,15 +2348,14 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 					svUnsetValue (routefile, metric_key);
 				else {
 					tmp = g_strdup_printf ("%u", (guint32) metric);
-					svSetValueString (routefile, metric_key, tmp);
+					svSetValueStr (routefile, metric_key, tmp);
 					g_free (tmp);
 				}
-			}
 
-			g_free (addr_key);
-			g_free (netmask_key);
-			g_free (gw_key);
-			g_free (metric_key);
+				options = get_route_attributes_string (route, AF_INET);
+				if (options)
+					svSetValueStr (routefile, options_key, options);
+			}
 		}
 		if (!svWriteFile (routefile, 0644, error)) {
 			svCloseFile (routefile);
@@ -2305,7 +2373,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	if (timeout < 0)
 		svUnsetValue (ifcfg, "ARPING_WAIT");
 	else if (timeout == 0)
-		svSetValueString (ifcfg, "ARPING_WAIT", "0");
+		svSetValueStr (ifcfg, "ARPING_WAIT", "0");
 	else {
 		/* Round the value up to next integer */
 		svSetValueInt64 (ifcfg, "ARPING_WAIT", (timeout - 1) / 1000 + 1);
@@ -2325,7 +2393,7 @@ write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path)
 {
 	NMSettingIPConfig *s_ip4;
 	gs_free char *base_ifcfg_dir = NULL, *base_ifcfg_name = NULL;
-	const char*base_name;
+	const char *base_name;
 	int i, num, base_ifcfg_name_len, base_name_len;
 	GDir *dir;
 
@@ -2392,13 +2460,13 @@ write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path)
 		ifcfg = svCreateFile (path);
 		g_free (path);
 
-		svSetValueString (ifcfg, "DEVICE", label);
+		svSetValueStr (ifcfg, "DEVICE", label);
 
 		addr = nm_setting_ip_config_get_address (s_ip4, i);
-		svSetValueString (ifcfg, "IPADDR", nm_ip_address_get_address (addr));
+		svSetValueStr (ifcfg, "IPADDR", nm_ip_address_get_address (addr));
 
 		tmp = g_strdup_printf ("%u", nm_ip_address_get_prefix (addr));
-		svSetValueString (ifcfg, "PREFIX", tmp);
+		svSetValueStr (ifcfg, "PREFIX", tmp);
 		g_free (tmp);
 
 		svWriteFile (ifcfg, 0644, NULL);
@@ -2409,16 +2477,13 @@ write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path)
 static gboolean
 write_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error)
 {
-	char **route_items;
-	char *route_contents;
+	nm_auto_free_gstring GString *contents = NULL;
 	NMIPRoute *route;
 	guint32 i, num;
-	gboolean success = FALSE;
 
-	g_return_val_if_fail (filename != NULL, FALSE);
-	g_return_val_if_fail (s_ip6 != NULL, FALSE);
-	g_return_val_if_fail (error != NULL, FALSE);
-	g_return_val_if_fail (*error == NULL, FALSE);
+	g_return_val_if_fail (filename, FALSE);
+	g_return_val_if_fail (s_ip6, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
 
 	num = nm_setting_ip_config_get_num_routes (s_ip6);
 	if (num == 0) {
@@ -2426,38 +2491,39 @@ write_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **erro
 		return TRUE;
 	}
 
-	route_items = g_malloc0 (sizeof (char*) * (num + 1));
+	contents = g_string_new ("");
 	for (i = 0; i < num; i++) {
+		gs_free char *options = NULL;
+
 		route = nm_setting_ip_config_get_route (s_ip6, i);
+		options = get_route_attributes_string (route, AF_INET6);
 
 		if (nm_ip_route_get_metric (route) == -1) {
-			route_items[i] = g_strdup_printf ("%s/%u via %s\n",
+			g_string_append_printf (contents, "%s/%u via %s%s%s",
 			                                  nm_ip_route_get_dest (route),
 			                                  nm_ip_route_get_prefix (route),
-			                                  nm_ip_route_get_next_hop (route));
+			                                  nm_ip_route_get_next_hop (route),
+			                                  options ? " " : "",
+			                                  options ?: "");
 		} else {
-			route_items[i] = g_strdup_printf ("%s/%u via %s metric %u\n",
+			g_string_append_printf (contents, "%s/%u via %s metric %u%s%s",
 			                                  nm_ip_route_get_dest (route),
 			                                  nm_ip_route_get_prefix (route),
 			                                  nm_ip_route_get_next_hop (route),
-			                                  (guint32) nm_ip_route_get_metric (route));
+			                                  (unsigned) nm_ip_route_get_metric (route),
+			                                  options ? " " : "",
+			                                  options ?: "");
 		}
+		g_string_append (contents, "\n");
 	}
-	route_items[num] = NULL;
-	route_contents = g_strjoinv (NULL, route_items);
-	g_strfreev (route_items);
 
-	if (!g_file_set_contents (filename, route_contents, -1, NULL)) {
+	if (!g_file_set_contents (filename, contents->str, -1, NULL)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Writing route6 file '%s' failed", filename);
-		goto error;
+		return FALSE;
 	}
 
-	success = TRUE;
-
-error:
-	g_free (route_contents);
-	return success;
+	return TRUE;
 }
 
 static void
@@ -2466,7 +2532,7 @@ write_ip6_setting_dhcp_hostname (NMSettingIPConfig *s_ip6, shvarFile *ifcfg)
 	const char *hostname;
 
 	hostname = nm_setting_ip_config_get_dhcp_hostname (s_ip6);
-	svSetValueString (ifcfg, "DHCPV6_HOSTNAME", hostname);
+	svSetValueStr (ifcfg, "DHCPV6_HOSTNAME", hostname);
 
 	/* Missing DHCPV6_SEND_HOSTNAME means TRUE, and we prefer not write it
 	 * explicitly in that case, because it is NM-specific variable
@@ -2474,7 +2540,7 @@ write_ip6_setting_dhcp_hostname (NMSettingIPConfig *s_ip6, shvarFile *ifcfg)
 	if (nm_setting_ip_config_get_dhcp_send_hostname (s_ip6))
 		svUnsetValue (ifcfg, "DHCPV6_SEND_HOSTNAME");
 	else
-		svSetValueString (ifcfg, "DHCPV6_SEND_HOSTNAME", "no");
+		svSetValueStr (ifcfg, "DHCPV6_SEND_HOSTNAME", "no");
 }
 
 static gboolean
@@ -2483,11 +2549,9 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	NMSettingIPConfig *s_ip6;
 	NMSettingIPConfig *s_ip4;
 	const char *value;
-	char *addr_key;
 	char *tmp;
-	guint32 i, num, num4;
+	guint i, num, num4;
 	gint priority;
-	GString *searches;
 	NMIPAddress *addr;
 	const char *dns;
 	gint64 route_metric;
@@ -2518,28 +2582,28 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	value = nm_setting_ip_config_get_method (s_ip6);
 	g_assert (value);
 	if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
-		svSetValueString (ifcfg, "IPV6INIT", "no");
+		svSetValueStr (ifcfg, "IPV6INIT", "no");
 		svUnsetValue (ifcfg, "DHCPV6C");
 		return TRUE;
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
-		svSetValueString (ifcfg, "IPV6INIT", "yes");
-		svSetValueString (ifcfg, "IPV6_AUTOCONF", "yes");
+		svSetValueStr (ifcfg, "IPV6INIT", "yes");
+		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "yes");
 		svUnsetValue (ifcfg, "DHCPV6C");
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
-		svSetValueString (ifcfg, "IPV6INIT", "yes");
-		svSetValueString (ifcfg, "IPV6_AUTOCONF", "no");
-		svSetValueString (ifcfg, "DHCPV6C", "yes");
+		svSetValueStr (ifcfg, "IPV6INIT", "yes");
+		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "no");
+		svSetValueStr (ifcfg, "DHCPV6C", "yes");
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
-		svSetValueString (ifcfg, "IPV6INIT", "yes");
-		svSetValueString (ifcfg, "IPV6_AUTOCONF", "no");
+		svSetValueStr (ifcfg, "IPV6INIT", "yes");
+		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "no");
 		svUnsetValue (ifcfg, "DHCPV6C");
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
-		svSetValueString (ifcfg, "IPV6INIT", "yes");
-		svSetValueString (ifcfg, "IPV6_AUTOCONF", "no");
+		svSetValueStr (ifcfg, "IPV6INIT", "yes");
+		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "no");
 		svUnsetValue (ifcfg, "DHCPV6C");
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
-		svSetValueString (ifcfg, "IPV6INIT", "yes");
-		svSetValueString (ifcfg, "IPV6_AUTOCONF", "shared");
+		svSetValueStr (ifcfg, "IPV6INIT", "yes");
+		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "shared");
 		svUnsetValue (ifcfg, "DHCPV6C");
 	}
 
@@ -2563,9 +2627,9 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		                        nm_ip_address_get_address (addr),
 		                        nm_ip_address_get_prefix (addr));
 	}
-	svSetValueString (ifcfg, "IPV6ADDR", ip_str1->str);
-	svSetValueString (ifcfg, "IPV6ADDR_SECONDARIES", ip_str2->str);
-	svSetValueString (ifcfg, "IPV6_DEFAULTGW", nm_setting_ip_config_get_gateway (s_ip6));
+	svSetValueStr (ifcfg, "IPV6ADDR", ip_str1->str);
+	svSetValueStr (ifcfg, "IPV6ADDR_SECONDARIES", ip_str2->str);
+	svSetValueStr (ifcfg, "IPV6_DEFAULTGW", nm_setting_ip_config_get_gateway (s_ip6));
 	g_string_free (ip_str1, TRUE);
 	g_string_free (ip_str2, TRUE);
 
@@ -2574,68 +2638,68 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	num4 = s_ip4 ? nm_setting_ip_config_get_num_dns (s_ip4) : 0; /* from where to start with IPv6 entries */
 	num = nm_setting_ip_config_get_num_dns (s_ip6);
 	for (i = 0; i < 254; i++) {
-		addr_key = g_strdup_printf ("DNS%d", i + num4 + 1);
+		char addr_key[64];
+
+		nm_sprintf_buf (addr_key, "DNS%u", i + num4 + 1);
 
 		if (i >= num)
 			svUnsetValue (ifcfg, addr_key);
 		else {
 			dns = nm_setting_ip_config_get_dns (s_ip6, i);
-			svSetValueString (ifcfg, addr_key, dns);
+			svSetValueStr (ifcfg, addr_key, dns);
 		}
-		g_free (addr_key);
 	}
 
 	/* Write out DNS domains - 'DOMAIN' key is shared for both IPv4 and IPv6 domains */
 	num = nm_setting_ip_config_get_num_dns_searches (s_ip6);
 	if (num > 0) {
-		char *ip4_domains;
-		ip4_domains = svGetValueString (ifcfg, "DOMAIN");
-		searches = g_string_new (ip4_domains);
+		gs_free char *ip4_domains = NULL;
+		nm_auto_free_gstring GString *searches = NULL;
+
+		searches = g_string_new (svGetValueStr (ifcfg, "DOMAIN", &ip4_domains));
 		for (i = 0; i < num; i++) {
 			if (searches->len > 0)
 				g_string_append_c (searches, ' ');
 			g_string_append (searches, nm_setting_ip_config_get_dns_search (s_ip6, i));
 		}
-		svSetValueString (ifcfg, "DOMAIN", searches->str);
-		g_string_free (searches, TRUE);
-		g_free (ip4_domains);
+		svSetValueStr (ifcfg, "DOMAIN", searches->str);
 	}
 
 
 	/* handle IPV6_DEFROUTE */
 	/* IPV6_DEFROUTE has the opposite meaning from 'never-default' */
-	if (nm_setting_ip_config_get_never_default(s_ip6))
-		svSetValueString (ifcfg, "IPV6_DEFROUTE", "no");
+	if (nm_setting_ip_config_get_never_default (s_ip6))
+		svSetValueStr (ifcfg, "IPV6_DEFROUTE", "no");
 	else
-		svSetValueString (ifcfg, "IPV6_DEFROUTE", "yes");
+		svSetValueStr (ifcfg, "IPV6_DEFROUTE", "yes");
 
-	svSetValueString (ifcfg, "IPV6_PEERDNS",
-	                  nm_setting_ip_config_get_ignore_auto_dns (s_ip6) ? "no" : NULL);
+	svSetValueStr (ifcfg, "IPV6_PEERDNS",
+	               nm_setting_ip_config_get_ignore_auto_dns (s_ip6) ? "no" : NULL);
 
-	svSetValueString (ifcfg, "IPV6_PEERROUTES",
-	                  nm_setting_ip_config_get_ignore_auto_routes (s_ip6) ? "no" : NULL);
+	svSetValueStr (ifcfg, "IPV6_PEERROUTES",
+	               nm_setting_ip_config_get_ignore_auto_routes (s_ip6) ? "no" : NULL);
 
-	svSetValueString (ifcfg, "IPV6_FAILURE_FATAL",
-	                  nm_setting_ip_config_get_may_fail (s_ip6) ? "no" : "yes");
+	svSetValueStr (ifcfg, "IPV6_FAILURE_FATAL",
+	               nm_setting_ip_config_get_may_fail (s_ip6) ? "no" : "yes");
 
 	route_metric = nm_setting_ip_config_get_route_metric (s_ip6);
 	tmp = route_metric != -1 ? g_strdup_printf ("%"G_GINT64_FORMAT, route_metric) : NULL;
-	svSetValueString (ifcfg, "IPV6_ROUTE_METRIC", tmp);
+	svSetValueStr (ifcfg, "IPV6_ROUTE_METRIC", tmp);
 	g_free (tmp);
 
 	/* IPv6 Privacy Extensions */
 	svUnsetValue (ifcfg, "IPV6_PRIVACY");
 	svUnsetValue (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP");
-	switch (nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6))){
+	switch (nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6))) {
 	case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
-		svSetValueString (ifcfg, "IPV6_PRIVACY", "no");
+		svSetValueStr (ifcfg, "IPV6_PRIVACY", "no");
 	break;
 	case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR:
-		svSetValueString (ifcfg, "IPV6_PRIVACY", "rfc3041");
-		svSetValueString (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", "yes");
+		svSetValueStr (ifcfg, "IPV6_PRIVACY", "rfc3041");
+		svSetValueStr (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", "yes");
 	break;
 	case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR:
-		svSetValueString (ifcfg, "IPV6_PRIVACY", "rfc3041");
+		svSetValueStr (ifcfg, "IPV6_PRIVACY", "rfc3041");
 	break;
 	default:
 	break;
@@ -2646,7 +2710,7 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	if (addr_gen_mode != NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) {
 		tmp = nm_utils_enum_to_str (nm_setting_ip6_config_addr_gen_mode_get_type (),
 		                            addr_gen_mode);
-		svSetValueString (ifcfg, "IPV6_ADDR_GEN_MODE", tmp);
+		svSetValueStr (ifcfg, "IPV6_ADDR_GEN_MODE", tmp);
 		g_free (tmp);
 	} else {
 		svUnsetValue (ifcfg, "IPV6_ADDR_GEN_MODE");
@@ -2654,7 +2718,7 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 
 	/* IPv6 tokenized interface identifier */
 	value = nm_setting_ip6_config_get_token (NM_SETTING_IP6_CONFIG (s_ip6));
-	svSetValueString (ifcfg, "IPV6_TOKEN", value);
+	svSetValueStr (ifcfg, "IPV6_TOKEN", value);
 
 	priority = nm_setting_ip_config_get_dns_priority (s_ip6);
 	if (priority)
@@ -2667,17 +2731,14 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	if (!route6_path) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Could not get route6 file path for '%s'", svFileGetName (ifcfg));
-		goto error;
+		return FALSE;
 	}
 	write_route6_file (route6_path, s_ip6, error);
 	g_free (route6_path);
 	if (error && *error)
-		goto error;
+		return FALSE;
 
 	return TRUE;
-
-error:
-	return FALSE;
 }
 
 static void
@@ -2762,18 +2823,20 @@ write_connection (NMConnection *connection,
                   const char *ifcfg_dir,
                   const char *filename,
                   char **out_filename,
+                  NMConnection **out_reread,
+                  gboolean *out_reread_same,
                   GError **error)
 {
 	NMSettingConnection *s_con;
-	gboolean success = FALSE;
-	shvarFile *ifcfg = NULL;
-	char *ifcfg_name = NULL;
+	nm_auto_shvar_file_close shvarFile *ifcfg = NULL;
+	gs_free char *ifcfg_name = NULL;
 	const char *type;
 	gboolean no_8021x = FALSE;
 	gboolean wired = FALSE;
 
 	nm_assert (NM_IS_CONNECTION (connection));
-	nm_assert (nm_connection_verify (connection, NULL));
+	nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS);
+	nm_assert (!out_reread || !*out_reread);
 
 	if (!writer_can_write_connection (connection, error))
 		return FALSE;
@@ -2802,13 +2865,12 @@ write_connection (NMConnection *connection,
 		if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS)) {
 			guint32 idx = 0;
 
-			g_free (ifcfg_name);
+			nm_clear_g_free (&ifcfg_name);
 			while (idx++ < 500) {
 				ifcfg_name = g_strdup_printf ("%s/ifcfg-%s-%u", ifcfg_dir, escaped, idx);
 				if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS) == FALSE)
 					break;
-				g_free (ifcfg_name);
-				ifcfg_name = NULL;
+				nm_clear_g_free (&ifcfg_name);
 			}
 		}
 		g_free (escaped);
@@ -2826,7 +2888,7 @@ write_connection (NMConnection *connection,
 	if (!type) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Missing connection type!");
-		goto out;
+		return FALSE;
 	}
 
 	if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) {
@@ -2835,82 +2897,113 @@ write_connection (NMConnection *connection,
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 			             "Can't write connection type '%s'",
 			             NM_SETTING_PPPOE_SETTING_NAME);
-			goto out;
+			return FALSE;
 		}
 
 		if (!write_wired_setting (connection, ifcfg, error))
-			goto out;
+			return FALSE;
 		wired = TRUE;
 	} else if (!strcmp (type, NM_SETTING_VLAN_SETTING_NAME)) {
 		if (!write_vlan_setting (connection, ifcfg, &wired, error))
-			goto out;
+			return FALSE;
 	} else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) {
 		if (!write_wireless_setting (connection, ifcfg, &no_8021x, error))
-			goto out;
+			return FALSE;
 	} else if (!strcmp (type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
 		if (!write_infiniband_setting (connection, ifcfg, error))
-			goto out;
+			return FALSE;
 	} else if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME)) {
 		if (!write_bonding_setting (connection, ifcfg, &wired, error))
-			goto out;
+			return FALSE;
 	} else if (!strcmp (type, NM_SETTING_TEAM_SETTING_NAME)) {
 		if (!write_team_setting (connection, ifcfg, &wired, error))
-			goto out;
+			return FALSE;
 	} else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)) {
 		if (!write_bridge_setting (connection, ifcfg, error))
-			goto out;
+			return FALSE;
 	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Can't write connection type '%s'", type);
-		goto out;
+		return FALSE;
 	}
 
 	if (!no_8021x) {
 		if (!write_8021x_setting (connection, ifcfg, wired, error))
-			goto out;
+			return FALSE;
 	}
 
 	if (!write_bridge_port_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 
 	if (!write_team_port_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 
 	if (!write_dcb_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 
 	if (!write_proxy_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
+
+	if (!write_user_setting (connection, ifcfg, error))
+		return FALSE;
 
 	svUnsetValue (ifcfg, "DHCP_HOSTNAME");
 	svUnsetValue (ifcfg, "DHCP_FQDN");
 
 	if (!write_ip4_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 	write_ip4_aliases (connection, ifcfg_name);
 
 	if (!write_ip6_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 
 	if (!write_res_options (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 
 	write_connection_setting (s_con, ifcfg);
 
 	if (!svWriteFile (ifcfg, 0644, error))
-		goto out;
+		return FALSE;
+
+	if (out_reread || out_reread_same) {
+		gs_unref_object NMConnection *reread = NULL;
+		gs_free_error GError *local = NULL;
+		gs_free char *unhandled = NULL;
+		gboolean reread_same = FALSE;
+
+		reread = connection_from_file (ifcfg_name, &unhandled, &local, NULL);
+
+		if (unhandled) {
+			_LOGW ("failure to re-read the new connection from file \"%s\": %s",
+			       ifcfg_name, "connection is unhandled");
+			g_clear_object (&reread);
+		} else if (!reread) {
+			_LOGW ("failure to re-read the new connection from file \"%s\": %s",
+			       ifcfg_name, local ? local->message : "<unknown>");
+		} else {
+			if (out_reread_same) {
+				if (nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT))
+					reread_same = TRUE;
+
+				nm_assert (reread_same == nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT));
+				nm_assert (reread_same == ({
+				                                gs_unref_hashtable GHashTable *_settings = NULL;
+
+				                                (   nm_connection_diff (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT, &_settings)
+				                                 && !_settings);
+				                           }));
+			}
+		}
+
+		NM_SET_OUT (out_reread, g_steal_pointer (&reread));
+		NM_SET_OUT (out_reread_same, reread_same);
+	}
 
 	/* Only return the filename if this was a newly written ifcfg */
 	if (out_filename && !filename)
-		*out_filename = g_strdup (ifcfg_name);
-
-	success = TRUE;
+		*out_filename = g_steal_pointer (&ifcfg_name);
 
-out:
-	if (ifcfg)
-		svCloseFile (ifcfg);
-	g_free (ifcfg_name);
-	return success;
+	return TRUE;
 }
 
 gboolean
@@ -2942,15 +3035,19 @@ gboolean
 writer_new_connection (NMConnection *connection,
                        const char *ifcfg_dir,
                        char **out_filename,
+                       NMConnection **out_reread,
+                       gboolean *out_reread_same,
                        GError **error)
 {
-	return write_connection (connection, ifcfg_dir, NULL, out_filename, error);
+	return write_connection (connection, ifcfg_dir, NULL, out_filename, out_reread, out_reread_same, error);
 }
 
 gboolean
 writer_update_connection (NMConnection *connection,
                           const char *ifcfg_dir,
                           const char *filename,
+                          NMConnection **out_reread,
+                          gboolean *out_reread_same,
                           GError **error)
 {
 	if (utils_has_complex_routes (filename)) {
@@ -2959,6 +3056,6 @@ writer_update_connection (NMConnection *connection,
 		return FALSE;
 	}
 
-	return write_connection (connection, ifcfg_dir, filename, NULL, error);
+	return write_connection (connection, ifcfg_dir, filename, NULL, out_reread, out_reread_same, error);
 }
 
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h
index 2662f79b..9cd9513e 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h
@@ -29,11 +29,15 @@ gboolean writer_can_write_connection (NMConnection *connection,
 gboolean writer_new_connection (NMConnection *connection,
                                 const char *ifcfg_dir,
                                 char **out_filename,
+                                NMConnection **out_reread,
+                                gboolean *out_reread_same,
                                 GError **error);
 
 gboolean writer_update_connection (NMConnection *connection,
                                    const char *ifcfg_dir,
                                    const char *filename,
+                                   NMConnection **out_reread,
+                                   gboolean *out_reread_same,
                                    GError **error);
 
 #endif /* _WRITER_H_ */
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 65bcc0d9..9fce5aa1 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -41,7 +41,7 @@
 
 /*****************************************************************************/
 
-typedef struct {
+struct _shvarLine {
 	/* There are three cases:
 	 *
 	 * 1) the line is not a valid variable assignment (that is, it doesn't
@@ -61,7 +61,9 @@ typedef struct {
 	char *line;
 	const char *key;
 	char *key_with_prefix;
-} shvarLine;
+};
+
+typedef struct _shvarLine shvarLine;
 
 struct _shvarFile {
 	char      *fileName;
@@ -220,14 +222,12 @@ svEscape (const char *s, char **to_free)
 	int newlen;
 	size_t i, j, slen;
 
-	slen = strlen (s);
-
-	for (i = 0; i < slen; i++) {
-		if (_char_req_escape (s[i]))
+	for (slen = 0; s[slen]; slen++) {
+		if (_char_req_escape (s[slen]))
 			mangle++;
-		else if (_char_req_quotes (s[i]))
+		else if (_char_req_quotes (s[slen]))
 			requires_quotes = TRUE;
-		else if (s[i] < ' ') {
+		else if (s[slen] < ' ') {
 			/* if the string contains newline we can only express it using ANSI C quotation
 			 * (as we don't support line continuation).
 			 * Additionally, ANSI control characters look odd with regular quotation, so handle
@@ -639,15 +639,19 @@ svFileGetName (const shvarFile *s)
 }
 
 void
-svFileSetName (shvarFile *s, const char *fileName)
+svFileSetName_test_only (shvarFile *s, const char *fileName)
 {
+	/* changing the file name is not supported for regular
+	 * operation. Only allowed to use in tests, othewise,
+	 * the filename is immutable. */
 	g_free (s->fileName);
 	s->fileName = g_strdup (fileName);
 }
 
 void
-svFileSetModified (shvarFile *s)
+svFileSetModified_test_only (shvarFile *s)
 {
+	/* marking a file as modified is only for testing. */
 	s->modified = TRUE;
 }
 
@@ -877,6 +881,30 @@ shlist_find (const GList *current, const char *key)
 
 /*****************************************************************************/
 
+GHashTable *
+svGetKeys (shvarFile *s)
+{
+	GHashTable *keys = NULL;
+	const GList *current;
+	const shvarLine *line;
+
+	nm_assert (s);
+
+	for (current = s->lineList; current; current = current->next) {
+		line = current->data;
+		if (line->key && line->line) {
+			/* we don't clone the keys. The keys are only valid
+			 * until @s gets modified. */
+			if (!keys)
+				keys = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+			g_hash_table_add (keys, (gpointer) line->key);
+		}
+	}
+	return keys;
+}
+
+/*****************************************************************************/
+
 static const char *
 _svGetValue (shvarFile *s, const char *key, char **to_free)
 {
@@ -895,39 +923,104 @@ _svGetValue (shvarFile *s, const char *key, char **to_free)
 	}
 	if (last) {
 		line = last->data;
-		if (line->line)
-			return svUnescape (line->line, to_free);
+		if (line->line) {
+			const char *v;
+
+			v = svUnescape (line->line, to_free);
+			if (!v) {
+				/* a wrongly quoted value is treated like the empty string.
+				 * See also svWriteFile(), which handles unparsable values
+				 * that way. */
+				nm_assert (!*to_free);
+				return "";
+			}
+			return v;
+		}
 	}
 	*to_free = NULL;
 	return NULL;
 }
 
+/* Returns the value for key. The value is either owned by @s
+ * or returned as to_free. This aims to avoid cloning the string.
+ *
+ * - like svGetValue_cp(), but avoids cloning the value if possible.
+ * - like svGetValueStr(), but does not ignore empty string values.
+ */
 const char *
 svGetValue (shvarFile *s, const char *key, char **to_free)
 {
-	g_return_val_if_fail (s != NULL, NULL);
-	g_return_val_if_fail (key != NULL, NULL);
+	g_return_val_if_fail (s, NULL);
+	g_return_val_if_fail (key, NULL);
 	g_return_val_if_fail (to_free, NULL);
 
 	return _svGetValue (s, key, to_free);
 }
 
-/* Get the value associated with the key, and leave the current pointer
- * pointing at the line containing the value.  The char* returned MUST
- * be freed by the caller.
+/* Returns the value for key. The value is either owned by @s
+ * or returned as to_free. This aims to avoid cloning the string.
+ *
+ * - like svGetValue(), but does not return an empty string.
+ * - like svGetValueStr_cp(), but avoids cloning the value if possible.
+ */
+const char *
+svGetValueStr (shvarFile *s, const char *key, char **to_free)
+{
+	const char *value;
+
+	g_return_val_if_fail (s, NULL);
+	g_return_val_if_fail (key, NULL);
+	g_return_val_if_fail (to_free, NULL);
+
+	value = _svGetValue (s, key, to_free);
+	if (!value || !value[0]) {
+		nm_assert (!*to_free);
+		return NULL;
+	}
+	return value;
+}
+
+/* Returns the value for key. The returned value must be freed
+ * by the caller.
+ *
+ * - like svGetValue(), but always returns a copy of the value.
+ * - like svGetValueStr_cp(), but does not ignore an empty string.
  */
 char *
-svGetValueString (shvarFile *s, const char *key)
+svGetValue_cp (shvarFile *s, const char *key)
 {
 	char *to_free;
 	const char *value;
 
+	g_return_val_if_fail (s, NULL);
+	g_return_val_if_fail (key, NULL);
+
 	value = _svGetValue (s, key, &to_free);
 	if (!value) {
 		nm_assert (!to_free);
 		return NULL;
 	}
-	if (!value[0]) {
+	return to_free ?: g_strdup (value);
+}
+
+/* Returns the value for key. The returned value must be freed
+ * by the caller.
+ * If the key is unset or the value an empty string, NULL is returned.
+ *
+ * - like svGetValueStr(), but always returns a copy of the value.
+ * - like svGetValue_cp(), but returns NULL instead of an empty string.
+ */
+char *
+svGetValueStr_cp (shvarFile *s, const char *key)
+{
+	char *to_free;
+	const char *value;
+
+	g_return_val_if_fail (s, NULL);
+	g_return_val_if_fail (key, NULL);
+
+	value = _svGetValue (s, key, &to_free);
+	if (!value || !value[0]) {
 		nm_assert (!to_free);
 		return NULL;
 	}
@@ -991,8 +1084,8 @@ svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 m
 
 /*****************************************************************************/
 
-/* Same as svSetValueString() but it preserves empty @value -- contrary to
- * svSetValueString() for which "" effectively means to remove the value. */
+/* Same as svSetValueStr() but it preserves empty @value -- contrary to
+ * svSetValueStr() for which "" effectively means to remove the value. */
 void
 svSetValue (shvarFile *s, const char *key, const char *value)
 {
@@ -1041,7 +1134,7 @@ svSetValue (shvarFile *s, const char *key, const char *value)
  * to the bottom of the file.
  */
 void
-svSetValueString (shvarFile *s, const char *key, const char *value)
+svSetValueStr (shvarFile *s, const char *key, const char *value)
 {
 	svSetValue (s, key, value && value[0] ? value : NULL);
 }
@@ -1066,6 +1159,27 @@ svUnsetValue (shvarFile *s, const char *key)
 	svSetValue (s, key, NULL);
 }
 
+void
+svUnsetValuesWithPrefix (shvarFile *s, const char *prefix)
+{
+	GList *current;
+
+	g_return_if_fail (s);
+	g_return_if_fail (prefix);
+
+	for (current = s->lineList; current; current = current->next) {
+		shvarLine *line = current->data;
+
+		ASSERT_shvarLine (line);
+		if (   line->key
+		    && g_str_has_prefix (line->key, prefix)) {
+			if (nm_clear_g_free (&line->line))
+				s->modified = TRUE;
+		}
+		ASSERT_shvarLine (line);
+	}
+}
+
 /*****************************************************************************/
 
 /* Write the current contents iff modified.  Returns FALSE on error
diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h
index a6498fce..9d8c2364 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/settings/plugins/ifcfg-rh/shvar.h
@@ -34,9 +34,9 @@
 typedef struct _shvarFile shvarFile;
 
 const char *svFileGetName (const shvarFile *s);
-void svFileSetName (shvarFile *s, const char *fileName);
 
-void svFileSetModified (shvarFile *s);
+void svFileSetName_test_only (shvarFile *s, const char *fileName);
+void svFileSetModified_test_only (shvarFile *s);
 
 /* Create the file <name>, return a shvarFile (never fails) */
 shvarFile *svCreateFile (const char *name);
@@ -49,10 +49,15 @@ shvarFile *svOpenFile (const char *name, GError **error);
  * be freed by the caller.
  */
 const char *svGetValue (shvarFile *s, const char *key, char **to_free);
-char *svGetValueString (shvarFile *s, const char *key);
+char *svGetValue_cp (shvarFile *s, const char *key);
+
+const char *svGetValueStr (shvarFile *s, const char *key, char **to_free);
+char *svGetValueStr_cp (shvarFile *s, const char *key);
 
 gint svParseBoolean (const char *value, gint def);
 
+GHashTable *svGetKeys (shvarFile *s);
+
 /* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true")
  * return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false")
  * return <def> otherwise
@@ -66,13 +71,15 @@ gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, g
  * the key=value pair after that line.  Otherwise, prepend the pair
  * to the top of the file.
  */
-void svSetValueString (shvarFile *s, const char *key, const char *value);
 void svSetValue (shvarFile *s, const char *key, const char *value);
+void svSetValueStr (shvarFile *s, const char *key, const char *value);
 void svSetValueBoolean (shvarFile *s, const char *key, gboolean value);
 void svSetValueInt64 (shvarFile *s, const char *key, gint64 value);
 
 void svUnsetValue (shvarFile *s, const char *key);
 
+void svUnsetValuesWithPrefix (shvarFile *s, const char *prefix);
+
 /* Write the current contents iff modified.  Returns FALSE on error
  * and TRUE on success.  Do not write if no values have been modified.
  * The mode argument is only used if creating the file, not if
@@ -87,4 +94,16 @@ void svCloseFile (shvarFile *s);
 const char *svEscape (const char *s, char **to_free);
 const char *svUnescape (const char *s, char **to_free);
 
+static inline void
+_nm_auto_shvar_file_close (shvarFile **p_s)
+{
+	if (*p_s) {
+		int errsv = errno;
+
+		svCloseFile (*p_s);
+		errno = errsv;
+	}
+}
+#define nm_auto_shvar_file_close nm_auto(_nm_auto_shvar_file_close)
+
 #endif /* _SHVAR_H */
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected
new file mode 100644
index 00000000..d81d9187
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected
@@ -0,0 +1,8 @@
+HWADDR=00:22:15:59:62:97
+TYPE=Ethernet
+BRIDGING_OPTS="priority=28 hairpin_mode=1"
+NAME="System test-bridge-component"
+UUID=${UUID}
+DEVICE=eth0
+ONBOOT=no
+BRIDGE=br0
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
new file mode 100644
index 00000000..d4785ff5
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
@@ -0,0 +1,17 @@
+HWADDR=00:22:15:59:62:97
+TYPE=Ethernet
+BRIDGING_OPTS="priority=28 hairpin_mode=1"
+NAME="System test-bridge-component"
+UUID=${UUID}
+DEVICE=eth0
+ONBOOT=no
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected
new file mode 100644
index 00000000..59a6f79b
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected
@@ -0,0 +1,22 @@
+HWADDR=00:11:22:33:44:EE
+TYPE=Ethernet
+KEY_MGMT=IEEE8021X
+IEEE_8021X_EAP_METHODS=PEAP
+IEEE_8021X_IDENTITY="Jara Cimrman"
+IEEE_8021X_PASSWORD_FLAGS="user ask"
+IEEE_8021X_PEAP_VERSION=1
+IEEE_8021X_INNER_AUTH_METHODS=GTC
+IEEE_8021X_SUBJECT_MATCH=server1.yourdomain.tld
+IEEE_8021X_PHASE2_SUBJECT_MATCH=server2.yourdomain.tld
+IEEE_8021X_ALTSUBJECT_MATCHES="a.yourdomain.tld b.yourdomain.tld c.yourdomain.tld"
+IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES="x.yourdomain.tld y.yourdomain.tld"
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="System test-wired-802-1X-subj-matches"
+UUID=${UUID}
+DEVICE=eth0
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected
new file mode 100644
index 00000000..a48be78a
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected
@@ -0,0 +1,34 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+NM_USER__M_Y___053=val=MY.+
+NM_USER__M_Y___055=val=MY.-
+NM_USER__M_Y___057=val=MY./
+NM_USER__M_Y__8_053_V=val=MY.8+V
+NM_USER__M_Y__8_055_V=val=MY.8-V
+NM_USER__M_Y__8_057_V=val=MY.8/V
+NM_USER__M_Y__8_075_V=val=MY.8=V
+NM_USER__M_Y__8_V=val=MY.8V
+NM_USER__M_Y__8_137_V=val=MY.8_V
+NM_USER__M_Y___075=val=MY.=
+NM_USER__M_Y___A_V=val=MY.AV
+NM_USER__M_Y___137=val=MY._
+NM_USER_MY___AV=val=my.Av
+NM_USER_MY___137V=val=my._v
+NM_USER_MY__KEYS__1=val=my.keys.1
+NM_USER_MY__OTHER___K_E_Y__42=val=my.other.KEY.42
+NM_USER_MY__V_053=val=my.v+
+NM_USER_MY__V_137_137AL3=val=my.v__al3
+NM_USER_MY__VAL1=
+NM_USER_MY__VAL2=val=my.val2
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test User 1"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected
new file mode 100644
index 00000000..3956003d
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected
@@ -0,0 +1,16 @@
+DEVICE=bond0
+BONDING_OPTS=mode=balance-rr
+TYPE=Bond
+BONDING_MASTER=yes
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=none
+IPADDR=1.1.1.3
+PREFIX=24
+GATEWAY=1.1.1.1
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write Bond Main"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected
new file mode 100644
index 00000000..c478db38
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected
@@ -0,0 +1,8 @@
+HWADDR=31:33:33:37:BE:CD
+MTU=1492
+TYPE=Ethernet
+BRIDGING_OPTS="priority=50 path_cost=33"
+NAME="Test Write Bridge Component"
+UUID=${UUID}
+ONBOOT=yes
+BRIDGE=br0
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected
new file mode 100644
index 00000000..80e96921
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected
@@ -0,0 +1,11 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write Permissions"
+UUID=${UUID}
+ONBOOT=yes
+USERS="blahblah foobar asdfasdf"
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected
new file mode 100644
index 00000000..716eedc2
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=auto
+PAC_URL=https://wpad.neverland.org/wpad.dat
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write Proxy Basic"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected
new file mode 100644
index 00000000..0b1deb80
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected
@@ -0,0 +1,6 @@
+TEAM_PORT_CONFIG="{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }"
+NAME="Test Write Team Port"
+UUID=${UUID}
+ONBOOT=yes
+TEAM_MASTER=team0
+DEVICETYPE=TeamPort
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected
new file mode 100644
index 00000000..339f8107
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected
@@ -0,0 +1,20 @@
+VLAN=yes
+TYPE=Vlan
+PHYSDEV=eth0
+VLAN_ID=444
+REORDER_HDR=yes
+GVRP=no
+MVRP=no
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write VLAN reorder_hdr"
+UUID=${UUID}
+ONBOOT=no
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected
new file mode 100644
index 00000000..a95a58db
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected
@@ -0,0 +1,18 @@
+ESSID="Test SSID"
+MODE=Managed
+BAND=a
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi Band A"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected
new file mode 100644
index 00000000..026993b8
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected
@@ -0,0 +1,18 @@
+ESSID="Test SSID"
+MODE=Managed
+SSID_HIDDEN=yes
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi Hidden"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected
new file mode 100644
index 00000000..f3704f10
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected
@@ -0,0 +1,18 @@
+MACADDR=random
+ESSID="Test SSID"
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=always
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi MAC always"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected
new file mode 100644
index 00000000..005c6179
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected
@@ -0,0 +1,17 @@
+ESSID="Test SSID"
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi MAC default"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected
new file mode 100644
index 00000000..dff17ef2
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected
@@ -0,0 +1,18 @@
+MACADDR=permanent
+ESSID="Test SSID"
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=never
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi MAC missing"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected
new file mode 100644
index 00000000..94274cf9
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected
@@ -0,0 +1,18 @@
+MACADDR=permanent
+ESSID="Test SSID"
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=never
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi MAC never"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected
new file mode 100644
index 00000000..d3db19b2
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected
@@ -0,0 +1,16 @@
+ESSID=blahblah
+MODE=Managed
+KEY_MGMT=IEEE8021X
+SECURITYMODE=leap
+IEEE_8021X_IDENTITY="Bill Smith"
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write Wifi LEAP"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected
new file mode 100644
index 00000000..32db7262
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected
@@ -0,0 +1,15 @@
+ESSID=blahblah104
+MODE=Managed
+SECURITYMODE=open
+DEFAULTKEY=1
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write Wifi WEP 104 ASCII"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected
new file mode 100644
index 00000000..8f421cfb
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected
@@ -0,0 +1,15 @@
+ETHTOOL_OPTS="autoneg off speed 10 duplex half"
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write Wired Auto-Negotiate"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected
new file mode 100644
index 00000000..398a3017
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected
@@ -0,0 +1,15 @@
+ETHTOOL_OPTS="wol umgs sopass 00:00:00:11:22:33"
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write Wired Wake-on-LAN"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected
new file mode 100644
index 00000000..60091b7b
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected
@@ -0,0 +1,22 @@
+VLAN=yes
+TYPE=Vlan
+DEVICE=vlan43
+PHYSDEV=eth9
+VLAN_ID=43
+REORDER_HDR=yes
+GVRP=yes
+VLAN_FLAGS=LOOSE_BINDING
+MVRP=no
+VLAN_INGRESS_PRIORITY_MAP=0:1,2:5
+VLAN_EGRESS_PRIORITY_MAP=3:1,12:3,14:7
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=none
+IPADDR=192.168.43.149
+PREFIX=24
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Vlan test-vlan-interface"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3
new file mode 100644
index 00000000..b7bdd781
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3
@@ -0,0 +1,11 @@
+TYPE=Ethernet
+DEVICE=aliasem0
+HWADDR=00:11:22:33:44:55
+BOOTPROTO=none
+ONBOOT=yes
+DNS1=4.2.2.1
+DNS2=4.2.2.2
+IPADDR=192.168.1.5
+PREFIX=24
+NETMASK=255.255.255.0
+IPV6INIT=no
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3:1 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3:1
new file mode 100644
index 00000000..5e151875
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3:1
@@ -0,0 +1,4 @@
+DEVICE=aliasem3:1
+IPADDR=192.168.1.6
+DEFROUTE=yes
+GATEWAY=192.168.1.1
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected
new file mode 100644
index 00000000..56e233cc
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected
@@ -0,0 +1,41 @@
+TYPE=Ethernet
+DCB=yes
+DCB_APP_FCOE_ENABLE=yes
+DCB_APP_FCOE_ADVERTISE=yes
+DCB_APP_FCOE_WILLING=yes
+DCB_APP_FCOE_PRIORITY=5
+DCB_APP_FCOE_MODE=fabric
+DCB_APP_ISCSI_ENABLE=yes
+DCB_APP_ISCSI_ADVERTISE=yes
+DCB_APP_ISCSI_WILLING=yes
+DCB_APP_ISCSI_PRIORITY=1
+DCB_APP_FIP_ENABLE=yes
+DCB_APP_FIP_ADVERTISE=yes
+DCB_APP_FIP_WILLING=yes
+DCB_APP_FIP_PRIORITY=3
+DCB_PFC_ENABLE=yes
+DCB_PFC_ADVERTISE=yes
+DCB_PFC_WILLING=yes
+DCB_PFC_UP=11010110
+DCB_PG_ENABLE=yes
+DCB_PG_ADVERTISE=yes
+DCB_PG_WILLING=yes
+DCB_PG_ID=4f6f173f
+DCB_PG_PCT=10,20,15,10,2,3,35,5
+DCB_PG_UPPCT=10,20,30,40,50,10,0,25
+DCB_PG_STRICT=10110001
+DCB_PG_UP2TC=34721056
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=dcb-test
+UUID=${UUID}
+DEVICE=eth0
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected
new file mode 100644
index 00000000..ead3a047
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected
@@ -0,0 +1,13 @@
+ESSID=blahblah
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="random wifi connection"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected
new file mode 100644
index 00000000..7bc1ae6c
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected
@@ -0,0 +1,15 @@
+ESSID=SomeSSID
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="random wifi connection 2"
+UUID=${UUID}
+ONBOOT=yes
+USERS=superman
+DEFAULTKEY=1
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected
new file mode 100644
index 00000000..367d0ddb
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected
@@ -0,0 +1,12 @@
+VLAN=yes
+DEVICE=enp31s0f1-142
+PHYSDEV=enp31s0f1
+VLAN_ID=142
+REORDER_HDR=yes
+GVRP=no
+MVRP=no
+NAME=team-slave-enp31s0f1-142
+UUID=74f435bb-ede4-415a-9d48-f580b60eba04
+ONBOOT=no
+TEAM_MASTER=team142
+DEVICETYPE=TeamPort
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-eth-type b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-eth-type
new file mode 100644
index 00000000..8d295c76
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-eth-type
@@ -0,0 +1,7 @@
+DEVICE=bond0
+NM_CONTROLLED=yes
+TYPE=Ethernet
+BONDING_OPTS="miimon=213 mode=4 lacp_rate=1"
+BONDING_MASTER=yes
+ONBOOT=yes
+BOOTPROTO=none
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected
new file mode 100644
index 00000000..a28c5c1c
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected
@@ -0,0 +1,12 @@
+HWADDR=00:16:41:11:22:33
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME=test-static-routes-legacy
+UUID=ba60d05a-7898-820d-c2db-427a88f8f2a5
+DEVICE=eth0
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes
index ee2a32d8..10a63b67 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes
@@ -6,3 +6,4 @@ ADDRESS1=44.55.66.77
 NETMASK1=255.255.255.255
 GATEWAY1=192.168.1.7
 METRIC1=3
+OPTIONS1="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 window 30000 initcwnd lock 13 initrwnd 14"
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy
index cb7d42bd..3f02032a 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy
@@ -3,5 +3,5 @@
 
 21.31.41.0/24 via 9.9.9.9 metric 1
 	via 8.8.8.8 to 32.42.52.62
- 43.53.0.0/16 metric 3 via 7.7.7.7 dev eth2
+ 43.53.0.0/16 metric 3 via 7.7.7.7 dev eth2 cwnd 14 mtu lock 9000  initrwnd 20 window lock 10000 initcwnd lock 42 src 1.2.3.4
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual
index b3259ab7..8bdf0acf 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual
@@ -5,3 +5,5 @@ default via dead::beaf
 
 # routes without "via" are valid
 abbe::cafe/64 metric 777
+
+aaaa::cccc/64 from 1111::2222/48 via 3333::4444 src 5555::6666 mtu lock 1450 cwnd 13
diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index f467d864..babb068d 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -33,6 +33,7 @@
 #include "nm-utils.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-wired.h"
+#include "nm-setting-user.h"
 #include "nm-setting-wireless.h"
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-ip4-config.h"
@@ -81,27 +82,170 @@
 		shvarFile *const _f = (f); \
 		const char *const _key = (key); \
 		\
-		_val_string = svGetValueString (_f, _key); \
+		_val_string = svGetValueStr_cp (_f, _key); \
 		_val = svGetValue (_f, _key, &_to_free); \
 		g_assert_cmpstr (_val, ==, (expected_value)); \
 		g_assert (   (!_val_string && (!_val || !_val[0])) \
 		          || ( _val_string && nm_streq0 (_val, _val_string))); \
 	} G_STMT_END
 
-#define _writer_update_connection(connection, ifcfg_dir, filename) \
+static void
+_assert_reread_same (NMConnection *connection, NMConnection *reread)
+{
+	nmtst_assert_connection_verifies_without_normalization (reread);
+	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
+}
+
+static void
+_assert_reread_same_FIXME (NMConnection *connection, NMConnection *reread)
+{
+	gs_unref_object NMConnection *connection_normalized = NULL;
+	gs_unref_hashtable GHashTable *settings = NULL;
+
+	/* FIXME: these assertion failures should not happen as we expect
+	 * that re-reading a connection after write yields the same result.
+	 *
+	 * Needs investation and fixing. */
+	nmtst_assert_connection_verifies_without_normalization (reread);
+
+	connection_normalized = nmtst_connection_duplicate_and_normalize (connection);
+
+	g_assert (!nm_connection_compare (connection_normalized, reread, NM_SETTING_COMPARE_FLAG_EXACT));
+	g_assert (!nm_connection_diff (connection_normalized, reread, NM_SETTING_COMPARE_FLAG_EXACT, &settings));
+}
+
+/* dummy path for an "expected" file, meaning: don't check for expected
+ * written ifcfg file. */
+static const char const NO_EXPECTED[1];
+
+static void
+_assert_expected_content (NMConnection *connection, const char *filename, const char *expected)
+{
+	gs_free char *content_expectd = NULL;
+	gs_free char *content_written = NULL;
+	GError *error = NULL;
+	gsize len_expectd = 0;
+	gsize len_written = 0;
+	gboolean success;
+	const char *uuid = NULL;
+
+	g_assert (NM_IS_CONNECTION (connection));
+	g_assert (filename);
+	g_assert (g_file_test (filename, G_FILE_TEST_EXISTS));
+
+	g_assert (expected);
+	if (expected == NO_EXPECTED)
+		return;
+
+	success = g_file_get_contents (filename, &content_written, &len_written, &error);
+	nmtst_assert_success (success, error);
+
+	success = g_file_get_contents (expected, &content_expectd, &len_expectd, &error);
+	nmtst_assert_success (success, error);
+
+	{
+		gsize i, j;
+
+		for (i = 0; i < len_expectd; ) {
+			if (content_expectd[i] != '$') {
+				i++;
+				continue;
+			}
+			if (g_str_has_prefix (&content_expectd[i], "${UUID}")) {
+				GString *str;
+
+				if (!uuid) {
+					uuid = nm_connection_get_uuid (connection);
+					g_assert (uuid);
+				}
+
+				j = strlen (uuid);
+
+				str = g_string_new_len (content_expectd, len_expectd);
+				g_string_erase (str, i, NM_STRLEN ("${UUID}"));
+				g_string_insert_len (str, i, uuid, j);
+
+				g_free (content_expectd);
+				len_expectd = str->len;
+				content_expectd = g_string_free (str, FALSE);
+				i += j;
+				continue;
+			}
+
+			/* other '$' is not supported. If need be, support escaping of
+			 * '$' via '$$'. */
+			g_assert_not_reached ();
+		}
+	}
+
+	if (   len_expectd != len_written
+	    || memcmp (content_expectd, content_written, len_expectd) != 0) {
+		if (g_getenv ("NMTST_IFCFG_RH_UPDATE_EXPECTED")) {
+			if (uuid) {
+				gs_free char *search = g_strdup_printf ("UUID=%s\n", uuid);
+				const char *s;
+				gsize i;
+				GString *str;
+
+				s = content_written;
+				while (TRUE) {
+					s = strstr (s, search);
+					g_assert (s);
+					if (   s == content_written
+					    || s[-1] == '\n')
+						break;
+					s += strlen (search);
+				}
+
+				i = s - content_written;
+
+				str = g_string_new_len (content_written, len_written);
+				g_string_erase (str, i, strlen (search));
+				g_string_insert (str, i, "UUID=${UUID}\n");
+
+				len_written = str->len;
+				content_written = g_string_free (str, FALSE);
+			}
+			success = g_file_set_contents (expected, content_written, len_written, &error);
+			nmtst_assert_success (success, error);
+		} else {
+			g_error ("The content of \"%s\" (%zu) differs from \"%s\" (%zu). Set NMTST_IFCFG_RH_UPDATE_EXPECTED=yes to update the files inplace\n\n>>>%s<<<\n\n>>>%s<<<\n",
+			         filename, len_written,
+			         expected, len_expectd,
+			         content_written,
+			         content_expectd);
+		}
+	}
+}
+
+#define _writer_update_connection_reread(connection, ifcfg_dir, filename, expected, out_reread, out_reread_same) \
 	G_STMT_START { \
-		NMConnection *_connection = (connection); \
+		gs_unref_object NMConnection *_connection = nmtst_connection_duplicate_and_normalize (connection); \
+		NMConnection **_out_reread = (out_reread); \
+		gboolean *_out_reread_same = (out_reread_same); \
 		const char *_ifcfg_dir = (ifcfg_dir); \
 		const char *_filename = (filename); \
+		const char *_expected = (expected); \
 		GError *_error = NULL; \
 		gboolean _success; \
 		\
-		g_assert (NM_IS_CONNECTION (connection)); \
 		g_assert (_ifcfg_dir && _ifcfg_dir[0]); \
 		g_assert (_filename && _filename[0]); \
 		\
-		_success = writer_update_connection (_connection, _ifcfg_dir, _filename, &_error); \
+		_success = writer_update_connection (_connection, _ifcfg_dir, _filename, _out_reread, _out_reread_same, &_error); \
 		nmtst_assert_success (_success, _error); \
+		_assert_expected_content (_connection, _filename, _expected); \
+	} G_STMT_END
+
+#define _writer_update_connection(connection, ifcfg_dir, filename, expected) \
+	G_STMT_START { \
+		gs_unref_object NMConnection *_reread = NULL; \
+		NMConnection *_c = (connection); \
+		gboolean _reread_same = FALSE; \
+		\
+		_writer_update_connection_reread (_c, ifcfg_dir, filename, expected, &_reread, &_reread_same); \
+		_assert_reread_same (_c, _reread); \
+		g_assert (_reread_same); \
 	} G_STMT_END
 
 static NMConnection *
@@ -147,14 +291,19 @@ _connection_from_file_fail (const char *filename,
 }
 
 static void
-_writer_new_connection (NMConnection *connection,
-                        const char *ifcfg_dir,
-                        char **out_filename)
+_writer_new_connection_reread (NMConnection *connection,
+                               const char *ifcfg_dir,
+                               char **out_filename,
+                               const char *expected,
+                               NMConnection **out_reread,
+                               gboolean *out_reread_same)
 {
 	gboolean success;
 	GError *error = NULL;
 	char *filename = NULL;
 	gs_unref_object NMConnection *con_verified = NULL;
+	gs_unref_object NMConnection *reread_copy = NULL;
+	NMConnection **reread = out_reread ?: ((nmtst_get_rand_int () % 2) ? &reread_copy : NULL);
 
 	g_assert (NM_IS_CONNECTION (connection));
 	g_assert (ifcfg_dir);
@@ -164,14 +313,59 @@ _writer_new_connection (NMConnection *connection,
 	success = writer_new_connection (con_verified,
 	                                 ifcfg_dir,
 	                                 &filename,
+	                                 reread,
+	                                 out_reread_same,
 	                                 &error);
 	nmtst_assert_success (success, error);
 	g_assert (filename && filename[0]);
 
+	if (reread)
+		nmtst_assert_connection_verifies_without_normalization (*reread);
+
+	_assert_expected_content (con_verified, filename, expected);
+
 	if (out_filename)
 		*out_filename = filename;
 	else
 		g_free (filename);
+
+}
+
+static void
+_writer_new_connec_exp (NMConnection *connection,
+                        const char *ifcfg_dir,
+                        const char *expected,
+                        char **out_filename)
+{
+	gs_unref_object NMConnection *reread = NULL;
+	gboolean reread_same = FALSE;
+
+	_writer_new_connection_reread (connection, ifcfg_dir, out_filename, expected, &reread, &reread_same);
+	_assert_reread_same (connection, reread);
+	g_assert (reread_same);
+}
+
+static void
+_writer_new_connection (NMConnection *connection,
+                        const char *ifcfg_dir,
+                        char **out_filename)
+{
+	_writer_new_connec_exp (connection, ifcfg_dir, NO_EXPECTED, out_filename);
+}
+
+static void
+_writer_new_connection_FIXME (NMConnection *connection,
+                              const char *ifcfg_dir,
+                              char **out_filename)
+{
+	gs_unref_object NMConnection *reread = NULL;
+	gboolean reread_same = FALSE;
+
+	/* FIXME: this should not happen. Fix it to use _writer_new_connection() instead. */
+
+	_writer_new_connection_reread (connection, ifcfg_dir, out_filename, NO_EXPECTED, &reread, &reread_same);
+	_assert_reread_same_FIXME (connection, reread);
+	g_assert (!reread_same);
 }
 
 static void
@@ -179,6 +373,8 @@ _writer_new_connection_fail (NMConnection *connection,
                              const char *ifcfg_dir,
                              GError **error)
 {
+	gs_unref_object NMConnection *connection_normalized = NULL;
+	gs_unref_object NMConnection *reread = NULL;
 	gboolean success;
 	GError *local = NULL;
 	char *filename = NULL;
@@ -186,12 +382,17 @@ _writer_new_connection_fail (NMConnection *connection,
 	g_assert (NM_IS_CONNECTION (connection));
 	g_assert (ifcfg_dir);
 
-	success = writer_new_connection (connection,
+	connection_normalized = nmtst_connection_duplicate_and_normalize (connection);
+
+	success = writer_new_connection (connection_normalized,
 	                                 ifcfg_dir,
 	                                 &filename,
+	                                 &reread,
+	                                 NULL,
 	                                 &local);
 	nmtst_assert_no_success (success, local);
 	g_assert (!filename);
+	g_assert (!reread);
 
 	g_propagate_error (error, local);
 }
@@ -897,6 +1098,62 @@ test_read_wired_obsolete_gateway_n (void)
 }
 
 static void
+test_user_1 (void)
+{
+	nmtst_auto_unlinkfile char *testfile = NULL;
+	gs_unref_object NMConnection *connection = NULL;
+	gs_unref_object NMConnection *reread = NULL;
+	NMSettingUser *s_user;
+
+	connection = nmtst_create_minimal_connection ("Test User 1", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
+	s_user = NM_SETTING_USER (nm_setting_user_new ());
+
+#define _USER_SET_DATA(s_user, key, val) \
+	G_STMT_START { \
+		GError *_error = NULL; \
+		gboolean _success; \
+		\
+		_success = nm_setting_user_set_data ((s_user), (key), (val), &_error); \
+		nmtst_assert_success (_success, _error); \
+	} G_STMT_END
+
+#define _USER_SET_DATA_X(s_user, key) \
+	_USER_SET_DATA (s_user, key, "val="key"")
+
+	_USER_SET_DATA (s_user, "my.val1", "");
+	_USER_SET_DATA_X (s_user, "my.val2");
+	_USER_SET_DATA_X (s_user, "my.v__al3");
+	_USER_SET_DATA_X (s_user, "my._v");
+	_USER_SET_DATA_X (s_user, "my.v+");
+	_USER_SET_DATA_X (s_user, "my.Av");
+	_USER_SET_DATA_X (s_user, "MY.AV");
+	_USER_SET_DATA_X (s_user, "MY.8V");
+	_USER_SET_DATA_X (s_user, "MY.8-V");
+	_USER_SET_DATA_X (s_user, "MY.8_V");
+	_USER_SET_DATA_X (s_user, "MY.8+V");
+	_USER_SET_DATA_X (s_user, "MY.8/V");
+	_USER_SET_DATA_X (s_user, "MY.8=V");
+	_USER_SET_DATA_X (s_user, "MY.-");
+	_USER_SET_DATA_X (s_user, "MY._");
+	_USER_SET_DATA_X (s_user, "MY.+");
+	_USER_SET_DATA_X (s_user, "MY./");
+	_USER_SET_DATA_X (s_user, "MY.=");
+	_USER_SET_DATA_X (s_user, "my.keys.1");
+	_USER_SET_DATA_X (s_user, "my.other.KEY.42");
+
+	nm_connection_add_setting (connection, NM_SETTING (s_user));
+
+	_writer_new_connec_exp (connection,
+	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_User_1.cexpected",
+	                        &testfile);
+
+	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
+
+	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
+}
+
+static void
 test_read_wired_never_default (void)
 {
 	NMConnection *connection;
@@ -1033,6 +1290,15 @@ test_read_wired_static_routes (void)
 	g_assert_cmpint (nm_ip_route_get_prefix (ip4_route), ==, 32);
 	g_assert_cmpstr (nm_ip_route_get_next_hop (ip4_route), ==, "192.168.1.7");
 	g_assert_cmpint (nm_ip_route_get_metric (ip4_route), ==, 3);
+	nmtst_assert_route_attribute_byte (ip4_route, NM_IP_ROUTE_ATTRIBUTE_TOS, 0x28);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 30000);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 12);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITCWND, 13);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITRWND, 14);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000);
+	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE);
+	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, TRUE);
+	nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.1.1.1");
 
 	g_object_unref (connection);
 }
@@ -1094,6 +1360,14 @@ test_read_wired_static_routes_legacy (void)
 	g_assert_cmpint (nm_ip_route_get_prefix (ip4_route), ==, 16);
 	g_assert_cmpstr (nm_ip_route_get_next_hop (ip4_route), ==, "7.7.7.7");
 	g_assert_cmpint (nm_ip_route_get_metric (ip4_route), ==, 3);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 10000);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 14);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITCWND, 42);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITRWND, 20);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000);
+	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, TRUE);
+	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE);
+	nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.2.3.4");
 
 	g_object_unref (connection);
 }
@@ -1234,7 +1508,7 @@ test_read_wired_ipv6_manual (void)
 	g_assert_cmpint (nm_ip_address_get_prefix (ip6_addr), ==, 96);
 
 	/* Routes */
-	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 2);
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 3);
 	/* Route #1 */
 	ip6_route = nm_setting_ip_config_get_route (s_ip6, 0);
 	g_assert (ip6_route);
@@ -1249,6 +1523,17 @@ test_read_wired_ipv6_manual (void)
 	g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 64);
 	g_assert_cmpstr (nm_ip_route_get_next_hop (ip6_route), ==, NULL);
 	g_assert_cmpint (nm_ip_route_get_metric (ip6_route), ==, 777);
+	/* Route #3 */
+	ip6_route = nm_setting_ip_config_get_route (s_ip6, 2);
+	g_assert (ip6_route);
+	g_assert_cmpstr (nm_ip_route_get_dest (ip6_route), ==, "aaaa::cccc");
+	g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 64);
+	g_assert_cmpstr (nm_ip_route_get_next_hop (ip6_route), ==, "3333::4444");
+	nmtst_assert_route_attribute_uint32 (ip6_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 13);
+	nmtst_assert_route_attribute_uint32 (ip6_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 1450);
+	nmtst_assert_route_attribute_boolean (ip6_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE);
+	nmtst_assert_route_attribute_string (ip6_route, NM_IP_ROUTE_ATTRIBUTE_FROM, "1111::2222/48");
+	nmtst_assert_route_attribute_string (ip6_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "5555::6666");
 
 	/* DNS Addresses */
 	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 2);
@@ -1567,12 +1852,16 @@ test_read_write_802_1X_subj_matches (void)
 	g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 0), ==, "x.yourdomain.tld");
 	g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 1), ==, "y.yourdomain.tld");
 
-	_writer_new_connection (connection,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!");
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected",
 	                        &testfile);
+	g_test_assert_expected_messages ();
 
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*missing IEEE_8021X_CA_CERT*peap*");
+	                       "*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!");
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
 	g_test_assert_expected_messages ();
 
@@ -1624,24 +1913,38 @@ test_read_802_1x_ttls_eapgtc (void)
 }
 
 static void
-test_read_wired_aliases_good (void)
+test_read_wired_aliases_good (gconstpointer test_data)
 {
+	const int N = GPOINTER_TO_INT (test_data);
 	NMConnection *connection;
 	NMSettingConnection *s_con;
 	NMSettingIPConfig *s_ip4;
-	int expected_num_addresses = 4;
-	const char *expected_address[4] = { "192.168.1.5", "192.168.1.6", "192.168.1.9", "192.168.1.99" };
-	const char *expected_label[4] = { NULL, "aliasem0:1", "aliasem0:2", "aliasem0:99" };
+	int expected_num_addresses;
+	const char *expected_address_0[] = { "192.168.1.5", "192.168.1.6", "192.168.1.9", "192.168.1.99", NULL };
+	const char *expected_address_3[] = { "192.168.1.5", "192.168.1.6", NULL };
+	const char *expected_label_0[] = { NULL, "aliasem0:1", "aliasem0:2", "aliasem0:99", NULL, };
+	const char *expected_label_3[] = { NULL, "aliasem3:1", NULL, };
+	const char **expected_address;
+	const char **expected_label;
 	int i, j;
+	char path[256];
 
-	connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem0",
-	                                    NULL, TYPE_ETHERNET, NULL);
+	expected_address = N == 0 ? expected_address_0 : expected_address_3;
+	expected_label   = N == 0 ? expected_label_0   : expected_label_3;
+	expected_num_addresses = g_strv_length ((char **) expected_address);
+
+	nm_sprintf_buf (path, TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem%d", N);
+
+	connection = _connection_from_file (path, NULL, TYPE_ETHERNET, NULL);
 
 	/* ===== CONNECTION SETTING ===== */
 
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
-	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "System aliasem0");
+	if (N == 0)
+		g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "System aliasem0");
+	else
+		g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "System aliasem3");
 
 	/* ===== IPv4 SETTING ===== */
 
@@ -1680,6 +1983,7 @@ test_read_wired_aliases_good (void)
 	}
 
 	/* Gateway */
+	g_assert (!nm_setting_ip_config_get_never_default (s_ip4));
 	g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "192.168.1.1");
 
 	for (i = 0; i < expected_num_addresses; i++)
@@ -1803,8 +2107,9 @@ test_clear_master (void)
 	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, "bridge");
 
 	/* 2. write the connection to a new file */
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-System_test-bridge-component-a.cexpected",
 	                        &testfile);
 
 	/* 3. clear master and slave-type */
@@ -1816,10 +2121,13 @@ test_clear_master (void)
 	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, NULL);
 	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NULL);
 
+	nmtst_assert_connection_verifies_after_normalization (connection, 0, 0);
+
 	/* 4. update the connection on disk */
 	_writer_update_connection (connection,
 	                           TEST_SCRATCH_DIR "/network-scripts/",
-	                           testfile);
+	                           testfile,
+	                           TEST_IFCFG_DIR "/network-scripts/ifcfg-System_test-bridge-component-b.cexpected");
 	keyfile = utils_get_keys_path (testfile);
 	g_assert (!g_file_test (keyfile, G_FILE_TEST_EXISTS));
 
@@ -1902,9 +2210,9 @@ test_write_dns_options (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
 
@@ -3120,8 +3428,9 @@ test_write_wifi_hidden (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
@@ -3169,6 +3478,7 @@ test_write_wifi_mac_random (gconstpointer user_data)
 	const char *name, *write_expected;
 	gpointer value_p;
 	NMSettingMacRandomization value;
+	char cexpected[NM_STRLEN (TEST_IFCFG_DIR) + 100];
 
 	nmtst_test_data_unpack (user_data, &name, &value_p, &write_expected);
 	value = GPOINTER_TO_INT (value_p);
@@ -3203,8 +3513,9 @@ test_write_wifi_mac_random (gconstpointer user_data)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        nm_sprintf_buf (cexpected, TEST_IFCFG_DIR"/network-scripts/ifcfg-Test_Write_WiFi_MAC_%s.cexpected", name),
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
@@ -3255,12 +3566,13 @@ test_write_wired_wake_on_lan (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
-	val = svGetValueString (f, "ETHTOOL_OPTS");
+	val = svGetValueStr_cp (f, "ETHTOOL_OPTS");
 	g_assert (val);
 	g_assert (strstr (val, "wol"));
 	g_assert (strstr (val, "sopass 00:00:00:11:22:33"));
@@ -3290,12 +3602,13 @@ test_write_wired_auto_negotiate_off (void)
 	              NM_SETTING_WIRED_SPEED, 10,
 	              NULL);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
-	val = svGetValueString (f, "ETHTOOL_OPTS");
+	val = svGetValueStr_cp (f, "ETHTOOL_OPTS");
 	g_assert (val);
 	g_assert (strstr (val, "autoneg off"));
 	g_assert (strstr (val, "speed 10"));
@@ -3329,7 +3642,7 @@ test_write_wired_auto_negotiate_on (void)
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
-	val = svGetValueString (f, "ETHTOOL_OPTS");
+	val = svGetValueStr_cp (f, "ETHTOOL_OPTS");
 	g_assert (val);
 	g_assert (strstr (val, "autoneg on"));
 	g_assert (!strstr (val, "speed"));
@@ -3403,8 +3716,9 @@ test_write_wifi_band_a (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
@@ -3753,6 +4067,12 @@ test_write_wired_static (void)
 
 	route6 = nm_ip_route_new (AF_INET6, "::", 128, "2222:aaaa::9999", 1, &error);
 	g_assert_no_error (error);
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_TOS, g_variant_new_byte (0xb8));
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (100));
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1280));
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, g_variant_new_boolean (TRUE));
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_FROM, g_variant_new_string ("2222::bbbb/32"));
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string ("::42"));
 	nm_setting_ip_config_add_route (s_ip6, route6);
 	nm_ip_route_unref (route6);
 
@@ -3766,9 +4086,9 @@ test_write_wired_static (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 	route6file = utils_get_route6_path (testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -4082,7 +4402,7 @@ test_write_wired_static_ip6_only_gw (gconstpointer user_data)
 	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
 
 	ifcfg = _svOpenFile (testfile);
-	written_ifcfg_gateway = svGetValueString (ifcfg, "IPV6_DEFAULTGW");
+	written_ifcfg_gateway = svGetValueStr_cp (ifcfg, "IPV6_DEFAULTGW");
 	svCloseFile (ifcfg);
 
 	/* access the gateway from the loaded connection. */
@@ -4147,8 +4467,9 @@ test_read_write_static_routes_legacy (void)
 	 * we can clean up after the written connection in both the original
 	 * source tree and for 'make distcheck'.
 	 */
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR_TMP,
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-test-static-routes-legacy.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -4436,9 +4757,9 @@ test_write_wired_8021x_tls (gconstpointer test_data)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
 
@@ -4486,15 +4807,15 @@ test_write_wired_8021x_tls (gconstpointer test_data)
 	}
 
 	/* Clean up created certs and keys */
-	tmp = utils_cert_path (testfile, "ca-cert.der");
+	tmp = utils_cert_path (testfile, "ca-cert", "der");
 	nmtst_file_unlink_if_exists (tmp);
 	g_free (tmp);
 
-	tmp = utils_cert_path (testfile, "client-cert.der");
+	tmp = utils_cert_path (testfile, "client-cert", "der");
 	nmtst_file_unlink_if_exists (tmp);
 	g_free (tmp);
 
-	tmp = utils_cert_path (testfile, "private-key.pem");
+	tmp = utils_cert_path (testfile, "private-key", "pem");
 	nmtst_file_unlink_if_exists (tmp);
 	g_free (tmp);
 }
@@ -4562,22 +4883,22 @@ test_write_wired_aliases (void)
 
 	/* Create some pre-existing alias files, to make sure they get overwritten / deleted. */
 	ifcfg = svCreateFile (TEST_SCRATCH_ALIAS_BASE ":2");
-	svSetValueString (ifcfg, "DEVICE", "alias0:2");
-	svSetValueString (ifcfg, "IPADDR", "192.168.1.2");
+	svSetValueStr (ifcfg, "DEVICE", "alias0:2");
+	svSetValueStr (ifcfg, "IPADDR", "192.168.1.2");
 	svWriteFile (ifcfg, 0644, NULL);
 	svCloseFile (ifcfg);
 	g_assert (g_file_test (TEST_SCRATCH_ALIAS_BASE ":2", G_FILE_TEST_EXISTS));
 
 	ifcfg = svCreateFile (TEST_SCRATCH_ALIAS_BASE ":5");
-	svSetValueString (ifcfg, "DEVICE", "alias0:5");
-	svSetValueString (ifcfg, "IPADDR", "192.168.1.5");
+	svSetValueStr (ifcfg, "DEVICE", "alias0:5");
+	svSetValueStr (ifcfg, "IPADDR", "192.168.1.5");
 	svWriteFile (ifcfg, 0644, NULL);
 	svCloseFile (ifcfg);
 	g_assert (g_file_test (TEST_SCRATCH_ALIAS_BASE ":5", G_FILE_TEST_EXISTS));
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 
 	/* Re-check the alias files */
 	g_assert (g_file_test (TEST_SCRATCH_ALIAS_BASE ":2", G_FILE_TEST_EXISTS));
@@ -5263,8 +5584,9 @@ test_write_wifi_wep_104_ascii (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
@@ -5347,8 +5669,9 @@ test_write_wifi_leap (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
@@ -5429,9 +5752,9 @@ test_write_wifi_leap_secret_flags (gconstpointer data)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
 
@@ -5701,6 +6024,11 @@ test_write_wifi_wpa_eap_tls (void)
 	nm_connection_add_setting (connection, NM_SETTING (s_8021x));
 
 	g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL);
+	g_object_set (s_8021x,
+	              NM_SETTING_802_1X_PHASE1_AUTH_FLAGS,
+	              (guint) (NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE |
+	                       NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE),
+	              NULL);
 
 	nm_setting_802_1x_add_eap_method (s_8021x, "tls");
 
@@ -6080,7 +6408,8 @@ test_write_wifi_wpa_then_open (void)
 	/* Write it back out */
 	_writer_update_connection (connection,
 	                           TEST_SCRATCH_DIR "/network-scripts/",
-	                           testfile);
+	                           testfile,
+	                           TEST_IFCFG_DIR "/network-scripts/ifcfg-random_wifi_connection.cexpected");
 	keyfile = utils_get_keys_path (testfile);
 	g_assert (!g_file_test (keyfile, G_FILE_TEST_EXISTS));
 
@@ -6199,7 +6528,8 @@ test_write_wifi_wpa_then_wep_with_perms (void)
 	/* Write it back out */
 	_writer_update_connection (connection,
 	                           TEST_SCRATCH_DIR "/network-scripts/",
-	                           testfile);
+	                           testfile,
+	                           TEST_IFCFG_DIR "/network-scripts/ifcfg-random_wifi_connection_2.cexpected");
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
 
@@ -6498,8 +6828,9 @@ test_write_permissions (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Permissions.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -6572,9 +6903,9 @@ test_write_wifi_wep_agent_keys (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
 
@@ -6953,8 +7284,9 @@ test_write_bridge_component (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -7198,8 +7530,9 @@ test_write_vlan (void)
 	connection = _connection_from_file (TEST_IFCFG_VLAN_INTERFACE,
 	                                    NULL, TYPE_VLAN, NULL);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected",
 	                        &testfile);
 }
 
@@ -7278,8 +7611,9 @@ test_write_vlan_reorder_hdr (void)
 	              NM_SETTING_VLAN_FLAGS, 1,
 	              NULL);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -7374,6 +7708,28 @@ test_read_bond_main (void)
 }
 
 static void
+test_read_bond_eth_type (void)
+{
+	NMConnection *connection;
+	NMSettingBond *s_bond;
+
+	connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-bond-eth-type",
+	                                    NULL, TYPE_ETHERNET,NULL);
+
+	g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "bond0");
+
+	/* ===== Bonding SETTING ===== */
+
+	s_bond = nm_connection_get_setting_bond (connection);
+	g_assert (s_bond);
+
+	g_assert_cmpstr (nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MIIMON), ==, "213");
+	g_assert_cmpstr (nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_LACP_RATE), ==, "1");
+
+	g_object_unref (connection);
+}
+
+static void
 test_write_bond_main (void)
 {
 	nmtst_auto_unlinkfile char *testfile = NULL;
@@ -7436,8 +7792,9 @@ test_write_bond_main (void)
 
 	nmtst_assert_connection_verifies_without_normalization (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_BOND, NULL);
@@ -7830,8 +8187,9 @@ test_write_dcb_basic (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts//ifcfg-dcb-test.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -8228,8 +8586,9 @@ test_write_team_port (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Team_Port.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
@@ -8269,6 +8628,69 @@ test_read_team_port_empty_config (void)
 }
 
 static void
+test_team_reread_slave (void)
+{
+	nmtst_auto_unlinkfile char *testfile = NULL;
+	gs_unref_object NMConnection *connection_1 = NULL;
+	gs_unref_object NMConnection *connection_2 = NULL;
+	gs_unref_object NMConnection *reread = NULL;
+	gboolean reread_same = FALSE;
+	NMSettingConnection *s_con;
+
+	connection_1 = nmtst_create_connection_from_keyfile (
+	        "[connection]\n"
+	        "id=team-slave-enp31s0f1-142\n"
+	        "uuid=74f435bb-ede4-415a-9d48-f580b60eba04\n"
+	        "type=vlan\n"
+	        "autoconnect=false\n"
+	        "interface-name=enp31s0f1-142\n"
+	        "master=team142\n"
+	        "permissions=\n"
+	        "slave-type=team\n"
+	        "\n"
+	        "[vlan]\n"
+	        "egress-priority-map=\n"
+	        "flags=1\n"
+	        "id=142\n"
+	        "ingress-priority-map=\n"
+	        "parent=enp31s0f1\n"
+	        , "/test_team_reread_slave", NULL);
+
+	/* to double-check keyfile syntax, re-create the connection by hand. */
+	connection_2 = nmtst_create_minimal_connection ("team-slave-enp31s0f1-142", "74f435bb-ede4-415a-9d48-f580b60eba04", NM_SETTING_VLAN_SETTING_NAME, &s_con);
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, "enp31s0f1-142",
+	              NM_SETTING_CONNECTION_MASTER, "team142",
+	              NM_SETTING_CONNECTION_SLAVE_TYPE, "team",
+	              NULL);
+	g_object_set (nm_connection_get_setting_vlan (connection_2),
+	              NM_SETTING_VLAN_FLAGS, 1,
+	              NM_SETTING_VLAN_ID, 142,
+	              NM_SETTING_VLAN_PARENT, "enp31s0f1",
+	              NULL);
+	nm_connection_add_setting (connection_2, nm_setting_team_port_new ());
+	nmtst_connection_normalize (connection_2);
+
+	nmtst_assert_connection_equals (connection_1, FALSE, connection_2, FALSE);
+
+	_writer_new_connection_reread ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2,
+	                               TEST_SCRATCH_DIR "/network-scripts/",
+	                               &testfile,
+	                               TEST_IFCFG_DIR "/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected",
+	                               &reread,
+	                               &reread_same);
+	_assert_reread_same ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, reread);
+	g_assert (reread_same);
+	g_clear_object (&reread);
+
+	reread = _connection_from_file (testfile, NULL, TYPE_VLAN,
+	                                NULL);
+	nmtst_assert_connection_equals ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, FALSE,
+	                                reread, FALSE);
+}
+
+static void
 test_read_proxy_basic (void)
 {
 	NMConnection *connection;
@@ -8327,8 +8749,9 @@ test_write_proxy_basic (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
@@ -8682,12 +9105,12 @@ test_write_unknown (gconstpointer test_data)
 
 	sv = _svOpenFile (testfile);
 
-	svFileSetName (sv, filename_tmp_1);
-	svFileSetModified (sv);
+	svFileSetName_test_only (sv, filename_tmp_1);
+	svFileSetModified_test_only (sv);
 
 	if (g_str_has_suffix (testfile, "ifcfg-test-write-unknown-4")) {
 		_svGetValue_check (sv, "NAME", "l4x");
-		_svGetValue_check (sv, "NAME2", NULL);
+		_svGetValue_check (sv, "NAME2", "");
 		_svGetValue_check (sv, "NAME3", "name3-value");
 
 		svSetValue (sv, "NAME", "set-by-test1");
@@ -8944,6 +9367,8 @@ int main (int argc, char **argv)
 	nmtst_add_test_func (TPATH "wired/read/manual/3", test_read_wired_ipv4_manual, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-ipv4-manual-3", "System test-wired-ipv4-manual-3");
 	nmtst_add_test_func (TPATH "wired/read/manual/4", test_read_wired_ipv4_manual, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-ipv4-manual-4", "System test-wired-ipv4-manual-4");
 
+	g_test_add_func (TPATH "user/1", test_user_1);
+
 	g_test_add_func (TPATH "wired/ipv6-manual", test_read_wired_ipv6_manual);
 
 	nmtst_add_test_func (TPATH "wired-ipv6-only/0", test_read_wired_ipv6_only, TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-ipv6-only",   "System test-wired-ipv6-only");
@@ -8960,7 +9385,8 @@ int main (int argc, char **argv)
 
 	g_test_add_func (TPATH "802-1x/subj-matches", test_read_write_802_1X_subj_matches);
 	g_test_add_func (TPATH "802-1x/ttls-eapgtc", test_read_802_1x_ttls_eapgtc);
-	g_test_add_func (TPATH "wired/read/aliases", test_read_wired_aliases_good);
+	g_test_add_data_func (TPATH "wired/read/aliases/good/0", GINT_TO_POINTER (0), test_read_wired_aliases_good);
+	g_test_add_data_func (TPATH "wired/read/aliases/good/3", GINT_TO_POINTER (3), test_read_wired_aliases_good);
 	g_test_add_func (TPATH "wired/read/aliases/bad1", test_read_wired_aliases_bad_1);
 	g_test_add_func (TPATH "wired/read/aliases/bad2", test_read_wired_aliases_bad_2);
 	g_test_add_func (TPATH "wifi/read/open", test_read_wifi_open);
@@ -9112,6 +9538,7 @@ int main (int argc, char **argv)
 	g_test_add_data_func (TPATH "fcoe/write-vn2vn", (gpointer) NM_SETTING_DCB_FCOE_MODE_VN2VN, test_write_fcoe_mode);
 
 	g_test_add_func (TPATH "bond/read-master", test_read_bond_main);
+	g_test_add_func (TPATH "bond/read-master-eth-type", test_read_bond_eth_type);
 	g_test_add_func (TPATH "bond/read-slave", test_read_bond_slave);
 	g_test_add_func (TPATH "bond/read-slave-ib", test_read_bond_slave_ib);
 	g_test_add_func (TPATH "bond/write-master", test_write_bond_main);
@@ -9133,6 +9560,7 @@ int main (int argc, char **argv)
 	g_test_add_data_func (TPATH "team/read-port-2", TEST_IFCFG_DIR"/network-scripts/ifcfg-test-team-port-2", test_read_team_port);
 	g_test_add_func (TPATH "team/write-port", test_write_team_port);
 	g_test_add_func (TPATH "team/read-port-empty-config", test_read_team_port_empty_config);
+	g_test_add_func (TPATH "team/reread-slave", test_team_reread_slave);
 
 	g_test_add_func (TPATH "proxy/read-proxy-basic", test_read_proxy_basic);
 	g_test_add_func (TPATH "proxy/write-proxy-basic", test_write_proxy_basic);
diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c
index 84f2d3f4..c5129fea 100644
--- a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c
+++ b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c
@@ -31,6 +31,7 @@
 #include "settings/nm-settings-plugin.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
+#include "nm-setting-metadata.h"
 
 #include "nms-ifnet-net-utils.h"
 #include "nms-ifnet-wpa-parser.h"
@@ -1688,96 +1689,43 @@ error:
 	return NULL;
 }
 
-typedef NMSetting8021xCKScheme (*SchemeFunc) (NMSetting8021x * setting);
-typedef const char *(*PathFunc) (NMSetting8021x * setting);
-typedef GBytes *(*BlobFunc) (NMSetting8021x * setting);
-
-typedef struct ObjectType {
-	const char *setting_key;
-	SchemeFunc scheme_func;
-	PathFunc path_func;
-	BlobFunc blob_func;
-	const char *conn_name_key;
-	const char *suffix;
-} ObjectType;
-
-static const ObjectType ca_type = {
-	NM_SETTING_802_1X_CA_CERT,
-	nm_setting_802_1x_get_ca_cert_scheme,
-	nm_setting_802_1x_get_ca_cert_path,
-	nm_setting_802_1x_get_ca_cert_blob,
-	"ca_cert",
-	"ca-cert.der"
-};
-
-static const ObjectType phase2_ca_type = {
-	NM_SETTING_802_1X_PHASE2_CA_CERT,
-	nm_setting_802_1x_get_phase2_ca_cert_scheme,
-	nm_setting_802_1x_get_phase2_ca_cert_path,
-	nm_setting_802_1x_get_phase2_ca_cert_blob,
-	"ca_cert2",
-	"inner-ca-cert.der"
-};
-
-static const ObjectType client_type = {
-	NM_SETTING_802_1X_CLIENT_CERT,
-	nm_setting_802_1x_get_client_cert_scheme,
-	nm_setting_802_1x_get_client_cert_path,
-	nm_setting_802_1x_get_client_cert_blob,
-	"client_cert",
-	"client-cert.der"
-};
-
-static const ObjectType phase2_client_type = {
-	NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
-	nm_setting_802_1x_get_phase2_client_cert_scheme,
-	nm_setting_802_1x_get_phase2_client_cert_path,
-	nm_setting_802_1x_get_phase2_client_cert_blob,
-	"client_cert2",
-	"inner-client-cert.der"
-};
-
-static const ObjectType pk_type = {
-	NM_SETTING_802_1X_PRIVATE_KEY,
-	nm_setting_802_1x_get_private_key_scheme,
-	nm_setting_802_1x_get_private_key_path,
-	nm_setting_802_1x_get_private_key_blob,
-	"private_key",
-	"private-key.pem"
-};
-
-static const ObjectType phase2_pk_type = {
-	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-	nm_setting_802_1x_get_phase2_private_key_scheme,
-	nm_setting_802_1x_get_phase2_private_key_path,
-	nm_setting_802_1x_get_phase2_private_key_blob,
-	"private_key2",
-	"inner-private-key.pem"
-};
-
-static const ObjectType p12_type = {
-	NM_SETTING_802_1X_PRIVATE_KEY,
-	nm_setting_802_1x_get_private_key_scheme,
-	nm_setting_802_1x_get_private_key_path,
-	nm_setting_802_1x_get_private_key_blob,
-	"private_key",
-	"private-key.p12"
-};
-
-static const ObjectType phase2_p12_type = {
-	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-	nm_setting_802_1x_get_phase2_private_key_scheme,
-	nm_setting_802_1x_get_phase2_private_key_path,
-	nm_setting_802_1x_get_phase2_private_key_blob,
-	"private_key2",
-	"inner-private-key.p12"
+typedef struct Setting8021xSchemeVtable {
+	const NMSetting8021xSchemeVtable *vtable;
+	const char *ifnet_key;
+} Setting8021xSchemeVtable;
+
+static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = {
+	[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT],
+		.ifnet_key              = "ca_cert",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT],
+		.ifnet_key              = "ca_cert2",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT],
+		.ifnet_key              = "client_cert",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT],
+		.ifnet_key              = "client_cert2",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY],
+		.ifnet_key              = "private_key",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY],
+		.ifnet_key              = "private_key2",
+	},
 };
 
 static gboolean
 write_object (NMSetting8021x *s_8021x,
               const char *conn_name,
               GBytes *override_data,
-              const ObjectType *objtype,
+              const Setting8021xSchemeVtable *objtype,
               GError **error)
 {
 	NMSetting8021xCKScheme scheme;
@@ -1792,13 +1740,13 @@ write_object (NMSetting8021x *s_8021x,
 		 */
 		blob = override_data;
 	else {
-		scheme = (*(objtype->scheme_func)) (s_8021x);
+		scheme = (*(objtype->vtable->scheme_func)) (s_8021x);
 		switch (scheme) {
 		case NM_SETTING_802_1X_CK_SCHEME_BLOB:
-			blob = (*(objtype->blob_func)) (s_8021x);
+			blob = (*(objtype->vtable->blob_func)) (s_8021x);
 			break;
 		case NM_SETTING_802_1X_CK_SCHEME_PATH:
-			path = (*(objtype->path_func)) (s_8021x);
+			path = (*(objtype->vtable->path_func)) (s_8021x);
 			break;
 		default:
 			break;
@@ -1809,8 +1757,8 @@ write_object (NMSetting8021x *s_8021x,
 	 * may have been sent.
 	 */
 	if (path) {
-		wpa_set_data (conn_name, (gchar *) objtype->conn_name_key,
-			      (gchar *) path);
+		wpa_set_data (conn_name, (gchar *) objtype->ifnet_key,
+		              (gchar *) path);
 		return TRUE;
 	}
 
@@ -1828,17 +1776,16 @@ write_8021x_certs (NMSetting8021x *s_8021x,
                    GError **error)
 {
 	char *password = NULL;
-	const ObjectType *otype = NULL;
+	const Setting8021xSchemeVtable *otype = NULL;
 	gboolean is_pkcs12 = FALSE, success = FALSE;
 	GBytes *blob = NULL;
 	GBytes *enc_key = NULL;
 	gchar *generated_pw = NULL;
 
 	/* CA certificate */
-	if (phase2)
-		otype = &phase2_ca_type;
-	else
-		otype = &ca_type;
+	otype = phase2
+	        ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT]
+	        : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT];
 
 	if (!write_object (s_8021x, conn_name, NULL, otype, error))
 		return FALSE;
@@ -1864,14 +1811,13 @@ write_8021x_certs (NMSetting8021x *s_8021x,
 		    nm_setting_802_1x_get_private_key_password (s_8021x);
 	}
 
-	if (is_pkcs12)
-		otype = phase2 ? &phase2_p12_type : &p12_type;
-	else
-		otype = phase2 ? &phase2_pk_type : &pk_type;
+	otype = phase2
+	        ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY]
+	        : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY];
 
-	if ((*(otype->scheme_func)) (s_8021x) ==
+	if ((*(otype->vtable->scheme_func)) (s_8021x) ==
 	    NM_SETTING_802_1X_CK_SCHEME_BLOB)
-		blob = (*(otype->blob_func)) (s_8021x);
+		blob = (*(otype->vtable->blob_func)) (s_8021x);
 
 	/* Only do the private key re-encrypt dance if we got the raw key data, which
 	 * by definition will be unencrypted.  If we're given a direct path to the
@@ -1883,7 +1829,7 @@ write_8021x_certs (NMSetting8021x *s_8021x,
 		/* Encrypt the unencrypted private key with the fake password */
 		tmp_enc_key =
 		    nm_utils_rsa_key_encrypt (g_bytes_get_data (blob, NULL), g_bytes_get_size (blob),
-					      password, &generated_pw, error);
+		                              password, &generated_pw, error);
 		if (!tmp_enc_key)
 			goto out;
 
@@ -1906,12 +1852,11 @@ write_8021x_certs (NMSetting8021x *s_8021x,
 	/* Client certificate */
 	if (is_pkcs12) {
 		wpa_set_data (conn_name,
-			      phase2 ? "client_cert2" : "client_cert", NULL);
+		              phase2 ? "client_cert2" : "client_cert", NULL);
 	} else {
-		if (phase2)
-			otype = &phase2_client_type;
-		else
-			otype = &client_type;
+		otype = phase2
+		        ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT]
+		        : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT];
 
 		/* Save the client certificate */
 		if (!write_object (s_8021x, conn_name, NULL, otype, error))
diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h
index e6c0e88c..51bc34b9 100644
--- a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h
+++ b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h
@@ -22,7 +22,7 @@
 #ifndef _CONNECTION_PARSER_H
 #define _CONNECTION_PARSER_H
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 #include "nms-ifnet-net-parser.h"
 
diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-utils.h b/src/settings/plugins/ifnet/nms-ifnet-net-utils.h
index 79d44642..cc273aa7 100644
--- a/src/settings/plugins/ifnet/nms-ifnet-net-utils.h
+++ b/src/settings/plugins/ifnet/nms-ifnet-net-utils.h
@@ -26,8 +26,8 @@
 
 #include <arpa/inet.h>
 
-#include <nm-setting-ip6-config.h>
-#include <nm-setting-ip4-config.h>
+#include "nm-setting-ip6-config.h"
+#include "nm-setting-ip4-config.h"
 
 #include "nms-ifnet-net-parser.h"
 
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
index 36e52e2c..6a86bf86 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
@@ -24,7 +24,7 @@
 #ifndef __PARSER_H__
 #define __PARSER_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 #include "nms-ifupdown-interface-parser.h"
 
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
index 211e5d57..189b6e69 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
@@ -28,8 +28,8 @@
 
 #include <string.h>
 #include <arpa/inet.h>
-#include <gudev/gudev.h>
 #include <gmodule.h>
+#include <libudev.h>
 
 #include "nm-setting-connection.h"
 #include "nm-dbus-interface.h"
@@ -42,6 +42,7 @@
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 #include "nm-config.h"
+#include "nm-utils/nm-udev-utils.h"
 
 #include "nms-ifupdown-interface-parser.h"
 #include "nms-ifupdown-connection.h"
@@ -62,7 +63,7 @@
 /*****************************************************************************/
 
 typedef struct {
-	GUdevClient *client;
+	NMUdevClient *udev_client;
 
 	GHashTable *connections;  /* /e/n/i block name :: NMIfupdownConnection */
 
@@ -103,21 +104,21 @@ NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfupdown, settings_plugin_ifupdown_get
 
 static void
 bind_device_to_connection (SettingsPluginIfupdown *self,
-                           GUdevDevice *device,
+                           struct udev_device *device,
                            NMIfupdownConnection *exported)
 {
 	NMSettingWired *s_wired;
 	NMSettingWireless *s_wifi;
 	const char *iface, *address;
 
-	iface = g_udev_device_get_name (device);
+	iface = udev_device_get_sysname (device);
 	if (!iface) {
 		nm_log_warn (LOGD_SETTINGS, "failed to get ifname for device.");
 		return;
 	}
 
-	address = g_udev_device_get_sysfs_attr (device, "address");
-	if (!address || !strlen (address)) {
+	address = udev_device_get_sysattr_value (device, "address");
+	if (!address || !address[0]) {
 		nm_log_warn (LOGD_SETTINGS, "failed to get MAC address for %s", iface);
 		return;
 	}
@@ -142,14 +143,14 @@ bind_device_to_connection (SettingsPluginIfupdown *self,
 }
 
 static void
-udev_device_added (SettingsPluginIfupdown *self, GUdevDevice *device)
+udev_device_added (SettingsPluginIfupdown *self, struct udev_device *device)
 {
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
 	const char *iface, *path;
 	NMIfupdownConnection *exported;
 
-	iface = g_udev_device_get_name (device);
-	path = g_udev_device_get_sysfs_path (device);
+	iface = udev_device_get_sysname (device);
+	path = udev_device_get_syspath (device);
 	if (!iface || !path)
 		return;
 
@@ -165,7 +166,7 @@ udev_device_added (SettingsPluginIfupdown *self, GUdevDevice *device)
 		return;
 	}
 
-	g_hash_table_insert (priv->kernel_ifaces, g_strdup (iface), g_object_ref (device));
+	g_hash_table_insert (priv->kernel_ifaces, g_strdup (iface), udev_device_ref (device));
 
 	if (exported)
 		bind_device_to_connection (self, device, exported);
@@ -175,13 +176,13 @@ udev_device_added (SettingsPluginIfupdown *self, GUdevDevice *device)
 }
 
 static void
-udev_device_removed (SettingsPluginIfupdown *self, GUdevDevice *device)
+udev_device_removed (SettingsPluginIfupdown *self, struct udev_device *device)
 {
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
 	const char *iface, *path;
 
-	iface = g_udev_device_get_name (device);
-	path = g_udev_device_get_sysfs_path (device);
+	iface = udev_device_get_sysname (device);
+	path = udev_device_get_syspath (device);
 	if (!iface || !path)
 		return;
 
@@ -195,13 +196,13 @@ udev_device_removed (SettingsPluginIfupdown *self, GUdevDevice *device)
 }
 
 static void
-udev_device_changed (SettingsPluginIfupdown *self, GUdevDevice *device)
+udev_device_changed (SettingsPluginIfupdown *self, struct udev_device *device)
 {
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
 	const char *iface, *path;
 
-	iface = g_udev_device_get_name (device);
-	path = g_udev_device_get_sysfs_path (device);
+	iface = udev_device_get_sysname (device);
+	path = udev_device_get_syspath (device);
 	if (!iface || !path)
 		return;
 
@@ -215,20 +216,21 @@ udev_device_changed (SettingsPluginIfupdown *self, GUdevDevice *device)
 }
 
 static void
-handle_uevent (GUdevClient *client,
-               const char *action,
-               GUdevDevice *device,
+handle_uevent (NMUdevClient *client,
+               struct udev_device *device,
                gpointer user_data)
 {
 	SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (user_data);
 	const char *subsys;
+	const char *action;
+
+	action = udev_device_get_action (device);
 
 	g_return_if_fail (action != NULL);
 
 	/* A bit paranoid */
-	subsys = g_udev_device_get_subsystem (device);
-	g_return_if_fail (subsys != NULL);
-	g_return_if_fail (strcmp (subsys, "net") == 0);
+	subsys = udev_device_get_subsystem (device);
+	g_return_if_fail (nm_streq0 (subsys, "net"));
 
 	if (!strcmp (action, "add"))
 		udev_device_added (self, device);
@@ -271,7 +273,7 @@ get_unmanaged_specs (NMSettingsPlugin *config)
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE ((SettingsPluginIfupdown *) config);
 	GSList *specs = NULL;
 	GHashTableIter iter;
-	GUdevDevice *device;
+	struct udev_device *device;
 	const char *iface;
 
 	if (!ALWAYS_UNMANAGE && !priv->unmanage_well_known)
@@ -284,7 +286,7 @@ get_unmanaged_specs (NMSettingsPlugin *config)
 	while (g_hash_table_iter_next (&iter, (gpointer) &iface, (gpointer) &device)) {
 		const char *address;
 
-		address = g_udev_device_get_sysfs_attr (device, "address");
+		address = udev_device_get_sysattr_value (device, "address");
 		if (address)
 			specs = g_slist_append (specs, g_strdup_printf ("mac:%s", address));
 		else
@@ -318,17 +320,23 @@ get_property (GObject *object, guint prop_id,
 /*****************************************************************************/
 
 static void
+_udev_device_unref (gpointer ptr)
+{
+	udev_device_unref (ptr);
+}
+
+static void
 init (NMSettingsPlugin *config)
 {
 	SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (config);
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
 	GHashTable *auto_ifaces;
 	if_block *block = NULL;
-	GList *keys, *iter;
+	struct udev_enumerate *enumerate;
+	struct udev_list_entry *keys;
 	GHashTableIter con_iter;
 	const char *block_name;
 	NMIfupdownConnection *connection;
-	const char *subsys[2] = { "net", NULL };
 
 	auto_ifaces = g_hash_table_new (g_str_hash, g_str_equal);
 
@@ -336,18 +344,15 @@ init (NMSettingsPlugin *config)
 		priv->connections = g_hash_table_new (g_str_hash, g_str_equal);
 
 	if(!priv->kernel_ifaces)
-		priv->kernel_ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+		priv->kernel_ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _udev_device_unref);
 
 	if(!priv->eni_ifaces)
 		priv->eni_ifaces = g_hash_table_new (g_str_hash, g_str_equal);
 
 	nm_log_info (LOGD_SETTINGS, "init!");
 
-	priv->client = g_udev_client_new (subsys);
-	if (!priv->client) {
-		nm_log_warn (LOGD_SETTINGS, "    error initializing libgudev");
-	} else
-		g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self);
+	priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL },
+	                                        handle_uevent, self);
 
 	/* Read in all the interfaces */
 	ifparser_init (ENI_INTERFACES_FILE, 0);
@@ -445,12 +450,20 @@ init (NMSettingsPlugin *config)
 	nm_log_info (LOGD_SETTINGS, "management mode: %s", priv->unmanage_well_known ? "unmanaged" : "managed");
 
 	/* Add well-known interfaces */
-	keys = g_udev_client_query_by_subsystem (priv->client, "net");
-	for (iter = keys; iter; iter = g_list_next (iter)) {
-		udev_device_added (self, G_UDEV_DEVICE (iter->data));
-		g_object_unref (G_UDEV_DEVICE (iter->data));
+	enumerate = nm_udev_client_enumerate_new (priv->udev_client);
+	udev_enumerate_scan_devices (enumerate);
+	keys = udev_enumerate_get_list_entry (enumerate);
+	for (; keys; keys = udev_list_entry_get_next (keys)) {
+		struct udev_device *udevice;
+
+		udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate),
+		                                        udev_list_entry_get_name (keys));
+		if (udevice) {
+			udev_device_added (self, udevice);
+			udev_device_unref (udevice);
+		}
 	}
-	g_list_free (keys);
+	udev_enumerate_unref (enumerate);
 
 	/* Now if we're running in managed mode, let NM know there are new connections */
 	if (!priv->unmanage_well_known) {
@@ -483,7 +496,8 @@ dispose (GObject *object)
 
 	g_clear_pointer (&priv->kernel_ifaces, g_hash_table_destroy);
 	g_clear_pointer (&priv->eni_ifaces, g_hash_table_destroy);
-	g_clear_object (&priv->client);
+
+	priv->udev_client = nm_udev_client_unref (priv->udev_client);
 
 	G_OBJECT_CLASS (settings_plugin_ifupdown_parent_class)->dispose (object);
 }
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c
index ff654acf..bd07d263 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-connection.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-connection.c
@@ -58,12 +58,16 @@ commit_changes (NMSettingsConnection *connection,
 {
 	char *path = NULL;
 	GError *error = NULL;
+	gs_unref_object NMConnection *reread = NULL;
+	gboolean reread_same = FALSE;
 
 	if (!nms_keyfile_writer_connection (NM_CONNECTION (connection),
 	                                    nm_settings_connection_get_filename (connection),
 	                                    NM_FLAGS_ALL (commit_reason,   NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION
 	                                                                 | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED),
 	                                    &path,
+	                                    &reread,
+	                                    &reread_same,
 	                                    &error)) {
 		callback (connection, error, user_data);
 		g_clear_error (&error);
@@ -89,6 +93,18 @@ commit_changes (NMSettingsConnection *connection,
 		             NMS_KEYFILE_CONNECTION_LOG_ARG (connection));
 	}
 
+	if (reread && !reread_same) {
+		gs_free_error GError *local = NULL;
+
+		if (!nm_settings_connection_replace_settings (connection, reread, FALSE, "update-during-write", &local)) {
+			nm_log_warn (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" after persisting connection failed: %s",
+			             NMS_KEYFILE_CONNECTION_LOG_ARG (connection), local->message);
+		} else {
+			nm_log_info (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" after persisting connection",
+			             NMS_KEYFILE_CONNECTION_LOG_ARG (connection));
+		}
+	}
+
 	g_free (path);
 
 	NM_SETTINGS_CONNECTION_CLASS (nms_keyfile_connection_parent_class)->commit_changes (connection,
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index 97306d66..4af80142 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -77,7 +77,7 @@ G_DEFINE_TYPE_EXTENDED (NMSKeyfilePlugin, nms_keyfile_plugin, G_TYPE_OBJECT, 0,
 #define _NMLOG_PREFIX_NAME      "keyfile"
 #define _NMLOG_DOMAIN           LOGD_SETTINGS
 #define _NMLOG(level, ...) \
-    nm_log ((level), _NMLOG_DOMAIN, \
+    nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
             "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
             _NMLOG_PREFIX_NAME": " \
             _NM_UTILS_MACRO_REST (__VA_ARGS__))
@@ -530,12 +530,19 @@ add_connection (NMSettingsPlugin *config,
 {
 	NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config);
 	gs_free char *path = NULL;
+	gs_unref_object NMConnection *reread = NULL;
 
 	if (save_to_disk) {
-		if (!nms_keyfile_writer_connection (connection, NULL, FALSE, &path, error))
+		if (!nms_keyfile_writer_connection (connection,
+		                                    NULL,
+		                                    FALSE,
+		                                    &path,
+		                                    &reread,
+		                                    NULL,
+		                                    error))
 			return NULL;
 	}
-	return NM_SETTINGS_CONNECTION (update_connection (self, connection, path, NULL, FALSE, NULL, error));
+	return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error));
 }
 
 static GSList *
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index 39a01480..cb4b8379 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -56,6 +56,10 @@ _fmt_warn (const char *group, NMSetting *setting, const char *property_name, con
 		return message;
 }
 
+typedef struct {
+	bool verbose;
+} HandlerReadData;
+
 static gboolean
 _handler_read (GKeyFile *keyfile,
                NMConnection *connection,
@@ -64,11 +68,16 @@ _handler_read (GKeyFile *keyfile,
                void *user_data,
                GError **error)
 {
+	const HandlerReadData *handler_data = user_data;
+
 	if (type == NM_KEYFILE_READ_TYPE_WARN) {
 		NMKeyfileReadTypeDataWarn *warn_data = type_data;
 		NMLogLevel level;
 		char *message_free = NULL;
 
+		if (!handler_data->verbose)
+			return TRUE;
+
 		if (warn_data->severity > NM_KEYFILE_WARN_SEVERITY_WARN)
 			level = LOGL_ERR;
 		else if (warn_data->severity >= NM_KEYFILE_WARN_SEVERITY_WARN)
@@ -78,7 +87,9 @@ _handler_read (GKeyFile *keyfile,
 		else
 			level = LOGL_INFO;
 
-		nm_log (level, LOGD_SETTINGS, "keyfile: %s",
+		nm_log (level, LOGD_SETTINGS, NULL,
+		        nm_connection_get_uuid (connection),
+		        "keyfile: %s",
 		        _fmt_warn (warn_data->group, warn_data->setting,
 		                   warn_data->property_name, warn_data->message,
 		                   &message_free));
@@ -89,6 +100,19 @@ _handler_read (GKeyFile *keyfile,
 }
 
 NMConnection *
+nms_keyfile_reader_from_keyfile (GKeyFile *key_file,
+                                 const char *filename,
+                                 gboolean verbose,
+                                 GError **error)
+{
+	HandlerReadData data = {
+		.verbose = verbose,
+	};
+
+	return nm_keyfile_read (key_file, filename, NULL, _handler_read, &data, error);
+}
+
+NMConnection *
 nms_keyfile_reader_from_file (const char *filename, GError **error)
 {
 	GKeyFile *key_file;
@@ -122,7 +146,7 @@ nms_keyfile_reader_from_file (const char *filename, GError **error)
 	if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error))
 		goto out;
 
-	connection = nm_keyfile_read (key_file, filename, NULL, _handler_read, NULL, error);
+	connection = nms_keyfile_reader_from_keyfile (key_file, filename, TRUE, error);
 	if (!connection)
 		goto out;
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.h b/src/settings/plugins/keyfile/nms-keyfile-reader.h
index c52fea31..b60c1e69 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.h
@@ -22,7 +22,12 @@
 #ifndef __NMS_KEYFILE_READER_H__
 #define __NMS_KEYFILE_READER_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
+
+NMConnection *nms_keyfile_reader_from_keyfile (GKeyFile *key_file,
+                                               const char *filename,
+                                               gboolean verbose,
+                                               GError **error);
 
 NMConnection *nms_keyfile_reader_from_file (const char *filename, GError **error);
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c
index 95897db3..92ed2849 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -32,6 +32,7 @@
 #include "nm-keyfile-internal.h"
 
 #include "nms-keyfile-utils.h"
+#include "nms-keyfile-reader.h"
 
 /*****************************************************************************/
 
@@ -51,12 +52,12 @@ cert_writer (NMConnection *connection,
 	NMSetting8021xCKFormat format;
 	const char *path = NULL, *ext = "pem";
 
-	scheme = cert_data->scheme_func (cert_data->setting);
+	scheme = cert_data->vtable->scheme_func (cert_data->setting);
 	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
 		char *tmp = NULL;
 		const char *accepted_path = NULL;
 
-		path = cert_data->path_func (cert_data->setting);
+		path = cert_data->vtable->path_func (cert_data->setting);
 		g_assert (path);
 
 		if (g_str_has_prefix (path, info->keyfile_dir)) {
@@ -92,11 +93,11 @@ cert_writer (NMConnection *connection,
 
 		if (!accepted_path)
 			accepted_path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL);
-		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, accepted_path);
+		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key, accepted_path);
 		g_free (tmp);
 	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
-		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name,
-		                                 cert_data->uri_func (cert_data->setting));
+		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key,
+		                                 cert_data->vtable->uri_func (cert_data->setting));
 	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
 		GBytes *blob;
 		const guint8 *blob_data;
@@ -105,13 +106,13 @@ cert_writer (NMConnection *connection,
 		GError *local = NULL;
 		char *new_path;
 
-		blob = cert_data->blob_func (cert_data->setting);
+		blob = cert_data->vtable->blob_func (cert_data->setting);
 		g_assert (blob);
 		blob_data = g_bytes_get_data (blob, &blob_len);
 
-		if (cert_data->format_func) {
+		if (cert_data->vtable->format_func) {
 			/* Get the extension for a private key */
-			format = cert_data->format_func (cert_data->setting);
+			format = cert_data->vtable->format_func (cert_data->setting);
 			if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
 				ext = "p12";
 		} else {
@@ -124,17 +125,17 @@ cert_writer (NMConnection *connection,
 		 * from now on instead of pushing around the certificate data.
 		 */
 		new_path = g_strdup_printf ("%s/%s-%s.%s", info->keyfile_dir, nm_connection_get_uuid (connection),
-		                            cert_data->suffix, ext);
+		                            cert_data->vtable->file_suffix, ext);
 
 		success = nm_utils_file_set_contents (new_path, (const gchar *) blob_data,
 		                                      blob_len, 0600, &local);
 		if (success) {
 			/* Write the path value to the keyfile.
 			 * We know, that basename(new_path) starts with a UUID, hence no conflict with "data:;base64,"  */
-			nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, strrchr (new_path, '/') + 1);
+			nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key, strrchr (new_path, '/') + 1);
 		} else {
 			nm_log_warn (LOGD_SETTINGS, "keyfile: %s.%s: failed to write certificate to file %s: %s",
-			             setting_name, cert_data->property_name, new_path, local->message);
+			             setting_name, cert_data->vtable->setting_key, new_path, local->message);
 			g_error_free (local);
 		}
 		g_free (new_path);
@@ -174,9 +175,11 @@ _internal_write_connection (NMConnection *connection,
                             const char *existing_path,
                             gboolean force_rename,
                             char **out_path,
+                            NMConnection **out_reread,
+                            gboolean *out_reread_same,
                             GError **error)
 {
-	GKeyFile *key_file;
+	gs_unref_keyfile GKeyFile *key_file = NULL;
 	gs_free char *data = NULL;
 	gsize len;
 	gs_free char *path = NULL;
@@ -188,8 +191,15 @@ _internal_write_connection (NMConnection *connection,
 	g_return_val_if_fail (!out_path || !*out_path, FALSE);
 	g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE);
 
-	if (!nm_connection_verify (connection, error))
+	switch (_nm_connection_verify (connection, error)) {
+	case NM_SETTING_VERIFY_NORMALIZABLE:
+		nm_assert_not_reached ();
+		/* fall-through */
+	case NM_SETTING_VERIFY_SUCCESS:
+		break;
+	default:
 		g_return_val_if_reached (FALSE);
+	}
 
 	id = nm_connection_get_id (connection);
 	g_assert (id && *id);
@@ -200,7 +210,6 @@ _internal_write_connection (NMConnection *connection,
 	if (!key_file)
 		return FALSE;
 	data = g_key_file_to_data (key_file, &len, error);
-	g_key_file_unref (key_file);
 	if (!data)
 		return FALSE;
 
@@ -290,15 +299,48 @@ _internal_write_connection (NMConnection *connection,
 		path = NULL;
 	}
 
+	if (out_reread || out_reread_same)
+	{
+		gs_unref_object NMConnection *reread = NULL;
+		gboolean reread_same = FALSE;
+
+		reread = nms_keyfile_reader_from_keyfile (key_file, path, FALSE, NULL);
+
+		nm_assert (NM_IS_CONNECTION (reread));
+
+		if (   reread
+		    && !nm_connection_normalize (reread, NULL, NULL, NULL)) {
+			nm_assert_not_reached ();
+			g_clear_object (&reread);
+		}
+
+		if (reread && out_reread_same) {
+			reread_same = !!nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT);
+
+			nm_assert (reread_same == nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT));
+			nm_assert (reread_same == ({
+			                                gs_unref_hashtable GHashTable *_settings = NULL;
+
+			                                (   nm_connection_diff (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT, &_settings)
+			                                 && !_settings);
+			                           }));
+		}
+
+		NM_SET_OUT (out_reread, g_steal_pointer (&reread));
+		NM_SET_OUT (out_reread_same, reread_same);
+	}
+
 	return TRUE;
 }
 
 gboolean
 nms_keyfile_writer_connection (NMConnection *connection,
-                          const char *existing_path,
-                          gboolean force_rename,
-                          char **out_path,
-                          GError **error)
+                               const char *existing_path,
+                               gboolean force_rename,
+                               char **out_path,
+                               NMConnection **out_reread,
+                               gboolean *out_reread_same,
+                               GError **error)
 {
 	return _internal_write_connection (connection,
 	                                   nms_keyfile_utils_get_path (),
@@ -306,16 +348,20 @@ nms_keyfile_writer_connection (NMConnection *connection,
 	                                   existing_path,
 	                                   force_rename,
 	                                   out_path,
+	                                   out_reread,
+	                                   out_reread_same,
 	                                   error);
 }
 
 gboolean
 nms_keyfile_writer_test_connection (NMConnection *connection,
-                               const char *keyfile_dir,
-                               uid_t owner_uid,
-                               pid_t owner_grp,
-                               char **out_path,
-                               GError **error)
+                                    const char *keyfile_dir,
+                                    uid_t owner_uid,
+                                    pid_t owner_grp,
+                                    char **out_path,
+                                    NMConnection **out_reread,
+                                    gboolean *out_reread_same,
+                                    GError **error)
 {
 	return _internal_write_connection (connection,
 	                                   keyfile_dir,
@@ -323,6 +369,8 @@ nms_keyfile_writer_test_connection (NMConnection *connection,
 	                                   NULL,
 	                                   FALSE,
 	                                   out_path,
+	                                   out_reread,
+	                                   out_reread_same,
 	                                   error);
 }
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.h b/src/settings/plugins/keyfile/nms-keyfile-writer.h
index 4f43455d..ac41dfa2 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.h
@@ -22,12 +22,14 @@
 #ifndef __NMS_KEYFILE_WRITER_H__
 #define __NMS_KEYFILE_WRITER_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 gboolean nms_keyfile_writer_connection (NMConnection *connection,
                                         const char *existing_path,
                                         gboolean force_rename,
                                         char **out_path,
+                                        NMConnection **out_reread,
+                                        gboolean *out_reread_same,
                                         GError **error);
 
 gboolean nms_keyfile_writer_test_connection (NMConnection *connection,
@@ -35,6 +37,8 @@ gboolean nms_keyfile_writer_test_connection (NMConnection *connection,
                                              uid_t owner_uid,
                                              pid_t owner_grp,
                                              char **out_path,
+                                             NMConnection **out_reread,
+                                             gboolean *out_reread_same,
                                              GError **error);
 
 #endif /* __NMS_KEYFILE_WRITER_H__ */
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
index de8373be..5cb4d726 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
@@ -34,6 +34,7 @@ routes8=1.1.1.8/18,0.0.0.0,
 routes9=1.1.1.9/19,0.0.0.0,0
 routes10=1.1.1.10/20,,0
 routes11=1.1.1.11/21,,21
+routes11_options=cwnd=10,lock-cwnd=true,mtu=1430,src=7.7.7.7
 ignore-auto-routes=false
 ignore-auto-dns=false
 
@@ -58,5 +59,6 @@ route3=6:7:8:9:0:1:2:3/126,,1
 route4=7:8:9:0:1:2:3:4/125/::,5
 route5=8:9:0:1:2:3:4:5/124,6
 route6=8:9:0:1:2:3:4:6/123,,
+route6_options=from=abce::/63
 ignore-auto-routes=false
 ignore-auto-dns=false
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index f1102bd3..d9da5317 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -113,13 +113,25 @@ assert_reread_and_unlink (NMConnection *connection, gboolean normalize_connectio
 }
 
 static void
-write_test_connection (NMConnection *connection, char **testfile)
+assert_reread_same (NMConnection *connection,
+                    NMConnection *reread)
+{
+	nmtst_assert_connection_verifies_without_normalization (reread);
+	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
+}
+
+static void
+write_test_connection_reread (NMConnection *connection,
+                              char **testfile,
+                              NMConnection **out_reread,
+                              gboolean *out_reread_same)
 {
 	uid_t owner_uid;
 	gid_t owner_grp;
 	gboolean success;
 	GError *error = NULL;
 	GError **p_error = (nmtst_get_rand_int () % 2) ? &error : NULL;
+	gs_unref_object NMConnection *connection_normalized = NULL;
 
 	g_assert (NM_IS_CONNECTION (connection));
 	g_assert (testfile && !*testfile);
@@ -127,13 +139,33 @@ write_test_connection (NMConnection *connection, char **testfile)
 	owner_uid = geteuid ();
 	owner_grp = getegid ();
 
-	success = nms_keyfile_writer_test_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, testfile, p_error);
+	connection_normalized = nmtst_connection_duplicate_and_normalize (connection);
+
+	success = nms_keyfile_writer_test_connection (connection_normalized,
+	                                              TEST_SCRATCH_DIR,
+	                                              owner_uid,
+	                                              owner_grp,
+	                                              testfile,
+	                                              out_reread,
+	                                              out_reread_same,
+	                                              p_error);
 	g_assert_no_error (error);
 	g_assert (success);
 	g_assert (*testfile && (*testfile)[0]);
 }
 
 static void
+write_test_connection (NMConnection *connection, char **testfile)
+{
+	gs_unref_object NMConnection *reread = NULL;
+	gboolean reread_same = FALSE;
+
+	write_test_connection_reread (connection, testfile, &reread, &reread_same);
+	assert_reread_same (connection, reread);
+	g_assert (reread_same);
+}
+
+static void
 write_test_connection_and_reread (NMConnection *connection, gboolean normalize_connection)
 {
 	gs_free char *testfile = NULL;
@@ -161,6 +193,27 @@ keyfile_load_from_file (const char *testfile)
 	return keyfile;
 }
 
+static void
+_setting_copy_property_gbytes (NMConnection *src, NMConnection *dst, const char *setting_name, const char *property_name)
+{
+	gs_unref_bytes GBytes *blob = NULL;
+	NMSetting *s_src;
+	NMSetting *s_dst;
+
+	g_assert (NM_IS_CONNECTION (src));
+	g_assert (NM_IS_CONNECTION (dst));
+	g_assert (setting_name);
+	g_assert (property_name);
+
+	s_src = nm_connection_get_setting_by_name (src, setting_name);
+	g_assert (NM_IS_SETTING (s_src));
+	s_dst = nm_connection_get_setting_by_name (dst, setting_name);
+	g_assert (NM_IS_SETTING (s_dst));
+
+	g_object_get (s_src, property_name, &blob, NULL);
+	g_object_set (s_dst, property_name, blob, NULL);
+}
+
 /*****************************************************************************/
 
 static void
@@ -171,6 +224,7 @@ test_read_valid_wired_connection (void)
 	NMSettingWired *s_wired;
 	NMSettingIPConfig *s_ip4;
 	NMSettingIPConfig *s_ip6;
+	NMIPRoute *route;
 	gs_free_error GError *error = NULL;
 	const char *mac;
 	char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
@@ -265,6 +319,15 @@ test_read_valid_wired_connection (void)
 	check_ip_route (s_ip4, 10, "1.1.1.10", 20, NULL, -1);
 	check_ip_route (s_ip4, 11, "1.1.1.11", 21, NULL, 21);
 
+	/* Route attributes */
+	route = nm_setting_ip_config_get_route (s_ip4, 11);
+	g_assert (route);
+
+	nmtst_assert_route_attribute_uint32  (route, NM_IP_ROUTE_ATTRIBUTE_CWND, 10);
+	nmtst_assert_route_attribute_uint32  (route, NM_IP_ROUTE_ATTRIBUTE_MTU, 1430);
+	nmtst_assert_route_attribute_boolean (route, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, TRUE);
+	nmtst_assert_route_attribute_string  (route, NM_IP_ROUTE_ATTRIBUTE_SRC, "7.7.7.7");
+
 	/* ===== IPv6 SETTING ===== */
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	g_assert (s_ip6);
@@ -304,6 +367,11 @@ test_read_valid_wired_connection (void)
 	check_ip_route (s_ip6, 4, "7:8:9:0:1:2:3:4", 125, NULL, 5);
 	check_ip_route (s_ip6, 5, "8:9:0:1:2:3:4:5", 124, NULL, 6);
 	check_ip_route (s_ip6, 6, "8:9:0:1:2:3:4:6", 123, NULL, -1);
+
+	/* Route attributes */
+	route = nm_setting_ip_config_get_route (s_ip6, 6);
+	g_assert (route);
+	nmtst_assert_route_attribute_string (route, NM_IP_ROUTE_ATTRIBUTE_FROM, "abce::/63");
 }
 
 static void
@@ -349,6 +417,7 @@ test_write_wired_connection (void)
 	NMSettingWired *s_wired;
 	NMSettingIPConfig *s_ip4;
 	NMSettingIPConfig *s_ip6;
+	NMIPRoute *rt;
 	const char *mac = "99:88:77:66:55:44";
 	const char *dns1 = "4.2.2.1";
 	const char *dns2 = "4.2.2.2";
@@ -376,6 +445,7 @@ test_write_wired_connection (void)
 	const char *route6_4 = "5:6:7:8:9:0:1:2";
 	const char *route6_4_nh = "::";
 	guint64 timestamp = 0x12345678L;
+	GError *error = NULL;
 
 	connection = nm_simple_connection_new ();
 
@@ -420,7 +490,14 @@ test_write_wired_connection (void)
 	add_one_ip_route (s_ip4, route1, route1_nh, 24, 3);
 	add_one_ip_route (s_ip4, route2, route2_nh, 8, 1);
 	add_one_ip_route (s_ip4, route3, route3_nh, 7, -1);
-	add_one_ip_route (s_ip4, route4, route4_nh, 6, 4);
+
+	rt = nm_ip_route_new (AF_INET, route4, 6, route4_nh, 4, &error);
+	g_assert_no_error (error);
+	nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (10));
+	nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1492));
+	nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string ("1.2.3.4"));
+	g_assert (nm_setting_ip_config_add_route (s_ip4, rt));
+	nm_ip_route_unref (rt);
 
 	/* DNS servers */
 	nm_setting_ip_config_add_dns (s_ip4, dns1);
@@ -1639,11 +1716,18 @@ test_write_wired_8021x_tls_connection_path (void)
 	gs_free_error GError *error = NULL;
 	gs_unref_keyfile GKeyFile *keyfile = NULL;
 	gboolean relative = FALSE;
+	gboolean reread_same = FALSE;
 
 	connection = create_wired_tls_connection (NM_SETTING_802_1X_CK_SCHEME_PATH);
 	g_assert (connection != NULL);
 
-	write_test_connection (connection, &testfile);
+	write_test_connection_reread (connection, &testfile, &reread, &reread_same);
+	nmtst_assert_connection_verifies_without_normalization (reread);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY);
+	assert_reread_same (connection, reread);
+	g_clear_object (&reread);
 
 	/* Read the connection back in and compare it to the one we just wrote out */
 	reread = nms_keyfile_reader_from_file (testfile, &error);
@@ -1716,6 +1800,7 @@ test_write_wired_8021x_tls_connection_blob (void)
 	char *new_client_cert;
 	char *new_priv_key;
 	const char *uuid;
+	gboolean reread_same = FALSE;
 	gs_free_error GError *error = NULL;
 	GBytes *password_raw = NULL;
 #define PASSWORD_RAW "password-raw\0test"
@@ -1733,7 +1818,13 @@ test_write_wired_8021x_tls_connection_blob (void)
 	              NULL);
 	g_bytes_unref (password_raw);
 
-	write_test_connection (connection, &testfile);
+	write_test_connection_reread (connection, &testfile, &reread, &reread_same);
+	nmtst_assert_connection_verifies_without_normalization (reread);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY);
+	assert_reread_same (connection, reread);
+	g_clear_object (&reread);
 
 	/* Check that the new certs got written out */
 	s_con = nm_connection_get_setting_connection (connection);
diff --git a/src/supplicant/nm-supplicant-config.c b/src/supplicant/nm-supplicant-config.c
index 8f766d7c..f9a84620 100644
--- a/src/supplicant/nm-supplicant-config.c
+++ b/src/supplicant/nm-supplicant-config.c
@@ -28,6 +28,7 @@
 
 #include "nm-supplicant-settings-verify.h"
 #include "nm-setting.h"
+#include "nm-auth-subject.h"
 #include "NetworkManagerUtils.h"
 #include "nm-utils.h"
 
@@ -828,6 +829,53 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
 	return TRUE;
 }
 
+static gboolean
+add_pkcs11_uri_with_pin (NMSupplicantConfig *self,
+                         const char *name,
+                         const char *uri,
+                         const char *pin,
+                         const NMSettingSecretFlags pin_flags,
+                         GError **error)
+{
+	gs_strfreev gchar **split = NULL;
+	gs_free char *tmp = NULL;
+	gs_free char *tmp_log = NULL;
+	gs_free char *pin_qattr = NULL;
+	char *escaped = NULL;
+
+	if (uri == NULL)
+		return TRUE;
+
+	/* We ignore the attributes -- RFC 7512 suggests that some of them
+	 * might be unsafe and we want to be on the safe side. Also, we're
+	 * installing our attributes, so this makes things a bit easier for us. */
+	split = g_strsplit (uri, "&", 2);
+	if (split[1])
+		nm_log_info (LOGD_SUPPLICANT, "URI attributes ignored");
+
+	/* Fill in the PIN if required. */
+	if (pin) {
+		escaped = g_uri_escape_string (pin, NULL, TRUE);
+		pin_qattr = g_strdup_printf ("pin-value=%s", escaped);
+		g_free (escaped);
+	} else if (!(pin_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
+		/* Include an empty PIN to indicate the login is still needed.
+		 * Probably a token that has a PIN path and the actual PIN will
+		 * be entered using a protected path. */
+		pin_qattr = g_strdup ("pin-value=");
+	}
+
+	tmp = g_strdup_printf ("%s%s%s", split[0],
+	                       (pin_qattr ? "&" : ""),
+	                       (pin_qattr ? pin_qattr : ""));
+
+	tmp_log = g_strdup_printf ("%s%s%s", split[0],
+	                           (pin_qattr ? "&" : ""),
+	                           (pin_qattr ? "pin-value=<hidden>" : ""));
+
+	return add_string_val (self, tmp, name, FALSE, tmp_log, error);
+}
+
 gboolean
 nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
                                         NMSetting8021x *setting,
@@ -848,6 +896,7 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
 	const char *ca_path_override = NULL, *ca_cert_override = NULL;
 	guint32 frag, hdrs;
 	gs_free char *frag_str = NULL;
+	NMSetting8021xAuthFlags phase1_auth_flags;
 
 	g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
 	g_return_val_if_fail (setting != NULL, FALSE);
@@ -934,6 +983,14 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
 			fast_provisoning_allowed = TRUE;
 	}
 
+	phase1_auth_flags = nm_setting_802_1x_get_phase1_auth_flags (setting);
+	if (NM_FLAGS_HAS (phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE))
+		g_string_append_printf (phase1, "%stls_disable_tlsv1_0=1", (phase1->len ? " " : ""));
+	if (NM_FLAGS_HAS (phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE))
+		g_string_append_printf (phase1, "%stls_disable_tlsv1_1=1", (phase1->len ? " " : ""));
+	if (NM_FLAGS_HAS (phase1_auth_flags, NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE))
+		g_string_append_printf (phase1, "%stls_disable_tlsv1_2=1", (phase1->len ? " " : ""));
+
 	if (phase1->len) {
 		if (!add_string_val (self, phase1->str, "phase1", FALSE, NULL, error)) {
 			g_string_free (phase1, TRUE);
@@ -1033,9 +1090,13 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
 				return FALSE;
 			break;
 		case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
-			path = nm_setting_802_1x_get_ca_cert_uri (setting);
-			if (!add_string_val (self, path, "ca_cert", FALSE, NULL, error))
+			if (!add_pkcs11_uri_with_pin (self, "ca_cert",
+			                              nm_setting_802_1x_get_ca_cert_uri (setting),
+			                              nm_setting_802_1x_get_ca_cert_password (setting),
+			                              nm_setting_802_1x_get_ca_cert_password_flags (setting),
+			                              error)) {
 				return FALSE;
+			}
 			break;
 		default:
 			break;
@@ -1059,9 +1120,13 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
 				return FALSE;
 			break;
 		case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
-			path = nm_setting_802_1x_get_phase2_ca_cert_uri (setting);
-			if (!add_string_val (self, path, "ca_cert2", FALSE, NULL, error))
+			if (!add_pkcs11_uri_with_pin (self, "ca_cert2",
+			                              nm_setting_802_1x_get_phase2_ca_cert_uri (setting),
+			                              nm_setting_802_1x_get_phase2_ca_cert_password (setting),
+			                              nm_setting_802_1x_get_phase2_ca_cert_password_flags (setting),
+			                              error)) {
 				return FALSE;
+			}
 			break;
 		default:
 			break;
@@ -1106,9 +1171,13 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
 		added = TRUE;
 		break;
 	case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
-		path = nm_setting_802_1x_get_private_key_uri (setting);
-		if (!add_string_val (self, path, "private_key", FALSE, NULL, error))
+		if (!add_pkcs11_uri_with_pin (self, "private_key",
+		                              nm_setting_802_1x_get_private_key_uri (setting),
+		                              nm_setting_802_1x_get_private_key_password (setting),
+		                              nm_setting_802_1x_get_private_key_password_flags (setting),
+		                              error)) {
 			return FALSE;
+		}
 		added = TRUE;
 		break;
 	default:
@@ -1149,9 +1218,13 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
 					return FALSE;
 				break;
 			case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
-				path = nm_setting_802_1x_get_client_cert_uri (setting);
-				if (!add_string_val (self, path, "client_cert", FALSE, NULL, error))
+				if (!add_pkcs11_uri_with_pin (self, "client_cert",
+				                              nm_setting_802_1x_get_client_cert_uri (setting),
+				                              nm_setting_802_1x_get_client_cert_password (setting),
+				                              nm_setting_802_1x_get_client_cert_password_flags (setting),
+				                              error)) {
 					return FALSE;
+				}
 				break;
 			default:
 				break;
@@ -1175,9 +1248,13 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
 		added = TRUE;
 		break;
 	case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
-		path = nm_setting_802_1x_get_phase2_private_key_uri (setting);
-		if (!add_string_val (self, path, "private_key2", FALSE, NULL, error))
+		if (!add_pkcs11_uri_with_pin (self, "private_key2",
+		                              nm_setting_802_1x_get_phase2_private_key_uri (setting),
+		                              nm_setting_802_1x_get_phase2_private_key_password (setting),
+		                              nm_setting_802_1x_get_phase2_private_key_password_flags (setting),
+		                              error)) {
 			return FALSE;
+		}
 		added = TRUE;
 		break;
 	default:
@@ -1218,9 +1295,13 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
 					return FALSE;
 				break;
 			case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
-				path = nm_setting_802_1x_get_phase2_client_cert_uri (setting);
-				if (!add_string_val (self, path, "client_cert2", FALSE, NULL, error))
+				if (!add_pkcs11_uri_with_pin (self, "client_cert2",
+				                              nm_setting_802_1x_get_phase2_client_cert_uri (setting),
+				                              nm_setting_802_1x_get_phase2_client_cert_password (setting),
+				                              nm_setting_802_1x_get_phase2_client_cert_password_flags (setting),
+				                              error)) {
 					return FALSE;
+				}
 				break;
 			default:
 				break;
diff --git a/src/supplicant/nm-supplicant-config.h b/src/supplicant/nm-supplicant-config.h
index 40fca61b..6acfb7ee 100644
--- a/src/supplicant/nm-supplicant-config.h
+++ b/src/supplicant/nm-supplicant-config.h
@@ -22,10 +22,10 @@
 #ifndef __NETWORKMANAGER_SUPPLICANT_CONFIG_H__
 #define __NETWORKMANAGER_SUPPLICANT_CONFIG_H__
 
-#include <nm-setting-macsec.h>
-#include <nm-setting-wireless.h>
-#include <nm-setting-wireless-security.h>
-#include <nm-setting-8021x.h>
+#include "nm-setting-macsec.h"
+#include "nm-setting-wireless.h"
+#include "nm-setting-wireless-security.h"
+#include "nm-setting-8021x.h"
 
 #include "nm-supplicant-types.h"
 
diff --git a/src/supplicant/nm-supplicant-interface.c b/src/supplicant/nm-supplicant-interface.c
index f932fe8b..71f1f9aa 100644
--- a/src/supplicant/nm-supplicant-interface.c
+++ b/src/supplicant/nm-supplicant-interface.c
@@ -39,14 +39,35 @@
 
 /*****************************************************************************/
 
+typedef struct {
+	GDBusProxy *proxy;
+	gulong change_id;
+} BssData;
+
+struct _AddNetworkData;
+
+typedef struct {
+	NMSupplicantInterface *self;
+	NMSupplicantConfig *cfg;
+	GCancellable *cancellable;
+	NMSupplicantInterfaceAssocCb callback;
+	gpointer user_data;
+	guint fail_on_idle_id;
+	guint blobs_left;
+	struct _AddNetworkData *add_network_data;
+} AssocData;
+
+typedef struct _AddNetworkData {
+	/* the assoc_data at the time when doing the call. */
+	AssocData *assoc_data;
+} AddNetworkData;
+
 enum {
 	STATE,               /* change in the interface's state */
 	REMOVED,             /* interface was removed by the supplicant */
-	NEW_BSS,             /* interface saw a new access point from a scan */
-	BSS_UPDATED,         /* a BSS property changed */
+	BSS_UPDATED,         /* a new BSS appeared or an existing had properties changed */
 	BSS_REMOVED,         /* supplicant removed BSS from its scan list */
 	SCAN_DONE,           /* wifi scan is complete */
-	CONNECTION_ERROR,    /* an error occurred during a connection request */
 	CREDENTIALS_REQUEST, /* 802.1x identity or password requested */
 	LAST_SIGNAL
 };
@@ -71,24 +92,27 @@ typedef struct {
 	guint32        ready_count;
 
 	char *         object_path;
-	guint32        state;
+	NMSupplicantInterfaceState state;
 	int            disconnect_reason;
 
-	gboolean       scanning;
+	gboolean       scanning:1;
+
+	bool           scan_done_pending:1;
+	bool           scan_done_success:1;
 
 	GDBusProxy *   wpas_proxy;
 	GCancellable * init_cancellable;
 	GDBusProxy *   iface_proxy;
 	GCancellable * other_cancellable;
-	GCancellable * assoc_cancellable;
+
+	AssocData *    assoc_data;
+
 	char *         net_path;
-	guint32        blobs_left;
 	GHashTable *   bss_proxies;
 	char *         current_bss;
 
 	gint32         last_scan; /* timestamp as returned by nm_utils_get_monotonic_timestamp_s() */
 
-	NMSupplicantConfig *cfg;
 } NMSupplicantInterfacePrivate;
 
 struct _NMSupplicantInterface {
@@ -111,38 +135,57 @@ G_DEFINE_TYPE (NMSupplicantInterface, nm_supplicant_interface, G_TYPE_OBJECT)
 #define _NMLOG(level, ...) \
     G_STMT_START { \
          char _sbuf[64]; \
+         const char *__ifname = self ?  NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev : NULL; \
          \
-         nm_log ((level), _NMLOG_DOMAIN, \
+         nm_log ((level), _NMLOG_DOMAIN, __ifname, NULL, \
                  "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                  _NMLOG_PREFIX_NAME, \
-                 ((self) \
-                      ? nm_sprintf_buf (_sbuf, \
-                                        "[%p,%s]", \
-                                        (self), \
-                                        NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev) \
-                      : "") \
+                 ((self) ? nm_sprintf_buf (_sbuf, "[%p,%s]", (self), __ifname) : "") \
                  _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
     } G_STMT_END
 
 /*****************************************************************************/
 
+static void scan_done_emit_signal (NMSupplicantInterface *self);
+
+/*****************************************************************************/
+
+NM_UTILS_LOOKUP_STR_DEFINE (nm_supplicant_interface_state_to_string, NMSupplicantInterfaceState,
+	NM_UTILS_LOOKUP_DEFAULT_WARN ("unknown"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INVALID,         "invalid"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INIT,            "init"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_STARTING,        "starting"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_READY,           "ready"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISABLED,        "disabled"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED,    "disconnected"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_INACTIVE,        "inactive"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_SCANNING,        "scanning"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING,  "authenticating"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING,     "associating"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED,      "associated"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE,  "4-way handshake"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE, "group handshake"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_COMPLETED,       "completed"),
+	NM_UTILS_LOOKUP_STR_ITEM (NM_SUPPLICANT_INTERFACE_STATE_DOWN,            "down"),
+);
+
+/*****************************************************************************/
+
 static void
-emit_error_helper (NMSupplicantInterface *self, GError *error)
+bss_data_destroy (gpointer user_data)
 {
-	char *name = NULL;
+	BssData *bss_data = user_data;
 
-	if (g_dbus_error_is_remote_error (error))
-		name = g_dbus_error_get_remote_error (error);
-
-	g_signal_emit (self, signals[CONNECTION_ERROR], 0, name, error->message);
-	g_free (name);
+	nm_clear_g_signal_handler (bss_data->proxy, &bss_data->change_id);
+	g_object_unref (bss_data->proxy);
+	g_slice_free (BssData, bss_data);
 }
 
 static void
-bss_props_changed_cb (GDBusProxy *proxy,
-                      GVariant *changed_properties,
-                      char **invalidated_properties,
-                      gpointer user_data)
+bss_proxy_properties_changed_cb (GDBusProxy *proxy,
+                                 GVariant *changed_properties,
+                                 char **invalidated_properties,
+                                 gpointer user_data)
 {
 	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
@@ -156,63 +199,73 @@ bss_props_changed_cb (GDBusProxy *proxy,
 }
 
 static GVariant *
-_get_bss_proxy_properties (NMSupplicantInterface *self, GDBusProxy *proxy)
+bss_proxy_get_properties (NMSupplicantInterface *self, GDBusProxy *proxy)
 {
 	gs_strfreev char **properties = NULL;
 	GVariantBuilder builder;
 	char **iter;
 
 	iter = properties = g_dbus_proxy_get_cached_property_names (proxy);
-	if (!iter)
-		return NULL;
 
 	g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
-	while (*iter) {
-		GVariant *copy = g_dbus_proxy_get_cached_property (proxy, *iter);
+	if (iter) {
+		while (*iter) {
+			GVariant *copy = g_dbus_proxy_get_cached_property (proxy, *iter);
 
-		g_variant_builder_add (&builder, "{sv}", *iter++, copy);
-		g_variant_unref (copy);
+			g_variant_builder_add (&builder, "{sv}", *iter++, copy);
+			g_variant_unref (copy);
+		}
 	}
-
 	return g_variant_builder_end (&builder);
 }
 
-#define BSS_PROXY_INITED "bss-proxy-inited"
-
 static void
-on_bss_proxy_acquired (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+bss_proxy_acquired_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
 	NMSupplicantInterface *self;
+	NMSupplicantInterfacePrivate *priv;
 	gs_free_error GError *error = NULL;
-	gs_unref_variant GVariant *props = NULL;
+	GVariant *props = NULL;
+	const char *object_path;
+	BssData *bss_data;
 
-	if (!g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error)) {
-		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-			self = NM_SUPPLICANT_INTERFACE (user_data);
-			_LOGD ("failed to acquire BSS proxy: (%s)", error->message);
-			g_hash_table_remove (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->bss_proxies,
-			                     g_dbus_proxy_get_object_path (proxy));
-		}
+	g_async_initable_init_finish (G_ASYNC_INITABLE (proxy), result, &error);
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
-	}
 
 	self = NM_SUPPLICANT_INTERFACE (user_data);
-	props = _get_bss_proxy_properties (self, proxy);
-	if (!props)
+	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	if (error) {
+		_LOGD ("failed to acquire BSS proxy: (%s)", error->message);
+		g_hash_table_remove (priv->bss_proxies,
+		                     g_dbus_proxy_get_object_path (proxy));
 		return;
+	}
 
-	g_object_set_data (G_OBJECT (proxy), BSS_PROXY_INITED, GUINT_TO_POINTER (TRUE));
+	object_path = g_dbus_proxy_get_object_path (proxy);
+	bss_data = g_hash_table_lookup (priv->bss_proxies, object_path);
+	if (!bss_data)
+		return;
 
-	g_signal_emit (self, signals[NEW_BSS], 0,
+	bss_data->change_id = g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (bss_proxy_properties_changed_cb), self);
+
+	props = bss_proxy_get_properties (self, proxy);
+	g_signal_emit (self, signals[BSS_UPDATED], 0,
 	               g_dbus_proxy_get_object_path (proxy),
 	               g_variant_ref_sink (props));
+	g_variant_unref (props);
+
+	if (priv->scan_done_pending)
+		scan_done_emit_signal (self);
 }
 
 static void
-handle_new_bss (NMSupplicantInterface *self, const char *object_path)
+bss_add_new (NMSupplicantInterface *self, const char *object_path)
 {
 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 	GDBusProxy *bss_proxy;
+	BssData *bss_data;
 
 	g_return_if_fail (object_path != NULL);
 
@@ -226,24 +279,25 @@ handle_new_bss (NMSupplicantInterface *self, const char *object_path)
 	                          "g-object-path", object_path,
 	                          "g-interface-name", WPAS_DBUS_IFACE_BSS,
 	                          NULL);
+	bss_data = g_slice_new0 (BssData);
+	bss_data->proxy = bss_proxy;
 	g_hash_table_insert (priv->bss_proxies,
 	                     (char *) g_dbus_proxy_get_object_path (bss_proxy),
-	                     bss_proxy);
-	g_signal_connect (bss_proxy, "g-properties-changed", G_CALLBACK (bss_props_changed_cb), self);
+	                     bss_data);
 	g_async_initable_init_async (G_ASYNC_INITABLE (bss_proxy),
 	                             G_PRIORITY_DEFAULT,
 	                             priv->other_cancellable,
-	                             (GAsyncReadyCallback) on_bss_proxy_acquired,
+	                             (GAsyncReadyCallback) bss_proxy_acquired_cb,
 	                             self);
 }
 
+/*****************************************************************************/
+
 static void
-set_state (NMSupplicantInterface *self, guint32 new_state)
+set_state (NMSupplicantInterface *self, NMSupplicantInterfaceState new_state)
 {
 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
-	guint32 old_state = priv->state;
-
-	g_return_if_fail (new_state < NM_SUPPLICANT_INTERFACE_STATE_LAST);
+	NMSupplicantInterfaceState old_state = priv->state;
 
 	if (new_state == priv->state)
 		return;
@@ -256,20 +310,11 @@ set_state (NMSupplicantInterface *self, guint32 new_state)
 		g_return_if_fail (new_state > NM_SUPPLICANT_INTERFACE_STATE_READY);
 
 	if (new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) {
-		if (priv->other_cancellable) {
-			g_warn_if_fail (priv->other_cancellable == NULL);
-			g_cancellable_cancel (priv->other_cancellable);
-			g_clear_object (&priv->other_cancellable);
-		}
+		nm_clear_g_cancellable (&priv->other_cancellable);
 		priv->other_cancellable = g_cancellable_new ();
 	} else if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
-		if (priv->init_cancellable)
-			g_cancellable_cancel (priv->init_cancellable);
-		g_clear_object (&priv->init_cancellable);
-
-		if (priv->other_cancellable)
-			g_cancellable_cancel (priv->other_cancellable);
-		g_clear_object (&priv->other_cancellable);
+		nm_clear_g_cancellable (&priv->init_cancellable);
+		nm_clear_g_cancellable (&priv->other_cancellable);
 
 		if (priv->iface_proxy)
 			g_signal_handlers_disconnect_by_data (priv->iface_proxy, self);
@@ -286,12 +331,12 @@ set_state (NMSupplicantInterface *self, guint32 new_state)
 		priv->disconnect_reason = 0;
 
 	g_signal_emit (self, signals[STATE], 0,
-	               priv->state,
-	               old_state,
-	               priv->disconnect_reason);
+	               (int) priv->state,
+	               (int) old_state,
+	               (int) priv->disconnect_reason);
 }
 
-static int
+static NMSupplicantInterfaceState
 wpas_state_string_to_enum (const char *str_state)
 {
 	if (!strcmp (str_state, "interface_disabled"))
@@ -315,20 +360,20 @@ wpas_state_string_to_enum (const char *str_state)
 	else if (!strcmp (str_state, "completed"))
 		return NM_SUPPLICANT_INTERFACE_STATE_COMPLETED;
 
-	return -1;
+	return NM_SUPPLICANT_INTERFACE_STATE_INVALID;
 }
 
 static void
 set_state_from_string (NMSupplicantInterface *self, const char *new_state)
 {
-	int state;
+	NMSupplicantInterfaceState state;
 
 	state = wpas_state_string_to_enum (new_state);
-	if (state == -1) {
+	if (state == NM_SUPPLICANT_INTERFACE_STATE_INVALID) {
 		_LOGW ("unknown supplicant state '%s'", new_state);
 		return;
 	}
-	set_state (self, (guint32) state);
+	set_state (self, state);
 }
 
 static void
@@ -548,38 +593,56 @@ iface_introspect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data
 }
 
 static void
-wpas_iface_scan_done (GDBusProxy *proxy,
-                      gboolean success,
-                      gpointer user_data)
+scan_done_emit_signal (NMSupplicantInterface *self)
 {
-	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
-	GVariant *props;
+	const char *object_path;
+	BssData *bss_data;
+	gboolean success;
 	GHashTableIter iter;
-	char *bss_path;
-	GDBusProxy *bss_proxy;
 
-	/* Cache last scan completed time */
-	priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
-
-	/* Emit NEW_BSS so that wifi device has the APs (in case it removed them) */
 	g_hash_table_iter_init (&iter, priv->bss_proxies);
-	while (g_hash_table_iter_next (&iter, (gpointer) &bss_path, (gpointer) &bss_proxy)) {
-		if (g_object_get_data (G_OBJECT (bss_proxy), BSS_PROXY_INITED)) {
-			props = _get_bss_proxy_properties (self, bss_proxy);
-			if (props) {
-				g_signal_emit (self, signals[NEW_BSS], 0,
-				               bss_path,
-				               g_variant_ref_sink (props));
-				g_variant_unref (props);
-			}
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &bss_data)) {
+		/* we have some BSS' that need to be initialized first. Delay
+		 * emitting signal. */
+		if (!bss_data->change_id) {
+			priv->scan_done_pending = TRUE;
+			return;
 		}
 	}
 
+	/* Emit BSS_UPDATED so that wifi device has the APs (in case it removed them) */
+	g_hash_table_iter_init (&iter, priv->bss_proxies);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &object_path, (gpointer *) &bss_data)) {
+		gs_unref_variant GVariant *props = NULL;
+
+		props = bss_proxy_get_properties (self, bss_data->proxy);
+		g_signal_emit (self, signals[BSS_UPDATED], 0,
+		               object_path,
+		               g_variant_ref_sink (props));
+	}
+
+	success = priv->scan_done_success;
+	priv->scan_done_success = FALSE;
+	priv->scan_done_pending = FALSE;
 	g_signal_emit (self, signals[SCAN_DONE], 0, success);
 }
 
 static void
+wpas_iface_scan_done (GDBusProxy *proxy,
+                      gboolean success,
+                      gpointer user_data)
+{
+	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	/* Cache last scan completed time */
+	priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
+	priv->scan_done_success |= success;
+	scan_done_emit_signal (self);
+}
+
+static void
 wpas_iface_bss_added (GDBusProxy *proxy,
                       const char *path,
                       GVariant *props,
@@ -591,7 +654,7 @@ wpas_iface_bss_added (GDBusProxy *proxy,
 	if (priv->scanning)
 		priv->last_scan = nm_utils_get_monotonic_timestamp_s ();
 
-	handle_new_bss (self, path);
+	bss_add_new (self, path);
 }
 
 static void
@@ -601,9 +664,14 @@ wpas_iface_bss_removed (GDBusProxy *proxy,
 {
 	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	BssData *bss_data;
 
+	bss_data = g_hash_table_lookup (priv->bss_proxies, path);
+	if (!bss_data)
+		return;
+	g_hash_table_steal (priv->bss_proxies, path);
 	g_signal_emit (self, signals[BSS_REMOVED], 0, path);
-	g_hash_table_remove (priv->bss_proxies, path);
+	bss_data_destroy (bss_data);
 }
 
 static void
@@ -651,7 +719,7 @@ props_changed_cb (GDBusProxy *proxy,
 	if (g_variant_lookup (changed_properties, "BSSs", "^a&o", &array)) {
 		iter = array;
 		while (*iter)
-			handle_new_bss (self, *iter++);
+			bss_add_new (self, *iter++);
 		g_free (array);
 	}
 
@@ -955,8 +1023,7 @@ interface_add (NMSupplicantInterface *self)
 	/* Move to starting to prevent double-calls of interface_add() */
 	set_state (self, NM_SUPPLICANT_INTERFACE_STATE_STARTING);
 
-	g_warn_if_fail (priv->init_cancellable == NULL);
-	g_clear_object (&priv->init_cancellable);
+	nm_clear_g_cancellable (&priv->init_cancellable);
 	priv->init_cancellable = g_cancellable_new ();
 
 	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
@@ -1009,6 +1076,39 @@ log_result_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	}
 }
 
+/*****************************************************************************/
+
+static void
+assoc_return (NMSupplicantInterface *self, GError *error, const char *message)
+{
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	AssocData *assoc_data;
+
+	assoc_data = g_steal_pointer (&priv->assoc_data);
+	if (!assoc_data)
+		return;
+
+	if (error) {
+		g_dbus_error_strip_remote_error (error);
+		_LOGW ("assoc[%p]: %s: %s", assoc_data, message, error->message);
+	} else
+		_LOGD ("assoc[%p]: association request successful", assoc_data);
+
+	if (assoc_data->add_network_data) {
+		/* signal that this request already completed */
+		assoc_data->add_network_data->assoc_data = NULL;
+	}
+
+	nm_clear_g_source (&assoc_data->fail_on_idle_id);
+	nm_clear_g_cancellable (&assoc_data->cancellable);
+
+	if (assoc_data->callback)
+		assoc_data->callback (self, error, assoc_data->user_data);
+
+	g_object_unref (assoc_data->cfg);
+	g_slice_free (AssocData, assoc_data);
+}
+
 void
 nm_supplicant_interface_disconnect (NMSupplicantInterface * self)
 {
@@ -1019,9 +1119,11 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self)
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
 	/* Cancel all pending calls related to a prior connection attempt */
-	if (priv->assoc_cancellable) {
-		g_cancellable_cancel (priv->assoc_cancellable);
-		g_clear_object (&priv->assoc_cancellable);
+	if (priv->assoc_data) {
+		gs_free GError *error = NULL;
+
+		nm_utils_error_set_cancelled (&error, FALSE, "NMSupplicantInterface");
+		assoc_return (self, error, "abort due to disconnect");
 	}
 
 	/* Don't do anything if there is no connection to the supplicant yet. */
@@ -1057,42 +1159,40 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self)
 }
 
 static void
-select_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+assoc_select_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
 	NMSupplicantInterface *self;
 	gs_unref_variant GVariant *reply = NULL;
 	gs_free_error GError *error = NULL;
 
 	reply = g_dbus_proxy_call_finish (proxy, result, &error);
-	if (   !reply
-	    && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-		self = NM_SUPPLICANT_INTERFACE (user_data);
-		g_dbus_error_strip_remote_error (error);
-		_LOGW ("couldn't select network config: %s", error->message);
-		emit_error_helper (self, error);
-	}
+	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+		return;
+
+	self = NM_SUPPLICANT_INTERFACE (user_data);
+	if (error)
+		assoc_return (self, error, "failure to select network config");
+	else
+		assoc_return (self, NULL, NULL);
 }
 
 static void
-call_select_network (NMSupplicantInterface *self)
+assoc_call_select_network (NMSupplicantInterface *self)
 {
 	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-	/* We only select the network after all blobs (if any) have been set */
-	if (priv->blobs_left == 0) {
-		g_dbus_proxy_call (priv->iface_proxy,
-		                   "SelectNetwork",
-		                   g_variant_new ("(o)", priv->net_path),
-		                   G_DBUS_CALL_FLAGS_NONE,
-		                   -1,
-		                   priv->assoc_cancellable,
-		                   (GAsyncReadyCallback) select_network_cb,
-		                   self);
-	}
+	g_dbus_proxy_call (priv->iface_proxy,
+	                   "SelectNetwork",
+	                   g_variant_new ("(o)", priv->net_path),
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   -1,
+	                   priv->assoc_data->cancellable,
+	                   (GAsyncReadyCallback) assoc_select_network_cb,
+	                   self);
 }
 
 static void
-add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+assoc_add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
 	NMSupplicantInterface *self;
 	NMSupplicantInterfacePrivate *priv;
@@ -1106,19 +1206,22 @@ add_blob_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	self = NM_SUPPLICANT_INTERFACE (user_data);
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-	priv->blobs_left--;
-	if (reply)
-		call_select_network (self);
-	else {
-		g_dbus_error_strip_remote_error (error);
-		_LOGW ("couldn't set network certificates: %s", error->message);
-		emit_error_helper (self, error);
+	if (error) {
+		assoc_return (self, error, "failure to set network certificates");
+		return;
 	}
+
+	priv->assoc_data->blobs_left--;
+	_LOGT ("assoc[%p]: blob added (%u left)", priv->assoc_data, priv->assoc_data->blobs_left);
+	if (priv->assoc_data->blobs_left == 0)
+		assoc_call_select_network (self);
 }
 
 static void
-add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+assoc_add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
+	AddNetworkData *add_network_data = user_data;
+	AssocData *assoc_data;
 	NMSupplicantInterface *self;
 	NMSupplicantInterfacePrivate *priv;
 	gs_unref_variant GVariant *reply = NULL;
@@ -1128,71 +1231,81 @@ add_network_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	const char *blob_name;
 	GByteArray *blob_data;
 
+	assoc_data = add_network_data->assoc_data;
+	if (assoc_data)
+		assoc_data->add_network_data = NULL;
+	g_slice_free (AddNetworkData, add_network_data);
+
 	reply = _nm_dbus_proxy_call_finish (proxy, result,
 	                                    G_VARIANT_TYPE ("(o)"),
 	                                    &error);
-	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+
+	if (!assoc_data) {
+		if (!error) {
+			gs_free char *net_path = NULL;
+
+			/* the assoc-request was already cancelled, but the AddNetwork request succeeded.
+			 * Cleanup the created network.
+			 *
+			 * This cleanup action does not work when NetworkManager is about to exit
+			 * and leaves the mainloop. During program shutdown, we may orphan networks. */
+			g_variant_get (reply, "(o)", &net_path);
+			g_dbus_proxy_call (proxy,
+			                   "RemoveNetwork",
+			                   g_variant_new ("(o)", net_path),
+			                   G_DBUS_CALL_FLAGS_NONE,
+			                   -1,
+			                   NULL,
+			                   NULL,
+			                   NULL);
+		}
 		return;
+	}
 
-	self = NM_SUPPLICANT_INTERFACE (user_data);
+	self = NM_SUPPLICANT_INTERFACE (assoc_data->self);
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-	g_free (priv->net_path);
-	priv->net_path = NULL;
-
 	if (error) {
-		g_dbus_error_strip_remote_error (error);
-		_LOGW ("adding network to supplicant failed: %s", error->message);
-		emit_error_helper (self, error);
+		assoc_return (self, error, "failure to add network");
 		return;
 	}
 
 	g_variant_get (reply, "(o)", &priv->net_path);
 
 	/* Send blobs first; otherwise jump to selecting the network */
-	blobs = nm_supplicant_config_get_blobs (priv->cfg);
-	priv->blobs_left = g_hash_table_size (blobs);
-
-	g_hash_table_iter_init (&iter, blobs);
-	while (g_hash_table_iter_next (&iter, (gpointer) &blob_name, (gpointer) &blob_data)) {
-		g_dbus_proxy_call (priv->iface_proxy,
-		                   "AddBlob",
-		                   g_variant_new ("(s@ay)",
-		                                  blob_name,
-		                                  g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
-		                                                             blob_data->data, blob_data->len, 1)),
-		                   G_DBUS_CALL_FLAGS_NONE,
-		                   -1,
-		                   priv->assoc_cancellable,
-		                   (GAsyncReadyCallback) add_blob_cb,
-		                   self);
-	}
+	blobs = nm_supplicant_config_get_blobs (priv->assoc_data->cfg);
+	priv->assoc_data->blobs_left = g_hash_table_size (blobs);
 
-	call_select_network (self);
-}
+	_LOGT ("assoc[%p]: network added (%s) (%u blobs left)", priv->assoc_data, priv->net_path, priv->assoc_data->blobs_left);
 
-static void
-add_network (NMSupplicantInterface *self)
-{
-	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
-
-	g_dbus_proxy_call (priv->iface_proxy,
-	                   "AddNetwork",
-	                   g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->cfg)),
-	                   G_DBUS_CALL_FLAGS_NONE,
-	                   -1,
-	                   priv->assoc_cancellable,
-	                   (GAsyncReadyCallback) add_network_cb,
-	                   self);
+	if (priv->assoc_data->blobs_left == 0)
+		assoc_call_select_network (self);
+	else {
+		g_hash_table_iter_init (&iter, blobs);
+		while (g_hash_table_iter_next (&iter, (gpointer) &blob_name, (gpointer) &blob_data)) {
+			g_dbus_proxy_call (priv->iface_proxy,
+			                   "AddBlob",
+			                   g_variant_new ("(s@ay)",
+			                                  blob_name,
+			                                  g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+			                                                             blob_data->data, blob_data->len, 1)),
+			                   G_DBUS_CALL_FLAGS_NONE,
+			                   -1,
+			                   priv->assoc_data->cancellable,
+			                   (GAsyncReadyCallback) assoc_add_blob_cb,
+			                   self);
+		}
+	}
 }
 
 static void
-set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
+assoc_set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
 	NMSupplicantInterface *self;
 	NMSupplicantInterfacePrivate *priv;
 	gs_unref_variant GVariant *reply = NULL;
 	gs_free_error GError *error = NULL;
+	AddNetworkData *add_network_data;
 
 	reply = g_dbus_proxy_call_finish (proxy, result, &error);
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
@@ -1201,62 +1314,109 @@ set_ap_scan_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	self = NM_SUPPLICANT_INTERFACE (user_data);
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-	if (!reply) {
-		g_dbus_error_strip_remote_error (error);
-		_LOGW ("couldn't send AP scan mode to the supplicant interface: %s",
-		       error->message);
-		emit_error_helper (self, error);
+	if (error) {
+		assoc_return (self, error, "failure to set AP scan mode");
 		return;
 	}
 
-	_LOGI ("config: set interface ap_scan to %d",
-	       nm_supplicant_config_get_ap_scan (priv->cfg));
+	_LOGT ("assoc[%p]: set interface ap_scan to %d",
+	       priv->assoc_data,
+	       nm_supplicant_config_get_ap_scan (priv->assoc_data->cfg));
 
-	add_network (self);
+	add_network_data = g_slice_new0 (AddNetworkData);
+	priv->assoc_data->add_network_data = add_network_data;
+
+	add_network_data->assoc_data = priv->assoc_data;
+
+	g_dbus_proxy_call (priv->iface_proxy,
+	                   "AddNetwork",
+	                   g_variant_new ("(@a{sv})", nm_supplicant_config_to_variant (priv->assoc_data->cfg)),
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   -1,
+	                   NULL,
+	                   (GAsyncReadyCallback) assoc_add_network_cb,
+	                   add_network_data);
 }
 
-gboolean
-nm_supplicant_interface_set_config (NMSupplicantInterface *self,
-                                    NMSupplicantConfig *cfg,
-                                    GError **error)
+static gboolean
+assoc_fail_on_idle_cb (gpointer user_data)
+{
+	NMSupplicantInterface *self = user_data;
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	gs_free_error GError *error = NULL;
+
+	priv->assoc_data->fail_on_idle_id = 0;
+	g_set_error (&error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
+	             "EAP-FAST is not supported by the supplicant");
+	assoc_return (self, error, "failure due to missing supplicant support");
+	return G_SOURCE_REMOVE;
+}
+
+/**
+ * nm_supplicant_interface_assoc:
+ * @self: the supplicant interface instance
+ * @cfg: the configuration with the data for the association
+ * @callback: callback invoked when the association completes or fails.
+ * @user_data: data for the callback.
+ *
+ * Calls AddNetwork and SelectNetwork to start associating according to @cfg.
+ *
+ * The callback is invoked exactly once (always) and always asynchronously.
+ * The pending association can be aborted via nm_supplicant_interface_disconnect()
+ * or by destroying @self. In that case, the @callback is invoked synchornously with
+ * an error reason indicating cancellation/disposing (see nm_utils_error_is_cancelled()).
+ */
+void
+nm_supplicant_interface_assoc (NMSupplicantInterface *self,
+                               NMSupplicantConfig *cfg,
+                               NMSupplicantInterfaceAssocCb callback,
+                               gpointer user_data)
 {
 	NMSupplicantInterfacePrivate *priv;
+	AssocData *assoc_data;
 
-	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
+	g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self));
+	g_return_if_fail (NM_IS_SUPPLICANT_CONFIG (cfg));
 
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
 	nm_supplicant_interface_disconnect (self);
 
+	assoc_data = g_slice_new0 (AssocData);
+	priv->assoc_data = assoc_data;
+
+	assoc_data->self = self;
+	assoc_data->cfg = g_object_ref (cfg);
+	assoc_data->callback = callback;
+	assoc_data->user_data = user_data;
+
+	_LOGD ("assoc[%p]: starting association...", assoc_data);
+
 	/* Make sure the supplicant supports EAP-FAST before trying to send
 	 * it an EAP-FAST configuration.
 	 */
 	if (   priv->fast_support == NM_SUPPLICANT_FEATURE_NO
 	    && nm_supplicant_config_fast_required (cfg)) {
-		g_set_error (error, NM_SUPPLICANT_ERROR, NM_SUPPLICANT_ERROR_CONFIG,
-		             "EAP-FAST is not supported by the supplicant");
-		return FALSE;
+		assoc_data->fail_on_idle_id = g_idle_add (assoc_fail_on_idle_cb, self);
+		return;
 	}
 
-	g_clear_object (&priv->cfg);
-	if (cfg) {
-		priv->assoc_cancellable = g_cancellable_new ();
-		priv->cfg = g_object_ref (cfg);
-		g_dbus_proxy_call (priv->iface_proxy,
-		                   DBUS_INTERFACE_PROPERTIES ".Set",
-		                   g_variant_new ("(ssv)",
-		                                  WPAS_DBUS_IFACE_INTERFACE,
-		                                  "ApScan",
-		                                  g_variant_new_uint32 (nm_supplicant_config_get_ap_scan (priv->cfg))),
-		                   G_DBUS_CALL_FLAGS_NONE,
-		                   -1,
-		                   priv->assoc_cancellable,
-		                   (GAsyncReadyCallback) set_ap_scan_cb,
-		                   self);
-	}
-	return TRUE;
+	assoc_data->cancellable = g_cancellable_new();
+	g_dbus_proxy_call (priv->iface_proxy,
+	                   DBUS_INTERFACE_PROPERTIES ".Set",
+	                   g_variant_new ("(ssv)",
+	                                  WPAS_DBUS_IFACE_INTERFACE,
+	                                  "ApScan",
+	                                  g_variant_new_uint32 (nm_supplicant_config_get_ap_scan (priv->assoc_data->cfg))),
+	                   G_DBUS_CALL_FLAGS_NONE,
+	                   -1,
+	                   priv->assoc_data->cancellable,
+	                   (GAsyncReadyCallback) assoc_set_ap_scan_cb,
+	                   self);
 }
 
+/*****************************************************************************/
+
 static void
 scan_request_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 {
@@ -1279,14 +1439,14 @@ scan_request_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 	}
 }
 
-gboolean
+void
 nm_supplicant_interface_request_scan (NMSupplicantInterface *self, const GPtrArray *ssids)
 {
 	NMSupplicantInterfacePrivate *priv;
 	GVariantBuilder builder;
 	guint i;
 
-	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
+	g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (self));
 
 	priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
@@ -1314,10 +1474,11 @@ nm_supplicant_interface_request_scan (NMSupplicantInterface *self, const GPtrArr
 	                   priv->other_cancellable,
 	                   (GAsyncReadyCallback) scan_request_cb,
 	                   self);
-	return TRUE;
 }
 
-guint32
+/*****************************************************************************/
+
+NMSupplicantInterfaceState
 nm_supplicant_interface_get_state (NMSupplicantInterface * self)
 {
 	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NM_SUPPLICANT_INTERFACE_STATE_DOWN);
@@ -1326,44 +1487,6 @@ nm_supplicant_interface_get_state (NMSupplicantInterface * self)
 }
 
 const char *
-nm_supplicant_interface_state_to_string (guint32 state)
-{
-	switch (state) {
-	case NM_SUPPLICANT_INTERFACE_STATE_INIT:
-		return "init";
-	case NM_SUPPLICANT_INTERFACE_STATE_STARTING:
-		return "starting";
-	case NM_SUPPLICANT_INTERFACE_STATE_READY:
-		return "ready";
-	case NM_SUPPLICANT_INTERFACE_STATE_DISABLED:
-		return "disabled";
-	case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED:
-		return "disconnected";
-	case NM_SUPPLICANT_INTERFACE_STATE_INACTIVE:
-		return "inactive";
-	case NM_SUPPLICANT_INTERFACE_STATE_SCANNING:
-		return "scanning";
-	case NM_SUPPLICANT_INTERFACE_STATE_AUTHENTICATING:
-		return "authenticating";
-	case NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING:
-		return "associating";
-	case NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED:
-		return "associated";
-	case NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE:
-		return "4-way handshake";
-	case NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE:
-		return "group handshake";
-	case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
-		return "completed";
-	case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
-		return "down";
-	default:
-		break;
-	}
-	return "unknown";
-}
-
-const char *
 nm_supplicant_interface_get_object_path (NMSupplicantInterface *self)
 {
 	g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NULL);
@@ -1389,29 +1512,25 @@ nm_supplicant_interface_get_max_scan_ssids (NMSupplicantInterface *self)
 
 /*****************************************************************************/
 
-NMSupplicantInterface *
-nm_supplicant_interface_new (const char *ifname,
-                             NMSupplicantDriver driver,
-                             NMSupplicantFeature fast_support,
-                             NMSupplicantFeature ap_support)
-{
-	g_return_val_if_fail (ifname != NULL, NULL);
-
-	return g_object_new (NM_TYPE_SUPPLICANT_INTERFACE,
-	                     NM_SUPPLICANT_INTERFACE_IFACE, ifname,
-	                     NM_SUPPLICANT_INTERFACE_DRIVER, (guint) driver,
-	                     NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support,
-	                     NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support,
-	                     NULL);
-}
-
 static void
-nm_supplicant_interface_init (NMSupplicantInterface * self)
+get_property (GObject *object,
+              guint prop_id,
+              GValue *value,
+              GParamSpec *pspec)
 {
-	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE ((NMSupplicantInterface *) object);
 
-	priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT;
-	priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+	switch (prop_id) {
+	case PROP_SCANNING:
+		g_value_set_boolean (value, priv->scanning);
+		break;
+	case PROP_CURRENT_BSS:
+		g_value_set_string (value, priv->current_bss);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -1447,30 +1566,42 @@ set_property (GObject *object,
 }
 
 static void
-get_property (GObject *object,
-              guint prop_id,
-              GValue *value,
-              GParamSpec *pspec)
+nm_supplicant_interface_init (NMSupplicantInterface * self)
 {
-	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE ((NMSupplicantInterface *) object);
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
 
-	switch (prop_id) {
-	case PROP_SCANNING:
-		g_value_set_boolean (value, priv->scanning);
-		break;
-	case PROP_CURRENT_BSS:
-		g_value_set_string (value, priv->current_bss);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT;
+	priv->bss_proxies = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, bss_data_destroy);
+}
+
+NMSupplicantInterface *
+nm_supplicant_interface_new (const char *ifname,
+                             NMSupplicantDriver driver,
+                             NMSupplicantFeature fast_support,
+                             NMSupplicantFeature ap_support)
+{
+	g_return_val_if_fail (ifname != NULL, NULL);
+
+	return g_object_new (NM_TYPE_SUPPLICANT_INTERFACE,
+	                     NM_SUPPLICANT_INTERFACE_IFACE, ifname,
+	                     NM_SUPPLICANT_INTERFACE_DRIVER, (guint) driver,
+	                     NM_SUPPLICANT_INTERFACE_FAST_SUPPORT, (int) fast_support,
+	                     NM_SUPPLICANT_INTERFACE_AP_SUPPORT, (int) ap_support,
+	                     NULL);
 }
 
 static void
 dispose (GObject *object)
 {
-	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE ((NMSupplicantInterface *) object);
+	NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (object);
+	NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+	if (priv->assoc_data) {
+		gs_free GError *error = NULL;
+
+		nm_utils_error_set_cancelled (&error, TRUE, "NMSupplicantInterface");
+		assoc_return (self, error, "cancelled due to dispose of supplicant interface");
+	}
 
 	if (priv->iface_proxy)
 		g_signal_handlers_disconnect_by_data (priv->iface_proxy, object);
@@ -1478,7 +1609,6 @@ dispose (GObject *object)
 
 	nm_clear_g_cancellable (&priv->init_cancellable);
 	nm_clear_g_cancellable (&priv->other_cancellable);
-	nm_clear_g_cancellable (&priv->assoc_cancellable);
 
 	g_clear_object (&priv->wpas_proxy);
 	g_clear_pointer (&priv->bss_proxies, (GDestroyNotify) g_hash_table_destroy);
@@ -1488,9 +1618,6 @@ dispose (GObject *object)
 	g_clear_pointer (&priv->object_path, g_free);
 	g_clear_pointer (&priv->current_bss, g_free);
 
-	g_clear_object (&priv->cfg);
-
-	/* Chain up to the parent class */
 	G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object);
 }
 
@@ -1550,7 +1677,7 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
 	                  G_SIGNAL_RUN_LAST,
 	                  0,
 	                  NULL, NULL, NULL,
-	                  G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INT);
+	                  G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
 
 	signals[REMOVED] =
 	    g_signal_new (NM_SUPPLICANT_INTERFACE_REMOVED,
@@ -1560,14 +1687,6 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
 	                  NULL, NULL, NULL,
 	                  G_TYPE_NONE, 0);
 
-	signals[NEW_BSS] =
-	    g_signal_new (NM_SUPPLICANT_INTERFACE_NEW_BSS,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_LAST,
-	                  0,
-	                  NULL, NULL, NULL,
-	                  G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT);
-
 	signals[BSS_UPDATED] =
 	    g_signal_new (NM_SUPPLICANT_INTERFACE_BSS_UPDATED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
@@ -1592,14 +1711,6 @@ nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
 	                  NULL, NULL, NULL,
 	                  G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
 
-	signals[CONNECTION_ERROR] =
-	    g_signal_new (NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_LAST,
-	                  0,
-	                  NULL, NULL, NULL,
-	                  G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
-
 	signals[CREDENTIALS_REQUEST] =
 	    g_signal_new (NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST,
 	                  G_OBJECT_CLASS_TYPE (object_class),
diff --git a/src/supplicant/nm-supplicant-interface.h b/src/supplicant/nm-supplicant-interface.h
index 2ef63d1e..d60d4a54 100644
--- a/src/supplicant/nm-supplicant-interface.h
+++ b/src/supplicant/nm-supplicant-interface.h
@@ -28,7 +28,8 @@
  * Supplicant interface states
  *   A mix of wpa_supplicant interface states and internal states.
  */
-enum {
+typedef enum {
+	NM_SUPPLICANT_INTERFACE_STATE_INVALID = -1,
 	NM_SUPPLICANT_INTERFACE_STATE_INIT = 0,
 	NM_SUPPLICANT_INTERFACE_STATE_STARTING,
 	NM_SUPPLICANT_INTERFACE_STATE_READY,
@@ -43,8 +44,7 @@ enum {
 	NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE,
 	NM_SUPPLICANT_INTERFACE_STATE_COMPLETED,
 	NM_SUPPLICANT_INTERFACE_STATE_DOWN,
-	NM_SUPPLICANT_INTERFACE_STATE_LAST
-};
+} NMSupplicantInterfaceState;
 
 #define NM_TYPE_SUPPLICANT_INTERFACE            (nm_supplicant_interface_get_type ())
 #define NM_SUPPLICANT_INTERFACE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterface))
@@ -64,11 +64,9 @@ enum {
 /* Signals */
 #define NM_SUPPLICANT_INTERFACE_STATE            "state"
 #define NM_SUPPLICANT_INTERFACE_REMOVED          "removed"
-#define NM_SUPPLICANT_INTERFACE_NEW_BSS          "new-bss"
 #define NM_SUPPLICANT_INTERFACE_BSS_UPDATED      "bss-updated"
 #define NM_SUPPLICANT_INTERFACE_BSS_REMOVED      "bss-removed"
 #define NM_SUPPLICANT_INTERFACE_SCAN_DONE        "scan-done"
-#define NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR "connection-error"
 #define NM_SUPPLICANT_INTERFACE_CREDENTIALS_REQUEST "credentials-request"
 
 typedef struct _NMSupplicantInterfaceClass NMSupplicantInterfaceClass;
@@ -83,19 +81,25 @@ NMSupplicantInterface * nm_supplicant_interface_new (const char *ifname,
 void nm_supplicant_interface_set_supplicant_available (NMSupplicantInterface *self,
                                                        gboolean available);
 
-gboolean nm_supplicant_interface_set_config (NMSupplicantInterface * iface,
-                                             NMSupplicantConfig * cfg,
-                                             GError **error);
+typedef void (*NMSupplicantInterfaceAssocCb) (NMSupplicantInterface *iface,
+                                              GError *error,
+                                              gpointer user_data);
+
+void
+nm_supplicant_interface_assoc (NMSupplicantInterface *self,
+                               NMSupplicantConfig *cfg,
+                               NMSupplicantInterfaceAssocCb callback,
+                               gpointer user_data);
 
 void nm_supplicant_interface_disconnect (NMSupplicantInterface * iface);
 
 const char *nm_supplicant_interface_get_object_path (NMSupplicantInterface * iface);
 
-gboolean nm_supplicant_interface_request_scan (NMSupplicantInterface * self, const GPtrArray *ssids);
+void nm_supplicant_interface_request_scan (NMSupplicantInterface * self, const GPtrArray *ssids);
 
-guint32 nm_supplicant_interface_get_state (NMSupplicantInterface * self);
+NMSupplicantInterfaceState nm_supplicant_interface_get_state (NMSupplicantInterface * self);
 
-const char *nm_supplicant_interface_state_to_string (guint32 state);
+const char *nm_supplicant_interface_state_to_string (NMSupplicantInterfaceState state);
 
 gboolean nm_supplicant_interface_get_scanning (NMSupplicantInterface *self);
 
diff --git a/src/supplicant/nm-supplicant-manager.c b/src/supplicant/nm-supplicant-manager.c
index cf53fb41..49650ab7 100644
--- a/src/supplicant/nm-supplicant-manager.c
+++ b/src/supplicant/nm-supplicant-manager.c
@@ -63,7 +63,7 @@ G_DEFINE_TYPE (NMSupplicantManager, nm_supplicant_manager, G_TYPE_OBJECT)
 
 /*****************************************************************************/
 
-G_DEFINE_QUARK (nm-supplicant-error-quark, nm_supplicant_error);
+NM_CACHED_QUARK_FCN ("nm-supplicant-error-quark", nm_supplicant_error_quark)
 
 /*****************************************************************************/
 
@@ -398,10 +398,7 @@ dispose (GObject *object)
 
 	nm_clear_g_source (&priv->die_count_reset_id);
 
-	if (priv->cancellable) {
-		g_cancellable_cancel (priv->cancellable);
-		g_clear_object (&priv->cancellable);
-	}
+	nm_clear_g_cancellable (&priv->cancellable);
 
 	if (priv->ifaces) {
 		for (ifaces = priv->ifaces; ifaces; ifaces = ifaces->next)
diff --git a/src/supplicant/nm-supplicant-settings-verify.c b/src/supplicant/nm-supplicant-settings-verify.c
index 9e220808..ce3e46d8 100644
--- a/src/supplicant/nm-supplicant-settings-verify.c
+++ b/src/supplicant/nm-supplicant-settings-verify.c
@@ -81,7 +81,10 @@ const char * phase1_allowed[] =   {"peapver=0", "peapver=1", "peaplabel=1",
                                     "peap_outer_success=0", "include_tls_length=1",
                                     "sim_min_num_chal=3", "fast_provisioning=0",
                                     "fast_provisioning=1", "fast_provisioning=2",
-                                    "fast_provisioning=3", NULL };
+                                    "fast_provisioning=3", "tls_disable_tlsv1_0=0",
+                                    "tls_disable_tlsv1_0=1", "tls_disable_tlsv1_1=0",
+                                    "tls_disable_tlsv1_1=1", "tls_disable_tlsv1_2=0",
+                                    "tls_disable_tlsv1_2=1", NULL };
 const char * phase2_allowed[] =   {"auth=PAP", "auth=CHAP", "auth=MSCHAP",
                                    "auth=MSCHAPV2", "auth=GTC", "auth=OTP",
                                    "auth=MD5", "auth=TLS", "autheap=MD5",
diff --git a/src/systemd/sd-adapt/env-util.h b/src/systemd/sd-adapt/env-util.h
new file mode 100644
index 00000000..637892c2
--- /dev/null
+++ b/src/systemd/sd-adapt/env-util.h
@@ -0,0 +1,3 @@
+#pragma once
+
+/* dummy header */
diff --git a/src/systemd/sd-adapt/nm-sd-adapt.h b/src/systemd/sd-adapt/nm-sd-adapt.h
index cf27c1af..a8ff18bc 100644
--- a/src/systemd/sd-adapt/nm-sd-adapt.h
+++ b/src/systemd/sd-adapt/nm-sd-adapt.h
@@ -56,7 +56,7 @@ _slog_level_to_nm (int slevel)
 	if (nm_logging_enabled (_nm_l, LOGD_SYSTEMD)) { \
 		const char *_nm_location = strrchr ((""file), '/'); \
 		\
-		_nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_DHCP, _nm_e, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \
+		_nm_log_impl (_nm_location ? _nm_location + 1 : (""file), (line), (func), _nm_l, LOGD_DHCP, _nm_e, NULL, NULL, ("%s"format), "libsystemd: ", ## __VA_ARGS__); \
 	} \
 	(_nm_e > 0 ? -_nm_e : _nm_e); \
 })
diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c
index 8ef0db7f..711580a4 100644
--- a/src/systemd/src/basic/fileio.c
+++ b/src/systemd/src/basic/fileio.c
@@ -32,6 +32,7 @@
 
 #include "alloc-util.h"
 #include "ctype.h"
+#include "env-util.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
@@ -555,13 +556,14 @@ static int parse_env_file_internal(
                 }
         }
 
-        if (state == PRE_VALUE ||
-            state == VALUE ||
-            state == VALUE_ESCAPE ||
-            state == SINGLE_QUOTE_VALUE ||
-            state == SINGLE_QUOTE_VALUE_ESCAPE ||
-            state == DOUBLE_QUOTE_VALUE ||
-            state == DOUBLE_QUOTE_VALUE_ESCAPE) {
+        if (IN_SET(state,
+                   PRE_VALUE,
+                   VALUE,
+                   VALUE_ESCAPE,
+                   SINGLE_QUOTE_VALUE,
+                   SINGLE_QUOTE_VALUE_ESCAPE,
+                   DOUBLE_QUOTE_VALUE,
+                   DOUBLE_QUOTE_VALUE_ESCAPE)) {
 
                 key[n_key] = 0;
 
@@ -588,14 +590,9 @@ fail:
         return r;
 }
 
-static int parse_env_file_push(
+static int check_utf8ness_and_warn(
                 const char *filename, unsigned line,
-                const char *key, char *value,
-                void *userdata,
-                int *n_pushed) {
-
-        const char *k;
-        va_list aq, *ap = userdata;
+                const char *key, char *value) {
 
         if (!utf8_is_valid(key)) {
                 _cleanup_free_ char *p = NULL;
@@ -613,6 +610,23 @@ static int parse_env_file_push(
                 return -EINVAL;
         }
 
+        return 0;
+}
+
+static int parse_env_file_push(
+                const char *filename, unsigned line,
+                const char *key, char *value,
+                void *userdata,
+                int *n_pushed) {
+
+        const char *k;
+        va_list aq, *ap = userdata;
+        int r;
+
+        r = check_utf8ness_and_warn(filename, line, key, value);
+        if (r < 0)
+                return r;
+
         va_copy(aq, *ap);
 
         while ((k = va_arg(aq, const char *))) {
@@ -655,6 +669,7 @@ int parse_env_file(
         return r < 0 ? r : n_pushed;
 }
 
+#if 0 /* NM_IGNORED */
 static int load_env_file_push(
                 const char *filename, unsigned line,
                 const char *key, char *value,
@@ -664,27 +679,19 @@ static int load_env_file_push(
         char *p;
         int r;
 
-        if (!utf8_is_valid(key)) {
-                _cleanup_free_ char *t = utf8_escape_invalid(key);
-
-                log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
-                return -EINVAL;
-        }
-
-        if (value && !utf8_is_valid(value)) {
-                _cleanup_free_ char *t = utf8_escape_invalid(value);
-
-                log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
-                return -EINVAL;
-        }
+        r = check_utf8ness_and_warn(filename, line, key, value);
+        if (r < 0)
+                return r;
 
-        p = strjoin(key, "=", strempty(value));
+        p = strjoin(key, "=", value);
         if (!p)
                 return -ENOMEM;
 
-        r = strv_consume(m, p);
-        if (r < 0)
+        r = strv_env_replace(m, p);
+        if (r < 0) {
+                free(p);
                 return r;
+        }
 
         if (n_pushed)
                 (*n_pushed)++;
@@ -718,19 +725,9 @@ static int load_env_file_push_pairs(
         char ***m = userdata;
         int r;
 
-        if (!utf8_is_valid(key)) {
-                _cleanup_free_ char *t = utf8_escape_invalid(key);
-
-                log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
-                return -EINVAL;
-        }
-
-        if (value && !utf8_is_valid(value)) {
-                _cleanup_free_ char *t = utf8_escape_invalid(value);
-
-                log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
-                return -EINVAL;
-        }
+        r = check_utf8ness_and_warn(filename, line, key, value);
+        if (r < 0)
+                return r;
 
         r = strv_extend(m, key);
         if (r < 0)
@@ -769,6 +766,52 @@ int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char **
         return 0;
 }
 
+static int merge_env_file_push(
+                const char *filename, unsigned line,
+                const char *key, char *value,
+                void *userdata,
+                int *n_pushed) {
+
+        char ***env = userdata;
+        char *expanded_value;
+
+        assert(env);
+
+        if (!value) {
+                log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
+                return 0;
+        }
+
+        if (!env_name_is_valid(key)) {
+                log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key);
+                free(value);
+                return 0;
+        }
+
+        expanded_value = replace_env(value, *env,
+                                     REPLACE_ENV_USE_ENVIRONMENT|
+                                     REPLACE_ENV_ALLOW_BRACELESS|
+                                     REPLACE_ENV_ALLOW_EXTENDED);
+        if (!expanded_value)
+                return -ENOMEM;
+
+        free_and_replace(value, expanded_value);
+
+        return load_env_file_push(filename, line, key, value, env, n_pushed);
+}
+
+int merge_env_file(
+                char ***env,
+                FILE *f,
+                const char *fname) {
+
+        /* NOTE: this function supports braceful and braceless variable expansions,
+         * plus "extended" substitutions, unlike other exported parsing functions.
+         */
+
+        return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL);
+}
+
 static void write_env_var(FILE *f, const char *v) {
         const char *p;
 
@@ -828,6 +871,7 @@ int write_env_file(const char *fname, char **l) {
         unlink(p);
         return r;
 }
+#endif /* NM_IGNORED */
 
 int executable_is_script(const char *path, char **interpreter) {
         int r;
@@ -1349,6 +1393,25 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
         return fd;
 }
 
+int open_serialization_fd(const char *ident) {
+        int fd = -1;
+
+        fd = memfd_create(ident, MFD_CLOEXEC);
+        if (fd < 0) {
+                const char *path;
+
+                path = getpid() == 1 ? "/run/systemd" : "/tmp";
+                fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
+                if (fd < 0)
+                        return fd;
+
+                log_debug("Serializing %s to %s.", ident, path);
+        } else
+                log_debug("Serializing %s to memfd.", ident);
+
+        return fd;
+}
+
 int link_tmpfile(int fd, const char *path, const char *target) {
 
         assert(fd >= 0);
diff --git a/src/systemd/src/basic/fileio.h b/src/systemd/src/basic/fileio.h
index 17b38a5d..e547614c 100644
--- a/src/systemd/src/basic/fileio.h
+++ b/src/systemd/src/basic/fileio.h
@@ -48,6 +48,8 @@ int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
 int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
 int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
 
+int merge_env_file(char ***env, FILE *f, const char *fname);
+
 int write_env_file(const char *fname, char **l);
 
 int executable_is_script(const char *path, char **interpreter);
@@ -84,6 +86,7 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
 
 int open_tmpfile_unlinkable(const char *directory, int flags);
 int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
+int open_serialization_fd(const char *ident);
 
 int link_tmpfile(int fd, const char *path, const char *target);
 
diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c
index 3919fbcd..5e980329 100644
--- a/src/systemd/src/basic/fs-util.c
+++ b/src/systemd/src/basic/fs-util.c
@@ -730,6 +730,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                         return -errno;
 
                 if (S_ISLNK(st.st_mode)) {
+                        char *joined;
+
                         _cleanup_free_ char *destination = NULL;
 
                         /* This is a symlink, in this case read the destination. But let's make sure we don't follow
@@ -753,9 +755,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                                 if (fd < 0)
                                         return -errno;
 
-                                free_and_replace(buffer, destination);
-
-                                todo = buffer;
                                 free(done);
 
                                 /* Note that we do not revalidate the root, we take it as is. */
@@ -767,19 +766,17 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                                                 return -ENOMEM;
                                 }
 
-                        } else {
-                                char *joined;
+                        }
 
-                                /* A relative destination. If so, this is what we'll prefix what's left to do with what
-                                 * we just read, and start the loop again, but remain in the current directory. */
+                        /* Prefix what's left to do with what we just read, and start the loop again,
+                         * but remain in the current directory. */
 
-                                joined = strjoin("/", destination, todo);
-                                if (!joined)
-                                        return -ENOMEM;
+                        joined = strjoin("/", destination, todo);
+                        if (!joined)
+                                return -ENOMEM;
 
-                                free(buffer);
-                                todo = buffer = joined;
-                        }
+                        free(buffer);
+                        todo = buffer = joined;
 
                         continue;
                 }
@@ -806,8 +803,10 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                         return -ENOMEM;
         }
 
-        *ret = done;
-        done = NULL;
+        if (ret) {
+                *ret = done;
+                done = NULL;
+        }
 
         return exists;
 }
diff --git a/src/systemd/src/basic/fs-util.h b/src/systemd/src/basic/fs-util.h
index 5fe5c71f..094acf17 100644
--- a/src/systemd/src/basic/fs-util.h
+++ b/src/systemd/src/basic/fs-util.h
@@ -91,3 +91,9 @@ static inline void rmdir_and_free(char *p) {
         free(p);
 }
 DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
+
+static inline void unlink_and_free(char *p) {
+        (void) unlink(p);
+        free(p);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
diff --git a/src/systemd/src/basic/hexdecoct.c b/src/systemd/src/basic/hexdecoct.c
index 950e3230..2da8f8a2 100644
--- a/src/systemd/src/basic/hexdecoct.c
+++ b/src/systemd/src/basic/hexdecoct.c
@@ -74,10 +74,10 @@ int unhexchar(char c) {
 }
 
 char *hexmem(const void *p, size_t l) {
-        char *r, *z;
         const uint8_t *x;
+        char *r, *z;
 
-        z = r = malloc(l * 2 + 1);
+        z = r = new(char, l * 2 + 1);
         if (!r)
                 return NULL;
 
@@ -99,6 +99,9 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
         assert(len);
         assert(p);
 
+        if (l % 2 != 0)
+                return -EINVAL;
+
         z = r = malloc((l + 1) / 2 + 1);
         if (!r)
                 return -ENOMEM;
@@ -109,12 +112,10 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
                 a = unhexchar(x[0]);
                 if (a < 0)
                         return a;
-                else if (x+1 < p + l) {
-                        b = unhexchar(x[1]);
-                        if (b < 0)
-                                return b;
-                } else
-                        b = 0;
+
+                b = unhexchar(x[1]);
+                if (b < 0)
+                        return b;
 
                 *(z++) = (uint8_t) a << 4 | (uint8_t) b;
         }
diff --git a/src/systemd/src/basic/hostname-util.c b/src/systemd/src/basic/hostname-util.c
index 732a0ce7..823aa26a 100644
--- a/src/systemd/src/basic/hostname-util.c
+++ b/src/systemd/src/basic/hostname-util.c
@@ -47,6 +47,7 @@ bool hostname_is_set(void) {
         return true;
 }
 
+#if 0 /* NM_IGNORED */
 char* gethostname_malloc(void) {
         struct utsname u;
 
@@ -57,10 +58,11 @@ char* gethostname_malloc(void) {
         assert_se(uname(&u) >= 0);
 
         if (isempty(u.nodename) || streq(u.nodename, "(none)"))
-                return strdup(u.sysname);
+                return strdup(FALLBACK_HOSTNAME);
 
         return strdup(u.nodename);
 }
+#endif /* NM_IGNORED */
 
 int gethostname_strict(char **ret) {
         struct utsname u;
diff --git a/src/systemd/src/basic/in-addr-util.c b/src/systemd/src/basic/in-addr-util.c
index 37dc14f0..1140ca76 100644
--- a/src/systemd/src/basic/in-addr-util.c
+++ b/src/systemd/src/basic/in-addr-util.c
@@ -68,6 +68,18 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) {
         return -EAFNOSUPPORT;
 }
 
+int in_addr_is_multicast(int family, const union in_addr_union *u) {
+        assert(u);
+
+        if (family == AF_INET)
+                return IN_MULTICAST(be32toh(u->in.s_addr));
+
+        if (family == AF_INET6)
+                return IN6_IS_ADDR_MULTICAST(&u->in6);
+
+        return -EAFNOSUPPORT;
+}
+
 bool in4_addr_is_localhost(const struct in_addr *a) {
         assert(a);
 
diff --git a/src/systemd/src/basic/in-addr-util.h b/src/systemd/src/basic/in-addr-util.h
index 64a812c3..51a5aa67 100644
--- a/src/systemd/src/basic/in-addr-util.h
+++ b/src/systemd/src/basic/in-addr-util.h
@@ -39,6 +39,8 @@ struct in_addr_data {
 bool in4_addr_is_null(const struct in_addr *a);
 int in_addr_is_null(int family, const union in_addr_union *u);
 
+int in_addr_is_multicast(int family, const union in_addr_union *u);
+
 bool in4_addr_is_link_local(const struct in_addr *a);
 int in_addr_is_link_local(int family, const union in_addr_union *u);
 
diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h
index ccf2930f..d8335d12 100644
--- a/src/systemd/src/basic/log.h
+++ b/src/systemd/src/basic/log.h
@@ -216,13 +216,13 @@ bool log_on_console(void) _pure_;
 const char *log_target_to_string(LogTarget target) _const_;
 LogTarget log_target_from_string(const char *s) _pure_;
 
-/* Helpers to prepare various fields for structured logging */
+/* Helper to prepare various field for structured logging */
 #define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
-#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x)
 
 void log_received_signal(int level, const struct signalfd_siginfo *si);
 
 void log_set_upgrade_syslog_to_journal(bool b);
+void log_set_always_reopen_console(bool b);
 
 int log_syntax_internal(
                 const char *unit,
diff --git a/src/systemd/src/basic/parse-util.c b/src/systemd/src/basic/parse-util.c
index 9a7a0bbc..8ffd9464 100644
--- a/src/systemd/src/basic/parse-util.c
+++ b/src/systemd/src/basic/parse-util.c
@@ -25,7 +25,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <xlocale.h>
 
 #include "alloc-util.h"
 #include "extract-word.h"
diff --git a/src/systemd/src/basic/path-util.c b/src/systemd/src/basic/path-util.c
index dec95041..12ba6ae7 100644
--- a/src/systemd/src/basic/path-util.c
+++ b/src/systemd/src/basic/path-util.c
@@ -705,10 +705,7 @@ bool filename_is_valid(const char *p) {
         if (isempty(p))
                 return false;
 
-        if (streq(p, "."))
-                return false;
-
-        if (streq(p, ".."))
+        if (dot_or_dot_dot(p))
                 return false;
 
         e = strchrnul(p, '/');
@@ -727,14 +724,17 @@ bool path_is_safe(const char *p) {
         if (isempty(p))
                 return false;
 
-        if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
+        if (dot_or_dot_dot(p))
+                return false;
+
+        if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
                 return false;
 
         if (strlen(p)+1 > PATH_MAX)
                 return false;
 
         /* The following two checks are not really dangerous, but hey, they still are confusing */
-        if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
+        if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
                 return false;
 
         if (strstr(p, "//"))
@@ -900,3 +900,16 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version)
         return false;
 }
 #endif /* NM_IGNORED */
+
+bool dot_or_dot_dot(const char *path) {
+        if (!path)
+                return false;
+        if (path[0] != '.')
+                return false;
+        if (path[1] == 0)
+                return true;
+        if (path[1] != '.')
+                return false;
+
+        return path[2] == 0;
+}
diff --git a/src/systemd/src/basic/path-util.h b/src/systemd/src/basic/path-util.h
index 349cdac7..35aef3ad 100644
--- a/src/systemd/src/basic/path-util.h
+++ b/src/systemd/src/basic/path-util.h
@@ -141,3 +141,5 @@ bool is_device_path(const char *path);
 bool is_deviceallow_pattern(const char *path);
 
 int systemd_installation_has_version(const char *root, unsigned minimal_version);
+
+bool dot_or_dot_dot(const char *path);
diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c
index 57bc4b70..e63dd0db 100644
--- a/src/systemd/src/basic/socket-util.c
+++ b/src/systemd/src/basic/socket-util.c
@@ -116,6 +116,30 @@ int socket_address_parse(SocketAddress *a, const char *s) {
                 memcpy(a->sockaddr.un.sun_path+1, s+1, l);
                 a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
 
+        } else if (startswith(s, "vsock:")) {
+                /* AF_VSOCK socket in vsock:cid:port notation */
+                const char *cid_start = s + strlen("vsock:");
+
+                e = strchr(cid_start, ':');
+                if (!e)
+                        return -EINVAL;
+
+                r = safe_atou(e+1, &u);
+                if (r < 0)
+                        return r;
+
+                n = strndupa(cid_start, e - cid_start);
+                if (!isempty(n)) {
+                        r = safe_atou(n, &a->sockaddr.vm.svm_cid);
+                        if (r < 0)
+                                return r;
+                } else
+                        a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
+
+                a->sockaddr.vm.svm_family = AF_VSOCK;
+                a->sockaddr.vm.svm_port = u;
+                a->size = sizeof(struct sockaddr_vm);
+
         } else {
                 e = strchr(s, ':');
                 if (e) {
@@ -292,6 +316,15 @@ int socket_address_verify(const SocketAddress *a) {
 
                 return 0;
 
+        case AF_VSOCK:
+                if (a->size != sizeof(struct sockaddr_vm))
+                        return -EINVAL;
+
+                if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+                        return -EINVAL;
+
+                return 0;
+
         default:
                 return -EAFNOSUPPORT;
         }
@@ -397,6 +430,15 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
 
                 break;
 
+        case AF_VSOCK:
+                if (a->sockaddr.vm.svm_cid != b->sockaddr.vm.svm_cid)
+                        return false;
+
+                if (a->sockaddr.vm.svm_port != b->sockaddr.vm.svm_port)
+                        return false;
+
+                break;
+
         default:
                 /* Cannot compare, so we assume the addresses are different */
                 return false;
@@ -483,15 +525,27 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
         return socket_address_equal(a, &b);
 }
 
-int sockaddr_port(const struct sockaddr *_sa) {
+int sockaddr_port(const struct sockaddr *_sa, unsigned *port) {
         union sockaddr_union *sa = (union sockaddr_union*) _sa;
 
         assert(sa);
 
-        if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
-                return -EAFNOSUPPORT;
+        switch (sa->sa.sa_family) {
+        case AF_INET:
+                *port = be16toh(sa->in.sin_port);
+                return 0;
 
-        return be16toh(sa->sa.sa_family == AF_INET6 ? sa->in6.sin6_port : sa->in.sin_port);
+        case AF_INET6:
+                *port = be16toh(sa->in6.sin6_port);
+                return 0;
+
+        case AF_VSOCK:
+                *port = sa->vm.svm_port;
+                return 0;
+
+        default:
+                return -EAFNOSUPPORT;
+        }
 }
 
 int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
@@ -594,6 +648,18 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
 
                 break;
 
+        case AF_VSOCK:
+                if (include_port)
+                        r = asprintf(&p,
+                                     "vsock:%u:%u",
+                                     sa->vm.svm_cid,
+                                     sa->vm.svm_port);
+                else
+                        r = asprintf(&p, "vsock:%u", sa->vm.svm_cid);
+                if (r < 0)
+                        return -ENOMEM;
+                break;
+
         default:
                 return -EOPNOTSUPP;
         }
@@ -751,6 +817,9 @@ bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b
         if (a->sa.sa_family == AF_INET6)
                 return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
 
+        if (a->sa.sa_family == AF_VSOCK)
+                return a->vm.svm_cid == b->vm.svm_cid;
+
         return false;
 }
 
@@ -811,7 +880,7 @@ bool ifname_valid(const char *p) {
         if (strlen(p) >= IFNAMSIZ)
                 return false;
 
-        if (STR_IN_SET(p, ".", ".."))
+        if (dot_or_dot_dot(p))
                 return false;
 
         while (*p) {
@@ -834,6 +903,26 @@ bool ifname_valid(const char *p) {
         return true;
 }
 
+bool address_label_valid(const char *p) {
+
+        if (isempty(p))
+                return false;
+
+        if (strlen(p) >= IFNAMSIZ)
+                return false;
+
+        while (*p) {
+                if ((uint8_t) *p >= 127U)
+                        return false;
+
+                if ((uint8_t) *p <= 31U)
+                        return false;
+                p++;
+        }
+
+        return true;
+}
+
 int getpeercred(int fd, struct ucred *ucred) {
         socklen_t n = sizeof(struct ucred);
         struct ucred u;
@@ -1012,6 +1101,7 @@ fallback:
         return (ssize_t) k;
 }
 
+#if 0 /* NM_IGNORED */
 int flush_accept(int fd) {
 
         struct pollfd pollfd = {
@@ -1081,3 +1171,4 @@ int socket_ioctl_fd(void) {
 
         return fd;
 }
+#endif /* NM_IGNORED */
diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h
index 2ef572ba..19a9ddb2 100644
--- a/src/systemd/src/basic/socket-util.h
+++ b/src/systemd/src/basic/socket-util.h
@@ -30,6 +30,7 @@
 #include <linux/if_packet.h>
 
 #include "macro.h"
+#include "missing.h"
 #include "util.h"
 
 union sockaddr_union {
@@ -40,6 +41,9 @@ union sockaddr_union {
         struct sockaddr_nl nl;
         struct sockaddr_storage storage;
         struct sockaddr_ll ll;
+#if 0 /* NM_IGNORED */
+        struct sockaddr_vm vm;
+#endif /* NM_IGNORED */
 };
 
 typedef struct SocketAddress {
@@ -100,7 +104,7 @@ const char* socket_address_get_path(const SocketAddress *a);
 
 bool socket_ipv6_is_supported(void);
 
-int sockaddr_port(const struct sockaddr *_sa) _pure_;
+int sockaddr_port(const struct sockaddr *_sa, unsigned *port);
 
 int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
 int getpeername_pretty(int fd, bool include_port, char **ret);
@@ -124,6 +128,7 @@ int ip_tos_to_string_alloc(int i, char **s);
 int ip_tos_from_string(const char *s);
 
 bool ifname_valid(const char *p);
+bool address_label_valid(const char *p);
 
 int getpeercred(int fd, struct ucred *ucred);
 int getpeersec(int fd, char **ret);
diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c
index aeb9e2a9..406d6d3c 100644
--- a/src/systemd/src/basic/string-util.c
+++ b/src/systemd/src/basic/string-util.c
@@ -825,6 +825,7 @@ int free_and_strdup(char **p, const char *s) {
         return 1;
 }
 
+#if !HAVE_DECL_EXPLICIT_BZERO
 /*
  * Pointer to memset is volatile so that compiler must de-reference
  * the pointer and can't assume that it points to any function in
@@ -835,19 +836,19 @@ typedef void *(*memset_t)(void *,int,size_t);
 
 static volatile memset_t memset_func = memset;
 
-void* memory_erase(void *p, size_t l) {
-        return memset_func(p, 'x', l);
+void explicit_bzero(void *p, size_t l) {
+        memset_func(p, '\0', l);
 }
+#endif
 
 char* string_erase(char *x) {
-
         if (!x)
                 return NULL;
 
         /* A delicious drop of snake-oil! To be called on memory where
          * we stored passphrases or so, after we used them. */
-
-        return memory_erase(x, strlen(x));
+        explicit_bzero(x, strlen(x));
+        return x;
 }
 
 char *string_free_erase(char *s) {
diff --git a/src/systemd/src/basic/string-util.h b/src/systemd/src/basic/string-util.h
index e99f7964..be44dedf 100644
--- a/src/systemd/src/basic/string-util.h
+++ b/src/systemd/src/basic/string-util.h
@@ -189,7 +189,10 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
         return memmem(haystack, haystacklen, needle, needlelen);
 }
 
-void* memory_erase(void *p, size_t l);
+#if !HAVE_DECL_EXPLICIT_BZERO
+void explicit_bzero(void *p, size_t l);
+#endif
+
 char *string_erase(char *x);
 
 char *string_free_erase(char *s);
diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c
index fa488787..e8158d55 100644
--- a/src/systemd/src/basic/time-util.c
+++ b/src/systemd/src/basic/time-util.c
@@ -187,7 +187,7 @@ usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
 usec_t timespec_load(const struct timespec *ts) {
         assert(ts);
 
-        if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
+        if (ts->tv_sec < 0 || ts->tv_nsec < 0)
                 return USEC_INFINITY;
 
         if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
@@ -201,7 +201,7 @@ usec_t timespec_load(const struct timespec *ts) {
 nsec_t timespec_load_nsec(const struct timespec *ts) {
         assert(ts);
 
-        if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
+        if (ts->tv_sec < 0 || ts->tv_nsec < 0)
                 return NSEC_INFINITY;
 
         if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
@@ -213,7 +213,8 @@ nsec_t timespec_load_nsec(const struct timespec *ts) {
 struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
         assert(ts);
 
-        if (u == USEC_INFINITY) {
+        if (u == USEC_INFINITY ||
+            u / USEC_PER_SEC >= TIME_T_MAX) {
                 ts->tv_sec = (time_t) -1;
                 ts->tv_nsec = (long) -1;
                 return ts;
@@ -229,8 +230,7 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
 usec_t timeval_load(const struct timeval *tv) {
         assert(tv);
 
-        if (tv->tv_sec == (time_t) -1 &&
-            tv->tv_usec == (suseconds_t) -1)
+        if (tv->tv_sec < 0 || tv->tv_usec < 0)
                 return USEC_INFINITY;
 
         if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
@@ -244,7 +244,8 @@ usec_t timeval_load(const struct timeval *tv) {
 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
         assert(tv);
 
-        if (u == USEC_INFINITY) {
+        if (u == USEC_INFINITY||
+            u / USEC_PER_SEC > TIME_T_MAX) {
                 tv->tv_sec = (time_t) -1;
                 tv->tv_usec = (suseconds_t) -1;
         } else {
@@ -291,9 +292,11 @@ static char *format_timestamp_internal(
         if (t <= 0 || t == USEC_INFINITY)
                 return NULL; /* Timestamp is unset */
 
+        /* Let's not format times with years > 9999 */
+        if (t > USEC_TIMESTAMP_FORMATTABLE_MAX)
+                return NULL;
+
         sec = (time_t) (t / USEC_PER_SEC); /* Round down */
-        if ((usec_t) sec != (t / USEC_PER_SEC))
-                return NULL; /* overflow? */
 
         if (!localtime_or_gmtime_r(&sec, &tm, utc))
                 return NULL;
@@ -556,12 +559,12 @@ void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
 }
 
 int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
-        unsigned long long a, b;
+        uint64_t a, b;
 
         assert(value);
         assert(t);
 
-        if (sscanf(value, "%llu %llu", &a, &b) != 2) {
+        if (sscanf(value, "%" PRIu64 "%" PRIu64, &a, &b) != 2) {
                 log_debug("Failed to parse dual timestamp value \"%s\": %m", value);
                 return -EINVAL;
         }
@@ -835,16 +838,23 @@ parse_usec:
 
 from_tm:
         x = mktime_or_timegm(&tm, utc);
-        if (x == (time_t) -1)
+        if (x < 0)
                 return -EINVAL;
 
         if (weekday >= 0 && tm.tm_wday != weekday)
                 return -EINVAL;
 
         ret = (usec_t) x * USEC_PER_SEC + x_usec;
+        if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
+                return -EINVAL;
 
 finish:
+        if (ret + plus < ret) /* overflow? */
+                return -EINVAL;
         ret += plus;
+        if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
+                return -EINVAL;
+
         if (ret > minus)
                 ret -= minus;
         else
diff --git a/src/systemd/src/basic/time-util.h b/src/systemd/src/basic/time-util.h
index f67a4474..7463507f 100644
--- a/src/systemd/src/basic/time-util.h
+++ b/src/systemd/src/basic/time-util.h
@@ -181,3 +181,14 @@ static inline usec_t usec_sub(usec_t timestamp, int64_t delta) {
 
         return timestamp - delta;
 }
+
+#if SIZEOF_TIME_T == 8
+/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year
+ * territory. However, since we want to stay away from this in all timezones we take one day off. */
+#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000)
+#elif SIZEOF_TIME_T == 4
+/* With a 32bit time_t we can't go beyond 2038... */
+#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000)
+#else
+#error "Yuck, time_t is neither 4 not 8 bytes wide?"
+#endif
diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c
index 09651e7d..563ee87e 100644
--- a/src/systemd/src/basic/util.c
+++ b/src/systemd/src/basic/util.c
@@ -61,9 +61,6 @@
 #include "user-util.h"
 #include "util.h"
 
-/* Put this test here for a lack of better place */
-assert_cc(EAGAIN == EWOULDBLOCK);
-
 #if 0 /* NM_IGNORED */
 int saved_argc = 0;
 char **saved_argv = NULL;
@@ -85,146 +82,6 @@ size_t page_size(void) {
 }
 
 #if 0 /* NM_IGNORED */
-static int do_execute(char **directories, usec_t timeout, char *argv[]) {
-        _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
-        _cleanup_set_free_free_ Set *seen = NULL;
-        char **directory;
-
-        /* We fork this all off from a child process so that we can
-         * somewhat cleanly make use of SIGALRM to set a time limit */
-
-        (void) reset_all_signal_handlers();
-        (void) reset_signal_mask();
-
-        assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
-        pids = hashmap_new(NULL);
-        if (!pids)
-                return log_oom();
-
-        seen = set_new(&string_hash_ops);
-        if (!seen)
-                return log_oom();
-
-        STRV_FOREACH(directory, directories) {
-                _cleanup_closedir_ DIR *d;
-                struct dirent *de;
-
-                d = opendir(*directory);
-                if (!d) {
-                        if (errno == ENOENT)
-                                continue;
-
-                        return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
-                }
-
-                FOREACH_DIRENT(de, d, break) {
-                        _cleanup_free_ char *path = NULL;
-                        pid_t pid;
-                        int r;
-
-                        if (!dirent_is_file(de))
-                                continue;
-
-                        if (set_contains(seen, de->d_name)) {
-                                log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
-                                continue;
-                        }
-
-                        r = set_put_strdup(seen, de->d_name);
-                        if (r < 0)
-                                return log_oom();
-
-                        path = strjoin(*directory, "/", de->d_name);
-                        if (!path)
-                                return log_oom();
-
-                        if (null_or_empty_path(path)) {
-                                log_debug("%s is empty (a mask).", path);
-                                continue;
-                        }
-
-                        pid = fork();
-                        if (pid < 0) {
-                                log_error_errno(errno, "Failed to fork: %m");
-                                continue;
-                        } else if (pid == 0) {
-                                char *_argv[2];
-
-                                assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
-                                if (!argv) {
-                                        _argv[0] = path;
-                                        _argv[1] = NULL;
-                                        argv = _argv;
-                                } else
-                                        argv[0] = path;
-
-                                execv(path, argv);
-                                return log_error_errno(errno, "Failed to execute %s: %m", path);
-                        }
-
-                        log_debug("Spawned %s as " PID_FMT ".", path, pid);
-
-                        r = hashmap_put(pids, PID_TO_PTR(pid), path);
-                        if (r < 0)
-                                return log_oom();
-                        path = NULL;
-                }
-        }
-
-        /* Abort execution of this process after the timout. We simply
-         * rely on SIGALRM as default action terminating the process,
-         * and turn on alarm(). */
-
-        if (timeout != USEC_INFINITY)
-                alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
-
-        while (!hashmap_isempty(pids)) {
-                _cleanup_free_ char *path = NULL;
-                pid_t pid;
-
-                pid = PTR_TO_PID(hashmap_first_key(pids));
-                assert(pid > 0);
-
-                path = hashmap_remove(pids, PID_TO_PTR(pid));
-                assert(path);
-
-                wait_for_terminate_and_warn(path, pid, true);
-        }
-
-        return 0;
-}
-
-void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
-        pid_t executor_pid;
-        int r;
-        char *name;
-        char **dirs = (char**) directories;
-
-        assert(!strv_isempty(dirs));
-
-        name = basename(dirs[0]);
-        assert(!isempty(name));
-
-        /* Executes all binaries in the directories in parallel and waits
-         * for them to finish. Optionally a timeout is applied. If a file
-         * with the same name exists in more than one directory, the
-         * earliest one wins. */
-
-        executor_pid = fork();
-        if (executor_pid < 0) {
-                log_error_errno(errno, "Failed to fork: %m");
-                return;
-
-        } else if (executor_pid == 0) {
-                r = do_execute(dirs, timeout, argv);
-                _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
-        }
-
-        wait_for_terminate_and_warn(name, executor_pid, true);
-}
-
 bool plymouth_running(void) {
         return access("/run/plymouth/pid", F_OK) >= 0;
 }
diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h
index c3802a81..c7da6c39 100644
--- a/src/systemd/src/basic/util.h
+++ b/src/systemd/src/basic/util.h
@@ -65,8 +65,6 @@ static inline const char* enable_disable(bool b) {
         return b ? "enable" : "disable";
 }
 
-void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
-
 bool plymouth_running(void);
 
 bool display_is_local(const char *display) _pure_;
diff --git a/src/systemd/src/libsystemd-network/arp-util.c b/src/systemd/src/libsystemd-network/arp-util.c
index 8c678626..69bd3e75 100644
--- a/src/systemd/src/libsystemd-network/arp-util.c
+++ b/src/systemd/src/libsystemd-network/arp-util.c
@@ -60,7 +60,7 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_
                 BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* A xor X */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1),                                  /* A == 0 ? */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
-                /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/
+                /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */
                 BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)),                                  /* A <- clients IP */
                 BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
                 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)),       /* A <- SPA */
diff --git a/src/systemd/src/libsystemd-network/lldp-network.c b/src/systemd/src/libsystemd-network/lldp-network.c
index bba3a752..4466a050 100644
--- a/src/systemd/src/libsystemd-network/lldp-network.c
+++ b/src/systemd/src/libsystemd-network/lldp-network.c
@@ -49,6 +49,13 @@ int lldp_network_bind_raw_socket(int ifindex) {
                 .filter = (struct sock_filter*) filter,
         };
 
+        struct packet_mreq mreq = {
+                .mr_ifindex = ifindex,
+                .mr_type = PACKET_MR_MULTICAST,
+                .mr_alen = ETH_ALEN,
+                .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }
+        };
+
         union sockaddr_union saddrll = {
                 .ll.sll_family = AF_PACKET,
                 .ll.sll_ifindex = ifindex,
@@ -68,6 +75,20 @@ int lldp_network_bind_raw_socket(int ifindex) {
         if (r < 0)
                 return -errno;
 
+        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+        if (r < 0)
+                return -errno;
+
+        mreq.mr_address[ETH_ALEN - 1] = 0x03;
+        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+        if (r < 0)
+                return -errno;
+
+        mreq.mr_address[ETH_ALEN - 1] = 0x0E;
+        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+        if (r < 0)
+                return -errno;
+
         r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
         if (r < 0)
                 return -errno;
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
index 7809a812..17393e20 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
@@ -829,6 +829,15 @@ static int client_send_request(sd_dhcp_client *client) {
                         return r;
         }
 
+        if (client->vendor_class_identifier) {
+                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+                                       SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
+                                       strlen(client->vendor_class_identifier),
+                                       client->vendor_class_identifier);
+                if (r < 0)
+                        return r;
+        }
+
         r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
                                SD_DHCP_OPTION_END, 0, NULL);
         if (r < 0)
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4acd.c b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
index 913c1adb..3976768b 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4acd.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4acd.c
@@ -244,8 +244,6 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata)
                         r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
                         if (r < 0)
                                 goto fail;
-
-                        acd->n_conflict = 0;
                 } else {
                         r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
                         if (r < 0)
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
index 2420c99f..47fc141c 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
@@ -250,6 +250,12 @@ static int ipv4ll_pick_address(sd_ipv4ll *ll) {
         return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr });
 }
 
+int sd_ipv4ll_restart(sd_ipv4ll *ll) {
+        ll->address = 0;
+
+        return sd_ipv4ll_start(ll);
+}
+
 #define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
 
 int sd_ipv4ll_start(sd_ipv4ll *ll) {
diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c
index 66c9780e..2a64a99b 100644
--- a/src/systemd/src/libsystemd-network/sd-lldp.c
+++ b/src/systemd/src/libsystemd-network/sd-lldp.c
@@ -21,6 +21,7 @@
 #include "nm-sd-adapt.h"
 
 #include <arpa/inet.h>
+#include <linux/sockios.h>
 
 #include "sd-lldp.h"
 
diff --git a/src/systemd/src/libsystemd/sd-event/sd-event.c b/src/systemd/src/libsystemd/sd-event/sd-event.c
index a51ab9e6..3f7b703e 100644
--- a/src/systemd/src/libsystemd/sd-event/sd-event.c
+++ b/src/systemd/src/libsystemd/sd-event/sd-event.c
@@ -732,7 +732,6 @@ static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig
 
                 /* If all the mask is all-zero we can get rid of the structure */
                 hashmap_remove(e->signal_data, &d->priority);
-                assert(!d->current);
                 safe_close(d->fd);
                 free(d);
                 return;
@@ -2230,11 +2229,16 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) {
 }
 
 static int source_dispatch(sd_event_source *s) {
+        EventSourceType saved_type;
         int r = 0;
 
         assert(s);
         assert(s->pending || s->type == SOURCE_EXIT);
 
+        /* Save the event source type, here, so that we still know it after the event callback which might invalidate
+         * the event. */
+        saved_type = s->type;
+
         if (s->type != SOURCE_DEFER && s->type != SOURCE_EXIT) {
                 r = source_set_pending(s, false);
                 if (r < 0)
@@ -2322,7 +2326,7 @@ static int source_dispatch(sd_event_source *s) {
 
         if (r < 0)
                 log_debug_errno(r, "Event source %s (type %s) returned error, disabling: %m",
-                                strna(s->description), event_source_type_to_string(s->type));
+                                strna(s->description), event_source_type_to_string(saved_type));
 
         if (s->n_ref == 0)
                 source_free(s);
diff --git a/src/systemd/src/systemd/_sd-common.h b/src/systemd/src/systemd/_sd-common.h
index 3bb886be..97c39438 100644
--- a/src/systemd/src/systemd/_sd-common.h
+++ b/src/systemd/src/systemd/_sd-common.h
@@ -22,8 +22,8 @@
 
 /* This is a private header; never even think of including this directly! */
 
-#if __INCLUDE_LEVEL__ <= 1
-#error "Do not include _sd-common.h directly; it is a private header."
+#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1
+#  error "Do not include _sd-common.h directly; it is a private header."
 #endif
 
 #ifndef _sd_printf_
diff --git a/src/systemd/src/systemd/sd-event.h b/src/systemd/src/systemd/sd-event.h
index cc26b7df..f8cb8956 100644
--- a/src/systemd/src/systemd/sd-event.h
+++ b/src/systemd/src/systemd/sd-event.h
@@ -69,7 +69,7 @@ typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata);
 typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata);
 typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata);
 typedef int (*sd_event_signal_handler_t)(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata);
-#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED
+#if defined _GNU_SOURCE || _POSIX_C_SOURCE >= 199309L
 typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, void *userdata);
 #else
 typedef void* sd_event_child_handler_t;
diff --git a/src/systemd/src/systemd/sd-id128.h b/src/systemd/src/systemd/sd-id128.h
index 6cc8e4ac..9b38969b 100644
--- a/src/systemd/src/systemd/sd-id128.h
+++ b/src/systemd/src/systemd/sd-id128.h
@@ -100,6 +100,9 @@ int sd_id128_get_invocation(sd_id128_t *ret);
                 ((x).bytes[15] & 15) >= 10 ? 'a' + ((x).bytes[15] & 15) - 10 : '0' + ((x).bytes[15] & 15), \
                 0 })
 
+#define SD_ID128_MAKE_STR(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+        #a #b #c #d #e #f #g #h #i #j #k #l #m #n #o #p
+
 _sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
         return memcmp(&a, &b, 16) == 0;
 }
diff --git a/src/systemd/src/systemd/sd-ipv4ll.h b/src/systemd/src/systemd/sd-ipv4ll.h
index 1109ec52..5ba92083 100644
--- a/src/systemd/src/systemd/sd-ipv4ll.h
+++ b/src/systemd/src/systemd/sd-ipv4ll.h
@@ -47,6 +47,7 @@ int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index);
 int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address);
 int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed);
 int sd_ipv4ll_is_running(sd_ipv4ll *ll);
+int sd_ipv4ll_restart(sd_ipv4ll *ll);
 int sd_ipv4ll_start(sd_ipv4ll *ll);
 int sd_ipv4ll_stop(sd_ipv4ll *ll);
 sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll);
diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c
index dd2a58a0..edb5c1aa 100644
--- a/src/tests/config/test-config.c
+++ b/src/tests/config/test-config.c
@@ -97,7 +97,7 @@ setup_config (GError **error, const char *config_file, const char *intern_config
 	argv = (char **)args->pdata;
 	argc = args->len;
 
-	cli = nm_config_cmd_line_options_new ();
+	cli = nm_config_cmd_line_options_new (FALSE);
 
 	context = g_option_context_new (NULL);
 	nm_config_cmd_line_options_add_to_entries (cli, context);
diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c
index fbed7799..492e1b1f 100644
--- a/src/tests/test-general-with-expect.c
+++ b/src/tests/test-general-with-expect.c
@@ -751,7 +751,7 @@ _mi_rebucket (GRand *rand, guint num_values, guint num_buckets, NMMultiIndexOper
 	         op == MI_OP_ADD ? "ADD" : (op == MI_OP_REMOVE ? "REM" : "MOV"),
 	         bucket_old, had_bucket_old ? '*' : ' ',
 	         bucket, had_bucket ? '*' : ' ',
-	         (long long unsigned) buckets_old, (long long unsigned) v->buckets,
+	         (unsigned long long) buckets_old, (unsigned long long) v->buckets,
 	         buckets_old != v->buckets ? "(changed)" : "(unchanged)");
 #endif
 
diff --git a/src/tests/test-general.c b/src/tests/test-general.c
index 81d8e05c..37bbc5ca 100644
--- a/src/tests/test-general.c
+++ b/src/tests/test-general.c
@@ -23,6 +23,9 @@
 #include <string.h>
 #include <errno.h>
 
+/* need math.h for isinf() and INFINITY. No need to link with -lm */
+#include <math.h>
+
 #include "NetworkManagerUtils.h"
 #include "nm-core-internal.h"
 
@@ -225,7 +228,7 @@ test_nm_utils_log_connection_diff (void)
 	 * early without doing anything. Hence, in the normal testing, this test does nothing.
 	 * It only gets interesting, when run verbosely with NMTST_DEBUG=debug ... */
 
-	nm_log (LOGL_DEBUG, LOGD_CORE, "START TEST test_nm_utils_log_connection_diff...");
+	nm_log (LOGL_DEBUG, LOGD_CORE, NULL, NULL, "START TEST test_nm_utils_log_connection_diff...");
 
 	connection = nm_simple_connection_new ();
 	nm_connection_add_setting (connection, nm_setting_connection_new ());
@@ -309,6 +312,30 @@ _match_connection_new (void)
 	return connection;
 }
 
+static NMConnection *
+_match_connection (GSList *connections,
+                   NMConnection *original,
+                   gboolean device_has_carrier,
+                   gint64 default_v4_metric,
+                   gint64 default_v6_metric)
+{
+	NMConnection **list;
+	guint i, len;
+
+	len = g_slist_length (connections);
+	g_assert (len < 10);
+
+	list = g_alloca ((len + 1) * sizeof (NMConnection *));
+	for (i = 0; i < len; i++, connections = connections->next) {
+		g_assert (connections);
+		g_assert (connections->data);
+		list[i] = connections->data;
+	}
+	list[i] = NULL;
+
+	return nm_utils_match_connection (list, original, device_has_carrier, default_v4_metric, default_v6_metric, NULL, NULL);
+}
+
 static void
 test_connection_match_basic (void)
 {
@@ -320,7 +347,7 @@ test_connection_match_basic (void)
 	copy = nm_simple_connection_new_clone (orig);
 	connections = g_slist_append (connections, copy);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == copy);
 
 	/* Now change a material property like IPv4 method and ensure matching fails */
@@ -329,7 +356,7 @@ test_connection_match_basic (void)
 	g_object_set (G_OBJECT (s_ip4),
 	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL,
 	              NULL);
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == NULL);
 
 	g_slist_free (connections);
@@ -365,7 +392,7 @@ test_connection_match_ip6_method (void)
 	              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
 	              NULL);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == copy);
 
 	g_slist_free (connections);
@@ -399,7 +426,7 @@ test_connection_match_ip6_method_ignore (void)
 	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
 	              NULL);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == copy);
 
 	g_slist_free (connections);
@@ -433,7 +460,7 @@ test_connection_match_ip6_method_ignore_auto (void)
 	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
 	              NULL);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == copy);
 
 	g_slist_free (connections);
@@ -469,11 +496,11 @@ test_connection_match_ip4_method (void)
 	              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
 	              NULL);
 
-	matched = nm_utils_match_connection (connections, orig, FALSE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, FALSE, 0, 0);
 	g_assert (matched == copy);
 
 	/* Ensure when carrier=true matching fails */
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == NULL);
 
 	g_slist_free (connections);
@@ -507,7 +534,7 @@ test_connection_match_interface_name (void)
 	              NM_SETTING_CONNECTION_INTERFACE_NAME, NULL,
 	              NULL);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == copy);
 
 	g_slist_free (connections);
@@ -544,7 +571,7 @@ test_connection_match_wired (void)
 	              NM_SETTING_WIRED_S390_NETTYPE, "qeth",
 	              NULL);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == copy);
 
 	g_slist_free (connections);
@@ -576,7 +603,7 @@ test_connection_match_wired2 (void)
 	 * the connections match. It can happen if assuming VLAN devices. */
 	nm_connection_remove_setting (orig, NM_TYPE_SETTING_WIRED);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == copy);
 
 	g_slist_free (connections);
@@ -601,7 +628,7 @@ test_connection_match_cloned_mac (void)
 	              NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "52:54:00:ab:db:23",
 	              NULL);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == fuzzy);
 
 	exact = nm_simple_connection_new_clone (orig);
@@ -612,14 +639,14 @@ test_connection_match_cloned_mac (void)
 	              NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "52:54:00:ab:db:23",
 	              NULL);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == exact);
 
 	g_object_set (G_OBJECT (s_wired),
 	              NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "52:54:00:ab:db:24",
 	              NULL);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched == fuzzy);
 
 	g_slist_free (connections);
@@ -679,7 +706,7 @@ test_connection_no_match_ip4_addr (void)
 	nm_setting_ip_config_add_address (s_ip4, nm_addr);
 	nm_ip_address_unref (nm_addr);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched != copy);
 
 	g_slist_free (connections);
@@ -725,7 +752,7 @@ test_connection_no_match_vlan (void)
 	              NM_SETTING_VLAN_FLAGS, 0,
 	              NULL);
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched != copy);
 
 	/* Check that the connections do not match if VLAN priorities differ */
@@ -735,7 +762,7 @@ test_connection_no_match_vlan (void)
 	g_object_set (G_OBJECT (s_vlan_copy), NM_SETTING_VLAN_FLAGS, 0, NULL);
 	nm_setting_vlan_add_priority_str (s_vlan_copy, NM_VLAN_INGRESS_MAP, "4:2");
 
-	matched = nm_utils_match_connection (connections, orig, TRUE, 0, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, TRUE, 0, 0);
 	g_assert (matched != copy);
 
 	g_slist_free (connections);
@@ -775,7 +802,7 @@ test_connection_match_ip4_routes1 (void)
 	nmtst_setting_ip_config_add_route (s_ip4, "172.25.17.0", 24, "10.0.0.3", 20);
 
 	/* Try to match the connections */
-	matched = nm_utils_match_connection (connections, orig, FALSE, 100, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, FALSE, 100, 0);
 	g_assert (matched == NULL);
 }
 
@@ -812,9 +839,9 @@ test_connection_match_ip4_routes2 (void)
 	nmtst_setting_ip_config_add_route (s_ip4, "172.25.16.0", 24, "10.0.0.2", 100);
 
 	/* Try to match the connections using different default metrics */
-	matched = nm_utils_match_connection (connections, orig, FALSE, 100, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, FALSE, 100, 0);
 	g_assert (matched == copy);
-	matched = nm_utils_match_connection (connections, orig, FALSE, 500, 0, NULL, NULL);
+	matched = _match_connection (connections, orig, FALSE, 500, 0);
 	g_assert (matched == NULL);
 }
 
@@ -849,9 +876,9 @@ test_connection_match_ip6_routes (void)
 	nmtst_setting_ip_config_add_route (s_ip6, "2001:db8:a:b:0:0:0:0", 64, "fd01::16", 50);
 
 	/* Try to match the connections */
-	matched = nm_utils_match_connection (connections, orig, FALSE, 0, 100, NULL, NULL);
+	matched = _match_connection (connections, orig, FALSE, 0, 100);
 	g_assert (matched == NULL);
-	matched = nm_utils_match_connection (connections, orig, FALSE, 0, 50, NULL, NULL);
+	matched = _match_connection (connections, orig, FALSE, 0, 50);
 	g_assert (matched == copy);
 }
 
@@ -870,6 +897,12 @@ _create_connection_autoconnect (const char *id, gboolean autoconnect, int autoco
 	return c;
 }
 
+static int
+_cmp_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data)
+{
+	return nm_utils_cmp_connection_by_autoconnect_priority (*((NMConnection **) pa), *((NMConnection **) pb));
+}
+
 static void
 _test_connection_sort_autoconnect_priority_one (NMConnection **list, gboolean shuffle)
 {
@@ -892,12 +925,12 @@ _test_connection_sort_autoconnect_priority_one (NMConnection **list, gboolean sh
 	}
 
 	/* sort it... */
-	g_ptr_array_sort (connections, (GCompareFunc) nm_utils_cmp_connection_by_autoconnect_priority);
+	g_ptr_array_sort_with_data (connections, _cmp_autoconnect_priority_p_with_data, NULL);
 
 	for (i = 0; i < count; i++) {
 		if (list[i] == connections->pdata[i])
 			continue;
-		if (shuffle && nm_utils_cmp_connection_by_autoconnect_priority (&list[i], (NMConnection **) &connections->pdata[i]) == 0)
+		if (shuffle && nm_utils_cmp_connection_by_autoconnect_priority (list[i], connections->pdata[i]) == 0)
 			continue;
 		g_message ("After sorting, the order of connections is not as expected!! Offending index: %d", i);
 		for (j = 0; j < count; j++)
@@ -956,13 +989,25 @@ test_connection_sort_autoconnect_priority (void)
 /*****************************************************************************/
 
 #define MATCH_S390 "S390:"
+#define MATCH_DRIVER "DRIVER:"
 
 static NMMatchSpecMatchType
 _test_match_spec_device (const GSList *specs, const char *match_str)
 {
 	if (match_str && g_str_has_prefix (match_str, MATCH_S390))
-		return nm_match_spec_device (specs, NULL, NULL, NULL, &match_str[NM_STRLEN (MATCH_S390)]);
-	return nm_match_spec_device (specs, match_str, NULL, NULL, NULL);
+		return nm_match_spec_device (specs, NULL, NULL, NULL, NULL, NULL, &match_str[NM_STRLEN (MATCH_S390)]);
+	if (match_str && g_str_has_prefix (match_str, MATCH_DRIVER)) {
+		gs_free char *s = g_strdup (&match_str[NM_STRLEN (MATCH_DRIVER)]);
+		char *t;
+
+		t = strchr (s, '|');
+		if (t) {
+			t[0] = '\0';
+			t++;
+		}
+		return nm_match_spec_device (specs, NULL, NULL, s, t, NULL, NULL);
+	}
+	return nm_match_spec_device (specs, match_str, NULL, NULL, NULL, NULL, NULL);
 }
 
 static void
@@ -1067,6 +1112,10 @@ test_match_spec_device (void)
 	                            S ("em\\", "em\\*", "em\\1", "em\\11", "em\\2"),
 	                            NULL,
 	                            NULL);
+	_do_test_match_spec_device ("except:*",
+	                            NULL,
+	                            S (NULL),
+	                            S ("a"));
 	_do_test_match_spec_device ("interface-name:=em*",
 	                            S ("em*"),
 	                            NULL,
@@ -1108,6 +1157,23 @@ test_match_spec_device (void)
 	                            NULL,
 	                            S (NULL),
 	                            S (MATCH_S390"0.0.1000", MATCH_S390"0.0.1000,deadbeef", MATCH_S390"0.0.1000,0.0.1001", MATCH_S390"0.0.1000,0.0.1002"));
+
+	_do_test_match_spec_device ("driver:DRV",
+	                            S (MATCH_DRIVER"DRV", MATCH_DRIVER"DRV|1.6"),
+	                            S (MATCH_DRIVER"DR", MATCH_DRIVER"DR*"),
+	                            NULL);
+	_do_test_match_spec_device ("driver:DRV//",
+	                            S (MATCH_DRIVER"DRV/"),
+	                            S (MATCH_DRIVER"DRV/|1.6", MATCH_DRIVER"DR", MATCH_DRIVER"DR*"),
+	                            NULL);
+	_do_test_match_spec_device ("driver:DRV//*",
+	                            S (MATCH_DRIVER"DRV/", MATCH_DRIVER"DRV/|1.6"),
+	                            S (MATCH_DRIVER"DR", MATCH_DRIVER"DR*"),
+	                            NULL);
+	_do_test_match_spec_device ("driver:DRV//1.5*",
+	                            S (MATCH_DRIVER"DRV/|1.5", MATCH_DRIVER"DRV/|1.5.2"),
+	                            S (MATCH_DRIVER"DRV/", MATCH_DRIVER"DRV/|1.6", MATCH_DRIVER"DR", MATCH_DRIVER"DR*"),
+	                            NULL);
 #undef S
 }
 
@@ -1590,6 +1656,53 @@ test_stable_id_generated_complete (void)
 
 /*****************************************************************************/
 
+static void
+test_nm_utils_exp10 (void)
+{
+#define FLOAT_CMP(a, b) \
+	G_STMT_START { \
+		double _a = (a); \
+		double _b = (b); \
+		\
+		if (isinf (_b)) \
+			g_assert (isinf (_a)); \
+		else if (_b >= 0.0 && _b <= 0.0) \
+			g_assert (_a - _b < G_MINFLOAT); \
+		else { \
+			double _x = (_a) - (_b); \
+			g_assert (_b > 0.0); \
+			if (_x < 0.0) \
+				_x = -_x; \
+			g_assert (_x / _b <  1E-10); \
+		} \
+	} G_STMT_END
+
+	FLOAT_CMP (nm_utils_exp10 (G_MININT16),  0.0);
+	FLOAT_CMP (nm_utils_exp10 (-310),        0.0);
+	FLOAT_CMP (nm_utils_exp10 (-309),        0.0);
+	FLOAT_CMP (nm_utils_exp10 (-308),        1e-308);
+	FLOAT_CMP (nm_utils_exp10 (-307),        1e-307);
+	FLOAT_CMP (nm_utils_exp10 (-1),          1e-1);
+	FLOAT_CMP (nm_utils_exp10 (-2),          1e-2);
+	FLOAT_CMP (nm_utils_exp10 (0),           1e0);
+	FLOAT_CMP (nm_utils_exp10 (1),           1e1);
+	FLOAT_CMP (nm_utils_exp10 (2),           1e2);
+	FLOAT_CMP (nm_utils_exp10 (3),           1e3);
+	FLOAT_CMP (nm_utils_exp10 (4),           1e4);
+	FLOAT_CMP (nm_utils_exp10 (5),           1e5);
+	FLOAT_CMP (nm_utils_exp10 (6),           1e6);
+	FLOAT_CMP (nm_utils_exp10 (7),           1e7);
+	FLOAT_CMP (nm_utils_exp10 (122),         1e122);
+	FLOAT_CMP (nm_utils_exp10 (200),         1e200);
+	FLOAT_CMP (nm_utils_exp10 (307),         1e307);
+	FLOAT_CMP (nm_utils_exp10 (308),         1e308);
+	FLOAT_CMP (nm_utils_exp10 (309),         INFINITY);
+	FLOAT_CMP (nm_utils_exp10 (310),         INFINITY);
+	FLOAT_CMP (nm_utils_exp10 (G_MAXINT16),  INFINITY);
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int
@@ -1603,6 +1716,8 @@ main (int argc, char **argv)
 	g_test_add_func ("/general/nm_utils_ip6_address_same_prefix", test_nm_utils_ip6_address_same_prefix);
 	g_test_add_func ("/general/nm_utils_log_connection_diff", test_nm_utils_log_connection_diff);
 
+	g_test_add_func ("/general/exp10", test_nm_utils_exp10);
+
 	g_test_add_func ("/general/connection-match/basic", test_connection_match_basic);
 	g_test_add_func ("/general/connection-match/ip6-method", test_connection_match_ip6_method);
 	g_test_add_func ("/general/connection-match/ip6-method-ignore", test_connection_match_ip6_method_ignore);
diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c
index 505f9a5d..7e83625c 100644
--- a/src/tests/test-ip6-config.c
+++ b/src/tests/test-ip6-config.c
@@ -37,8 +37,8 @@ build_test_config (void)
 	config = nm_ip6_config_new (1);
 
 	nm_ip6_config_add_address (config, nmtst_platform_ip6_address ("abcd:1234:4321::cdde", "1:2:3:4::5", 64));
-	nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2"));
-	nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("2001:abba::", 16, "2001:abba::2234"));
+	nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2", NULL));
+	nm_ip6_config_add_route (config, nmtst_platform_ip6_route ("2001:abba::", 16, "2001:abba::2234", NULL));
 
 	nm_ip6_config_set_gateway (config, nmtst_inet6_from_string ("3001:abba::3234"));
 
@@ -74,7 +74,7 @@ test_subtract (void)
 	/* add a couple more things to the test config */
 	dst = build_test_config ();
 	nm_ip6_config_add_address (dst, nmtst_platform_ip6_address (expected_addr, NULL, expected_addr_plen));
-	nm_ip6_config_add_route (dst, nmtst_platform_ip6_route (expected_route_dest, expected_route_plen, expected_route_next_hop));
+	nm_ip6_config_add_route (dst, nmtst_platform_ip6_route (expected_route_dest, expected_route_plen, expected_route_next_hop, NULL));
 
 	expected_ns1 = *nmtst_inet6_from_string ("2222:3333:4444::5555");
 	nm_ip6_config_add_nameserver (dst, &expected_ns1);
@@ -139,7 +139,7 @@ test_compare_with_source (void)
 	nm_ip6_config_add_address (b, &addr);
 
 	/* Route */
-	route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2");
+	route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2", NULL);
 	route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip6_config_add_route (a, &route);
 
@@ -203,7 +203,7 @@ test_add_route_with_source (void)
 	a = nm_ip6_config_new (1);
 
 	/* Test that a higher priority source is not overwritten */
-	route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2");
+	route = *nmtst_platform_ip6_route ("abcd:1234:4321::", 24, "abcd:1234:4321:cdde::2", NULL);
 	route.rt_source = NM_IP_CONFIG_SOURCE_USER;
 	nm_ip6_config_add_route (a, &route);
 
diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c
index df43dd98..6650d26c 100644
--- a/src/tests/test-route-manager.c
+++ b/src/tests/test-route-manager.c
@@ -33,6 +33,10 @@ typedef struct {
 	int ifindex0, ifindex1;
 } test_fixture;
 
+NMRouteManager *route_manager_get (void);
+
+NM_DEFINE_SINGLETON_GETTER (NMRouteManager, route_manager_get, NM_TYPE_ROUTE_MANAGER);
+
 /*****************************************************************************/
 
 static void
@@ -60,7 +64,7 @@ setup_dev0_ip4 (int ifindex, guint mss_of_first_route, guint32 metric_of_second_
 	route.mss = 0;
 	g_array_append_val (routes, route);
 
-	nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE);
+	nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
 	g_array_free (routes, TRUE);
 }
 
@@ -75,16 +79,15 @@ setup_dev1_ip4 (int ifindex)
 
 	/* Add some route outside of route manager. The route manager
 	 * should get rid of it upon sync. */
-	if (!nm_platform_ip4_route_add (NM_PLATFORM_GET,
-	                                route.ifindex,
-	                                NM_IP_CONFIG_SOURCE_USER,
-	                                nmtst_inet4_from_string ("9.0.0.0"),
-	                                8,
-	                                INADDR_ANY,
-	                                0,
-	                                10,
-	                                route.mss))
-		g_assert_not_reached ();
+	nmtstp_ip4_route_add (NM_PLATFORM_GET,
+	                      route.ifindex,
+	                      NM_IP_CONFIG_SOURCE_USER,
+	                      nmtst_inet4_from_string ("9.0.0.0"),
+	                      8,
+	                      INADDR_ANY,
+	                      0,
+	                      10,
+	                      route.mss);
 
 	route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
 	inet_pton (AF_INET, "6.6.6.0", &route.network);
@@ -107,7 +110,7 @@ setup_dev1_ip4 (int ifindex)
 	route.metric = 22;
 	g_array_append_val (routes, route);
 
-	nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE);
+	nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
 	g_array_free (routes, TRUE);
 }
 
@@ -134,7 +137,7 @@ update_dev0_ip4 (int ifindex)
 	route.metric = 21;
 	g_array_append_val (routes, route);
 
-	nm_route_manager_ip4_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE);
+	nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
 	g_array_free (routes, TRUE);
 }
 
@@ -346,7 +349,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 	nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state2, routes->len, TRUE);
 	g_array_free (routes, TRUE);
 
-	nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex0);
+	nm_route_manager_route_flush (route_manager_get (), fixture->ifindex0);
 
 	/* 6.6.6.0/24 is now on dev1
 	 * 6.6.6.0/24 is also still on dev1 with bumped metric 21.
@@ -358,7 +361,7 @@ test_ip4 (test_fixture *fixture, gconstpointer user_data)
 	nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state3, routes->len, TRUE);
 	g_array_free (routes, TRUE);
 
-	nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex1);
+	nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1);
 
 	/* No routes left. */
 	routes = ip4_routes (fixture);
@@ -409,7 +412,7 @@ setup_dev0_ip6 (int ifindex)
 	                                       0);
 	g_array_append_val (routes, *route);
 
-	nm_route_manager_ip6_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE);
+	nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
 	g_array_free (routes, TRUE);
 }
 
@@ -421,15 +424,15 @@ setup_dev1_ip6 (int ifindex)
 
 	/* Add some route outside of route manager. The route manager
 	 * should get rid of it upon sync. */
-	if (!nm_platform_ip6_route_add (NM_PLATFORM_GET,
-	                                ifindex,
-	                                NM_IP_CONFIG_SOURCE_USER,
-	                                *nmtst_inet6_from_string ("2001:db8:8088::"),
-	                                48,
-	                                in6addr_any,
-	                                10,
-	                                0))
-		g_assert_not_reached ();
+	nmtstp_ip6_route_add (NM_PLATFORM_GET,
+	                      ifindex,
+	                      NM_IP_CONFIG_SOURCE_USER,
+	                      *nmtst_inet6_from_string ("2001:db8:8088::"),
+	                      48,
+	                      in6addr_any,
+	                      in6addr_any,
+	                      10,
+	                      0);
 
 	route = nmtst_platform_ip6_route_full ("2001:db8:8086::",
 	                                       48,
@@ -467,7 +470,7 @@ setup_dev1_ip6 (int ifindex)
 	                                       0);
 	g_array_append_val (routes, *route);
 
-	nm_route_manager_ip6_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE);
+	nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
 	g_array_free (routes, TRUE);
 }
 
@@ -514,7 +517,7 @@ update_dev0_ip6 (int ifindex)
 	                                       0);
 	g_array_append_val (routes, *route);
 
-	nm_route_manager_ip6_route_sync (nm_route_manager_get (), ifindex, routes, TRUE, TRUE);
+	nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE);
 	g_array_free (routes, TRUE);
 }
 
@@ -760,7 +763,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 	nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state2, routes->len, TRUE);
 	g_array_free (routes, TRUE);
 
-	nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex0);
+	nm_route_manager_route_flush (route_manager_get (), fixture->ifindex0);
 
 	/* 2001:db8:abad:c0de::/64 on dev1 is still there, went away from dev0
 	 * 2001:db8:8086::/48 is now on dev1
@@ -772,7 +775,7 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data)
 	nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state3, routes->len, TRUE);
 	g_array_free (routes, TRUE);
 
-	nm_route_manager_route_flush (nm_route_manager_get (), fixture->ifindex1);
+	nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1);
 
 	/* No routes left. */
 	routes = ip6_routes (fixture);
@@ -807,7 +810,7 @@ _assert_route_check (const NMPlatformVTableRoute *vtable, gboolean has, const NM
 				c.r6 = route->r6;
 			c.rx.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (c.rx.rt_source);
 		}
-		if (!r || vtable->route_cmp (r, &c) != 0) {
+		if (!r || vtable->route_cmp (r, &c, TRUE) != 0) {
 			g_error ("Invalid route. Expect %s, has %s",
 			         vtable->route_to_string (&c, NULL, 0),
 			         vtable->route_to_string (r, buf, sizeof (buf)));
@@ -836,7 +839,7 @@ test_ip4_full_sync (test_fixture *fixture, gconstpointer user_data)
 	g_array_set_size (routes, 2);
 	g_array_index (routes, NMPlatformIP4Route, 0) = r01;
 	g_array_index (routes, NMPlatformIP4Route, 1) = r02;
-	nm_route_manager_ip4_route_sync (nm_route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE);
+	nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE);
 
 	_assert_route_check (vtable, TRUE,  (const NMPlatformIPXRoute *) &r01);
 	_assert_route_check (vtable, TRUE,  (const NMPlatformIPXRoute *) &r02);
@@ -848,7 +851,7 @@ test_ip4_full_sync (test_fixture *fixture, gconstpointer user_data)
 	_assert_route_check (vtable, TRUE,  (const NMPlatformIPXRoute *) &r02);
 	_assert_route_check (vtable, TRUE,  (const NMPlatformIPXRoute *) &r03);
 
-	nm_route_manager_ip4_route_sync (nm_route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE);
+	nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE);
 
 	_assert_route_check (vtable, TRUE,  (const NMPlatformIPXRoute *) &r01);
 	_assert_route_check (vtable, TRUE,  (const NMPlatformIPXRoute *) &r02);
@@ -856,13 +859,13 @@ test_ip4_full_sync (test_fixture *fixture, gconstpointer user_data)
 
 	g_array_set_size (routes, 1);
 
-	nm_route_manager_ip4_route_sync (nm_route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE);
+	nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE);
 
 	_assert_route_check (vtable, TRUE,  (const NMPlatformIPXRoute *) &r01);
 	_assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02);
 	_assert_route_check (vtable, TRUE,  (const NMPlatformIPXRoute *) &r03);
 
-	nm_route_manager_ip4_route_sync (nm_route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE);
+	nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE);
 
 	_assert_route_check (vtable, TRUE,  (const NMPlatformIPXRoute *) &r01);
 	_assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02);
diff --git a/src/tests/test-systemd.c b/src/tests/test-systemd.c
index 07887960..f6cf9b58 100644
--- a/src/tests/test-systemd.c
+++ b/src/tests/test-systemd.c
@@ -54,6 +54,8 @@ _nm_log_impl (const char *file,
               NMLogLevel level,
               NMLogDomain domain,
               int error,
+              const char *ifname,
+              const char *con_uuid,
               const char *fmt,
               ...)
 {
diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c
index 3d4536e7..ecc82068 100644
--- a/src/vpn/nm-vpn-connection.c
+++ b/src/vpn/nm-vpn-connection.c
@@ -40,6 +40,7 @@
 #include "NetworkManagerUtils.h"
 #include "settings/nm-settings-connection.h"
 #include "nm-dispatcher.h"
+#include "nm-netns.h"
 #include "settings/nm-agent-manager.h"
 #include "nm-core-internal.h"
 #include "nm-pacrunner-manager.h"
@@ -109,7 +110,7 @@ typedef struct {
 
 	VpnState vpn_state;
 	guint dispatcher_id;
-	NMVpnConnectionStateReason failure_reason;
+	NMActiveConnectionStateReason failure_reason;
 
 	NMVpnServiceState service_state;
 	guint start_timeout;
@@ -120,13 +121,15 @@ typedef struct {
 	/* Firewall */
 	NMFirewallManagerCallId fw_call;
 
-	NMDefaultRouteManager *default_route_manager;
-	NMRouteManager *route_manager;
+	NMNetns *netns;
+
 	GDBusProxy *proxy;
 	GCancellable *cancellable;
 	GVariant *connect_hash;
 	guint connect_timeout;
 	NMProxyConfig *proxy_config;
+	NMPacrunnerManager *pacrunner_manager;
+	NMPacrunnerCallId *pacrunner_call_id;
 	gboolean has_ip4;
 	NMIP4Config *ip4_config;
 	guint32 ip4_internal_gw;
@@ -159,13 +162,13 @@ struct _NMVpnConnectionClass {
 	/* Signals */
 	void (*vpn_state_changed) (NMVpnConnection *self,
 	                           NMVpnConnectionState new_state,
-	                           NMVpnConnectionStateReason reason);
+	                           NMActiveConnectionStateReason reason);
 
 	/* not exported over D-Bus */
 	void (*internal_state_changed) (NMVpnConnection *self,
 	                                NMVpnConnectionState new_state,
 	                                NMVpnConnectionState old_state,
-	                                NMVpnConnectionStateReason reason);
+	                                NMActiveConnectionStateReason reason);
 
 	void (*internal_failed_retry)  (NMVpnConnection *self);
 };
@@ -189,7 +192,7 @@ static void plugin_interactive_secrets_required (NMVpnConnection *self,
 
 static void _set_vpn_state (NMVpnConnection *self,
                             VpnState vpn_state,
-                            NMVpnConnectionStateReason reason,
+                            NMActiveConnectionStateReason reason,
                             gboolean quitting);
 
 /*****************************************************************************/
@@ -200,18 +203,15 @@ static void _set_vpn_state (NMVpnConnection *self,
 #define __NMLOG_prefix_buf_len 128
 
 static const char *
-__LOG_create_prefix (char *buf, NMVpnConnection *self)
+__LOG_create_prefix (char *buf, NMVpnConnection *self, NMConnection *con)
 {
 	NMVpnConnectionPrivate *priv;
-	NMConnection *con;
 	const char *id;
 
 	if (!self)
 		return _NMLOG_PREFIX_NAME;
 
 	priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-
-	con = NM_CONNECTION (_get_settings_connection (self, TRUE));
 	id = con ? nm_connection_get_id (con) : NULL;
 
 	g_snprintf (buf, __NMLOG_prefix_buf_len,
@@ -236,13 +236,16 @@ __LOG_create_prefix (char *buf, NMVpnConnection *self)
 #define _NMLOG(level, ...) \
     G_STMT_START { \
         const NMLogLevel __level = (level); \
+        NMConnection *__con = (self) ? (NMConnection *) _get_settings_connection (self, TRUE) : NULL; \
         \
         if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
             char __prefix[__NMLOG_prefix_buf_len]; \
             \
             _nm_log (__level, _NMLOG_DOMAIN, 0, \
+                     (self) ? NM_VPN_CONNECTION_GET_PRIVATE (self)->ip_iface : NULL, \
+                     (__con) ? nm_connection_get_uuid (__con) : NULL, \
                      "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
-                     __LOG_create_prefix (__prefix, self) \
+                     __LOG_create_prefix (__prefix, (self), __con) \
                      _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
     } G_STMT_END
@@ -358,21 +361,6 @@ disconnect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 }
 
 static void
-call_plugin_disconnect (NMVpnConnection *self)
-{
-	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-
-	g_dbus_proxy_call (priv->proxy,
-	                   "Disconnect",
-	                   NULL,
-	                   G_DBUS_CALL_FLAGS_NONE,
-	                   -1,
-	                   priv->cancellable,
-	                   (GAsyncReadyCallback) disconnect_cb,
-	                   g_object_ref (self));
-}
-
-static void
 fw_call_cleanup (NMVpnConnection *self)
 {
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
@@ -406,9 +394,9 @@ vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev)
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
 	if (priv->ip_ifindex) {
-		nm_platform_link_set_down (NM_PLATFORM_GET, priv->ip_ifindex);
-		nm_route_manager_route_flush (priv->route_manager, priv->ip_ifindex);
-		nm_platform_address_flush (NM_PLATFORM_GET, priv->ip_ifindex);
+		nm_platform_link_set_down (nm_netns_get_platform (priv->netns), priv->ip_ifindex);
+		nm_route_manager_route_flush (nm_netns_get_route_manager (priv->netns), priv->ip_ifindex);
+		nm_platform_address_flush (nm_netns_get_platform (priv->netns), priv->ip_ifindex);
 	}
 
 	remove_parent_device_config (self, parent_dev);
@@ -447,7 +435,7 @@ dispatcher_pre_down_done (guint call_id, gpointer user_data)
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
 	priv->dispatcher_id = 0;
-	_set_vpn_state (self, STATE_DISCONNECTED, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+	_set_vpn_state (self, STATE_DISCONNECTED, NM_ACTIVE_CONNECTION_STATE_REASON_USER_DISCONNECTED, FALSE);
 }
 
 static void
@@ -457,7 +445,7 @@ dispatcher_pre_up_done (guint call_id, gpointer user_data)
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
 	priv->dispatcher_id = 0;
-	_set_vpn_state (self, STATE_ACTIVATED, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+	_set_vpn_state (self, STATE_ACTIVATED, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE);
 }
 
 static void
@@ -474,13 +462,14 @@ dispatcher_cleanup (NMVpnConnection *self)
 static void
 _set_vpn_state (NMVpnConnection *self,
                 VpnState vpn_state,
-                NMVpnConnectionStateReason reason,
+                NMActiveConnectionStateReason reason,
                 gboolean quitting)
 {
 	NMVpnConnectionPrivate *priv;
 	VpnState old_vpn_state;
 	NMVpnConnectionState new_external_state, old_external_state;
 	NMDevice *parent_dev = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (self));
+	NMConnection *applied;
 
 	g_return_if_fail (NM_IS_VPN_CONNECTION (self));
 
@@ -500,15 +489,16 @@ _set_vpn_state (NMVpnConnection *self,
 
 	/* Update active connection base class state */
 	nm_active_connection_set_state (NM_ACTIVE_CONNECTION (self),
-	                                _state_to_ac_state (vpn_state));
+	                                _state_to_ac_state (vpn_state),
+	                                reason);
 
 	/* Clear any in-progress secrets request */
 	cancel_get_secrets (self);
 
 	dispatcher_cleanup (self);
 
-	nm_default_route_manager_ip4_update_default_route (priv->default_route_manager, self);
-	nm_default_route_manager_ip6_update_default_route (priv->default_route_manager, self);
+	nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self);
+	nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self);
 
 	/* The connection gets destroyed by the VPN manager when it enters the
 	 * disconnected/failed state, but we need to keep it around for a bit
@@ -535,7 +525,7 @@ _set_vpn_state (NMVpnConnection *self,
 		 */
 		break;
 	case STATE_PRE_UP:
-		if (!nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_PRE_UP,
+		if (!nm_dispatcher_call_vpn (NM_DISPATCHER_ACTION_VPN_PRE_UP,
 		                             _get_settings_connection (self, FALSE),
 		                             _get_applied_connection (self),
 		                             parent_dev,
@@ -551,13 +541,15 @@ _set_vpn_state (NMVpnConnection *self,
 		}
 		break;
 	case STATE_ACTIVATED:
+		applied = _get_applied_connection (self);
+
 		/* Secrets no longer needed now that we're connected */
 		nm_active_connection_clear_secrets (NM_ACTIVE_CONNECTION (self));
 
 		/* Let dispatcher scripts know we're up and running */
-		nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_UP,
+		nm_dispatcher_call_vpn (NM_DISPATCHER_ACTION_VPN_UP,
 		                        _get_settings_connection (self, FALSE),
-		                        _get_applied_connection (self),
+		                        applied,
 		                        parent_dev,
 		                        priv->ip_iface,
 		                        priv->proxy_config,
@@ -568,27 +560,35 @@ _set_vpn_state (NMVpnConnection *self,
 		                        NULL);
 
 		if (priv->proxy_config) {
-			nm_pacrunner_manager_send (nm_pacrunner_manager_get (),
-			                           priv->ip_iface,
-			                           priv->proxy_config,
-			                           priv->ip4_config,
-			                           priv->ip6_config);
+			nm_pacrunner_manager_remove_clear (priv->pacrunner_manager,
+			                                   &priv->pacrunner_call_id);
+			if (!priv->pacrunner_manager) {
+				/* the pending call doesn't keep NMPacrunnerManager alive.
+				 * Take a reference to it. */
+				priv->pacrunner_manager = g_object_ref (nm_pacrunner_manager_get ());
+			}
+			priv->pacrunner_call_id = nm_pacrunner_manager_send (priv->pacrunner_manager,
+			                                                     priv->ip_iface,
+			                                                     priv->proxy_config,
+			                                                     priv->ip4_config,
+			                                                     priv->ip6_config);
 		}
 		break;
 	case STATE_DEACTIVATING:
+		applied = _get_applied_connection (self);
 		if (quitting) {
-			nm_dispatcher_call_vpn_sync (DISPATCHER_ACTION_VPN_PRE_DOWN,
+			nm_dispatcher_call_vpn_sync (NM_DISPATCHER_ACTION_VPN_PRE_DOWN,
 			                             _get_settings_connection (self, FALSE),
-			                             _get_applied_connection (self),
+			                             applied,
 			                             parent_dev,
 			                             priv->ip_iface,
 			                             priv->proxy_config,
 			                             priv->ip4_config,
 			                             priv->ip6_config);
 		} else {
-			if (!nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_PRE_DOWN,
+			if (!nm_dispatcher_call_vpn (NM_DISPATCHER_ACTION_VPN_PRE_DOWN,
 			                             _get_settings_connection (self, FALSE),
-			                             _get_applied_connection (self),
+			                             applied,
 			                             parent_dev,
 			                             priv->ip_iface,
 			                             priv->proxy_config,
@@ -602,8 +602,8 @@ _set_vpn_state (NMVpnConnection *self,
 			}
 		}
 
-		/* Remove config from PacRunner */
-		nm_pacrunner_manager_remove (nm_pacrunner_manager_get(), priv->ip_iface);
+		nm_pacrunner_manager_remove_clear (priv->pacrunner_manager,
+		                                   &priv->pacrunner_call_id);
 		break;
 	case STATE_FAILED:
 	case STATE_DISCONNECTED:
@@ -611,7 +611,7 @@ _set_vpn_state (NMVpnConnection *self,
 		    && old_vpn_state <= STATE_DEACTIVATING) {
 			/* Let dispatcher scripts know we're about to go down */
 			if (quitting) {
-				nm_dispatcher_call_vpn_sync (DISPATCHER_ACTION_VPN_DOWN,
+				nm_dispatcher_call_vpn_sync (NM_DISPATCHER_ACTION_VPN_DOWN,
 				                             _get_settings_connection (self, FALSE),
 				                             _get_applied_connection (self),
 				                             parent_dev,
@@ -620,7 +620,7 @@ _set_vpn_state (NMVpnConnection *self,
 				                             NULL,
 				                             NULL);
 			} else {
-				nm_dispatcher_call_vpn (DISPATCHER_ACTION_VPN_DOWN,
+				nm_dispatcher_call_vpn (NM_DISPATCHER_ACTION_VPN_DOWN,
 				                        _get_settings_connection (self, FALSE),
 				                        _get_applied_connection (self),
 				                        parent_dev,
@@ -635,7 +635,17 @@ _set_vpn_state (NMVpnConnection *self,
 		}
 
 		/* Tear down and clean up the connection */
-		call_plugin_disconnect (self);
+		if (priv->proxy) {
+			g_dbus_proxy_call (priv->proxy,
+			                   "Disconnect",
+			                   NULL,
+			                   G_DBUS_CALL_FLAGS_NONE,
+			                   -1,
+			                   priv->cancellable,
+			                   (GAsyncReadyCallback) disconnect_cb,
+			                   g_object_ref (self));
+		}
+
 		vpn_cleanup (self, parent_dev);
 		/* fall through */
 	default:
@@ -679,12 +689,12 @@ device_state_changed (NMActiveConnection *active,
 	if (new_state <= NM_DEVICE_STATE_DISCONNECTED) {
 		_set_vpn_state (NM_VPN_CONNECTION (active),
 		                STATE_DISCONNECTED,
-		                NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,
+		                NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,
 		                FALSE);
 	} else if (new_state == NM_DEVICE_STATE_FAILED) {
 		_set_vpn_state (NM_VPN_CONNECTION (active),
 		                STATE_FAILED,
-		                NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,
+		                NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,
 		                FALSE);
 	}
 
@@ -845,13 +855,13 @@ plugin_failed (NMVpnConnection *self, guint reason)
 
 	switch (reason) {
 	case NM_VPN_PLUGIN_FAILURE_LOGIN_FAILED:
-		priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED;
+		priv->failure_reason = NM_ACTIVE_CONNECTION_STATE_REASON_LOGIN_FAILED;
 		break;
 	case NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG:
-		priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID;
+		priv->failure_reason = NM_ACTIVE_CONNECTION_STATE_REASON_IP_CONFIG_INVALID;
 		break;
 	default:
-		priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN;
+		priv->failure_reason = NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN;
 		break;
 	}
 }
@@ -884,23 +894,6 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_vpn_state_to_string, VpnState,
 );
 #define vpn_state_to_string(state) NM_UTILS_LOOKUP_STR (_vpn_state_to_string, state)
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_vpn_reason_to_string, NMVpnConnectionStateReason,
-	NM_UTILS_LOOKUP_DEFAULT (NULL),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_UNKNOWN,               "unknown"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_NONE,                  "none"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_USER_DISCONNECTED,     "user-disconnected"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED,   "device-disconnected"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED,       "service-stopped"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID,     "ip-config-invalid"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT,       "connect-timeout"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT, "service-start-timeout"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED,  "service-start-failed"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS,            "no-secrets"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_LOGIN_FAILED,          "login-failed"),
-	NM_UTILS_LOOKUP_STR_ITEM (NM_VPN_CONNECTION_STATE_REASON_CONNECTION_REMOVED,    "connection-removed"),
-);
-#define vpn_reason_to_string(reason) NM_UTILS_LOOKUP_STR (_vpn_reason_to_string, reason)
-
 static void
 plugin_state_changed (NMVpnConnection *self, NMVpnServiceState new_service_state)
 {
@@ -920,12 +913,10 @@ plugin_state_changed (NMVpnConnection *self, NMVpnServiceState new_service_state
 		if ((priv->vpn_state >= STATE_WAITING) && (priv->vpn_state <= STATE_ACTIVATED)) {
 			VpnState old_state = priv->vpn_state;
 
-			_LOGI ("VPN plugin: state change reason: %s (%d)",
-			       vpn_reason_to_string (priv->failure_reason), priv->failure_reason);
 			_set_vpn_state (self, STATE_FAILED, priv->failure_reason, FALSE);
 
 			/* Reset the failure reason */
-			priv->failure_reason = NM_VPN_CONNECTION_STATE_REASON_UNKNOWN;
+			priv->failure_reason = NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN;
 
 			/* If the connection failed, the service cannot persist, but the
 			 * connection can persist, ask listeners to re-activate the connection.
@@ -938,7 +929,7 @@ plugin_state_changed (NMVpnConnection *self, NMVpnServiceState new_service_state
 	} else if (new_service_state == NM_VPN_SERVICE_STATE_STARTING &&
 	           old_service_state == NM_VPN_SERVICE_STATE_STARTED) {
 		/* The VPN service got disconnected and is attempting to reconnect */
-		_set_vpn_state (self, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE);
+		_set_vpn_state (self, STATE_CONNECT, NM_ACTIVE_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE);
 	}
 }
 
@@ -1103,10 +1094,13 @@ nm_vpn_connection_apply_config (NMVpnConnection *self)
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 
 	if (priv->ip_ifindex > 0) {
-		nm_platform_link_set_up (NM_PLATFORM_GET, priv->ip_ifindex, NULL);
+		nm_platform_link_set_up (nm_netns_get_platform (priv->netns), priv->ip_ifindex, NULL);
 
 		if (priv->ip4_config) {
-			if (!nm_ip4_config_commit (priv->ip4_config, priv->ip_ifindex,
+			if (!nm_ip4_config_commit (priv->ip4_config,
+			                           nm_netns_get_platform (priv->netns),
+			                           nm_netns_get_route_manager (priv->netns),
+			                           priv->ip_ifindex,
 			                           TRUE,
 			                           nm_vpn_connection_get_ip4_route_metric (self)))
 				return FALSE;
@@ -1114,23 +1108,25 @@ nm_vpn_connection_apply_config (NMVpnConnection *self)
 
 		if (priv->ip6_config) {
 			if (!nm_ip6_config_commit (priv->ip6_config,
+			                           nm_netns_get_platform (priv->netns),
+			                           nm_netns_get_route_manager (priv->netns),
 			                           priv->ip_ifindex,
 			                           TRUE))
 				return FALSE;
 		}
 
-		if (priv->mtu && priv->mtu != nm_platform_link_get_mtu (NM_PLATFORM_GET, priv->ip_ifindex))
-			nm_platform_link_set_mtu (NM_PLATFORM_GET, priv->ip_ifindex, priv->mtu);
+		if (priv->mtu && priv->mtu != nm_platform_link_get_mtu (nm_netns_get_platform (priv->netns), priv->ip_ifindex))
+			nm_platform_link_set_mtu (nm_netns_get_platform (priv->netns), priv->ip_ifindex, priv->mtu);
 	}
 
 	apply_parent_device_config (self);
 
-	nm_default_route_manager_ip4_update_default_route (priv->default_route_manager, self);
-	nm_default_route_manager_ip6_update_default_route (priv->default_route_manager, self);
+	nm_default_route_manager_ip4_update_default_route (nm_netns_get_default_route_manager (priv->netns), self);
+	nm_default_route_manager_ip6_update_default_route (nm_netns_get_default_route_manager (priv->netns), self);
 
 	_LOGI ("VPN connection: (IP Config Get) complete");
 	if (priv->vpn_state < STATE_PRE_UP)
-		_set_vpn_state (self, STATE_PRE_UP, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+		_set_vpn_state (self, STATE_PRE_UP, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE);
 	return TRUE;
 }
 
@@ -1143,7 +1139,7 @@ _cleanup_failed_config (NMVpnConnection *self)
 	nm_exported_object_clear_and_unexport (&priv->ip6_config);
 
 	_LOGW ("VPN connection: did not receive valid IP config information");
-	_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE);
+	_set_vpn_state (self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_IP_CONFIG_INVALID, FALSE);
 }
 
 static void
@@ -1279,10 +1275,10 @@ process_generic_config (NMVpnConnection *self, GVariant *dict)
 
 	if (priv->ip_iface) {
 		/* Grab the interface index for address/routing operations */
-		priv->ip_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface);
+		priv->ip_ifindex = nm_platform_link_get_ifindex (nm_netns_get_platform (priv->netns), priv->ip_iface);
 		if (priv->ip_ifindex <= 0) {
-			nm_platform_process_events (NM_PLATFORM_GET);
-			priv->ip_ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, priv->ip_iface);
+			nm_platform_process_events (nm_netns_get_platform (priv->netns));
+			priv->ip_ifindex = nm_platform_link_get_ifindex (nm_netns_get_platform (priv->netns), priv->ip_iface);
 		}
 		if (priv->ip_ifindex <= 0) {
 			_LOGE ("failed to look up VPN interface index for \"%s\"", priv->ip_iface);
@@ -1348,7 +1344,7 @@ nm_vpn_connection_config_get (NMVpnConnection *self, GVariant *dict)
 	_LOGI ("VPN connection: (IP Config Get) reply received.");
 
 	if (priv->vpn_state == STATE_CONNECT)
-		_set_vpn_state (self, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+		_set_vpn_state (self, STATE_IP_CONFIG_GET, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE);
 
 	if (!process_generic_config (self, dict))
 		return;
@@ -1408,7 +1404,7 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 	g_return_if_fail (dict && g_variant_is_of_type (dict, G_VARIANT_TYPE_VARDICT));
 
 	if (priv->vpn_state == STATE_CONNECT)
-		_set_vpn_state (self, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+		_set_vpn_state (self, STATE_IP_CONFIG_GET, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE);
 
 	if (priv->vpn_state > STATE_ACTIVATED) {
 		_LOGI ("VPN connection: (IP4 Config Get) ignoring, the connection is no longer active");
@@ -1582,7 +1578,7 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict)
 	_LOGI ("VPN connection: (IP6 Config Get) reply received");
 
 	if (priv->vpn_state == STATE_CONNECT)
-		_set_vpn_state (self, STATE_IP_CONFIG_GET, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+		_set_vpn_state (self, STATE_IP_CONFIG_GET, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE);
 
 	if (priv->vpn_state > STATE_ACTIVATED) {
 		_LOGI ("VPN connection: (IP6 Config Get) ignoring, the connection is no longer active");
@@ -1735,7 +1731,7 @@ connect_timeout_cb (gpointer user_data)
 	if (priv->vpn_state == STATE_CONNECT ||
 	    priv->vpn_state == STATE_IP_CONFIG_GET) {
 		_LOGW ("VPN connection: connect timeout exceeded.");
-		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE);
+		_set_vpn_state (self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_CONNECT_TIMEOUT, FALSE);
 	}
 
 	return FALSE;
@@ -1785,7 +1781,7 @@ connect_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_data)
 		g_dbus_error_strip_remote_error (error);
 		_LOGW ("VPN connection: failed to connect: '%s'",
 		       error->message);
-		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
+		_set_vpn_state (self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
 	} else
 		connect_success (self);
 }
@@ -1823,7 +1819,7 @@ connect_interactive_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_d
 		g_dbus_error_strip_remote_error (error);
 		_LOGW ("VPN connection: failed to connect interactively: '%s'",
 		       error->message);
-		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
+		_set_vpn_state (self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
 	} else
 		connect_success (self);
 }
@@ -1898,7 +1894,7 @@ really_activate (NMVpnConnection *self, const char *username)
 		                   self);
 	}
 
-	_set_vpn_state (self, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+	_set_vpn_state (self, STATE_CONNECT, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE);
 }
 
 static void
@@ -2004,7 +2000,7 @@ _name_owner_changed (GObject *object,
 		_nm_dbus_signal_connect (priv->proxy, "Ip6Config", G_VARIANT_TYPE ("(a{sv})"),
 		                         G_CALLBACK (ip6_config_cb), self);
 
-		_set_vpn_state (self, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+		_set_vpn_state (self, STATE_NEED_AUTH, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE);
 
 		/* Kick off the secrets requests; first we get existing system secrets
 		 * and ask the plugin if these are sufficient, next we get all existing
@@ -2016,7 +2012,7 @@ _name_owner_changed (GObject *object,
 		/* service went away */
 		priv->service_running = FALSE;
 		_LOGI ("VPN service disappeared");
-		nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED, FALSE);
+		nm_vpn_connection_disconnect (self, NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_STOPPED, FALSE);
 	}
 
 	g_free (owner);
@@ -2031,7 +2027,7 @@ _daemon_exec_timeout (gpointer data)
 
 	_LOGW ("Timed out waiting for the service to start");
 	priv->start_timeout = 0;
-	nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT, FALSE);
+	nm_vpn_connection_disconnect (self, NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT, FALSE);
 	return G_SOURCE_REMOVE;
 }
 
@@ -2158,7 +2154,7 @@ on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_data)
 		       error->message);
 		_set_vpn_state (self,
 		                STATE_FAILED,
-		                NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED,
+		                NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED,
 		                FALSE);
 		return;
 	}
@@ -2176,7 +2172,7 @@ on_proxy_acquired (GObject *object, GAsyncResult *result, gpointer user_data)
 		_LOGW ("Could not launch the VPN service. error: %s.",
 		       error->message);
 
-		nm_vpn_connection_disconnect (self, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
+		nm_vpn_connection_disconnect (self, NM_ACTIVE_CONNECTION_STATE_REASON_SERVICE_START_FAILED, FALSE);
 	}
 }
 
@@ -2226,7 +2222,7 @@ nm_vpn_connection_activate (NMVpnConnection *self,
 	                          (GAsyncReadyCallback) on_proxy_acquired,
 	                          self);
 
-	_set_vpn_state (self, STATE_PREPARE, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+	_set_vpn_state (self, STATE_PREPARE, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE);
 }
 
 NMVpnConnectionState
@@ -2350,7 +2346,7 @@ nm_vpn_connection_get_ip6_internal_gateway (NMVpnConnection *self)
 
 void
 nm_vpn_connection_disconnect (NMVpnConnection *self,
-                              NMVpnConnectionStateReason reason,
+                              NMActiveConnectionStateReason reason,
                               gboolean quitting)
 {
 	g_return_if_fail (NM_IS_VPN_CONNECTION (self));
@@ -2360,7 +2356,7 @@ nm_vpn_connection_disconnect (NMVpnConnection *self,
 
 gboolean
 nm_vpn_connection_deactivate (NMVpnConnection *self,
-                              NMVpnConnectionStateReason reason,
+                              NMActiveConnectionStateReason reason,
                               gboolean quitting)
 {
 	NMVpnConnectionPrivate *priv;
@@ -2399,7 +2395,7 @@ plugin_need_secrets_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_d
 		_LOGE ("plugin NeedSecrets request #%d failed: %s",
 		       priv->secrets_idx + 1,
 		       error->message);
-		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
+		_set_vpn_state (self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
 		return;
 	}
 
@@ -2415,7 +2411,7 @@ plugin_need_secrets_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_d
 	/* More secrets required */
 	if (priv->secrets_idx == SECRETS_REQ_NEW) {
 		_LOGE ("final secrets request failed to provide sufficient secrets");
-		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
+		_set_vpn_state (self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
 	} else {
 		_LOGD ("service indicated additional secrets required");
 		get_secrets (self, priv->secrets_idx + 1, NULL);
@@ -2441,9 +2437,9 @@ plugin_new_secrets_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_da
 		g_dbus_error_strip_remote_error (error);
 		_LOGE ("sending new secrets to the plugin failed: %s",
 		       error->message);
-		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
+		_set_vpn_state (self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
 	} else
-		_set_vpn_state (self, STATE_CONNECT, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+		_set_vpn_state (self, STATE_CONNECT, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE);
 }
 
 static void
@@ -2473,7 +2469,7 @@ get_secrets_cb (NMSettingsConnection *connection,
 	if (error && priv->secrets_idx >= SECRETS_REQ_NEW) {
 		_LOGE ("Failed to request VPN secrets #%d: %s",
 		       priv->secrets_idx + 1, error->message);
-		_set_vpn_state (self, STATE_FAILED, NM_VPN_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
+		_set_vpn_state (self, STATE_FAILED, NM_ACTIVE_CONNECTION_STATE_REASON_NO_SECRETS, FALSE);
 		return;
 	}
 
@@ -2574,7 +2570,7 @@ plugin_interactive_secrets_required (NMVpnConnection *self,
 	                  priv->vpn_state == STATE_NEED_AUTH);
 
 	priv->secrets_idx = SECRETS_REQ_INTERACTIVE;
-	_set_vpn_state (self, STATE_NEED_AUTH, NM_VPN_CONNECTION_STATE_REASON_NONE, FALSE);
+	_set_vpn_state (self, STATE_NEED_AUTH, NM_ACTIVE_CONNECTION_STATE_REASON_NONE, FALSE);
 
 	/* Copy hints and add message to the end */
 	hints = g_malloc0 (sizeof (char *) * (secrets_len + 2));
@@ -2627,8 +2623,7 @@ nm_vpn_connection_init (NMVpnConnection *self)
 
 	priv->vpn_state = STATE_WAITING;
 	priv->secrets_idx = SECRETS_REQ_SYSTEM;
-	priv->default_route_manager = g_object_ref (nm_default_route_manager_get ());
-	priv->route_manager = g_object_ref (nm_route_manager_get ());
+	priv->netns = g_object_ref (nm_netns_get ());
 }
 
 static void
@@ -2647,10 +2642,8 @@ dispose (GObject *object)
 
 	cancel_get_secrets (self);
 
-	if (priv->cancellable) {
-		g_cancellable_cancel (priv->cancellable);
-		g_clear_object (&priv->cancellable);
-	}
+	nm_clear_g_cancellable (&priv->cancellable);
+
 	g_clear_object (&priv->proxy_config);
 	nm_exported_object_clear_and_unexport (&priv->ip4_config);
 	nm_exported_object_clear_and_unexport (&priv->ip6_config);
@@ -2659,10 +2652,11 @@ dispose (GObject *object)
 
 	fw_call_cleanup (self);
 
-	G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object);
+	nm_pacrunner_manager_remove_clear (priv->pacrunner_manager,
+	                                   &priv->pacrunner_call_id);
+	g_clear_object (&priv->pacrunner_manager);
 
-	g_clear_object (&priv->default_route_manager);
-	g_clear_object (&priv->route_manager);
+	G_OBJECT_CLASS (nm_vpn_connection_parent_class)->dispose (object);
 }
 
 static void
@@ -2677,6 +2671,8 @@ finalize (GObject *object)
 	g_free (priv->ip6_external_gw);
 
 	G_OBJECT_CLASS (nm_vpn_connection_parent_class)->finalize (object);
+
+	g_clear_object (&priv->netns);
 }
 
 static gboolean
diff --git a/src/vpn/nm-vpn-connection.h b/src/vpn/nm-vpn-connection.h
index 8393f081..038d0efd 100644
--- a/src/vpn/nm-vpn-connection.h
+++ b/src/vpn/nm-vpn-connection.h
@@ -62,10 +62,10 @@ const char *         nm_vpn_connection_get_banner      (NMVpnConnection *self);
 const gchar *        nm_vpn_connection_get_service     (NMVpnConnection *self);
 
 gboolean             nm_vpn_connection_deactivate      (NMVpnConnection *self,
-                                                        NMVpnConnectionStateReason reason,
+                                                        NMActiveConnectionStateReason reason,
                                                         gboolean quitting);
 void                 nm_vpn_connection_disconnect      (NMVpnConnection *self,
-                                                        NMVpnConnectionStateReason reason,
+                                                        NMActiveConnectionStateReason reason,
                                                         gboolean quitting);
 
 NMProxyConfig *      nm_vpn_connection_get_proxy_config (NMVpnConnection *self);