about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/NetworkManagerUtils.c24
-rw-r--r--src/NetworkManagerUtils.h1
-rw-r--r--src/devices/bluetooth/nm-bluez-device.c3
-rw-r--r--src/devices/bluetooth/nm-bluez5-manager.c2
-rw-r--r--src/devices/nm-device-ethernet.c4
-rw-r--r--src/devices/nm-device-macsec.c4
-rw-r--r--src/devices/nm-device-vlan.c6
-rw-r--r--src/devices/nm-device.c261
-rw-r--r--src/devices/nm-device.h3
-rw-r--r--src/devices/tests/test-lldp.c2
-rw-r--r--src/devices/wifi/nm-device-wifi.c8
-rw-r--r--src/devices/wwan/nm-modem.c4
-rw-r--r--src/dns/nm-dns-dnsmasq.c6
-rw-r--r--src/dns/nm-dns-manager.c350
-rw-r--r--src/dns/nm-dns-manager.h19
-rw-r--r--src/dns/nm-dns-systemd-resolved.c5
-rw-r--r--src/main-utils.c2
-rw-r--r--src/nm-act-request.c109
-rw-r--r--src/nm-act-request.h20
-rw-r--r--src/nm-active-connection.c66
-rw-r--r--src/nm-active-connection.h14
-rw-r--r--src/nm-checkpoint.c26
-rw-r--r--src/nm-config-data.c24
-rw-r--r--src/nm-config-data.h2
-rw-r--r--src/nm-config.h2
-rw-r--r--src/nm-core-utils.c10
-rw-r--r--src/nm-firewall-manager.c8
-rw-r--r--src/nm-ip4-config.c134
-rw-r--r--src/nm-ip4-config.h137
-rw-r--r--src/nm-ip6-config.c80
-rw-r--r--src/nm-manager.c520
-rw-r--r--src/nm-manager.h15
-rw-r--r--src/nm-pacrunner-manager.c6
-rw-r--r--src/nm-policy.c556
-rw-r--r--src/nm-sleep-monitor.c2
-rw-r--r--src/nm-types.h7
-rw-r--r--src/platform/nm-fake-platform.c28
-rw-r--r--src/platform/nm-linux-platform.c460
-rw-r--r--src/platform/nm-platform.c383
-rw-r--r--src/platform/nm-platform.h114
-rw-r--r--src/platform/nmp-netns.c5
-rw-r--r--src/platform/nmp-object.c103
-rw-r--r--src/platform/nmp-object.h85
-rw-r--r--src/platform/tests/test-common.c54
-rw-r--r--src/platform/tests/test-common.h20
-rw-r--r--src/platform/tests/test-link.c15
-rw-r--r--src/platform/tests/test-nmp-object.c91
-rw-r--r--src/platform/tests/test-route.c208
-rw-r--r--src/ppp/nm-ppp-manager.c4
-rw-r--r--src/settings/nm-agent-manager.c32
-rw-r--r--src/settings/nm-agent-manager.h6
-rw-r--r--src/settings/nm-secret-agent.c68
-rw-r--r--src/settings/nm-secret-agent.h33
-rw-r--r--src/settings/nm-settings-connection.c1042
-rw-r--r--src/settings/nm-settings-connection.h122
-rw-r--r--src/settings/nm-settings.c143
-rw-r--r--src/settings/nm-settings.h10
-rw-r--r--src/settings/plugins/ibft/nms-ibft-connection.c11
-rw-r--r--src/settings/plugins/ibft/nms-ibft-plugin.c2
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-inotify-helper.c (renamed from src/settings/nm-inotify-helper.c)0
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-inotify-helper.h (renamed from src/settings/nm-inotify-helper.h)13
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c78
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c13
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c652
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c8
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h3
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c201
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c12
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h3
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected20
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dns-options3
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-12
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-22
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-12
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-22
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-only2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes6
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c148
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-connection.c17
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-plugin.c13
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.c12
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-connection.c15
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c11
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_TC_Config11
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c125
-rw-r--r--src/tests/config/test-config.c6
-rw-r--r--src/tests/test-general-with-expect.c8
-rw-r--r--src/tests/test-general.c2
-rw-r--r--src/tests/test-ip4-config.c2
-rw-r--r--src/vpn/nm-vpn-connection.c56
94 files changed, 4354 insertions, 2582 deletions
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 596c5bb3..89bd357f 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -773,6 +773,10 @@ check_possible_match (NMConnection *orig,
  * @connections: a (optionally pre-sorted) list of connections from which to
  * find a matching connection to @original based on "inferrable" properties
  * @original: the #NMConnection to find a match for from @connections
+ * @indicated: whether the match is already hinted/indicated. That is the
+ *   case when we found the connection in the state file from a previous run.
+ *   In this case, we perform a relexed check, as we have a good hint
+ *   that the connection actually matches.
  * @device_has_carrier: pass %TRUE if the device that generated @original has
  * a carrier, %FALSE if not
  * @match_filter_func: a function to check whether each connection from @connections
@@ -792,6 +796,7 @@ check_possible_match (NMConnection *orig,
 NMConnection *
 nm_utils_match_connection (NMConnection *const*connections,
                            NMConnection *original,
+                           gboolean indicated,
                            gboolean device_has_carrier,
                            gint64 default_v4_metric,
                            gint64 default_v6_metric,
@@ -812,7 +817,24 @@ nm_utils_match_connection (NMConnection *const*connections,
 				continue;
 		}
 
-		if (!nm_connection_diff (original, candidate, NM_SETTING_COMPARE_FLAG_INFERRABLE, &diffs)) {
+		if (indicated) {
+			NMSettingConnection *s_orig, *s_cand;
+
+			s_orig = nm_connection_get_setting_connection (original);
+			s_cand = nm_connection_get_setting_connection (candidate);
+
+			/* It is indicated that this connection matches. Assume we have
+			 * a match, but check for particular differences that let us
+			 * reject the candidate. */
+			if (!nm_streq0 (nm_setting_connection_get_connection_type (s_orig),
+			                nm_setting_connection_get_connection_type (s_cand)))
+				continue;
+			if (!nm_streq0 (nm_setting_connection_get_slave_type (s_orig),
+			                nm_setting_connection_get_slave_type (s_cand)))
+				continue;
+
+			/* this is good enough for a match */
+		} else if (!nm_connection_diff (original, candidate, NM_SETTING_COMPARE_FLAG_INFERRABLE, &diffs)) {
 			if (!best_match) {
 				best_match = check_possible_match (original, candidate, diffs, device_has_carrier,
 				                                   default_v4_metric, default_v6_metric);
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index c20f2439..e5f28b27 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -41,6 +41,7 @@ typedef gboolean (NMUtilsMatchFilterFunc) (NMConnection *connection, gpointer us
 
 NMConnection *nm_utils_match_connection (NMConnection *const*connections,
                                          NMConnection *original,
+                                         gboolean indicated,
                                          gboolean device_has_carrier,
                                          gint64 default_v4_metric,
                                          gint64 default_v6_metric,
diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c
index 182527d9..bd3cf18a 100644
--- a/src/devices/bluetooth/nm-bluez-device.c
+++ b/src/devices/bluetooth/nm-bluez-device.c
@@ -1185,7 +1185,8 @@ dispose (GObject *object)
 	if (priv->pan_connection) {
 		/* Check whether we want to remove the created connection. If so, we take a reference
 		 * and delete it at the end of dispose(). */
-		if (nm_settings_connection_get_nm_generated (NM_SETTINGS_CONNECTION (priv->pan_connection)))
+		if (NM_FLAGS_HAS (nm_settings_connection_get_flags (NM_SETTINGS_CONNECTION (priv->pan_connection)),
+		                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED))
 			to_delete = g_object_ref (priv->pan_connection);
 
 		priv->pan_connection = NULL;
diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c
index 8c93f2a5..de3f4072 100644
--- a/src/devices/bluetooth/nm-bluez5-manager.c
+++ b/src/devices/bluetooth/nm-bluez5-manager.c
@@ -158,7 +158,7 @@ static void
 _network_server_free (NMBluez5Manager *self, NetworkServer *network_server)
 {
 	_network_server_unregister (self, network_server);
-	c_list_unlink (&network_server->lst_ns);
+	c_list_unlink_stale (&network_server->lst_ns);
 	g_free (network_server->path);
 	g_free (network_server->addr);
 	g_slice_free (NetworkServer, network_server);
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index 7807100f..3ca86e06 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -104,7 +104,7 @@ typedef struct _NMDeviceEthernetPrivate {
 	char *              s390_nettype;
 	GHashTable *        s390_options;
 
-	NMActRequestGetSecretsCallId wired_secrets_id;
+	NMActRequestGetSecretsCallId *wired_secrets_id;
 
 	/* PPPoE */
 	NMPPPManager *ppp_manager;
@@ -416,7 +416,7 @@ supplicant_interface_release (NMDeviceEthernet *self)
 
 static void
 wired_secrets_cb (NMActRequest *req,
-                  NMActRequestGetSecretsCallId call_id,
+                  NMActRequestGetSecretsCallId *call_id,
                   NMSettingsConnection *connection,
                   GError *error,
                   gpointer user_data)
diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c
index 95587278..5ef8f938 100644
--- a/src/devices/nm-device-macsec.c
+++ b/src/devices/nm-device-macsec.c
@@ -71,7 +71,7 @@ typedef struct {
 	gulong parent_state_id;
 	Supplicant supplicant;
 	guint supplicant_timeout_id;
-	NMActRequestGetSecretsCallId macsec_secrets_id;
+	NMActRequestGetSecretsCallId *macsec_secrets_id;
 } NMDeviceMacsecPrivate;
 
 struct _NMDeviceMacsec {
@@ -276,7 +276,7 @@ supplicant_iface_assoc_cb (NMSupplicantInterface *iface,
 
 static void
 macsec_secrets_cb (NMActRequest *req,
-                   NMActRequestGetSecretsCallId call_id,
+                   NMActRequestGetSecretsCallId *call_id,
                    NMSettingsConnection *connection,
                    GError *error,
                    gpointer user_data)
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index e30dae74..81512335 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -66,7 +66,7 @@ struct _NMDeviceVlanClass {
 
 G_DEFINE_TYPE (NMDeviceVlan, nm_device_vlan, NM_TYPE_DEVICE)
 
-#define NM_DEVICE_VLAN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceVlan, NM_IS_DEVICE_VLAN)
+#define NM_DEVICE_VLAN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceVlan, NM_IS_DEVICE_VLAN, NMDevice)
 
 /*****************************************************************************/
 
@@ -418,7 +418,7 @@ complete_connection (NMDevice *device,
 static void
 update_connection (NMDevice *device, NMConnection *connection)
 {
-	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE ((NMDeviceVlan *) device);
+	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device);
 	NMSettingVlan *s_vlan = nm_connection_get_setting_vlan (connection);
 	int ifindex = nm_device_get_ifindex (device);
 	const char *setting_parent, *new_parent;
@@ -558,7 +558,7 @@ static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
-	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE ((NMDeviceVlan *) object);
+	NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_VLAN_ID:
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 6a1a10f4..afc81dcd 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -35,6 +35,7 @@
 #include <fcntl.h>
 #include <linux/if_addr.h>
 #include <linux/rtnetlink.h>
+#include <linux/pkt_sched.h>
 
 #include "nm-utils/nm-dedup-multi.h"
 
@@ -328,7 +329,7 @@ typedef struct _NMDevicePrivate {
 	guint32         v4_route_table;
 	guint32         v6_route_table;
 
-	/* when carrier goes away, we give a grace period of CARRIER_WAIT_TIME_MS
+	/* when carrier goes away, we give a grace period of _get_carrier_wait_ms()
 	 * until taking action.
 	 *
 	 * When changing MTU, the device might take longer then that. So, whenever
@@ -351,8 +352,8 @@ typedef struct _NMDevicePrivate {
 
 	NMDeviceSysIfaceState sys_iface_state:2;
 
-	bool            v4_route_table_initalized:1;
-	bool            v6_route_table_initalized:1;
+	bool            v4_route_table_initialized:1;
+	bool            v6_route_table_initialized:1;
 
 	NMDeviceAutoconnectBlockedFlags autoconnect_blocked_flags:4;
 
@@ -530,6 +531,7 @@ static gboolean addrconf6_start_with_link_ready (NMDevice *self);
 static NMActStageReturn linklocal6_start (NMDevice *self);
 
 static void _carrier_wait_check_queued_act_request (NMDevice *self);
+static gint64 _get_carrier_wait_ms (NMDevice *self);
 
 static const char *_activation_func_to_string (ActivationHandleFunc func);
 static void activation_source_handle_cb (NMDevice *self, int addr_family);
@@ -582,7 +584,7 @@ nm_device_state_to_str (NMDeviceState state)
 	return queued_state_to_string (state) + NM_STRLEN (NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE);
 }
 
-NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason,
+NM_UTILS_LOOKUP_STR_DEFINE (nm_device_state_reason_to_str, NMDeviceStateReason,
 	NM_UTILS_LOOKUP_DEFAULT (NULL),
 	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_UNKNOWN,                        "unknown"),
 	NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_NONE,                           "none"),
@@ -651,7 +653,7 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_reason_to_string, NMDeviceStateReason,
 );
 
 #define reason_to_string(reason) \
-	NM_UTILS_LOOKUP_STR (_reason_to_string, reason)
+	NM_UTILS_LOOKUP_STR (nm_device_state_reason_to_str, reason)
 
 /*****************************************************************************/
 
@@ -1163,9 +1165,9 @@ nm_device_get_ip_iface (NMDevice *self)
 }
 
 int
-nm_device_get_ip_ifindex (NMDevice *self)
+nm_device_get_ip_ifindex (const NMDevice *self)
 {
-	NMDevicePrivate *priv;
+	const NMDevicePrivate *priv;
 
 	g_return_val_if_fail (self != NULL, 0);
 
@@ -1485,7 +1487,7 @@ _stats_set_refresh_rate (NMDevice *self, guint refresh_rate_ms)
 	if (!refresh_rate_ms)
 		return;
 
-	/* trigger an inital refresh of the data whenever the refresh-rate changes.
+	/* trigger an initial refresh of the data whenever the refresh-rate changes.
 	 * As we process the result in an idle handler with device_link_changed(),
 	 * we don't get the result right away. */
 	ifindex = nm_device_get_ip_ifindex (self);
@@ -1787,10 +1789,10 @@ nm_device_get_route_table (NMDevice *self,
 	/* the route table setting affects how we sync routes. We shall
 	 * not change it while the device is active, hence, cache it. */
 	if (addr_family == AF_INET) {
-		if (priv->v4_route_table_initalized)
+		if (priv->v4_route_table_initialized)
 			return priv->v4_route_table ?: (fallback_main ? RT_TABLE_MAIN : 0);
 	} else {
-		if (priv->v6_route_table_initalized)
+		if (priv->v6_route_table_initialized)
 			return priv->v6_route_table ?: (fallback_main ? RT_TABLE_MAIN : 0);
 	}
 
@@ -1820,10 +1822,10 @@ nm_device_get_route_table (NMDevice *self,
 	}
 
 	if (addr_family == AF_INET) {
-		priv->v4_route_table_initalized = TRUE;
+		priv->v4_route_table_initialized = TRUE;
 		priv->v4_route_table = route_table;
 	} else {
-		priv->v6_route_table_initalized = TRUE;
+		priv->v6_route_table_initialized = TRUE;
 		priv->v6_route_table = route_table;
 	}
 
@@ -2260,7 +2262,7 @@ nm_device_master_release_one_slave (NMDevice *self, NMDevice *slave, gboolean co
 	 * Transfers ownership from slave_priv->master.  */
 	self_free = self;
 
-	c_list_unlink_init (&info->lst_slave);
+	c_list_unlink (&info->lst_slave);
 	slave_priv->master = NULL;
 
 	g_signal_handler_disconnect (slave, info->watch_id);
@@ -2464,8 +2466,6 @@ carrier_changed (NMDevice *self, gboolean carrier)
 	}
 }
 
-#define LINK_DISCONNECT_DELAY 4
-
 static gboolean
 carrier_disconnected_action_cb (gpointer user_data)
 {
@@ -2522,10 +2522,13 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier)
 			_LOGD (LOGD_DEVICE, "carrier: link disconnected");
 			carrier_changed (self, FALSE);
 		} else {
-			priv->carrier_defer_id = g_timeout_add_seconds (LINK_DISCONNECT_DELAY,
-			                                                carrier_disconnected_action_cb, self);
-			_LOGD (LOGD_DEVICE, "carrier: link disconnected (deferring action for %d seconds) (id=%u)",
-			       LINK_DISCONNECT_DELAY, priv->carrier_defer_id);
+			gint64 now_ms, until_ms;
+
+			now_ms = nm_utils_get_monotonic_timestamp_ms ();
+			until_ms = NM_MAX (now_ms + _get_carrier_wait_ms (self), priv->carrier_wait_until_ms);
+			priv->carrier_defer_id = g_timeout_add (until_ms - now_ms, carrier_disconnected_action_cb, self);
+			_LOGD (LOGD_DEVICE, "carrier: link disconnected (deferring action for %ld milli seconds) (id=%u)",
+			       (long) (until_ms - now_ms), priv->carrier_defer_id);
 		}
 	}
 }
@@ -2828,6 +2831,9 @@ device_ip_link_changed (NMDevice *self)
 	if (!pllink)
 		return G_SOURCE_REMOVE;
 
+	if (priv->ifindex <= 0 && pllink->mtu)
+		_set_mtu (self, pllink->mtu);
+
 	_stats_update_counters_from_pllink (self, pllink);
 
 	if (_ip_iface_update (self, pllink->name))
@@ -2910,9 +2916,9 @@ _v4_has_shadowed_routes_detect (NMDevice *self)
 	platform = nm_device_get_platform (self);
 
 	head_entry = nm_platform_lookup (platform,
-	                                 nmp_lookup_init_addrroute (&lookup,
-	                                                            NMP_OBJECT_TYPE_IP4_ROUTE,
-	                                                            ifindex));
+	                                 nmp_lookup_init_object (&lookup,
+	                                                         NMP_OBJECT_TYPE_IP4_ROUTE,
+	                                                         ifindex));
 	if (!head_entry)
 		return FALSE;
 
@@ -4080,6 +4086,7 @@ nm_device_slave_notify_release (NMDevice *self, NMDeviceStateReason reason)
 			break;
 		case NM_DEVICE_STATE_REASON_USER_REQUESTED:
 			new_state = NM_DEVICE_STATE_DEACTIVATING;
+			reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED;
 			master_status = "deactivated by user request";
 			break;
 		default:
@@ -5213,6 +5220,88 @@ lldp_init (NMDevice *self, gboolean restart)
 	}
 }
 
+static gboolean
+tc_commit (NMDevice *self)
+{
+	NMConnection *connection = NULL;
+	gs_unref_ptrarray GPtrArray *qdiscs = NULL;
+	gs_unref_ptrarray GPtrArray *tfilters = NULL;
+	NMSettingTCConfig *s_tc = NULL;
+	int ip_ifindex;
+	guint nqdiscs, ntfilters;
+	int i;
+
+	connection = nm_device_get_applied_connection (self);
+	if (connection)
+		s_tc = nm_connection_get_setting_tc_config (connection);
+
+	ip_ifindex = nm_device_get_ip_ifindex (self);
+	if (!ip_ifindex)
+	       return s_tc == NULL;
+
+	if (s_tc) {
+		nqdiscs = nm_setting_tc_config_get_num_qdiscs (s_tc);
+		qdiscs = g_ptr_array_new_full (nqdiscs, (GDestroyNotify) nmp_object_unref);
+
+		for (i = 0; i < nqdiscs; i++) {
+			NMTCQdisc *s_qdisc = nm_setting_tc_config_get_qdisc (s_tc, i);
+			NMPObject *q = nmp_object_new (NMP_OBJECT_TYPE_QDISC, NULL);
+			NMPlatformQdisc *qdisc = NMP_OBJECT_CAST_QDISC (q);
+
+			qdisc->ifindex = ip_ifindex;
+			qdisc->kind = nm_tc_qdisc_get_kind (s_qdisc);
+			qdisc->addr_family = AF_UNSPEC;
+			qdisc->handle = nm_tc_qdisc_get_handle (s_qdisc);
+			qdisc->parent = nm_tc_qdisc_get_parent (s_qdisc);
+			qdisc->info = 0;
+
+			g_ptr_array_add (qdiscs, q);
+		}
+
+		ntfilters = nm_setting_tc_config_get_num_tfilters (s_tc);
+		tfilters = g_ptr_array_new_full (ntfilters, (GDestroyNotify) nmp_object_unref);
+
+		for (i = 0; i < ntfilters; i++) {
+			NMTCTfilter *s_tfilter = nm_setting_tc_config_get_tfilter (s_tc, i);
+			NMTCAction *action;
+			NMPObject *q = nmp_object_new (NMP_OBJECT_TYPE_TFILTER, NULL);
+			NMPlatformTfilter *tfilter = NMP_OBJECT_CAST_TFILTER (q);
+
+			tfilter->ifindex = ip_ifindex;
+			tfilter->kind = nm_tc_tfilter_get_kind (s_tfilter);
+			tfilter->addr_family = AF_UNSPEC;
+			tfilter->handle = nm_tc_tfilter_get_handle (s_tfilter);
+			tfilter->parent = nm_tc_tfilter_get_parent (s_tfilter);
+			tfilter->info = TC_H_MAKE (0, htons (ETH_P_ALL));
+
+			action = nm_tc_tfilter_get_action (s_tfilter);
+			if (action) {
+				tfilter->action.kind = nm_tc_action_get_kind (action);
+				if (strcmp (tfilter->action.kind, "simple") == 0) {
+					GVariant *sdata;
+
+					sdata = nm_tc_action_get_attribute (action, "sdata");
+					if (sdata && g_variant_is_of_type (sdata, G_VARIANT_TYPE_BYTESTRING)) {
+						g_strlcpy (tfilter->action.simple.sdata,
+						           g_variant_get_bytestring (sdata),
+						           sizeof (tfilter->action.simple.sdata));
+					}
+				}
+			}
+
+			g_ptr_array_add (tfilters, q);
+		}
+	}
+
+	if (!nm_platform_qdisc_sync (nm_device_get_platform (self), ip_ifindex, qdiscs))
+		return FALSE;
+
+	if (!nm_platform_tfilter_sync (nm_device_get_platform (self), ip_ifindex, tfilters))
+		return FALSE;
+
+	return TRUE;
+}
+
 /*
  * activate_stage2_device_config
  *
@@ -5234,6 +5323,11 @@ activate_stage2_device_config (NMDevice *self)
 	if (!nm_device_sys_iface_state_is_external_or_assume (self)) {
 		NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE;
 
+		if (!tc_commit (self)) {
+			_LOGW (LOGD_IP6, "failed applying traffic control rules");
+			nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
+		}
+
 		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);
@@ -5811,6 +5905,9 @@ ip4_config_merge_and_apply (NMDevice *self,
 	GSList *iter;
 	gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL;
 
+	if (nm_device_sys_iface_state_is_external (self))
+		commit = 0;
+
 	/* Apply ignore-auto-routes and ignore-auto-dns settings */
 	connection = nm_device_get_applied_connection (self);
 	if (connection) {
@@ -6474,6 +6571,9 @@ ip6_config_merge_and_apply (NMDevice *self,
 	const char *token = NULL;
 	GSList *iter;
 
+	if (nm_device_sys_iface_state_is_external (self))
+		commit = 0;
+
 	/* Apply ignore-auto-routes and ignore-auto-dns settings */
 	connection = nm_device_get_applied_connection (self);
 	if (connection) {
@@ -7001,7 +7101,7 @@ nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMIP6Config *from_config = NULL;
-	int i;
+	guint i, len;
 
 	if (priv->ac_ip6_config) {
 		nm_ip6_config_reset_nameservers (priv->ac_ip6_config);
@@ -7014,14 +7114,16 @@ nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device)
 	if (!from_config)
 		return;
 
-	for (i = 0; i < nm_ip6_config_get_num_nameservers (from_config); i++) {
+	len = nm_ip6_config_get_num_nameservers (from_config);
+	for (i = 0; i < len; i++) {
 		nm_ip6_config_add_nameserver (priv->ac_ip6_config,
 		                              nm_ip6_config_get_nameserver (from_config, i));
 	}
 
-	for (i = 0; i < nm_ip6_config_get_num_searches (from_config); i++) {
+	len = nm_ip6_config_get_num_searches (from_config);
+	for (i = 0; i < len; i++) {
 		nm_ip6_config_add_search (priv->ac_ip6_config,
-		                              nm_ip6_config_get_search (from_config, i));
+		                          nm_ip6_config_get_search (from_config, i));
 	}
 
 	if (!ip6_config_merge_and_apply (self, TRUE))
@@ -7105,11 +7207,11 @@ check_and_add_ipv6ll_addr (NMDevice *self)
 	if (priv->nm_ipv6ll == FALSE)
 		return;
 
-	if (priv->ip6_config) {
+	if (priv->ext_ip6_config_captured) {
 		NMDedupMultiIter ipconf_iter;
 		const NMPlatformIP6Address *addr;
 
-		nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, priv->ip6_config, &addr) {
+		nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, priv->ext_ip6_config_captured, &addr) {
 			if (   IN6_IS_ADDR_LINKLOCAL (&addr->address)
 			    && !(addr->n_ifa_flags & IFA_F_DADFAILED)) {
 				/* Already have an LL address, nothing to do */
@@ -8376,7 +8478,7 @@ nm_device_activate_schedule_ip6_config_timeout (NMDevice *self)
 }
 
 static gboolean
-share_init (NMDevice *self)
+share_init (NMDevice *self, GError **error)
 {
 	char *modules[] = { "ip_tables", "iptable_nat", "nf_nat_ftp", "nf_nat_irc",
 	                    "nf_nat_sip", "nf_nat_tftp", "nf_nat_pptp", "nf_nat_h323",
@@ -8384,17 +8486,23 @@ share_init (NMDevice *self)
 	char **iter;
 	int errsv;
 
-	if (!nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_forward"), "1")) {
+	if (nm_platform_sysctl_get_int32 (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_forward"), -1) == 1) {
+		/* nothing to do. */
+	} else 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));
+		_LOGD (LOGD_SHARING, "share: error enabling IPv4 forwarding: (%d) %s",
+		       errsv, g_strerror (errsv));
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "cannot set ipv4/ip_forward: %s", g_strerror (errsv));
 		return FALSE;
 	}
 
-	if (!nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_dynaddr"), "1")) {
+	if (nm_platform_sysctl_get_int32 (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_dynaddr"), -1) == 1) {
+		/* nothing to do. */
+	} else 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));
+		_LOGD (LOGD_SHARING, "share: error enabling dynamic addresses: (%d) %s",
+		       errsv, strerror (errsv));
 	}
 
 	for (iter = modules; *iter; iter++)
@@ -8411,41 +8519,45 @@ share_init (NMDevice *self)
 	} G_STMT_END
 
 static gboolean
-start_sharing (NMDevice *self, NMIP4Config *config)
+start_sharing (NMDevice *self, NMIP4Config *config, GError **error)
 {
 	NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
 	NMActRequest *req;
-	GError *error = NULL;
-	char str_addr[INET_ADDRSTRLEN + 1];
-	char str_mask[INET_ADDRSTRLEN + 1];
+	char str_addr[INET_ADDRSTRLEN];
+	char str_mask[INET_ADDRSTRLEN];
 	guint32 netmask, network;
 	const NMPlatformIP4Address *ip4_addr = NULL;
 	const char *ip_iface;
+	GError *local = NULL;
 
-	g_return_val_if_fail (config != NULL, FALSE);
+	g_return_val_if_fail (config, FALSE);
 
 	ip_iface = nm_device_get_ip_iface (self);
-	if (!ip_iface)
+	if (!ip_iface) {
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "device has no ip interface");
 		return FALSE;
+	}
 
 	ip4_addr = nm_ip4_config_get_first_address (config);
-	if (!ip4_addr || !ip4_addr->address)
-		return FALSE;
-
-	netmask = _nm_utils_ip4_prefix_to_netmask (ip4_addr->plen);
-	if (!inet_ntop (AF_INET, &netmask, str_mask, sizeof (str_mask)))
-		return FALSE;
-
-	network = ip4_addr->address & netmask;
-	if (!inet_ntop (AF_INET, &network, str_addr, sizeof (str_addr)))
+	if (!ip4_addr || !ip4_addr->address) {
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "could not determine IPv4 address");
 		return FALSE;
+	}
 
-	if (!share_init (self))
+	if (!share_init (self, error))
 		return FALSE;
 
 	req = nm_device_get_act_request (self);
 	g_assert (req);
 
+	netmask = _nm_utils_ip4_prefix_to_netmask (ip4_addr->plen);
+	nm_utils_inet4_ntop (netmask, str_mask);
+
+	network = ip4_addr->address & netmask;
+	nm_utils_inet4_ntop (network, str_addr);
+
 	add_share_rule (req, "nat", "POSTROUTING --source %s/%s ! --destination %s/%s --jump MASQUERADE", str_addr, str_mask, str_addr, str_mask);
 	add_share_rule (req, "filter", "FORWARD --destination %s/%s --out-interface %s --match state --state ESTABLISHED,RELATED --jump ACCEPT", str_addr, str_mask, ip_iface);
 	add_share_rule (req, "filter", "FORWARD --source %s/%s --in-interface %s --jump ACCEPT", str_addr, str_mask, ip_iface);
@@ -8459,10 +8571,10 @@ start_sharing (NMDevice *self, NMIP4Config *config)
 
 	nm_act_request_set_shared (req, TRUE);
 
-	if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, config, &error)) {
-		_LOGE (LOGD_SHARING, "share: (%s) failed to start dnsmasq: %s",
-		       ip_iface, error->message);
-		g_error_free (error);
+	if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, config, &local)) {
+		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
+		             "could not start dnsmasq due to %s", local->message);
+		g_error_free (local);
 		nm_act_request_set_shared (req, FALSE);
 		return FALSE;
 	}
@@ -8564,8 +8676,10 @@ activate_stage5_ip4_config_result (NMDevice *self)
 	method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
 
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) {
-		if (!start_sharing (self, priv->ip4_config)) {
-			_LOGW (LOGD_SHARING, "Activation: Stage 5 of 5 (IPv4 Commit) start sharing failed.");
+		gs_free_error GError *error = NULL;
+
+		if (!start_sharing (self, priv->ip4_config, &error)) {
+			_LOGW (LOGD_SHARING, "Activation: Stage 5 of 5 (IPv4 Commit) start sharing failed: %s", error->message);
 			nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_SHARED_START_FAILED);
 			return;
 		}
@@ -9441,6 +9555,9 @@ reapply_cb (NMDevice *self,
 		return;
 	}
 
+	if (nm_device_sys_iface_state_is_external (self))
+		nm_device_sys_iface_state_set (self, NM_DEVICE_SYS_IFACE_STATE_MANAGED);
+
 	if (!check_and_reapply_connection (self,
 	                                   connection ? : (NMConnection *) nm_device_get_settings_connection (self),
 	                                   version_id,
@@ -10144,7 +10261,8 @@ nm_device_set_ip4_config (NMDevice *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_FLAGS_HAS (nm_settings_connection_get_flags (settings_connection),
+		                     NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED)
 		    && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request)) == NM_ACTIVATION_TYPE_EXTERNAL) {
 			NMSetting *s_ip4;
 
@@ -10315,7 +10433,8 @@ nm_device_set_ip6_config (NMDevice *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_FLAGS_HAS (nm_settings_connection_get_flags (settings_connection),
+		                     NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED)
 		    && nm_active_connection_get_activation_type (NM_ACTIVE_CONNECTION (priv->act_request)) == NM_ACTIVATION_TYPE_EXTERNAL) {
 			NMSetting *s_ip6;
 
@@ -10670,6 +10789,18 @@ nm_device_is_up (NMDevice *self)
 	return ifindex > 0 ? nm_platform_link_is_up (nm_device_get_platform (self), ifindex) : TRUE;
 }
 
+static gint64
+_get_carrier_wait_ms (NMDevice *self)
+{
+	gs_free char *value = NULL;
+
+	value = nm_config_data_get_device_config (NM_CONFIG_GET_DATA,
+	                                          NM_CONFIG_KEYFILE_KEY_DEVICE_CARRIER_WAIT_TIMEOUT,
+	                                          self,
+	                                          NULL);
+	return _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXINT32, CARRIER_WAIT_TIME_MS);
+}
+
 gboolean
 nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 {
@@ -10744,7 +10875,7 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware)
 			nm_device_add_pending_action (self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE);
 
 		now_ms = nm_utils_get_monotonic_timestamp_ms ();
-		until_ms = NM_MAX (now_ms + CARRIER_WAIT_TIME_MS, priv->carrier_wait_until_ms);
+		until_ms = NM_MAX (now_ms + _get_carrier_wait_ms (self), priv->carrier_wait_until_ms);
 		priv->carrier_wait_id = g_timeout_add (until_ms - now_ms, carrier_wait_timeout, self);
 	}
 
@@ -12378,8 +12509,8 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 	priv->v4_commit_first_time = TRUE;
 	priv->v6_commit_first_time = TRUE;
 
-	priv->v4_route_table_initalized = FALSE;
-	priv->v6_route_table_initalized = FALSE;
+	priv->v4_route_table_initialized = FALSE;
+	priv->v6_route_table_initialized = FALSE;
 
 	priv->default_route_metric_penalty_ip4_has = FALSE;
 	priv->default_route_metric_penalty_ip6_has = FALSE;
@@ -12418,7 +12549,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type)
 	nm_assert (priv->needs_ip6_subnet == FALSE);
 
 	if (priv->act_request) {
-		nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request), FALSE);
+		nm_active_connection_set_default (NM_ACTIVE_CONNECTION (priv->act_request), AF_INET, FALSE);
 
 		priv->master_ready_handled = FALSE;
 		nm_clear_g_signal_handler (priv->act_request, &priv->master_ready_id);
@@ -12493,6 +12624,8 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean
 
 			nm_platform_ip_route_flush (platform, AF_UNSPEC, ifindex);
 			nm_platform_ip_address_flush (platform, AF_UNSPEC, ifindex);
+			nm_platform_tfilter_sync (platform, ifindex, NULL);
+			nm_platform_qdisc_sync (platform, ifindex, NULL);
 		}
 	}
 
@@ -13344,7 +13477,7 @@ nm_device_update_hw_address (NMDevice *self)
 	        && !nm_device_is_activating (self))) {
 		/* when we get a hw_addr the first time or while the device
 		 * is not activated (with no explict hw address set), always
-		 * update our inital hw-address as well. */
+		 * update our initial hw-address as well. */
 		nm_device_update_initial_hw_address (self);
 	}
 	return TRUE;
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index bd8104b4..810a613d 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -438,7 +438,7 @@ int             nm_device_get_ifindex           (NMDevice *dev);
 gboolean        nm_device_is_software           (NMDevice *dev);
 gboolean        nm_device_is_real               (NMDevice *dev);
 const char *    nm_device_get_ip_iface          (NMDevice *dev);
-int             nm_device_get_ip_ifindex        (NMDevice *dev);
+int             nm_device_get_ip_ifindex        (const NMDevice *dev);
 const char *    nm_device_get_driver            (NMDevice *dev);
 const char *    nm_device_get_driver_version    (NMDevice *dev);
 const char *    nm_device_get_type_desc         (NMDevice *dev);
@@ -794,5 +794,6 @@ struct _NMBtVTableNetworkServer {
 };
 
 const char *nm_device_state_to_str (NMDeviceState state);
+const char *nm_device_state_reason_to_str (NMDeviceStateReason reason);
 
 #endif /* __NETWORKMANAGER_DEVICE_H__ */
diff --git a/src/devices/tests/test-lldp.c b/src/devices/tests/test-lldp.c
index 1e600cdf..c3c4f0d2 100644
--- a/src/devices/tests/test-lldp.c
+++ b/src/devices/tests/test-lldp.c
@@ -366,7 +366,7 @@ _test_recv_fixture_setup (TestRecvFixture *fixture, gconstpointer user_data)
 	g_assert (s >= 0);
 	ifr.ifr_flags |= IFF_UP;
 	g_assert (ioctl (s, SIOCSIFFLAGS, &ifr) >= 0);
-	close (s);
+	nm_close (s);
 
 	link = nmtstp_assert_wait_for_link (NM_PLATFORM_GET, TEST_IFNAME, NM_LINK_TYPE_TAP, 100);
 	fixture->ifindex = link->ifindex;
diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c
index 5e92f47b..bf021095 100644
--- a/src/devices/wifi/nm-device-wifi.c
+++ b/src/devices/wifi/nm-device-wifi.c
@@ -107,7 +107,7 @@ typedef struct {
 
 	NM80211Mode       mode;
 
-	NMActRequestGetSecretsCallId wifi_secrets_id;
+	NMActRequestGetSecretsCallId *wifi_secrets_id;
 
 	guint             periodic_source_id;
 	guint             link_timeout_id;
@@ -1428,8 +1428,8 @@ build_hidden_probe_list (NMDeviceWifi *self)
 
 	connections = nm_settings_get_connections_clone (nm_device_get_settings ((NMDevice *) self),
 	                                                 &len,
-	                                                 hidden_filter_func,
-	                                                 NULL);
+	                                                 hidden_filter_func, NULL,
+	                                                 NULL, NULL);
 	if (!connections[0])
 		return NULL;
 
@@ -1786,7 +1786,7 @@ cleanup_supplicant_failures (NMDeviceWifi *self)
 
 static void
 wifi_secrets_cb (NMActRequest *req,
-                 NMActRequestGetSecretsCallId call_id,
+                 NMActRequestGetSecretsCallId *call_id,
                  NMSettingsConnection *connection,
                  GError *error,
                  gpointer user_data)
diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c
index 77495b62..1c32791f 100644
--- a/src/devices/wwan/nm-modem.c
+++ b/src/devices/wwan/nm-modem.c
@@ -94,7 +94,7 @@ typedef struct _NMModemPrivate {
 
 	NMActRequest *act_request;
 	guint32 secrets_tries;
-	NMActRequestGetSecretsCallId secrets_id;
+	NMActRequestGetSecretsCallId *secrets_id;
 
 	guint32 mm_ip_timeout;
 
@@ -880,7 +880,7 @@ cancel_get_secrets (NMModem *self)
 
 static void
 modem_secrets_cb (NMActRequest *req,
-                  NMActRequestGetSecretsCallId call_id,
+                  NMActRequestGetSecretsCallId *call_id,
                   NMSettingsConnection *connection,
                   GError *error,
                   gpointer user_data)
diff --git a/src/dns/nm-dns-dnsmasq.c b/src/dns/nm-dns-dnsmasq.c
index 3ec1fd2d..e6436c79 100644
--- a/src/dns/nm-dns-dnsmasq.c
+++ b/src/dns/nm-dns-dnsmasq.c
@@ -568,12 +568,14 @@ update (NMDnsPlugin *plugin,
 		add_global_config (self, &servers, global_config);
 	else {
 		for (i = 0; i < configs->len; i++) {
-			prio = nm_dns_ip_config_data_get_dns_priority (configs->pdata[i]);
+			const NMDnsIPConfigData *data = configs->pdata[i];
+
+			prio = nm_ip_config_get_dns_priority (data->config);
 			if (i == 0)
 				first_prio = prio;
 			else if (first_prio < 0 && first_prio != prio)
 				break;
-			add_ip_config_data (self, &servers, configs->pdata[i]);
+			add_ip_config_data (self, &servers, data);
 		}
 	}
 
diff --git a/src/dns/nm-dns-manager.c b/src/dns/nm-dns-manager.c
index d5392b7d..973abc1c 100644
--- a/src/dns/nm-dns-manager.c
+++ b/src/dns/nm-dns-manager.c
@@ -197,19 +197,6 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_config_type_to_string, NMDnsIPConfigType,
 	NM_UTILS_LOOKUP_STR_ITEM (NM_DNS_IP_CONFIG_TYPE_VPN, "vpn"),
 );
 
-int
-nm_dns_ip_config_data_get_dns_priority (const NMDnsIPConfigData *config)
-{
-	g_return_val_if_fail (config, 0);
-
-	if (NM_IS_IP4_CONFIG (config->config))
-		return nm_ip4_config_get_dns_priority (config->config);
-	else if (NM_IS_IP6_CONFIG (config->config))
-		return nm_ip6_config_get_dns_priority (config->config);
-	else
-		g_return_val_if_reached (0);
-}
-
 static NMDnsIPConfigData *
 ip_config_data_new (gpointer config, NMDnsIPConfigType type, const char *iface)
 {
@@ -241,8 +228,8 @@ ip_config_data_compare (const NMDnsIPConfigData *a, const NMDnsIPConfigData *b)
 {
 	int a_prio, b_prio;
 
-	a_prio = nm_dns_ip_config_data_get_dns_priority (a);
-	b_prio = nm_dns_ip_config_data_get_dns_priority (b);
+	a_prio = nm_ip_config_get_dns_priority (a->config);
+	b_prio = nm_ip_config_get_dns_priority (b->config);
 
 	/* Configurations with lower priority value first */
 	if (a_prio < b_prio)
@@ -288,135 +275,83 @@ add_string_item (GPtrArray *array, const char *str)
 }
 
 static void
-add_dns_option_item (GPtrArray *array, const char *str, gboolean ipv6)
+add_dns_option_item (GPtrArray *array, const char *str)
 {
 	if (_nm_utils_dns_option_find_idx (array, str) < 0)
 		g_ptr_array_add (array, g_strdup (str));
 }
 
 static void
-merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src)
+merge_one_ip_config (NMResolvConfData *rc,
+                     const NMIPConfig *config,
+                     const char *iface)
 {
-	guint32 num, num_domains, num_searches, i;
-
-	num = nm_ip4_config_get_num_nameservers (src);
-	for (i = 0; i < num; i++) {
-		add_string_item (rc->nameservers,
-		                 nm_utils_inet4_ntop (nm_ip4_config_get_nameserver (src, i), NULL));
-	}
-
-	num_domains = nm_ip4_config_get_num_domains (src);
-	num_searches = nm_ip4_config_get_num_searches (src);
-
-	for (i = 0; i < num_searches; i++) {
-		const char *search;
-
-		search = nm_ip4_config_get_search (src, i);
-		if (!domain_is_valid (search, FALSE))
-			continue;
-		add_string_item (rc->searches, search);
-	}
-
-	if (num_domains > 1 || !num_searches) {
-		for (i = 0; i < num_domains; i++) {
-			const char *domain;
-
-			domain = nm_ip4_config_get_domain (src, i);
-			if (!domain_is_valid (domain, FALSE))
-				continue;
-			add_string_item (rc->searches, domain);
-		}
-	}
-
-	num = nm_ip4_config_get_num_dns_options (src);
-	for (i = 0; i < num; i++) {
-		const char *option;
-
-		option = nm_ip4_config_get_dns_option (src, i);
-		add_dns_option_item (rc->options, option, FALSE);
-	}
-
-	/* NIS stuff */
-	num = nm_ip4_config_get_num_nis_servers (src);
-	for (i = 0; i < num; i++) {
-		add_string_item (rc->nis_servers,
-		                 nm_utils_inet4_ntop (nm_ip4_config_get_nis_server (src, i), NULL));
-	}
+	int addr_family;
+	guint num, num_domains, num_searches, i;
+	char buf[NM_UTILS_INET_ADDRSTRLEN + 50];
+	const char *str;
 
-	if (nm_ip4_config_get_nis_domain (src)) {
-		/* FIXME: handle multiple domains */
-		if (!rc->nis_domain)
-			rc->nis_domain = nm_ip4_config_get_nis_domain (src);
-	}
-}
+	addr_family = nm_ip_config_get_addr_family (config);
 
-static void
-merge_one_ip6_config (NMResolvConfData *rc, NMIP6Config *src, const char *iface)
-{
-	guint32 num, num_domains, num_searches, i;
+	nm_assert_addr_family (addr_family);
 
-	num = nm_ip6_config_get_num_nameservers (src);
+	num = nm_ip_config_get_num_nameservers (config);
 	for (i = 0; i < num; i++) {
-		const struct in6_addr *addr;
-		char buf[NM_UTILS_INET_ADDRSTRLEN + 50];
-
-		addr = nm_ip6_config_get_nameserver (src, i);
+		const NMIPAddr *addr;
 
-		/* inet_ntop is probably supposed to do this for us, but it doesn't */
-		if (IN6_IS_ADDR_V4MAPPED (addr))
-			nm_utils_inet4_ntop (addr->s6_addr32[3], buf);
+		addr = nm_ip_config_get_nameserver (config, i);
+		if (addr_family == AF_INET)
+			nm_utils_inet_ntop (addr_family, addr, buf);
+		else if (IN6_IS_ADDR_V4MAPPED (addr))
+			nm_utils_inet4_ntop (addr->addr6.s6_addr32[3], buf);
 		else {
-			nm_utils_inet6_ntop (addr, buf);
+			nm_utils_inet6_ntop (&addr->addr6, buf);
 			if (IN6_IS_ADDR_LINKLOCAL (addr)) {
 				g_strlcat (buf, "%", sizeof (buf));
 				g_strlcat (buf, iface, sizeof (buf));
 			}
 		}
+
 		add_string_item (rc->nameservers, buf);
 	}
 
-	num_domains = nm_ip6_config_get_num_domains (src);
-	num_searches = nm_ip6_config_get_num_searches (src);
-
+	num_domains = nm_ip_config_get_num_domains (config);
+	num_searches = nm_ip_config_get_num_searches (config);
 	for (i = 0; i < num_searches; i++) {
-		const char *search;
-
-		search = nm_ip6_config_get_search (src, i);
-		if (!domain_is_valid (search, FALSE))
-			continue;
-		add_string_item (rc->searches, search);
+		str = nm_ip_config_get_search (config, i);
+		if (domain_is_valid (str, FALSE))
+			add_string_item (rc->searches, str);
 	}
-
 	if (num_domains > 1 || !num_searches) {
 		for (i = 0; i < num_domains; i++) {
-			const char *domain;
-
-			domain = nm_ip6_config_get_domain (src, i);
-			if (!domain_is_valid (domain, FALSE))
-				continue;
-			add_string_item (rc->searches, domain);
+			str = nm_ip_config_get_domain (config, i);
+			if (domain_is_valid (str, FALSE))
+				add_string_item (rc->searches, str);
 		}
 	}
 
-	num = nm_ip6_config_get_num_dns_options (src);
+	num = nm_ip_config_get_num_dns_options (config);
 	for (i = 0; i < num; i++) {
-		const char *option;
-
-		option = nm_ip6_config_get_dns_option (src, i);
-		add_dns_option_item (rc->options, option, TRUE);
+		add_dns_option_item (rc->options,
+		                     nm_ip_config_get_dns_option (config, i));
 	}
-}
 
-static void
-merge_one_ip_config_data (NMResolvConfData *rc,
-                          NMDnsIPConfigData *data)
-{
-	if (NM_IS_IP4_CONFIG (data->config))
-		merge_one_ip4_config (rc, (NMIP4Config *) data->config);
-	else if (NM_IS_IP6_CONFIG (data->config))
-		merge_one_ip6_config (rc, (NMIP6Config *) data->config, data->iface);
-	else
-		g_return_if_reached ();
+	if (addr_family == AF_INET) {
+		const NMIP4Config *config4 = (const NMIP4Config *) config;
+
+		/* NIS stuff */
+		num = nm_ip4_config_get_num_nis_servers (config4);
+		for (i = 0; i < num; i++) {
+			add_string_item (rc->nis_servers,
+			                 nm_utils_inet4_ntop (nm_ip4_config_get_nis_server (config4, i), buf));
+		}
+
+		if (nm_ip4_config_get_nis_domain (config4)) {
+			/* FIXME: handle multiple domains */
+			if (!rc->nis_domain)
+				rc->nis_domain = nm_ip4_config_get_nis_domain (config4);
+		}
+	}
 }
 
 static GPid
@@ -940,39 +875,27 @@ merge_global_dns_config (NMResolvConfData *rc, NMGlobalDnsConfig *global_conf)
 }
 
 static const char *
-get_nameserver_list (void *config, GString **str)
+get_nameserver_list (const NMIPConfig *config, GString **str)
 {
-	NMIP4Config *ip4;
-	NMIP6Config *ip6;
 	guint num, i;
-
-	nm_assert (str);
+	char buf[NM_UTILS_INET_ADDRSTRLEN];
+	int addr_family;
 
 	if (*str)
 		g_string_truncate (*str, 0);
 	else
 		*str = g_string_sized_new (64);
 
-	if (NM_IS_IP4_CONFIG (config)) {
-		ip4 = (NMIP4Config *) config;
-		num = nm_ip4_config_get_num_nameservers (ip4);
-		for (i = 0; i < num; i++) {
-			g_string_append (*str,
-			                 nm_utils_inet4_ntop (nm_ip4_config_get_nameserver (ip4, i),
-			                                      NULL));
-			g_string_append_c (*str, ' ');
-		}
-	} else if (NM_IS_IP6_CONFIG (config)) {
-		ip6 = (NMIP6Config *) config;
-		num = nm_ip6_config_get_num_nameservers (ip6);
-		for (i = 0; i < num; i++) {
-			g_string_append (*str,
-			                 nm_utils_inet6_ntop (nm_ip6_config_get_nameserver (ip6, i),
-			                                      NULL));
+	addr_family = nm_ip_config_get_addr_family (config);
+	num = nm_ip_config_get_num_nameservers (config);
+	for (i = 0; i < num; i++) {
+		nm_utils_inet_ntop (addr_family,
+		                    nm_ip_config_get_nameserver (config, i),
+		                    buf);
+		if (i > 0)
 			g_string_append_c (*str, ' ');
-		}
-	} else
-		g_return_val_if_reached (NULL);
+		g_string_append (*str, buf);
+	}
 
 	return (*str)->str;
 }
@@ -1011,34 +934,31 @@ _collect_resolv_conf_data (NMDnsManager *self, /* only for logging context, no o
 		nm_auto_free_gstring GString *tmp_gstring = NULL;
 		int prio, first_prio = 0;
 		NMDnsIPConfigData *current;
-		gboolean v4;
 
 		for (i = 0, j = 0; i < configs->len; i++) {
 			gboolean skip = FALSE;
 
 			current = configs->pdata[i];
 
-			prio = nm_dns_ip_config_data_get_dns_priority (current);
+			prio = nm_ip_config_get_dns_priority (current->config);
 
 			if (i == 0)
 				first_prio = prio;
 			else if (first_prio < 0 && first_prio != prio)
 				skip = TRUE;
 
-			v4 = NM_IS_IP4_CONFIG (current->config);
-			if (   ( v4 && nm_ip4_config_get_num_nameservers ((NMIP4Config *) current->config))
-			    || (!v4 && nm_ip6_config_get_num_nameservers ((NMIP6Config *) current->config))) {
+			if (nm_ip_config_get_num_nameservers (current->config)) {
 				_LOGT ("config: %8d %-7s v%c %-16s %s: %s",
 				       prio,
 				       _config_type_to_string (current->type),
-				       v4 ? '4' : '6',
+				       nm_utils_addr_family_to_char (nm_ip_config_get_addr_family (current->config)),
 				       current->iface,
 				       skip ? "<SKIP>" : "",
 				       get_nameserver_list (current->config, &tmp_gstring));
 			}
 
 			if (!skip)
-				merge_one_ip_config_data (&rc, current);
+				merge_one_ip_config (&rc, current->config, current->iface);
 		}
 	}
 
@@ -1305,7 +1225,7 @@ forget_data (NMDnsManager *self, NMDnsIPConfigData *data)
 	g_signal_handlers_disconnect_by_func (data->config, ip_config_dns_priority_changed, self);
 }
 
-static gboolean
+gboolean
 nm_dns_manager_add_ip_config (NMDnsManager *self,
                               const char *iface,
                               gpointer config,
@@ -1320,6 +1240,7 @@ nm_dns_manager_add_ip_config (NMDnsManager *self,
 	g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE);
 	g_return_val_if_fail (config, FALSE);
 	g_return_val_if_fail (iface && iface[0], FALSE);
+	nm_assert (NM_IS_IP_CONFIG (config));
 
 	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
@@ -1368,24 +1289,6 @@ nm_dns_manager_add_ip_config (NMDnsManager *self,
 }
 
 gboolean
-nm_dns_manager_add_ip4_config (NMDnsManager *self,
-                               const char *iface,
-                               NMIP4Config *config,
-                               NMDnsIPConfigType cfg_type)
-{
-	return nm_dns_manager_add_ip_config (self, iface, config, cfg_type);
-}
-
-gboolean
-nm_dns_manager_add_ip6_config (NMDnsManager *self,
-                               const char *iface,
-                               NMIP6Config *config,
-                               NMDnsIPConfigType cfg_type)
-{
-	return nm_dns_manager_add_ip_config (self, iface, config, cfg_type);
-}
-
-static gboolean
 nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config)
 {
 	NMDnsManagerPrivate *priv;
@@ -1395,6 +1298,7 @@ nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config)
 
 	g_return_val_if_fail (NM_IS_DNS_MANAGER (self), FALSE);
 	g_return_val_if_fail (config, FALSE);
+	nm_assert (NM_IS_IP_CONFIG (config));
 
 	priv = NM_DNS_MANAGER_GET_PRIVATE (self);
 
@@ -1416,18 +1320,6 @@ nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config)
 	return FALSE;
 }
 
-gboolean
-nm_dns_manager_remove_ip4_config (NMDnsManager *self, NMIP4Config *config)
-{
-	return nm_dns_manager_remove_ip_config (self, config);
-}
-
-gboolean
-nm_dns_manager_remove_ip6_config (NMDnsManager *self, NMIP6Config *config)
-{
-	return nm_dns_manager_remove_ip_config (self, config);
-}
-
 void
 nm_dns_manager_set_initial_hostname (NMDnsManager *self,
                                      const char *hostname)
@@ -1643,6 +1535,11 @@ static gboolean
 _resolvconf_resolved_managed (void)
 {
 	static const char *const RESOLVED_PATHS[] = {
+		"../run/systemd/resolve/stub-resolv.conf",
+		"../run/systemd/resolve/resolv.conf",
+		"../lib/systemd/resolv.conf",
+		"../usr/lib/systemd/resolv.conf",
+		"/run/systemd/resolve/stub-resolv.conf",
 		"/run/systemd/resolve/resolv.conf",
 		"/lib/systemd/resolv.conf",
 		"/usr/lib/systemd/resolv.conf",
@@ -1697,7 +1594,10 @@ _resolvconf_resolved_managed (void)
 	/* see if resolv.conf resolves to one of the candidate
 	 * paths (or whether it is hard-linked). */
 	for (i = 0; i < G_N_ELEMENTS (RESOLVED_PATHS); i++) {
-		if (   stat (RESOLVED_PATHS[i], &st_test) == 0
+		const char *p = RESOLVED_PATHS[i];
+
+		if (   p[0] == '/'
+		    && stat (p, &st_test) == 0
 		    && st.st_dev == st_test.st_dev
 		    && st.st_ino == st_test.st_ino)
 			return TRUE;
@@ -1926,92 +1826,46 @@ _get_config_variant (NMDnsManager *self)
 
 	for (i = 0; i < priv->configs->len; i++) {
 		NMDnsIPConfigData *current = priv->configs->pdata[i];
+		const NMIPConfig *config = current->config;
 		GVariantBuilder entry_builder;
 		GVariantBuilder strv_builder;
-		gboolean v4 = NM_IS_IP4_CONFIG (current->config);
-		gint priority;
+		guint num;
+		const int addr_family = nm_ip_config_get_addr_family (config);
+		char buf[NM_UTILS_INET_ADDRSTRLEN];
+		const NMIPAddr *addr;
 
-		if (v4) {
-			NMIP4Config *config = NM_IP4_CONFIG (current->config);
-			guint num = nm_ip4_config_get_num_nameservers (config);
-			guint32 ns;
-
-			if (!num)
-				continue;
-
-			g_variant_builder_init (&entry_builder, G_VARIANT_TYPE ("a{sv}"));
-
-			/* Add nameservers */
-			g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as"));
-			for (j = 0; j < num; j++) {
-				ns = nm_ip4_config_get_nameserver (config, j);
-				g_variant_builder_add (&strv_builder,
-				                       "s",
-				                       nm_utils_inet4_ntop (ns, NULL));
-			}
-			g_variant_builder_add (&entry_builder,
-			                       "{sv}",
-			                       "nameservers",
-			                       g_variant_builder_end (&strv_builder));
-
-			/* Add domains */
-			num = nm_ip4_config_get_num_domains (config);
-			if (num > 0) {
-				g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as"));
-				for (j = 0; j < num; j++) {
-					g_variant_builder_add (&strv_builder,
-					                       "s",
-					                       nm_ip4_config_get_domain (config, j));
-				}
-				g_variant_builder_add (&entry_builder,
-				                       "{sv}",
-				                       "domains",
-				                       g_variant_builder_end (&strv_builder));
-			}
-
-			priority = nm_ip4_config_get_dns_priority (config);
-		} else {
-			NMIP6Config *config = NM_IP6_CONFIG (current->config);
-			guint num = nm_ip6_config_get_num_nameservers (config);
-			const struct in6_addr *ns;
+		num = nm_ip_config_get_num_nameservers (config);
+		if (!num)
+			continue;
 
-			if (!num)
-				continue;
+		g_variant_builder_init (&entry_builder, G_VARIANT_TYPE ("a{sv}"));
 
-			g_variant_builder_init (&entry_builder, G_VARIANT_TYPE ("a{sv}"));
+		g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as"));
+		for (j = 0; j < num; j++) {
+			addr = nm_ip_config_get_nameserver (config, j);
+			g_variant_builder_add (&strv_builder,
+			                       "s",
+			                       nm_utils_inet_ntop (addr_family, addr, buf));
+		}
+		g_variant_builder_add (&entry_builder,
+		                       "{sv}",
+		                       "nameservers",
+		                       g_variant_builder_end (&strv_builder));
 
-			/* Add nameservers */
+		num = nm_ip_config_get_num_domains (config);
+		if (num > 0) {
 			g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as"));
 			for (j = 0; j < num; j++) {
-				ns = nm_ip6_config_get_nameserver (config, j);
 				g_variant_builder_add (&strv_builder,
 				                       "s",
-				                       nm_utils_inet6_ntop (ns, NULL));
+				                       nm_ip_config_get_domain (config, j));
 			}
 			g_variant_builder_add (&entry_builder,
 			                       "{sv}",
-			                       "nameservers",
+			                       "domains",
 			                       g_variant_builder_end (&strv_builder));
-
-			/* Add domains */
-			num = nm_ip6_config_get_num_domains (config);
-			if (num > 0) {
-				g_variant_builder_init (&strv_builder, G_VARIANT_TYPE ("as"));
-				for (j = 0; j < num; j++) {
-					g_variant_builder_add (&strv_builder,
-					                       "s",
-					                       nm_ip6_config_get_domain (config, j));
-				}
-				g_variant_builder_add (&entry_builder,
-				                       "{sv}",
-				                       "domains",
-				                       g_variant_builder_end (&strv_builder));
-			}
-
-			priority = nm_ip6_config_get_dns_priority (config);
 		}
 
-		/* Add device */
 		if (current->iface) {
 			g_variant_builder_add (&entry_builder,
 			                       "{sv}",
@@ -2019,13 +1873,11 @@ _get_config_variant (NMDnsManager *self)
 			                       g_variant_new_string (current->iface));
 		}
 
-		/* Add priority */
 		g_variant_builder_add (&entry_builder,
 		                       "{sv}",
 		                       "priority",
-		                       g_variant_new_int32 (priority));
+		                       g_variant_new_int32 (nm_ip_config_get_dns_priority (config)));
 
-		/* Add VPN */
 		g_variant_builder_add (&entry_builder,
 		                       "{sv}",
 		                       "vpn",
diff --git a/src/dns/nm-dns-manager.h b/src/dns/nm-dns-manager.h
index ee835927..b38ea701 100644
--- a/src/dns/nm-dns-manager.h
+++ b/src/dns/nm-dns-manager.h
@@ -44,8 +44,6 @@ typedef struct {
 	char *iface;
 } NMDnsIPConfigData;
 
-int nm_dns_ip_config_data_get_dns_priority (const NMDnsIPConfigData *config);
-
 #define NM_TYPE_DNS_MANAGER (nm_dns_manager_get_type ())
 #define NM_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_DNS_MANAGER, NMDnsManager))
 #define NM_DNS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DNS_MANAGER, NMDnsManagerClass))
@@ -72,19 +70,12 @@ NMDnsManager * nm_dns_manager_get (void);
 void nm_dns_manager_begin_updates (NMDnsManager *self, const char *func);
 void nm_dns_manager_end_updates (NMDnsManager *self, const char *func);
 
-gboolean nm_dns_manager_add_ip4_config (NMDnsManager *self,
-                                        const char *iface,
-                                        NMIP4Config *config,
-                                        NMDnsIPConfigType cfg_type);
-
-gboolean nm_dns_manager_remove_ip4_config (NMDnsManager *self, NMIP4Config *config);
-
-gboolean nm_dns_manager_add_ip6_config (NMDnsManager *self,
-                                        const char *iface,
-                                        NMIP6Config *config,
-                                        NMDnsIPConfigType cfg_type);
+gboolean nm_dns_manager_add_ip_config (NMDnsManager *self,
+                                       const char *iface,
+                                       gpointer config,
+                                       NMDnsIPConfigType cfg_type);
 
-gboolean nm_dns_manager_remove_ip6_config (NMDnsManager *self, NMIP6Config *config);
+gboolean nm_dns_manager_remove_ip_config (NMDnsManager *self, gpointer config);
 
 void nm_dns_manager_set_initial_hostname (NMDnsManager *self,
                                           const char *hostname);
diff --git a/src/dns/nm-dns-systemd-resolved.c b/src/dns/nm-dns-systemd-resolved.c
index 315f2c4d..6ab2ea18 100644
--- a/src/dns/nm-dns-systemd-resolved.c
+++ b/src/dns/nm-dns-systemd-resolved.c
@@ -289,14 +289,15 @@ update (NMDnsPlugin *plugin,
 	int prio, first_prio = 0;
 
 	for (i = 0; i < configs->len; i++) {
+		const NMDnsIPConfigData *data = configs->pdata[i];
 		gboolean skip = FALSE;
 
-		prio = nm_dns_ip_config_data_get_dns_priority (configs->pdata[i]);
+		prio = nm_ip_config_get_dns_priority (data->config);
 		if (i == 0)
 			first_prio = prio;
 		else if (first_prio < 0 && first_prio != prio)
 			skip = TRUE;
-		add_interface_configuration (self, interfaces, configs->pdata[i], skip);
+		add_interface_configuration (self, interfaces, data, skip);
 	}
 
 	free_pending_updates (self);
diff --git a/src/main-utils.c b/src/main-utils.c
index c84f1e4c..0f790536 100644
--- a/src/main-utils.c
+++ b/src/main-utils.c
@@ -106,7 +106,7 @@ nm_main_utils_write_pidfile (const char *pidfile)
 	else
 		success = TRUE;
 
-	if (close (fd))
+	if (nm_close (fd))
 		fprintf (stderr, _("Closing %s failed: %s\n"), pidfile, strerror (errno));
 
 	return success;
diff --git a/src/nm-act-request.c b/src/nm-act-request.c
index 6d8bb065..87070794 100644
--- a/src/nm-act-request.c
+++ b/src/nm-act-request.c
@@ -28,6 +28,8 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include "nm-utils/c-list.h"
+
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-8021x.h"
 #include "devices/nm-device.h"
@@ -41,7 +43,7 @@ typedef struct {
 } ShareRule;
 
 typedef struct {
-	GSList *secrets_calls;
+	CList call_ids_lst_head;
 	gboolean shared;
 	GSList *share_rules;
 } NMActRequestPrivate;
@@ -90,64 +92,52 @@ nm_act_request_get_applied_connection (NMActRequest *req)
 /*****************************************************************************/
 
 struct _NMActRequestGetSecretsCallId {
+	CList call_ids_lst;
 	NMActRequest *self;
 	NMActRequestSecretsFunc callback;
 	gpointer callback_data;
-	NMSettingsConnectionCallId call_id;
+	NMSettingsConnectionCallId *call_id;
 	bool has_ref;
 };
 
-typedef struct _NMActRequestGetSecretsCallId GetSecretsInfo;
-
-static GetSecretsInfo *
-_get_secrets_info_new (NMActRequest *self, gboolean ref_self, NMActRequestSecretsFunc callback, gpointer callback_data)
-{
-	GetSecretsInfo *info;
-
-	info = g_slice_new0 (GetSecretsInfo);
-	info->has_ref = ref_self;
-	info->self = ref_self ? g_object_ref (self) : self;
-	info->callback = callback;
-	info->callback_data = callback_data;
-
-	return info;
-}
-
 static void
-_get_secrets_info_free (GetSecretsInfo *info)
+_get_secrets_call_id_free (NMActRequestGetSecretsCallId *call_id)
 {
-	if (info->has_ref)
-		g_object_unref (info->self);
-	g_slice_free (GetSecretsInfo, info);
+	nm_assert (call_id);
+	nm_assert (!c_list_is_linked (&call_id->call_ids_lst));
+
+	if (call_id->has_ref)
+		g_object_unref (call_id->self);
+	g_slice_free (NMActRequestGetSecretsCallId, call_id);
 }
 
 static void
 get_secrets_cb (NMSettingsConnection *connection,
-                NMSettingsConnectionCallId call_id_s,
+                NMSettingsConnectionCallId *call_id_s,
                 const char *agent_username,
                 const char *setting_name,
                 GError *error,
                 gpointer user_data)
 {
-	GetSecretsInfo *info = user_data;
+	NMActRequestGetSecretsCallId *call_id = user_data;
 	NMActRequestPrivate *priv;
 
-	g_return_if_fail (info && info->call_id == call_id_s);
-	g_return_if_fail (NM_IS_ACT_REQUEST (info->self));
+	g_return_if_fail (call_id && call_id->call_id == call_id_s);
+	g_return_if_fail (NM_IS_ACT_REQUEST (call_id->self));
 
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
 
-	priv = NM_ACT_REQUEST_GET_PRIVATE (info->self);
+	priv = NM_ACT_REQUEST_GET_PRIVATE (call_id->self);
 
-	g_return_if_fail (g_slist_find (priv->secrets_calls, info));
+	nm_assert (c_list_contains (&priv->call_ids_lst_head, &call_id->call_ids_lst));
 
-	priv->secrets_calls = g_slist_remove (priv->secrets_calls, info);
+	c_list_unlink (&call_id->call_ids_lst);
 
-	if (info->callback)
-		info->callback (info->self, info, connection, error, info->callback_data);
+	if (call_id->callback)
+		call_id->callback (call_id->self, call_id, connection, error, call_id->callback_data);
 
-	_get_secrets_info_free (info);
+	_get_secrets_call_id_free (call_id);
 }
 
 /**
@@ -171,7 +161,7 @@ get_secrets_cb (NMSettingsConnection *connection,
  *
  * Returns: a call-id.
  */
-NMActRequestGetSecretsCallId
+NMActRequestGetSecretsCallId *
 nm_act_request_get_secrets (NMActRequest *self,
                             gboolean ref_self,
                             const char *setting_name,
@@ -181,8 +171,8 @@ nm_act_request_get_secrets (NMActRequest *self,
                             gpointer callback_data)
 {
 	NMActRequestPrivate *priv;
-	GetSecretsInfo *info;
-	NMSettingsConnectionCallId call_id_s;
+	NMActRequestGetSecretsCallId *call_id;
+	NMSettingsConnectionCallId *call_id_s;
 	NMSettingsConnection *settings_connection;
 	NMConnection *applied_connection;
 	const char *hints[2] = { hint, NULL };
@@ -194,9 +184,12 @@ nm_act_request_get_secrets (NMActRequest *self,
 	settings_connection = nm_act_request_get_settings_connection (self);
 	applied_connection = nm_act_request_get_applied_connection (self);
 
-	info = _get_secrets_info_new (self, ref_self, callback, callback_data);
-
-	priv->secrets_calls = g_slist_append (priv->secrets_calls, info);
+	call_id = g_slice_new0 (NMActRequestGetSecretsCallId);
+	call_id->has_ref = ref_self;
+	call_id->self = ref_self ? g_object_ref (self) : self;
+	call_id->callback = callback;
+	call_id->callback_data = callback_data;
+	c_list_link_tail (&priv->call_ids_lst_head, &call_id->call_ids_lst);
 
 	if (nm_active_connection_get_user_requested (NM_ACTIVE_CONNECTION (self)))
 		flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED;
@@ -208,32 +201,32 @@ nm_act_request_get_secrets (NMActRequest *self,
 	                                                flags,
 	                                                hints,
 	                                                get_secrets_cb,
-	                                                info);
-	info->call_id = call_id_s;
+	                                                call_id);
+	call_id->call_id = call_id_s;
 	g_return_val_if_fail (call_id_s, NULL);
-	return info;
+	return call_id;
 }
 
 static void
-_do_cancel_secrets (NMActRequest *self, GetSecretsInfo *info, gboolean is_disposing)
+_do_cancel_secrets (NMActRequest *self, NMActRequestGetSecretsCallId *call_id, gboolean is_disposing)
 {
 	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (self);
 
-	nm_assert (info && info->self == self);
-	nm_assert (g_slist_find (priv->secrets_calls, info));
+	nm_assert (call_id && call_id->self == self);
+	nm_assert (c_list_contains (&priv->call_ids_lst_head, &call_id->call_ids_lst));
 
-	priv->secrets_calls = g_slist_remove (priv->secrets_calls, info);
+	c_list_unlink (&call_id->call_ids_lst);
 
-	nm_settings_connection_cancel_secrets (nm_act_request_get_settings_connection (self), info->call_id);
+	nm_settings_connection_cancel_secrets (nm_act_request_get_settings_connection (self), call_id->call_id);
 
-	if (info->callback) {
+	if (call_id->callback) {
 		gs_free_error GError *error = NULL;
 
 		nm_utils_error_set_cancelled (&error, is_disposing, "NMActRequest");
-		info->callback (self, info, NULL, error, info->callback_data);
+		call_id->callback (self, call_id, NULL, error, call_id->callback_data);
 	}
 
-	_get_secrets_info_free (info);
+	_get_secrets_call_id_free (call_id);
 }
 
 /**
@@ -247,7 +240,7 @@ _do_cancel_secrets (NMActRequest *self, GetSecretsInfo *info, gboolean is_dispos
  * synchronously.
  */
 void
-nm_act_request_cancel_secrets (NMActRequest *self, NMActRequestGetSecretsCallId call_id)
+nm_act_request_cancel_secrets (NMActRequest *self, NMActRequestGetSecretsCallId *call_id)
 {
 	NMActRequestPrivate *priv;
 
@@ -264,7 +257,7 @@ nm_act_request_cancel_secrets (NMActRequest *self, NMActRequestGetSecretsCallId
 
 	priv = NM_ACT_REQUEST_GET_PRIVATE (self);
 
-	if (!g_slist_find (priv->secrets_calls, call_id))
+	if (!c_list_is_linked (&call_id->call_ids_lst))
 		g_return_if_reached ();
 
 	_do_cancel_secrets (self, call_id, FALSE);
@@ -461,10 +454,8 @@ device_state_changed (NMActiveConnection *active,
 	}
 
 	if (   ac_state == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED
-	    || ac_state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN) {
-		nm_active_connection_set_default (active, FALSE);
-		nm_active_connection_set_default6 (active, FALSE);
-	}
+	    || ac_state == NM_ACTIVE_CONNECTION_STATE_UNKNOWN)
+		nm_active_connection_set_default (active, AF_UNSPEC, FALSE);
 
 	nm_active_connection_set_state (active, ac_state, ac_state_reason);
 }
@@ -537,6 +528,9 @@ get_property (GObject *object, guint prop_id,
 static void
 nm_act_request_init (NMActRequest *req)
 {
+	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (req);
+
+	c_list_init (&priv->call_ids_lst_head);
 }
 
 /**
@@ -582,10 +576,11 @@ dispose (GObject *object)
 {
 	NMActRequest *self = NM_ACT_REQUEST (object);
 	NMActRequestPrivate *priv = NM_ACT_REQUEST_GET_PRIVATE (self);
+	NMActRequestGetSecretsCallId *call_id, *call_id_safe;
 
 	/* Kill any in-progress secrets requests */
-	while (priv->secrets_calls)
-		_do_cancel_secrets (self, priv->secrets_calls->data, TRUE);
+	c_list_for_each_entry_safe (call_id, call_id_safe, &priv->call_ids_lst_head, call_ids_lst)
+		_do_cancel_secrets (self, call_id, TRUE);
 
 	/* Clear any share rules */
 	if (priv->share_rules) {
diff --git a/src/nm-act-request.h b/src/nm-act-request.h
index b8549404..5d1c9dfd 100644
--- a/src/nm-act-request.h
+++ b/src/nm-act-request.h
@@ -32,7 +32,7 @@
 #define NM_ACT_REQUEST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ACT_REQUEST, NMActRequestClass))
 
 struct _NMActRequestGetSecretsCallId;
-typedef struct _NMActRequestGetSecretsCallId *NMActRequestGetSecretsCallId;
+typedef struct _NMActRequestGetSecretsCallId NMActRequestGetSecretsCallId;
 
 GType nm_act_request_get_type (void);
 
@@ -58,20 +58,20 @@ void                  nm_act_request_add_share_rule (NMActRequest *req,
 /* Secrets handling */
 
 typedef void (*NMActRequestSecretsFunc) (NMActRequest *req,
-                                         NMActRequestGetSecretsCallId call_id,
+                                         NMActRequestGetSecretsCallId *call_id,
                                          NMSettingsConnection *connection,
                                          GError *error,
                                          gpointer user_data);
 
-NMActRequestGetSecretsCallId nm_act_request_get_secrets (NMActRequest *req,
-                                                         gboolean take_ref,
-                                                         const char *setting_name,
-                                                         NMSecretAgentGetSecretsFlags flags,
-                                                         const char *hint,
-                                                         NMActRequestSecretsFunc callback,
-                                                         gpointer callback_data);
+NMActRequestGetSecretsCallId *nm_act_request_get_secrets (NMActRequest *req,
+                                                          gboolean take_ref,
+                                                          const char *setting_name,
+                                                          NMSecretAgentGetSecretsFlags flags,
+                                                          const char *hint,
+                                                          NMActRequestSecretsFunc callback,
+                                                          gpointer callback_data);
 
-void nm_act_request_cancel_secrets (NMActRequest *req, NMActRequestGetSecretsCallId call_id);
+void nm_act_request_cancel_secrets (NMActRequest *req, NMActRequestGetSecretsCallId *call_id);
 void nm_act_request_clear_secrets (NMActRequest *self);
 
 #endif /* __NM_ACT_REQUEST_H__ */
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index 002f11f7..2286a74b 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -494,53 +494,46 @@ nm_active_connection_set_specific_object (NMActiveConnection *self,
 }
 
 void
-nm_active_connection_set_default (NMActiveConnection *self, gboolean is_default)
+nm_active_connection_set_default (NMActiveConnection *self,
+                                  int addr_family,
+                                  gboolean is_default)
 {
 	NMActiveConnectionPrivate *priv;
 
 	g_return_if_fail (NM_IS_ACTIVE_CONNECTION (self));
+	nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6));
 
 	is_default = !!is_default;
 
 	priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
-	if (priv->is_default == is_default)
-		return;
-
-	priv->is_default = is_default;
-	_notify (self, PROP_DEFAULT);
+	if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) {
+		if (priv->is_default != is_default) {
+			priv->is_default = is_default;
+			_notify (self, PROP_DEFAULT);
+		}
+	}
+	if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) {
+		if (priv->is_default6 != is_default) {
+			priv->is_default6 = is_default;
+			_notify (self, PROP_DEFAULT6);
+		}
+	}
 }
 
 gboolean
-nm_active_connection_get_default (NMActiveConnection *self)
-{
-	g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), FALSE);
-
-	return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->is_default;
-}
-
-void
-nm_active_connection_set_default6 (NMActiveConnection *self, gboolean is_default6)
+nm_active_connection_get_default (NMActiveConnection *self, int addr_family)
 {
 	NMActiveConnectionPrivate *priv;
 
-	g_return_if_fail (NM_IS_ACTIVE_CONNECTION (self));
-
-	is_default6 = !!is_default6;
-
-	priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
-	if (priv->is_default6 == is_default6)
-		return;
-
-	priv->is_default6 = is_default6;
-	_notify (self, PROP_DEFAULT6);
-}
-
-gboolean
-nm_active_connection_get_default6 (NMActiveConnection *self)
-{
 	g_return_val_if_fail (NM_IS_ACTIVE_CONNECTION (self), FALSE);
+	nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET, AF_INET6));
 
-	return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->is_default6;
+	priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
+	switch (addr_family) {
+	case AF_INET:   return priv->is_default;
+	case AF_INET6:  return priv->is_default6;
+	default:        return priv->is_default || priv->is_default6;
+	}
 }
 
 NMAuthSubject *
@@ -873,7 +866,8 @@ _settings_connection_notify_flags (NMSettingsConnection *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))
+	if (NM_FLAGS_HAS (nm_settings_connection_get_flags (settings_connection),
+	                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED))
 		return;
 
 	_set_activation_type_managed (self);
@@ -1263,10 +1257,10 @@ set_property (GObject *object, guint prop_id,
 			priv->specific_object = g_strdup (tmp);
 		break;
 	case PROP_DEFAULT:
-		priv->is_default = !!g_value_get_boolean (value);
+		priv->is_default = g_value_get_boolean (value);
 		break;
 	case PROP_DEFAULT6:
-		priv->is_default6 = !!g_value_get_boolean (value);
+		priv->is_default6 = g_value_get_boolean (value);
 		break;
 	case PROP_VPN:
 		priv->vpn = g_value_get_boolean (value);
@@ -1289,6 +1283,8 @@ nm_active_connection_init (NMActiveConnection *self)
 	priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionPrivate);
 	self->_priv = priv;
 
+	c_list_init (&self->active_connections_lst);
+
 	_LOGT ("creating");
 
 	priv->activation_type = NM_ACTIVATION_TYPE_MANAGED;
@@ -1331,6 +1327,8 @@ dispose (GObject *object)
 	NMActiveConnection *self = NM_ACTIVE_CONNECTION (object);
 	NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
 
+	nm_assert (!c_list_is_linked (&self->active_connections_lst));
+
 	_LOGD ("disposing");
 
 	if (priv->chain) {
diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h
index 5562b42f..5ede2b04 100644
--- a/src/nm-active-connection.h
+++ b/src/nm-active-connection.h
@@ -24,6 +24,8 @@
 #include "nm-exported-object.h"
 #include "nm-connection.h"
 
+#include "nm-utils/c-list.h"
+
 #define NM_TYPE_ACTIVE_CONNECTION            (nm_active_connection_get_type ())
 #define NM_ACTIVE_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnection))
 #define NM_ACTIVE_CONNECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ACTIVE_CONNECTION, NMActiveConnectionClass))
@@ -71,6 +73,10 @@ struct _NMActiveConnectionPrivate;
 struct _NMActiveConnection {
 	NMExportedObject parent;
 	struct _NMActiveConnectionPrivate *_priv;
+
+	/* active connection can be tracked in a list by NMManager. This is
+	 * the list node. */
+	CList active_connections_lst;
 };
 
 typedef struct {
@@ -131,14 +137,10 @@ void          nm_active_connection_set_specific_object (NMActiveConnection *self
                                                         const char *specific_object);
 
 void          nm_active_connection_set_default (NMActiveConnection *self,
+                                                int addr_family,
                                                 gboolean is_default);
 
-gboolean      nm_active_connection_get_default (NMActiveConnection *self);
-
-void          nm_active_connection_set_default6 (NMActiveConnection *self,
-                                                 gboolean is_default6);
-
-gboolean      nm_active_connection_get_default6 (NMActiveConnection *self);
+gboolean      nm_active_connection_get_default (NMActiveConnection *self, int addr_family);
 
 NMActiveConnectionState nm_active_connection_get_state (NMActiveConnection *self);
 
diff --git a/src/nm-checkpoint.c b/src/nm-checkpoint.c
index c3a2e743..d606dee3 100644
--- a/src/nm-checkpoint.c
+++ b/src/nm-checkpoint.c
@@ -125,10 +125,10 @@ find_settings_connection (NMCheckpoint *self,
                           gboolean *need_activation)
 {
 	NMCheckpointPrivate *priv = NM_CHECKPOINT_GET_PRIVATE (self);
-	const GSList *active_connections, *iter;
-	NMActiveConnection *active = NULL;
+	NMActiveConnection *active;
 	NMSettingsConnection *connection;
 	const char *uuid, *ac_uuid;
+	const CList *tmp_clist;
 
 	*need_activation = FALSE;
 	*need_update = FALSE;
@@ -149,9 +149,7 @@ find_settings_connection (NMCheckpoint *self,
 	}
 
 	/* ... is active, ... */
-	active_connections = nm_manager_get_active_connections (priv->manager);
-	for (iter = active_connections; iter; iter = g_slist_next (iter)) {
-		active = iter->data;
+	nm_manager_for_each_active_connection (priv->manager, active, tmp_clist) {
 		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);
@@ -159,7 +157,7 @@ find_settings_connection (NMCheckpoint *self,
 		}
 	}
 
-	if (!iter) {
+	if (!active) {
 		_LOGT ("rollback: connection %s is not active", uuid);
 		*need_activation = TRUE;
 		return connection;
@@ -256,12 +254,12 @@ activate:
 				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,
-					                                       NULL,
-					                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
-					                                       NULL);
+					nm_settings_connection_update (connection,
+					                               dev_checkpoint->settings_connection,
+					                               NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+					                               NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+					                               "checkpoint-rollback",
+					                               NULL);
 				}
 			} else {
 				/* The connection was deleted, recreate it */
@@ -335,7 +333,9 @@ next_dev:
 		guint i;
 
 		g_return_val_if_fail (priv->connection_uuids, NULL);
-		list = nm_settings_get_connections_sorted (nm_settings_get (), NULL);
+		list = nm_settings_get_connections_clone (nm_settings_get (), NULL,
+		                                          NULL, NULL,
+		                                          nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
 
 		for (i = 0; list[i]; i++) {
 			con = list[i];
diff --git a/src/nm-config-data.c b/src/nm-config-data.c
index 5b06e00a..00e5c635 100644
--- a/src/nm-config-data.c
+++ b/src/nm-config-data.c
@@ -95,6 +95,8 @@ typedef struct {
 		guint interval;
 	} connectivity;
 
+	int autoconnect_retries_default;
+
 	struct {
 		char **arr;
 		GSList *specs;
@@ -275,6 +277,14 @@ nm_config_data_get_connectivity_response (const NMConfigData *self)
 	return NM_CONFIG_DATA_GET_PRIVATE (self)->connectivity.response;
 }
 
+int
+nm_config_data_get_autoconnect_retries_default (const NMConfigData *self)
+{
+	g_return_val_if_fail (self, FALSE);
+
+	return NM_CONFIG_DATA_GET_PRIVATE (self)->autoconnect_retries_default;
+}
+
 const char *const*
 nm_config_data_get_no_auto_default (const NMConfigData *self)
 {
@@ -1527,7 +1537,7 @@ constructed (GObject *object)
 {
 	NMConfigData *self = NM_CONFIG_DATA (object);
 	NMConfigDataPrivate *priv = NM_CONFIG_DATA_GET_PRIVATE (self);
-	char *interval;
+	char *str;
 
 	priv->keyfile = _merge_keyfiles (priv->keyfile_user, priv->keyfile_intern);
 
@@ -1538,13 +1548,15 @@ constructed (GObject *object)
 	priv->connectivity.uri = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "uri", NULL));
 	priv->connectivity.response = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "response", NULL);
 
+	str = nm_config_keyfile_get_value (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_AUTOCONNECT_RETRIES_DEFAULT, NM_CONFIG_GET_VALUE_NONE);
+	priv->autoconnect_retries_default = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXINT32, 4);
+	g_free (str);
+
 	/* On missing config value, fallback to 300. On invalid value, disable connectivity checking by setting
 	 * the interval to zero. */
-	interval = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "interval", NULL);
-	priv->connectivity.interval = interval
-	    ? _nm_utils_ascii_str_to_int64 (interval, 10, 0, G_MAXUINT, 0)
-	    : NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL;
-	g_free (interval);
+	str = g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_CONNECTIVITY, "interval", NULL);
+	priv->connectivity.interval = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXUINT, NM_CONFIG_DEFAULT_CONNECTIVITY_INTERVAL);
+	g_free (str);
 
 	priv->dns_mode = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "dns", NULL));
 	priv->rc_manager = nm_strstrip (g_key_file_get_string (priv->keyfile, NM_CONFIG_KEYFILE_GROUP_MAIN, "rc-manager", NULL));
diff --git a/src/nm-config-data.h b/src/nm-config-data.h
index 3efe4259..3092a87f 100644
--- a/src/nm-config-data.h
+++ b/src/nm-config-data.h
@@ -165,6 +165,8 @@ const char *nm_config_data_get_connectivity_uri (const NMConfigData *config_data
 guint nm_config_data_get_connectivity_interval (const NMConfigData *config_data);
 const char *nm_config_data_get_connectivity_response (const NMConfigData *config_data);
 
+int nm_config_data_get_autoconnect_retries_default (const NMConfigData *config_data);
+
 const char *const*nm_config_data_get_no_auto_default (const NMConfigData *config_data);
 gboolean          nm_config_data_get_no_auto_default_for_device (const NMConfigData *self, NMDevice *device);
 
diff --git a/src/nm-config.h b/src/nm-config.h
index 47e92988..8bdd5002 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -60,6 +60,7 @@
 #define NM_CONFIG_KEYFILE_GROUP_IFNET                       "ifnet"
 
 #define NM_CONFIG_KEYFILE_KEY_MAIN_AUTH_POLKIT              "auth-polkit"
+#define NM_CONFIG_KEYFILE_KEY_MAIN_AUTOCONNECT_RETRIES_DEFAULT "autoconnect-retries-default"
 #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"
@@ -78,6 +79,7 @@
 #define NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED                "managed"
 #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_KEY_DEVICE_CARRIER_WAIT_TIMEOUT   "carrier-wait-timeout"
 
 #define NM_CONFIG_KEYFILE_KEYPREFIX_WAS                     ".was."
 #define NM_CONFIG_KEYFILE_KEYPREFIX_SET                     ".set."
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c
index a8ff3513..40a0c352 100644
--- a/src/nm-core-utils.c
+++ b/src/nm-core-utils.c
@@ -2835,13 +2835,13 @@ nm_utils_fd_get_contents (int fd,
 		if (fd_keeper >= 0)
 			fd2 = nm_steal_fd (&fd_keeper);
 		else {
-			fd2 = dup (fd);
+			fd2 = fcntl (fd, F_DUPFD_CLOEXEC, 0);
 			if (fd2 < 0)
 				return _get_contents_error (error, 0, "error during dup");
 		}
 
 		if (!(f = fdopen (fd2, "r"))) {
-			close (fd2);
+			nm_close (fd2);
 			return _get_contents_error (error, 0, "failure during fdopen");
 		}
 
@@ -4119,7 +4119,7 @@ nm_utils_file_set_contents (const gchar *filename,
 			if (errsv == EINTR)
 				continue;
 
-			close (fd);
+			nm_close (fd);
 			unlink (tmp_name);
 
 			g_set_error (error,
@@ -4148,7 +4148,7 @@ nm_utils_file_set_contents (const gchar *filename,
 	    && fsync (fd) != 0) {
 		errsv = errno;
 
-		close (fd);
+		nm_close (fd);
 		unlink (tmp_name);
 
 		g_set_error (error,
@@ -4160,7 +4160,7 @@ nm_utils_file_set_contents (const gchar *filename,
 		return FALSE;
 	}
 
-	close (fd);
+	nm_close (fd);
 
 	if (rename (tmp_name, filename)) {
 		errsv = errno;
diff --git a/src/nm-firewall-manager.c b/src/nm-firewall-manager.c
index b9878592..216d5fc8 100644
--- a/src/nm-firewall-manager.c
+++ b/src/nm-firewall-manager.c
@@ -186,7 +186,7 @@ _cb_info_create (NMFirewallManager *self,
 static void
 _cb_info_free (CBInfo *info)
 {
-	c_list_unlink (&info->lst);
+	c_list_unlink_stale (&info->lst);
 	if (info->mode != CB_INFO_MODE_IDLE) {
 		if (info->dbus.arg)
 			g_variant_unref (info->dbus.arg);
@@ -213,7 +213,7 @@ _cb_info_complete_normal (CBInfo *info, GError *error)
 
 	nm_assert (c_list_contains (&priv->pending_calls, &info->lst));
 
-	c_list_unlink_init (&info->lst);
+	c_list_unlink (&info->lst);
 
 	_cb_info_callback (info, error);
 	_cb_info_free (info);
@@ -428,7 +428,7 @@ nm_firewall_manager_cancel_call (NMFirewallManagerCallId call)
 
 	nm_assert (c_list_contains (&priv->pending_calls, &info->lst));
 
-	c_list_unlink_init (&info->lst);
+	c_list_unlink (&info->lst);
 
 	nm_utils_error_set_cancelled (&error, FALSE, "NMFirewallManager");
 
@@ -527,7 +527,7 @@ again:
 			_handle_dbus_start (self, info);
 		} else {
 			_LOGD (info, "complete: fake success");
-			c_list_unlink_init (&info->lst);
+			c_list_unlink (&info->lst);
 			_cb_info_callback (info, NULL);
 			_cb_info_free (info);
 			goto again;
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index e2f99e24..15157739 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.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 - 2014 Red Hat, Inc.
+ * Copyright (C) 2005 - 2017 Red Hat, Inc.
  * Copyright (C) 2006 - 2008 Novell, Inc.
  */
 
@@ -600,9 +600,9 @@ nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i
 	self = nm_ip4_config_new (multi_idx, ifindex);
 	priv = NM_IP4_CONFIG_GET_PRIVATE (self);
 
-	head_entry = nm_platform_lookup_addrroute (platform,
-	                                           NMP_OBJECT_TYPE_IP4_ADDRESS,
-	                                           ifindex);
+	head_entry = nm_platform_lookup_object (platform,
+	                                        NMP_OBJECT_TYPE_IP4_ADDRESS,
+	                                        ifindex);
 	if (head_entry) {
 		nmp_cache_iter_for_each (&iter, head_entry, &plobj) {
 			if (!_nm_ip_config_add_obj (priv->multi_idx,
@@ -625,9 +625,9 @@ nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i
 		_notify_addresses (self);
 	}
 
-	head_entry = nm_platform_lookup_addrroute (platform,
-	                                           NMP_OBJECT_TYPE_IP4_ROUTE,
-	                                           ifindex);
+	head_entry = nm_platform_lookup_object (platform,
+	                                        NMP_OBJECT_TYPE_IP4_ROUTE,
+	                                        ifindex);
 
 	/* Extract gateway from default route */
 	nmp_cache_iter_for_each (&iter, head_entry, &plobj)
@@ -806,44 +806,87 @@ nm_ip4_config_commit (const NMIP4Config *self,
 	return success;
 }
 
-static void
-merge_route_attributes (NMIPRoute *s_route,
-                        NMPlatformIP4Route *r,
-                        guint32 route_table)
+void
+_nm_ip_config_merge_route_attributes (int addr_family,
+                                      NMIPRoute *s_route,
+                                      NMPlatformIPRoute *r,
+                                      guint32 route_table)
 {
 	GVariant *variant;
-	guint32 u32;
-	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);
-
-	variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_TABLE);
-	u32 =   variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)
-	      ? g_variant_get_uint32 (variant)
-	      : 0;
-	r->table_coerced = nm_platform_route_table_coerce (u32 ?: (route_table ?: RT_TABLE_MAIN));
-
-	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);
+	guint32 table;
+	NMIPAddr addr;
+	NMPlatformIP4Route *r4 = (NMPlatformIP4Route *) r;
+	NMPlatformIP6Route *r6 = (NMPlatformIP6Route *) r;
+	gboolean onlink;
+
+	nm_assert (s_route);
+	nm_assert_addr_family (addr_family);
+	nm_assert (r);
+
+#define GET_ATTR(name, dst, variant_type, type, dflt) \
+	G_STMT_START { \
+		GVariant *_variant = nm_ip_route_get_attribute (s_route, ""name""); \
+		\
+		if (   _variant \
+		    && g_variant_is_of_type (_variant, G_VARIANT_TYPE_ ## variant_type)) \
+			(dst) = g_variant_get_ ## type (_variant); \
+		else \
+			(dst) = (dflt); \
+	} G_STMT_END
+
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TABLE, table, UINT32, uint32, 0);
+	r->table_coerced = nm_platform_route_table_coerce (table ?: (route_table ?: RT_TABLE_MAIN));
+
+	if (addr_family == AF_INET) {
+		GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS,        r4->tos,           BYTE,     byte, 0);
+		GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_ONLINK,     onlink,            BOOLEAN,  boolean, FALSE);
+	} else
+		onlink = FALSE;
+
+	r->r_rtm_flags = 0;
+	if (onlink)
+		r->r_rtm_flags = RTNH_F_ONLINK;
+
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW,         r->window,         UINT32,   uint32, 0);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND,           r->cwnd,           UINT32,   uint32, 0);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND,       r->initcwnd,       UINT32,   uint32, 0);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITRWND,       r->initrwnd,       UINT32,   uint32, 0);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_MTU,            r->mtu,            UINT32,   uint32, 0);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW,    r->lock_window,    BOOLEAN,  boolean, FALSE);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND,      r->lock_cwnd,      BOOLEAN,  boolean, FALSE);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND,  r->lock_initcwnd,  BOOLEAN,  boolean, FALSE);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND,  r->lock_initrwnd,  BOOLEAN,  boolean, FALSE);
+	GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU,       r->lock_mtu,       BOOLEAN,  boolean, FALSE);
 
 	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;
+		if (inet_pton (addr_family, g_variant_get_string (variant, NULL), &addr) == 1) {
+			if (addr_family == AF_INET)
+				r4->pref_src = addr.addr4;
+			else
+				r6->pref_src = addr.addr6;
+		}
 	}
 
+	if (   addr_family == AF_INET6
+	    && (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) {
+			r6->src = addr.addr6;
+			r6->src_plen = plen;
+		}
+	}
 #undef GET_ATTR
 }
 
@@ -911,8 +954,6 @@ nm_ip4_config_merge_setting (NMIP4Config *self,
 	}
 
 	/* Routes */
-	if (nm_setting_ip_config_get_ignore_auto_routes (setting))
-		nm_ip4_config_reset_routes (self);
 	for (i = 0; i < nroutes; i++) {
 		NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i);
 		NMPlatformIP4Route route;
@@ -939,7 +980,10 @@ nm_ip4_config_merge_setting (NMIP4Config *self,
 
 		route.network = nm_utils_ip4_address_clear_host_address (route.network, route.plen);
 
-		merge_route_attributes (s_route, &route, route_table);
+		_nm_ip_config_merge_route_attributes (AF_INET,
+		                                      s_route,
+		                                      NM_PLATFORM_IP_ROUTE_CAST (&route),
+		                                      route_table);
 		_add_route (self, NULL, &route, NULL);
 	}
 
@@ -2216,6 +2260,14 @@ nm_ip4_config_get_nameserver (const NMIP4Config *self, guint i)
 	return g_array_index (priv->nameservers, guint32, i);
 }
 
+const in_addr_t *
+_nm_ip4_config_get_nameserver (const NMIP4Config *self, guint i)
+{
+	const NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (self);
+
+	return &g_array_index (priv->nameservers, guint32, i);
+}
+
 /*****************************************************************************/
 
 void
diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
index 978471df..7c345d20 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -113,6 +113,11 @@ const NMDedupMultiEntry *_nm_ip_config_lookup_ip_route (const NMDedupMultiIndex
                                                         const NMPObject *needle,
                                                         NMPlatformIPRouteCmpType cmp_type);
 
+void _nm_ip_config_merge_route_attributes (int addr_family,
+                                           NMIPRoute *s_route,
+                                           NMPlatformIPRoute *r,
+                                           guint32 route_table);
+
 /*****************************************************************************/
 
 #define NM_TYPE_IP4_CONFIG (nm_ip4_config_get_type ())
@@ -216,6 +221,7 @@ void nm_ip4_config_add_nameserver (NMIP4Config *self, guint32 nameserver);
 void nm_ip4_config_del_nameserver (NMIP4Config *self, guint i);
 guint nm_ip4_config_get_num_nameservers (const NMIP4Config *self);
 guint32 nm_ip4_config_get_nameserver (const NMIP4Config *self, guint i);
+const in_addr_t *_nm_ip4_config_get_nameserver (const NMIP4Config *self, guint i);
 
 void nm_ip4_config_reset_domains (NMIP4Config *self);
 void nm_ip4_config_add_domain (NMIP4Config *self, const char *domain);
@@ -267,4 +273,135 @@ gboolean nm_ip4_config_nmpobj_remove (NMIP4Config *self,
 void nm_ip4_config_hash (const NMIP4Config *self, GChecksum *sum, gboolean dns_only);
 gboolean nm_ip4_config_equal (const NMIP4Config *a, const NMIP4Config *b);
 
+/*****************************************************************************/
+
+#include "nm-ip6-config.h"
+
+static inline gboolean
+NM_IS_IP_CONFIG (gconstpointer config)
+{
+	return NM_IS_IP4_CONFIG (config) || NM_IS_IP6_CONFIG (config);
+}
+
+#if _NM_CC_SUPPORT_GENERIC
+/* _NM_IS_IP_CONFIG() is a bit unusual. If _Generic() is supported,
+ * it checks whether @config is either NM_IS_IP4_CONFIG() or NM_IS_IP6_CONFIG(),
+ * depending on the pointer type of @config.
+ *
+ * For example, with _Generic() support, the following assertions would fail:
+ *    NMIP6Config *ptr = (NMIP6Config *) nm_ip4_config_new(...);
+ *    g_assert (_NM_IS_IP_CONFIG (ptr, ptr));
+ * but the following would pass:
+ *    NMIP4Config *ptr = nm_ip4_config_new(...);
+ *    g_assert (_NM_IS_IP_CONFIG (ptr, ptr));
+ */
+#define _NM_IS_IP_CONFIG(typeexpr, config) \
+	({ \
+		const void *const _config = (config); \
+		_Generic ((typeexpr), \
+		          const void        *const: (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \
+		          const void        *     : (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \
+		                void        *const: (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \
+		                void        *     : (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \
+		          const NMIPConfig  *const: (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \
+		          const NMIPConfig  *     : (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \
+		                NMIPConfig  *const: (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \
+		                NMIPConfig  *     : (NM_IS_IP4_CONFIG (_config) || NM_IS_IP6_CONFIG (_config)), \
+		          const NMIP4Config *const: (NM_IS_IP4_CONFIG (_config)), \
+		          const NMIP4Config *     : (NM_IS_IP4_CONFIG (_config)), \
+		                NMIP4Config *const: (NM_IS_IP4_CONFIG (_config)), \
+		                NMIP4Config *     : (NM_IS_IP4_CONFIG (_config)), \
+		          const NMIP6Config *const: (NM_IS_IP6_CONFIG (_config)), \
+		          const NMIP6Config *     : (NM_IS_IP6_CONFIG (_config)), \
+		                NMIP6Config *const: (NM_IS_IP6_CONFIG (_config)), \
+		                NMIP6Config *     : (NM_IS_IP6_CONFIG (_config))); \
+	})
+#else
+#define _NM_IS_IP_CONFIG(typeexpr, config) NM_IS_IP_CONFIG(config)
+#endif
+
+#define NM_IP_CONFIG_CAST(config) \
+	({ \
+		const void *const _configx = (config); \
+		\
+		nm_assert (!_configx || _NM_IS_IP_CONFIG ((config), _configx)); \
+		NM_CONSTCAST_FULL (NMIPConfig, (config), _configx, NMIP4Config, NMIP6Config); \
+	})
+
+static inline int
+nm_ip_config_get_addr_family (const NMIPConfig *config)
+{
+	if (NM_IS_IP4_CONFIG (config))
+		return AF_INET;
+	if (NM_IS_IP6_CONFIG (config))
+		return AF_INET6;
+	g_return_val_if_reached (AF_UNSPEC);
+}
+
+#define _NM_IP_CONFIG_DISPATCH(config, v4_func, v6_func, dflt, ...) \
+	G_STMT_START { \
+		gconstpointer _config = (config); \
+		\
+		if (NM_IS_IP4_CONFIG (_config)) { \
+			return v4_func ((NMIP4Config *) _config, ##__VA_ARGS__); \
+		} else { \
+			nm_assert (NM_IS_IP6_CONFIG (_config)); \
+			return v6_func ((NMIP6Config *) _config, ##__VA_ARGS__); \
+		} \
+	} G_STMT_END
+
+static inline int
+nm_ip_config_get_dns_priority (const NMIPConfig *self)
+{
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_dns_priority, nm_ip6_config_get_dns_priority, 0);
+}
+
+static inline guint
+nm_ip_config_get_num_nameservers (const NMIPConfig *self)
+{
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_nameservers, nm_ip6_config_get_num_nameservers, 0);
+}
+
+static inline gconstpointer
+nm_ip_config_get_nameserver (const NMIPConfig *self, guint i)
+{
+	_NM_IP_CONFIG_DISPATCH (self, _nm_ip4_config_get_nameserver, nm_ip6_config_get_nameserver, 0, i);
+}
+
+static inline guint
+nm_ip_config_get_num_domains (const NMIPConfig *self)
+{
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_domains, nm_ip6_config_get_num_domains, 0);
+}
+
+static inline const char *
+nm_ip_config_get_domain (const NMIPConfig *self, guint i)
+{
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_domain, nm_ip6_config_get_domain, NULL, i);
+}
+
+static inline guint
+nm_ip_config_get_num_searches (const NMIPConfig *self)
+{
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_searches, nm_ip6_config_get_num_searches, 0);
+}
+
+static inline const char *
+nm_ip_config_get_search (const NMIPConfig *self, guint i)
+{
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_search, nm_ip6_config_get_search, NULL, i);
+}
+
+static inline guint
+nm_ip_config_get_num_dns_options (const NMIPConfig *self)
+{
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_num_dns_options, nm_ip6_config_get_num_dns_options, 0);
+}
+
+static inline const char *
+nm_ip_config_get_dns_option (const NMIPConfig *self, guint i)
+{
+	_NM_IP_CONFIG_DISPATCH (self, nm_ip4_config_get_dns_option, nm_ip6_config_get_dns_option, NULL, i);
+}
+
 #endif /* __NETWORKMANAGER_IP4_CONFIG_H__ */
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 3f0bbfe7..ea3a2029 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.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.
  */
 
@@ -384,9 +384,9 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i
 	self = nm_ip6_config_new (multi_idx, ifindex);
 	priv = NM_IP6_CONFIG_GET_PRIVATE (self);
 
-	head_entry = nm_platform_lookup_addrroute (platform,
-	                                           NMP_OBJECT_TYPE_IP6_ADDRESS,
-	                                           ifindex);
+	head_entry = nm_platform_lookup_object (platform,
+	                                        NMP_OBJECT_TYPE_IP6_ADDRESS,
+	                                        ifindex);
 	if (head_entry) {
 		nmp_cache_iter_for_each (&iter, head_entry, &plobj) {
 			if (!_nm_ip_config_add_obj (priv->multi_idx,
@@ -409,9 +409,9 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i
 		_notify_addresses (self);
 	}
 
-	head_entry = nm_platform_lookup_addrroute (platform,
-	                                           NMP_OBJECT_TYPE_IP6_ROUTE,
-	                                           ifindex);
+	head_entry = nm_platform_lookup_object (platform,
+	                                        NMP_OBJECT_TYPE_IP6_ROUTE,
+	                                        ifindex);
 
 	nmp_cache_iter_for_each (&iter, head_entry, &plobj)
 		_add_route (self, plobj, NULL, NULL);
@@ -571,65 +571,6 @@ nm_ip6_config_commit (const NMIP6Config *self,
 	return success;
 }
 
-static void
-merge_route_attributes (NMIPRoute *s_route,
-                        NMPlatformIP6Route *r,
-                        guint32 route_table)
-{
-	GVariant *variant;
-	guint32 u32;
-	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);
-
-	variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_TABLE);
-	u32 =   variant && g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)
-	      ? g_variant_get_uint32 (variant)
-	      : 0;
-	r->table_coerced = nm_platform_route_table_coerce (u32 ?: (route_table ?: RT_TABLE_MAIN));
-
-	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 *self,
                              NMSettingIPConfig *setting,
@@ -688,8 +629,6 @@ nm_ip6_config_merge_setting (NMIP6Config *self,
 	}
 
 	/* Routes */
-	if (nm_setting_ip_config_get_ignore_auto_routes (setting))
-		nm_ip6_config_reset_routes (self);
 	for (i = 0; i < nroutes; i++) {
 		NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i);
 		NMPlatformIP6Route route;
@@ -716,7 +655,10 @@ nm_ip6_config_merge_setting (NMIP6Config *self,
 
 		nm_utils_ip6_address_clear_host_address (&route.network, &route.network, route.plen);
 
-		merge_route_attributes (s_route, &route, route_table);
+		_nm_ip_config_merge_route_attributes (AF_INET,
+		                                      s_route,
+		                                      NM_PLATFORM_IP_ROUTE_CAST (&route),
+		                                      route_table);
 		_add_route (self, NULL, &route, NULL);
 	}
 
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 2766e86a..7f1b9a9d 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -62,41 +62,7 @@
 #include "introspection/org.freedesktop.NetworkManager.h"
 #include "introspection/org.freedesktop.NetworkManager.Device.h"
 
-static gboolean add_device (NMManager *self, NMDevice *device, GError **error);
-
-static NMActiveConnection *_new_active_connection (NMManager *self,
-                                                   NMConnection *connection,
-                                                   NMConnection *applied,
-                                                   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);
-
-static gboolean find_master (NMManager *self,
-                             NMConnection *connection,
-                             NMDevice *device,
-                             NMSettingsConnection **out_master_connection,
-                             NMDevice **out_master_device,
-                             NMActiveConnection **out_master_ac,
-                             GError **error);
-
-static void nm_manager_update_state (NMManager *manager);
-
-static void connection_changed (NMManager *self, NMConnection *connection);
-static void device_sleep_cb (NMDevice *device,
-                             GParamSpec *pspec,
-                             NMManager *self);
-
-static void settings_startup_complete_changed (NMSettings *settings,
-                                               GParamSpec *pspec,
-                                               NMManager *self);
-
-static void retry_connections_for_parent_device (NMManager *self, NMDevice *device);
-
-static NM_CACHED_QUARK_FCN ("active-connection-add-and-activate", active_connection_add_and_activate_quark)
+/*****************************************************************************/
 
 typedef struct {
 	gboolean user_enabled;
@@ -109,12 +75,56 @@ typedef struct {
 	const char *hw_prop;
 } RadioState;
 
+enum {
+	DEVICE_ADDED,
+	INTERNAL_DEVICE_ADDED,
+	DEVICE_REMOVED,
+	INTERNAL_DEVICE_REMOVED,
+	STATE_CHANGED,
+	CHECK_PERMISSIONS,
+	ACTIVE_CONNECTION_ADDED,
+	ACTIVE_CONNECTION_REMOVED,
+	CONFIGURE_QUIT,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMManager,
+	PROP_VERSION,
+	PROP_CAPABILITIES,
+	PROP_STATE,
+	PROP_STARTUP,
+	PROP_NETWORKING_ENABLED,
+	PROP_WIRELESS_ENABLED,
+	PROP_WIRELESS_HARDWARE_ENABLED,
+	PROP_WWAN_ENABLED,
+	PROP_WWAN_HARDWARE_ENABLED,
+	PROP_WIMAX_ENABLED,
+	PROP_WIMAX_HARDWARE_ENABLED,
+	PROP_ACTIVE_CONNECTIONS,
+	PROP_CONNECTIVITY,
+	PROP_CONNECTIVITY_CHECK_AVAILABLE,
+	PROP_CONNECTIVITY_CHECK_ENABLED,
+	PROP_PRIMARY_CONNECTION,
+	PROP_PRIMARY_CONNECTION_TYPE,
+	PROP_ACTIVATING_CONNECTION,
+	PROP_DEVICES,
+	PROP_METERED,
+	PROP_GLOBAL_DNS_CONFIGURATION,
+	PROP_ALL_DEVICES,
+
+	/* Not exported */
+	PROP_SLEEPING,
+);
+
 typedef struct {
 	NMPlatform *platform;
 
 	GArray *capabilities;
 
-	GSList *active_connections;
+	CList active_connections_lst_head;
 	GSList *authorizing_connections;
 	guint ac_cleanup_id;
 	NMActiveConnection *primary_connection;
@@ -166,6 +176,9 @@ typedef struct {
 
 	bool sleeping:1;
 	bool net_enabled:1;
+
+	guint delete_volatile_connection_idle_id;
+	CList delete_volatile_connection_lst_head;
 } NMManagerPrivate;
 
 struct _NMManager {
@@ -181,49 +194,7 @@ G_DEFINE_TYPE (NMManager, nm_manager, NM_TYPE_EXPORTED_OBJECT)
 
 #define NM_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMManager, NM_IS_MANAGER)
 
-enum {
-	DEVICE_ADDED,
-	INTERNAL_DEVICE_ADDED,
-	DEVICE_REMOVED,
-	INTERNAL_DEVICE_REMOVED,
-	STATE_CHANGED,
-	CHECK_PERMISSIONS,
-	ACTIVE_CONNECTION_ADDED,
-	ACTIVE_CONNECTION_REMOVED,
-	CONFIGURE_QUIT,
-
-	LAST_SIGNAL
-};
-
-static guint signals[LAST_SIGNAL] = { 0 };
-
-NM_GOBJECT_PROPERTIES_DEFINE (NMManager,
-	PROP_VERSION,
-	PROP_CAPABILITIES,
-	PROP_STATE,
-	PROP_STARTUP,
-	PROP_NETWORKING_ENABLED,
-	PROP_WIRELESS_ENABLED,
-	PROP_WIRELESS_HARDWARE_ENABLED,
-	PROP_WWAN_ENABLED,
-	PROP_WWAN_HARDWARE_ENABLED,
-	PROP_WIMAX_ENABLED,
-	PROP_WIMAX_HARDWARE_ENABLED,
-	PROP_ACTIVE_CONNECTIONS,
-	PROP_CONNECTIVITY,
-	PROP_CONNECTIVITY_CHECK_AVAILABLE,
-	PROP_CONNECTIVITY_CHECK_ENABLED,
-	PROP_PRIMARY_CONNECTION,
-	PROP_PRIMARY_CONNECTION_TYPE,
-	PROP_ACTIVATING_CONNECTION,
-	PROP_DEVICES,
-	PROP_METERED,
-	PROP_GLOBAL_DNS_CONFIGURATION,
-	PROP_ALL_DEVICES,
-
-	/* Not exported */
-	PROP_SLEEPING,
-);
+/*****************************************************************************/
 
 NM_DEFINE_SINGLETON_INSTANCE (NMManager);
 
@@ -296,7 +267,39 @@ NM_DEFINE_SINGLETON_INSTANCE (NMManager);
 
 /*****************************************************************************/
 
-static NM_CACHED_QUARK_FCN ("autoconnect-root", autoconnect_root_quark)
+static gboolean add_device (NMManager *self, NMDevice *device, GError **error);
+
+static NMActiveConnection *_new_active_connection (NMManager *self,
+                                                   NMConnection *connection,
+                                                   NMConnection *applied,
+                                                   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);
+
+static gboolean find_master (NMManager *self,
+                             NMConnection *connection,
+                             NMDevice *device,
+                             NMSettingsConnection **out_master_connection,
+                             NMDevice **out_master_device,
+                             NMActiveConnection **out_master_ac,
+                             GError **error);
+
+static void nm_manager_update_state (NMManager *manager);
+
+static void connection_changed (NMManager *self, NMConnection *connection);
+static void device_sleep_cb (NMDevice *device,
+                             GParamSpec *pspec,
+                             NMManager *self);
+
+static void settings_startup_complete_changed (NMSettings *settings,
+                                               GParamSpec *pspec,
+                                               NMManager *self);
+
+static void retry_connections_for_parent_device (NMManager *self, NMDevice *device);
 
 static void active_connection_state_changed (NMActiveConnection *active,
                                              GParamSpec *pspec,
@@ -308,44 +311,68 @@ static void active_connection_parent_active (NMActiveConnection *active,
                                              NMActiveConnection *parent_ac,
                                              NMManager *self);
 
+static NMActiveConnection *active_connection_find_first (NMManager *self,
+                                                         NMSettingsConnection *settings_connection,
+                                                         const char *uuid,
+                                                         NMActiveConnectionState max_state);
+
+/*****************************************************************************/
+
+static NM_CACHED_QUARK_FCN ("active-connection-add-and-activate", active_connection_add_and_activate_quark)
+
+static NM_CACHED_QUARK_FCN ("autoconnect-root", autoconnect_root_quark)
+
+/*****************************************************************************/
+
+static void
+_delete_volatile_connection_do (NMManager *self,
+                                NMSettingsConnection *connection)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
+	                   NM_SETTINGS_CONNECTION_FLAGS_VOLATILE))
+		return;
+	if (active_connection_find_first (self,
+	                                  connection,
+	                                  NULL,
+	                                  NM_ACTIVE_CONNECTION_STATE_DEACTIVATED))
+		return;
+	if (!nm_settings_has_connection (priv->settings, connection))
+		return;
+
+	_LOGD (LOGD_DEVICE, "volatile connection disconnected. Deleting connection '%s' (%s)",
+	       nm_settings_connection_get_id (connection), nm_settings_connection_get_uuid (connection));
+	nm_settings_connection_delete (connection, NULL);
+}
+
 /* Returns: whether to notify D-Bus of the removal or not */
 static gboolean
 active_connection_remove (NMManager *self, NMActiveConnection *active)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	gboolean notify = nm_exported_object_is_exported (NM_EXPORTED_OBJECT (active));
-	GSList *found;
-
-	/* FIXME: switch to a GList for faster removal */
-	found = g_slist_find (priv->active_connections, active);
-	if (found) {
-		NMSettingsConnection *connection;
-
-		priv->active_connections = g_slist_remove (priv->active_connections, active);
-		g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active);
-		g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self);
-		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 (   (connection = nm_active_connection_get_settings_connection (active))
-		    && nm_settings_connection_get_volatile (connection))
-			g_object_ref (connection);
-		else
-			connection = NULL;
+	gs_unref_object NMSettingsConnection *connection = NULL;
+	gboolean notify;
 
-		nm_exported_object_clear_and_unexport (&active);
+	nm_assert (NM_IS_ACTIVE_CONNECTION (active));
+	nm_assert (c_list_contains (&priv->active_connections_lst_head, &active->active_connections_lst));
 
-		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);
-			}
-			g_object_unref (connection);
-		}
-	}
+	notify = nm_exported_object_is_exported (NM_EXPORTED_OBJECT (active));
+
+	c_list_unlink (&active->active_connections_lst);
+	g_signal_emit (self, signals[ACTIVE_CONNECTION_REMOVED], 0, active);
+	g_signal_handlers_disconnect_by_func (active, active_connection_state_changed, self);
+	g_signal_handlers_disconnect_by_func (active, active_connection_default_changed, self);
+	g_signal_handlers_disconnect_by_func (active, active_connection_parent_active, self);
+
+	connection = nm_g_object_ref (nm_active_connection_get_settings_connection (active));
+
+	nm_exported_object_clear_and_unexport (&active);
 
-	return found && notify;
+	if (connection)
+		_delete_volatile_connection_do (self, connection);
+
+	return notify;
 }
 
 static gboolean
@@ -353,16 +380,12 @@ _active_connection_cleanup (gpointer user_data)
 {
 	NMManager *self = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	GSList *iter;
+	NMActiveConnection *ac, *ac_safe;
 
 	priv->ac_cleanup_id = 0;
 
 	g_object_freeze_notify (G_OBJECT (self));
-	iter = priv->active_connections;
-	while (iter) {
-		NMActiveConnection *ac = iter->data;
-
-		iter = iter->next;
+	c_list_for_each_entry_safe (ac, ac_safe, &priv->active_connections_lst_head, active_connections_lst) {
 		if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_DEACTIVATED) {
 			if (active_connection_remove (self, ac))
 				_notify (self, PROP_ACTIVE_CONNECTIONS);
@@ -420,10 +443,11 @@ active_connection_add (NMManager *self, NMActiveConnection *active)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 
-	g_return_if_fail (g_slist_find (priv->active_connections, active) == FALSE);
+	nm_assert (NM_IS_ACTIVE_CONNECTION (active));
+	nm_assert (!c_list_is_linked (&active->active_connections_lst));
 
-	priv->active_connections = g_slist_prepend (priv->active_connections,
-	                                            g_object_ref (active));
+	c_list_link_front (&priv->active_connections_lst_head, &active->active_connections_lst);
+	g_object_ref (active);
 
 	g_signal_connect (active,
 	                  "notify::" NM_ACTIVE_CONNECTION_STATE,
@@ -445,10 +469,10 @@ active_connection_add (NMManager *self, NMActiveConnection *active)
 		_notify (self, PROP_ACTIVE_CONNECTIONS);
 }
 
-const GSList *
+const CList *
 nm_manager_get_active_connections (NMManager *manager)
 {
-	return NM_MANAGER_GET_PRIVATE (manager)->active_connections;
+	return &NM_MANAGER_GET_PRIVATE (manager)->active_connections_lst_head;
 }
 
 static NMActiveConnection *
@@ -457,16 +481,12 @@ active_connection_find_first (NMManager *self,
                               const char *uuid,
                               NMActiveConnectionState max_state)
 {
-	NMManagerPrivate *priv;
-	GSList *iter;
-
-	g_return_val_if_fail (NM_IS_MANAGER (self), NULL);
-	g_return_val_if_fail (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection), NULL);
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	NMActiveConnection *ac;
 
-	priv = NM_MANAGER_GET_PRIVATE (self);
+	nm_assert (!settings_connection || NM_IS_SETTINGS_CONNECTION (settings_connection));
 
-	for (iter = priv->active_connections; iter; iter = iter->next) {
-		NMActiveConnection *ac = iter->data;
+	c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
 		NMSettingsConnection *con;
 
 		con = nm_active_connection_get_settings_connection (ac);
@@ -505,47 +525,35 @@ _get_activatable_connections_filter (NMSettings *settings,
                                      NMSettingsConnection *connection,
                                      gpointer user_data)
 {
-	if (nm_settings_connection_get_volatile (connection))
+	if (NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
+	                  NM_SETTINGS_CONNECTION_FLAGS_VOLATILE))
 		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().
- */
 NMSettingsConnection **
 nm_manager_get_activatable_connections (NMManager *manager, guint *out_len, gboolean sort)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	NMSettingsConnection **connections;
-	guint len;
 
-	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;
+	return nm_settings_get_connections_clone (priv->settings, out_len,
+	                                          _get_activatable_connections_filter,
+	                                          manager,
+	                                          sort ? nm_settings_connection_cmp_autoconnect_priority_p_with_data : NULL,
+	                                          NULL);
 }
 
 static NMActiveConnection *
 active_connection_get_by_path (NMManager *manager, const char *path)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	GSList *iter;
-
-	g_return_val_if_fail (manager != NULL, NULL);
-	g_return_val_if_fail (path != NULL, NULL);
+	NMActiveConnection *ac;
 
-	for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) {
-		NMActiveConnection *candidate = iter->data;
+	nm_assert (path);
 
-		if (g_strcmp0 (path, nm_exported_object_get_path (NM_EXPORTED_OBJECT (candidate))) == 0)
-			return candidate;
+	c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
+		if (nm_streq0 (path, nm_exported_object_get_path (NM_EXPORTED_OBJECT (ac))))
+			return ac;
 	}
 	return NULL;
 }
@@ -827,16 +835,14 @@ find_best_device_state (NMManager *manager)
 {
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
 	NMState best_state = NM_STATE_DISCONNECTED;
-	GSList *iter;
+	NMActiveConnection *ac;
 
-	for (iter = priv->active_connections; iter; iter = iter->next) {
-		NMActiveConnection *ac = NM_ACTIVE_CONNECTION (iter->data);
+	c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
 		NMActiveConnectionState ac_state = nm_active_connection_get_state (ac);
 
 		switch (ac_state) {
 		case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
-			if (   nm_active_connection_get_default (ac)
-			    || nm_active_connection_get_default6 (ac)) {
+			if (nm_active_connection_get_default (ac, AF_UNSPEC)) {
 				if (priv->connectivity_state == NM_CONNECTIVITY_FULL)
 					return NM_STATE_CONNECTED_GLOBAL;
 
@@ -1391,7 +1397,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, NULL);
+	connections = nm_settings_get_connections_clone (priv->settings, NULL,
+	                                                 NULL, NULL,
+	                                                 nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
 	for (i = 0; connections[i]; i++) {
 		NMConnection *candidate = NM_CONNECTION (connections[i]);
 		NMSettingConnection *s_con;
@@ -1435,7 +1443,9 @@ retry_connections_for_parent_device (NMManager *self, NMDevice *device)
 
 	g_return_if_fail (device);
 
-	connections = nm_settings_get_connections_sorted (priv->settings, NULL);
+	connections = nm_settings_get_connections_clone (priv->settings, NULL,
+	                                                 NULL, NULL,
+	                                                 nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
 	for (i = 0; connections[i]; i++) {
 		NMConnection *candidate = NM_CONNECTION (connections[i]);
 		gs_free_error GError *error = NULL;
@@ -1491,6 +1501,74 @@ connection_updated_cb (NMSettings *settings,
 		connection_changed (self, connection);
 }
 
+/*****************************************************************************/
+
+typedef struct {
+	CList delete_volatile_connection_lst;
+	NMSettingsConnection *connection;
+} DeleteVolatileConnectionData;
+
+static void
+_delete_volatile_connection_all (NMManager *self, gboolean do_delete)
+{
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	CList *lst;
+	DeleteVolatileConnectionData *data;
+
+	while ((lst = c_list_first (&priv->delete_volatile_connection_lst_head))) {
+		gs_unref_object NMSettingsConnection *connection = NULL;
+
+		data = c_list_entry (lst,
+		                     DeleteVolatileConnectionData,
+		                     delete_volatile_connection_lst);
+		connection = data->connection;
+		c_list_unlink_stale (&data->delete_volatile_connection_lst);
+		g_slice_free (DeleteVolatileConnectionData, data);
+
+		if (do_delete)
+			_delete_volatile_connection_do (self, connection);
+	}
+}
+
+static gboolean
+_delete_volatile_connection_cb (gpointer user_data)
+{
+	NMManager *self = user_data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+	priv->delete_volatile_connection_idle_id = 0;
+	_delete_volatile_connection_all (self, TRUE);
+	return G_SOURCE_REMOVE;
+}
+
+static void
+connection_flags_changed (NMSettings *settings,
+                          NMSettingsConnection *connection,
+                          gpointer user_data)
+{
+	NMManager *self = user_data;
+	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+	DeleteVolatileConnectionData *data;
+
+	if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
+	                   NM_SETTINGS_CONNECTION_FLAGS_VOLATILE))
+		return;
+
+	if (active_connection_find_first (self, connection, NULL, NM_ACTIVE_CONNECTION_STATE_DEACTIVATED)) {
+		/* the connection still have an active-connection. It will be purged
+		 * when the active connection(s) get(s) removed. */
+		return;
+	}
+
+	data = g_slice_new (DeleteVolatileConnectionData);
+	data->connection = g_object_ref (connection);
+	c_list_link_tail (&priv->delete_volatile_connection_lst_head, &data->delete_volatile_connection_lst);
+	if (!priv->delete_volatile_connection_idle_id)
+		priv->delete_volatile_connection_idle_id = g_idle_add (_delete_volatile_connection_cb, self);
+}
+
+/*****************************************************************************/
+
 static void
 system_unmanaged_devices_changed_cb (NMSettings *settings,
                                      GParamSpec *pspec,
@@ -1860,6 +1938,7 @@ get_existing_connection (NMManager *self,
 
 		matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection (connections,
 		                                                             connection,
+		                                                             TRUE,
 		                                                             nm_device_has_carrier (device),
 		                                                             nm_device_get_route_metric (device, AF_INET),
 		                                                             nm_device_get_route_metric (device, AF_INET6),
@@ -1889,6 +1968,7 @@ get_existing_connection (NMManager *self,
 
 			matched = NM_SETTINGS_CONNECTION (nm_utils_match_connection ((NMConnection *const*) connections,
 			                                                             connection,
+			                                                             FALSE,
 			                                                             nm_device_has_carrier (device),
 			                                                             nm_device_get_route_metric (device, AF_INET),
 			                                                             nm_device_get_route_metric (device, AF_INET6),
@@ -2476,7 +2556,7 @@ _platform_link_cb_idle (PlatformLinkCbData *data)
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	const NMPlatformLink *plink;
 
-	c_list_unlink (&data->lst);
+	c_list_unlink_stale (&data->lst);
 	g_slice_free (PlatformLinkCbData, data);
 
 	plink = nm_platform_link_get (priv->platform, ifindex);
@@ -3021,7 +3101,9 @@ find_slaves (NMManager *manager,
 	 * even if a slave was already active, it might be deactivated during
 	 * master reactivation.
 	 */
-	all_connections = nm_settings_get_connections_sorted (priv->settings, &n_all_connections);
+	all_connections = nm_settings_get_connections_clone (priv->settings, &n_all_connections,
+	                                                     NULL, NULL,
+	                                                     nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
 	for (i = 0; i < n_all_connections; i++) {
 		NMSettingsConnection *master_connection = NULL;
 		NMDevice *master_device = NULL, *slave_device;
@@ -3627,11 +3709,11 @@ _internal_activation_auth_done (NMActiveConnection *active,
                                 gpointer user_data1,
                                 gpointer user_data2)
 {
+	_nm_unused gs_unref_object NMActiveConnection *active_to_free = active;
 	NMManager *self = user_data1;
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
-	NMActiveConnection *candidate;
-	GError *error = NULL;
-	GSList *iter;
+	NMActiveConnection *ac;
+	gs_free_error GError *error = NULL;
 
 	priv->authorizing_connections = g_slist_remove (priv->authorizing_connections, active);
 
@@ -3640,12 +3722,12 @@ _internal_activation_auth_done (NMActiveConnection *active,
 	 * detect a duplicate if the existing active connection is undergoing
 	 * authorization in impl_manager_activate_connection().
 	 */
-	if (success && nm_auth_subject_is_internal (nm_active_connection_get_subject (active))) {
-		for (iter = priv->active_connections; iter; iter = iter->next) {
-			candidate = iter->data;
-			if (   nm_active_connection_get_device (candidate) == nm_active_connection_get_device (active)
-			    && nm_active_connection_get_settings_connection (candidate) == nm_active_connection_get_settings_connection (active)
-			    && NM_IN_SET (nm_active_connection_get_state (candidate),
+	if (   success
+	    && nm_auth_subject_is_internal (nm_active_connection_get_subject (active))) {
+		c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
+			if (   nm_active_connection_get_device (ac) == nm_active_connection_get_device (active)
+			    && nm_active_connection_get_settings_connection (ac) == nm_active_connection_get_settings_connection (active)
+			    && NM_IN_SET (nm_active_connection_get_state (ac),
 			                  NM_ACTIVE_CONNECTION_STATE_ACTIVATING,
 			                  NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) {
 				g_set_error (&error,
@@ -3660,16 +3742,12 @@ _internal_activation_auth_done (NMActiveConnection *active,
 	}
 
 	if (success) {
-		if (_internal_activate_generic (self, active, &error)) {
-			g_object_unref (active);
+		if (_internal_activate_generic (self, active, &error))
 			return;
-		}
 	}
 
-	g_assert (error_desc || error);
+	nm_assert (error_desc || error);
 	_internal_activation_failed (self, active, error_desc ? error_desc : error->message);
-	g_object_unref (active);
-	g_clear_error (&error);
 }
 
 /**
@@ -3894,6 +3972,9 @@ _activation_auth_done (NMActiveConnection *active,
 
 	if (success) {
 		if (_internal_activate_generic (self, active, &error)) {
+			nm_settings_connection_autoconnect_blocked_reason_set (connection,
+			                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
+			                                                       FALSE);
 			g_dbus_method_invocation_return_value (context,
 			                                       g_variant_new ("(o)",
 			                                       nm_exported_object_get_path (NM_EXPORTED_OBJECT (active))));
@@ -4037,10 +4118,12 @@ activation_add_done (NMSettings *settings,
 		nm_active_connection_set_settings_connection (active, new_connection);
 
 		if (_internal_activate_generic (self, active, &local)) {
-			nm_settings_connection_commit_changes (new_connection,
-			                                       NULL,
-			                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED,
-			                                       NULL);
+			nm_settings_connection_update (new_connection,
+			                               NULL,
+			                               NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+			                               NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED,
+			                               "add-and-activate",
+			                               NULL);
 			g_dbus_method_invocation_return_value (
 			    context,
 			    g_variant_new ("(oo)",
@@ -4166,7 +4249,9 @@ impl_manager_add_and_activate_connection (NMManager *self,
 		gs_free NMSettingsConnection **connections = NULL;
 		guint i, len;
 
-		connections = nm_settings_get_connections_sorted (priv->settings, &len);
+		connections = nm_settings_get_connections_clone (priv->settings, &len,
+		                                                 NULL, NULL,
+		                                                 nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
 		all_connections = NULL;
 		for (i = len; i > 0; ) {
 			i--;
@@ -4508,7 +4593,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 	waking_from_suspend = sleeping_changed && !priv->sleeping;
 
 	if (manager_sleeping (self)) {
-		_LOGI (LOGD_SUSPEND, "%s...", suspending ? "sleeping" : "disabling");
+		_LOGD (LOGD_SUSPEND, "sleep: %s...", suspending ? "sleeping" : "disabling");
 
 		/* FIXME: are there still hardware devices that need to be disabled around
 		 * suspend/resume?
@@ -4539,7 +4624,7 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 			}
 		}
 	} else {
-		_LOGI (LOGD_SUSPEND, "%s...", waking_from_suspend ? "waking up" : "re-enabling");
+		_LOGD (LOGD_SUSPEND, "sleep: %s...", waking_from_suspend ? "waking up" : "re-enabling");
 
 		if (waking_from_suspend) {
 			sleep_devices_clear (self);
@@ -4596,7 +4681,6 @@ do_sleep_wake (NMManager *self, gboolean sleeping_changed)
 					       enabled ? "enabling" : "disabling",
 					       rstate->desc, rstate->hw_enabled, rstate->sw_enabled, rstate->user_enabled);
 				}
-
 				if (nm_device_get_rfkill_type (device) == rstate->rtype)
 					nm_device_set_enabled (device, enabled);
 			}
@@ -4620,7 +4704,7 @@ _internal_sleep (NMManager *self, gboolean do_sleep)
 	if (priv->sleeping == do_sleep)
 		return;
 
-	_LOGI (LOGD_SUSPEND, "%s requested (sleeping: %s  enabled: %s)",
+	_LOGI (LOGD_SUSPEND, "sleep: %s requested (sleeping: %s  enabled: %s)",
 	       do_sleep ? "sleep" : "wake",
 	       priv->sleeping ? "yes" : "no",
 	       priv->net_enabled ? "yes" : "no");
@@ -4732,7 +4816,7 @@ sleeping_cb (NMSleepMonitor *monitor, gboolean is_about_to_suspend, gpointer use
 {
 	NMManager *self = user_data;
 
-	_LOGD (LOGD_SUSPEND, "Received %s signal", is_about_to_suspend ? "sleeping" : "resuming");
+	_LOGT (LOGD_SUSPEND, "sleep: received %s signal", is_about_to_suspend ? "sleeping" : "resuming");
 	_internal_sleep (self, is_about_to_suspend);
 }
 
@@ -5225,7 +5309,9 @@ 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, NULL);
+	connections = nm_settings_get_connections_clone (priv->settings, NULL,
+	                                                 NULL, NULL,
+	                                                 nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
 	for (i = 0; connections[i]; i++)
 		connection_changed (self, NM_CONNECTION (connections[i]));
 
@@ -5908,7 +5994,7 @@ rfkill_change (NMManager *self, const char *desc, RfKillType rtype, gboolean ena
 	if (fcntl (fd, F_SETFL, O_NONBLOCK) < 0) {
 		_LOGW (LOGD_RFKILL, "rfkill: (%s): failed to set killswitch device for "
 		       "non-blocking operation", desc);
-		close (fd);
+		nm_close (fd);
 		return;
 	}
 
@@ -5938,7 +6024,7 @@ rfkill_change (NMManager *self, const char *desc, RfKillType rtype, gboolean ena
 		_LOGW (LOGD_RFKILL, "rfkill: (%s): failed to change WiFi killswitch state", desc);
 	}
 
-	close (fd);
+	nm_close (fd);
 }
 
 static void
@@ -5989,19 +6075,15 @@ periodic_update_active_connection_timestamps (gpointer user_data)
 {
 	NMManager *manager = NM_MANAGER (user_data);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-	GSList *iter;
-
-	for (iter = priv->active_connections; iter; iter = g_slist_next (iter)) {
-		NMActiveConnection *ac = iter->data;
-		NMSettingsConnection *connection;
+	NMActiveConnection *ac;
 
+	c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
 		if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
-			connection = nm_active_connection_get_settings_connection (ac);
-			nm_settings_connection_update_timestamp (connection, (guint64) time (NULL), FALSE);
+			nm_settings_connection_update_timestamp (nm_active_connection_get_settings_connection (ac),
+			                                         (guint64) time (NULL), FALSE);
 		}
 	}
-
-	return TRUE;
+	return G_SOURCE_CONTINUE;
 }
 
 static void
@@ -6106,6 +6188,7 @@ constructed (GObject *object)
 	                  G_CALLBACK (connection_added_cb), self);
 	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,
 	                  G_CALLBACK (connection_updated_cb), self);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_FLAGS_CHANGED, G_CALLBACK (connection_flags_changed), self);
 
 	priv->hostname_manager = g_object_ref (nm_hostname_manager_get ());
 	g_signal_connect (priv->hostname_manager, "notify::" NM_HOSTNAME_MANAGER_HOSTNAME,
@@ -6163,6 +6246,8 @@ nm_manager_init (NMManager *self)
 	GFile *file;
 
 	c_list_init (&priv->link_cb_lst);
+	c_list_init (&priv->active_connections_lst_head);
+	c_list_init (&priv->delete_volatile_connection_lst_head);
 
 	priv->platform = g_object_ref (NM_PLATFORM_GET);
 
@@ -6250,7 +6335,10 @@ get_property (GObject *object, guint prop_id,
 	NMConfigData *config_data;
 	const NMGlobalDnsConfig *dns_config;
 	const char *type;
-	NMConnectivity *connectivity;
+	const char *path;
+	NMActiveConnection *ac;
+	GPtrArray *ptrarr;
+	gboolean vbool;
 
 	switch (prop_id) {
 	case PROP_VERSION:
@@ -6290,7 +6378,14 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_boolean (value, FALSE);
 		break;
 	case PROP_ACTIVE_CONNECTIONS:
-		nm_utils_g_value_set_object_path_array (value, priv->active_connections, NULL, NULL);
+		ptrarr = g_ptr_array_new ();
+		c_list_for_each_entry (ac, &priv->active_connections_lst_head, active_connections_lst) {
+			path = nm_exported_object_get_path (NM_EXPORTED_OBJECT (ac));
+			if (path)
+				g_ptr_array_add (ptrarr, g_strdup (path));
+		}
+		g_ptr_array_add (ptrarr, NULL);
+		g_value_take_boxed (value, g_ptr_array_free (ptrarr, FALSE));
 		break;
 	case PROP_CONNECTIVITY:
 		g_value_set_uint (value, priv->connectivity_state);
@@ -6300,8 +6395,12 @@ get_property (GObject *object, guint prop_id,
 		g_value_set_boolean (value, nm_config_data_get_connectivity_uri (config_data) != NULL);
 		break;
 	case PROP_CONNECTIVITY_CHECK_ENABLED:
-		connectivity = nm_connectivity_get ();
-		g_value_set_boolean (value, nm_connectivity_check_enabled (connectivity));
+#if WITH_CONCHECK
+		vbool = nm_connectivity_check_enabled (nm_connectivity_get ());
+#else
+		vbool = FALSE;
+#endif
+		g_value_set_boolean (value, FALSE);
 		break;
 	case PROP_PRIMARY_CONNECTION:
 		nm_utils_g_value_set_object_path (value, priv->primary_connection);
@@ -6400,6 +6499,12 @@ dispose (GObject *object)
 	NMManager *self = NM_MANAGER (object);
 	NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
 	CList *iter, *iter_safe;
+	NMActiveConnection *ac, *ac_safe;
+
+	nm_clear_g_source (&priv->delete_volatile_connection_idle_id);
+	_delete_volatile_connection_all (self, FALSE);
+	nm_assert (!priv->delete_volatile_connection_idle_id);
+	nm_assert (c_list_is_empty (&priv->delete_volatile_connection_lst_head));
 
 	g_signal_handlers_disconnect_by_func (priv->platform,
 	                                      G_CALLBACK (platform_link_cb),
@@ -6408,7 +6513,7 @@ dispose (GObject *object)
 		PlatformLinkCbData *data = c_list_entry (iter, PlatformLinkCbData, lst);
 
 		g_source_remove (data->idle_id);
-		c_list_unlink (iter);
+		c_list_unlink_stale (iter);
 		g_slice_free (PlatformLinkCbData, data);
 	}
 
@@ -6433,9 +6538,9 @@ dispose (GObject *object)
 
 	nm_clear_g_source (&priv->ac_cleanup_id);
 
-	while (priv->active_connections)
-		active_connection_remove (self, NM_ACTIVE_CONNECTION (priv->active_connections->data));
-	g_clear_pointer (&priv->active_connections, g_slist_free);
+	c_list_for_each_entry_safe (ac, ac_safe, &priv->active_connections_lst_head, active_connections_lst)
+		active_connection_remove (self, ac);
+	nm_assert (c_list_is_empty (&priv->active_connections_lst_head));
 	g_clear_object (&priv->primary_connection);
 	g_clear_object (&priv->activating_connection);
 
@@ -6455,6 +6560,7 @@ dispose (GObject *object)
 		g_signal_handlers_disconnect_by_func (priv->settings, system_unmanaged_devices_changed_cb, self);
 		g_signal_handlers_disconnect_by_func (priv->settings, connection_added_cb, self);
 		g_signal_handlers_disconnect_by_func (priv->settings, connection_updated_cb, self);
+		g_signal_handlers_disconnect_by_func (priv->settings, connection_flags_changed, self);
 		g_clear_object (&priv->settings);
 	}
 
diff --git a/src/nm-manager.h b/src/nm-manager.h
index 418c5ea9..2d463c71 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -24,6 +24,7 @@
 
 #include "nm-exported-object.h"
 #include "settings/nm-settings-connection.h"
+#include "nm-utils/c-list.h"
 
 #define NM_TYPE_MANAGER            (nm_manager_get_type ())
 #define NM_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_MANAGER, NMManager))
@@ -84,7 +85,19 @@ gboolean      nm_manager_start                         (NMManager *manager,
                                                         GError **error);
 void          nm_manager_stop                          (NMManager *manager);
 NMState       nm_manager_get_state                     (NMManager *manager);
-const GSList *nm_manager_get_active_connections        (NMManager *manager);
+const CList * nm_manager_get_active_connections        (NMManager *manager);
+
+#define nm_manager_for_each_active_connection(manager, iter, tmp_list) \
+	for (tmp_list = nm_manager_get_active_connections (manager), \
+	     iter = c_list_entry (tmp_list->next, NMActiveConnection, active_connections_lst); \
+	     ({ \
+	         gboolean _has_next = (&iter->active_connections_lst != tmp_list); \
+	         \
+	         if (!_has_next) \
+	             iter = NULL; \
+	         _has_next; \
+	    }); \
+	    iter = c_list_entry (iter->active_connections_lst.next, NMActiveConnection, active_connections_lst))
 
 NMSettingsConnection **nm_manager_get_activatable_connections (NMManager *manager,
                                                                guint *out_len,
diff --git a/src/nm-pacrunner-manager.c b/src/nm-pacrunner-manager.c
index 08e10e40..36f517a2 100644
--- a/src/nm-pacrunner-manager.c
+++ b/src/nm-pacrunner-manager.c
@@ -128,7 +128,7 @@ config_unref (Config *config)
 	if (config->refcount == 1) {
 		g_variant_unref (config->args);
 		g_free (config->path);
-		c_list_unlink (&config->lst);
+		c_list_unlink_stale (&config->lst);
 		g_slice_free (Config, config);
 	} else
 		config->refcount--;
@@ -524,7 +524,7 @@ nm_pacrunner_manager_remove (NMPacrunnerManager *self, NMPacrunnerCallId *call_i
 		}
 	}
 
-	c_list_unlink_init (&config->lst);
+	c_list_unlink (&config->lst);
 	config_unref (config);
 }
 
@@ -570,7 +570,7 @@ dispose (GObject *object)
 	CList *iter, *safe;
 
 	c_list_for_each_safe (iter, safe, &priv->configs) {
-		c_list_unlink_init (iter);
+		c_list_unlink (iter);
 		config_unref (c_list_entry (iter, Config, lst));
 	}
 
diff --git a/src/nm-policy.c b/src/nm-policy.c
index e56fce0b..8dfb0ab0 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -44,6 +44,7 @@
 #include "nm-manager.h"
 #include "settings/nm-settings.h"
 #include "settings/nm-settings-connection.h"
+#include "settings/nm-agent-manager.h"
 #include "nm-dhcp4-config.h"
 #include "nm-dhcp6-config.h"
 #include "nm-config.h"
@@ -65,7 +66,9 @@ typedef struct {
 	NMManager *manager;
 	NMNetns *netns;
 	NMFirewallManager *firewall_manager;
-	GSList *pending_activation_checks;
+	CList pending_activation_checks;
+
+	NMAgentManager *agent_mgr;
 
 	GHashTable *devices;
 	GHashTable *pending_active_connections;
@@ -312,8 +315,9 @@ device_ip6_prefix_delegated (NMDevice *device,
 	NMPolicyPrivate *priv = user_data;
 	NMPolicy *self = _PRIV_TO_SELF (priv);
 	IP6PrefixDelegation *delegation = NULL;
-	const GSList *connections, *iter;
 	guint i;
+	const CList *tmp_list;
+	NMActiveConnection *ac;
 
 	_LOGI (LOGD_IP6, "ipv6-pd: received a prefix %s/%d from %s",
 	       nm_utils_inet6_ntop (&prefix->address, NULL),
@@ -344,10 +348,10 @@ device_ip6_prefix_delegated (NMDevice *device,
 	 * so traversing it from the beginning makes it likely for newly
 	 * activated connections that have no subnet assigned to be served
 	 * first. That is a simple yet fair policy, which is good. */
-	connections = nm_manager_get_active_connections (priv->manager);
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMDevice *to_device = nm_active_connection_get_device (iter->data);
+	nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
+		NMDevice *to_device;
 
+		to_device = nm_active_connection_get_device (ac);
 		if (nm_device_needs_ip6_subnet (to_device))
 			ip6_subnet_from_delegation (delegation, to_device);
 	}
@@ -818,25 +822,25 @@ update_system_hostname (NMPolicy *self, const char *msg)
 
 static void
 update_default_ac (NMPolicy *self,
-                   NMActiveConnection *best,
-                   void (*set_active_func)(NMActiveConnection*, gboolean))
+                   int addr_family,
+                   NMActiveConnection *best)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	const GSList *connections, *iter;
+	const CList *tmp_list;
+	NMActiveConnection *ac;
 
 	/* Clear the 'default[6]' flag on all active connections that aren't the new
 	 * default active connection.  We'll set the new default after; this ensures
 	 * we don't ever have two marked 'default[6]' simultaneously.
 	 */
-	connections = nm_manager_get_active_connections (priv->manager);
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		if (NM_ACTIVE_CONNECTION (iter->data) != best)
-			set_active_func (NM_ACTIVE_CONNECTION (iter->data), FALSE);
+	nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
+		if (ac != best)
+			nm_active_connection_set_default (ac, addr_family, FALSE);
 	}
 
 	/* Mark new default active connection */
 	if (best)
-		set_active_func (best, TRUE);
+		nm_active_connection_set_default (best, addr_family, TRUE);
 }
 
 static gpointer
@@ -850,19 +854,19 @@ get_best_ip_config (NMPolicy *self,
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	NMDevice *device;
 	gpointer conf;
-	const GSList *iter;
+	const CList *tmp_list;
+	NMActiveConnection *ac;
 
 	nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6));
 
-	for (iter = nm_manager_get_active_connections (priv->manager); iter; iter = iter->next) {
-		NMActiveConnection *active = NM_ACTIVE_CONNECTION (iter->data);
+	nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
 		NMVpnConnection *candidate;
 		NMVpnConnectionState vpn_state;
 
-		if (!NM_IS_VPN_CONNECTION (active))
+		if (!NM_IS_VPN_CONNECTION (ac))
 			continue;
 
-		candidate = NM_VPN_CONNECTION (active);
+		candidate = NM_VPN_CONNECTION (ac);
 
 		vpn_state = nm_vpn_connection_get_vpn_state (candidate);
 		if (vpn_state != NM_VPN_CONNECTION_STATE_ACTIVATED)
@@ -887,7 +891,7 @@ get_best_ip_config (NMPolicy *self,
 		 * best metric. */
 		NM_SET_OUT (out_device, NULL);
 		NM_SET_OUT (out_vpn, candidate);
-		NM_SET_OUT (out_ac, active);
+		NM_SET_OUT (out_ac, ac);
 		NM_SET_OUT (out_ip_iface, nm_vpn_connection_get_ip_iface (candidate, TRUE));
 		return conf;
 	}
@@ -919,26 +923,6 @@ get_best_ip_config (NMPolicy *self,
 }
 
 static void
-update_ip4_dns (NMPolicy *self, NMDnsManager *dns_mgr)
-{
-	NMIP4Config *ip4_config;
-	const char *ip_iface = NULL;
-	NMVpnConnection *vpn = NULL;
-	NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
-
-	ip4_config = get_best_ip_config (self, AF_INET, &ip_iface, NULL, NULL, &vpn);
-	if (ip4_config) {
-		if (vpn)
-			dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
-
-		/* Tell the DNS manager this config is preferred by re-adding it with
-		 * a different IP config type.
-		 */
-		nm_dns_manager_add_ip4_config (dns_mgr, ip_iface, ip4_config, dns_type);
-	}
-}
-
-static void
 update_ip4_routing (NMPolicy *self, gboolean force_update)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
@@ -946,6 +930,8 @@ update_ip4_routing (NMPolicy *self, gboolean force_update)
 	NMVpnConnection *vpn = NULL;
 	NMActiveConnection *best_ac = NULL;
 	const char *ip_iface = NULL;
+	const CList *tmp_list;
+	NMActiveConnection *ac;
 
 	/* Note that we might have an IPv4 VPN tunneled over an IPv6-only device,
 	 * so we can get (vpn != NULL && best == NULL).
@@ -965,23 +951,18 @@ update_ip4_routing (NMPolicy *self, gboolean force_update)
 		return;
 
 	if (best) {
-		const GSList *connections, *iter;
-
-		connections = nm_manager_get_active_connections (priv->manager);
-		for (iter = connections; iter; iter = g_slist_next (iter)) {
-			NMActiveConnection *active = iter->data;
-
-			if (   NM_IS_VPN_CONNECTION (active)
-			    && nm_vpn_connection_get_ip4_config (NM_VPN_CONNECTION (active))
-			    && !nm_active_connection_get_device (active))
-				nm_active_connection_set_device (active, best);
+		nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
+			if (   NM_IS_VPN_CONNECTION (ac)
+			    && nm_vpn_connection_get_ip4_config (NM_VPN_CONNECTION (ac))
+			    && !nm_active_connection_get_device (ac))
+				nm_active_connection_set_device (ac, best);
 		}
 	}
 
 	if (vpn)
 		best = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
 
-	update_default_ac (self, best_ac, nm_active_connection_set_default);
+	update_default_ac (self, AF_INET, best_ac);
 
 	if (!nm_g_object_ref_set (&priv->default_device4, best))
 		return;
@@ -997,50 +978,28 @@ static void
 update_ip6_dns_delegation (NMPolicy *self)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	const GSList *connections, *iter;
-
-	connections = nm_manager_get_active_connections (priv->manager);
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMDevice *device = nm_active_connection_get_device (iter->data);
+	NMDevice *device;
+	NMActiveConnection *ac;
+	const CList *tmp_list;
 
+	nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
+		device = nm_active_connection_get_device (ac);
 		if (device && nm_device_needs_ip6_subnet (device))
 			nm_device_copy_ip6_dns_config (device, priv->default_device6);
 	}
 }
 
 static void
-update_ip6_dns (NMPolicy *self, NMDnsManager *dns_mgr)
-{
-	NMIP6Config *ip6_config;
-	const char *ip_iface = NULL;
-	NMVpnConnection *vpn = NULL;
-	NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
-
-	ip6_config = get_best_ip_config (self, AF_INET6, &ip_iface, NULL, NULL, &vpn);
-	if (ip6_config) {
-		if (vpn)
-			dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
-
-		/* Tell the DNS manager this config is preferred by re-adding it with
-		 * a different IP config type.
-		 */
-		nm_dns_manager_add_ip6_config (dns_mgr, ip_iface, ip6_config, dns_type);
-	}
-
-	update_ip6_dns_delegation (self);
-}
-
-static void
 update_ip6_prefix_delegation (NMPolicy *self)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	const GSList *connections, *iter;
+	NMDevice *device;
+	NMActiveConnection *ac;
+	const CList *tmp_list;
 
 	/* There's new default IPv6 connection, try to get a prefix for everyone. */
-	connections = nm_manager_get_active_connections (priv->manager);
-	for (iter = connections; iter; iter = g_slist_next (iter)) {
-		NMDevice *device = nm_active_connection_get_device (iter->data);
-
+	nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
+		device = nm_active_connection_get_device (ac);
 		if (device && nm_device_needs_ip6_subnet (device))
 			ip6_subnet_from_device (self, priv->default_device6, device);
 	}
@@ -1054,6 +1013,8 @@ update_ip6_routing (NMPolicy *self, gboolean force_update)
 	NMVpnConnection *vpn = NULL;
 	NMActiveConnection *best_ac = NULL;
 	const char *ip_iface = NULL;
+	NMActiveConnection *ac;
+	const CList *tmp_list;
 
 	/* Note that we might have an IPv6 VPN tunneled over an IPv4-only device,
 	 * so we can get (vpn != NULL && best == NULL).
@@ -1073,23 +1034,18 @@ update_ip6_routing (NMPolicy *self, gboolean force_update)
 		return;
 
 	if (best) {
-		const GSList *connections, *iter;
-
-		connections = nm_manager_get_active_connections (priv->manager);
-		for (iter = connections; iter; iter = g_slist_next (iter)) {
-			NMActiveConnection *active = iter->data;
-
-			if (   NM_IS_VPN_CONNECTION (active)
-			    && nm_vpn_connection_get_ip6_config (NM_VPN_CONNECTION (active))
-			    && !nm_active_connection_get_device (active))
-				nm_active_connection_set_device (active, best);
+		nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
+			if (   NM_IS_VPN_CONNECTION (ac)
+			    && nm_vpn_connection_get_ip6_config (NM_VPN_CONNECTION (ac))
+			    && !nm_active_connection_get_device (ac))
+				nm_active_connection_set_device (ac, best);
 		}
 	}
 
 	if (vpn)
 		best = nm_active_connection_get_device (NM_ACTIVE_CONNECTION (vpn));
 
-	update_default_ac (self, best_ac, nm_active_connection_set_default6);
+	update_default_ac (self, AF_INET6, best_ac);
 
 	if (!nm_g_object_ref_set (&priv->default_device6, best))
 		return;
@@ -1104,14 +1060,42 @@ update_ip6_routing (NMPolicy *self, gboolean force_update)
 }
 
 static void
+update_ip_dns (NMPolicy *self, int addr_family)
+{
+	gpointer ip_config;
+	const char *ip_iface = NULL;
+	NMVpnConnection *vpn = NULL;
+	NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
+
+	nm_assert_addr_family (addr_family);
+
+	ip_config = get_best_ip_config (self, addr_family, &ip_iface, NULL, NULL, &vpn);
+	if (ip_config) {
+		if (vpn)
+			dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
+
+		/* Tell the DNS manager this config is preferred by re-adding it with
+		 * a different IP config type.
+		 */
+		nm_dns_manager_add_ip_config (NM_POLICY_GET_PRIVATE (self)->dns_manager,
+		                              ip_iface,
+		                              ip_config,
+		                              dns_type);
+	}
+
+	if (addr_family == AF_INET6)
+		update_ip6_dns_delegation (self);
+}
+
+static void
 update_routing_and_dns (NMPolicy *self, gboolean force_update)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 
 	nm_dns_manager_begin_updates (priv->dns_manager, __func__);
 
-	update_ip4_dns (self, priv->dns_manager);
-	update_ip6_dns (self, priv->dns_manager);
+	update_ip_dns (self, AF_INET);
+	update_ip_dns (self, AF_INET6);
 
 	update_ip4_routing (self, force_update);
 	update_ip6_routing (self, force_update);
@@ -1146,6 +1130,7 @@ check_activating_devices (NMPolicy *self)
 }
 
 typedef struct {
+	CList pending_lst;
 	NMPolicy *policy;
 	NMDevice *device;
 	guint autoactivate_id;
@@ -1154,14 +1139,10 @@ typedef struct {
 static void
 activate_data_free (ActivateData *data)
 {
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (data->policy);
-
 	nm_device_remove_pending_action (data->device, NM_PENDING_ACTION_AUTOACTIVATE, TRUE);
-	priv->pending_activation_checks = g_slist_remove (priv->pending_activation_checks, data);
-
+	c_list_unlink_stale (&data->pending_lst);
 	nm_clear_g_source (&data->autoactivate_id);
 	g_object_unref (data->device);
-
 	g_slice_free (ActivateData, data);
 }
 
@@ -1192,7 +1173,7 @@ pending_ac_state_changed (NMActiveConnection *ac, guint state, guint reason, NMP
 		 * loop.
 		 */
 		con = nm_active_connection_get_settings_connection (ac);
-		nm_settings_connection_autoconnect_blocked_reason_set (con, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED);
+		nm_settings_connection_autoconnect_blocked_reason_set (con, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED, TRUE);
 		schedule_activate_check (self, nm_active_connection_get_device (ac));
 
 		/* Cleanup */
@@ -1213,7 +1194,7 @@ auto_activate_device (NMPolicy *self,
 	gs_free char *specific_object = NULL;
 	gs_free NMSettingsConnection **connections = NULL;
 	guint i, len;
-	GError *error = NULL;
+	gs_free_error GError *error = NULL;
 	NMAuthSubject *subject;
 	NMActiveConnection *ac;
 
@@ -1243,9 +1224,7 @@ auto_activate_device (NMPolicy *self,
 		NMSettingConnection *s_con;
 		const char *permission;
 
-		if (   !nm_settings_connection_is_visible (candidate)
-		    || nm_settings_connection_autoconnect_retries_get (candidate) == 0
-		    || nm_settings_connection_autoconnect_blocked_reason_get (candidate) != NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE)
+		if (nm_settings_connection_autoconnect_is_blocked (candidate))
 			continue;
 
 		s_con = nm_connection_get_setting_connection (NM_CONNECTION (candidate));
@@ -1278,13 +1257,12 @@ auto_activate_device (NMPolicy *self,
 	                                     NM_ACTIVATION_TYPE_MANAGED,
 	                                     &error);
 	if (!ac) {
-		_LOGI (LOGD_DEVICE, "connection '%s' auto-activation failed: (%d) %s",
+		_LOGI (LOGD_DEVICE, "connection '%s' auto-activation failed: %s",
 		       nm_settings_connection_get_id (best_connection),
-		       error->code,
 		       error->message);
-		g_error_free (error);
 		nm_settings_connection_autoconnect_blocked_reason_set (best_connection,
-		                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED);
+		                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED,
+		                                                       TRUE);
 		schedule_activate_check (self, device);
 		return;
 	}
@@ -1318,13 +1296,14 @@ auto_activate_device_cb (gpointer user_data)
 }
 
 static ActivateData *
-find_pending_activation (GSList *list, NMDevice *device)
+find_pending_activation (NMPolicy *self, NMDevice *device)
 {
-	GSList *iter;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
+	ActivateData *data;
 
-	for (iter = list; iter; iter = g_slist_next (iter)) {
-		if (((ActivateData *) iter->data)->device == device)
-			return iter->data;
+	c_list_for_each_entry (data, &priv->pending_activation_checks, pending_lst) {
+		if (data->device == device)
+			return data;
 	}
 	return NULL;
 }
@@ -1422,48 +1401,56 @@ hostname_changed (NMHostnameManager *hostname_manager, GParamSpec *pspec, gpoint
 	update_system_hostname (self, "hostname changed");
 }
 
-static void
-reset_autoconnect_all (NMPolicy *self, NMDevice *device)
+static gboolean
+reset_autoconnect_all (NMPolicy *self,
+                       NMDevice *device, /* if present, only reset connections compatible with @device */
+                       gboolean only_no_secrets)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	gs_free NMSettingsConnection **connections = NULL;
+	NMSettingsConnection *const*connections = NULL;
 	guint i;
+	gboolean changed;
 
-	if (device) {
-		_LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections on %s",
-		       nm_device_get_iface (device));
-	} else
-		_LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections");
+	_LOGD (LOGD_DEVICE, "re-enabling autoconnect for all connections%s%s%s",
+	       device ? " on " : "",
+	       device ? nm_device_get_iface (device) : "",
+	       only_no_secrets ? " (only clear no-secrets flag)" : "");
 
-	connections = nm_settings_get_connections_sorted (priv->settings, NULL);
+	connections = nm_settings_get_connections (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_autoconnect_retries_reset (connection);
-			nm_settings_connection_autoconnect_blocked_reason_set (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE);
-		}
-	}
-}
-
-static void
-reset_autoconnect_for_failed_secrets (NMPolicy *self)
-{
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	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, NULL);
-	for (i = 0; connections[i]; i++) {
-		NMSettingsConnection *connection = connections[i];
+		if (   device
+		    && !nm_device_check_connection_compatible (device, NM_CONNECTION (connection)))
+			continue;
 
-		if (nm_settings_connection_autoconnect_blocked_reason_get (connection) == NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS) {
+		if (only_no_secrets) {
+			/* we only reset the no-secrets blocked flag. */
+			if (nm_settings_connection_autoconnect_blocked_reason_set (connection,
+			                                                           NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS,
+			                                                           FALSE)) {
+				/* maybe the connection is still blocked afterwards for other reasons
+				 * and in the larger picture nothing changed. But it's too complicated
+				 * to find out exactly. Just assume, something changed to be sure. */
+				if (!nm_settings_connection_autoconnect_is_blocked (connection))
+					changed = TRUE;
+			}
+		} else {
+			/* we reset the tries-count and any blocked-reason */
+			if (nm_settings_connection_autoconnect_retries_get (connection) == 0)
+				changed = TRUE;
 			nm_settings_connection_autoconnect_retries_reset (connection);
-			nm_settings_connection_autoconnect_blocked_reason_set (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE);
+
+			if (nm_settings_connection_autoconnect_blocked_reason_set (connection,
+			                                                             NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_ALL
+			                                                           & ~NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
+			                                                           FALSE)) {
+				if (!nm_settings_connection_autoconnect_is_blocked (connection))
+					changed = TRUE;
+			}
 		}
 	}
+	return changed;
 }
 
 static void
@@ -1478,7 +1465,7 @@ sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
 
 	/* Reset retries on all connections so they'll checked on wakeup */
 	if (sleeping || !enabled)
-		reset_autoconnect_all (self, NULL);
+		reset_autoconnect_all (self, NULL, FALSE);
 }
 
 static void
@@ -1486,7 +1473,8 @@ schedule_activate_check (NMPolicy *self, NMDevice *device)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	ActivateData *data;
-	const GSList *active_connections, *iter;
+	NMActiveConnection *ac;
+	const CList *tmp_list;
 
 	if (nm_manager_get_state (priv->manager) == NM_STATE_ASLEEP)
 		return;
@@ -1494,12 +1482,11 @@ schedule_activate_check (NMPolicy *self, NMDevice *device)
 	if (!nm_device_autoconnect_allowed (device))
 		return;
 
-	if (find_pending_activation (priv->pending_activation_checks, device))
+	if (find_pending_activation (self, device))
 		return;
 
-	active_connections = nm_manager_get_active_connections (priv->manager);
-	for (iter = active_connections; iter; iter = iter->next) {
-		if (nm_active_connection_get_device (NM_ACTIVE_CONNECTION (iter->data)) == device)
+	nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
+		if (nm_active_connection_get_device (ac) == device)
 			return;
 	}
 
@@ -1509,18 +1496,7 @@ schedule_activate_check (NMPolicy *self, NMDevice *device)
 	data->policy = self;
 	data->device = g_object_ref (device);
 	data->autoactivate_id = g_idle_add (auto_activate_device_cb, data);
-	priv->pending_activation_checks = g_slist_append (priv->pending_activation_checks, data);
-}
-
-static void
-clear_pending_activate_check (NMPolicy *self, NMDevice *device)
-{
-	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	ActivateData *data;
-
-	data = find_pending_activation (priv->pending_activation_checks, device);
-	if (data && data->autoactivate_id)
-		activate_data_free (data);
+	c_list_link_tail (&priv->pending_activation_checks, &data->pending_lst);
 }
 
 static gboolean
@@ -1528,7 +1504,7 @@ reset_connections_retries (gpointer user_data)
 {
 	NMPolicy *self = (NMPolicy *) user_data;
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	gs_free NMSettingsConnection **connections = NULL;
+	NMSettingsConnection *const*connections = NULL;
 	guint i;
 	gint32 con_stamp, min_stamp, now;
 	gboolean changed = FALSE;
@@ -1537,11 +1513,11 @@ reset_connections_retries (gpointer user_data)
 
 	min_stamp = 0;
 	now = nm_utils_get_monotonic_timestamp_s ();
-	connections = nm_settings_get_connections_sorted (priv->settings, NULL);
+	connections = nm_settings_get_connections (priv->settings, NULL);
 	for (i = 0; connections[i]; i++) {
 		NMSettingsConnection *connection = connections[i];
 
-		con_stamp = nm_settings_connection_autoconnect_blocked_until_get (connection);
+		con_stamp = nm_settings_connection_autoconnect_retries_blocked_until (connection);
 		if (con_stamp == 0)
 			continue;
 
@@ -1564,6 +1540,29 @@ reset_connections_retries (gpointer user_data)
 }
 
 static void
+_connection_autoconnect_retries_set (NMPolicy *self,
+                                     NMSettingsConnection *connection,
+                                     int tries)
+{
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
+
+	nm_assert (NM_IS_SETTINGS_CONNECTION (connection));
+	nm_assert (tries >= 0);
+
+	nm_settings_connection_autoconnect_retries_set (connection, tries);
+
+	if (tries == 0) {
+		/* Schedule a handler to reset retries count */
+		if (!priv->reset_retries_id) {
+			gint32 retry_time = nm_settings_connection_autoconnect_retries_blocked_until (connection);
+
+			g_warn_if_fail (retry_time != 0);
+			priv->reset_retries_id = g_timeout_add_seconds (MAX (0, retry_time - nm_utils_get_monotonic_timestamp_s ()), reset_connections_retries, self);
+		}
+	}
+}
+
+static void
 activate_slave_connections (NMPolicy *self, NMDevice *device)
 {
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
@@ -1571,7 +1570,8 @@ activate_slave_connections (NMPolicy *self, NMDevice *device)
 	guint i;
 	NMActRequest *req;
 	gboolean internal_activation = FALSE;
-	gs_free NMSettingsConnection **connections = NULL;
+	NMSettingsConnection *const*connections;
+	gboolean changed;
 
 	master_device = nm_device_get_iface (device);
 	g_assert (master_device);
@@ -1595,38 +1595,37 @@ activate_slave_connections (NMPolicy *self, NMDevice *device)
 		internal_activation = subject && nm_auth_subject_is_internal (subject);
 	}
 
-	connections = nm_settings_get_connections_sorted (priv->settings, NULL);
+	changed = FALSE;
+	connections = nm_settings_get_connections (priv->settings, NULL);
 	for (i = 0; connections[i]; i++) {
-		NMConnection *slave;
+		NMSettingsConnection *connection = connections[i];
 		NMSettingConnection *s_slave_con;
 		const char *slave_master;
 
-		slave = NM_CONNECTION (connections[i]);
-
-		s_slave_con = nm_connection_get_setting_connection (slave);
-		g_assert (s_slave_con);
+		s_slave_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
 		slave_master = nm_setting_connection_get_master (s_slave_con);
 		if (!slave_master)
 			continue;
+		if (!NM_IN_STRSET (slave_master, master_device,
+		                                 master_uuid_applied,
+		                                 master_uuid_settings))
+			continue;
 
-		if (   nm_streq0 (slave_master, master_device)
-		    || nm_streq0 (slave_master, master_uuid_applied)
-		    || nm_streq0 (slave_master, master_uuid_settings)) {
-			NMSettingsConnection *settings = NM_SETTINGS_CONNECTION (slave);
-			NMSettingsAutoconnectBlockedReason reason;
-
-			if (!internal_activation)
-				nm_settings_connection_autoconnect_retries_reset (settings);
-
-			reason = nm_settings_connection_autoconnect_blocked_reason_get (settings);
-			if (reason == NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED) {
-				reason = NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE;
-				nm_settings_connection_autoconnect_blocked_reason_set (settings, reason);
-			}
+		if (!internal_activation) {
+			if (nm_settings_connection_autoconnect_retries_get (connection) == 0)
+				changed = TRUE;
+			nm_settings_connection_autoconnect_retries_reset (connection);
+		}
+		if (nm_settings_connection_autoconnect_blocked_reason_set (connection,
+		                                                           NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED,
+		                                                           FALSE)) {
+			if (!nm_settings_connection_autoconnect_is_blocked (connection))
+				changed = TRUE;
 		}
 	}
 
-	schedule_activate_all (self);
+	if (changed)
+		schedule_activate_all (self);
 }
 
 static gboolean
@@ -1711,9 +1710,7 @@ device_state_changed (NMDevice *device,
 	NMPolicyPrivate *priv = user_data;
 	NMPolicy *self = _PRIV_TO_SELF (priv);
 	NMActiveConnection *ac;
-
 	NMSettingsConnection *connection = nm_device_get_settings_connection (device);
-
 	const char *ip_iface = nm_device_get_ip_iface (device);
 	NMIP4Config *ip4_config;
 	NMIP6Config *ip6_config;
@@ -1734,7 +1731,8 @@ device_state_changed (NMDevice *device,
 		 */
 		if (connection) {
 			nm_settings_connection_autoconnect_blocked_reason_set (connection,
-			                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED);
+			                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED,
+			                                                       TRUE);
 		}
 		break;
 	default:
@@ -1749,36 +1747,40 @@ device_state_changed (NMDevice *device,
 		if (   connection
 		    && old_state >= NM_DEVICE_STATE_PREPARE
 		    && old_state <= NM_DEVICE_STATE_ACTIVATED) {
+			gboolean block_no_secrets = FALSE;
 			int tries;
+			guint64 con_v;
 
-			tries = nm_settings_connection_autoconnect_retries_get (connection);
 			if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_NO_SECRETS) {
+				/* we want to block the connection from auto-connect if it failed due to no-secrets.
+				 * However, if a secret-agent registered, since the connection made the last
+				 * secret-request, we do not block it. The new secret-agent might not yet
+				 * been consulted, and it may be able to provide the secrets.
+				 *
+				 * We detect this by using a version-id of the agent-manager, which increments
+				 * whenever new agents register. */
+				con_v = nm_settings_connection_get_last_secret_agent_version_id (connection);
+				if (   con_v == 0
+				    || con_v != nm_agent_manager_get_agent_version_id (priv->agent_mgr))
+					block_no_secrets = TRUE;
+			}
+
+			if (block_no_secrets) {
 				_LOGD (LOGD_DEVICE, "connection '%s' now blocked from autoconnect due to no secrets",
 				       nm_settings_connection_get_id (connection));
-
-				nm_settings_connection_autoconnect_blocked_reason_set (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS);
-			} else if (tries != 0) {
+				nm_settings_connection_autoconnect_blocked_reason_set (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS, TRUE);
+			} else {
+				tries = nm_settings_connection_autoconnect_retries_get (connection);
 				if (tries > 0) {
 					_LOGD (LOGD_DEVICE, "connection '%s' failed to autoconnect; %d tries left",
-					       nm_settings_connection_get_id (connection), tries);
-					nm_settings_connection_autoconnect_retries_set (connection, --tries);
-				} else {
+					       nm_settings_connection_get_id (connection), tries - 1);
+					_connection_autoconnect_retries_set (self, connection, tries - 1);
+				} else if (tries != 0) {
 					_LOGD (LOGD_DEVICE, "connection '%s' failed to autoconnect; infinite tries left",
 					       nm_settings_connection_get_id (connection));
 				}
 			}
 
-			if (nm_settings_connection_autoconnect_retries_get (connection) == 0) {
-				_LOGI (LOGD_DEVICE, "disabling autoconnect for connection '%s'.",
-				       nm_settings_connection_get_id (connection));
-				/* Schedule a handler to reset retries count */
-				if (!priv->reset_retries_id) {
-					gint32 retry_time = nm_settings_connection_autoconnect_blocked_until_get (connection);
-
-					g_warn_if_fail (retry_time != 0);
-					priv->reset_retries_id = g_timeout_add_seconds (MAX (0, retry_time - nm_utils_get_monotonic_timestamp_s ()), reset_connections_retries, self);
-				}
-			}
 			nm_connection_clear_secrets (NM_CONNECTION (connection));
 		}
 		break;
@@ -1800,10 +1802,10 @@ device_state_changed (NMDevice *device,
 
 		ip4_config = nm_device_get_ip4_config (device);
 		if (ip4_config)
-			nm_dns_manager_add_ip4_config (priv->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
+			nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 		ip6_config = nm_device_get_ip6_config (device);
 		if (ip6_config)
-			nm_dns_manager_add_ip6_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
+			nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 
 		update_routing_and_dns (self, FALSE);
 
@@ -1815,13 +1817,25 @@ device_state_changed (NMDevice *device,
 			update_routing_and_dns (self, FALSE);
 		break;
 	case NM_DEVICE_STATE_DEACTIVATING:
-		if (nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_USER_REQUESTED) {
-			if (connection) {
-				/* The connection was deactivated, so block just this connection */
-				_LOGD (LOGD_DEVICE, "blocking autoconnect of connection '%s' by user request",
-				       nm_settings_connection_get_id (connection));
-				nm_settings_connection_autoconnect_blocked_reason_set (connection,
-				                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST);
+		if (connection) {
+			NMSettingsAutoconnectBlockedReason blocked_reason = NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE;
+
+			switch (nm_device_state_reason_check (reason)) {
+			case NM_DEVICE_STATE_REASON_USER_REQUESTED:
+				 blocked_reason = NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST;
+				break;
+			case NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED:
+				blocked_reason = NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED;
+				break;
+			default:
+				break;
+			}
+			if (blocked_reason != NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE) {
+				_LOGD (LOGD_DEVICE, "blocking autoconnect of connection '%s': %s",
+				       nm_settings_connection_get_id (connection),
+				       NM_UTILS_LOOKUP_STR (nm_device_state_reason_to_str,
+				                            nm_device_state_reason_check (reason)));
+				nm_settings_connection_autoconnect_blocked_reason_set (connection, blocked_reason, TRUE);
 			}
 		}
 		ip6_remove_device_prefix_delegations (self, device);
@@ -1832,7 +1846,7 @@ device_state_changed (NMDevice *device,
 		 */
 		if (   nm_device_state_reason_check (reason) == NM_DEVICE_STATE_REASON_CARRIER
 		    && old_state == NM_DEVICE_STATE_UNAVAILABLE)
-			reset_autoconnect_all (self, device);
+			reset_autoconnect_all (self, device, FALSE);
 
 		if (old_state > NM_DEVICE_STATE_DISCONNECTED)
 			update_routing_and_dns (self, FALSE);
@@ -1858,7 +1872,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_autoconnect_blocked_reason_set (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE);
+			nm_settings_connection_autoconnect_blocked_reason_set (connection, NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_ALL, FALSE);
 		break;
 	case NM_DEVICE_STATE_SECONDARIES:
 		if (connection)
@@ -1903,17 +1917,17 @@ device_ip4_config_changed (NMDevice *device,
 	if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) {
 		if (old_config != new_config) {
 			if (old_config)
-				nm_dns_manager_remove_ip4_config (priv->dns_manager, old_config);
+				nm_dns_manager_remove_ip_config (priv->dns_manager, old_config);
 			if (new_config)
-				nm_dns_manager_add_ip4_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
+				nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 		}
-		update_ip4_dns (self, priv->dns_manager);
+		update_ip_dns (self, AF_INET);
 		update_ip4_routing (self, TRUE);
 		update_system_hostname (self, "ip4 conf");
 	} else {
 		/* Old configs get removed immediately */
 		if (old_config)
-			nm_dns_manager_remove_ip4_config (priv->dns_manager, old_config);
+			nm_dns_manager_remove_ip_config (priv->dns_manager, old_config);
 	}
 
 	nm_dns_manager_end_updates (priv->dns_manager, __func__);
@@ -1939,17 +1953,17 @@ device_ip6_config_changed (NMDevice *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);
+				nm_dns_manager_remove_ip_config (priv->dns_manager, old_config);
 			if (new_config)
-				nm_dns_manager_add_ip6_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
+				nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
 		}
-		update_ip6_dns (self, priv->dns_manager);
+		update_ip_dns (self, AF_INET6);
 		update_ip6_routing (self, TRUE);
 		update_system_hostname (self, "ip6 conf");
 	} else {
 		/* Old configs get removed immediately */
 		if (old_config)
-			nm_dns_manager_remove_ip6_config (priv->dns_manager, old_config);
+			nm_dns_manager_remove_ip_config (priv->dns_manager, old_config);
 	}
 
 	nm_dns_manager_end_updates (priv->dns_manager, __func__);
@@ -2021,13 +2035,16 @@ device_removed (NMManager *manager, NMDevice *device, gpointer user_data)
 {
 	NMPolicyPrivate *priv = user_data;
 	NMPolicy *self = _PRIV_TO_SELF (priv);
+	ActivateData *data;
 
 	/* XXX is this needed? The delegations are cleaned up
 	 * on transition to deactivated too. */
 	ip6_remove_device_prefix_delegations (self, device);
 
 	/* Clear any idle callbacks for this device */
-	clear_pending_activate_check (self, device);
+	data = find_pending_activation (self, device);
+	if (data && data->autoactivate_id)
+		activate_data_free (data);
 
 	if (g_hash_table_remove (priv->devices, device))
 		devices_list_unregister (self, device);
@@ -2055,11 +2072,11 @@ vpn_connection_activated (NMPolicy *self, NMVpnConnection *vpn)
 
 	ip4_config = nm_vpn_connection_get_ip4_config (vpn);
 	if (ip4_config)
-		nm_dns_manager_add_ip4_config (priv->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_VPN);
+		nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, ip4_config, NM_DNS_IP_CONFIG_TYPE_VPN);
 
 	ip6_config = nm_vpn_connection_get_ip6_config (vpn);
 	if (ip6_config)
-		nm_dns_manager_add_ip6_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_VPN);
+		nm_dns_manager_add_ip_config (priv->dns_manager, ip_iface, ip6_config, NM_DNS_IP_CONFIG_TYPE_VPN);
 
 	update_routing_and_dns (self, TRUE);
 
@@ -2078,13 +2095,13 @@ vpn_connection_deactivated (NMPolicy *self, NMVpnConnection *vpn)
 	ip4_config = nm_vpn_connection_get_ip4_config (vpn);
 	if (ip4_config) {
 		/* Remove the VPN connection's IP4 config from DNS */
-		nm_dns_manager_remove_ip4_config (priv->dns_manager, ip4_config);
+		nm_dns_manager_remove_ip_config (priv->dns_manager, ip4_config);
 	}
 
 	ip6_config = nm_vpn_connection_get_ip6_config (vpn);
 	if (ip6_config) {
 		/* Remove the VPN connection's IP6 config from DNS */
-		nm_dns_manager_remove_ip6_config (priv->dns_manager, ip6_config);
+		nm_dns_manager_remove_ip_config (priv->dns_manager, ip6_config);
 	}
 
 	update_routing_and_dns (self, TRUE);
@@ -2317,19 +2334,21 @@ connection_updated (NMSettings *settings,
 }
 
 static void
-_deactivate_if_active (NMManager *manager, NMSettingsConnection *connection)
+_deactivate_if_active (NMPolicy *self, NMSettingsConnection *connection)
 {
-	const GSList *active, *iter;
+	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
+	NMActiveConnection *ac;
+	const CList *tmp_list;
+	GError *error = NULL;
+
+	nm_assert (NM_IS_SETTINGS_CONNECTION (connection));
 
-	active = nm_manager_get_active_connections (manager);
-	for (iter = active; iter; iter = g_slist_next (iter)) {
-		NMActiveConnection *ac = iter->data;
+	nm_manager_for_each_active_connection (priv->manager, ac, tmp_list) {
 		NMActiveConnectionState state = nm_active_connection_get_state (ac);
-		GError *error = NULL;
 
-		if (nm_active_connection_get_settings_connection (ac) == connection &&
-		    (state <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) {
-			if (!nm_manager_deactivate_connection (manager,
+		if (   nm_active_connection_get_settings_connection (ac) == connection
+		    && (state <= NM_ACTIVE_CONNECTION_STATE_ACTIVATED)) {
+			if (!nm_manager_deactivate_connection (priv->manager,
 			                                       ac,
 			                                       NM_DEVICE_STATE_REASON_CONNECTION_REMOVED,
 			                                       &error)) {
@@ -2349,22 +2368,25 @@ connection_removed (NMSettings *settings,
                     gpointer user_data)
 {
 	NMPolicyPrivate *priv = user_data;
+	NMPolicy *self = _PRIV_TO_SELF (priv);
 
-	_deactivate_if_active (priv->manager, connection);
+	_deactivate_if_active (self, connection);
 }
 
 static void
-connection_visibility_changed (NMSettings *settings,
-                               NMSettingsConnection *connection,
-                               gpointer user_data)
+connection_flags_changed (NMSettings *settings,
+                          NMSettingsConnection *connection,
+                          gpointer user_data)
 {
 	NMPolicyPrivate *priv = user_data;
 	NMPolicy *self = _PRIV_TO_SELF (priv);
 
-	if (nm_settings_connection_is_visible (connection))
-		schedule_activate_all (self);
-	else
-		_deactivate_if_active (priv->manager, connection);
+	if (NM_FLAGS_HAS (nm_settings_connection_get_flags (connection),
+	                  NM_SETTINGS_CONNECTION_FLAGS_VISIBLE)) {
+		if (!nm_settings_connection_autoconnect_is_blocked (connection))
+			schedule_activate_all (self);
+	} else
+		_deactivate_if_active (self, connection);
 }
 
 static void
@@ -2372,15 +2394,14 @@ secret_agent_registered (NMSettings *settings,
                          NMSecretAgent *agent,
                          gpointer user_data)
 {
-	NMPolicyPrivate *priv = user_data;
-	NMPolicy *self = _PRIV_TO_SELF (priv);
+	NMPolicy *self = NM_POLICY (user_data);
 
 	/* The registered secret agent may provide some missing secrets. Thus we
 	 * reset retries count here and schedule activation, so that the
 	 * connections failed due to missing secrets may re-try auto-connection.
 	 */
-	reset_autoconnect_for_failed_secrets (self);
-	schedule_activate_all (self);
+	if (reset_autoconnect_all (self, NULL, TRUE))
+		schedule_activate_all (self);
 }
 
 NMDevice *
@@ -2476,6 +2497,8 @@ nm_policy_init (NMPolicy *self)
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
 	const char *hostname_mode;
 
+	c_list_init (&priv->pending_activation_checks);
+
 	priv->netns = g_object_ref (nm_netns_get ());
 
 	priv->hostname_manager = g_object_ref (nm_hostname_manager_get ());
@@ -2516,6 +2539,8 @@ constructed (GObject *object)
 	_LOGT (LOGD_DNS, "hostname-original: set to %s%s%s",
 	       NM_PRINT_FMT_QUOTE_STRING (priv->orig_hostname));
 
+	priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
+
 	priv->firewall_manager = g_object_ref (nm_firewall_manager_get ());
 	g_signal_connect (priv->firewall_manager, NM_FIREWALL_MANAGER_STATE_CHANGED,
 	                  G_CALLBACK (firewall_state_changed), self);
@@ -2536,11 +2561,12 @@ constructed (GObject *object)
 	g_signal_connect (priv->manager, NM_MANAGER_ACTIVE_CONNECTION_ADDED,       (GCallback) active_connection_added, priv);
 	g_signal_connect (priv->manager, NM_MANAGER_ACTIVE_CONNECTION_REMOVED,     (GCallback) active_connection_removed, priv);
 
-	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED,              (GCallback) connection_added, priv);
-	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,            (GCallback) connection_updated, priv);
-	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,            (GCallback) connection_removed, priv);
-	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED, (GCallback) connection_visibility_changed, priv);
-	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_AGENT_REGISTERED,              (GCallback) secret_agent_registered, priv);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_ADDED,         (GCallback) connection_added, priv);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_UPDATED,       (GCallback) connection_updated, priv);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED,       (GCallback) connection_removed, priv);
+	g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_FLAGS_CHANGED, (GCallback) connection_flags_changed, priv);
+
+	g_signal_connect (priv->agent_mgr, NM_AGENT_MANAGER_AGENT_REGISTERED, G_CALLBACK (secret_agent_registered), self);
 
 	G_OBJECT_CLASS (nm_policy_parent_class)->constructed (object);
 
@@ -2564,9 +2590,9 @@ dispose (GObject *object)
 {
 	NMPolicy *self = NM_POLICY (object);
 	NMPolicyPrivate *priv = NM_POLICY_GET_PRIVATE (self);
-	const GSList *connections;
 	GHashTableIter h_iter;
 	NMDevice *device;
+	ActivateData *data, *data_safe;
 
 	nm_clear_g_cancellable (&priv->lookup.cancellable);
 	g_clear_object (&priv->lookup.addr);
@@ -2578,8 +2604,8 @@ dispose (GObject *object)
 	nm_clear_g_object (&priv->activating_device6);
 	g_clear_pointer (&priv->pending_active_connections, g_hash_table_unref);
 
-	while (priv->pending_activation_checks)
-		activate_data_free (priv->pending_activation_checks->data);
+	c_list_for_each_entry_safe (data, data_safe, &priv->pending_activation_checks, pending_lst)
+		activate_data_free (data);
 
 	g_slist_free_full (priv->pending_secondaries, (GDestroyNotify) pending_secondary_data_free);
 	priv->pending_secondaries = NULL;
@@ -2589,6 +2615,11 @@ dispose (GObject *object)
 		g_clear_object (&priv->firewall_manager);
 	}
 
+	if (priv->agent_mgr) {
+		g_signal_handlers_disconnect_by_func (priv->agent_mgr, secret_agent_registered, self);
+		g_clear_object (&priv->agent_mgr);
+	}
+
 	if (priv->dns_manager) {
 		nm_clear_g_signal_handler (priv->dns_manager, &priv->config_changed_id);
 		g_clear_object (&priv->dns_manager);
@@ -2604,8 +2635,7 @@ dispose (GObject *object)
 	 * will have called active_connection_removed() and thus we don't need
 	 * to clean anything up.  Assert that this is TRUE.
 	 */
-	connections = nm_manager_get_active_connections (priv->manager);
-	g_assert (connections == NULL);
+	nm_assert (c_list_is_empty (nm_manager_get_active_connections (priv->manager)));
 
 	nm_clear_g_source (&priv->reset_retries_id);
 	nm_clear_g_source (&priv->schedule_activate_all_id);
diff --git a/src/nm-sleep-monitor.c b/src/nm-sleep-monitor.c
index 037db11b..7a373874 100644
--- a/src/nm-sleep-monitor.c
+++ b/src/nm-sleep-monitor.c
@@ -134,7 +134,7 @@ drop_inhibitor (NMSleepMonitor *self, gboolean force)
 
 	if (self->inhibit_fd >= 0) {
 		_LOGD ("inhibit: dropping sleep inhibitor %d", self->inhibit_fd);
-		close (self->inhibit_fd);
+		nm_close (self->inhibit_fd);
 		self->inhibit_fd = -1;
 	}
 
diff --git a/src/nm-types.h b/src/nm-types.h
index e01c48b0..cc657397 100644
--- a/src/nm-types.h
+++ b/src/nm-types.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) 2012 Red Hat, Inc.
+ * Copyright (C) 2012 - 2017 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_TYPES_H__
@@ -42,6 +42,7 @@ typedef struct _NMDevice             NMDevice;
 typedef struct _NMDhcp4Config        NMDhcp4Config;
 typedef struct _NMDhcp6Config        NMDhcp6Config;
 typedef struct _NMProxyConfig        NMProxyConfig;
+typedef struct _NMIPConfig           NMIPConfig;
 typedef struct _NMIP4Config          NMIP4Config;
 typedef struct _NMIP6Config          NMIP6Config;
 typedef struct _NMManager            NMManager;
@@ -176,6 +177,10 @@ typedef enum {
 	NMP_OBJECT_TYPE_IP4_ROUTE,
 	NMP_OBJECT_TYPE_IP6_ROUTE,
 
+	NMP_OBJECT_TYPE_QDISC,
+
+	NMP_OBJECT_TYPE_TFILTER,
+
 	NMP_OBJECT_TYPE_LNK_GRE,
 	NMP_OBJECT_TYPE_LNK_INFINIBAND,
 	NMP_OBJECT_TYPE_LNK_IP6TNL,
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c
index c199c5ed..be430152 100644
--- a/src/platform/nm-fake-platform.c
+++ b/src/platform/nm-fake-platform.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) 2012–2013 Red Hat, Inc.
+ * Copyright (C) 2012–2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -1056,11 +1056,11 @@ ipx_address_delete (NMPlatform *platform,
 	peer_addr_i = peer_addr ? *((guint32 *) peer_addr) : 0;
 
 	nmp_cache_iter_for_each (&iter,
-	                         nm_platform_lookup_addrroute (platform,
-	                                                       addr_family == AF_INET
-	                                                         ? NMP_OBJECT_TYPE_IP4_ADDRESS
-	                                                         : NMP_OBJECT_TYPE_IP6_ADDRESS,
-	                                                       0),
+	                         nm_platform_lookup_object (platform,
+	                                                    addr_family == AF_INET
+	                                                      ? NMP_OBJECT_TYPE_IP4_ADDRESS
+	                                                      : NMP_OBJECT_TYPE_IP6_ADDRESS,
+	                                                    0),
 	                         &o) {
 		const NMPObject *obj_old = NULL;
 
@@ -1144,9 +1144,9 @@ ipx_route_delete (NMPlatform *platform,
 	}
 
 	nmp_cache_iter_for_each (&iter,
-	                         nm_platform_lookup_addrroute (platform,
-	                                                       obj_type,
-	                                                       ifindex),
+	                         nm_platform_lookup_object (platform,
+	                                                    obj_type,
+	                                                    ifindex),
 	                         &o) {
 		const NMPObject *obj_old = NULL;
 
@@ -1190,7 +1190,7 @@ ipx_route_delete (NMPlatform *platform,
 }
 
 static gboolean
-ip_route_delete (NMPlatform *platform, const NMPObject *obj)
+object_delete (NMPlatform *platform, const NMPObject *obj)
 {
 	g_assert (NM_IS_FAKE_PLATFORM (platform));
 	g_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE,
@@ -1254,9 +1254,9 @@ ip_route_add (NMPlatform *platform,
 		gboolean has_route_to_gw = FALSE;
 
 		nmp_cache_iter_for_each (&iter,
-		                         nm_platform_lookup_addrroute (platform,
-		                                                       NMP_OBJECT_GET_TYPE (obj),
-		                                                       0),
+		                         nm_platform_lookup_object (platform,
+		                                                    NMP_OBJECT_GET_TYPE (obj),
+		                                                    0),
 		                         &o) {
 			if (addr_family == AF_INET) {
 				const NMPlatformIP4Route *item = NMP_OBJECT_CAST_IP4_ROUTE (o);
@@ -1462,5 +1462,5 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass)
 	platform_class->ip6_address_delete = ip6_address_delete;
 
 	platform_class->ip_route_add = ip_route_add;
-	platform_class->ip_route_delete = ip_route_delete;
+	platform_class->object_delete = object_delete;
 }
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index c4c93ed3..10d1a6ef 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.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) 2012-2015 Red Hat, Inc.
+ * Copyright (C) 2012 - 2017 Red Hat, Inc.
  */
 #include "nm-default.h"
 
@@ -54,6 +54,29 @@
 #include "nm-utils/unaligned.h"
 #include "nm-utils/nm-udev-utils.h"
 
+/*****************************************************************************/
+
+/* re-implement <linux/tc_act/tc_defact.h> to build against kernel
+ * headers that lack this. */
+
+#include <linux/pkt_cls.h>
+
+struct tc_defact {
+	tc_gen;
+};
+
+enum {
+	TCA_DEF_UNSPEC,
+	TCA_DEF_TM,
+	TCA_DEF_PARMS,
+	TCA_DEF_DATA,
+	TCA_DEF_PAD,
+	__TCA_DEF_MAX
+};
+#define TCA_DEF_MAX (__TCA_DEF_MAX - 1)
+
+/*****************************************************************************/
+
 #define VLAN_FLAG_MVRP 0x8
 
 /* nm-internal error codes for libnl. Make sure they don't overlap. */
@@ -227,27 +250,33 @@ enum {
 	DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES,
 	DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES,
 	DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES,
+	DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS,
+	DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS,
 	_DELAYED_ACTION_IDX_REFRESH_ALL_NUM,
 };
 
 typedef enum {
 	DELAYED_ACTION_TYPE_NONE                        = 0,
-	DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS           = (1LL << DELAYED_ACTION_IDX_REFRESH_ALL_LINKS),
-	DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES   = (1LL << DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES),
-	DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES   = (1LL << DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES),
-	DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES      = (1LL << DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES),
-	DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES      = (1LL << DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES),
-	DELAYED_ACTION_TYPE_REFRESH_LINK                = (1LL << 5),
-	DELAYED_ACTION_TYPE_MASTER_CONNECTED            = (1LL << 6),
-	DELAYED_ACTION_TYPE_READ_NETLINK                = (1LL << 7),
-	DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE        = (1LL << 8),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS           = (1LL << /* 0 */ DELAYED_ACTION_IDX_REFRESH_ALL_LINKS),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES   = (1LL << /* 1 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ADDRESSES),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES   = (1LL << /* 2 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES      = (1LL << /* 3 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES      = (1LL << /* 4 */ DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS          = (1LL << /* 5 */ DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS),
+	DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS        = (1LL << /* 6 */ DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS),
+	DELAYED_ACTION_TYPE_REFRESH_LINK                = (1LL <<    7),
+	DELAYED_ACTION_TYPE_MASTER_CONNECTED            = (1LL <<   11),
+	DELAYED_ACTION_TYPE_READ_NETLINK                = (1LL <<   12),
+	DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE        = (1LL <<   13),
 	__DELAYED_ACTION_TYPE_MAX,
 
 	DELAYED_ACTION_TYPE_REFRESH_ALL                 = DELAYED_ACTION_TYPE_REFRESH_ALL_LINKS |
 	                                                  DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES |
 	                                                  DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES |
 	                                                  DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES |
-	                                                  DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,
+	                                                  DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES |
+	                                                  DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS |
+	                                                  DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,
 
 	DELAYED_ACTION_TYPE_MAX                         = __DELAYED_ACTION_TYPE_MAX -1,
 } DelayedActionType;
@@ -941,17 +970,25 @@ _nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len)
 	b = buf;
 
 	switch (hdr->nlmsg_type) {
-	case RTM_NEWLINK:  s = "NEWLINK";  break;
-	case RTM_DELLINK:  s = "DELLINK";  break;
-	case RTM_NEWADDR:  s = "NEWADDR";  break;
-	case RTM_DELADDR:  s = "DELADDR";  break;
-	case RTM_NEWROUTE: s = "NEWROUTE"; break;
-	case RTM_DELROUTE: s = "DELROUTE"; break;
-	default:           s = NULL;       break;
+	case RTM_NEWLINK:    s = "RTM_NEWLINK";  break;
+	case RTM_DELLINK:    s = "RTM_DELLINK";  break;
+	case RTM_NEWADDR:    s = "RTM_NEWADDR";  break;
+	case RTM_DELADDR:    s = "RTM_DELADDR";  break;
+	case RTM_NEWROUTE:   s = "RTM_NEWROUTE"; break;
+	case RTM_DELROUTE:   s = "RTM_DELROUTE"; break;
+	case RTM_NEWQDISC:   s = "RTM_NEWQDISC"; break;
+	case RTM_DELQDISC:   s = "RTM_DELQDISC"; break;
+	case RTM_NEWTFILTER: s = "RTM_NEWTFILTER"; break;
+	case RTM_DELTFILTER: s = "RTM_DELTFILTER"; break;
+	case NLMSG_NOOP:     s = "NLMSG_NOOP"; break;
+	case NLMSG_ERROR:    s = "NLMSG_ERROR"; break;
+	case NLMSG_DONE:     s = "NLMSG_DONE"; break;
+	case NLMSG_OVERRUN:  s = "NLMSG_OVERRUN"; break;
+	default:             s = NULL;       break;
 	}
 
 	if (s)
-		nm_utils_strbuf_append (&buf, &len, "RTM_%s", s);
+		nm_utils_strbuf_append_str (&buf, &len, s);
 	else
 		nm_utils_strbuf_append (&buf, &len, "(%u)", (unsigned) hdr->nlmsg_type);
 
@@ -989,6 +1026,8 @@ _nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len)
 	case RTM_NEWLINK:
 	case RTM_NEWADDR:
 	case RTM_NEWROUTE:
+	case RTM_NEWQDISC:
+	case RTM_NEWTFILTER:
 		_F (NLM_F_REPLACE, "replace");
 		_F (NLM_F_EXCL, "excl");
 		_F (NLM_F_CREATE, "create");
@@ -997,6 +1036,8 @@ _nl_nlmsghdr_to_str (const struct nlmsghdr *hdr, char *buf, gsize len)
 	case RTM_GETLINK:
 	case RTM_GETADDR:
 	case RTM_GETROUTE:
+	case RTM_DELQDISC:
+	case RTM_DELTFILTER:
 		_F (NLM_F_DUMP, "dump");
 		_F (NLM_F_ROOT, "root");
 		_F (NLM_F_MATCH, "match");
@@ -2291,17 +2332,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only)
 			obj->ip6_route.rt_pref = nla_get_u8 (tb[RTA_PREF]);
 	}
 
-	if (NM_FLAGS_HAS (rtm->rtm_flags, RTM_F_CLONED)) {
-		/* we must not straight way reject cloned routes, because we might have cached
-		 * a non-cloned route. If we now receive an update of the route with the route
-		 * being cloned, we must still return the object, so that we can remove the old
-		 * one from the cache.
-		 *
-		 * This happens, because this route is not nmp_object_is_alive().
-		 * */
-		obj->ip_route.rt_cloned = TRUE;
-	}
-
+	obj->ip_route.r_rtm_flags = rtm->rtm_flags;
 	obj->ip_route.rt_source = nmp_utils_ip_config_source_from_rtprot (rtm->rtm_protocol);
 
 	obj_result = obj;
@@ -2310,6 +2341,74 @@ errout:
 	return obj_result;
 }
 
+static NMPObject *
+_new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only)
+{
+	NMPObject *obj = NULL;
+	const struct tcmsg *tcm;
+	struct nlattr *tb[TCA_MAX + 1];
+	int err;
+	static const struct nla_policy policy[TCA_MAX + 1] = {
+		[TCA_KIND] = { .type = NLA_STRING },
+	};
+
+	if (!nlmsg_valid_hdr (nlh, sizeof (*tcm)))
+		return NULL;
+	tcm = nlmsg_data (nlh);
+
+	err = nlmsg_parse (nlh, sizeof (*tcm), tb, TCA_MAX, policy);
+	if (err < 0)
+		return NULL;
+
+	if (!tb[TCA_KIND])
+		return NULL;
+
+	obj = nmp_object_new (NMP_OBJECT_TYPE_QDISC, NULL);
+
+	obj->qdisc.kind = g_intern_string (nla_get_string (tb[TCA_KIND]));
+	obj->qdisc.ifindex = tcm->tcm_ifindex;
+	obj->qdisc.addr_family = tcm->tcm_family;
+	obj->qdisc.handle = tcm->tcm_handle;
+	obj->qdisc.parent = tcm->tcm_parent;
+	obj->qdisc.info = tcm->tcm_info;
+
+	return obj;
+}
+
+static NMPObject *
+_new_from_nl_tfilter (struct nlmsghdr *nlh, gboolean id_only)
+{
+	NMPObject *obj = NULL;
+	const struct tcmsg *tcm;
+	struct nlattr *tb[TCA_MAX + 1];
+	int err;
+	static const struct nla_policy policy[TCA_MAX + 1] = {
+		[TCA_KIND] = { .type = NLA_STRING },
+	};
+
+	if (!nlmsg_valid_hdr (nlh, sizeof (*tcm)))
+		return NULL;
+	tcm = nlmsg_data (nlh);
+
+	err = nlmsg_parse (nlh, sizeof (*tcm), tb, TCA_MAX, policy);
+	if (err < 0)
+		return NULL;
+
+	if (!tb[TCA_KIND])
+		return NULL;
+
+	obj = nmp_object_new (NMP_OBJECT_TYPE_TFILTER, NULL);
+
+	obj->tfilter.kind = g_intern_string (nla_get_string (tb[TCA_KIND]));
+	obj->tfilter.ifindex = tcm->tcm_ifindex;
+	obj->tfilter.addr_family = tcm->tcm_family;
+	obj->tfilter.handle = tcm->tcm_handle;
+	obj->tfilter.parent = tcm->tcm_parent;
+	obj->tfilter.info = tcm->tcm_info;
+
+	return obj;
+}
+
 /**
  * nmp_object_new_from_nl:
  * @platform: (allow-none): for creating certain objects, the constructor wants to check
@@ -2346,6 +2445,14 @@ nmp_object_new_from_nl (NMPlatform *platform, const NMPCache *cache, struct nl_m
 	case RTM_DELROUTE:
 	case RTM_GETROUTE:
 		return _new_from_nl_route (msghdr, id_only);
+	case RTM_NEWQDISC:
+	case RTM_DELQDISC:
+	case RTM_GETQDISC:
+		return _new_from_nl_qdisc (msghdr, id_only);
+	case RTM_NEWTFILTER:
+	case RTM_DELTFILTER:
+	case RTM_GETTFILTER:
+		return _new_from_nl_tfilter (msghdr, id_only);
 	default:
 		return NULL;
 	}
@@ -2700,7 +2807,9 @@ _nl_msg_new_route (int nlmsg_type,
 		             ? nm_platform_route_scope_inv (obj->ip4_route.scope_inv)
 		             : RT_SCOPE_NOWHERE,
 		.rtm_type = RTN_UNICAST,
-		.rtm_flags = 0,
+		.rtm_flags = obj->ip_route.r_rtm_flags & (is_v4
+		                                          ? (unsigned) (RTNH_F_ONLINK)
+		                                          : (unsigned) 0),
 		.rtm_dst_len = obj->ip_route.plen,
 		.rtm_src_len = is_v4
 		               ? 0
@@ -2797,6 +2906,124 @@ nla_put_failure:
 	g_return_val_if_reached (NULL);
 }
 
+static struct nl_msg *
+_nl_msg_new_qdisc (int nlmsg_type,
+                   int nlmsg_flags,
+                   const NMPlatformQdisc *qdisc)
+{
+	struct nl_msg *msg;
+	struct tcmsg tcm = {
+		.tcm_family = qdisc->addr_family,
+		.tcm_ifindex = qdisc->ifindex,
+		.tcm_handle = qdisc->handle,
+		.tcm_parent = qdisc->parent,
+		.tcm_info = qdisc->info,
+	};
+
+	msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags);
+	if (!msg)
+		return NULL;
+
+	if (nlmsg_append (msg, &tcm, sizeof (tcm), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	NLA_PUT_STRING (msg, TCA_KIND, qdisc->kind);
+
+	return msg;
+nla_put_failure:
+	nlmsg_free (msg);
+	g_return_val_if_reached (NULL);
+}
+
+static gboolean
+_add_action_simple (struct nl_msg *msg,
+                    const NMPlatformActionSimple *simple)
+{
+	struct nlattr *act_options;
+	struct tc_defact sel = { 0, };
+
+	if (!(act_options = nla_nest_start (msg, TCA_ACT_OPTIONS)))
+		goto nla_put_failure;
+
+	NLA_PUT (msg, TCA_DEF_PARMS, sizeof (sel), &sel);
+	NLA_PUT (msg, TCA_DEF_DATA, sizeof (simple->sdata), simple->sdata);
+
+	nla_nest_end (msg, act_options);
+
+	return TRUE;
+
+nla_put_failure:
+	return FALSE;
+}
+
+static gboolean
+_add_action (struct nl_msg *msg,
+             const NMPlatformAction *action)
+{
+	struct nlattr *prio;
+
+	nm_assert (action || action->kind);
+
+	if (!(prio = nla_nest_start (msg, 1 /* priority */)))
+		goto nla_put_failure;
+
+	NLA_PUT_STRING (msg, TCA_ACT_KIND, action->kind);
+
+	if (nm_streq (action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE))
+		_add_action_simple (msg, &action->simple);
+
+	nla_nest_end (msg, prio);
+
+	return TRUE;
+
+nla_put_failure:
+	return FALSE;
+}
+
+static struct nl_msg *
+_nl_msg_new_tfilter (int nlmsg_type,
+                     int nlmsg_flags,
+                     const NMPlatformTfilter *tfilter)
+{
+	struct nl_msg *msg;
+	struct nlattr *tc_options;
+	struct nlattr *act_tab;
+	struct tcmsg tcm = {
+		.tcm_family = tfilter->addr_family,
+		.tcm_ifindex = tfilter->ifindex,
+		.tcm_handle = tfilter->handle,
+		.tcm_parent = tfilter->parent,
+		.tcm_info = tfilter->info,
+	};
+
+	msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags);
+	if (!msg)
+		return NULL;
+
+	if (nlmsg_append (msg, &tcm, sizeof (tcm), NLMSG_ALIGNTO) < 0)
+		goto nla_put_failure;
+
+	NLA_PUT_STRING (msg, TCA_KIND, tfilter->kind);
+
+	if (!(tc_options = nla_nest_start (msg, TCA_OPTIONS)))
+		goto nla_put_failure;
+
+	if (!(act_tab = nla_nest_start (msg, TCA_OPTIONS))) // 3 TCA_ACT_KIND TCA_ACT_KIND
+		goto nla_put_failure;
+
+	if (tfilter->action.kind)
+		_add_action (msg, &tfilter->action);
+
+	nla_nest_end (msg, tc_options);
+
+	nla_nest_end (msg, act_tab);
+
+	return msg;
+nla_put_failure:
+	nlmsg_free (msg);
+	g_return_val_if_reached (NULL);
+}
+
 /******************************************************************
  * NMPlatform types and functions
  ******************************************************************/
@@ -2867,7 +3094,7 @@ struct _NMLinuxPlatformClass {
 
 G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM)
 
-#define NM_LINUX_PLATFORM_GET_PRIVATE(self) _NM_GET_PRIVATE_VOID(self, NMLinuxPlatform, NM_IS_LINUX_PLATFORM)
+#define NM_LINUX_PLATFORM_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMLinuxPlatform, NM_IS_LINUX_PLATFORM, NMPlatform)
 
 NMPlatform *
 nm_linux_platform_new (gboolean log_with_ptr, gboolean netns_support)
@@ -3198,6 +3425,8 @@ _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_from_object_type, NMPObj
 	NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP6_ADDRESS, DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES),
 	NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP4_ROUTE,   DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES),
 	NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_IP6_ROUTE,   DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES),
+	NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_QDISC,       DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS),
+	NM_UTILS_LOOKUP_ITEM (NMP_OBJECT_TYPE_TFILTER,     DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS),
 	NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (),
 );
 
@@ -3208,6 +3437,8 @@ _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_to_object_type, DelayedA
 	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, NMP_OBJECT_TYPE_IP6_ADDRESS),
 	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES,    NMP_OBJECT_TYPE_IP4_ROUTE),
 	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,    NMP_OBJECT_TYPE_IP6_ROUTE),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS,        NMP_OBJECT_TYPE_QDISC),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,      NMP_OBJECT_TYPE_TFILTER),
 	NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (),
 );
 
@@ -3218,6 +3449,8 @@ _NM_UTILS_LOOKUP_DEFINE (static, delayed_action_refresh_all_to_idx, DelayedActio
 	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ADDRESSES),
 	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES,    DELAYED_ACTION_IDX_REFRESH_ALL_IP4_ROUTES),
 	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,    DELAYED_ACTION_IDX_REFRESH_ALL_IP6_ROUTES),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS,        DELAYED_ACTION_IDX_REFRESH_ALL_QDISCS),
+	NM_UTILS_LOOKUP_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,      DELAYED_ACTION_IDX_REFRESH_ALL_TFILTERS),
 	NM_UTILS_LOOKUP_ITEM_IGNORE_OTHER (),
 );
 
@@ -3228,6 +3461,8 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (delayed_action_to_string, DelayedActionType,
 	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES, "refresh-all-ip6-addresses"),
 	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES,    "refresh-all-ip4-routes"),
 	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,    "refresh-all-ip6-routes"),
+	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS,        "refresh-all-qdiscs"),
+	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,      "refresh-all-tfilters"),
 	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_REFRESH_LINK,              "refresh-link"),
 	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_MASTER_CONNECTED,          "master-connected"),
 	NM_UTILS_LOOKUP_STR_ITEM (DELAYED_ACTION_TYPE_READ_NETLINK,              "read-netlink"),
@@ -3665,7 +3900,7 @@ cache_on_change (NMPlatform *platform,
 		{
 			int ifindex = 0;
 
-			/* if we remove a link (from netlink), we must refresh the addresses and routes */
+			/* if we remove a link (from netlink), we must refresh the addresses, routes, qdiscs and tfilters */
 			if (   cache_op == NMP_CACHE_OPS_REMOVED
 			    && obj_old /* <-- nonsensical, make coverity happy */)
 				ifindex = obj_old->link.ifindex;
@@ -3680,7 +3915,9 @@ cache_on_change (NMPlatform *platform,
 				                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES |
 				                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES |
 				                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES |
-				                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,
+				                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES |
+				                         DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS |
+				                         DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,
 				                         NULL);
 			}
 		}
@@ -4003,9 +4240,6 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio
 		NMPObjectType obj_type = delayed_action_refresh_to_object_type (iflags);
 		const NMPClass *klass = nmp_class_from_type (obj_type);
 		nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
-		struct rtgenmsg gmsg = {
-			.rtgen_family = klass->addr_family,
-		};
 		int nle;
 		gint *out_refresh_all_in_progess;
 
@@ -4032,7 +4266,18 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio
 		if (!nlmsg)
 			continue;
 
-		nle = nlmsg_append (nlmsg, &gmsg, sizeof (gmsg), NLMSG_ALIGNTO);
+		if (   klass->obj_type == NMP_OBJECT_TYPE_QDISC
+		    || klass->obj_type == NMP_OBJECT_TYPE_TFILTER) {
+			struct tcmsg tcmsg = {
+				.tcm_family = AF_UNSPEC,
+			};
+			nle = nlmsg_append (nlmsg, &tcmsg, sizeof (tcmsg), NLMSG_ALIGNTO);
+		} else {
+			struct rtgenmsg gmsg = {
+				.rtgen_family = klass->addr_family,
+			};
+			nle = nlmsg_append (nlmsg, &gmsg, sizeof (gmsg), NLMSG_ALIGNTO);
+		}
 		if (nle < 0)
 			continue;
 
@@ -4154,6 +4399,8 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
 	case RTM_NEWADDR:
 	case RTM_NEWLINK:
 	case RTM_NEWROUTE:
+	case RTM_NEWQDISC:
+	case RTM_NEWTFILTER:
 		is_dump = delayed_action_refresh_all_in_progress (platform,
 		                                                  delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj)));
 		break;
@@ -4177,6 +4424,8 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
 		case RTM_NEWLINK:
 		case RTM_NEWADDR:
 		case RTM_GETLINK:
+		case RTM_NEWQDISC:
+		case RTM_NEWTFILTER:
 			cache_op = nmp_cache_update_netlink (cache, obj, is_dump, &obj_old, &obj_new);
 			if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
 				cache_on_change (platform, cache_op, obj_old, obj_new);
@@ -4189,7 +4438,7 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
 			gboolean resync_required = FALSE;
 			gboolean only_dirty = FALSE;
 
-			if (obj->ip_route.rt_cloned) {
+			if (NM_FLAGS_HAS (obj->ip_route.r_rtm_flags, RTM_F_CLONED)) {
 				/* a cloned route might be a response for RTM_GETROUTE. Check, whether it is. */
 				nm_assert (!nmp_object_is_alive (obj));
 				priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform);
@@ -4270,13 +4519,14 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event
 		case RTM_DELLINK:
 		case RTM_DELADDR:
 		case RTM_DELROUTE:
+		case RTM_DELQDISC:
+		case RTM_DELTFILTER:
 			cache_op = nmp_cache_remove_netlink (cache, obj, &obj_old, &obj_new);
 			if (cache_op != NMP_CACHE_OPS_UNCHANGED) {
 				cache_on_change (platform, cache_op, obj_old, obj_new);
 				nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new);
 			}
 			break;
-
 		default:
 			break;
 		}
@@ -5652,7 +5902,7 @@ tun_add (NMPlatform *platform, const char *name, gboolean tap,
 	if (out_link)
 		*out_link = obj ? &obj->link : NULL;
 
-	close (fd);
+	nm_close (fd);
 	return !!obj;
 }
 
@@ -5886,15 +6136,15 @@ link_can_assume (NMPlatform *platform, int ifindex)
 	if (link->link.master > 0)
 		return TRUE;
 
-	nmp_lookup_init_addrroute (&lookup,
-	                           NMP_OBJECT_TYPE_IP4_ADDRESS,
-	                           ifindex);
+	nmp_lookup_init_object (&lookup,
+	                        NMP_OBJECT_TYPE_IP4_ADDRESS,
+	                        ifindex);
 	if (nmp_cache_lookup (cache, &lookup))
 		return TRUE;
 
-	nmp_lookup_init_addrroute (&lookup,
-	                           NMP_OBJECT_TYPE_IP6_ADDRESS,
-	                           ifindex);
+	nmp_lookup_init_object (&lookup,
+	                        NMP_OBJECT_TYPE_IP6_ADDRESS,
+	                        ifindex);
 	nmp_cache_iter_for_each (&iter,
 	                         nmp_cache_lookup (cache, &lookup),
 	                         &o) {
@@ -6120,19 +6370,30 @@ ip_route_add (NMPlatform *platform,
 }
 
 static gboolean
-ip_route_delete (NMPlatform *platform,
-                 const NMPObject *obj)
+object_delete (NMPlatform *platform,
+               const NMPObject *obj)
 {
 	nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL;
 	nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
 
-	nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE,
-	                                                 NMP_OBJECT_TYPE_IP6_ROUTE));
-
 	if (!NMP_OBJECT_IS_STACKINIT (obj))
 		obj_keep_alive = nmp_object_ref (obj);
 
-	nlmsg = _nl_msg_new_route (RTM_DELROUTE, 0, obj);
+	switch (NMP_OBJECT_GET_TYPE (obj)) {
+	case NMP_OBJECT_TYPE_IP4_ROUTE:
+	case NMP_OBJECT_TYPE_IP6_ROUTE:
+		nlmsg = _nl_msg_new_route (RTM_DELROUTE, 0, obj);
+		break;
+	case NMP_OBJECT_TYPE_QDISC:
+		nlmsg = _nl_msg_new_qdisc (RTM_DELQDISC, 0, NMP_OBJECT_CAST_QDISC (obj));
+		break;
+	case NMP_OBJECT_TYPE_TFILTER:
+		nlmsg = _nl_msg_new_tfilter (RTM_DELTFILTER, 0, NMP_OBJECT_CAST_TFILTER (obj));
+		break;
+	default:
+		break;
+	}
+
 	if (!nlmsg)
 		g_return_val_if_reached (FALSE);
 	return do_delete_object (platform, obj, nlmsg);
@@ -6219,6 +6480,84 @@ ip_route_get (NMPlatform *platform,
 
 /*****************************************************************************/
 
+static NMPlatformError
+qdisc_add (NMPlatform *platform,
+           NMPNlmFlags flags,
+           const NMPlatformQdisc *qdisc)
+{
+	WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
+	int nle;
+	char s_buf[256];
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
+
+	msg = _nl_msg_new_qdisc (RTM_NEWQDISC, flags, qdisc);
+
+	event_handler_read_netlink (platform, FALSE);
+
+	nle = _nl_send_nlmsg (platform, msg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
+	if (nle < 0) {
+		_LOGE ("do-add-qdisc: failed sending netlink request \"%s\" (%d)",
+		      nl_geterror (nle), -nle);
+		return NM_PLATFORM_ERROR_NETLINK;
+	}
+
+	delayed_action_handle_all (platform, FALSE);
+
+	nm_assert (seq_result);
+
+	_NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK
+	            ? LOGL_DEBUG
+	            : LOGL_WARN,
+	        "do-add-qdisc: %s",
+	        wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)));
+
+	if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK)
+		return NM_PLATFORM_ERROR_SUCCESS;
+
+	return NM_PLATFORM_ERROR_UNSPECIFIED;
+}
+
+/*****************************************************************************/
+
+static NMPlatformError
+tfilter_add (NMPlatform *platform,
+             NMPNlmFlags flags,
+             const NMPlatformTfilter *tfilter)
+{
+	WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN;
+	int nle;
+	char s_buf[256];
+	nm_auto_nlmsg struct nl_msg *msg = NULL;
+
+	msg = _nl_msg_new_tfilter (RTM_NEWTFILTER, flags, tfilter);
+
+	event_handler_read_netlink (platform, FALSE);
+
+	nle = _nl_send_nlmsg (platform, msg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL);
+	if (nle < 0) {
+		_LOGE ("do-add-tfilter: failed sending netlink request \"%s\" (%d)",
+		      nl_geterror (nle), -nle);
+		return NM_PLATFORM_ERROR_NETLINK;
+	}
+
+	delayed_action_handle_all (platform, FALSE);
+
+	nm_assert (seq_result);
+
+	_NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK
+	            ? LOGL_DEBUG
+	            : LOGL_WARN,
+	        "do-add-tfilter: %s",
+	        wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)));
+
+	if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK)
+		return NM_PLATFORM_ERROR_SUCCESS;
+
+	return NM_PLATFORM_ERROR_UNSPECIFIED;
+}
+
+/*****************************************************************************/
+
 #define EVENT_CONDITIONS      ((GIOCondition) (G_IO_IN | G_IO_PRI))
 #define ERROR_CONDITIONS      ((GIOCondition) (G_IO_ERR | G_IO_NVAL))
 #define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP))
@@ -6500,7 +6839,9 @@ event_handler_read_netlink (NMPlatform *platform, gboolean wait_for_acks)
 					                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES |
 					                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES |
 					                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES |
-					                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,
+					                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES |
+					                         DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS |
+					                         DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,
 					                         NULL);
 					break;
 				default:
@@ -6760,13 +7101,13 @@ constructed (GObject *_object)
 	                                 RTNLGRP_LINK,
 	                                 RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR,
 	                                 RTNLGRP_IPV4_ROUTE,  RTNLGRP_IPV6_ROUTE,
+	                                 RTNLGRP_TC,
 	                                 0);
 	g_assert (!nle);
 	_LOGD ("Netlink socket for events established: port=%u, fd=%d", nl_socket_get_local_port (priv->nlh), nl_socket_get_fd (priv->nlh));
 
 	priv->event_channel = g_io_channel_unix_new (nl_socket_get_fd (priv->nlh));
 	g_io_channel_set_encoding (priv->event_channel, NULL, NULL);
-	g_io_channel_set_close_on_unref (priv->event_channel, TRUE);
 
 	channel_flags = g_io_channel_get_flags (priv->event_channel);
 	status = g_io_channel_set_flags (priv->event_channel,
@@ -6785,7 +7126,9 @@ constructed (GObject *_object)
 	                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ADDRESSES |
 	                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ADDRESSES |
 	                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES |
-	                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES,
+	                         DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES |
+	                         DELAYED_ACTION_TYPE_REFRESH_ALL_QDISCS |
+	                         DELAYED_ACTION_TYPE_REFRESH_ALL_TFILTERS,
 	                         NULL);
 
 	delayed_action_handle_all (platform, FALSE);
@@ -6942,15 +7285,18 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass)
 	platform_class->link_ipip_add = link_ipip_add;
 	platform_class->link_sit_add = link_sit_add;
 
+	platform_class->object_delete = object_delete;
 	platform_class->ip4_address_add = ip4_address_add;
 	platform_class->ip6_address_add = ip6_address_add;
 	platform_class->ip4_address_delete = ip4_address_delete;
 	platform_class->ip6_address_delete = ip6_address_delete;
 
 	platform_class->ip_route_add = ip_route_add;
-	platform_class->ip_route_delete = ip_route_delete;
 	platform_class->ip_route_get = ip_route_get;
 
+	platform_class->qdisc_add = qdisc_add;
+	platform_class->tfilter_add = tfilter_add;
+
 	platform_class->check_kernel_support = check_kernel_support;
 
 	platform_class->process_events = process_events;
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index ffc4b395..2ca379e4 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.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) 2012 Red Hat, Inc.
+ * Copyright (C) 2012 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -508,6 +508,7 @@ nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *pathid, int di
 {
 	char *value = NULL;
 	gint32 ret;
+	int errsv;
 
 	_CHECK_SELF (self, klass, fallback);
 
@@ -522,7 +523,9 @@ nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *pathid, int di
 	}
 
 	ret = _nm_utils_ascii_str_to_int64 (value, base, min, max, fallback);
+	errsv = errno;
 	g_free (value);
+	errno = errsv;
 	return ret;
 }
 
@@ -3343,9 +3346,9 @@ delete_and_next:
 	}
 
 	plat_addresses = nm_platform_lookup_clone (self,
-	                                           nmp_lookup_init_addrroute (&lookup,
-	                                                                      NMP_OBJECT_TYPE_IP4_ADDRESS,
-	                                                                      ifindex),
+	                                           nmp_lookup_init_object (&lookup,
+	                                                                   NMP_OBJECT_TYPE_IP4_ADDRESS,
+	                                                                   ifindex),
 	                                           NULL, NULL);
 	if (plat_addresses)
 		plat_subnets = ip4_addr_subnets_build_index (plat_addresses, TRUE, TRUE);
@@ -3485,9 +3488,9 @@ nm_platform_ip6_address_sync (NMPlatform *self,
 
 	/* Delete unknown addresses */
 	plat_addresses = nm_platform_lookup_clone (self,
-	                                           nmp_lookup_init_addrroute (&lookup,
-	                                                                      NMP_OBJECT_TYPE_IP6_ADDRESS,
-	                                                                      ifindex),
+	                                           nmp_lookup_init_object (&lookup,
+	                                                                   NMP_OBJECT_TYPE_IP6_ADDRESS,
+	                                                                   ifindex),
 	                                           NULL, NULL);
 	if (plat_addresses) {
 		for (i = 0; i < plat_addresses->len; i++) {
@@ -3608,11 +3611,11 @@ nm_platform_ip_route_get_prune_list (NMPlatform *self,
 	                                        NM_IP_ROUTE_TABLE_SYNC_MODE_FULL,
 	                                        NM_IP_ROUTE_TABLE_SYNC_MODE_ALL));
 
-	nmp_lookup_init_addrroute (&lookup,
-	                           addr_family == AF_INET
-	                             ? NMP_OBJECT_TYPE_IP4_ROUTE
-	                             : NMP_OBJECT_TYPE_IP6_ROUTE,
-	                           ifindex);
+	nmp_lookup_init_object (&lookup,
+	                        addr_family == AF_INET
+	                          ? NMP_OBJECT_TYPE_IP4_ROUTE
+	                          : NMP_OBJECT_TYPE_IP6_ROUTE,
+	                        ifindex);
 	head_entry = nm_platform_lookup (self, &lookup);
 	if (!head_entry)
 		return NULL;
@@ -3729,7 +3732,7 @@ nm_platform_ip_route_sync (NMPlatform *self,
 
 				/* we need to replace the existing route with a (slightly) differnt
 				 * one. Delete it first. */
-				if (!nm_platform_ip_route_delete (self, plat_o)) {
+				if (!nm_platform_object_delete (self, plat_o)) {
 					/* ignore error. */
 				}
 			}
@@ -3810,7 +3813,7 @@ nm_platform_ip_route_sync (NMPlatform *self,
 			                               prune_o))
 				continue;
 
-			if (!nm_platform_ip_route_delete (self, prune_o)) {
+			if (!nm_platform_object_delete (self, prune_o)) {
 				/* ignore error... */
 			}
 		}
@@ -3988,20 +3991,22 @@ nm_platform_ip6_route_add (NMPlatform *self,
 }
 
 gboolean
-nm_platform_ip_route_delete (NMPlatform *self,
-                             const NMPObject *obj)
+nm_platform_object_delete (NMPlatform *self,
+                           const NMPObject *obj)
 {
 	_CHECK_SELF (self, klass, FALSE);
 
 	if (!NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE,
-	                                           NMP_OBJECT_TYPE_IP6_ROUTE))
+	                                           NMP_OBJECT_TYPE_IP6_ROUTE,
+	                                           NMP_OBJECT_TYPE_QDISC,
+	                                           NMP_OBJECT_TYPE_TFILTER))
 		g_return_val_if_reached (FALSE);
 
-	_LOGD ("route: delete     IPv%c route %s",
-	       NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE ? '4' : '6',
+	_LOGD ("%s: delete %s",
+	       NMP_OBJECT_GET_CLASS (obj)->obj_type_name,
 	       nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
 
-	return klass->ip_route_delete (self, obj);
+	return klass->object_delete (self, obj);
 }
 
 /*****************************************************************************/
@@ -4112,7 +4117,7 @@ again:
 
 		_LOGT ("ip4-dev-route: delete %s",
 		       nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
-		nm_platform_ip_route_delete (self, p_obj);
+		nm_platform_object_delete (self, p_obj);
 		goto again;
 	}
 
@@ -4329,6 +4334,138 @@ nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self,
 
 /*****************************************************************************/
 
+NMPlatformError
+nm_platform_qdisc_add (NMPlatform *self,
+                       NMPNlmFlags flags,
+                       const NMPlatformQdisc *qdisc)
+{
+	_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
+
+	_LOGD ("adding or updating a qdisc: %s", nm_platform_qdisc_to_string (qdisc, NULL, 0));
+	return klass->qdisc_add (self, flags, qdisc);
+}
+
+gboolean
+nm_platform_qdisc_sync (NMPlatform *self,
+                        int ifindex,
+                        GPtrArray *known_qdiscs)
+{
+	gs_unref_ptrarray GPtrArray *plat_qdiscs = NULL;
+	NMPLookup lookup;
+	guint i;
+	gboolean success = TRUE;
+	gs_unref_hashtable GHashTable *known_qdiscs_idx = NULL;
+
+	nm_assert (NM_IS_PLATFORM (self));
+	nm_assert (ifindex > 0);
+
+	known_qdiscs_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash,
+	                                     (GEqualFunc) nmp_object_id_equal);
+
+	if (known_qdiscs) {
+		for (i = 0; i < known_qdiscs->len; i++) {
+			const NMPObject *q = g_ptr_array_index (known_qdiscs, i);
+
+			g_hash_table_insert (known_qdiscs_idx, (gpointer) q, (gpointer) q);
+		}
+	}
+
+	plat_qdiscs = nm_platform_lookup_clone (self,
+	                                        nmp_lookup_init_object (&lookup,
+	                                                                NMP_OBJECT_TYPE_QDISC,
+	                                                                ifindex),
+	                                        NULL, NULL);
+
+
+	if (plat_qdiscs) {
+		for (i = 0; i < plat_qdiscs->len; i++) {
+			const NMPObject *q = g_ptr_array_index (plat_qdiscs, i);
+
+			if (!g_hash_table_lookup (known_qdiscs_idx, q))
+				success &= nm_platform_object_delete (self, q);
+		}
+	}
+
+	if (known_qdiscs) {
+		for (i = 0; i < known_qdiscs->len; i++) {
+			const NMPObject *q = g_ptr_array_index (known_qdiscs, i);
+
+			success &= (nm_platform_qdisc_add (self, NMP_NLM_FLAG_ADD,
+			                                   NMP_OBJECT_CAST_QDISC (q)) == NM_PLATFORM_ERROR_SUCCESS);
+		}
+	}
+
+	return success;
+}
+
+/*****************************************************************************/
+
+NMPlatformError
+nm_platform_tfilter_add (NMPlatform *self,
+                         NMPNlmFlags flags,
+                         const NMPlatformTfilter *tfilter)
+{
+	_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
+
+	_LOGD ("adding or updating a tfilter: %s", nm_platform_tfilter_to_string (tfilter, NULL, 0));
+	return klass->tfilter_add (self, flags, tfilter);
+}
+
+gboolean
+nm_platform_tfilter_sync (NMPlatform *self,
+                          int ifindex,
+                          GPtrArray *known_tfilters)
+{
+	gs_unref_ptrarray GPtrArray *plat_tfilters = NULL;
+	NMPLookup lookup;
+	guint i;
+	gboolean success = TRUE;
+	gs_unref_hashtable GHashTable *known_tfilters_idx = NULL;
+
+	nm_assert (NM_IS_PLATFORM (self));
+	nm_assert (ifindex > 0);
+
+	known_tfilters_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash,
+	                                       (GEqualFunc) nmp_object_id_equal);
+
+	if (known_tfilters) {
+		for (i = 0; i < known_tfilters->len; i++) {
+			const NMPObject *q = g_ptr_array_index (known_tfilters, i);
+
+			g_hash_table_insert (known_tfilters_idx, (gpointer) q, (gpointer) q);
+		}
+	}
+
+	plat_tfilters = nm_platform_lookup_clone (self,
+	                                          nmp_lookup_init_object (&lookup,
+	                                                                  NMP_OBJECT_TYPE_TFILTER,
+	                                                                  ifindex),
+	                                          NULL, NULL);
+
+
+	if (plat_tfilters) {
+		for (i = 0; i < plat_tfilters->len; i++) {
+			const NMPObject *q = g_ptr_array_index (plat_tfilters, i);
+
+			if (!g_hash_table_lookup (known_tfilters_idx, q))
+				success &= nm_platform_object_delete (self, q);
+		}
+	}
+
+	if (known_tfilters) {
+		for (i = 0; i < known_tfilters->len; i++) {
+			const NMPObject *q = g_ptr_array_index (known_tfilters, i);
+
+			success &= (nm_platform_tfilter_add (self, NMP_NLM_FLAG_ADD,
+			                                     NMP_OBJECT_CAST_TFILTER (q)) == NM_PLATFORM_ERROR_SUCCESS);
+		}
+	}
+
+	return success;
+}
+
+/*****************************************************************************/
+
 const char *
 nm_platform_vlan_qos_mapping_to_string (const char *name,
                                         const NMVlanQosMapping *map,
@@ -4976,6 +5113,40 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *bu
 	return buf;
 }
 
+NM_UTILS_FLAGS2STR_DEFINE_STATIC (_rtm_flags_to_string, unsigned,
+	NM_UTILS_FLAGS2STR (RTNH_F_DEAD,                   "dead"),
+	NM_UTILS_FLAGS2STR (RTNH_F_PERVASIVE,              "pervasive"),
+	NM_UTILS_FLAGS2STR (RTNH_F_ONLINK,                 "onlink"),
+	NM_UTILS_FLAGS2STR (8  /*RTNH_F_OFFLOAD*/,         "offload"),
+	NM_UTILS_FLAGS2STR (16 /*RTNH_F_LINKDOWN*/,        "linkdown"),
+	NM_UTILS_FLAGS2STR (32 /*RTNH_F_UNRESOLVED*/,      "unresolved"),
+
+	NM_UTILS_FLAGS2STR (RTM_F_NOTIFY,                  "notify"),
+	NM_UTILS_FLAGS2STR (RTM_F_CLONED,                  "cloned"),
+	NM_UTILS_FLAGS2STR (RTM_F_EQUALIZE,                "equalize"),
+	NM_UTILS_FLAGS2STR (RTM_F_PREFIX,                  "prefix"),
+	NM_UTILS_FLAGS2STR (0x1000 /*RTM_F_LOOKUP_TABLE*/, "lookup-table"),
+	NM_UTILS_FLAGS2STR (0x2000 /*RTM_F_FIB_MATCH*/,    "fib-match"),
+);
+
+#define _RTM_FLAGS_TO_STRING_MAXLEN 200
+
+static const char *
+_rtm_flags_to_string_full (char *buf, gsize buf_size, unsigned rtm_flags)
+{
+	const char *buf0 = buf;
+
+	nm_assert (buf_size >= _RTM_FLAGS_TO_STRING_MAXLEN);
+
+	if (!rtm_flags)
+		return "";
+
+	nm_utils_strbuf_append_str (&buf, &buf_size, " rtm_flags ");
+	_rtm_flags_to_string (rtm_flags, buf, buf_size);
+	nm_assert (strlen (buf) < buf_size);
+	return buf0;
+}
+
 /**
  * nm_platform_ip4_route_to_string:
  * @route: pointer to NMPlatformIP4Route route structure
@@ -4997,6 +5168,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi
 	char str_table[30];
 	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];
+	char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN];
 
 	if (!nm_utils_to_string_buffer_init_null (route, &buf, &len))
 		return buf;
@@ -5015,7 +5187,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi
 	            " metric %"G_GUINT32_FORMAT
 	            " mss %"G_GUINT32_FORMAT
 	            " rt-src %s" /* protocol */
-	            "%s" /* cloned */
+	            "%s" /* rtm_flags */
 	            "%s%s" /* scope */
 	            "%s%s" /* pref-src */
 	            "%s" /* tos */
@@ -5033,7 +5205,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *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" : "",
+	            _rtm_flags_to_string_full (str_rtm_flags, sizeof (str_rtm_flags), route->r_rtm_flags),
 	            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 " : "",
@@ -5069,6 +5241,7 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi
 	char str_pref2[30];
 	char str_dev[TO_STRING_DEV_BUF_SIZE], s_source[50];
 	char str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32];
+	char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN];
 
 	if (!nm_utils_to_string_buffer_init_null (route, &buf, &len))
 		return buf;
@@ -5092,7 +5265,7 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi
 	            " mss %"G_GUINT32_FORMAT
 	            " rt-src %s" /* protocol */
 	            "%s" /* source */
-	            "%s" /* cloned */
+	            "%s" /* rtm_flags */
 	            "%s%s" /* pref-src */
 	            "%s" /* window */
 	            "%s" /* cwnd */
@@ -5112,7 +5285,7 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi
 	            route->src_plen || !IN6_IS_ADDR_UNSPECIFIED (&route->src)
 	              ? nm_sprintf_buf (s_src_all, " src %s/%u", nm_utils_inet6_ntop (&route->src, s_src), (unsigned) route->src_plen)
 	              : "",
-	            route->rt_cloned ? " cloned" : "",
+	            _rtm_flags_to_string_full (str_rtm_flags, sizeof (str_rtm_flags), route->r_rtm_flags),
 	            s_pref_src[0] ? " pref-src " : "",
 	            s_pref_src[0] ? s_pref_src : "",
 	            route->window   || route->lock_window   ? nm_sprintf_buf (str_window,   " window %s%"G_GUINT32_FORMAT,   route->lock_window   ? "lock " : "", route->window)   : "",
@@ -5125,6 +5298,129 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi
 	return buf;
 }
 
+const char *
+nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len)
+{
+	char str_dev[TO_STRING_DEV_BUF_SIZE];
+
+	if (!nm_utils_to_string_buffer_init_null (qdisc, &buf, &len))
+		return buf;
+
+	g_snprintf (buf, len, "%s%s family %d handle %x parent %x info %x",
+	            qdisc->kind,
+	            _to_string_dev (NULL, qdisc->ifindex, str_dev, sizeof (str_dev)),
+	            qdisc->addr_family,
+	            qdisc->handle,
+	            qdisc->parent,
+	            qdisc->info);
+
+	return buf;
+}
+
+void
+nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj, NMHashState *h)
+{
+	nm_hash_update_str (h, obj->kind);
+	nm_hash_update_vals (h,
+	                     obj->ifindex,
+	                     obj->addr_family,
+	                     obj->handle,
+	                     obj->parent,
+	                     obj->info);
+}
+
+int
+nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b)
+{
+	NM_CMP_SELF (a, b);
+	NM_CMP_FIELD (a, b, ifindex);
+	NM_CMP_FIELD (a, b, parent);
+	NM_CMP_FIELD_STR_INTERNED (a, b, kind);
+	NM_CMP_FIELD (a, b, addr_family);
+	NM_CMP_FIELD (a, b, handle);
+	NM_CMP_FIELD (a, b, info);
+
+	return 0;
+}
+
+const char *
+nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsize len)
+{
+	char str_dev[TO_STRING_DEV_BUF_SIZE];
+	char act_buf[300];
+	char *p;
+	gsize l;
+
+	if (!nm_utils_to_string_buffer_init_null (tfilter, &buf, &len))
+		return buf;
+
+	if (tfilter->action.kind) {
+		p = act_buf;
+		l = sizeof (act_buf);
+
+		nm_utils_strbuf_append (&p, &l, " \"%s\"", tfilter->action.kind);
+		if (nm_streq (tfilter->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) {
+			gs_free char *t = NULL;
+
+			nm_utils_strbuf_append (&p, &l,
+			                        " (\"%s\")",
+			                        nm_utils_str_utf8safe_escape (tfilter->action.kind,
+			                                                        NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL
+			                                                      | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII,
+			                                                      &t));
+		}
+	} else
+		act_buf[0] = '\0';
+
+	g_snprintf (buf, len, "%s%s family %d handle %x parent %x info %x%s",
+	            tfilter->kind,
+	            _to_string_dev (NULL, tfilter->ifindex, str_dev, sizeof (str_dev)),
+	            tfilter->addr_family,
+	            tfilter->handle,
+	            tfilter->parent,
+	            tfilter->info,
+	            act_buf);
+
+	return buf;
+}
+
+void
+nm_platform_tfilter_hash_update (const NMPlatformTfilter *obj, NMHashState *h)
+{
+	nm_hash_update_str (h, obj->kind);
+	nm_hash_update_vals (h,
+	                     obj->ifindex,
+	                     obj->addr_family,
+	                     obj->handle,
+	                     obj->parent,
+	                     obj->info);
+	nm_hash_update_str (h, obj->action.kind);
+	if (obj->action.kind) {
+		if (nm_streq (obj->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE))
+			nm_hash_update_str (h, obj->action.simple.sdata);
+	}
+}
+
+int
+nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b)
+{
+	NM_CMP_SELF (a, b);
+	NM_CMP_FIELD (a, b, ifindex);
+	NM_CMP_FIELD (a, b, parent);
+	NM_CMP_FIELD_STR_INTERNED (a, b, kind);
+	NM_CMP_FIELD (a, b, addr_family);
+	NM_CMP_FIELD (a, b, handle);
+	NM_CMP_FIELD (a, b, info);
+
+	NM_CMP_FIELD_STR_INTERNED (a, b, action.kind);
+	if (a->action.kind) {
+		if (nm_streq (a->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE))
+			NM_CMP_FIELD_STR (a, b, action.simple.sdata);
+	}
+
+	return 0;
+}
+
 void
 nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h)
 {
@@ -5538,6 +5834,7 @@ nm_platform_ip4_route_hash_update (const NMPlatformIP4Route *obj, NMPlatformIPRo
 		                     obj->initcwnd,
 		                     obj->initrwnd,
 		                     obj->mtu,
+		                     obj->r_rtm_flags & RTNH_F_ONLINK,
 		                     NM_HASH_COMBINE_BOOLS (guint8,
 		                                            obj->lock_window,
 		                                            obj->lock_cwnd,
@@ -5563,8 +5860,8 @@ nm_platform_ip4_route_hash_update (const NMPlatformIP4Route *obj, NMPlatformIPRo
 		                     obj->initcwnd,
 		                     obj->initrwnd,
 		                     obj->mtu,
+		                     obj->r_rtm_flags & (RTM_F_CLONED | RTNH_F_ONLINK),
 		                     NM_HASH_COMBINE_BOOLS (guint8,
-		                                            obj->rt_cloned,
 		                                            obj->lock_window,
 		                                            obj->lock_cwnd,
 		                                            obj->lock_initcwnd,
@@ -5589,8 +5886,8 @@ nm_platform_ip4_route_hash_update (const NMPlatformIP4Route *obj, NMPlatformIPRo
 		                     obj->initcwnd,
 		                     obj->initrwnd,
 		                     obj->mtu,
+		                     obj->r_rtm_flags,
 		                     NM_HASH_COMBINE_BOOLS (guint8,
-		                                            obj->rt_cloned,
 		                                            obj->lock_window,
 		                                            obj->lock_cwnd,
 		                                            obj->lock_initcwnd,
@@ -5627,6 +5924,8 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route
 			NM_CMP_FIELD (a, b, initcwnd);
 			NM_CMP_FIELD (a, b, initrwnd);
 			NM_CMP_FIELD (a, b, mtu);
+			NM_CMP_DIRECT (a->r_rtm_flags & RTNH_F_ONLINK,
+			               b->r_rtm_flags & RTNH_F_ONLINK);
 			NM_CMP_FIELD_UNSAFE (a, b, lock_window);
 			NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd);
 			NM_CMP_FIELD_UNSAFE (a, b, lock_initcwnd);
@@ -5660,7 +5959,11 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route
 		}
 		NM_CMP_FIELD (a, b, mss);
 		NM_CMP_FIELD (a, b, pref_src);
-		NM_CMP_FIELD_UNSAFE (a, b, rt_cloned);
+		if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
+			NM_CMP_DIRECT (a->r_rtm_flags & (RTM_F_CLONED | RTNH_F_ONLINK),
+			               b->r_rtm_flags & (RTM_F_CLONED | RTNH_F_ONLINK));
+		} else
+			NM_CMP_FIELD (a, b, r_rtm_flags);
 		NM_CMP_FIELD (a, b, tos);
 		NM_CMP_FIELD_UNSAFE (a, b, lock_window);
 		NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd);
@@ -5717,8 +6020,8 @@ nm_platform_ip6_route_hash_update (const NMPlatformIP6Route *obj, NMPlatformIPRo
 		                     obj->src_plen,
 		                     nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source),
 		                     obj->mss,
+		                     obj->r_rtm_flags & RTM_F_CLONED,
 		                     NM_HASH_COMBINE_BOOLS (guint8,
-		                                            obj->rt_cloned,
 		                                            obj->lock_window,
 		                                            obj->lock_cwnd,
 		                                            obj->lock_initcwnd,
@@ -5744,8 +6047,8 @@ nm_platform_ip6_route_hash_update (const NMPlatformIP6Route *obj, NMPlatformIPRo
 		                     obj->src_plen,
 		                     obj->rt_source,
 		                     obj->mss,
+		                     obj->r_rtm_flags,
 		                     NM_HASH_COMBINE_BOOLS (guint8,
-		                                            obj->rt_cloned,
 		                                            obj->lock_window,
 		                                            obj->lock_cwnd,
 		                                            obj->lock_initcwnd,
@@ -5810,7 +6113,11 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route
 			NM_CMP_FIELD (a, b, rt_source);
 		}
 		NM_CMP_FIELD (a, b, mss);
-		NM_CMP_FIELD_UNSAFE (a, b, rt_cloned);
+		if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
+			NM_CMP_DIRECT (a->r_rtm_flags & RTM_F_CLONED,
+			               b->r_rtm_flags & RTM_F_CLONED);
+		} else
+			NM_CMP_FIELD (a, b, r_rtm_flags);
 		NM_CMP_FIELD_UNSAFE (a, b, lock_window);
 		NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd);
 		NM_CMP_FIELD_UNSAFE (a, b, lock_initcwnd);
@@ -5927,6 +6234,18 @@ log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatform
 	_LOGD ("signal: route   6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_route_to_string (route, NULL, 0));
 }
 
+static void
+log_qdisc (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformQdisc *qdisc, NMPlatformSignalChangeType change_type, gpointer user_data)
+{
+	_LOGD ("signal: qdisc %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_qdisc_to_string (qdisc, NULL, 0));
+}
+
+static void
+log_tfilter (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformTfilter *tfilter, NMPlatformSignalChangeType change_type, gpointer user_data)
+{
+	_LOGD ("signal: tfilter %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_tfilter_to_string (tfilter, NULL, 0));
+}
+
 /*****************************************************************************/
 
 void
@@ -6200,4 +6519,6 @@ nm_platform_class_init (NMPlatformClass *platform_class)
 	SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address);
 	SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE,   NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED,   log_ip4_route);
 	SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE,   NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED,   log_ip6_route);
+	SIGNAL (NM_PLATFORM_SIGNAL_ID_QDISC,       NM_PLATFORM_SIGNAL_QDISC_CHANGED,       log_qdisc);
+	SIGNAL (NM_PLATFORM_SIGNAL_ID_TFILTER,     NM_PLATFORM_SIGNAL_TFILTER_CHANGED,     log_tfilter);
 }
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index d155c109..f6bf02bf 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.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) 2009 - 2010 Red Hat, Inc.
+ * Copyright (C) 2009 - 2017 Red Hat, Inc.
  */
 
 #ifndef __NETWORKMANAGER_PLATFORM_H__
@@ -236,6 +236,8 @@ typedef enum { /*< skip >*/
 	NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS,
 	NM_PLATFORM_SIGNAL_ID_IP4_ROUTE,
 	NM_PLATFORM_SIGNAL_ID_IP6_ROUTE,
+	NM_PLATFORM_SIGNAL_ID_QDISC,
+	NM_PLATFORM_SIGNAL_ID_TFILTER,
 	_NM_PLATFORM_SIGNAL_ID_LAST,
 } NMPlatformSignalIdType;
 
@@ -375,13 +377,6 @@ typedef union {
 	\
 	guint8 plen; \
 	\
-	/* the route has rtm_flags set to RTM_F_CLONED. Such a route
-	 * is hidden by platform and does not exist from the point-of-view
-	 * 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; \
-	\
-	\
 	/* RTA_METRICS:
 	 *
 	 * For IPv4 routes, these properties are part of their
@@ -401,6 +396,18 @@ typedef union {
 	bool lock_initrwnd:1; \
 	bool lock_mtu:1; \
 	\
+	/* rtnh_flags
+	 *
+	 * Routes with rtm_flags RTM_F_CLONED are hidden by platform and
+	 * do not exist from the point-of-view of platform users.
+	 * Such a route is not alive, according to nmp_object_is_alive().
+	 *
+	 * XXX: currently we ignore all flags except RTM_F_CLONED
+	 * and RTNH_F_ONLINK for IPv4.
+	 * We also may not properly consider the flags as part of the ID
+	 * in route-cmp. */ \
+	unsigned r_rtm_flags; \
+	\
 	/* RTA_METRICS.RTAX_ADVMSS (iproute2: advmss) */ \
 	guint32 mss; \
 	\
@@ -441,23 +448,11 @@ typedef struct {
 	};
 } NMPlatformIPRoute;
 
-#if _NM_CC_SUPPORT_GENERIC
-#define NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route) \
-	(_Generic ((route), \
-	           const NMPlatformIPRoute  *: ((const NMPlatformIPRoute *) (route))->plen, \
-	                 NMPlatformIPRoute  *: ((const NMPlatformIPRoute *) (route))->plen, \
-	           const NMPlatformIPXRoute *: ((const NMPlatformIPRoute *) (route))->plen, \
-	                 NMPlatformIPXRoute *: ((const NMPlatformIPRoute *) (route))->plen, \
-	           const NMPlatformIP4Route *: ((const NMPlatformIPRoute *) (route))->plen, \
-	                 NMPlatformIP4Route *: ((const NMPlatformIPRoute *) (route))->plen, \
-	           const NMPlatformIP6Route *: ((const NMPlatformIPRoute *) (route))->plen, \
-	                 NMPlatformIP6Route *: ((const NMPlatformIPRoute *) (route))->plen, \
-	           const void               *: ((const NMPlatformIPRoute *) (route))->plen, \
-	                 void               *: ((const NMPlatformIPRoute *) (route))->plen) == 0)
-#else
+#define NM_PLATFORM_IP_ROUTE_CAST(route) \
+	NM_CONSTCAST (NMPlatformIPRoute, (route), NMPlatformIPXRoute, NMPlatformIP4Route, NMPlatformIP6Route)
+
 #define NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route) \
-	( ((const NMPlatformIPRoute *) (route))->plen <= 0 )
-#endif
+	(NM_PLATFORM_IP_ROUTE_CAST (route)->plen <= 0)
 
 struct _NMPlatformIP4Route {
 	__NMPlatformIPRoute_COMMON;
@@ -534,6 +529,37 @@ typedef union {
 
 #undef __NMPlatformIPRoute_COMMON
 
+typedef struct {
+	__NMPlatformObject_COMMON;
+	const char *kind;
+	int addr_family;
+	guint32 handle;
+	guint32 parent;
+	guint32 info;
+} NMPlatformQdisc;
+
+typedef struct {
+	char sdata[32];
+} NMPlatformActionSimple;
+
+typedef struct {
+	const char *kind;
+	union {
+		NMPlatformActionSimple simple;
+	};
+} NMPlatformAction;
+
+#define NM_PLATFORM_ACTION_KIND_SIMPLE "simple"
+
+typedef struct {
+	__NMPlatformObject_COMMON;
+	const char *kind;
+	int addr_family;
+	guint32 handle;
+	guint32 parent;
+	guint32 info;
+	NMPlatformAction action;
+} NMPlatformTfilter;
 
 #undef __NMPlatformObject_COMMON
 
@@ -805,6 +831,8 @@ typedef struct {
 	gboolean    (*mesh_set_channel)      (NMPlatform *, int ifindex, guint32 channel);
 	gboolean    (*mesh_set_ssid)         (NMPlatform *, int ifindex, const guint8 *ssid, gsize len);
 
+	gboolean (*object_delete) (NMPlatform *, const NMPObject *obj);
+
 	gboolean (*ip4_address_add) (NMPlatform *,
 	                             int ifindex,
 	                             in_addr_t address,
@@ -829,14 +857,20 @@ typedef struct {
 	                                 NMPNlmFlags flags,
 	                                 int addr_family,
 	                                 const NMPlatformIPRoute *route);
-	gboolean (*ip_route_delete) (NMPlatform *, const NMPObject *obj);
-
 	NMPlatformError (*ip_route_get) (NMPlatform *self,
 	                                 int addr_family,
 	                                 gconstpointer address,
 	                                 int oif_ifindex,
 	                                 NMPObject **out_route);
 
+	NMPlatformError (*qdisc_add)   (NMPlatform *self,
+	                                NMPNlmFlags flags,
+	                                const NMPlatformQdisc *qdisc);
+
+	NMPlatformError (*tfilter_add)   (NMPlatform *self,
+	                                  NMPNlmFlags flags,
+	                                  const NMPlatformTfilter *tfilter);
+
 	NMPlatformKernelSupportFlags (*check_kernel_support) (NMPlatform * self,
 	                                                      NMPlatformKernelSupportFlags request_flags);
 } NMPlatformClass;
@@ -857,6 +891,8 @@ typedef struct {
 #define NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED "ip6-address-changed"
 #define NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED "ip4-route-changed"
 #define NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED "ip6-route-changed"
+#define NM_PLATFORM_SIGNAL_QDISC_CHANGED "qdisc-changed"
+#define NM_PLATFORM_SIGNAL_TFILTER_CHANGED "tfilter-changed"
 
 const char *nm_platform_signal_change_type_to_string (NMPlatformSignalChangeType change_type);
 
@@ -1194,6 +1230,8 @@ NMPlatformError nm_platform_link_sit_add (NMPlatform *self,
 
 const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address);
 
+gboolean nm_platform_object_delete (NMPlatform *self, const NMPObject *route);
+
 gboolean nm_platform_ip4_address_add (NMPlatform *self,
                                       int ifindex,
                                       in_addr_t address,
@@ -1228,8 +1266,6 @@ NMPlatformError nm_platform_ip_route_add (NMPlatform *self,
 NMPlatformError nm_platform_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route);
 NMPlatformError nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route);
 
-gboolean nm_platform_ip_route_delete (NMPlatform *self, const NMPObject *route);
-
 GPtrArray *nm_platform_ip_route_get_prune_list (NMPlatform *self,
                                                 int addr_family,
                                                 int ifindex,
@@ -1252,6 +1288,20 @@ NMPlatformError nm_platform_ip_route_get (NMPlatform *self,
                                           int oif_ifindex,
                                           NMPObject **out_route);
 
+NMPlatformError nm_platform_qdisc_add   (NMPlatform *self,
+                                         NMPNlmFlags flags,
+                                         const NMPlatformQdisc *qdisc);
+gboolean nm_platform_qdisc_sync         (NMPlatform *self,
+                                         int ifindex,
+                                         GPtrArray *known_qdiscs);
+
+NMPlatformError nm_platform_tfilter_add   (NMPlatform *self,
+                                           NMPNlmFlags flags,
+                                           const NMPlatformTfilter *tfilter);
+gboolean nm_platform_tfilter_sync         (NMPlatform *self,
+                                           int ifindex,
+                                           GPtrArray *known_tfilters);
+
 const char *nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len);
 const char *nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len);
 const char *nm_platform_lnk_infiniband_to_string (const NMPlatformLnkInfiniband *lnk, char *buf, gsize len);
@@ -1266,6 +1316,8 @@ const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *addre
 const char *nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *buf, gsize len);
 const char *nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsize len);
 const char *nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len);
+const char *nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len);
+const char *nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsize len);
 
 const char *nm_platform_vlan_qos_mapping_to_string (const char *name,
                                                     const NMVlanQosMapping *map,
@@ -1301,6 +1353,9 @@ nm_platform_ip6_route_cmp_full (const NMPlatformIP6Route *a, const NMPlatformIP6
 	return nm_platform_ip6_route_cmp (a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL);
 }
 
+int nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b);
+int nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b);
+
 void nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h);
 void nm_platform_ip4_address_hash_update (const NMPlatformIP4Address *obj, NMHashState *h);
 void nm_platform_ip6_address_hash_update (const NMPlatformIP6Address *obj, NMHashState *h);
@@ -1316,6 +1371,9 @@ void nm_platform_lnk_sit_hash_update (const NMPlatformLnkSit *obj, NMHashState *
 void nm_platform_lnk_vlan_hash_update (const NMPlatformLnkVlan *obj, NMHashState *h);
 void nm_platform_lnk_vxlan_hash_update (const NMPlatformLnkVxlan *obj, NMHashState *h);
 
+void nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj, NMHashState *h);
+void nm_platform_tfilter_hash_update (const NMPlatformTfilter *obj, NMHashState *h);
+
 NMPlatformKernelSupportFlags nm_platform_check_kernel_support (NMPlatform *self,
                                                                NMPlatformKernelSupportFlags request_flags);
 
diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c
index 34215828..bc305f01 100644
--- a/src/platform/nmp-netns.c
+++ b/src/platform/nmp-netns.c
@@ -290,6 +290,7 @@ _netns_new (GError **error)
 		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
 		             "Failed opening netns: %s",
 		             g_strerror (errsv));
+		errno = errsv;
 		return NULL;
 	}
 
@@ -300,6 +301,7 @@ _netns_new (GError **error)
 		             "Failed opening mntns: %s",
 		             g_strerror (errsv));
 		nm_close (fd_net);
+		errno = errsv;
 		return NULL;
 	}
 
@@ -479,6 +481,7 @@ nmp_netns_new (void)
 	if (!_stack_peek ()) {
 		/* there are no netns instances. We cannot create a new one
 		 * (because after unshare we couldn't return to the original one). */
+		errno = ENOTSUP;
 		return NULL;
 	}
 
@@ -508,6 +511,7 @@ nmp_netns_new (void)
 
 	self = _netns_new (&error);
 	if (!self) {
+		errsv = errno;
 		_LOGE (NULL, "failed to create netns after unshare: %s", error->message);
 		g_clear_error (&error);
 		goto err_out;
@@ -518,6 +522,7 @@ nmp_netns_new (void)
 	return self;
 err_out:
 	_netns_switch_pop (NULL, _CLONE_NS_ALL);
+	errno = errsv;
 	return NULL;
 }
 
diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c
index a8600705..1a9b9325 100644
--- a/src/platform/nmp-object.c
+++ b/src/platform/nmp-object.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) 2015 Red Hat, Inc.
+ * Copyright (C) 2015 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -186,11 +186,13 @@ _idx_obj_part (const DedupMultiIdxType *idx_type,
 		}
 		return 1;
 
-	case NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX:
+	case NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX:
 		if (   !NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_a), NMP_OBJECT_TYPE_IP4_ADDRESS,
 		                                                NMP_OBJECT_TYPE_IP6_ADDRESS,
 		                                                NMP_OBJECT_TYPE_IP4_ROUTE,
-		                                                NMP_OBJECT_TYPE_IP6_ROUTE)
+		                                                NMP_OBJECT_TYPE_IP6_ROUTE,
+		                                                NMP_OBJECT_TYPE_QDISC,
+		                                                NMP_OBJECT_TYPE_TFILTER)
 		    || !nmp_object_is_visible (obj_a)) {
 			if (h)
 				nm_hash_update_val (h, obj_a);
@@ -737,6 +739,8 @@ _vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d%s%s",
                                                                obj->peer_address != obj->address ? "," : "",
                                                                obj->peer_address != obj->address ? nm_utils_inet4_ntop (nm_utils_ip4_address_clear_host_address (obj->peer_address, obj->plen), buf2) : "");
 _vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s",        obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1));
+_vt_cmd_plobj_to_string_id (qdisc,       NMPlatformQdisc,      "%d: %d",        obj->ifindex, obj->parent);
+_vt_cmd_plobj_to_string_id (tfilter,     NMPlatformTfilter,    "%d: %d",        obj->ifindex, obj->parent);
 
 void
 nmp_object_hash_update (const NMPObject *obj, NMHashState *h)
@@ -1035,6 +1039,14 @@ _vt_cmd_plobj_id_cmp (ip6_address, NMPlatformIP6Address,
                       /* for IPv6 addresses, the prefix length is not part of the primary identifier. */
                       NM_CMP_FIELD_IN6ADDR (obj1, obj2, address);
 )
+_vt_cmd_plobj_id_cmp (qdisc, NMPlatformQdisc,
+                      NM_CMP_FIELD (obj1, obj2, ifindex);
+                      NM_CMP_FIELD (obj1, obj2, parent);
+)
+_vt_cmd_plobj_id_cmp (tfilter, NMPlatformTfilter,
+                      NM_CMP_FIELD (obj1, obj2, ifindex);
+                      NM_CMP_FIELD (obj1, obj2, handle);
+)
 
 static int
 _vt_cmd_plobj_id_cmp_ip4_route (const NMPlatformObject *obj1, const NMPlatformObject *obj2)
@@ -1113,6 +1125,16 @@ _vt_cmd_plobj_id_hash_update (ip4_route, NMPlatformIP4Route, {
 _vt_cmd_plobj_id_hash_update (ip6_route, NMPlatformIP6Route, {
 	nm_platform_ip6_route_hash_update (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h);
 })
+_vt_cmd_plobj_id_hash_update (qdisc, NMPlatformQdisc, {
+	nm_hash_update_vals (h,
+	                     obj->ifindex,
+	                     obj->parent);
+})
+_vt_cmd_plobj_id_hash_update (tfilter, NMPlatformTfilter, {
+	nm_hash_update_vals (h,
+	                     obj->ifindex,
+	                     obj->handle);
+})
 
 static inline void
 _vt_cmd_plobj_hash_update_ip4_route (const NMPlatformObject *obj, NMHashState *h)
@@ -1170,7 +1192,20 @@ _vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj)
 	 * Instead we create a dead object, and nmp_cache_update_netlink()
 	 * will remove the old version of the update.
 	 **/
-	return obj->object.ifindex > 0 && !obj->ip_route.rt_cloned;
+	return    obj->object.ifindex > 0
+	       && !NM_FLAGS_HAS (obj->ip_route.r_rtm_flags, RTM_F_CLONED);
+}
+
+static gboolean
+_vt_cmd_obj_is_alive_qdisc (const NMPObject *obj)
+{
+	return obj->object.ifindex > 0;
+}
+
+static gboolean
+_vt_cmd_obj_is_alive_tfilter (const NMPObject *obj)
+{
+	return obj->object.ifindex > 0;
 }
 
 gboolean
@@ -1202,6 +1237,12 @@ _vt_cmd_obj_is_visible_link (const NMPObject *obj)
 
 /*****************************************************************************/
 
+static const guint8 _supported_cache_ids_object[] = {
+	NMP_CACHE_ID_TYPE_OBJECT_TYPE,
+	NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX,
+	0,
+};
+
 static const guint8 _supported_cache_ids_link[] = {
 	NMP_CACHE_ID_TYPE_OBJECT_TYPE,
 	NMP_CACHE_ID_TYPE_LINK_BY_IFNAME,
@@ -1210,13 +1251,13 @@ static const guint8 _supported_cache_ids_link[] = {
 
 static const guint8 _supported_cache_ids_ipx_address[] = {
 	NMP_CACHE_ID_TYPE_OBJECT_TYPE,
-	NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX,
+	NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX,
 	0,
 };
 
 static const guint8 _supported_cache_ids_ipx_route[] = {
 	NMP_CACHE_ID_TYPE_OBJECT_TYPE,
-	NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX,
+	NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX,
 	NMP_CACHE_ID_TYPE_DEFAULT_ROUTES,
 	NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID,
 	0,
@@ -1501,6 +1542,8 @@ nmp_lookup_init_obj_type (NMPLookup *lookup,
 	case NMP_OBJECT_TYPE_IP6_ADDRESS:
 	case NMP_OBJECT_TYPE_IP4_ROUTE:
 	case NMP_OBJECT_TYPE_IP6_ROUTE:
+	case NMP_OBJECT_TYPE_QDISC:
+	case NMP_OBJECT_TYPE_TFILTER:
 		o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type);
 		lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_TYPE;
 		return _L (lookup);
@@ -1528,9 +1571,9 @@ nmp_lookup_init_link_by_ifname (NMPLookup *lookup,
 }
 
 const NMPLookup *
-nmp_lookup_init_addrroute (NMPLookup *lookup,
-                           NMPObjectType obj_type,
-                           int ifindex)
+nmp_lookup_init_object (NMPLookup *lookup,
+                        NMPObjectType obj_type,
+                        int ifindex)
 {
 	NMPObject *o;
 
@@ -1538,7 +1581,9 @@ nmp_lookup_init_addrroute (NMPLookup *lookup,
 	nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS,
 	                                NMP_OBJECT_TYPE_IP6_ADDRESS,
 	                                NMP_OBJECT_TYPE_IP4_ROUTE,
-	                                NMP_OBJECT_TYPE_IP6_ROUTE));
+	                                NMP_OBJECT_TYPE_IP6_ROUTE,
+	                                NMP_OBJECT_TYPE_QDISC,
+	                                NMP_OBJECT_TYPE_TFILTER));
 
 	if (ifindex <= 0) {
 		return nmp_lookup_init_obj_type (lookup,
@@ -1547,7 +1592,7 @@ nmp_lookup_init_addrroute (NMPLookup *lookup,
 
 	o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type);
 	o->object.ifindex = ifindex;
-	lookup->cache_id_type = NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX;
+	lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX;
 	return _L (lookup);
 }
 
@@ -2566,6 +2611,42 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = {
 		.cmd_plobj_hash_update              = _vt_cmd_plobj_hash_update_ip6_route,
 		.cmd_plobj_cmp                      = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp_full,
 	},
+	[NMP_OBJECT_TYPE_QDISC - 1] = {
+		.parent                             = DEDUP_MULTI_OBJ_CLASS_INIT(),
+		.obj_type                           = NMP_OBJECT_TYPE_QDISC,
+		.sizeof_data                        = sizeof (NMPObjectQdisc),
+		.sizeof_public                      = sizeof (NMPlatformQdisc),
+		.obj_type_name                      = "qdisc",
+		.rtm_gettype                        = RTM_GETQDISC,
+		.signal_type_id                     = NM_PLATFORM_SIGNAL_ID_QDISC,
+		.signal_type                        = NM_PLATFORM_SIGNAL_QDISC_CHANGED,
+		.supported_cache_ids                = _supported_cache_ids_object,
+		.cmd_obj_is_alive                   = _vt_cmd_obj_is_alive_qdisc,
+		.cmd_plobj_id_cmp                   = _vt_cmd_plobj_id_cmp_qdisc,
+		.cmd_plobj_id_hash_update           = _vt_cmd_plobj_id_hash_update_qdisc,
+		.cmd_plobj_to_string_id             = _vt_cmd_plobj_to_string_id_qdisc,
+		.cmd_plobj_to_string                = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_qdisc_to_string,
+		.cmd_plobj_hash_update              = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_qdisc_hash_update,
+		.cmd_plobj_cmp                      = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_qdisc_cmp,
+	},
+	[NMP_OBJECT_TYPE_TFILTER - 1] = {
+		.parent                             = DEDUP_MULTI_OBJ_CLASS_INIT(),
+		.obj_type                           = NMP_OBJECT_TYPE_TFILTER,
+		.sizeof_data                        = sizeof (NMPObjectTfilter),
+		.sizeof_public                      = sizeof (NMPlatformTfilter),
+		.obj_type_name                      = "tfilter",
+		.rtm_gettype                        = RTM_GETTFILTER,
+		.signal_type_id                     = NM_PLATFORM_SIGNAL_ID_TFILTER,
+		.signal_type                        = NM_PLATFORM_SIGNAL_TFILTER_CHANGED,
+		.supported_cache_ids                = _supported_cache_ids_object,
+		.cmd_obj_is_alive                   = _vt_cmd_obj_is_alive_tfilter,
+		.cmd_plobj_id_cmp                   = _vt_cmd_plobj_id_cmp_tfilter,
+		.cmd_plobj_id_hash_update           = _vt_cmd_plobj_id_hash_update_tfilter,
+		.cmd_plobj_to_string_id             = _vt_cmd_plobj_to_string_id_tfilter,
+		.cmd_plobj_to_string                = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_tfilter_to_string,
+		.cmd_plobj_hash_update              = (void (*) (const NMPlatformObject *obj, NMHashState *h)) nm_platform_tfilter_hash_update,
+		.cmd_plobj_cmp                      = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_tfilter_cmp,
+	},
 	[NMP_OBJECT_TYPE_LNK_GRE - 1] = {
 		.parent                             = DEDUP_MULTI_OBJ_CLASS_INIT(),
 		.obj_type                           = NMP_OBJECT_TYPE_LNK_GRE,
diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h
index 41fd08cb..e3862f2d 100644
--- a/src/platform/nmp-object.h
+++ b/src/platform/nmp-object.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) 2015 Red Hat, Inc.
+ * Copyright (C) 2015 - 2017 Red Hat, Inc.
  */
 
 #ifndef __NMP_OBJECT_H__
@@ -35,8 +35,8 @@ typedef enum { /*< skip >*/
 
 typedef enum { /*< skip >*/
 	NMP_CACHE_OPS_UNCHANGED       = NM_PLATFORM_SIGNAL_NONE,
-	NMP_CACHE_OPS_UPDATED         = NM_PLATFORM_SIGNAL_CHANGED,
 	NMP_CACHE_OPS_ADDED           = NM_PLATFORM_SIGNAL_ADDED,
+	NMP_CACHE_OPS_UPDATED         = NM_PLATFORM_SIGNAL_CHANGED,
 	NMP_CACHE_OPS_REMOVED         = NM_PLATFORM_SIGNAL_REMOVED,
 } NMPCacheOpsType;
 
@@ -48,12 +48,12 @@ typedef enum { /*< skip >*/
  * but only route objects can be indexed by NMP_CACHE_ID_TYPE_ROUTES_VISIBLE_NO_DEFAULT.
  *
  * Of one index type, there can be multiple indexes or not.
- * For example, of the index type NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX there
+ * For example, of the index type NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX there
  * are multiple instances (for different route/addresses, v4/v6, per-ifindex).
  *
  * But one object, can only be indexed by one particular index of a
  * type. For example, a certain address instance is only indexed by
- * the index NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX with
+ * the index NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX with
  * matching v4/v6 and ifindex -- or maybe not at all if it isn't visible.
  * */
 typedef enum { /*< skip >*/
@@ -82,8 +82,8 @@ typedef enum { /*< skip >*/
 	 * separate for IPv4 and IPv6. */
 	NMP_CACHE_ID_TYPE_DEFAULT_ROUTES,
 
-	/* all the addresses/routes (by object-type) for an ifindex. */
-	NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX,
+	/* all the objects that have an ifindex (by object-type) for an ifindex. */
+	NMP_CACHE_ID_TYPE_OBJECT_BY_IFINDEX,
 
 	/* Consider all the destination fields of a route, that is, the ID without the ifindex
 	 * and gateway (meaning: network/plen,metric).
@@ -225,6 +225,14 @@ typedef struct {
 	NMPlatformIP6Route _public;
 } NMPObjectIP6Route;
 
+typedef struct {
+	NMPlatformQdisc _public;
+} NMPObjectQdisc;
+
+typedef struct {
+	NMPlatformTfilter _public;
+} NMPObjectTfilter;
+
 struct _NMPObject {
 	union {
 		NMDedupMultiObj parent;
@@ -276,6 +284,11 @@ struct _NMPObject {
 		NMPlatformIP6Route      ip6_route;
 		NMPObjectIP4Route       _ip4_route;
 		NMPObjectIP6Route       _ip6_route;
+
+		NMPlatformQdisc         qdisc;
+		NMPObjectQdisc          _qdisc;
+		NMPlatformTfilter       tfilter;
+		NMPObjectTfilter        _tfilter;
 	};
 };
 
@@ -341,7 +354,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj)
 		typeof (obj) _obj = (obj); \
 		\
 		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_LINK); \
-		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->link : NULL; \
+		_obj ? &NM_CONSTCAST (NMPObject, _obj)->link : NULL; \
 	})
 
 #define NMP_OBJECT_CAST_IP_ADDRESS(obj) \
@@ -349,7 +362,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj)
 		typeof (obj) _obj = (obj); \
 		\
 		nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \
-		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip_address : NULL; \
+		_obj ? &NM_CONSTCAST (NMPObject, _obj)->ip_address : NULL; \
 	})
 
 #define NMP_OBJECT_CAST_IPX_ADDRESS(obj) \
@@ -357,7 +370,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj)
 		typeof (obj) _obj = (obj); \
 		\
 		nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \
-		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ipx_address : NULL; \
+		_obj ? &NM_CONSTCAST (NMPObject, _obj)->ipx_address : NULL; \
 	})
 
 #define NMP_OBJECT_CAST_IP4_ADDRESS(obj) \
@@ -365,7 +378,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj)
 		typeof (obj) _obj = (obj); \
 		\
 		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); \
-		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip4_address : NULL; \
+		_obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_address : NULL; \
 	})
 
 #define NMP_OBJECT_CAST_IP6_ADDRESS(obj) \
@@ -373,7 +386,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj)
 		typeof (obj) _obj = (obj); \
 		\
 		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); \
-		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip6_address : NULL; \
+		_obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_address : NULL; \
 	})
 
 #define NMP_OBJECT_CAST_IPX_ROUTE(obj) \
@@ -381,7 +394,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj)
 		typeof (obj) _obj = (obj); \
 		\
 		nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \
-		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ipx_route : NULL; \
+		_obj ? &NM_CONSTCAST (NMPObject, _obj)->ipx_route : NULL; \
 	})
 
 #define NMP_OBJECT_CAST_IP_ROUTE(obj) \
@@ -389,7 +402,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj)
 		typeof (obj) _obj = (obj); \
 		\
 		nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \
-		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip_route : NULL; \
+		_obj ? &NM_CONSTCAST (NMPObject, _obj)->ip_route : NULL; \
 	})
 
 #define NMP_OBJECT_CAST_IP4_ROUTE(obj) \
@@ -397,7 +410,7 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj)
 		typeof (obj) _obj = (obj); \
 		\
 		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP4_ROUTE); \
-		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip4_route : NULL; \
+		_obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_route : NULL; \
 	})
 
 #define NMP_OBJECT_CAST_IP6_ROUTE(obj) \
@@ -405,7 +418,23 @@ NMP_OBJECT_GET_TYPE (const NMPObject *obj)
 		typeof (obj) _obj = (obj); \
 		\
 		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_IP6_ROUTE); \
-		_obj ? &_NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \
+		_obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \
+	})
+
+#define NMP_OBJECT_CAST_QDISC(obj) \
+	({ \
+		typeof (obj) _obj = (obj); \
+		\
+		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_QDISC); \
+		_obj ? &NM_CONSTCAST (NMPObject, _obj)->qdisc : NULL; \
+	})
+
+#define NMP_OBJECT_CAST_TFILTER(obj) \
+	({ \
+		typeof (obj) _obj = (obj); \
+		\
+		nm_assert (!_obj || NMP_OBJECT_GET_TYPE ((const NMPObject *) _obj) == NMP_OBJECT_TYPE_TFILTER); \
+		_obj ? &NM_CONSTCAST (NMPObject, _obj)->tfilter : NULL; \
 	})
 
 const NMPClass *nmp_class_from_type (NMPObjectType obj_type);
@@ -536,9 +565,9 @@ const NMPLookup *nmp_lookup_init_obj_type (NMPLookup *lookup,
                                            NMPObjectType obj_type);
 const NMPLookup *nmp_lookup_init_link_by_ifname (NMPLookup *lookup,
                                                  const char *ifname);
-const NMPLookup *nmp_lookup_init_addrroute (NMPLookup *lookup,
-                                            NMPObjectType obj_type,
-                                            int ifindex);
+const NMPLookup *nmp_lookup_init_object (NMPLookup *lookup,
+                                         NMPObjectType obj_type,
+                                         int ifindex);
 const NMPLookup *nmp_lookup_init_route_default (NMPLookup *lookup,
                                                 NMPObjectType obj_type);
 const NMPLookup *nmp_lookup_init_route_by_weak_id (NMPLookup *lookup,
@@ -717,26 +746,26 @@ nm_platform_lookup_link_by_ifname (NMPlatform *platform,
 }
 
 static inline const NMDedupMultiHeadEntry *
-nm_platform_lookup_addrroute (NMPlatform *platform,
-                              NMPObjectType obj_type,
-                              int ifindex)
+nm_platform_lookup_object (NMPlatform *platform,
+                           NMPObjectType obj_type,
+                           int ifindex)
 {
 	NMPLookup lookup;
 
-	nmp_lookup_init_addrroute (&lookup, obj_type, ifindex);
+	nmp_lookup_init_object (&lookup, obj_type, ifindex);
 	return nm_platform_lookup (platform, &lookup);
 }
 
 static inline GPtrArray *
-nm_platform_lookup_addrroute_clone (NMPlatform *platform,
-                                    NMPObjectType obj_type,
-                                    int ifindex,
-                                    NMPObjectPredicateFunc predicate,
-                                    gpointer user_data)
+nm_platform_lookup_object_clone (NMPlatform *platform,
+                                 NMPObjectType obj_type,
+                                 int ifindex,
+                                 NMPObjectPredicateFunc predicate,
+                                 gpointer user_data)
 {
 	NMPLookup lookup;
 
-	nmp_lookup_init_addrroute (&lookup, obj_type, ifindex);
+	nmp_lookup_init_object (&lookup, obj_type, ifindex);
 	return nm_platform_lookup_clone (platform, &lookup, predicate, user_data);
 }
 
diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c
index 18076712..9a12f8f9 100644
--- a/src/platform/tests/test-common.c
+++ b/src/platform/tests/test-common.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 2016 Red Hat, Inc.
+ * Copyright 2016 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -78,9 +78,9 @@ _ipx_address_get_all (NMPlatform *self, int ifindex, NMPObjectType obj_type)
 	g_assert (NM_IS_PLATFORM (self));
 	g_assert (ifindex > 0);
 	g_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS));
-	nmp_lookup_init_addrroute (&lookup,
-	                           obj_type,
-	                           ifindex);
+	nmp_lookup_init_object (&lookup,
+	                        obj_type,
+	                        ifindex);
 	return nmp_cache_lookup_to_array (nm_platform_lookup (self, &lookup),
 	                                  obj_type,
 	                                  FALSE /*addresses are always visible. */);
@@ -108,9 +108,9 @@ nmtstp_platform_ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t n
 	nm_platform_process_events (platform);
 
 	nm_dedup_multi_iter_for_each (&iter,
-	                              nm_platform_lookup_addrroute (platform,
-	                                                            NMP_OBJECT_TYPE_IP4_ROUTE,
-	                                                            ifindex)) {
+	                              nm_platform_lookup_object (platform,
+	                                                         NMP_OBJECT_TYPE_IP4_ROUTE,
+	                                                         ifindex)) {
 		const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (iter.current->obj);
 
 		if (   r->ifindex != ifindex
@@ -120,7 +120,7 @@ nmtstp_platform_ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t n
 			continue;
 		}
 
-		return nm_platform_ip_route_delete (platform, NMP_OBJECT_UP_CAST (r));
+		return nm_platform_object_delete (platform, NMP_OBJECT_UP_CAST (r));
 	}
 
 	return TRUE;
@@ -134,9 +134,9 @@ nmtstp_platform_ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_
 	nm_platform_process_events (platform);
 
 	nm_dedup_multi_iter_for_each (&iter,
-	                              nm_platform_lookup_addrroute (platform,
-	                                                            NMP_OBJECT_TYPE_IP6_ROUTE,
-	                                                            ifindex)) {
+	                              nm_platform_lookup_object (platform,
+	                                                         NMP_OBJECT_TYPE_IP6_ROUTE,
+	                                                         ifindex)) {
 		const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (iter.current->obj);
 
 		if (   r->ifindex != ifindex
@@ -146,7 +146,7 @@ nmtstp_platform_ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_
 			continue;
 		}
 
-		return nm_platform_ip_route_delete (platform, NMP_OBJECT_UP_CAST (r));
+		return nm_platform_object_delete (platform, NMP_OBJECT_UP_CAST (r));
 	}
 
 	return TRUE;
@@ -1703,8 +1703,8 @@ nmtstp_namespace_create (int unshare_flags, GError **error)
 		errsv = errno;
 		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
 		             "pipe() failed with %d (%s)", errsv, strerror (errsv));
-		close (pipefd_c2p[0]);
-		close (pipefd_c2p[1]);
+		nm_close (pipefd_c2p[0]);
+		nm_close (pipefd_c2p[1]);
 		return FALSE;
 	}
 
@@ -1713,18 +1713,18 @@ nmtstp_namespace_create (int unshare_flags, GError **error)
 		errsv = errno;
 		g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
 		             "fork() failed with %d (%s)", errsv, strerror (errsv));
-		close (pipefd_c2p[0]);
-		close (pipefd_c2p[1]);
-		close (pipefd_p2c[0]);
-		close (pipefd_p2c[1]);
+		nm_close (pipefd_c2p[0]);
+		nm_close (pipefd_c2p[1]);
+		nm_close (pipefd_p2c[0]);
+		nm_close (pipefd_p2c[1]);
 		return FALSE;
 	}
 
 	if (pid == 0) {
 		char read_buf[1];
 
-		close (pipefd_c2p[0]); /* close read-end */
-		close (pipefd_p2c[1]); /* close write-end */
+		nm_close (pipefd_c2p[0]); /* close read-end */
+		nm_close (pipefd_p2c[1]); /* close write-end */
 
 		if (unshare (unshare_flags) != 0) {
 			errsv = errno;
@@ -1742,7 +1742,7 @@ nmtstp_namespace_create (int unshare_flags, GError **error)
 			if (errsv == 0)
 				errsv = -2;
 		}
-		close (pipefd_c2p[1]);
+		nm_close (pipefd_c2p[1]);
 
 		/* wait until parent process terminates (or kills us). */
 		if (errsv == 0) {
@@ -1750,19 +1750,19 @@ nmtstp_namespace_create (int unshare_flags, GError **error)
 				r = read (pipefd_p2c[0], read_buf, sizeof (read_buf));
 			} while (r < 0 && errno == EINTR);
 		}
-		close (pipefd_p2c[0]);
+		nm_close (pipefd_p2c[0]);
 		_exit (0);
 	}
 
-	close (pipefd_c2p[1]); /* close write-end */
-	close (pipefd_p2c[0]); /* close read-end */
+	nm_close (pipefd_c2p[1]); /* close write-end */
+	nm_close (pipefd_p2c[0]); /* close read-end */
 
 	/* sync with child process. */
 	do {
 		r = read (pipefd_c2p[0], &errsv, sizeof (errsv));
 	} while (r < 0 && errno == EINTR);
 
-	close (pipefd_c2p[0]);
+	nm_close (pipefd_c2p[0]);
 
 	if (   r != sizeof (errsv)
 	    || errsv != 0) {
@@ -1775,7 +1775,7 @@ nmtstp_namespace_create (int unshare_flags, GError **error)
 			g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
 			             "child process signaled failure %d (%s)", errsv, strerror (errsv));
 		}
-		close (pipefd_p2c[1]);
+		nm_close (pipefd_p2c[1]);
 		kill (pid, SIGKILL);
 		do {
 			pid2 = waitpid (pid, &status, 0);
@@ -1809,7 +1809,7 @@ nmtstp_namespace_handle_release (NMTstpNamespaceHandle *ns_handle)
 
 	g_return_if_fail (ns_handle->pid > 0);
 
-	close (ns_handle->pipe_fd);
+	nm_close (ns_handle->pipe_fd);
 	ns_handle->pipe_fd = 0;
 
 	kill (ns_handle->pid, SIGKILL);
diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h
index 4010aa2f..fb406a3f 100644
--- a/src/platform/tests/test-common.h
+++ b/src/platform/tests/test-common.h
@@ -227,22 +227,22 @@ static inline GPtrArray *
 nmtstp_ip4_route_get_all (NMPlatform *platform,
                           int ifindex)
 {
-	return nm_platform_lookup_addrroute_clone (platform,
-	                                           NMP_OBJECT_TYPE_IP4_ROUTE,
-	                                           ifindex,
-	                                           nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel,
-	                                           NULL);
+	return nm_platform_lookup_object_clone (platform,
+	                                        NMP_OBJECT_TYPE_IP4_ROUTE,
+	                                        ifindex,
+	                                        nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel,
+	                                        NULL);
 }
 
 static inline GPtrArray *
 nmtstp_ip6_route_get_all (NMPlatform *platform,
                           int ifindex)
 {
-	return nm_platform_lookup_addrroute_clone (platform,
-	                                           NMP_OBJECT_TYPE_IP6_ROUTE,
-	                                           ifindex,
-	                                           nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel,
-	                                           NULL);
+	return nm_platform_lookup_object_clone (platform,
+	                                        NMP_OBJECT_TYPE_IP6_ROUTE,
+	                                        ifindex,
+	                                        nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel,
+	                                        NULL);
 }
 
 /*****************************************************************************/
diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c
index 9c72371c..d3a10bd6 100644
--- a/src/platform/tests/test-link.c
+++ b/src/platform/tests/test-link.c
@@ -1916,6 +1916,7 @@ _test_netns_check_skip (void)
 	static int support = -1;
 	static int support_errsv = 0;
 	NMPNetns *netns;
+	gs_unref_object NMPNetns *netns2 = NULL;
 
 	netns = nmp_netns_get_current ();
 	if (!netns) {
@@ -1931,10 +1932,20 @@ _test_netns_check_skip (void)
 			support_errsv = errno;
 	}
 	if (!support) {
-			_LOGD ("setns() failed with \"%s\". This indicates missing support (valgrind?)", g_strerror (support_errsv));
-			g_test_skip ("No netns support (setns failed)");
+		_LOGD ("setns() failed with \"%s\". This indicates missing support (valgrind?)", g_strerror (support_errsv));
+		g_test_skip ("No netns support (setns failed)");
 		return TRUE;
 	}
+
+	netns2 = nmp_netns_new ();
+	if (!netns2) {
+		/* skip tests for https://bugzilla.gnome.org/show_bug.cgi?id=790214 */
+		g_assert_cmpint (errno, ==, EINVAL);
+		g_test_skip ("No netns support to create another netns");
+		return TRUE;
+	}
+	nmp_netns_pop (netns2);
+
 	return FALSE;
 }
 
diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c
index a02388d2..3228de83 100644
--- a/src/platform/tests/test-nmp-object.c
+++ b/src/platform/tests/test-nmp-object.c
@@ -15,12 +15,13 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2015 Red Hat, Inc.
+ * Copyright (C) 2015 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
 
 #include <libudev.h>
+#include <linux/pkt_sched.h>
 
 #include "platform/nmp-object.h"
 #include "nm-utils/nm-udev-utils.h"
@@ -454,6 +455,93 @@ test_cache_link (void)
 	nmp_cache_free (cache);
 }
 
+const char noqueue[] = "noqueue";
+const char fq_codel[] = "fq_codel";
+const char ingress[] = "ingress";
+
+static const NMPlatformQdisc pl_qdisc_1a = {
+	.ifindex = 1,
+	.kind = noqueue,
+	.addr_family = AF_UNSPEC,
+	.handle = 0,
+	.parent = TC_H_ROOT,
+	.info = 0,
+};
+
+static const NMPlatformQdisc pl_qdisc_1b = {
+	.ifindex = 1,
+	.kind = fq_codel,
+	.addr_family = AF_UNSPEC,
+	.handle = 0,
+	.parent = TC_H_ROOT,
+	.info = 0,
+};
+
+static const NMPlatformQdisc pl_qdisc_1c = {
+	.ifindex = 1,
+	.kind = ingress,
+	.addr_family = AF_UNSPEC,
+	.handle = TC_H_MAKE(TC_H_INGRESS, 0),
+	.parent = TC_H_INGRESS,
+	.info = 0,
+};
+
+static const NMPlatformQdisc pl_qdisc_2 = {
+	.ifindex = 2,
+	.kind = fq_codel,
+	.addr_family = AF_UNSPEC,
+	.handle = 0,
+	.parent = TC_H_ROOT,
+	.info = 0,
+};
+
+static void
+test_cache_qdisc (void)
+{
+	NMPCache *cache;
+	nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = NULL;
+	NMPLookup lookup;
+	const NMDedupMultiHeadEntry *head_entry;
+	nm_auto_nmpobj NMPObject *obj1a = nmp_object_new (NMP_OBJECT_TYPE_QDISC, (NMPlatformObject *) &pl_qdisc_1a);
+	nm_auto_nmpobj NMPObject *obj1b = nmp_object_new (NMP_OBJECT_TYPE_QDISC, (NMPlatformObject *) &pl_qdisc_1b);
+	nm_auto_nmpobj NMPObject *obj1c = nmp_object_new (NMP_OBJECT_TYPE_QDISC, (NMPlatformObject *) &pl_qdisc_1c);
+	nm_auto_nmpobj NMPObject *obj2 = nmp_object_new (NMP_OBJECT_TYPE_QDISC, (NMPlatformObject *) &pl_qdisc_2);
+
+	multi_idx = nm_dedup_multi_index_new ();
+	cache = nmp_cache_new (multi_idx, nmtst_get_rand_int () % 2);
+
+	g_assert (nmp_cache_lookup_obj (cache, obj1a) == NULL);
+
+	g_assert (nmp_cache_update_netlink (cache, obj1a, FALSE, NULL, NULL) == NMP_CACHE_OPS_ADDED);
+	g_assert (nmp_cache_lookup_obj (cache, obj1a) == obj1a);
+	g_assert (nmp_cache_lookup_obj (cache, obj1b) == obj1a);
+	g_assert (nmp_cache_lookup_obj (cache, obj2) == NULL);
+
+	g_assert (nmp_cache_update_netlink (cache, obj1b, FALSE, NULL, NULL) == NMP_CACHE_OPS_UPDATED);
+	g_assert (nmp_cache_lookup_obj (cache, obj1a) == obj1b);
+	g_assert (nmp_cache_lookup_obj (cache, obj1b) == obj1b);
+	g_assert (nmp_cache_lookup_obj (cache, obj2) == NULL);
+
+	g_assert (nmp_cache_update_netlink (cache, obj1c, FALSE, NULL, NULL) == NMP_CACHE_OPS_ADDED);
+	g_assert (nmp_cache_lookup_obj (cache, obj1a) == obj1b);
+	g_assert (nmp_cache_lookup_obj (cache, obj1b) == obj1b);
+	g_assert (nmp_cache_lookup_obj (cache, obj1c) == obj1c);
+	g_assert (nmp_cache_lookup_obj (cache, obj2) == NULL);
+
+	g_assert (nmp_cache_update_netlink (cache, obj2, FALSE, NULL, NULL) == NMP_CACHE_OPS_ADDED);
+	g_assert (nmp_cache_lookup_obj (cache, obj1a) == obj1b);
+	g_assert (nmp_cache_lookup_obj (cache, obj1b) == obj1b);
+	g_assert (nmp_cache_lookup_obj (cache, obj2) == obj2);
+
+	head_entry = nmp_cache_lookup (cache,
+	                               nmp_lookup_init_object (&lookup,
+	                                                       NMP_OBJECT_TYPE_QDISC,
+	                                                       1));
+	g_assert (head_entry->len == 2);
+
+	nmp_cache_free (cache);
+}
+
 /*****************************************************************************/
 
 NMTST_DEFINE ();
@@ -500,6 +588,7 @@ main (int argc, char **argv)
 
 	g_test_add_func ("/nmp-object/obj-base", test_obj_base);
 	g_test_add_func ("/nmp-object/cache_link", test_cache_link);
+	g_test_add_func ("/nmp-object/cache_qdisc", test_cache_qdisc);
 
 	result = g_test_run ();
 
diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c
index 2c00fada..fe97db07 100644
--- a/src/platform/tests/test-route.c
+++ b/src/platform/tests/test-route.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 2016 Red Hat, Inc.
+ * Copyright 2016 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -31,6 +31,42 @@
 #define EX             NMTSTP_ENV1_EX
 
 static void
+_wait_for_ipv4_addr_device_route (NMPlatform *platform,
+                                  gint64 timeout_ms,
+                                  int ifindex,
+                                  in_addr_t addr,
+                                  guint8 plen)
+{
+	/* Wait that the addresses gets a device-route. After adding a address,
+	 * the device route is not added immediately. It takes a moment... */
+
+	addr = nm_utils_ip4_address_clear_host_address (addr, plen);
+	NMTST_WAIT_ASSERT (400, {
+		NMDedupMultiIter iter;
+		NMPLookup lookup;
+		const NMPObject *o;
+
+		nmp_cache_iter_for_each (&iter,
+		                         nm_platform_lookup (platform,
+		                                             nmp_lookup_init_object (&lookup,
+		                                                                     NMP_OBJECT_TYPE_IP4_ROUTE,
+		                                                                     ifindex)),
+		                         &o) {
+			const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o);
+
+			if (   r->plen == plen
+			    && addr == nm_utils_ip4_address_clear_host_address (r->network, plen)
+			    && r->metric == 0
+			    && r->scope_inv == nm_platform_route_scope_inv (RT_SCOPE_LINK)
+			    && r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL)
+				return;
+		}
+		nmtstp_assert_wait_for_signal (platform,
+		                               (nmtst_wait_end_us - g_get_monotonic_time ()) / 1000);
+	});
+}
+
+static void
 _wait_for_ipv6_addr_non_tentative (NMPlatform *platform,
                                    gint64 timeout_ms,
                                    int ifindex,
@@ -44,7 +80,7 @@ _wait_for_ipv6_addr_non_tentative (NMPlatform *platform,
 	 * small amount of time, which prevents the immediate addition of the route
 	 * with RTA_PREFSRC */
 
-	NMTST_WAIT_ASSERT (400, {
+	NMTST_WAIT_ASSERT (timeout_ms, {
 		gboolean should_wait = FALSE;
 		const NMPlatformIP6Address *plt_addr;
 
@@ -63,7 +99,6 @@ _wait_for_ipv6_addr_non_tentative (NMPlatform *platform,
 	});
 }
 
-
 static void
 ip4_route_callback (NMPlatform *platform, int obj_type_i, int ifindex, const NMPlatformIP4Route *received, int change_type_i, SignalData *data)
 {
@@ -417,7 +452,7 @@ test_ip_route_get (void)
 	g_assert (!NMP_OBJECT_IS_STACKINIT (route));
 	g_assert (route->parent._ref_count == 1);
 	r = NMP_OBJECT_CAST_IP4_ROUTE (route);
-	g_assert (r->rt_cloned);
+	g_assert (NM_FLAGS_HAS (r->r_rtm_flags, RTM_F_CLONED));
 	g_assert (r->ifindex == ifindex);
 	g_assert (r->network == a);
 	g_assert (r->plen == 32);
@@ -448,63 +483,114 @@ test_ip4_zero_gateway (void)
 }
 
 static void
-test_ip4_route_options (void)
+test_ip4_route_options (gconstpointer test_data)
 {
-	int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME);
-	NMPlatformIP4Route route = { };
-	in_addr_t network;
-	GPtrArray *routes;
-	NMPlatformIP4Route rts[1];
+	const int TEST_IDX = GPOINTER_TO_INT (test_data);
+	const int IFINDEX = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME);
+	gs_unref_ptrarray GPtrArray *routes = NULL;
+#define RTS_MAX 3
+	NMPlatformIP4Route rts_add[RTS_MAX] = { };
+	NMPlatformIP4Route rts_cmp[RTS_MAX] = { };
+	NMPlatformIP4Address addr[1] = { };
+	guint i;
+	guint rts_n = 0;
+	guint addr_n = 0;
 
-	inet_pton (AF_INET, "172.16.1.0", &network);
+	switch (TEST_IDX) {
+	case 1:
+		rts_add[rts_n++] = ((NMPlatformIP4Route) {
+			.ifindex = IFINDEX,
+			.rt_source = NM_IP_CONFIG_SOURCE_USER,
+			.network = nmtst_inet4_from_string ("172.16.1.0"),
+			.plen = 24,
+			.metric = 20,
+			.tos = 0x28,
+			.window = 10000,
+			.cwnd = 16,
+			.initcwnd = 30,
+			.initrwnd = 50,
+			.mtu = 1350,
+			.lock_cwnd = TRUE,
+		});
+		break;
+	case 2:
+		addr[addr_n++] = ((NMPlatformIP4Address) {
+			.ifindex = IFINDEX,
+			.address = nmtst_inet4_from_string ("172.16.1.5"),
+			.peer_address = nmtst_inet4_from_string ("172.16.1.5"),
+			.plen = 24,
+			.lifetime = NM_PLATFORM_LIFETIME_PERMANENT,
+			.preferred = NM_PLATFORM_LIFETIME_PERMANENT,
+			.n_ifa_flags = 0,
+		});
+		rts_add[rts_n++] = ((NMPlatformIP4Route) {
+			.ifindex = IFINDEX,
+			.rt_source = NM_IP_CONFIG_SOURCE_USER,
+			.network = nmtst_inet4_from_string ("172.17.1.0"),
+			.gateway = nmtst_inet4_from_string ("172.16.1.1"),
+			.plen = 24,
+			.metric = 20,
+		});
+		rts_add[rts_n++] = ((NMPlatformIP4Route) {
+			.ifindex = IFINDEX,
+			.rt_source = NM_IP_CONFIG_SOURCE_USER,
+			.network = nmtst_inet4_from_string ("172.19.1.0"),
+			.gateway = nmtst_inet4_from_string ("172.18.1.1"),
+			.r_rtm_flags = RTNH_F_ONLINK,
+			.plen = 24,
+			.metric = 20,
+		});
+		break;
+	default:
+		g_assert_not_reached ();
+		break;
+	}
+	g_assert (rts_n <= G_N_ELEMENTS (rts_add));
+	g_assert (addr_n <= G_N_ELEMENTS (addr));
 
-	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;
+	for (i = 0; i < addr_n; i++) {
+		const NMPlatformIP4Address *a = &addr[i];
+
+		g_assert (a->ifindex == IFINDEX);
+		g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET,
+		                                       a->ifindex,
+		                                       a->address,
+		                                       a->plen,
+		                                       a->peer_address,
+		                                       a->lifetime,
+		                                       a->preferred,
+		                                       a->n_ifa_flags,
+		                                       a->label));
+		if (a->peer_address == a->address)
+			_wait_for_ipv4_addr_device_route (NM_PLATFORM_GET, 200, a->ifindex, a->address, a->plen);
+	}
 
-	g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &route) == NM_PLATFORM_ERROR_SUCCESS);
+	for (i = 0; i < rts_n; i++)
+		g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]) == NM_PLATFORM_ERROR_SUCCESS);
 
-	/* Test route listing */
-	routes = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex);
-	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_aptr ((const NMPObject *const*) routes->pdata, rts, routes->len, TRUE);
+	for (i = 0; i < rts_n; i++) {
+		rts_cmp[i] = rts_add[i];
+		nm_platform_ip_route_normalize (AF_INET, NM_PLATFORM_IP_ROUTE_CAST (&rts_cmp[i]));
+	}
 
-	/* Remove route */
-	g_assert (nm_platform_ip_route_delete (NM_PLATFORM_GET, routes->pdata[0]));
+	routes = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, IFINDEX);
+	g_assert_cmpint (routes->len, ==, rts_n);
+	nmtst_platform_ip4_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts_cmp, routes->len, TRUE);
 
-	g_ptr_array_unref (routes);
+	for (i = 0; i < rts_n; i++) {
+		g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, IFINDEX,
+		                                            rts_add[i].network, rts_add[i].plen,
+		                                            rts_add[i].metric));
+	}
+#undef RTS_MAX
 }
 
-
 static void
 test_ip6_route_options (gconstpointer test_data)
 {
 	const int TEST_IDX = GPOINTER_TO_INT (test_data);
 	const int IFINDEX = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME);
-	GPtrArray *routes;
+	gs_unref_ptrarray GPtrArray *routes = NULL;
 #define RTS_MAX 3
 	NMPlatformIP6Route rts_add[RTS_MAX] = { };
 	NMPlatformIP6Route rts_cmp[RTS_MAX] = { };
@@ -582,6 +668,8 @@ test_ip6_route_options (gconstpointer test_data)
 	default:
 		g_assert_not_reached ();
 	}
+	g_assert (rts_n <= G_N_ELEMENTS (rts_add));
+	g_assert (addr_n <= G_N_ELEMENTS (addr));
 
 	for (i = 0; i < addr_n; i++) {
 		g_assert (addr[i].ifindex == IFINDEX);
@@ -595,28 +683,19 @@ test_ip6_route_options (gconstpointer test_data)
 		                                       addr[i].preferred,
 		                                       addr[i].n_ifa_flags));
 	}
-
 	_wait_for_ipv6_addr_non_tentative (NM_PLATFORM_GET, 400, IFINDEX, addr_n, addr_in6);
 
 	for (i = 0; i < rts_n; i++)
 		g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]) == NM_PLATFORM_ERROR_SUCCESS);
 
-	routes = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, IFINDEX);
-	switch (TEST_IDX) {
-	case 1:
-	case 2:
-	case 3:
-		for (i = 0; i < rts_n; i++) {
-			rts_cmp[i] = rts_add[i];
-			rts_cmp[i].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER);
-		}
-		break;
-	default:
-		g_assert_not_reached ();
+	for (i = 0; i < rts_n; i++) {
+		rts_cmp[i] = rts_add[i];
+		nm_platform_ip_route_normalize (AF_INET6, NM_PLATFORM_IP_ROUTE_CAST (&rts_cmp[i]));
 	}
+
+	routes = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, IFINDEX);
 	g_assert_cmpint (routes->len, ==, rts_n);
 	nmtst_platform_ip6_routes_equal_aptr ((const NMPObject *const*) routes->pdata, rts_cmp, routes->len, TRUE);
-	g_ptr_array_unref (routes);
 
 	for (i = 0; i < rts_n; i++) {
 		g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, IFINDEX,
@@ -631,6 +710,7 @@ test_ip6_route_options (gconstpointer test_data)
 		                        rts_add[i].network,
 		                        rts_add[i].plen);
 	}
+#undef RTS_MAX
 }
 
 /*****************************************************************************/
@@ -714,7 +794,7 @@ again_find_idx:
 			order_len--;
 
 			r->ifindex = iface_data[idx].ifindex;
-			g_assert (nm_platform_ip_route_delete (platform, &o));
+			g_assert (nm_platform_object_delete (platform, &o));
 		}
 
 		head_entry = nm_platform_lookup (platform,
@@ -771,7 +851,9 @@ _nmtstp_setup_tests (void)
 	add_test_func ("/route/ip4", test_ip4_route);
 	add_test_func ("/route/ip6", test_ip6_route);
 	add_test_func ("/route/ip4_metric0", test_ip4_route_metric0);
-	add_test_func ("/route/ip4_options", test_ip4_route_options);
+	add_test_func_data ("/route/ip4_options/1", test_ip4_route_options, GINT_TO_POINTER (1));
+	if (nmtstp_is_root_test ())
+		add_test_func_data ("/route/ip4_options/2", test_ip4_route_options, GINT_TO_POINTER (2));
 	add_test_func_data ("/route/ip6_options/1", test_ip6_route_options, GINT_TO_POINTER (1));
 	add_test_func_data ("/route/ip6_options/2", test_ip6_route_options, GINT_TO_POINTER (2));
 	add_test_func_data ("/route/ip6_options/3", test_ip6_route_options, GINT_TO_POINTER (3));
diff --git a/src/ppp/nm-ppp-manager.c b/src/ppp/nm-ppp-manager.c
index 3ef3f3dc..7dd6a9b9 100644
--- a/src/ppp/nm-ppp-manager.c
+++ b/src/ppp/nm-ppp-manager.c
@@ -96,7 +96,7 @@ typedef struct {
 
 	NMActRequest *act_req;
 	GDBusMethodInvocation *pending_secrets_context;
-	NMActRequestGetSecretsCallId secrets_id;
+	NMActRequestGetSecretsCallId *secrets_id;
 	const char *secrets_setting_name;
 
 	guint ppp_watch_id;
@@ -287,7 +287,7 @@ extract_details_from_connection (NMConnection *connection,
 
 static void
 ppp_secrets_cb (NMActRequest *req,
-                NMActRequestGetSecretsCallId call_id,
+                NMActRequestGetSecretsCallId *call_id,
                 NMSettingsConnection *settings_connection, /* unused (we pass NULL here) */
                 GError *error,
                 gpointer user_data)
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c
index bcd17843..a68db47e 100644
--- a/src/settings/nm-agent-manager.c
+++ b/src/settings/nm-agent-manager.c
@@ -62,6 +62,8 @@ typedef struct {
 	GHashTable *agents;
 
 	CList requests;
+
+	guint64 agent_version_id;
 } NMAgentManagerPrivate;
 
 struct _NMAgentManager {
@@ -137,6 +139,16 @@ static gboolean _con_get_try_complete_early (Request *req);
 
 /*****************************************************************************/
 
+guint64
+nm_agent_manager_get_agent_version_id (NMAgentManager *self)
+{
+	g_return_val_if_fail (NM_IS_AGENT_MANAGER (self), 0);
+
+	return NM_AGENT_MANAGER_GET_PRIVATE (self)->agent_version_id;
+}
+
+/*****************************************************************************/
+
 typedef enum {
 	REQUEST_TYPE_INVALID,
 	REQUEST_TYPE_CON_GET,
@@ -170,7 +182,7 @@ struct _NMAgentManagerCallId {
 
 	/* Current agent being asked for secrets */
 	NMSecretAgent *current;
-	NMSecretAgentCallId current_call_id;
+	NMSecretAgentCallId *current_call_id;
 
 	/* Stores the sorted list of NMSecretAgents which will be asked for secrets */
 	GSList *pending;
@@ -336,6 +348,7 @@ agent_register_permissions_done (NMAuthChain *chain,
 		if (result == NM_AUTH_CALL_RESULT_YES)
 			nm_secret_agent_add_permission (agent, NM_AUTH_PERMISSION_WIFI_SHARE_OPEN, TRUE);
 
+		priv->agent_version_id += 1;
 		sender = nm_secret_agent_get_dbus_owner (agent);
 		g_hash_table_insert (priv->agents, g_strdup (sender), agent);
 		_LOGD (agent, "agent registered");
@@ -609,7 +622,7 @@ req_complete (Request *req,
 
 	nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &req->lst_request));
 
-	c_list_unlink_init (&req->lst_request);
+	c_list_unlink (&req->lst_request);
 
 	req_complete_release (req, secrets, agent_dbus_owner, agent_username, error);
 }
@@ -832,7 +845,7 @@ out:
 
 static void
 _con_get_request_done (NMSecretAgent *agent,
-                       NMSecretAgentCallId call_id,
+                       NMSecretAgentCallId *call_id,
                        GVariant *secrets,
                        GError *error,
                        gpointer user_data)
@@ -1213,7 +1226,7 @@ nm_agent_manager_get_secrets (NMAgentManager *self,
                               GVariant *existing_secrets,
                               const char *setting_name,
                               NMSecretAgentGetSecretsFlags flags,
-                              const char **hints,
+                              const char *const*hints,
                               NMAgentSecretsResultFunc callback,
                               gpointer callback_data)
 {
@@ -1267,7 +1280,7 @@ nm_agent_manager_cancel_secrets (NMAgentManager *self,
 
 	nm_assert (c_list_contains (&NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, &request_id->lst_request));
 
-	c_list_unlink_init (&request_id->lst_request);
+	c_list_unlink (&request_id->lst_request);
 
 	req_complete_cancel (request_id, FALSE);
 }
@@ -1276,7 +1289,7 @@ nm_agent_manager_cancel_secrets (NMAgentManager *self,
 
 static void
 _con_save_request_done (NMSecretAgent *agent,
-                        NMSecretAgentCallId call_id,
+                        NMSecretAgentCallId *call_id,
                         GVariant *secrets,
                         GError *error,
                         gpointer user_data)
@@ -1362,7 +1375,7 @@ nm_agent_manager_save_secrets (NMAgentManager *self,
 
 static void
 _con_del_request_done (NMSecretAgent *agent,
-                       NMSecretAgentCallId call_id,
+                       NMSecretAgentCallId *call_id,
                        GVariant *secrets,
                        GError *error,
                        gpointer user_data)
@@ -1558,6 +1571,7 @@ nm_agent_manager_init (NMAgentManager *self)
 {
 	NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self);
 
+	priv->agent_version_id = 1;
 	c_list_init (&priv->requests);
 	priv->agents = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
 }
@@ -1588,7 +1602,7 @@ dispose (GObject *object)
 
 cancel_more:
 	c_list_for_each (iter, &priv->requests) {
-		c_list_unlink_init (iter);
+		c_list_unlink (iter);
 		req_complete_cancel (c_list_entry (iter, Request, lst_request), TRUE);
 		goto cancel_more;
 	}
@@ -1627,7 +1641,7 @@ nm_agent_manager_class_init (NMAgentManagerClass *agent_manager_class)
 	object_class->dispose = dispose;
 
 	signals[AGENT_REGISTERED] =
-	    g_signal_new ("agent-registered",
+	    g_signal_new (NM_AGENT_MANAGER_AGENT_REGISTERED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0,
diff --git a/src/settings/nm-agent-manager.h b/src/settings/nm-agent-manager.h
index 7f812693..f6845818 100644
--- a/src/settings/nm-agent-manager.h
+++ b/src/settings/nm-agent-manager.h
@@ -33,6 +33,8 @@
 #define NM_IS_AGENT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_AGENT_MANAGER))
 #define NM_AGENT_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_AGENT_MANAGER, NMAgentManagerClass))
 
+#define NM_AGENT_MANAGER_AGENT_REGISTERED "agent-registered"
+
 typedef struct _NMAgentManagerCallId *NMAgentManagerCallId;
 
 typedef struct _NMAgentManagerClass NMAgentManagerClass;
@@ -41,6 +43,8 @@ GType nm_agent_manager_get_type (void);
 
 NMAgentManager *nm_agent_manager_get (void);
 
+guint64 nm_agent_manager_get_agent_version_id (NMAgentManager *self);
+
 /* If no agent fulfilled the secrets request, agent_dbus_owner will be NULL */
 typedef void (*NMAgentSecretsResultFunc) (NMAgentManager *manager,
                                           NMAgentManagerCallId call_id,
@@ -60,7 +64,7 @@ NMAgentManagerCallId nm_agent_manager_get_secrets (NMAgentManager *manager,
                                                    GVariant *existing_secrets,
                                                    const char *setting_name,
                                                    NMSecretAgentGetSecretsFlags flags,
-                                                   const char **hints,
+                                                   const char *const*hints,
                                                    NMAgentSecretsResultFunc callback,
                                                    gpointer callback_data);
 
diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c
index 5fe4dd17..192e9877 100644
--- a/src/settings/nm-secret-agent.c
+++ b/src/settings/nm-secret-agent.c
@@ -50,16 +50,13 @@ typedef struct {
 	char *owner_username;
 	char *dbus_owner;
 	NMSecretAgentCapabilities capabilities;
-
 	GSList *permissions;
-
 	NMDBusSecretAgent *proxy;
 	NMBusManager *bus_mgr;
 	GDBusConnection *connection;
-	gboolean connection_is_private;
-	gulong on_disconnected_id;
-
 	CList requests;
+	gulong on_disconnected_id;
+	bool connection_is_private:1;
 } NMSecretAgentPrivate;
 
 struct _NMSecretAgent {
@@ -99,6 +96,13 @@ G_DEFINE_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT)
 
 /*****************************************************************************/
 
+NM_UTILS_FLAGS2STR_DEFINE_STATIC (_capabilities_to_string, NMSecretAgentCapabilities,
+	NM_UTILS_FLAGS2STR (NM_SECRET_AGENT_CAPABILITY_NONE, "none"),
+	NM_UTILS_FLAGS2STR (NM_SECRET_AGENT_CAPABILITY_VPN_HINTS, "vpn-hints"),
+);
+
+/*****************************************************************************/
+
 struct _NMSecretAgentCallId {
 	CList lst;
 	NMSecretAgent *agent;
@@ -111,9 +115,7 @@ struct _NMSecretAgentCallId {
 	gpointer callback_data;
 };
 
-typedef struct _NMSecretAgentCallId Request;
-
-static Request *
+static NMSecretAgentCallId *
 request_new (NMSecretAgent *self,
              const char *dbus_command, /* this must be a static string. */
              const char *path,
@@ -121,9 +123,9 @@ request_new (NMSecretAgent *self,
              NMSecretAgentCallback callback,
              gpointer callback_data)
 {
-	Request *r;
+	NMSecretAgentCallId *r;
 
-	r = g_slice_new0 (Request);
+	r = g_slice_new0 (NMSecretAgentCallId);
 	r->agent = self;
 	r->path = g_strdup (path);
 	r->setting_name = g_strdup (setting_name);
@@ -139,21 +141,21 @@ request_new (NMSecretAgent *self,
 #define request_new(self,dbus_command,path,setting_name,callback,callback_data) request_new(self,""dbus_command"",path,setting_name,callback,callback_data)
 
 static void
-request_free (Request *r)
+request_free (NMSecretAgentCallId *r)
 {
 	NMSecretAgent *self = r->agent;
 
 	_LOGt ("request "LOG_REQ_FMT": destroyed", LOG_REQ_ARG (r));
-	c_list_unlink (&r->lst);
+	c_list_unlink_stale (&r->lst);
 	g_free (r->path);
 	g_free (r->setting_name);
 	if (r->cancellable)
 		g_object_unref (r->cancellable);
-	g_slice_free (Request, r);
+	g_slice_free (NMSecretAgentCallId, r);
 }
 
 static gboolean
-request_check_return (Request *r)
+request_check_return (NMSecretAgentCallId *r)
 {
 	if (!r->cancellable)
 		return FALSE;
@@ -163,7 +165,7 @@ request_check_return (Request *r)
 	nm_assert (c_list_contains (&NM_SECRET_AGENT_GET_PRIVATE (r->agent)->requests,
 	                            &r->lst));
 
-	c_list_unlink_init (&r->lst);
+	c_list_unlink (&r->lst);
 
 	return TRUE;
 }
@@ -329,7 +331,7 @@ get_callback (GObject *proxy,
               GAsyncResult *result,
               gpointer user_data)
 {
-	Request *r = user_data;
+	NMSecretAgentCallId *r = user_data;
 
 	if (request_check_return (r)) {
 		NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent);
@@ -345,7 +347,7 @@ get_callback (GObject *proxy,
 	request_free (r);
 }
 
-NMSecretAgentCallId
+NMSecretAgentCallId *
 nm_secret_agent_get_secrets (NMSecretAgent *self,
                              const char *path,
                              NMConnection *connection,
@@ -358,7 +360,7 @@ nm_secret_agent_get_secrets (NMSecretAgent *self,
 	NMSecretAgentPrivate *priv;
 	static const char *no_hints[] = { NULL };
 	GVariant *dict;
-	Request *r;
+	NMSecretAgentCallId *r;
 
 	g_return_val_if_fail (NM_IS_SECRET_AGENT (self), NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
@@ -411,7 +413,7 @@ cancel_done (GObject *proxy, GAsyncResult *result, gpointer user_data)
 }
 
 static void
-do_cancel_secrets (NMSecretAgent *self, Request *r, gboolean disposing)
+do_cancel_secrets (NMSecretAgent *self, NMSecretAgentCallId *r, gboolean disposing)
 {
 	NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
 	GCancellable *cancellable;
@@ -467,9 +469,9 @@ do_cancel_secrets (NMSecretAgent *self, Request *r, gboolean disposing)
  * callback before nm_secret_agent_cancel_secrets() returns.
  */
 void
-nm_secret_agent_cancel_secrets (NMSecretAgent *self, NMSecretAgentCallId call_id)
+nm_secret_agent_cancel_secrets (NMSecretAgent *self, NMSecretAgentCallId *call_id)
 {
-	Request *r = call_id;
+	NMSecretAgentCallId *r = call_id;
 
 	g_return_if_fail (NM_IS_SECRET_AGENT (self));
 	g_return_if_fail (r);
@@ -477,7 +479,7 @@ nm_secret_agent_cancel_secrets (NMSecretAgent *self, NMSecretAgentCallId call_id
 	nm_assert (c_list_contains (&NM_SECRET_AGENT_GET_PRIVATE (self)->requests,
 	                            &r->lst));
 
-	c_list_unlink_init (&r->lst);
+	c_list_unlink (&r->lst);
 
 	do_cancel_secrets (self, r, FALSE);
 }
@@ -489,7 +491,7 @@ agent_save_cb (GObject *proxy,
                GAsyncResult *result,
                gpointer user_data)
 {
-	Request *r = user_data;
+	NMSecretAgentCallId *r = user_data;
 
 	if (request_check_return (r)) {
 		NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent);
@@ -504,7 +506,7 @@ agent_save_cb (GObject *proxy,
 	request_free (r);
 }
 
-NMSecretAgentCallId
+NMSecretAgentCallId *
 nm_secret_agent_save_secrets (NMSecretAgent *self,
                               const char *path,
                               NMConnection *connection,
@@ -513,7 +515,7 @@ nm_secret_agent_save_secrets (NMSecretAgent *self,
 {
 	NMSecretAgentPrivate *priv;
 	GVariant *dict;
-	Request *r;
+	NMSecretAgentCallId *r;
 
 	g_return_val_if_fail (NM_IS_SECRET_AGENT (self), NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
@@ -541,7 +543,7 @@ agent_delete_cb (GObject *proxy,
                  GAsyncResult *result,
                  gpointer user_data)
 {
-	Request *r = user_data;
+	NMSecretAgentCallId *r = user_data;
 
 	if (request_check_return (r)) {
 		NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (r->agent);
@@ -556,7 +558,7 @@ agent_delete_cb (GObject *proxy,
 	request_free (r);
 }
 
-NMSecretAgentCallId
+NMSecretAgentCallId *
 nm_secret_agent_delete_secrets (NMSecretAgent *self,
                                 const char *path,
                                 NMConnection *connection,
@@ -565,7 +567,7 @@ nm_secret_agent_delete_secrets (NMSecretAgent *self,
 {
 	NMSecretAgentPrivate *priv;
 	GVariant *dict;
-	Request *r;
+	NMSecretAgentCallId *r;
 
 	g_return_val_if_fail (NM_IS_SECRET_AGENT (self), NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
@@ -668,6 +670,7 @@ nm_secret_agent_new (GDBusMethodInvocation *context,
 	char *owner_username = NULL;
 	char *description = NULL;
 	char buf_subject[64];
+	char buf_caps[150];
 	gulong uid;
 	GDBusConnection *connection;
 
@@ -696,12 +699,13 @@ nm_secret_agent_new (GDBusMethodInvocation *context,
 	priv->connection = g_object_ref (connection);
 	priv->connection_is_private = !!nm_bus_manager_connection_get_private_name (priv->bus_mgr, connection);
 
-	_LOGt ("constructed: %s, owner=%s%s%s (%s), private-connection=%d, unique-name=%s%s%s",
+	_LOGt ("constructed: %s, owner=%s%s%s (%s), private-connection=%d, unique-name=%s%s%s, capabilities=%s",
 	       (description = _create_description (dbus_owner, identifier, uid)),
 	       NM_PRINT_FMT_QUOTE_STRING (owner_username),
 	       nm_auth_subject_to_string (subject, buf_subject, sizeof (buf_subject)),
 	       priv->connection_is_private,
-	       NM_PRINT_FMT_QUOTE_STRING (g_dbus_connection_get_unique_name (priv->connection)));
+	       NM_PRINT_FMT_QUOTE_STRING (g_dbus_connection_get_unique_name (priv->connection)),
+	       _capabilities_to_string (capabilities, buf_caps, sizeof (buf_caps)));
 
 	priv->identifier = g_strdup (identifier);
 	priv->owner_username = owner_username;
@@ -759,8 +763,8 @@ dispose (GObject *object)
 
 again:
 	c_list_for_each (iter, &priv->requests) {
-		c_list_unlink_init (iter);
-		do_cancel_secrets (self, c_list_entry (iter, Request, lst), TRUE);
+		c_list_unlink (iter);
+		do_cancel_secrets (self, c_list_entry (iter, NMSecretAgentCallId, lst), TRUE);
 		goto again;
 	}
 
diff --git a/src/settings/nm-secret-agent.h b/src/settings/nm-secret-agent.h
index 54c5b398..64b103aa 100644
--- a/src/settings/nm-secret-agent.h
+++ b/src/settings/nm-secret-agent.h
@@ -33,8 +33,7 @@
 #define NM_SECRET_AGENT_DISCONNECTED "disconnected"
 
 typedef struct _NMSecretAgentClass NMSecretAgentClass;
-
-typedef struct _NMSecretAgentCallId *NMSecretAgentCallId;
+typedef struct _NMSecretAgentCallId NMSecretAgentCallId;
 
 GType nm_secret_agent_get_type (void);
 
@@ -67,12 +66,12 @@ gboolean    nm_secret_agent_has_permission (NMSecretAgent *agent,
                                             const char *permission);
 
 typedef void (*NMSecretAgentCallback) (NMSecretAgent *agent,
-                                       NMSecretAgentCallId call_id,
+                                       NMSecretAgentCallId *call_id,
                                        GVariant *new_secrets, /* NULL for save & delete */
                                        GError *error,
                                        gpointer user_data);
 
-NMSecretAgentCallId nm_secret_agent_get_secrets  (NMSecretAgent *agent,
+NMSecretAgentCallId *nm_secret_agent_get_secrets (NMSecretAgent *agent,
                                                   const char *path,
                                                   NMConnection *connection,
                                                   const char *setting_name,
@@ -82,18 +81,18 @@ NMSecretAgentCallId nm_secret_agent_get_secrets  (NMSecretAgent *agent,
                                                   gpointer callback_data);
 
 void        nm_secret_agent_cancel_secrets (NMSecretAgent *agent,
-                                            NMSecretAgentCallId call_id);
-
-NMSecretAgentCallId nm_secret_agent_save_secrets (NMSecretAgent *agent,
-                                                  const char *path,
-                                                  NMConnection *connection,
-                                                  NMSecretAgentCallback callback,
-                                                  gpointer callback_data);
-
-NMSecretAgentCallId nm_secret_agent_delete_secrets (NMSecretAgent *agent,
-                                                    const char *path,
-                                                    NMConnection *connection,
-                                                    NMSecretAgentCallback callback,
-                                                    gpointer callback_data);
+                                            NMSecretAgentCallId *call_id);
+
+NMSecretAgentCallId *nm_secret_agent_save_secrets (NMSecretAgent *agent,
+                                                   const char *path,
+                                                   NMConnection *connection,
+                                                   NMSecretAgentCallback callback,
+                                                   gpointer callback_data);
+
+NMSecretAgentCallId *nm_secret_agent_delete_secrets (NMSecretAgent *agent,
+                                                     const char *path,
+                                                     NMConnection *connection,
+                                                     NMSecretAgentCallback callback,
+                                                     gpointer callback_data);
 
 #endif /* __NETWORKMANAGER_SECRET_AGENT_H__ */
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index ed69115c..b6e89404 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -25,6 +25,8 @@
 
 #include <string.h>
 
+#include "nm-utils/c-list.h"
+
 #include "nm-common-macros.h"
 #include "nm-config.h"
 #include "nm-config-data.h"
@@ -42,9 +44,8 @@
 #define SETTINGS_TIMESTAMPS_FILE  NMSTATEDIR "/timestamps"
 #define SETTINGS_SEEN_BSSIDS_FILE NMSTATEDIR "/seen-bssids"
 
-#define AUTOCONNECT_RETRIES_UNSET       -2
-#define AUTOCONNECT_RETRIES_FOREVER     -1
-#define AUTOCONNECT_RETRIES_DEFAULT      4
+#define AUTOCONNECT_RETRIES_UNSET        -2
+#define AUTOCONNECT_RETRIES_FOREVER      -1
 #define AUTOCONNECT_RESET_RETRIES_TIMER 300
 
 /*****************************************************************************/
@@ -52,7 +53,6 @@
 static void nm_settings_connection_connection_interface_init (NMConnectionInterface *iface);
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMSettingsConnection,
-	PROP_VISIBLE,
 	PROP_UNSAVED,
 	PROP_READY,
 	PROP_FLAGS,
@@ -74,21 +74,18 @@ typedef struct _NMSettingsConnectionPrivate {
 	NMSessionMonitor *session_monitor;
 	gulong session_changed_id;
 
-	NMSettingsConnectionFlags flags;
+	NMSettingsConnectionFlags flags:5;
 
 	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;
+	NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:4;
 
 	GSList *pending_auths; /* List of pending authentication requests */
 
-	GSList *get_secret_requests;  /* in-progress secrets requests */
+	CList call_ids_lst_head; /* in-progress secrets requests */
 
 	/* Caches secrets from on-disk connections; were they not cached any
 	 * call to nm_connection_clear_secrets() wipes them out and we'd have
@@ -105,13 +102,17 @@ typedef struct _NMSettingsConnectionPrivate {
 	 */
 	NMConnection *agent_secrets;
 
-	guint64 timestamp;   /* Up-to-date timestamp of connection use */
+	char *filename;
+
 	GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
 
+	guint64 timestamp;   /* Up-to-date timestamp of connection use */
+
+	guint64 last_secret_agent_version_id;
+
 	int autoconnect_retries;
-	gint32 autoconnect_blocked_until;
+	gint32 autoconnect_retries_blocked_until;
 
-	char *filename;
 } NMSettingsConnectionPrivate;
 
 G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_EXPORTED_OBJECT,
@@ -170,6 +171,16 @@ nm_settings_connection_has_unmodified_applied_connection (NMSettingsConnection *
 
 /*****************************************************************************/
 
+guint64
+nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *self)
+{
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), 0);
+
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->last_secret_agent_version_id;
+}
+
+/*****************************************************************************/
+
 /* Return TRUE to keep, FALSE to drop */
 typedef gboolean (*ForEachSecretFunc) (NMSettingSecretFlags flags,
                                        gpointer user_data);
@@ -308,20 +319,9 @@ find_secret (NMConnection *self,
 static void
 set_visible (NMSettingsConnection *self, gboolean new_visible)
 {
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
-
-	if (new_visible == priv->visible)
-		return;
-	priv->visible = new_visible;
-	_notify (self, PROP_VISIBLE);
-}
-
-gboolean
-nm_settings_connection_is_visible (NMSettingsConnection *self)
-{
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
-
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->visible;
+	nm_settings_connection_set_flags (self,
+	                                  NM_SETTINGS_CONNECTION_FLAGS_VISIBLE,
+	                                  new_visible);
 }
 
 void
@@ -389,7 +389,8 @@ nm_settings_connection_check_permission (NMSettingsConnection *self,
 
 	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
-	if (priv->visible == FALSE)
+	if (!NM_FLAGS_HAS (nm_settings_connection_get_flags (self),
+	                   NM_SETTINGS_CONNECTION_FLAGS_VISIBLE))
 		return FALSE;
 
 	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
@@ -487,33 +488,87 @@ secrets_cleared_cb (NMSettingsConnection *self)
 }
 
 static void
-set_unsaved (NMSettingsConnection *self, gboolean now_unsaved)
+set_persist_mode (NMSettingsConnection *self, NMSettingsConnectionPersistMode persist_mode)
 {
-	NMSettingsConnectionFlags flags = nm_settings_connection_get_flags (self);
+	NMSettingsConnectionFlags flags = NM_SETTINGS_CONNECTION_FLAGS_NONE;
+	const NMSettingsConnectionFlags ALL =   NM_SETTINGS_CONNECTION_FLAGS_UNSAVED
+	                                      | NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED
+	                                      | NM_SETTINGS_CONNECTION_FLAGS_VOLATILE;
 
-	if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED) != !!now_unsaved) {
-		if (now_unsaved)
-			flags |= NM_SETTINGS_CONNECTION_FLAGS_UNSAVED;
-		else {
-			flags &= ~(NM_SETTINGS_CONNECTION_FLAGS_UNSAVED |
-			           NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED |
-			           NM_SETTINGS_CONNECTION_FLAGS_VOLATILE);
-		}
-		nm_settings_connection_set_flags_all (self, flags);
+	switch (persist_mode) {
+	case NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK:
+		flags = NM_SETTINGS_CONNECTION_FLAGS_NONE;
+		break;
+	case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY:
+	case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED:
+	case NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY:
+		flags = NM_SETTINGS_CONNECTION_FLAGS_UNSAVED;
+		break;
+	case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED:
+	case NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY:
+		flags = NM_SETTINGS_CONNECTION_FLAGS_UNSAVED |
+		        NM_SETTINGS_CONNECTION_FLAGS_VOLATILE;
+		break;
+	case NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED:
+		/* only set the connection as unsaved, but preserve the nm-generated
+		 * and volatile flag. */
+		nm_settings_connection_set_flags (self,
+		                                  NM_SETTINGS_CONNECTION_FLAGS_UNSAVED,
+		                                  TRUE);
+		return;
+	case NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP:
+		/* Nothing to do */
+		return;
 	}
+
+	nm_settings_connection_set_flags_full (self, ALL, flags);
 }
 
 static void
 connection_changed_cb (NMSettingsConnection *self, gpointer unused)
 {
-	set_unsaved (self, TRUE);
+	set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED);
 	_emit_updated (self, FALSE);
 }
 
-gboolean
-nm_settings_connection_replace_settings_prepare (NMSettingsConnection *self,
-                                                 NMConnection *new_connection,
-                                                 GError **error)
+static gboolean
+_delete (NMSettingsConnection *self, GError **error)
+{
+	NMSettingsConnectionClass *klass;
+	GError *local = NULL;
+	const char *filename;
+
+	nm_assert (NM_IS_SETTINGS_CONNECTION (self));
+
+	klass = NM_SETTINGS_CONNECTION_GET_CLASS (self);
+	if (!klass->delete) {
+		g_set_error (&local,
+		             NM_SETTINGS_ERROR,
+		             NM_SETTINGS_ERROR_FAILED,
+		             "delete not supported");
+		goto fail;
+	}
+	if (!klass->delete (self,
+	                    &local))
+		goto fail;
+
+	filename = nm_settings_connection_get_filename (self);
+	if (filename) {
+		_LOGD ("delete: success deleting connection (\"%s\")", filename);
+		nm_settings_connection_set_filename (self, NULL);
+	} else
+		_LOGT ("delete: success deleting connection (no-file)");
+	return TRUE;
+fail:
+	_LOGD ("delete: failure deleting connection: %s", local->message);
+	g_propagate_error (error, local);
+	return FALSE;
+}
+
+static gboolean
+_update_prepare (NMSettingsConnection *self,
+                 NMConnection *new_connection,
+                 GError **error)
 {
 	NMSettingsConnectionPrivate *priv;
 
@@ -538,173 +593,136 @@ nm_settings_connection_replace_settings_prepare (NMSettingsConnection *self,
 }
 
 gboolean
-nm_settings_connection_replace_settings_full (NMSettingsConnection *self,
-                                              NMConnection *new_connection,
-                                              gboolean prepare_new_connection,
-                                              gboolean update_unsaved,
-                                              const char *log_diff_name,
-                                              GError **error)
+nm_settings_connection_update (NMSettingsConnection *self,
+                               NMConnection *new_connection,
+                               NMSettingsConnectionPersistMode persist_mode,
+                               NMSettingsConnectionCommitReason commit_reason,
+                               const char *log_diff_name,
+                               GError **error)
 {
 	NMSettingsConnectionPrivate *priv;
+	NMSettingsConnectionClass *klass = NULL;
+	gs_unref_object NMConnection *reread_connection = NULL;
+	NMConnection *replace_connection;
+	gboolean replaced = FALSE;
+	gs_free char *logmsg_change = NULL;
+	GError *local = NULL;
 
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
-	g_return_val_if_fail (NM_IS_CONNECTION (new_connection), FALSE);
 
 	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
-	if (   prepare_new_connection
-	    && !nm_settings_connection_replace_settings_prepare (self,
-	                                                         new_connection,
-	                                                         error))
-		return FALSE;
+	if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
+		klass = NM_SETTINGS_CONNECTION_GET_CLASS (self);
+		if (!klass->commit_changes) {
+			g_set_error (&local,
+			             NM_SETTINGS_ERROR,
+			             NM_SETTINGS_ERROR_FAILED,
+			             "writing settings not supported");
+			goto out;
+		}
+	}
 
-	/* Do nothing if there's nothing to update */
-	if (nm_connection_compare (NM_CONNECTION (self),
-	                           new_connection,
-	                           NM_SETTING_COMPARE_FLAG_EXACT)) {
-		return TRUE;
+	if (   new_connection
+	    && !_update_prepare (self,
+	                         new_connection,
+	                         &local))
+		goto out;
+
+	if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
+		if (!klass->commit_changes (self,
+		                            new_connection ?: NM_CONNECTION (self),
+		                            commit_reason,
+		                            &reread_connection,
+		                            &logmsg_change,
+		                            &local))
+			goto out;
+
+		if (   reread_connection
+		    && !_update_prepare (self,
+		                         reread_connection,
+		                         &local))
+			goto out;
 	}
 
+	replace_connection = reread_connection ?: new_connection;
+
 	/* Disconnect the changed signal to ensure we don't set Unsaved when
 	 * it's not required.
 	 */
 	g_signal_handlers_block_by_func (self, G_CALLBACK (connection_changed_cb), NULL);
 
-	if (log_diff_name)
-		nm_utils_log_connection_diff (new_connection, NM_CONNECTION (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ");
+	/* Do nothing if there's nothing to update */
+	if (   replace_connection
+	    && !nm_connection_compare (NM_CONNECTION (self),
+	                               replace_connection,
+	                               NM_SETTING_COMPARE_FLAG_EXACT)) {
+		if (log_diff_name)
+			nm_utils_log_connection_diff (replace_connection, NM_CONNECTION (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ");
 
-	nm_connection_replace_settings_from_connection (NM_CONNECTION (self), new_connection);
+		nm_connection_replace_settings_from_connection (NM_CONNECTION (self), replace_connection);
 
-	_LOGD ("replace settings from connection %p (%s)", new_connection, nm_connection_get_id (NM_CONNECTION (self)));
+		replaced = TRUE;
+	}
 
 	nm_settings_connection_set_flags (self,
 	                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_FLAGS_VOLATILE,
 	                                  FALSE);
 
-	/* Cache the just-updated system secrets in case something calls
-	 * nm_connection_clear_secrets() and clears them.
-	 */
-	update_system_secrets_cache (self);
+	if (replaced) {
+		/* Cache the just-updated system secrets in case something calls
+		 * nm_connection_clear_secrets() and clears them.
+		 */
+		update_system_secrets_cache (self);
 
-	/* Add agent and always-ask secrets back; they won't necessarily be
-	 * in the replacement connection data if it was eg reread from disk.
-	 */
-	if (priv->agent_secrets) {
-		GVariant *dict;
+		/* Add agent and always-ask secrets back; they won't necessarily be
+		 * in the replacement connection data if it was eg reread from disk.
+		 */
+		if (priv->agent_secrets) {
+			GVariant *dict;
 
-		dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
-		if (dict) {
-			(void) nm_connection_update_secrets (NM_CONNECTION (self), NULL, dict, NULL);
-			g_variant_unref (dict);
+			dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+			if (dict) {
+				(void) nm_connection_update_secrets (NM_CONNECTION (self), NULL, dict, NULL);
+				g_variant_unref (dict);
+			}
 		}
 	}
 
 	nm_settings_connection_recheck_visibility (self);
 
-	/* Manually emit changed signal since we disconnected the handler, but
-	 * only update Unsaved if the caller wanted us to.
-	 */
-	if (update_unsaved)
-		set_unsaved (self, TRUE);
+	if (   replaced
+	    && persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP)
+		set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED);
+	else
+		set_persist_mode (self, persist_mode);
+
+	if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+	                             NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY))
+		_delete (self, NULL);
+	else if (NM_IN_SET (persist_mode, NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+	                                  NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED))
+		nm_settings_connection_set_filename (self, NULL);
 
 	g_signal_handlers_unblock_by_func (self, G_CALLBACK (connection_changed_cb), NULL);
 
 	_emit_updated (self, TRUE);
 
-	return TRUE;
-}
-
-/* Update the settings of this connection to match that of 'new_connection',
- * taking care to make a private copy of secrets.
- */
-gboolean
-nm_settings_connection_replace_settings (NMSettingsConnection *self,
-                                         NMConnection *new_connection,
-                                         gboolean update_unsaved,
-                                         const char *log_diff_name,
-                                         GError **error)
-{
-	return nm_settings_connection_replace_settings_full (self,
-	                                                     new_connection,
-	                                                     TRUE,
-	                                                     update_unsaved,
-	                                                     log_diff_name,
-	                                                     error);
-}
-
-gboolean
-nm_settings_connection_commit_changes (NMSettingsConnection *self,
-                                       NMConnection *new_connection,
-                                       NMSettingsConnectionCommitReason commit_reason,
-                                       GError **error)
-{
-	NMSettingsConnectionClass *klass;
-	gs_free_error GError *local = NULL;
-	gs_unref_object NMConnection *reread_connection = NULL;
-	gs_free char *logmsg_change = NULL;
-
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
-
-	klass = NM_SETTINGS_CONNECTION_GET_CLASS (self);
-	if (!klass->commit_changes) {
-		_LOGW ("write: setting plugin %s does not support to write connection",
-		       G_OBJECT_TYPE_NAME (self));
-		g_set_error (error,
-		             NM_SETTINGS_ERROR,
-		             NM_SETTINGS_ERROR_FAILED,
-		             "writing settings not supported");
-		return FALSE;
-	}
-
-	if (   new_connection
-	    && !nm_settings_connection_replace_settings_prepare (self,
-	                                                         new_connection,
-	                                                         &local)) {
-		_LOGW ("write: failed to prepare connection for writing: %s",
-		       local->message);
-		g_propagate_error (error, g_steal_pointer (&local));
-		return FALSE;
-	}
-
-	if (!klass->commit_changes (self,
-	                            new_connection,
-	                            commit_reason,
-	                            &reread_connection,
-	                            &logmsg_change,
-	                            &local)) {
-		_LOGW ("write: failure to write setting: %s",
-		       local->message);
-		g_propagate_error (error, g_steal_pointer (&local));
+out:
+	if (local) {
+		_LOGI ("write: failure to update connection: %s", local->message);
+		g_propagate_error (error, local);
 		return FALSE;
 	}
 
-	if (reread_connection || new_connection) {
-		if (!nm_settings_connection_replace_settings_full (self,
-		                                                   reread_connection ?: new_connection,
-		                                                   !reread_connection,
-		                                                   FALSE,
-		                                                   new_connection
-		                                                     ? "update-during-write"
-		                                                     : "replace-and-commit-disk",
-		                                                   &local)) {
-			/* this can't really happen, because at this point replace-settings
-			 * is no longer supposed to fail. It's a bug. */
-			_LOGE ("write: replacing setting failed: %s",
-			       local->message);
-			g_propagate_error (error, g_steal_pointer (&local));
-			g_return_val_if_reached (FALSE);
-		}
+	if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
+		if (reread_connection)
+			_LOGI ("write: successfully updated (%s), connection was modified in the process", logmsg_change);
+		else if (new_connection)
+			_LOGI ("write: successfully updated (%s)", logmsg_change);
+		else
+			_LOGI ("write: successfully commited (%s)", logmsg_change);
 	}
-
-	set_unsaved (self, FALSE);
-
-	if (reread_connection)
-		_LOGI ("write: successfully updated (%s), connection was modified in the process", logmsg_change);
-	else if (new_connection)
-		_LOGI ("write: successfully updated (%s)", logmsg_change);
-	else
-		_LOGI ("write: successfully commited (%s)", logmsg_change);
-
 	return TRUE;
 }
 
@@ -749,25 +767,14 @@ nm_settings_connection_delete (NMSettingsConnection *self,
                                GError **error)
 {
 	gs_unref_object NMSettingsConnection *self_keep_alive = NULL;
-	NMSettingsConnectionClass *klass;
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	NMConnection *for_agents;
 
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), FALSE);
 
-	klass = NM_SETTINGS_CONNECTION_GET_CLASS (self);
-
 	self_keep_alive = g_object_ref (self);
 
-	if (!klass->delete) {
-		g_set_error (error,
-		             NM_SETTINGS_ERROR,
-		             NM_SETTINGS_ERROR_FAILED,
-		             "delete not supported");
-		return FALSE;
-	}
-	if (!klass->delete (self,
-	                    error))
+	if (!_delete (self, error))
 		return FALSE;
 
 	set_visible (self, FALSE);
@@ -795,18 +802,19 @@ nm_settings_connection_delete (NMSettingsConnection *self,
 
 
 typedef enum {
-	GET_SECRETS_INFO_TYPE_REQ,
-	GET_SECRETS_INFO_TYPE_IDLE,
-} GetSecretsInfoType;
+	CALL_ID_TYPE_REQ,
+	CALL_ID_TYPE_IDLE,
+} CallIdType;
 
 struct _NMSettingsConnectionCallId {
 	NMSettingsConnection *self;
+	CList call_ids_lst;
 	gboolean had_applied_connection;
 	NMConnection *applied_connection;
 	NMSettingsConnectionSecretsFunc callback;
 	gpointer callback_data;
 
-	GetSecretsInfoType type;
+	CallIdType type;
 	union {
 		struct {
 			NMAgentManagerCallId id;
@@ -818,59 +826,36 @@ struct _NMSettingsConnectionCallId {
 	} t;
 };
 
-typedef struct _NMSettingsConnectionCallId GetSecretsInfo;
-
-static GetSecretsInfo *
-_get_secrets_info_new (NMSettingsConnection *self,
-                       NMConnection *applied_connection,
-                       NMSettingsConnectionSecretsFunc callback,
-                       gpointer callback_data)
-{
-	GetSecretsInfo *info;
-
-	info = g_slice_new0 (GetSecretsInfo);
-
-	info->self = self;
-	if (applied_connection) {
-		info->had_applied_connection = TRUE;
-		info->applied_connection = applied_connection;
-		g_object_add_weak_pointer (G_OBJECT (applied_connection), (gpointer *) &info->applied_connection);
-	}
-	info->callback = callback;
-	info->callback_data = callback_data;
-
-	return info;
-}
-
 static void
-_get_secrets_info_callback (GetSecretsInfo *info,
+_get_secrets_info_callback (NMSettingsConnectionCallId *call_id,
                             const char *agent_username,
                             const char *setting_name,
                             GError *error)
 {
-	if (info->callback) {
-		info->callback (info->self,
-		                info,
-		                agent_username,
-		                setting_name,
-		                error,
-		                info->callback_data);
+	if (call_id->callback) {
+		call_id->callback (call_id->self,
+		                   call_id,
+		                   agent_username,
+		                   setting_name,
+		                   error,
+		                   call_id->callback_data);
 	}
 }
 
 static void
-_get_secrets_info_free (GetSecretsInfo *info)
+_get_secrets_info_free (NMSettingsConnectionCallId *call_id)
 {
-	g_return_if_fail (info && info->self);
+	g_return_if_fail (call_id && call_id->self);
+	nm_assert (!c_list_is_linked (&call_id->call_ids_lst));
 
-	if (info->applied_connection)
-		g_object_remove_weak_pointer (G_OBJECT (info->applied_connection), (gpointer *) &info->applied_connection);
+	if (call_id->applied_connection)
+		g_object_remove_weak_pointer (G_OBJECT (call_id->applied_connection), (gpointer *) &call_id->applied_connection);
 
-	if (info->type == GET_SECRETS_INFO_TYPE_IDLE)
-		g_clear_error (&info->t.idle.error);
+	if (call_id->type == CALL_ID_TYPE_IDLE)
+		g_clear_error (&call_id->t.idle.error);
 
-	memset (info, 0, sizeof (*info));
-	g_slice_free (GetSecretsInfo, info);
+	memset (call_id, 0, sizeof (*call_id));
+	g_slice_free (NMSettingsConnectionCallId, call_id);
 }
 
 static gboolean
@@ -907,7 +892,7 @@ secret_is_system_owned (NMSettingSecretFlags flags,
 
 static void
 get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */
-               GetSecretsInfo *info, /* only needed for logging */
+               NMSettingsConnectionCallId *call_id, /* only needed for logging */
                NMConnection *connection,
                const char *agent_dbus_owner,
                gboolean agent_has_modify,
@@ -930,7 +915,7 @@ get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */
 		if (is_self) {
 			_LOGD ("(%s:%p) secrets returned from agent %s",
 			       setting_name,
-			       info,
+			       call_id,
 			       agent_dbus_owner);
 		}
 
@@ -949,7 +934,7 @@ get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */
 				if (is_self) {
 					_LOGD ("(%s:%p) interaction forbidden but agent %s returned system secrets",
 					       setting_name,
-					       info,
+					       call_id,
 					       agent_dbus_owner);
 				}
 
@@ -961,7 +946,7 @@ get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */
 				if (is_self) {
 					_LOGD ("(%s:%p) agent failed to authenticate but provided system secrets",
 					       setting_name,
-					       info);
+					       call_id);
 				}
 
 				cmp_flags->required |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;
@@ -971,7 +956,7 @@ get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */
 		if (is_self) {
 			_LOGD ("(%s:%p) existing secrets returned",
 			       setting_name,
-			       info);
+			       call_id);
 		}
 	}
 
@@ -1004,10 +989,12 @@ nm_settings_connection_new_secrets (NMSettingsConnection *self,
 	update_system_secrets_cache (self);
 	update_agent_secrets_cache (self, NULL);
 
-	nm_settings_connection_commit_changes (self,
-	                                       NULL,
-	                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
-	                                       NULL);
+	nm_settings_connection_update (self,
+	                               NULL,
+	                               NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+	                               NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+	                               "new-secrets",
+	                               NULL);
 	return TRUE;
 }
 
@@ -1023,7 +1010,7 @@ get_secrets_done_cb (NMAgentManager *manager,
                      GError *error,
                      gpointer user_data)
 {
-	GetSecretsInfo *info = user_data;
+	NMSettingsConnectionCallId *call_id = user_data;
 	NMSettingsConnection *self;
 	NMSettingsConnectionPrivate *priv;
 	NMConnection *applied_connection;
@@ -1035,36 +1022,36 @@ get_secrets_done_cb (NMAgentManager *manager,
 	if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
 		return;
 
-	self = info->self;
+	self = call_id->self;
 	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
 
 	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
-	g_return_if_fail (g_slist_find (priv->get_secret_requests, info));
+	nm_assert (c_list_contains (&priv->call_ids_lst_head, &call_id->call_ids_lst));
 
-	priv->get_secret_requests = g_slist_remove (priv->get_secret_requests, info);
+	c_list_unlink (&call_id->call_ids_lst);
 
 	if (error) {
 		_LOGD ("(%s:%p) secrets request error: %s",
-		       setting_name, info, error->message);
+		       setting_name, call_id, error->message);
 
-		_get_secrets_info_callback (info, NULL, setting_name, error);
+		_get_secrets_info_callback (call_id, NULL, setting_name, error);
 		goto out;
 	}
 
-	if (   info->had_applied_connection
-	    && !info->applied_connection) {
+	if (   call_id->had_applied_connection
+	    && !call_id->applied_connection) {
 		g_set_error_literal (&local, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
 		                     "Applied connection deleted since requesting secrets");
-		_get_secrets_info_callback (info, NULL, setting_name, local);
+		_get_secrets_info_callback (call_id, NULL, setting_name, local);
 		goto out;
 	}
 
-	if (   info->had_applied_connection
-	    && !nm_settings_connection_has_unmodified_applied_connection (self, info->applied_connection, NM_SETTING_COMPARE_FLAG_NONE)) {
+	if (   call_id->had_applied_connection
+	    && !nm_settings_connection_has_unmodified_applied_connection (self, call_id->applied_connection, NM_SETTING_COMPARE_FLAG_NONE)) {
 		g_set_error_literal (&local, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		                     "The connection was modified since activation");
-		_get_secrets_info_callback (info, NULL, setting_name, local);
+		_get_secrets_info_callback (call_id, NULL, setting_name, local);
 		goto out;
 	}
 
@@ -1072,12 +1059,12 @@ get_secrets_done_cb (NMAgentManager *manager,
 		g_set_error (&local, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
 		             "Connection didn't have requested setting '%s'.",
 		             setting_name);
-		_get_secrets_info_callback (info, NULL, setting_name, local);
+		_get_secrets_info_callback (call_id, NULL, setting_name, local);
 		goto out;
 	}
 
 	get_cmp_flags (self,
-	               info,
+	               call_id,
 	               NM_CONNECTION (self),
 	               agent_dbus_owner,
 	               agent_has_modify,
@@ -1089,7 +1076,7 @@ get_secrets_done_cb (NMAgentManager *manager,
 
 	_LOGD ("(%s:%p) secrets request completed",
 	       setting_name,
-	       info);
+	       call_id);
 
 	dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
 
@@ -1119,36 +1106,38 @@ get_secrets_done_cb (NMAgentManager *manager,
 			if (agent_had_system) {
 				_LOGD ("(%s:%p) saving new secrets to backing storage",
 				       setting_name,
-				       info);
-
-				nm_settings_connection_commit_changes (self,
-				                                       NULL,
-				                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
-				                                       NULL);
+				       call_id);
+
+				nm_settings_connection_update (self,
+				                               NULL,
+				                               NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+				                               NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+				                               "get-new-secrets",
+				                               NULL);
 			} else {
 				_LOGD ("(%s:%p) new agent secrets processed",
 				       setting_name,
-				       info);
+				       call_id);
 			}
 
 		} else {
 			_LOGD ("(%s:%p) failed to update with agent secrets: %s",
 			       setting_name,
-			       info,
+			       call_id,
 			       local->message);
 		}
 		g_variant_unref (filtered_secrets);
 	} else {
 		_LOGD ("(%s:%p) failed to update with existing secrets: %s",
 		       setting_name,
-		       info,
+		       call_id,
 		       local->message);
 	}
 
-	applied_connection = info->applied_connection;
+	applied_connection = call_id->applied_connection;
 	if (applied_connection) {
 		get_cmp_flags (self,
-		               info,
+		               call_id,
 		               applied_connection,
 		               agent_dbus_owner,
 		               agent_has_modify,
@@ -1169,31 +1158,31 @@ get_secrets_done_cb (NMAgentManager *manager,
 		}
 	}
 
-	_get_secrets_info_callback (info, agent_username, setting_name, local);
+	_get_secrets_info_callback (call_id, agent_username, setting_name, local);
 	g_clear_error (&local);
 	if (dict)
 		g_variant_unref (dict);
 
 out:
-	_get_secrets_info_free (info);
+	_get_secrets_info_free (call_id);
 }
 
 static gboolean
-get_secrets_idle_cb (GetSecretsInfo *info)
+get_secrets_idle_cb (NMSettingsConnectionCallId *call_id)
 {
 	NMSettingsConnectionPrivate *priv;
 
-	g_return_val_if_fail (info && NM_IS_SETTINGS_CONNECTION (info->self), G_SOURCE_REMOVE);
+	g_return_val_if_fail (call_id && NM_IS_SETTINGS_CONNECTION (call_id->self), G_SOURCE_REMOVE);
 
-	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (info->self);
+	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (call_id->self);
 
-	g_return_val_if_fail (g_slist_find (priv->get_secret_requests, info), G_SOURCE_REMOVE);
+	nm_assert (c_list_contains (&priv->call_ids_lst_head, &call_id->call_ids_lst));
 
-	priv->get_secret_requests = g_slist_remove (priv->get_secret_requests, info);
+	c_list_unlink (&call_id->call_ids_lst);
 
-	_get_secrets_info_callback (info, NULL, NULL, info->t.idle.error);
+	_get_secrets_info_callback (call_id, NULL, NULL, call_id->t.idle.error);
 
-	_get_secrets_info_free (info);
+	_get_secrets_info_free (call_id);
 	return G_SOURCE_REMOVE;
 }
 
@@ -1222,13 +1211,13 @@ get_secrets_idle_cb (GetSecretsInfo *info)
  *
  * Returns: a call ID which may be used to cancel the ongoing secrets request.
  **/
-NMSettingsConnectionCallId
+NMSettingsConnectionCallId *
 nm_settings_connection_get_secrets (NMSettingsConnection *self,
                                     NMConnection *applied_connection,
                                     NMAuthSubject *subject,
                                     const char *setting_name,
                                     NMSecretAgentGetSecretsFlags flags,
-                                    const char **hints,
+                                    const char *const*hints,
                                     NMSettingsConnectionSecretsFunc callback,
                                     gpointer callback_data)
 {
@@ -1236,7 +1225,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	GVariant *existing_secrets;
 	NMAgentManagerCallId call_id_a;
 	gs_free char *joined_hints = NULL;
-	GetSecretsInfo *info;
+	NMSettingsConnectionCallId *call_id;
 	GError *local = NULL;
 
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
@@ -1244,12 +1233,16 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	                      || (   NM_IS_CONNECTION (applied_connection)
 	                          && (((NMConnection *) self) != applied_connection)), NULL);
 
-	info = _get_secrets_info_new (self,
-	                              applied_connection,
-	                              callback,
-	                              callback_data);
-
-	priv->get_secret_requests = g_slist_append (priv->get_secret_requests, info);
+	call_id = g_slice_new0 (NMSettingsConnectionCallId);
+	call_id->self = self;
+	if (applied_connection) {
+		call_id->had_applied_connection = TRUE;
+		call_id->applied_connection = applied_connection;
+		g_object_add_weak_pointer (G_OBJECT (applied_connection), (gpointer *) &call_id->applied_connection);
+	}
+	call_id->callback = callback;
+	call_id->callback_data = callback_data;
+	c_list_link_tail (&priv->call_ids_lst_head, &call_id->call_ids_lst);
 
 	/* Use priv->secrets to work around the fact that nm_connection_clear_secrets()
 	 * will clear secrets on this object's settings.
@@ -1278,6 +1271,14 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	existing_secrets = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
 	if (existing_secrets)
 		g_variant_ref_sink (existing_secrets);
+
+	/* we remember the current version-id of the secret-agents. The version-id is strictly increasing,
+	 * as new agents register the number. We know hence, that this request was made against a certain
+	 * set of secret-agents.
+	 * If after making this request a new secret-agent registeres, the version-id increases.
+	 * Then we know that the this request probably did not yet include the latest secret-agent. */
+	priv->last_secret_agent_version_id = nm_agent_manager_get_agent_version_id (priv->agent_mgr);
+
 	call_id_a = nm_agent_manager_get_secrets (priv->agent_mgr,
 	                                          nm_connection_get_path (NM_CONNECTION (self)),
 	                                          NM_CONNECTION (self),
@@ -1287,7 +1288,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	                                          flags,
 	                                          hints,
 	                                          get_secrets_done_cb,
-	                                          info);
+	                                          call_id);
 	g_assert (call_id_a);
 	if (existing_secrets)
 		g_variant_unref (existing_secrets);
@@ -1299,45 +1300,44 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	       (hints && hints[0]) ? (joined_hints = g_strjoinv (",", (char **) hints)) : "(none)");
 
 	if (call_id_a) {
-		info->type = GET_SECRETS_INFO_TYPE_REQ;
-		info->t.req.id = call_id_a;
+		call_id->type = CALL_ID_TYPE_REQ;
+		call_id->t.req.id = call_id_a;
 	} else {
 schedule_dummy:
-		info->type = GET_SECRETS_INFO_TYPE_IDLE;
-		g_propagate_error (&info->t.idle.error, local);
-		info->t.idle.id = g_idle_add ((GSourceFunc) get_secrets_idle_cb, info);
+		call_id->type = CALL_ID_TYPE_IDLE;
+		g_propagate_error (&call_id->t.idle.error, local);
+		call_id->t.idle.id = g_idle_add ((GSourceFunc) get_secrets_idle_cb, call_id);
 	}
-	return info;
+	return call_id;
 }
 
 static void
 _get_secrets_cancel (NMSettingsConnection *self,
-                     GetSecretsInfo *info,
+                     NMSettingsConnectionCallId *call_id,
                      gboolean is_disposing)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	gs_free_error GError *error = NULL;
 
-	if (!g_slist_find (priv->get_secret_requests, info))
-		g_return_if_reached ();
+	nm_assert (c_list_contains (&priv->call_ids_lst_head, &call_id->call_ids_lst));
 
-	priv->get_secret_requests = g_slist_remove (priv->get_secret_requests, info);
+	c_list_unlink (&call_id->call_ids_lst);
 
-	if (info->type == GET_SECRETS_INFO_TYPE_REQ)
-		nm_agent_manager_cancel_secrets (priv->agent_mgr, info->t.req.id);
+	if (call_id->type == CALL_ID_TYPE_REQ)
+		nm_agent_manager_cancel_secrets (priv->agent_mgr, call_id->t.req.id);
 	else
-		g_source_remove (info->t.idle.id);
+		g_source_remove (call_id->t.idle.id);
 
 	nm_utils_error_set_cancelled (&error, is_disposing, "NMSettingsConnection");
 
-	_get_secrets_info_callback (info, NULL, NULL, error);
+	_get_secrets_info_callback (call_id, NULL, NULL, error);
 
-	_get_secrets_info_free (info);
+	_get_secrets_info_free (call_id);
 }
 
 void
 nm_settings_connection_cancel_secrets (NMSettingsConnection *self,
-                                       NMSettingsConnectionCallId call_id)
+                                       NMSettingsConnectionCallId *call_id)
 {
 	_LOGD ("(%p) secrets canceled", call_id);
 
@@ -1581,8 +1581,9 @@ typedef struct {
 	NMAgentManager *agent_mgr;
 	NMAuthSubject *subject;
 	NMConnection *new_settings;
-	gboolean save_to_disk;
+	NMSettingsUpdate2Flags flags;
 	char *audit_args;
+	bool is_update2:1;
 } UpdateInfo;
 
 static void
@@ -1646,7 +1647,13 @@ update_complete (NMSettingsConnection *self,
 {
 	if (error)
 		g_dbus_method_invocation_return_gerror (info->context, error);
-	else
+	else if (info->is_update2) {
+		GVariantBuilder result;
+
+		g_variant_builder_init (&result, G_VARIANT_TYPE ("a{sv}"));
+		g_dbus_method_invocation_return_value (info->context,
+		                                       g_variant_new ("(@a{sv})", g_variant_builder_end (&result)));
+	} else
 		g_dbus_method_invocation_return_value (info->context, NULL);
 
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_UPDATE, self, !error, info->audit_args,
@@ -1656,8 +1663,7 @@ update_complete (NMSettingsConnection *self,
 	g_clear_object (&info->agent_mgr);
 	g_clear_object (&info->new_settings);
 	g_free (info->audit_args);
-	memset (info, 0, sizeof (*info));
-	g_free (info);
+	g_slice_free (UpdateInfo, info);
 }
 
 static void
@@ -1670,6 +1676,8 @@ update_auth_cb (NMSettingsConnection *self,
 	UpdateInfo *info = data;
 	NMSettingsConnectionCommitReason commit_reason;
 	gs_free_error GError *local = NULL;
+	NMSettingsConnectionPersistMode persist_mode;
+	const char *log_diff_name;
 
 	if (error) {
 		update_complete (self, info, error);
@@ -1690,7 +1698,9 @@ update_auth_cb (NMSettingsConnection *self,
 			 */
 			update_agent_secrets_cache (self, info->new_settings);
 		}
+	}
 
+	if (info->new_settings) {
 		if (nm_audit_manager_audit_enabled (nm_audit_manager_get ())) {
 			gs_unref_hashtable GHashTable *diff = NULL;
 			gboolean same;
@@ -1704,36 +1714,47 @@ update_auth_cb (NMSettingsConnection *self,
 		}
 	}
 
-	if (!info->save_to_disk) {
-		if (info->new_settings) {
-			nm_settings_connection_replace_settings (self,
-			                                         info->new_settings,
-			                                         TRUE,
-			                                         "replace-unsaved",
-			                                         &local);
-		}
-		goto out;
-	}
-
-	if (info->new_settings) {
-		if (!nm_settings_connection_replace_settings_prepare (self,
-		                                                      info->new_settings,
-		                                                      &local))
-			goto out;
-	}
-
 	commit_reason = NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION;
 	if (   info->new_settings
 	    && !nm_streq0 (nm_connection_get_id (NM_CONNECTION (self)),
 	                   nm_connection_get_id (info->new_settings)))
 		commit_reason |= NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED;
 
-	nm_settings_connection_commit_changes (self,
-	                                       info->new_settings,
-	                                       commit_reason,
-	                                       &local);
+	if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_TO_DISK))
+		persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK;
+	else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY))
+		persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY;
+	else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED)) {
+		persist_mode = NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
+		               ? NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED
+		               : NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED;
+	} else if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY)) {
+		persist_mode = NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
+		               ? NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY
+		               : NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY;
+	} else
+		persist_mode = NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP;
+
+	if (   persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK
+	    || (   persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP
+	        && !nm_settings_connection_get_unsaved (self)))
+		log_diff_name = info->new_settings ? "update-settings" : "write-out-to-disk";
+	else
+		log_diff_name = info->new_settings ? "update-unsaved" : "make-unsaved";
+
+	if (NM_FLAGS_HAS (info->flags, NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)) {
+		nm_settings_connection_autoconnect_blocked_reason_set (self,
+		                                                       NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
+		                                                       TRUE);
+	}
+
+	nm_settings_connection_update (self,
+	                               info->new_settings,
+	                               persist_mode,
+	                               commit_reason,
+	                               log_diff_name,
+	                               &local);
 
-out:
 	if (!local) {
 		gs_unref_object NMConnection *for_agent = NULL;
 
@@ -1782,10 +1803,11 @@ get_update_modify_permission (NMConnection *old, NMConnection *new)
 }
 
 static void
-settings_connection_update_helper (NMSettingsConnection *self,
-                                   GDBusMethodInvocation *context,
-                                   GVariant *new_settings,
-                                   gboolean save_to_disk)
+settings_connection_update (NMSettingsConnection *self,
+                            gboolean is_update2,
+                            GDBusMethodInvocation *context,
+                            GVariant *new_settings,
+                            NMSettingsUpdate2Flags flags)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 	NMAuthSubject *subject = NULL;
@@ -1795,8 +1817,6 @@ settings_connection_update_helper (NMSettingsConnection *self,
 	const char *permission;
 	char *error_desc = NULL;
 
-	g_assert (new_settings != NULL || save_to_disk == TRUE);
-
 	/* If the connection is read-only, that has to be changed at the source of
 	 * the problem (ex a system settings plugin that can't write connections out)
 	 * instead of over D-Bus.
@@ -1806,12 +1826,22 @@ settings_connection_update_helper (NMSettingsConnection *self,
 
 	/* Check if the settings are valid first */
 	if (new_settings) {
-		tmp = _nm_simple_connection_new_from_dbus (new_settings,
-		                                             NM_SETTING_PARSE_FLAGS_STRICT
-		                                           | NM_SETTING_PARSE_FLAGS_NORMALIZE,
-		                                           &error);
-		if (!tmp)
+		if (!g_variant_is_of_type (new_settings, NM_VARIANT_TYPE_CONNECTION)) {
+			g_set_error_literal (&error,
+			                     NM_SETTINGS_ERROR,
+			                     NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
+			                     "settings is of invalid type");
 			goto error;
+		}
+
+		if (g_variant_n_children (new_settings) > 0) {
+			tmp = _nm_simple_connection_new_from_dbus (new_settings,
+			                                             NM_SETTING_PARSE_FLAGS_STRICT
+			                                           | NM_SETTING_PARSE_FLAGS_NORMALIZE,
+			                                           &error);
+			if (!tmp)
+				goto error;
+		}
 	}
 
 	subject = _new_auth_subject (context, &error);
@@ -1832,11 +1862,12 @@ settings_connection_update_helper (NMSettingsConnection *self,
 		goto error;
 	}
 
-	info = g_malloc0 (sizeof (*info));
+	info = g_slice_new0 (UpdateInfo);
+	info->is_update2 = is_update2;
 	info->context = context;
 	info->agent_mgr = g_object_ref (priv->agent_mgr);
 	info->subject = subject;
-	info->save_to_disk = save_to_disk;
+	info->flags = flags;
 	info->new_settings = tmp;
 
 	permission = get_update_modify_permission (NM_CONNECTION (self),
@@ -1859,7 +1890,7 @@ impl_settings_connection_update (NMSettingsConnection *self,
                                  GDBusMethodInvocation *context,
                                  GVariant *new_settings)
 {
-	settings_connection_update_helper (self, context, new_settings, TRUE);
+	settings_connection_update (self, FALSE, context, new_settings, NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
 }
 
 static void
@@ -1867,14 +1898,76 @@ impl_settings_connection_update_unsaved (NMSettingsConnection *self,
                                          GDBusMethodInvocation *context,
                                          GVariant *new_settings)
 {
-	settings_connection_update_helper (self, context, new_settings, FALSE);
+	settings_connection_update (self, FALSE, context, new_settings, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY);
 }
 
 static void
 impl_settings_connection_save (NMSettingsConnection *self,
                                GDBusMethodInvocation *context)
 {
-	settings_connection_update_helper (self, context, NULL, TRUE);
+	settings_connection_update (self, FALSE, context, NULL, NM_SETTINGS_UPDATE2_FLAG_TO_DISK);
+}
+
+static void
+impl_settings_connection_update2 (NMSettingsConnection *self,
+                                  GDBusMethodInvocation *context,
+                                  GVariant *settings,
+                                  guint32 flags_u,
+                                  GVariant *args)
+{
+	GError *error = NULL;
+	GVariantIter iter;
+	const char *args_name;
+	const NMSettingsUpdate2Flags flags = (NMSettingsUpdate2Flags) flags_u;
+	const NMSettingsUpdate2Flags ALL_PERSIST_MODES =   NM_SETTINGS_UPDATE2_FLAG_TO_DISK
+	                                                 | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY
+	                                                 | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED
+	                                                 | NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY;
+
+	if (NM_FLAGS_ANY (flags_u, ~((guint32) (ALL_PERSIST_MODES |
+	                                        NM_SETTINGS_UPDATE2_FLAG_VOLATILE |
+	                                        NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT)))) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
+		                             "Unknown flags");
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	if (   (   NM_FLAGS_ANY (flags, ALL_PERSIST_MODES)
+	        && !nm_utils_is_power_of_two (flags & ALL_PERSIST_MODES))
+	    || (   NM_FLAGS_HAS (flags, NM_SETTINGS_UPDATE2_FLAG_VOLATILE)
+	        && !NM_FLAGS_ANY (flags, NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED |
+	                                 NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY))) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
+		                             "Conflicting flags");
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	if (!g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}"))) {
+		error = g_error_new_literal (NM_SETTINGS_ERROR,
+		                             NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
+		                             "args is of invalid type");
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	g_variant_iter_init (&iter, args);
+	while (g_variant_iter_next (&iter, "{&sv}", &args_name, NULL)) {
+		error = g_error_new (NM_SETTINGS_ERROR,
+		                     NM_SETTINGS_ERROR_INVALID_ARGUMENTS,
+		                     "Unsupported argument '%s'", args_name);
+		g_dbus_method_invocation_take_error (context, error);
+		return;
+	}
+
+	settings_connection_update (self,
+	                            TRUE,
+	                            context,
+	                            settings,
+	                            flags);
 }
 
 static void
@@ -1951,7 +2044,7 @@ out_err:
 
 static void
 dbus_get_agent_secrets_cb (NMSettingsConnection *self,
-                           NMSettingsConnectionCallId call_id,
+                           NMSettingsConnectionCallId *call_id,
                            const char *agent_username,
                            const char *setting_name,
                            GError *error,
@@ -2052,10 +2145,12 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
 	                                 nm_connection_get_path (NM_CONNECTION (self)),
 	                                 NM_CONNECTION (self));
 
-	nm_settings_connection_commit_changes (self,
-	                                       NULL,
-	                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
-	                                       &local);
+	nm_settings_connection_update (self,
+	                               NULL,
+	                               NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+	                               NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+	                               "clear-secrets",
+	                               &local);
 
 	nm_audit_log_connection_op (NM_AUDIT_OP_CONN_CLEAR_SECRETS, self,
 	                            !local, NULL, subject, local ? local->message : NULL);
@@ -2112,6 +2207,14 @@ nm_settings_connection_get_unsaved (NMSettingsConnection *self)
 
 /*****************************************************************************/
 
+NM_UTILS_FLAGS2STR_DEFINE_STATIC (_settings_connection_flags_to_string, NMSettingsConnectionFlags,
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_NONE,          "none"),
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_UNSAVED,       "unsaved"),
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED,  "nm-generated"),
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_VOLATILE,      "volatile"),
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_CONNECTION_FLAGS_VISIBLE,       "visible"),
+);
+
 NMSettingsConnectionFlags
 nm_settings_connection_get_flags (NMSettingsConnection *self)
 {
@@ -2123,35 +2226,38 @@ nm_settings_connection_get_flags (NMSettingsConnection *self)
 NMSettingsConnectionFlags
 nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set)
 {
-	NMSettingsConnectionFlags new_flags;
-
-	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
-	g_return_val_if_fail ((flags & ~NM_SETTINGS_CONNECTION_FLAGS_ALL) == 0, NM_SETTINGS_CONNECTION_FLAGS_NONE);
-
-	new_flags = NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->flags;
-	if (set)
-		new_flags |= flags;
-	else
-		new_flags &= ~flags;
-	return nm_settings_connection_set_flags_all (self, new_flags);
+	return nm_settings_connection_set_flags_full (self,
+	                                              flags,
+	                                              set ? flags : NM_SETTINGS_CONNECTION_FLAGS_NONE);
 }
 
 NMSettingsConnectionFlags
-nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConnectionFlags flags)
+nm_settings_connection_set_flags_full (NMSettingsConnection *self,
+                                       NMSettingsConnectionFlags mask,
+                                       NMSettingsConnectionFlags value)
 {
 	NMSettingsConnectionPrivate *priv;
 	NMSettingsConnectionFlags old_flags;
 
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NM_SETTINGS_CONNECTION_FLAGS_NONE);
-	g_return_val_if_fail ((flags & ~NM_SETTINGS_CONNECTION_FLAGS_ALL) == 0, NM_SETTINGS_CONNECTION_FLAGS_NONE);
+	nm_assert (mask && !NM_FLAGS_ANY (mask, ~NM_SETTINGS_CONNECTION_FLAGS_ALL));
+	nm_assert (!NM_FLAGS_ANY (value, ~mask));
+
 	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
+	value = (priv->flags & ~mask) | value;
+
 	old_flags = priv->flags;
-	if (old_flags != flags) {
-		_LOGT ("update settings-connection flags to 0x%x (was 0x%x)", (guint) flags, (guint) priv->flags);
-		priv->flags = flags;
+	if (old_flags != value) {
+		char buf1[255], buf2[255];
+
+		_LOGT ("update settings-connection flags to %s (was %s)",
+		       _settings_connection_flags_to_string (value, buf1, sizeof (buf1)),
+		       _settings_connection_flags_to_string (priv->flags, buf2, sizeof (buf2)));
+		priv->flags = value;
+		nm_assert (priv->flags == value);
 		_notify (self, PROP_FLAGS);
-		if (NM_FLAGS_HAS (old_flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED) != NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED))
+		if (NM_FLAGS_HAS (old_flags, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED) != NM_FLAGS_HAS (value, NM_SETTINGS_CONNECTION_FLAGS_UNSAVED))
 			_notify (self, PROP_UNSAVED);
 	}
 	return old_flags;
@@ -2533,6 +2639,54 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self)
 
 /*****************************************************************************/
 
+static int
+_autoconnect_retries_initial (NMSettingsConnection *self)
+{
+	NMSettingConnection *s_con;
+	int retries = -1;
+
+	s_con = nm_connection_get_setting_connection ((NMConnection *) self);
+	if (s_con)
+		retries = nm_setting_connection_get_autoconnect_retries (s_con);
+
+	/* -1 means 'default' */
+	if (retries == -1)
+		retries = nm_config_data_get_autoconnect_retries_default (NM_CONFIG_GET_DATA);
+
+	/* 0 means 'forever', which is translated to a retry count of -1 */
+	if (retries == 0)
+		retries = AUTOCONNECT_RETRIES_FOREVER;
+
+	nm_assert (retries == AUTOCONNECT_RETRIES_FOREVER || retries >= 0);
+	return retries;
+}
+
+static void
+_autoconnect_retries_set (NMSettingsConnection *self,
+                          int retries,
+                          gboolean is_reset)
+{
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+	g_return_if_fail (retries == AUTOCONNECT_RETRIES_FOREVER || retries >= 0);
+
+	if (priv->autoconnect_retries != retries) {
+		_LOGT ("autoconnect: retries set %d%s", retries,
+		       is_reset ? " (reset)" : "");
+		priv->autoconnect_retries = retries;
+	}
+
+	if (retries)
+		priv->autoconnect_retries_blocked_until = 0;
+	else {
+		/* XXX: the blocked time must be identical for all connections, otherwise
+		 * the tracking of resetting the retry count in NMPolicy needs adjustment
+		 * in _connection_autoconnect_retries_set() (as it would need to re-evaluate
+		 * the next-timeout everytime a connection gets blocked). */
+		priv->autoconnect_retries_blocked_until = nm_utils_get_monotonic_timestamp_s () + AUTOCONNECT_RESET_RETRIES_TIMER;
+	}
+}
+
 /**
  * nm_settings_connection_autoconnect_retries_get:
  * @self: the settings connection
@@ -2547,30 +2701,10 @@ nm_settings_connection_autoconnect_retries_get (NMSettingsConnection *self)
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
 	if (G_UNLIKELY (priv->autoconnect_retries == AUTOCONNECT_RETRIES_UNSET)) {
-		NMSettingConnection *s_con;
-		int retries = -1;
-
-		s_con = nm_connection_get_setting_connection ((NMConnection *) self);
-		if (s_con)
-			retries = nm_setting_connection_get_autoconnect_retries (s_con);
-
-		/* -1 means 'default' */
-		if (retries == -1) {
-			retries = nm_config_data_get_value_int64 (NM_CONFIG_GET_DATA,
-			                                          NM_CONFIG_KEYFILE_GROUP_MAIN,
-			                                          "autoconnect-retries-default",
-			                                          10, 0, G_MAXINT32,
-			                                          AUTOCONNECT_RETRIES_DEFAULT);
-		}
-
-		/* 0 means 'forever', which is translated to a retry count of -1 */
-		if (retries == 0)
-			retries = AUTOCONNECT_RETRIES_FOREVER;
-
-		_LOGT ("autoconnect-retries: init %d", retries);
-		priv->autoconnect_retries = retries;
+		_autoconnect_retries_set (self,
+		                          _autoconnect_retries_initial (self),
+		                          TRUE);
 	}
-
 	return priv->autoconnect_retries;
 }
 
@@ -2578,86 +2712,90 @@ void
 nm_settings_connection_autoconnect_retries_set (NMSettingsConnection *self,
                                                 int retries)
 {
-	NMSettingsConnectionPrivate *priv;
-
 	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
-	nm_assert (retries == AUTOCONNECT_RETRIES_UNSET || retries >= 0);
-
-	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	g_return_if_fail (retries >= 0);
 
-	if (priv->autoconnect_retries != retries) {
-		_LOGT ("autoconnect-retries: set %d", retries);
-		priv->autoconnect_retries = retries;
-	}
-	if (retries)
-		priv->autoconnect_blocked_until = 0;
-	else
-		priv->autoconnect_blocked_until = nm_utils_get_monotonic_timestamp_s () + AUTOCONNECT_RESET_RETRIES_TIMER;
+	_autoconnect_retries_set (self, retries, FALSE);
 }
 
 void
 nm_settings_connection_autoconnect_retries_reset (NMSettingsConnection *self)
 {
-	nm_settings_connection_autoconnect_retries_set (self, AUTOCONNECT_RETRIES_UNSET);
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (self));
+
+	_autoconnect_retries_set (self,
+	                          _autoconnect_retries_initial (self),
+	                          TRUE);
 }
 
 gint32
-nm_settings_connection_autoconnect_blocked_until_get (NMSettingsConnection *self)
+nm_settings_connection_autoconnect_retries_blocked_until (NMSettingsConnection *self)
 {
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_until;
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retries_blocked_until;
 }
 
+NM_UTILS_FLAGS2STR_DEFINE_STATIC (_autoconnect_blocked_reason_to_string, NMSettingsAutoconnectBlockedReason,
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE, "none"),
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST, "user-request"),
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED, "failed"),
+	NM_UTILS_FLAGS2STR (NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS, "no-secrets"),
+);
+
 NMSettingsAutoconnectBlockedReason
-nm_settings_connection_autoconnect_blocked_reason_get (NMSettingsConnection *self)
+nm_settings_connection_autoconnect_blocked_reason_get (NMSettingsConnection *self, NMSettingsAutoconnectBlockedReason mask)
 {
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason;
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason & (mask ?: NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_ALL);
 }
 
-void
-nm_settings_connection_autoconnect_blocked_reason_set (NMSettingsConnection *self,
-                                                       NMSettingsAutoconnectBlockedReason reason)
+gboolean
+nm_settings_connection_autoconnect_blocked_reason_set_full (NMSettingsConnection *self,
+                                                            NMSettingsAutoconnectBlockedReason mask,
+                                                            NMSettingsAutoconnectBlockedReason value)
 {
-	g_return_if_fail (NM_IN_SET (reason,
-	                             NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE,
-	                             NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST,
-	                             NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED,
-	                             NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS));
-	NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason = reason;
-}
+	NMSettingsAutoconnectBlockedReason v;
+	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	char buf[100];
 
-/*****************************************************************************/
+	nm_assert (mask);
+	nm_assert (!NM_FLAGS_ANY (value, ~mask));
 
-/**
- * nm_settings_connection_get_nm_generated:
- * @self: an #NMSettingsConnection
- *
- * Gets the "nm-generated" flag on @self.
- *
- * A connection is "nm-generated" if it was generated by
- * nm_device_generate_connection() and has not been modified or
- * saved by the user since then.
- */
-gboolean
-nm_settings_connection_get_nm_generated (NMSettingsConnection *self)
-{
-	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED);
+	v = priv->autoconnect_blocked_reason;
+	v = (v & ~mask) | (value & mask);
+
+	if (priv->autoconnect_blocked_reason == v)
+		return FALSE;
+
+	_LOGT ("autoconnect: blocked reason: %s", _autoconnect_blocked_reason_to_string (v, buf, sizeof (buf)));
+	priv->autoconnect_blocked_reason = v;
+	return TRUE;
 }
 
-/**
- * nm_settings_connection_get_volatile:
- * @self: an #NMSettingsConnection
- *
- * Gets the "volatile" flag on @self.
- *
- * The connection is marked as volatile and will be removed when
- * it disconnects.
- */
 gboolean
-nm_settings_connection_get_volatile (NMSettingsConnection *self)
+nm_settings_connection_autoconnect_is_blocked (NMSettingsConnection *self)
 {
-	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_VOLATILE);
+	NMSettingsConnectionPrivate *priv;
+	NMSettingsConnectionFlags flags;
+
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), TRUE);
+
+	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+
+	if (priv->autoconnect_blocked_reason != NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE)
+		return TRUE;
+	if (priv->autoconnect_retries == 0)
+		return TRUE;
+
+	flags = priv->flags;
+	if (NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_VOLATILE))
+		return TRUE;
+	if (!NM_FLAGS_HAS (flags, NM_SETTINGS_CONNECTION_FLAGS_VISIBLE))
+		return TRUE;
+
+	return FALSE;
 }
 
+/*****************************************************************************/
+
 gboolean
 nm_settings_connection_get_ready (NMSettingsConnection *self)
 {
@@ -2738,8 +2876,8 @@ nm_settings_connection_init (NMSettingsConnection *self)
 	priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnectionPrivate);
 	self->_priv = priv;
 
-	priv->visible = FALSE;
 	priv->ready = TRUE;
+	c_list_init (&priv->call_ids_lst_head);
 
 	priv->session_monitor = g_object_ref (nm_session_monitor_get ());
 	priv->session_changed_id = g_signal_connect (priv->session_monitor,
@@ -2771,17 +2909,14 @@ dispose (GObject *object)
 {
 	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
+	NMSettingsConnectionCallId *call_id, *call_id_safe;
 
 	_LOGD ("disposing");
 
 	/* Cancel in-progress secrets requests */
 	if (priv->agent_mgr) {
-		while (priv->get_secret_requests) {
-			GetSecretsInfo *info = priv->get_secret_requests->data;
-
-			_get_secrets_cancel (self, info, TRUE);
-			g_return_if_fail (!priv->get_secret_requests || (info != priv->get_secret_requests->data));
-		}
+		c_list_for_each_entry_safe (call_id, call_id_safe, &priv->call_ids_lst_head, call_ids_lst)
+			_get_secrets_cancel (self, call_id, TRUE);
 	}
 
 	/* Disconnect handlers.
@@ -2818,12 +2953,8 @@ get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
 	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
-	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
 	switch (prop_id) {
-	case PROP_VISIBLE:
-		g_value_set_boolean (value, priv->visible);
-		break;
 	case PROP_UNSAVED:
 		g_value_set_boolean (value, nm_settings_connection_get_unsaved (self));
 		break;
@@ -2876,12 +3007,6 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
 
 	class->supports_secrets = supports_secrets;
 
-	obj_properties[PROP_VISIBLE] =
-	     g_param_spec_boolean (NM_SETTINGS_CONNECTION_VISIBLE, "", "",
-	                           FALSE,
-	                           G_PARAM_READABLE |
-	                           G_PARAM_STATIC_STRINGS);
-
 	obj_properties[PROP_UNSAVED] =
 	     g_param_spec_boolean (NM_SETTINGS_CONNECTION_UNSAVED, "", "",
 	                           FALSE,
@@ -2948,6 +3073,7 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
 	                                        "GetSecrets", impl_settings_connection_get_secrets,
 	                                        "ClearSecrets", impl_settings_connection_clear_secrets,
 	                                        "Save", impl_settings_connection_save,
+	                                        "Update2", impl_settings_connection_update2,
 	                                        NULL);
 }
 
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index faacd949..fc8ad1de 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -44,8 +44,9 @@
 #define NM_SETTINGS_CONNECTION_UPDATED_INTERNAL "updated-internal"
 
 /* Properties */
-#define NM_SETTINGS_CONNECTION_VISIBLE  "visible"
 #define NM_SETTINGS_CONNECTION_UNSAVED  "unsaved"
+
+/* Internal properties */
 #define NM_SETTINGS_CONNECTION_READY    "ready"
 #define NM_SETTINGS_CONNECTION_FLAGS    "flags"
 #define NM_SETTINGS_CONNECTION_FILENAME "filename"
@@ -61,22 +62,25 @@
  * @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_VISIBLE: The connection is visible
  * @NM_SETTINGS_CONNECTION_FLAGS_ALL: special mask, for all known flags
  *
  * #NMSettingsConnection flags.
  **/
-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_VOLATILE                           = 0x04,
+typedef enum {
+	NM_SETTINGS_CONNECTION_FLAGS_NONE                               = 0,
+
+	NM_SETTINGS_CONNECTION_FLAGS_UNSAVED                            = (1LL <<  0),
+	NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED                       = (1LL <<  1),
+	NM_SETTINGS_CONNECTION_FLAGS_VOLATILE                           = (1LL <<  2),
+
+	NM_SETTINGS_CONNECTION_FLAGS_VISIBLE                            = (1LL <<  3),
 
 	__NM_SETTINGS_CONNECTION_FLAGS_LAST,
 	NM_SETTINGS_CONNECTION_FLAGS_ALL = ((__NM_SETTINGS_CONNECTION_FLAGS_LAST - 1) << 1) - 1,
 } NMSettingsConnectionFlags;
 
-typedef enum { /*< skip >*/
+typedef enum {
 	NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE                       = 0,
 	NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION                = (1LL << 0),
 	NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED                 = (1LL << 1),
@@ -84,13 +88,18 @@ typedef enum { /*< skip >*/
 
 typedef enum {
 	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE                    = 0,
-	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST            = 1,
-	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED                  = 2,
-	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS              = 3,
+
+	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST            = (1LL << 0),
+	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED                  = (1LL << 1),
+	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS              = (1LL << 2),
+
+	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_ALL                     = (  NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_USER_REQUEST
+	                                                                   | NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_FAILED
+	                                                                   | NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS),
 } NMSettingsAutoconnectBlockedReason;
 
 struct _NMSettingsConnectionCallId;
-typedef struct _NMSettingsConnectionCallId *NMSettingsConnectionCallId;
+typedef struct _NMSettingsConnectionCallId NMSettingsConnectionCallId;
 
 typedef struct _NMSettingsConnectionClass NMSettingsConnectionClass;
 
@@ -120,37 +129,39 @@ struct _NMSettingsConnectionClass {
 
 GType nm_settings_connection_get_type (void);
 
+guint64 nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *self);
+
 gboolean nm_settings_connection_has_unmodified_applied_connection (NMSettingsConnection *self,
                                                                    NMConnection *applied_connection,
                                                                    NMSettingCompareFlags compare_flage);
 
-gboolean  nm_settings_connection_commit_changes (NMSettingsConnection *self,
-                                                 NMConnection *new_connection,
-                                                 NMSettingsConnectionCommitReason commit_reason,
-                                                 GError **error);
-
-gboolean nm_settings_connection_replace_settings_prepare (NMSettingsConnection *self,
-                                                          NMConnection *new_connection,
-                                                          GError **error);
-
-gboolean nm_settings_connection_replace_settings (NMSettingsConnection *self,
-                                                  NMConnection *new_connection,
-                                                  gboolean update_unsaved,
-                                                  const char *log_diff_name,
-                                                  GError **error);
-
-gboolean nm_settings_connection_replace_settings_full (NMSettingsConnection *self,
-                                                       NMConnection *new_connection,
-                                                       gboolean prepare_new_connection,
-                                                       gboolean update_unsaved,
-                                                       const char *log_diff_name,
-                                                       GError **error);
+typedef enum {
+	NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+	NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+
+	/* unsaved, only sets the unsaved flag, but it doesn't touch
+	 * the NM_GENERATED nor VOLATILE flag. */
+	NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED,
+
+	NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY,
+	NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_DETACHED,
+	NM_SETTINGS_CONNECTION_PERSIST_MODE_IN_MEMORY_ONLY,
+	NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED,
+	NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_ONLY,
+} NMSettingsConnectionPersistMode;
+
+gboolean  nm_settings_connection_update (NMSettingsConnection *self,
+                                         NMConnection *new_connection,
+                                         NMSettingsConnectionPersistMode persist_mode,
+                                         NMSettingsConnectionCommitReason commit_reason,
+                                         const char *log_diff_name,
+                                         GError **error);
 
 gboolean nm_settings_connection_delete (NMSettingsConnection *self,
                                         GError **error);
 
 typedef void (*NMSettingsConnectionSecretsFunc) (NMSettingsConnection *self,
-                                                 NMSettingsConnectionCallId call_id,
+                                                 NMSettingsConnectionCallId *call_id,
                                                  const char *agent_username,
                                                  const char *setting_name,
                                                  GError *error,
@@ -162,19 +173,17 @@ gboolean nm_settings_connection_new_secrets (NMSettingsConnection *self,
                                              GVariant *secrets,
                                              GError **error);
 
-NMSettingsConnectionCallId nm_settings_connection_get_secrets (NMSettingsConnection *self,
-                                                               NMConnection *applied_connection,
-                                                               NMAuthSubject *subject,
-                                                               const char *setting_name,
-                                                               NMSecretAgentGetSecretsFlags flags,
-                                                               const char **hints,
-                                                               NMSettingsConnectionSecretsFunc callback,
-                                                               gpointer callback_data);
+NMSettingsConnectionCallId *nm_settings_connection_get_secrets (NMSettingsConnection *self,
+                                                                NMConnection *applied_connection,
+                                                                NMAuthSubject *subject,
+                                                                const char *setting_name,
+                                                                NMSecretAgentGetSecretsFlags flags,
+                                                                const char *const*hints,
+                                                                NMSettingsConnectionSecretsFunc callback,
+                                                                gpointer callback_data);
 
 void nm_settings_connection_cancel_secrets (NMSettingsConnection *self,
-                                            NMSettingsConnectionCallId call_id);
-
-gboolean nm_settings_connection_is_visible (NMSettingsConnection *self);
+                                            NMSettingsConnectionCallId *call_id);
 
 void nm_settings_connection_recheck_visibility (NMSettingsConnection *self);
 
@@ -187,7 +196,7 @@ gboolean nm_settings_connection_get_unsaved (NMSettingsConnection *self);
 
 NMSettingsConnectionFlags nm_settings_connection_get_flags (NMSettingsConnection *self);
 NMSettingsConnectionFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set);
-NMSettingsConnectionFlags nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConnectionFlags flags);
+NMSettingsConnectionFlags nm_settings_connection_set_flags_full (NMSettingsConnection *self, NMSettingsConnectionFlags mask, NMSettingsConnectionFlags value);
 
 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);
@@ -218,14 +227,23 @@ void nm_settings_connection_autoconnect_retries_set (NMSettingsConnection *self,
                                                      int retries);
 void nm_settings_connection_autoconnect_retries_reset (NMSettingsConnection *self);
 
-gint32 nm_settings_connection_autoconnect_blocked_until_get (NMSettingsConnection *self);
+gint32 nm_settings_connection_autoconnect_retries_blocked_until (NMSettingsConnection *self);
 
-NMSettingsAutoconnectBlockedReason nm_settings_connection_autoconnect_blocked_reason_get (NMSettingsConnection *self);
-void                               nm_settings_connection_autoconnect_blocked_reason_set (NMSettingsConnection *self,
-                                                                                          NMSettingsAutoconnectBlockedReason reason);
+NMSettingsAutoconnectBlockedReason nm_settings_connection_autoconnect_blocked_reason_get (NMSettingsConnection *self,
+                                                                                          NMSettingsAutoconnectBlockedReason mask);
+gboolean nm_settings_connection_autoconnect_blocked_reason_set_full (NMSettingsConnection *self,
+                                                                     NMSettingsAutoconnectBlockedReason mask,
+                                                                     NMSettingsAutoconnectBlockedReason value);
+
+static inline gboolean
+nm_settings_connection_autoconnect_blocked_reason_set (NMSettingsConnection *self,
+                                                       NMSettingsAutoconnectBlockedReason mask,
+                                                       gboolean set)
+{
+	return nm_settings_connection_autoconnect_blocked_reason_set_full (self, mask, set ? mask : NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NONE);
+}
 
-gboolean nm_settings_connection_get_nm_generated (NMSettingsConnection *self);
-gboolean nm_settings_connection_get_volatile (NMSettingsConnection *self);
+gboolean nm_settings_connection_autoconnect_is_blocked (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.c b/src/settings/nm-settings.c
index e2b467a2..21fdf9e0 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -75,7 +75,6 @@
 #include "nm-audit-manager.h"
 #include "NetworkManagerUtils.h"
 #include "nm-dispatcher.h"
-#include "nm-inotify-helper.h"
 #include "nm-hostname-manager.h"
 
 #include "introspection/org.freedesktop.NetworkManager.Settings.h"
@@ -84,13 +83,8 @@
 
 #define EXPORT(sym) void * __export_##sym = &sym;
 
-EXPORT(nm_inotify_helper_get_type)
-EXPORT(nm_inotify_helper_get)
-EXPORT(nm_inotify_helper_add_watch)
-EXPORT(nm_inotify_helper_remove_watch)
-
 EXPORT(nm_settings_connection_get_type)
-EXPORT(nm_settings_connection_replace_settings)
+EXPORT(nm_settings_connection_update)
 
 /*****************************************************************************/
 
@@ -127,8 +121,7 @@ enum {
 	CONNECTION_ADDED,
 	CONNECTION_UPDATED,
 	CONNECTION_REMOVED,
-	CONNECTION_VISIBILITY_CHANGED,
-	AGENT_REGISTERED,
+	CONNECTION_FLAGS_CHANGED,
 	NEW_CONNECTION, /* exported, not used internally */
 	LAST_SIGNAL
 };
@@ -431,6 +424,9 @@ nm_settings_get_connections (NMSettings *self, guint *out_len)
  * @out_len: (allow-none): optional output argument
  * @func: caller-supplied function for filtering connections
  * @func_data: caller-supplied data passed to @func
+ * @sort_compare_func: (allow-none): optional function pointer for
+ *   sorting the returned list.
+ * @sort_data: user data for @sort_compare_func.
  *
  * Returns: (transfer container) (element-type NMSettingsConnection):
  *   an NULL terminated array of #NMSettingsConnection objects that were
@@ -443,7 +439,9 @@ NMSettingsConnection **
 nm_settings_get_connections_clone (NMSettings *self,
                                    guint *out_len,
                                    NMSettingsConnectionFilterFunc func,
-                                   gpointer func_data)
+                                   gpointer func_data,
+                                   GCompareDataFunc sort_compare_func,
+                                   gpointer sort_data)
 {
 	NMSettingsConnection *const*list_cached;
 	NMSettingsConnection **list;
@@ -471,31 +469,15 @@ nm_settings_get_connections_clone (NMSettings *self,
 	} else
 		memcpy (list, list_cached, sizeof (list[0]) * ((gsize) len + 1));
 
+	if (   len > 1
+	    && sort_compare_func) {
+		g_qsort_with_data (list, len, sizeof (NMSettingsConnection *),
+		                   sort_compare_func, sort_data);
+	}
 	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_free(), but not the list items.
- */
-NMSettingsConnection **
-nm_settings_get_connections_sorted (NMSettings *self, guint *out_len)
-{
-	NMSettingsConnection **connections;
-	guint len;
-
-	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
-
-	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 *
 nm_settings_get_connection_by_path (NMSettings *self, const char *path)
 {
@@ -838,13 +820,12 @@ connection_updated (NMSettingsConnection *connection, gboolean by_user, gpointer
 }
 
 static void
-connection_visibility_changed (NMSettingsConnection *connection,
-                               GParamSpec *pspec,
-                               gpointer user_data)
+connection_flags_changed (NMSettingsConnection *connection,
+                          GParamSpec *pspec,
+                          gpointer user_data)
 {
-	/* Re-emit for listeners like NMPolicy */
 	g_signal_emit (NM_SETTINGS (user_data),
-	               signals[CONNECTION_VISIBILITY_CHANGED],
+	               signals[CONNECTION_FLAGS_CHANGED],
 	               0,
 	               connection);
 }
@@ -867,7 +848,7 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data)
 
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed), self);
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_updated), self);
-	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_visibility_changed), self);
+	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_flags_changed), self);
 	if (!priv->startup_complete)
 		g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_ready_changed), self);
 	g_object_unref (self);
@@ -889,18 +870,6 @@ connection_removed (NMSettingsConnection *connection, gpointer user_data)
 	g_object_unref (connection);
 }
 
-static void
-secret_agent_registered (NMAgentManager *agent_mgr,
-                         NMSecretAgent *agent,
-                         gpointer user_data)
-{
-	/* Re-emit for listeners like NMPolicy */
-	g_signal_emit (NM_SETTINGS (user_data),
-	               signals[AGENT_REGISTERED],
-	               0,
-	               agent);
-}
-
 #define NM_DBUS_SERVICE_OPENCONNECT    "org.freedesktop.NetworkManager.openconnect"
 #define NM_OPENCONNECT_KEY_GATEWAY "gateway"
 #define NM_OPENCONNECT_KEY_COOKIE "cookie"
@@ -1003,8 +972,8 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 	                        G_CALLBACK (connection_removed), self);
 	g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
 	                  G_CALLBACK (connection_updated), self);
-	g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_VISIBLE,
-	                  G_CALLBACK (connection_visibility_changed),
+	g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_FLAGS,
+	                  G_CALLBACK (connection_flags_changed),
 	                  self);
 	if (!priv->startup_complete) {
 		g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_READY,
@@ -1037,6 +1006,25 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 	}
 }
 
+static gboolean
+secrets_filter_cb (NMSetting *setting,
+                   const char *secret,
+                   NMSettingSecretFlags flags,
+                   gpointer user_data)
+{
+	NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data);
+
+	/* Returns TRUE to remove the secret */
+
+	/* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */
+	if (   (flags == NM_SETTING_SECRET_FLAG_NONE)
+	    && (filter_flags == NM_SETTING_SECRET_FLAG_NONE))
+		return FALSE;
+
+	/* Otherwise if the secret has at least one of the desired flags keep it */
+	return (flags & filter_flags) ? FALSE : TRUE;
+}
+
 /**
  * nm_settings_add_connection:
  * @self: the #NMSettings object
@@ -1087,9 +1075,22 @@ nm_settings_add_connection (NMSettings *self,
 	for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
 		NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
 		GError *add_error = NULL;
+		gs_unref_object NMConnection *simple = NULL;
+		gs_unref_variant GVariant *secrets = NULL;
+
+		/* Make a copy of agent-owned secrets because they won't be present in
+		 * the connection returned by plugins, as plugins return only what was
+		 * reread from the file. */
+		simple = nm_simple_connection_new_clone (connection);
+		nm_connection_clear_secrets_with_flags (simple,
+		                                        secrets_filter_cb,
+		                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
+		secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
 
 		added = nm_settings_plugin_add_connection (plugin, connection, save_to_disk, &add_error);
 		if (added) {
+			if (secrets)
+				nm_connection_update_secrets (NM_CONNECTION (added), NULL, secrets, NULL);
 			claim_connection (self, added);
 			return added;
 		}
@@ -1105,25 +1106,6 @@ nm_settings_add_connection (NMSettings *self,
 	return NULL;
 }
 
-static gboolean
-secrets_filter_cb (NMSetting *setting,
-                   const char *secret,
-                   NMSettingSecretFlags flags,
-                   gpointer user_data)
-{
-	NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data);
-
-	/* Returns TRUE to remove the secret */
-
-	/* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */
-	if (   (flags == NM_SETTING_SECRET_FLAG_NONE)
-	    && (filter_flags == NM_SETTING_SECRET_FLAG_NONE))
-		return FALSE;
-
-	/* Otherwise if the secret has at least one of the desired flags keep it */
-	return (flags & filter_flags) ? FALSE : TRUE;
-}
-
 static void
 send_agent_owned_secrets (NMSettings *self,
                           NMSettingsConnection *connection,
@@ -1886,16 +1868,8 @@ nm_settings_init (NMSettings *self)
 
 	priv->connections = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref);
 
-	/* Hold a reference to the agent manager so it stays alive; the only
-	 * other holders are NMSettingsConnection objects which are often
-	 * transient, and we don't want the agent manager to get destroyed and
-	 * recreated often.
-	 */
 	priv->agent_mgr = g_object_ref (nm_agent_manager_get ());
-
 	priv->config = g_object_ref (nm_config_get ());
-
-	g_signal_connect (priv->agent_mgr, "agent-registered", G_CALLBACK (secret_agent_registered), self);
 }
 
 NMSettings *
@@ -2012,23 +1986,14 @@ nm_settings_class_init (NMSettingsClass *class)
 	                  g_cclosure_marshal_VOID__OBJECT,
 	                  G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
 
-	signals[CONNECTION_VISIBILITY_CHANGED] =
-	    g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED,
+	signals[CONNECTION_FLAGS_CHANGED] =
+	    g_signal_new (NM_SETTINGS_SIGNAL_CONNECTION_FLAGS_CHANGED,
 	                  G_OBJECT_CLASS_TYPE (object_class),
 	                  G_SIGNAL_RUN_FIRST,
 	                  0, NULL, NULL,
 	                  g_cclosure_marshal_VOID__OBJECT,
 	                  G_TYPE_NONE, 1, NM_TYPE_SETTINGS_CONNECTION);
 
-	signals[AGENT_REGISTERED] =
-	    g_signal_new (NM_SETTINGS_SIGNAL_AGENT_REGISTERED,
-	                  G_OBJECT_CLASS_TYPE (object_class),
-	                  G_SIGNAL_RUN_FIRST,
-	                  0, NULL, NULL,
-	                  g_cclosure_marshal_VOID__OBJECT,
-	                  G_TYPE_NONE, 1, NM_TYPE_SECRET_AGENT);
-
-
 	signals[NEW_CONNECTION] =
 	    g_signal_new ("new-connection",
 	                  G_OBJECT_CLASS_TYPE (object_class),
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index eede76b0..0ecffb70 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -46,8 +46,7 @@
 #define NM_SETTINGS_SIGNAL_CONNECTION_ADDED              "connection-added"
 #define NM_SETTINGS_SIGNAL_CONNECTION_UPDATED            "connection-updated"
 #define NM_SETTINGS_SIGNAL_CONNECTION_REMOVED            "connection-removed"
-#define NM_SETTINGS_SIGNAL_CONNECTION_VISIBILITY_CHANGED "connection-visibility-changed"
-#define NM_SETTINGS_SIGNAL_AGENT_REGISTERED              "agent-registered"
+#define NM_SETTINGS_SIGNAL_CONNECTION_FLAGS_CHANGED      "connection-flags-changed"
 
 /**
  * NMConnectionFilterFunc:
@@ -100,10 +99,9 @@ NMSettingsConnection *const* nm_settings_get_connections (NMSettings *settings,
 NMSettingsConnection **nm_settings_get_connections_clone (NMSettings *self,
                                                           guint *out_len,
                                                           NMSettingsConnectionFilterFunc func,
-                                                          gpointer func_data);
-
-NMSettingsConnection **nm_settings_get_connections_sorted (NMSettings *self,
-                                                           guint *out_len);
+                                                          gpointer func_data,
+                                                          GCompareDataFunc sort_compare_func,
+                                                          gpointer sort_data);
 
 NMSettingsConnection *nm_settings_add_connection (NMSettings *settings,
                                                   NMConnection *connection,
diff --git a/src/settings/plugins/ibft/nms-ibft-connection.c b/src/settings/plugins/ibft/nms-ibft-connection.c
index 834ba834..2a7c5f4a 100644
--- a/src/settings/plugins/ibft/nms-ibft-connection.c
+++ b/src/settings/plugins/ibft/nms-ibft-connection.c
@@ -60,11 +60,12 @@ nms_ibft_connection_new (const GPtrArray *block, GError **error)
 
 	object = g_object_new (NMS_TYPE_IBFT_CONNECTION, NULL);
 	/* Update settings with what was read from iscsiadm */
-	if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
-	                                              source,
-	                                              FALSE,
-	                                              NULL,
-	                                              error))
+	if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object),
+	                                    source,
+	                                    NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+	                                    NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+	                                    NULL,
+	                                    error))
 		g_clear_object (&object);
 
 	return (NMSIbftConnection *) object;
diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.c b/src/settings/plugins/ibft/nms-ibft-plugin.c
index 9b1f5ccd..ea4d1524 100644
--- a/src/settings/plugins/ibft/nms-ibft-plugin.c
+++ b/src/settings/plugins/ibft/nms-ibft-plugin.c
@@ -202,5 +202,5 @@ settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface)
 G_MODULE_EXPORT GObject *
 nm_settings_plugin_factory (void)
 {
-	return g_object_ref (nms_ibft_plugin_get ());
+	return G_OBJECT (g_object_ref (nms_ibft_plugin_get ()));
 }
diff --git a/src/settings/nm-inotify-helper.c b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c
index 4c65b02d..4c65b02d 100644
--- a/src/settings/nm-inotify-helper.c
+++ b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c
diff --git a/src/settings/nm-inotify-helper.h b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.h
index 2715f3ee..b887ae37 100644
--- a/src/settings/nm-inotify-helper.h
+++ b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.h
@@ -43,4 +43,17 @@ int nm_inotify_helper_add_watch (NMInotifyHelper *helper, const char *path);
 
 void nm_inotify_helper_remove_watch (NMInotifyHelper *helper, int wd);
 
+static inline gboolean
+nm_inotify_helper_clear_watch (NMInotifyHelper *helper, int *wd)
+{
+	int x;
+
+	if (wd && ((x = *wd) >= 0)) {
+		*wd = -1;
+		nm_inotify_helper_remove_watch (helper, x);
+		return TRUE;
+	}
+	return FALSE;
+}
+
 #endif  /* __NM_INOTIFY_HELPER_H__ */
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 4c1d02ae..3cf5c978 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
@@ -36,13 +36,13 @@
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-8021x.h"
 #include "platform/nm-platform.h"
-#include "settings/nm-inotify-helper.h"
 #include "nm-config.h"
 
 #include "nms-ifcfg-rh-common.h"
 #include "nms-ifcfg-rh-reader.h"
 #include "nms-ifcfg-rh-writer.h"
 #include "nms-ifcfg-rh-utils.h"
+#include "nm-inotify-helper.h"
 
 /*****************************************************************************/
 
@@ -96,14 +96,6 @@ G_DEFINE_TYPE (NMIfcfgConnection, nm_ifcfg_connection, NM_TYPE_SETTINGS_CONNECTI
 
 /*****************************************************************************/
 
-static NMInotifyHelper *
-_get_inotify_helper (NMIfcfgConnectionPrivate *priv)
-{
-	if (!priv->inotify_helper)
-		priv->inotify_helper = g_object_ref (nm_inotify_helper_get ());
-	return priv->inotify_helper;
-}
-
 static gboolean
 devtimeout_ready (gpointer user_data)
 {
@@ -225,37 +217,17 @@ static void
 path_watch_stop (NMIfcfgConnection *self)
 {
 	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);
-	NMInotifyHelper *ih;
-
-	ih = _get_inotify_helper (priv);
-
-	nm_clear_g_signal_handler (ih, &priv->ih_event_id);
-
-	if (priv->file_wd >= 0) {
-		nm_inotify_helper_remove_watch (ih, priv->file_wd);
-		priv->file_wd = -1;
-	}
 
-	g_free (priv->keyfile);
-	priv->keyfile = NULL;
-	if (priv->keyfile_wd >= 0) {
-		nm_inotify_helper_remove_watch (ih, priv->keyfile_wd);
-		priv->keyfile_wd = -1;
-	}
+	nm_clear_g_signal_handler (priv->inotify_helper, &priv->ih_event_id);
 
-	g_free (priv->routefile);
-	priv->routefile = NULL;
-	if (priv->routefile_wd >= 0) {
-		nm_inotify_helper_remove_watch (ih, priv->routefile_wd);
-		priv->routefile_wd = -1;
-	}
+	nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->file_wd);
+	nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->keyfile_wd);
+	nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->routefile_wd);
+	nm_inotify_helper_clear_watch (priv->inotify_helper, &priv->route6file_wd);
 
-	g_free (priv->route6file);
-	priv->route6file = NULL;
-	if (priv->route6file_wd >= 0) {
-		nm_inotify_helper_remove_watch (ih, priv->route6file_wd);
-		priv->route6file_wd = -1;
-	}
+	nm_clear_g_free (&priv->keyfile);
+	nm_clear_g_free (&priv->routefile);
+	nm_clear_g_free (&priv->route6file);
 }
 
 static void
@@ -280,7 +252,9 @@ filename_changed (GObject *object,
 	if (nm_config_get_monitor_connection_files (nm_config_get ())) {
 		NMInotifyHelper *ih;
 
-		ih = _get_inotify_helper (priv);
+		if (!priv->inotify_helper)
+			priv->inotify_helper = g_object_ref (nm_inotify_helper_get ());
+		ih = priv->inotify_helper;
 
 		priv->ih_event_id = g_signal_connect (ih, "event", G_CALLBACK (files_changed_cb), self);
 		priv->file_wd = nm_inotify_helper_add_watch (ih, ifcfg_path);
@@ -324,7 +298,7 @@ commit_changes (NMSettingsConnection *connection,
 	nm_assert (!out_logmsg_change || !*out_logmsg_change);
 
 	filename = nm_settings_connection_get_filename (connection);
-	if (!nms_ifcfg_rh_writer_write_connection (new_connection ?: NM_CONNECTION (connection),
+	if (!nms_ifcfg_rh_writer_write_connection (new_connection,
 	                                           IFCFG_DIR,
 	                                           filename,
 	                                           &ifcfg_path,
@@ -415,6 +389,13 @@ set_property (GObject *object, guint prop_id,
 static void
 nm_ifcfg_connection_init (NMIfcfgConnection *connection)
 {
+	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (connection);
+
+	priv->file_wd = -1;
+	priv->keyfile_wd = -1;
+	priv->routefile_wd = -1;
+	priv->route6file_wd = -1;
+
 	g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_FILENAME,
 	                  G_CALLBACK (filename_changed), NULL);
 }
@@ -429,18 +410,12 @@ nm_ifcfg_connection_new (NMConnection *source,
 	NMConnection *tmp;
 	char *unhandled_spec = NULL;
 	const char *unmanaged_spec = NULL, *unrecognized_spec = NULL;
-	gboolean update_unsaved = TRUE;
 
 	g_assert (source || full_path);
 
 	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);
@@ -464,11 +439,14 @@ nm_ifcfg_connection_new (NMConnection *source,
 	                                   NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, unrecognized_spec,
 	                                   NULL);
 	/* Update our settings with what was read from the file */
-	if (nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
-	                                             tmp,
-	                                             update_unsaved,
-	                                             NULL,
-	                                             error))
+	if (nm_settings_connection_update (NM_SETTINGS_CONNECTION (object),
+	                                   tmp,
+	                                   full_path
+	                                     ? NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP /* connection is already on disk */
+	                                     : NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED,
+	                                   NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+	                                   NULL,
+	                                   error))
 		nm_ifcfg_connection_check_devtimeout (NM_IFCFG_CONNECTION (object));
 	else
 		g_clear_object (&object);
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 da0920ef..04e74bbd 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -313,11 +313,12 @@ update_connection (SettingsPluginIfcfg *self,
 			              NM_IFCFG_CONNECTION_UNRECOGNIZED_SPEC, new_unrecognized,
 			              NULL);
 
-			if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (connection_by_uuid),
-			                                              NM_CONNECTION (connection_new),
-			                                              FALSE,  /* don't set Unsaved */
-			                                              "ifcfg-update",
-			                                              &local)) {
+			if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid),
+			                                    NM_CONNECTION (connection_new),
+			                                    NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+			                                    NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+			                                    "ifcfg-update",
+			                                    &local)) {
 				/* Shouldn't ever get here as 'connection_new' was verified by the reader already
 				 * and the UUID did not change. */
 				g_assert_not_reached ();
@@ -1079,5 +1080,5 @@ settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface)
 G_MODULE_EXPORT GObject *
 nm_settings_plugin_factory (void)
 {
-	return g_object_ref (settings_plugin_ifcfg_get ());
+	return G_OBJECT (g_object_ref (settings_plugin_ifcfg_get ()));
 }
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 66add713..b9900eec 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.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 2008 - 2015 Red Hat, Inc.
+ * Copyright 2008 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -77,18 +77,6 @@
 
 /*****************************************************************************/
 
-static gboolean
-get_uint (const char *str, guint32 *value)
-{
-	gint64 tmp;
-
-	tmp = _nm_utils_ascii_str_to_int64 (str, 0, 0, G_MAXUINT32, -1);
-	if (tmp == -1)
-		return FALSE;
-	*value = tmp;
-	return TRUE;
-}
-
 static void
 check_if_bond_slave (shvarFile *ifcfg,
                      NMSettingConnection *s_con)
@@ -512,13 +500,13 @@ typedef struct {
 
 	bool int_base_16:1;
 
-	/* the type, one of PARSE_LINE_TYPE_* */
-	char type;
-
 	/* whether the command line option was found, and @v is
 	 * initialized. */
 	bool has:1;
 
+	/* the type, one of PARSE_LINE_TYPE_* */
+	char type;
+
 	union {
 		guint8 uint8;
 		guint32 uint32;
@@ -541,6 +529,7 @@ enum {
 	PARSE_LINE_ATTR_ROUTE_SRC,
 	PARSE_LINE_ATTR_ROUTE_FROM,
 	PARSE_LINE_ATTR_ROUTE_TOS,
+	PARSE_LINE_ATTR_ROUTE_ONLINK,
 	PARSE_LINE_ATTR_ROUTE_WINDOW,
 	PARSE_LINE_ATTR_ROUTE_CWND,
 	PARSE_LINE_ATTR_ROUTE_INITCWND,
@@ -562,6 +551,7 @@ enum {
 #define PARSE_LINE_TYPE_ADDR              'a'
 #define PARSE_LINE_TYPE_ADDR_WITH_PREFIX  'p'
 #define PARSE_LINE_TYPE_IFNAME            'i'
+#define PARSE_LINE_TYPE_FLAG              'f'
 
 /**
  * parse_route_line:
@@ -601,42 +591,45 @@ parse_route_line (const char *line,
 	char buf1[256];
 	char buf2[256];
 	ParseLineInfo infos[] = {
-		[PARSE_LINE_ATTR_ROUTE_TABLE]    = { .key = NM_IP_ROUTE_ATTRIBUTE_TABLE,
-		                                     .type = PARSE_LINE_TYPE_UINT32, },
-		[PARSE_LINE_ATTR_ROUTE_SRC]      = { .key = NM_IP_ROUTE_ATTRIBUTE_SRC,
-		                                     .type = PARSE_LINE_TYPE_ADDR, },
-		[PARSE_LINE_ATTR_ROUTE_FROM]     = { .key = NM_IP_ROUTE_ATTRIBUTE_FROM,
-		                                     .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
-		                                     .disabled = (addr_family != AF_INET6), },
-		[PARSE_LINE_ATTR_ROUTE_TOS]      = { .key = NM_IP_ROUTE_ATTRIBUTE_TOS,
-		                                     .type = PARSE_LINE_TYPE_UINT8,
-		                                     .int_base_16 = TRUE,
-		                                     .ignore = (addr_family != AF_INET), },
-		[PARSE_LINE_ATTR_ROUTE_WINDOW]   = { .key = NM_IP_ROUTE_ATTRIBUTE_WINDOW,
-		                                     .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
-		[PARSE_LINE_ATTR_ROUTE_CWND]     = { .key = NM_IP_ROUTE_ATTRIBUTE_CWND,
-		                                     .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
-		[PARSE_LINE_ATTR_ROUTE_INITCWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITCWND,
-		                                     .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
-		[PARSE_LINE_ATTR_ROUTE_INITRWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITRWND,
-		                                     .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
-		[PARSE_LINE_ATTR_ROUTE_MTU]      = { .key = NM_IP_ROUTE_ATTRIBUTE_MTU,
-		                                     .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
-
-		[PARSE_LINE_ATTR_ROUTE_TO]       = { .key = "to",
-		                                     .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
-		                                     .disabled = (options_route != NULL), },
-		[PARSE_LINE_ATTR_ROUTE_VIA]      = { .key = "via",
-		                                     .type = PARSE_LINE_TYPE_ADDR,
-		                                     .disabled = (options_route != NULL), },
-		[PARSE_LINE_ATTR_ROUTE_METRIC]   = { .key = "metric",
-		                                     .type = PARSE_LINE_TYPE_UINT32,
-		                                     .disabled = (options_route != NULL), },
-
-		[PARSE_LINE_ATTR_ROUTE_DEV]      = { .key = "dev",
-		                                     .type = PARSE_LINE_TYPE_IFNAME,
-		                                     .ignore = TRUE,
-		                                     .disabled = (options_route != NULL), },
+		[PARSE_LINE_ATTR_ROUTE_TABLE]     = { .key = NM_IP_ROUTE_ATTRIBUTE_TABLE,
+		                                      .type = PARSE_LINE_TYPE_UINT32, },
+		[PARSE_LINE_ATTR_ROUTE_SRC]       = { .key = NM_IP_ROUTE_ATTRIBUTE_SRC,
+		                                      .type = PARSE_LINE_TYPE_ADDR, },
+		[PARSE_LINE_ATTR_ROUTE_FROM]      = { .key = NM_IP_ROUTE_ATTRIBUTE_FROM,
+		                                      .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
+		                                      .disabled = (addr_family != AF_INET6), },
+		[PARSE_LINE_ATTR_ROUTE_TOS]       = { .key = NM_IP_ROUTE_ATTRIBUTE_TOS,
+		                                      .type = PARSE_LINE_TYPE_UINT8,
+		                                      .int_base_16 = TRUE,
+		                                      .ignore = (addr_family != AF_INET), },
+		[PARSE_LINE_ATTR_ROUTE_ONLINK]    = { .key = NM_IP_ROUTE_ATTRIBUTE_ONLINK,
+		                                      .type = PARSE_LINE_TYPE_FLAG,
+		                                      .ignore = (addr_family != AF_INET), },
+		[PARSE_LINE_ATTR_ROUTE_WINDOW]    = { .key = NM_IP_ROUTE_ATTRIBUTE_WINDOW,
+		                                      .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
+		[PARSE_LINE_ATTR_ROUTE_CWND]      = { .key = NM_IP_ROUTE_ATTRIBUTE_CWND,
+		                                      .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
+		[PARSE_LINE_ATTR_ROUTE_INITCWND]  = { .key = NM_IP_ROUTE_ATTRIBUTE_INITCWND,
+		                                      .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
+		[PARSE_LINE_ATTR_ROUTE_INITRWND]  = { .key = NM_IP_ROUTE_ATTRIBUTE_INITRWND,
+		                                      .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
+		[PARSE_LINE_ATTR_ROUTE_MTU]       = { .key = NM_IP_ROUTE_ATTRIBUTE_MTU,
+		                                      .type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
+
+		[PARSE_LINE_ATTR_ROUTE_TO]        = { .key = "to",
+		                                      .type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
+		                                      .disabled = (options_route != NULL), },
+		[PARSE_LINE_ATTR_ROUTE_VIA]       = { .key = "via",
+		                                      .type = PARSE_LINE_TYPE_ADDR,
+		                                      .disabled = (options_route != NULL), },
+		[PARSE_LINE_ATTR_ROUTE_METRIC]    = { .key = "metric",
+		                                      .type = PARSE_LINE_TYPE_UINT32,
+		                                      .disabled = (options_route != NULL), },
+
+		[PARSE_LINE_ATTR_ROUTE_DEV]       = { .key = "dev",
+		                                      .type = PARSE_LINE_TYPE_IFNAME,
+		                                      .ignore = TRUE,
+		                                      .disabled = (options_route != NULL), },
 	};
 
 	nm_assert (line);
@@ -705,6 +698,9 @@ parse_route_line (const char *line,
 			case PARSE_LINE_TYPE_IFNAME:
 				i_words++;
 				goto parse_line_type_ifname;
+			case PARSE_LINE_TYPE_FLAG:
+				i_words++;
+				goto next;
 			default:
 				nm_assert_not_reached ();
 			}
@@ -913,6 +909,15 @@ next:
 			                                                    ? nm_sprintf_buf (buf2, "/%u", (unsigned) info->v.addr.plen)
 			                                                    : ""));
 			break;
+		case PARSE_LINE_TYPE_FLAG:
+			/* XXX: the flag (for "onlink") only allows to explictly set "TRUE".
+			 * There is no way to express an explicit "FALSE" setting
+			 * of this attribute, hence, the file format cannot encode
+			 * that configuration. */
+			nm_ip_route_set_attribute (route,
+			                           info->key,
+			                           g_variant_new_boolean (TRUE));
+			break;
 		default:
 			nm_assert_not_reached ();
 			break;
@@ -1141,7 +1146,7 @@ error:
 }
 
 static NMSetting *
-make_user_setting (shvarFile *ifcfg, GError **error)
+make_user_setting (shvarFile *ifcfg)
 {
 	gboolean has_user_data = FALSE;
 	gs_unref_object NMSettingUser *s_user = NULL;
@@ -1189,7 +1194,7 @@ make_user_setting (shvarFile *ifcfg, GError **error)
 }
 
 static NMSetting *
-make_proxy_setting (shvarFile *ifcfg, GError **error)
+make_proxy_setting (shvarFile *ifcfg)
 {
 	NMSettingProxy *s_proxy = NULL;
 	gs_free char *value = NULL;
@@ -1239,7 +1244,7 @@ make_proxy_setting (shvarFile *ifcfg, GError **error)
 
 static NMSetting *
 make_ip4_setting (shvarFile *ifcfg,
-                  const char *network_file,
+                  shvarFile *network_ifcfg,
                   gboolean routes_read,
                   gboolean *out_has_defroute,
                   GError **error)
@@ -1255,7 +1260,6 @@ make_ip4_setting (shvarFile *ifcfg,
 	int i;
 	guint32 a;
 	gboolean has_key;
-	shvarFile *network_ifcfg;
 	shvarFile *route_ifcfg;
 	gboolean never_default;
 	gint64 timeout;
@@ -1282,7 +1286,6 @@ make_ip4_setting (shvarFile *ifcfg,
 	}
 
 	/* Then check if GATEWAYDEV; it's global and overrides DEFROUTE */
-	network_ifcfg = svOpenFile (network_file, NULL);
 	if (network_ifcfg) {
 		gs_free char *gatewaydev_value = NULL;
 		const char *gatewaydev;
@@ -1299,7 +1302,6 @@ make_ip4_setting (shvarFile *ifcfg,
 			never_default = !!strcmp (v, gatewaydev);
 
 		nm_clear_g_free (&value);
-		svCloseFile (network_ifcfg);
 	}
 
 	v = svGetValueStr (ifcfg, "BOOTPROTO", &value);
@@ -1424,12 +1426,10 @@ make_ip4_setting (shvarFile *ifcfg,
 
 	/* Gateway */
 	if (!gateway) {
-		network_ifcfg = svOpenFile (network_file, NULL);
 		if (network_ifcfg) {
 			gboolean read_success;
 
 			read_success = read_ip4_address (network_ifcfg, "GATEWAY", &has_key, &a, error);
-			svCloseFile (network_ifcfg);
 			if (!read_success)
 				return NULL;
 			if (has_key) {
@@ -1665,16 +1665,15 @@ read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *file
 
 static NMSetting *
 make_ip6_setting (shvarFile *ifcfg,
-                  const char *network_file,
+                  shvarFile *network_ifcfg,
                   gboolean routes_read,
                   GError **error)
 {
 	NMSettingIPConfig *s_ip6 = NULL;
+	const char *v;
 	char *value = NULL;
 	char *str_value;
 	char *route6_path = NULL;
-	gs_free char *dns_options_free = NULL;
-	const char *dns_options = NULL;
 	gboolean ipv6init, ipv6forwarding, dhcp6 = FALSE;
 	char *method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
 	char *ipv6addr, *ipv6addr_secondaries;
@@ -1684,7 +1683,6 @@ make_ip6_setting (shvarFile *ifcfg,
 	int i_val;
 	GError *local = NULL;
 	gint priority;
-	shvarFile *network_ifcfg;
 	gboolean never_default = FALSE;
 	gboolean ip6_privacy = FALSE, ip6_privacy_prefer_public_ip;
 	NMSettingIP6ConfigPrivacy ip6_privacy_val;
@@ -1703,7 +1701,6 @@ make_ip6_setting (shvarFile *ifcfg,
 	 * they are global and override IPV6_DEFROUTE
 	 * When both are set, the device specified in IPV6_DEFAULTGW takes preference.
 	 */
-	network_ifcfg = svOpenFile (network_file, NULL);
 	if (network_ifcfg) {
 		char *ipv6_defaultgw, *ipv6_defaultdev;
 		char *default_dev = NULL;
@@ -1712,7 +1709,6 @@ make_ip6_setting (shvarFile *ifcfg,
 		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) {
 			default_dev = strchr (ipv6_defaultgw, '%');
@@ -1731,7 +1727,6 @@ make_ip6_setting (shvarFile *ifcfg,
 		g_free (ipv6_defaultgw);
 		g_free (ipv6_defaultdev);
 		g_free (value);
-		svCloseFile (network_ifcfg);
 	}
 
 	/* Find out method property */
@@ -1739,11 +1734,8 @@ make_ip6_setting (shvarFile *ifcfg,
 	str_value = svGetValueStr_cp (ifcfg, "IPV6INIT");
 	ipv6init = svGetValueBoolean (ifcfg, "IPV6INIT", FALSE);
 	if (!str_value) {
-		network_ifcfg = svOpenFile (network_file, NULL);
-		if (network_ifcfg) {
+		if (network_ifcfg)
 			ipv6init = svGetValueBoolean (network_ifcfg, "IPV6INIT", FALSE);
-			svCloseFile (network_ifcfg);
-		}
 	}
 	g_free (str_value);
 
@@ -1865,11 +1857,8 @@ make_ip6_setting (shvarFile *ifcfg,
 		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) {
+			if (network_ifcfg)
 				value = svGetValueStr_cp (network_ifcfg, "IPV6_DEFAULTGW");
-				svCloseFile (network_ifcfg);
-			}
 		}
 		if (value) {
 			char *ptr;
@@ -1930,8 +1919,6 @@ make_ip6_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	/* DNS searches ('DOMAIN' key) are read by make_ip4_setting() and included in NMSettingIPConfig */
-
 	if (!routes_read) {
 		/* NOP */
 	} else {
@@ -1942,9 +1929,24 @@ make_ip6_setting (shvarFile *ifcfg,
 		g_free (route6_path);
 	}
 
+	/* DNS searches */
+	nm_clear_g_free (&value);
+	v = svGetValueStr (ifcfg, "IPV6_DOMAIN", &value);
+	if (v) {
+		gs_free const char **searches = NULL;
+
+		searches = nm_utils_strsplit_set (v, " ");
+		if (searches) {
+			for (iter = searches; *iter; iter++) {
+				if (!nm_setting_ip_config_add_dns_search (s_ip6, *iter))
+					PARSE_WARNING ("duplicate DNS domain '%s'", *iter);
+			}
+		}
+	}
+
 	/* DNS options */
-	parse_dns_options (s_ip6, svGetValue (ifcfg, "RES_OPTIONS", &value));
-	parse_dns_options (s_ip6, dns_options);
+	nm_clear_g_free (&value);
+	parse_dns_options (s_ip6, svGetValue (ifcfg, "IPV6_RES_OPTIONS", &value));
 	g_free (value);
 
 	/* DNS priority */
@@ -1962,6 +1964,59 @@ error:
 	return NULL;
 }
 
+static NMSetting *
+make_tc_setting (shvarFile *ifcfg)
+{
+	NMSettingTCConfig *s_tc = NULL;
+	char tag[256];
+	int i;
+
+	s_tc = (NMSettingTCConfig *) nm_setting_tc_config_new ();
+
+	for (i = 1;; i++) {
+		NMTCQdisc *qdisc = NULL;
+		gs_free char *value_to_free = NULL;
+		const char *value = NULL;
+		GError *local = NULL;
+
+		value = svGetValueStr (ifcfg, numbered_tag (tag, "QDISC", i), &value_to_free);
+		if (!value)
+			break;
+
+		qdisc = nm_utils_tc_qdisc_from_str (value, &local);
+		if (!qdisc)
+			PARSE_WARNING ("ignoring bad qdisc: '%s': %s", value, local->message);
+
+		if (!nm_setting_tc_config_add_qdisc (s_tc, qdisc))
+			PARSE_WARNING ("duplicate qdisc");
+	}
+
+	for (i = 1;; i++) {
+		NMTCTfilter *tfilter = NULL;
+		gs_free char *value_to_free = NULL;
+		const char *value = NULL;
+		GError *local = NULL;
+
+		value = svGetValueStr (ifcfg, numbered_tag (tag, "FILTER", i), &value_to_free);
+		if (!value)
+			break;
+
+		tfilter = nm_utils_tc_tfilter_from_str (value, &local);
+		if (!tfilter)
+			PARSE_WARNING ("ignoring bad tfilter: '%s': %s", value, local->message);
+
+		if (!nm_setting_tc_config_add_tfilter (s_tc, tfilter))
+			PARSE_WARNING ("duplicate filter");
+	}
+
+	if (   nm_setting_tc_config_get_num_qdiscs (s_tc) > 0
+	    || nm_setting_tc_config_get_num_tfilters (s_tc) > 0)
+		return NM_SETTING (s_tc);
+
+	g_object_unref (s_tc);
+	return NULL;
+}
+
 typedef struct {
 	const char *enable_key;
 	const char *advertise_key;
@@ -2195,7 +2250,6 @@ read_dcb_percent_array (shvarFile *ifcfg,
 
 static gboolean
 make_dcb_setting (shvarFile *ifcfg,
-                  const char *network_file,
                   NMSetting **out_setting,
                   GError **error)
 {
@@ -2350,54 +2404,39 @@ add_one_wep_key (shvarFile *ifcfg,
                  NMSettingWirelessSecurity *s_wsec,
                  GError **error)
 {
-	char *key = NULL;
-	char *value = NULL;
-	gboolean success = FALSE;
+	gs_free char *value_free = NULL;
+	const char *value;
+	const char *key = NULL;
 
 	g_return_val_if_fail (ifcfg != NULL, FALSE);
 	g_return_val_if_fail (shvar_key != NULL, FALSE);
 	g_return_val_if_fail (key_idx <= 3, FALSE);
 	g_return_val_if_fail (s_wsec != NULL, FALSE);
 
-	value = svGetValueStr_cp (ifcfg, shvar_key);
+	value = svGetValueStr (ifcfg, shvar_key, &value_free);
 	if (!value)
 		return TRUE;
 
 	/* Validate keys */
 	if (passphrase) {
-		if (strlen (value) && strlen (value) < 64) {
-			key = g_strdup (value);
-			g_object_set (G_OBJECT (s_wsec),
-			              NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE,
-			              NM_WEP_KEY_TYPE_PASSPHRASE,
-			              NULL);
-		}
+		if (value[0] && strlen (value) < 64)
+			key = value;
 	} else {
-		if (strlen (value) == 10 || strlen (value) == 26) {
+		if (NM_IN_SET (strlen (value), 10, 26)) {
 			/* Hexadecimal WEP key */
-			char *p = value;
-
-			while (*p) {
-				if (!g_ascii_isxdigit (*p)) {
-					g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-					             "Invalid hexadecimal WEP key.");
-					goto out;
-				}
-				p++;
+			if (NM_STRCHAR_ANY (value, ch, !g_ascii_isxdigit (ch))) {
+				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				             "Invalid hexadecimal WEP key.");
+				return FALSE;
 			}
-			key = g_strdup (value);
+			key = value;
 		} else if (   !strncmp (value, "s:", 2)
-		           && (strlen (value) == 7 || strlen (value) == 15)) {
+		           && NM_IN_SET (strlen (value), 7, 15)) {
 			/* ASCII key */
-			char *p = value + 2;
-
-			while (*p) {
-				if (!g_ascii_isprint ((int) (*p))) {
-					g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-					             "Invalid ASCII WEP key.");
-					goto out;
-				}
-				p++;
+			if (NM_STRCHAR_ANY (value + 2, ch, !g_ascii_isprint (ch))) {
+				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				             "Invalid ASCII WEP key.");
+				return FALSE;
 			}
 
 			/* Remove 's:' prefix.
@@ -2406,51 +2445,50 @@ add_one_wep_key (shvarFile *ifcfg,
 			 * before passing to wpa_supplicant, this prevents two unnecessary conversions. And mainly,
 			 * ASCII WEP key doesn't change to HEX WEP key in UI, which could confuse users.
 			 */
-			key = g_strdup (value + 2);
+			key = value + 2;
 		}
 	}
 
-	if (key) {
-		nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, key);
-		g_free (key);
-		success = TRUE;
-	} else {
+	if (!key) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Invalid WEP key length.");
+		return FALSE;
 	}
 
-out:
-	g_free (value);
-	return success;
+	nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, key);
+	return TRUE;
 }
 
 static gboolean
 read_wep_keys (shvarFile *ifcfg,
+               NMWepKeyType key_type,
                guint8 def_idx,
                NMSettingWirelessSecurity *s_wsec,
                GError **error)
 {
-	/* Try hex/ascii keys first */
-	if (!add_one_wep_key (ifcfg, "KEY1", 0, FALSE, s_wsec, error))
-		return FALSE;
-	if (!add_one_wep_key (ifcfg, "KEY2", 1, FALSE, s_wsec, error))
-		return FALSE;
-	if (!add_one_wep_key (ifcfg, "KEY3", 2, FALSE, s_wsec, error))
-		return FALSE;
-	if (!add_one_wep_key (ifcfg, "KEY4", 3, FALSE, s_wsec, error))
-		return FALSE;
-	if (!add_one_wep_key (ifcfg, "KEY", def_idx, FALSE, s_wsec, error))
-		return FALSE;
+	if (key_type != NM_WEP_KEY_TYPE_PASSPHRASE) {
+		if (!add_one_wep_key (ifcfg, "KEY1", 0, FALSE, s_wsec, error))
+			return FALSE;
+		if (!add_one_wep_key (ifcfg, "KEY2", 1, FALSE, s_wsec, error))
+			return FALSE;
+		if (!add_one_wep_key (ifcfg, "KEY3", 2, FALSE, s_wsec, error))
+			return FALSE;
+		if (!add_one_wep_key (ifcfg, "KEY4", 3, FALSE, s_wsec, error))
+			return FALSE;
+		if (!add_one_wep_key (ifcfg, "KEY", def_idx, FALSE, s_wsec, error))
+			return FALSE;
+	}
 
-	/* And then passphrases */
-	if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE1", 0, TRUE, s_wsec, error))
-		return FALSE;
-	if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE2", 1, TRUE, s_wsec, error))
-		return FALSE;
-	if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE3", 2, TRUE, s_wsec, error))
-		return FALSE;
-	if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE4", 3, TRUE, s_wsec, error))
-		return FALSE;
+	if (key_type != NM_WEP_KEY_TYPE_KEY) {
+		if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE1", 0, TRUE, s_wsec, error))
+			return FALSE;
+		if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE2", 1, TRUE, s_wsec, error))
+			return FALSE;
+		if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE3", 2, TRUE, s_wsec, error))
+			return FALSE;
+		if (!add_one_wep_key (ifcfg, "KEY_PASSPHRASE4", 3, TRUE, s_wsec, error))
+			return FALSE;
+	}
 
 	return TRUE;
 }
@@ -2515,19 +2553,40 @@ 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))
+		NMWepKeyType key_type;
+		const char *v;
+		gs_free char *to_free = NULL;
+
+		v = svGetValueStr (ifcfg, "KEY_TYPE", &to_free);
+		if (!v)
+			key_type = NM_WEP_KEY_TYPE_UNKNOWN;
+		else if (nm_streq (v, "key"))
+			key_type = NM_WEP_KEY_TYPE_KEY;
+		else if (nm_streq (v, "passphrase"))
+			key_type = NM_WEP_KEY_TYPE_PASSPHRASE;
+		else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Invalid KEY_TYPE value '%s'", v);
+			return FALSE;
+		}
+
+		if (!read_wep_keys (ifcfg, key_type, default_key_idx, s_wsec, 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)) {
+			if (!read_wep_keys (keys_ifcfg, key_type, default_key_idx, s_wsec, error)) {
 				svCloseFile (keys_ifcfg);
 				return NULL;
 			}
 			svCloseFile (keys_ifcfg);
 			g_assert (error == NULL || *error == NULL);
 		}
+
+		g_object_set (G_OBJECT (s_wsec),
+		              NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, key_type,
+		              NULL);
 	}
 
 	value = svGetValueStr_cp (ifcfg, "SECURITYMODE");
@@ -3577,25 +3636,25 @@ make_wireless_security_setting (shvarFile *ifcfg,
 	return NULL; /* unencrypted */
 }
 
-static char **
+static const char **
 transform_hwaddr_blacklist (const char *blacklist)
 {
-	char **strv, **iter;
-	int shift = 0;
-
-	strv = _nm_utils_strsplit_set (blacklist, " \t", 0);
-	for (iter = strv; iter && *iter; iter++) {
-		if (shift) {
-			*(iter - shift) = *iter;
-			*iter = NULL;
-		}
-		if (!nm_utils_hwaddr_valid (*(iter - shift), ETH_ALEN)) {
-			PARSE_WARNING ("invalid MAC in HWADDR_BLACKLIST '%s'", *(iter - shift));
-			g_free (*(iter - shift));
-			*(iter - shift) = NULL;
-			shift++;
+	const char **strv;
+	gsize i, j;
+
+	strv = nm_utils_strsplit_set (blacklist, " \t");
+	if (!strv)
+		return NULL;
+	for (i = 0, j = 0; strv[j]; j++) {
+		const char *s = strv[j];
+
+		if (!nm_utils_hwaddr_valid (s, ETH_ALEN)) {
+			PARSE_WARNING ("invalid MAC in HWADDR_BLACKLIST '%s'", s);
+			continue;
 		}
+		strv[i++] = s;
 	}
+	strv[i] = NULL;
 	return strv;
 }
 
@@ -3630,13 +3689,12 @@ make_wireless_setting (shvarFile *ifcfg,
 	g_object_set (s_wireless, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK, value, NULL);
 	g_free (value);
 
-	value = svGetValueStr_cp (ifcfg, "HWADDR_BLACKLIST");
-	if (value) {
-		char **strv;
+	cvalue = svGetValueStr (ifcfg, "HWADDR_BLACKLIST", &value);
+	if (cvalue) {
+		gs_free const char **strv = NULL;
 
-		strv = transform_hwaddr_blacklist (value);
+		strv = transform_hwaddr_blacklist (cvalue);
 		g_object_set (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, strv, NULL);
-		g_strfreev (strv);
 		g_free (value);
 	}
 
@@ -4096,6 +4154,7 @@ make_wired_setting (shvarFile *ifcfg,
                     GError **error)
 {
 	gs_unref_object NMSettingWired *s_wired = NULL;
+	const char *cvalue;
 	gs_free char *value = NULL;
 	char *nettype;
 
@@ -4204,11 +4263,11 @@ make_wired_setting (shvarFile *ifcfg,
 	              NULL);
 	nm_clear_g_free (&value);
 
-	value = svGetValueStr_cp (ifcfg, "HWADDR_BLACKLIST");
-	if (value) {
-		gs_strfreev char **strv = NULL;
+	cvalue = svGetValueStr (ifcfg, "HWADDR_BLACKLIST", &value);
+	if (cvalue) {
+		gs_free const char **strv = NULL;
 
-		strv = transform_hwaddr_blacklist (value);
+		strv = transform_hwaddr_blacklist (cvalue);
 		g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, strv, NULL);
 		nm_clear_g_free (&value);
 	}
@@ -4633,66 +4692,114 @@ team_connection_from_ifcfg (const char *file,
 	return connection;
 }
 
+typedef enum {
+	BRIDGE_OPT_TYPE_MAIN,
+	BRIDGE_OPT_TYPE_OPTION,
+	BRIDGE_OPT_TYPE_PORT_MAIN,
+	BRIDGE_OPT_TYPE_PORT_OPTION,
+} BridgeOptType;
+
 typedef void (*BridgeOptFunc) (NMSetting *setting,
                                gboolean stp,
                                const char *key,
-                               const char *value);
+                               const char *value,
+                               BridgeOptType opt_type);
 
 static void
 handle_bridge_option (NMSetting *setting,
                       gboolean stp,
                       const char *key,
-                      const char *value)
+                      const char *value,
+                      BridgeOptType opt_type)
 {
-	guint32 u = 0;
+	static const struct {
+		const char *key;
+		const char *property_name;
+		BridgeOptType opt_type;
+		gboolean only_with_stp;
+		gboolean extended_bool;
+	} m/*etadata*/[] = {
+		{ "DELAY",              NM_SETTING_BRIDGE_FORWARD_DELAY,      BRIDGE_OPT_TYPE_MAIN,   .only_with_stp = TRUE },
+		{ "priority",           NM_SETTING_BRIDGE_PRIORITY,           BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE },
+		{ "hello_time",         NM_SETTING_BRIDGE_HELLO_TIME,         BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE },
+		{ "max_age",            NM_SETTING_BRIDGE_MAX_AGE,            BRIDGE_OPT_TYPE_OPTION, .only_with_stp = TRUE },
+		{ "ageing_time",        NM_SETTING_BRIDGE_AGEING_TIME,        BRIDGE_OPT_TYPE_OPTION },
+		{ "multicast_snooping", NM_SETTING_BRIDGE_MULTICAST_SNOOPING, BRIDGE_OPT_TYPE_OPTION },
+		{ "group_fwd_mask",     NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, BRIDGE_OPT_TYPE_OPTION },
+		{ "priority",           NM_SETTING_BRIDGE_PORT_PRIORITY,      BRIDGE_OPT_TYPE_PORT_OPTION },
+		{ "path_cost",          NM_SETTING_BRIDGE_PORT_PATH_COST,     BRIDGE_OPT_TYPE_PORT_OPTION },
+		{ "hairpin_mode",       NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE,  BRIDGE_OPT_TYPE_PORT_OPTION, .extended_bool = TRUE, },
+	};
+	const char *error_message = NULL;
+	int i;
+	gint64 v;
 
-	if (!strcmp (key, "priority")) {
-		if (stp == FALSE)
-			PARSE_WARNING ("'priority' invalid when STP is disabled");
-		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)
-			PARSE_WARNING ("'hello_time' invalid when STP is disabled");
-		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)
-			PARSE_WARNING ("'max_age' invalid when STP is disabled");
-		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);
-	} else if (!strcmp (key, "ageing_time")) {
-		if (get_uint (value, &u))
-			g_object_set (setting, NM_SETTING_BRIDGE_AGEING_TIME, u, NULL);
-		else
-			PARSE_WARNING ("invalid ageing_time value '%s'", value);
-	} else if (!strcmp (key, "multicast_snooping")) {
-		if (get_uint (value, &u))
-			g_object_set (setting, NM_SETTING_BRIDGE_MULTICAST_SNOOPING,
-			              (gboolean) u, NULL);
-		else
-			PARSE_WARNING ("invalid multicast_snooping value '%s'", value);
-	} else if (!strcmp (key, "group_fwd_mask")) {
-		if (get_uint (value, &u) && u <= 0xFFFF && !NM_FLAGS_ANY (u, 7))
-			g_object_set (setting, NM_SETTING_BRIDGE_GROUP_FORWARD_MASK,
-			              (gboolean) u, NULL);
-		else
-			PARSE_WARNING ("invalid group_fwd_mask value '%s'", value);
-	} else
-			PARSE_WARNING ("unhandled bridge option '%s'", key);
+	for (i = 0; i < G_N_ELEMENTS (m); i++) {
+		GParamSpec *param_spec;
+
+		if (opt_type != m[i].opt_type)
+			continue;
+		if (!nm_streq (key, m[i].key))
+			continue;
+		if (m[i].only_with_stp && !stp) {
+			PARSE_WARNING ("'%s' invalid when STP is disabled", key);
+			return;
+		}
+
+		param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), m[i].property_name);
+		switch (param_spec->value_type) {
+		case G_TYPE_BOOLEAN:
+			if (m[i].extended_bool) {
+				if (!strcasecmp (value, "on") || !strcasecmp (value, "yes") || !strcmp (value, "1"))
+					v = TRUE;
+				else if (!strcasecmp (value, "off") || !strcasecmp (value, "no"))
+					v = FALSE;
+				else {
+					error_message = "is not a boolean";
+					goto warn;
+				}
+			} else {
+				v = _nm_utils_ascii_str_to_int64 (value, 10, 0, 1, -1);
+				if (v == -1) {
+					error_message = g_strerror (errno);
+					goto warn;
+				}
+			}
+			if (!nm_g_object_set_property_boolean (G_OBJECT (setting), m[i].property_name, v, NULL)) {
+				error_message = "number is out of range";
+				goto warn;
+			}
+			return;
+		case G_TYPE_UINT:
+			v = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT, -1);
+			if (v == -1) {
+				error_message = g_strerror (errno);
+				goto warn;
+			}
+			if (!nm_g_object_set_property_uint (G_OBJECT (setting), m[i].property_name, v, NULL)) {
+				error_message = "number is out of range";
+				goto warn;
+			}
+			return;
+		default:
+			nm_assert_not_reached ();
+			continue;
+		}
+
+warn:
+		PARSE_WARNING ("invalid %s value '%s': %s", key, value, error_message);
+		return;
+	}
+
+	PARSE_WARNING ("unhandled bridge option '%s'", key);
 }
 
 static void
 handle_bridging_opts (NMSetting *setting,
                       gboolean stp,
                       const char *value,
-                      BridgeOptFunc func)
+                      BridgeOptFunc func,
+                      BridgeOptType opt_type)
 {
 	gs_free const char **items = NULL;
 	const char *const *iter;
@@ -4707,7 +4814,7 @@ handle_bridging_opts (NMSetting *setting,
 			key = *keys;
 			val = *(keys + 1);
 			if (val && key[0] && val[0])
-				func (setting, stp, key, val);
+				func (setting, stp, key, val, opt_type);
 		}
 	}
 }
@@ -4717,30 +4824,29 @@ make_bridge_setting (shvarFile *ifcfg,
                      const char *file,
                      GError **error)
 {
-	NMSettingBridge *s_bridge;
-	char *value;
-	guint32 u;
+	gs_unref_object NMSettingBridge *s_bridge = NULL;
+	gs_free char *value_to_free = NULL;
+	const char *value;
 	gboolean stp = FALSE;
 	gboolean stp_set = FALSE;
 
-	value = svGetValueStr_cp (ifcfg, "DEVICE");
+	value = svGetValueStr (ifcfg, "DEVICE", &value_to_free);
 	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "mandatory DEVICE keyword missing");
 		return NULL;
 	}
-	g_free (value);
+	nm_clear_g_free (&value_to_free);
 
 	s_bridge = NM_SETTING_BRIDGE (nm_setting_bridge_new ());
 
-	value = svGetValueStr_cp (ifcfg, "MACADDR");
+	value = svGetValueStr (ifcfg, "BRIDGE_MACADDR", &value_to_free);
 	if (value) {
-		value = g_strstrip (value);
 		g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, value, NULL);
-		g_free (value);
+		nm_clear_g_free (&value_to_free);
 	}
 
-	value = svGetValueStr_cp (ifcfg, "STP");
+	value = svGetValueStr (ifcfg, "STP", &value_to_free);
 	if (value) {
 		if (!strcasecmp (value, "on") || !strcasecmp (value, "yes")) {
 			g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, TRUE, NULL);
@@ -4751,7 +4857,7 @@ make_bridge_setting (shvarFile *ifcfg,
 			stp_set = TRUE;
 		} else
 			PARSE_WARNING ("invalid STP value '%s'", value);
-		g_free (value);
+		nm_clear_g_free (&value_to_free);
 	}
 
 	if (!stp_set) {
@@ -4759,25 +4865,19 @@ make_bridge_setting (shvarFile *ifcfg,
 		g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL);
 	}
 
-	value = svGetValueStr_cp (ifcfg, "DELAY");
+	value = svGetValueStr (ifcfg, "DELAY", &value_to_free);
 	if (value) {
-		if (stp) {
-			if (get_uint (value, &u))
-				g_object_set (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, u, NULL);
-			else
-				PARSE_WARNING ("invalid forward delay value '%s'", value);
-		} else
-			PARSE_WARNING ("DELAY invalid when STP is disabled");
-		g_free (value);
+		handle_bridge_option (NM_SETTING (s_bridge), stp, "DELAY", value, BRIDGE_OPT_TYPE_MAIN);
+		nm_clear_g_free (&value_to_free);
 	}
 
-	value = svGetValueStr_cp (ifcfg, "BRIDGING_OPTS");
+	value = svGetValueStr (ifcfg, "BRIDGING_OPTS", &value_to_free);
 	if (value) {
-		handle_bridging_opts (NM_SETTING (s_bridge), stp, value, handle_bridge_option);
-		g_free (value);
+		handle_bridging_opts (NM_SETTING (s_bridge), stp, value, handle_bridge_option, BRIDGE_OPT_TYPE_OPTION);
+		nm_clear_g_free (&value_to_free);
 	}
 
-	return (NMSetting *) s_bridge;
+	return (NMSetting *) g_steal_pointer (&s_bridge);
 }
 
 static NMConnection *
@@ -4788,6 +4888,8 @@ bridge_connection_from_ifcfg (const char *file,
 	NMConnection *connection = NULL;
 	NMSetting *con_setting = NULL;
 	NMSetting *bridge_setting = NULL;
+	NMSetting *wired_setting = NULL;
+	NMSetting8021x *s_8021x = NULL;
 
 	g_return_val_if_fail (file != NULL, NULL);
 	g_return_val_if_fail (ifcfg != NULL, NULL);
@@ -4810,57 +4912,40 @@ bridge_connection_from_ifcfg (const char *file,
 	}
 	nm_connection_add_setting (connection, bridge_setting);
 
-	return connection;
-}
+	wired_setting = make_wired_setting (ifcfg, file, &s_8021x, error);
+	if (!wired_setting) {
+		g_object_unref (connection);
+		return NULL;
+	}
+	nm_connection_add_setting (connection, wired_setting);
 
-static void
-handle_bridge_port_option (NMSetting *setting,
-                           gboolean stp,
-                           const char *key,
-                           const char *value)
-{
-	guint32 u = 0;
+	if (s_8021x)
+		nm_connection_add_setting (connection, NM_SETTING (s_8021x));
 
-	if (!strcmp (key, "priority")) {
-		if (get_uint (value, &u))
-			g_object_set (setting, NM_SETTING_BRIDGE_PORT_PRIORITY, u, NULL);
-		else
-			PARSE_WARNING ("invalid priority value '%s'", value);
-	} else if (!strcmp (key, "path_cost")) {
-		if (get_uint (value, &u))
-			g_object_set (setting, NM_SETTING_BRIDGE_PORT_PATH_COST, u, NULL);
-		else
-			PARSE_WARNING ("invalid path_cost value '%s'", value);
-	} else if (!strcmp (key, "hairpin_mode")) {
-		if (!strcasecmp (value, "on") || !strcasecmp (value, "yes") || !strcmp (value, "1"))
-			g_object_set (setting, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, TRUE, NULL);
-		else if (!strcasecmp (value, "off") || !strcasecmp (value, "no"))
-			g_object_set (setting, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, FALSE, NULL);
-		else
-			PARSE_WARNING ("invalid hairpin_mode value '%s'", value);
-	} else
-			PARSE_WARNING ("unhandled bridge port option '%s'", key);
+	return connection;
 }
 
 static NMSetting *
 make_bridge_port_setting (shvarFile *ifcfg)
 {
 	NMSetting *s_port = NULL;
-	char *value;
+	gs_free char *value_to_free = NULL;
+	const char *value;
 
 	g_return_val_if_fail (ifcfg != NULL, FALSE);
 
-	value = svGetValueStr_cp (ifcfg, "BRIDGE_UUID");
+	value = svGetValueStr (ifcfg, "BRIDGE_UUID", &value_to_free);
 	if (!value)
-		value = svGetValueStr_cp (ifcfg, "BRIDGE");
+		value = svGetValueStr (ifcfg, "BRIDGE", &value_to_free);
 	if (value) {
-		g_free (value);
+		nm_clear_g_free (&value_to_free);
 
 		s_port = nm_setting_bridge_port_new ();
-		value = svGetValueStr_cp (ifcfg, "BRIDGING_OPTS");
-		if (value)
-			handle_bridging_opts (s_port, FALSE, value, handle_bridge_port_option);
-		g_free (value);
+		value = svGetValueStr (ifcfg, "BRIDGING_OPTS", &value_to_free);
+		if (value) {
+			handle_bridging_opts (s_port, FALSE, value, handle_bridge_option, BRIDGE_OPT_TYPE_PORT_OPTION);
+			nm_clear_g_free (&value_to_free);
+		}
 	}
 
 	return s_port;
@@ -5206,10 +5291,11 @@ connection_from_file_full (const char *filename,
                            gboolean *out_ignore_error)
 {
 	nm_auto_shvar_file_close shvarFile *parsed = NULL;
+	nm_auto_shvar_file_close shvarFile *network_ifcfg = 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, *s_user;
+	NMSetting *s_ip4, *s_ip6, *s_tc, *s_proxy, *s_port, *s_dcb = NULL, *s_user;
 	const char *ifcfg_name = NULL;
 	gboolean has_ip4_defroute = FALSE;
 	gboolean has_complex_routes_v4;
@@ -5233,6 +5319,8 @@ connection_from_file_full (const char *filename,
 	if (!parsed)
 		return NULL;
 
+	network_ifcfg = svOpenFile (network_file, NULL);
+
 	if (!svGetValueBoolean (parsed, "NM_CONTROLLED", TRUE)) {
 		connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled);
 		if (!connection) {
@@ -5442,7 +5530,7 @@ connection_from_file_full (const char *filename,
 	}
 
 	s_ip6 = make_ip6_setting (parsed,
-	                          network_file,
+	                          network_ifcfg,
 	                          !has_complex_routes_v4 && !has_complex_routes_v6,
 	                          error);
 	if (!s_ip6)
@@ -5451,7 +5539,7 @@ connection_from_file_full (const char *filename,
 		nm_connection_add_setting (connection, s_ip6);
 
 	s_ip4 = make_ip4_setting (parsed,
-	                          network_file,
+	                          network_ifcfg,
 	                          !has_complex_routes_v4 && !has_complex_routes_v6,
 	                          &has_ip4_defroute,
 	                          error);
@@ -5464,17 +5552,21 @@ connection_from_file_full (const char *filename,
 		nm_connection_add_setting (connection, s_ip4);
 	}
 
-	/* There is only one DOMAIN variable and it is read and put to IPv4 config
-	 * But if IPv4 is disabled or the config fails for some reason, we read
-	 * DOMAIN and put the values into IPv6 config instead.
+	s_tc = make_tc_setting (parsed);
+	if (s_tc)
+		nm_connection_add_setting (connection, s_tc);
+
+	/* For backwards compatibility, if IPv4 is disabled or the
+	 * config fails for some reason, we read DOMAIN and put the
+	 * values into IPv6 config instead of IPv4.
 	 */
 	check_dns_search_domains (parsed, s_ip4, s_ip6);
 
-	s_proxy = make_proxy_setting (parsed, error);
+	s_proxy = make_proxy_setting (parsed);
 	if (s_proxy)
 		nm_connection_add_setting (connection, s_proxy);
 
-	s_user = make_user_setting (parsed, error);
+	s_user = make_user_setting (parsed);
 	if (s_user)
 		nm_connection_add_setting (connection, s_user);
 
@@ -5488,7 +5580,7 @@ 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))
+	if (!make_dcb_setting (parsed, &s_dcb, error))
 		return NULL;
 	if (s_dcb)
 		nm_connection_add_setting (connection, s_dcb);
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 6434ad7d..862e640e 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.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.
  *
- * (C) Copyright 2008 - 2012 Red Hat, Inc.
+ * (C) Copyright 2008 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -243,12 +243,6 @@ utils_get_route_ifcfg (const char *parent, gboolean should_create)
 	return utils_get_extra_ifcfg (parent, ROUTE_TAG, should_create);
 }
 
-shvarFile *
-utils_get_route6_ifcfg (const char *parent, gboolean should_create)
-{
-	return utils_get_extra_ifcfg (parent, ROUTE6_TAG, should_create);
-}
-
 /* Finds out if route file has new or older format
  * Returns TRUE  - new syntax (ADDRESS<n>=a.b.c.d ...), error opening file or empty
  *         FALSE - older syntax, i.e. argument to 'ip route add' (1.2.3.0/24 via 11.22.33.44)
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 e7abf4d8..3756af7c 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.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.
  *
- * (C) Copyright 2008 - 2012 Red Hat, Inc.
+ * (C) Copyright 2008 - 2017 Red Hat, Inc.
  */
 
 #ifndef _UTILS_H_
@@ -45,7 +45,6 @@ char *utils_get_route6_path (const char *parent);
 shvarFile *utils_get_extra_ifcfg (const char *parent, const char *tag, gboolean should_create);
 shvarFile *utils_get_keys_ifcfg (const char *parent, gboolean should_create);
 shvarFile *utils_get_route_ifcfg (const char *parent, gboolean should_create);
-shvarFile *utils_get_route6_ifcfg (const char *parent, gboolean should_create);
 
 gboolean utils_has_route_file_new_syntax (const char *filename);
 gboolean utils_has_complex_routes (const char *filename, int addr_family);
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 5c8de7d1..5cb8ee98 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -150,7 +150,7 @@ write_secrets (shvarFile *ifcfg,
 
 	/* sort the keys. */
 	secrets_keys = (const char **) g_hash_table_get_keys_as_array (secrets, &secrets_keys_n);
-	if (secrets_keys) {
+	if (secrets_keys_n > 1) {
 		g_qsort_with_data (secrets_keys,
 		                   secrets_keys_n,
 		                   sizeof (const char *),
@@ -662,12 +662,26 @@ write_wireless_security_setting (NMConnection *connection,
 
 	/* And write the new ones out */
 	if (wep) {
+		NMWepKeyType key_type;
+		const char *key_type_str = NULL;
+
 		/* Default WEP TX key index */
 		svSetValueInt64 (ifcfg, "DEFAULTKEY", nm_setting_wireless_security_get_wep_tx_keyidx(s_wsec) + 1);
 
-		for (i = 0; i < 4; i++) {
-			NMWepKeyType key_type;
+		key_type = nm_setting_wireless_security_get_wep_key_type (s_wsec);
+		switch (key_type) {
+		case NM_WEP_KEY_TYPE_KEY:
+			key_type_str = "key";
+			break;
+		case NM_WEP_KEY_TYPE_PASSPHRASE:
+			key_type_str = "passphrase";
+			break;
+		case NM_WEP_KEY_TYPE_UNKNOWN:
+			break;
+		}
+		svSetValue (ifcfg, "KEY_TYPE", key_type_str);
 
+		for (i = 0; i < 4; i++) {
 			key = nm_setting_wireless_security_get_wep_key (s_wsec, i);
 			if (key) {
 				gs_free char *ascii_key = NULL;
@@ -678,7 +692,6 @@ write_wireless_security_setting (NMConnection *connection,
 				 * are some passphrases that are indistinguishable from WEP hex
 				 * keys.
 				 */
-				key_type = nm_setting_wireless_security_get_wep_key_type (s_wsec);
 				if (key_type == NM_WEP_KEY_TYPE_UNKNOWN) {
 					if (nm_utils_wep_key_valid (key, NM_WEP_KEY_TYPE_KEY))
 						key_type = NM_WEP_KEY_TYPE_KEY;
@@ -1309,25 +1322,19 @@ write_bond_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 	svUnsetValue (ifcfg, "BONDING_OPTS");
 
 	num_opts = nm_setting_bond_get_num_options (s_bond);
-	if (num_opts > 0) {
-		GString *str = g_string_sized_new (64);
-
-		for (i = 0; i < nm_setting_bond_get_num_options (s_bond); i++) {
-			const char *key, *value;
-
-			if (!nm_setting_bond_get_option (s_bond, i, &key, &value))
-				continue;
+	if (num_opts) {
+		nm_auto_free_gstring GString *str = NULL;
+		const char *name, *value;
 
+		str = g_string_sized_new (64);
+		for (i = 0; i < num_opts; i++) {
 			if (str->len)
 				g_string_append_c (str, ' ');
-
-			g_string_append_printf (str, "%s=%s", key, value);
+			nm_setting_bond_get_option (s_bond, i, &name, &value);
+			g_string_append_printf (str, "%s=%s", name, value);
 		}
 
-		if (str->len)
-			svSetValueStr (ifcfg, "BONDING_OPTS", str->str);
-
-		g_string_free (str, TRUE);
+		svSetValueStr (ifcfg, "BONDING_OPTS", str->str);
 	}
 
 	svSetValueStr (ifcfg, "TYPE", TYPE_BOND);
@@ -1414,7 +1421,7 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wire
 	svUnsetValue (ifcfg, "DELAY");
 
 	mac = nm_setting_bridge_get_mac_address (s_bridge);
-	svSetValueStr (ifcfg, "MACADDR", mac);
+	svSetValueStr (ifcfg, "BRIDGE_MACADDR", mac);
 
 	/* Bridge options */
 	opts = g_string_sized_new (32);
@@ -1928,8 +1935,11 @@ get_route_attributes_string (NMIPRoute *route, int family)
 			g_string_append_printf (str, "%s 0x%02x", names[i], (unsigned) g_variant_get_byte (attr));
 		} else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_TABLE)) {
 			g_string_append_printf (str, "%s %u", names[i], (unsigned) g_variant_get_uint32 (attr));
-		} else if (   nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC)
-		           || nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_FROM)) {
+		} else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_ONLINK)) {
+			if (g_variant_get_boolean (attr))
+				g_string_append (str, "onlink");
+		} else if (NM_IN_STRSET (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC,
+		                                   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));
@@ -2116,6 +2126,73 @@ write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 }
 
 static gboolean
+write_tc_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
+{
+	NMSettingTCConfig *s_tc;
+	guint i, num, n;
+	char tag[64];
+
+	svUnsetAll (ifcfg, SV_KEY_TYPE_TC);
+
+	s_tc = nm_connection_get_setting_tc_config (connection);
+	if (!s_tc)
+		return TRUE;
+
+	num = nm_setting_tc_config_get_num_qdiscs (s_tc);
+	for (n = 1, i = 0; i < num; i++) {
+		NMTCQdisc *qdisc;
+		gs_free char *str = NULL;
+
+		qdisc = nm_setting_tc_config_get_qdisc (s_tc, i);
+		str = nm_utils_tc_qdisc_to_str (qdisc, error);
+		if (!str)
+			return FALSE;
+
+		svSetValueStr (ifcfg, numbered_tag (tag, "QDISC", n), str);
+		n++;
+	}
+
+
+	num = nm_setting_tc_config_get_num_tfilters (s_tc);
+	for (n = 1, i = 0; i < num; i++) {
+		NMTCTfilter *tfilter;
+		gs_free char *str = NULL;
+
+		tfilter = nm_setting_tc_config_get_tfilter (s_tc, i);
+		str = nm_utils_tc_tfilter_to_str (tfilter, error);
+		if (!str)
+			return FALSE;
+
+		svSetValueStr (ifcfg, numbered_tag (tag, "FILTER", n), str);
+		n++;
+	}
+
+	return TRUE;
+}
+
+static void
+write_res_options (shvarFile *ifcfg, NMSettingIPConfig *s_ip, const char *var)
+{
+	nm_auto_free_gstring GString *value = NULL;
+	guint i, num_options;
+
+	if (!nm_setting_ip_config_has_dns_options (s_ip)) {
+		svUnsetValue (ifcfg, var);
+		return;
+	}
+
+	value = g_string_new (NULL);
+	num_options = nm_setting_ip_config_get_num_dns_options (s_ip);
+	for (i = 0; i < num_options; i++) {
+		if (i > 0)
+			g_string_append_c (value, ' ');
+		g_string_append (value, nm_setting_ip_config_get_dns_option (s_ip, i));
+	}
+
+	svSetValue (ifcfg, var, value->str);
+}
+
+static gboolean
 write_ip4_setting (NMConnection *connection,
                    shvarFile *ifcfg,
                    shvarFile **out_route_content_svformat,
@@ -2146,6 +2223,7 @@ write_ip4_setting (NMConnection *connection,
 		 * Some IPv4 setting related options are not cleared,
 		 * for no strong reason. */
 		svUnsetValue (ifcfg, "BOOTPROTO");
+		svUnsetValue (ifcfg, "RES_OPTIONS");
 		svUnsetAll (ifcfg, SV_KEY_TYPE_IP4_ADDRESS);
 		return TRUE;
 	}
@@ -2342,6 +2420,8 @@ write_ip4_setting (NMConnection *connection,
 	else
 		svUnsetValue (ifcfg, "IPV4_DNS_PRIORITY");
 
+	write_res_options (ifcfg, s_ip4, "RES_OPTIONS");
+
 	return TRUE;
 }
 
@@ -2483,6 +2563,7 @@ write_ip6_setting (NMConnection *connection,
 		svUnsetValue (ifcfg, "IPV6_FAILURE_FATAL");
 		svUnsetValue (ifcfg, "IPV6_ROUTE_METRIC");
 		svUnsetValue (ifcfg, "IPV6_ADDR_GEN_MODE");
+		svUnsetValue (ifcfg, "IPV6_RES_OPTIONS");
 		return TRUE;
 	}
 
@@ -2557,21 +2638,20 @@ write_ip6_setting (NMConnection *connection,
 		}
 	}
 
-	/* Write out DNS domains - 'DOMAIN' key is shared for both IPv4 and IPv6 domains */
+	/* Write out DNS domains */
 	num = nm_setting_ip_config_get_num_dns_searches (s_ip6);
 	if (num > 0) {
-		gs_free char *ip4_domains = NULL;
 		nm_auto_free_gstring GString *searches = NULL;
 
-		searches = g_string_new (svGetValueStr (ifcfg, "DOMAIN", &ip4_domains));
+		searches = g_string_new (NULL);
 		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));
 		}
-		svSetValueStr (ifcfg, "DOMAIN", searches->str);
-	}
-
+		svSetValueStr (ifcfg, "IPV6_DOMAIN", searches->str);
+	} else
+		svUnsetValue (ifcfg, "IPV6_DOMAIN");
 
 	/* handle IPV6_DEFROUTE */
 	/* IPV6_DEFROUTE has the opposite meaning from 'never-default' */
@@ -2638,66 +2718,9 @@ write_ip6_setting (NMConnection *connection,
 	else
 		svUnsetValue (ifcfg, "IPV6_DNS_PRIORITY");
 
-	NM_SET_OUT (out_route6_content, write_route_file (s_ip6));
+	write_res_options (ifcfg, s_ip6, "IPV6_RES_OPTIONS");
 
-	return TRUE;
-}
-
-static void
-add_dns_option (GPtrArray *array, const char *option)
-{
-	if (_nm_utils_dns_option_find_idx (array, option) < 0)
-		g_ptr_array_add (array, (gpointer) option);
-}
-
-static gboolean
-write_res_options (NMConnection *connection, shvarFile *ifcfg, GError **error)
-{
-	NMSettingIPConfig *s_ip6;
-	NMSettingIPConfig *s_ip4;
-	const char *method;
-	int i, num_options;
-	gs_unref_ptrarray GPtrArray *array = NULL;
-	GString *value;
-
-	s_ip4 = nm_connection_get_setting_ip4_config (connection);
-
-	if (!s_ip4) {
-		/* slave-type: clear res-options */
-		svUnsetValue (ifcfg, "RES_OPTIONS");
-		return TRUE;
-	}
-
-	array = g_ptr_array_new ();
-
-	method = nm_setting_ip_config_get_method (s_ip4);
-	if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) {
-		num_options = nm_setting_ip_config_get_num_dns_options (s_ip4);
-		for (i = 0; i < num_options; i++)
-			add_dns_option (array, nm_setting_ip_config_get_dns_option (s_ip4, i));
-	}
-
-	s_ip6 = nm_connection_get_setting_ip6_config (connection);
-	method = nm_setting_ip_config_get_method (s_ip6);
-	if (g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
-		num_options = nm_setting_ip_config_get_num_dns_options (s_ip6);
-		for (i = 0; i < num_options; i++)
-			add_dns_option (array, nm_setting_ip_config_get_dns_option (s_ip6, i));
-	}
-
-	if (   array->len > 0
-	    || nm_setting_ip_config_has_dns_options (s_ip4)
-	    || nm_setting_ip_config_has_dns_options (s_ip6)) {
-		value = g_string_new (NULL);
-		for (i = 0; i < array->len; i++) {
-			if (i > 0)
-				g_string_append_c (value, ' ');
-			g_string_append (value, array->pdata[i]);
-		}
-		svSetValue (ifcfg, "RES_OPTIONS", value->str);
-		g_string_free (value, TRUE);
-	} else
-		svUnsetValue (ifcfg, "RES_OPTIONS");
+	NM_SET_OUT (out_route6_content, write_route_file (s_ip6));
 
 	return TRUE;
 }
@@ -2882,6 +2905,9 @@ do_write_construct (NMConnection *connection,
 	if (!write_user_setting (connection, ifcfg, error))
 		return FALSE;
 
+	if (!write_tc_setting (connection, ifcfg, error))
+		return FALSE;
+
 	svUnsetValue (ifcfg, "DHCP_HOSTNAME");
 	svUnsetValue (ifcfg, "DHCP_FQDN");
 
@@ -2930,9 +2956,6 @@ do_write_construct (NMConnection *connection,
 	                        error))
 		return FALSE;
 
-	if (!write_res_options (connection, ifcfg, error))
-		return FALSE;
-
 	write_connection_setting (s_con, ifcfg);
 
 	NM_SET_OUT (out_ifcfg, g_steal_pointer (&ifcfg));
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index df03bf65..2b64f3fc 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -776,7 +776,7 @@ line_free (shvarLine *line)
 	ASSERT_shvarLine (line);
 	g_free (line->line);
 	g_free (line->key_with_prefix);
-	c_list_unlink (&line->lst);
+	c_list_unlink_stale (&line->lst);
 	g_slice_free (shvarLine, line);
 }
 
@@ -1171,6 +1171,11 @@ svUnsetAll (shvarFile *s, SvKeyType match_key_type)
 			if (g_str_has_prefix (line->key, "NM_USER_"))
 				goto do_clear;
 		}
+		if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_TC)) {
+			if (   IS_NUMBERED_TAG (line->key, "QDISC")
+			    || IS_NUMBERED_TAG (line->key, "FILTER"))
+				goto do_clear;
+		}
 
 		continue;
 do_clear:
@@ -1318,7 +1323,7 @@ svWriteFile (shvarFile *s, int mode, GError **error)
 			return FALSE;
 		}
 
-		tmpfd = dup (s->fd);
+		tmpfd = fcntl (s->fd, F_DUPFD_CLOEXEC, 0);
 		if (tmpfd == -1) {
 			int errsv = errno;
 
@@ -1373,7 +1378,8 @@ svCloseFile (shvarFile *s)
 
 	g_return_if_fail (s != NULL);
 
-	nm_close (s->fd);
+	if (s->fd >= 0)
+		nm_close (s->fd);
 	g_free (s->fileName);
 	c_list_for_each_safe (current, safe, &s->lst_head)
 		line_free (c_list_entry (current, shvarLine, lst));
diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h
index c48bbfd3..dbc4d950 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/settings/plugins/ifcfg-rh/shvar.h
@@ -90,7 +90,8 @@ typedef enum {
 	SV_KEY_TYPE_ANY                     = (1LL << 0),
 	SV_KEY_TYPE_ROUTE_SVFORMAT          = (1LL << 1),
 	SV_KEY_TYPE_IP4_ADDRESS             = (1LL << 2),
-	SV_KEY_TYPE_USER                    = (1LL << 3),
+	SV_KEY_TYPE_TC                      = (1LL << 3),
+	SV_KEY_TYPE_USER                    = (1LL << 4),
 } SvKeyType;
 
 gboolean svUnsetAll (shvarFile *s, SvKeyType match_key_type);
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
index 854d2490..5d81dfef 100644
--- 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
@@ -1,4 +1,4 @@
-BONDING_OPTS=mode=balance-rr
+BONDING_OPTS="downdelay=5 miimon=100 mode=balance-rr updelay=10"
 TYPE=Bond
 BONDING_MASTER=yes
 PROXY_METHOD=none
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected
index 460278e1..2df1fbb3 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected
@@ -1,6 +1,6 @@
 CONNECTED_MODE=no
 TYPE=InfiniBand
-TEAM_PORT_CONFIG="{ \"inf1\": { \"prio\": -10, \"sticky\": true } }"
+TEAM_PORT_CONFIG="{\"inf1\": {\"prio\": -10, \"sticky\": true}}"
 NAME="Test Write Team Infiniband Port"
 UUID=${UUID}
 DEVICE=inf1
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
index 0b1deb80..ff55cefe 100644
--- 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
@@ -1,4 +1,4 @@
-TEAM_PORT_CONFIG="{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }"
+TEAM_PORT_CONFIG="{\"p4p1\": {\"prio\": -10, \"sticky\": true}}"
 NAME="Test Write Team Port"
 UUID=${UUID}
 ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected
new file mode 100644
index 00000000..c0e47c48
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected
@@ -0,0 +1,20 @@
+HWADDR=31:33:33:37:BE:CD
+MTU=1492
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=none
+IPADDR=1.1.1.3
+PREFIX=24
+IPADDR1=1.1.1.5
+PREFIX1=24
+GATEWAY=1.1.1.1
+DNS1=4.2.2.1
+DNS2=4.2.2.2
+DOMAIN="foobar.com lab.foobar.com"
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write Wired Static Routes"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dns-options b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dns-options
index 62e301e3..cea2471a 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dns-options
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-dns-options
@@ -8,7 +8,8 @@ NM_CONTROLLED=yes
 PEERDNS=yes
 DNS1=10.2.0.4
 DOMAIN="lorem.com ipsum.org dolor.edu"
-RES_OPTIONS="ndots:3 single-request-reopen  inet6"
+RES_OPTIONS="ndots:3  single-request-reopen"
+IPV6_RES_OPTIONS="inet6"
 IPV6INIT=yes
 IPV6_AUTOCONF=no
 IPV6ADDR="1001:abba::1234/56"
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-1 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-1
index 7edc736a..209447b8 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-1
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-1
@@ -2,5 +2,5 @@ DEVICE=team0
 ONBOOT=no
 DEVICETYPE=Team
 BOOTPROTO=dhcp
-TEAM_CONFIG="{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }"
+TEAM_CONFIG="{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}"
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-2
index d01e37c5..26e448cc 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-2
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-master-2
@@ -1,5 +1,5 @@
 DEVICE=team0
 ONBOOT=no
 BOOTPROTO=dhcp
-TEAM_CONFIG="{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }"
+TEAM_CONFIG="{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}"
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-1 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-1
index 966bec67..80355c26 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-1
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-1
@@ -1,5 +1,5 @@
 TYPE=Ethernet
-TEAM_PORT_CONFIG="{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }"
+TEAM_PORT_CONFIG="{\"p4p1\": {\"prio\": -10, \"sticky\": true}}"
 DEVICE=p4p1
 TEAM_MASTER=team0
 DEVICETYPE=TeamPort
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-2 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-2
index 992510ee..4284737a 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-2
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-team-port-2
@@ -1,4 +1,4 @@
 TYPE=Ethernet
-TEAM_PORT_CONFIG="{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }"
+TEAM_PORT_CONFIG="{\"p4p1\": {\"prio\": -10, \"sticky\": true}}"
 DEVICE=p4p1
 TEAM_MASTER=team0
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-only b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-only
index 59ec32e5..94189064 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-only
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-ipv6-only
@@ -7,7 +7,7 @@ USERCTL=yes
 NM_CONTROLLED=yes
 PEERDNS=yes
 DNS1=1:2:3:4::a
-DOMAIN="lorem.com ipsum.org dolor.edu"
+IPV6_DOMAIN="lorem.com ipsum.org dolor.edu"
 IPV6INIT=yes
 IPV6_AUTOCONF=no
 IPV6ADDR="1001:abba::1234/56"
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static
index 6d49c01c..34acf9fe 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-wired-static
@@ -19,3 +19,4 @@ IPV6ADDR_SECONDARIES="dead:beaf::2/56"
 DNS3=1:2:3:4::a
 DNS4=1:2:3:4::b
 RES_OPTIONS=
+IPV6_RES_OPTIONS=
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 10a63b67..8d6aaac2 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
@@ -7,3 +7,9 @@ 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"
+
+ADDRESS2=44.55.66.78
+NETMASK2=255.255.255.255
+GATEWAY2=192.168.1.8
+METRIC2=3
+OPTIONS2="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 onlink window 30000 initcwnd lock 13 initrwnd 14"
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 5044a2d7..6bf27556 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -1318,7 +1318,7 @@ test_read_wired_static_routes (void)
 	g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
 
 	/* Routes */
-	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 2);
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 3);
 
 	ip4_route = nm_setting_ip_config_get_route (s_ip4, 0);
 	g_assert (ip4_route);
@@ -1343,6 +1343,23 @@ test_read_wired_static_routes (void)
 	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");
 
+	ip4_route = nm_setting_ip_config_get_route (s_ip4, 2);
+	g_assert (ip4_route);
+	g_assert_cmpstr (nm_ip_route_get_dest (ip4_route), ==, "44.55.66.78");
+	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.8");
+	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");
+	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, TRUE);
+
 	g_object_unref (connection);
 }
 
@@ -2145,8 +2162,9 @@ test_read_dns_options (void)
 	NMSettingIPConfig *s_ip4, *s_ip6;
 	char *unmanaged = NULL;
 	const char *option;
-	const char *options[] = { "ndots:3", "single-request-reopen", "inet6" };
-	guint32 i, options_len = sizeof (options) / sizeof (options[0]);
+	const char *options4[] = { "ndots:3", "single-request-reopen" };
+	const char *options6[] = { "inet6" };
+	guint32 i, num;
 
 	connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-dns-options",
 	                                    NULL, TYPE_ETHERNET, &unmanaged);
@@ -2158,18 +2176,20 @@ test_read_dns_options (void)
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	g_assert (s_ip6);
 
-	i = nm_setting_ip_config_get_num_dns_options (s_ip4);
-	g_assert_cmpint (i, ==, options_len);
-
-	i = nm_setting_ip_config_get_num_dns_options (s_ip6);
-	g_assert_cmpint (i, ==, options_len);
+	num = nm_setting_ip_config_get_num_dns_options (s_ip4);
+	g_assert_cmpint (num, ==, G_N_ELEMENTS (options4));
 
-	for (i = 0; i < options_len; i++) {
+	for (i = 0; i < num; i++) {
 		option = nm_setting_ip_config_get_dns_option (s_ip4, i);
-		g_assert_cmpstr (options[i], ==, option);
+		g_assert_cmpstr (options4[i], ==, option);
+	}
+
+	num = nm_setting_ip_config_get_num_dns_options (s_ip6);
+	g_assert_cmpint (num, ==, G_N_ELEMENTS (options6));
 
+	for (i = 0; i < num; i++) {
 		option = nm_setting_ip_config_get_dns_option (s_ip6, i);
-		g_assert_cmpstr (options[i], ==, option);
+		g_assert_cmpstr (options6[i], ==, option);
 	}
 
 	g_object_unref (connection);
@@ -2280,6 +2300,8 @@ test_write_dns_options (void)
 	nm_setting_ip_config_add_address (s_ip4, addr);
 	nm_ip_address_unref (addr);
 
+	nm_setting_ip_config_add_dns_option (s_ip4, "debug");
+
 	/* IP6 setting */
 	s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
@@ -2295,26 +2317,16 @@ test_write_dns_options (void)
 	nm_setting_ip_config_add_address (s_ip6, addr6);
 	nm_ip_address_unref (addr6);
 
-	nm_setting_ip_config_add_dns_option (s_ip4, "debug");
 	nm_setting_ip_config_add_dns_option (s_ip6, "timeout:3");
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection_FIXME (connection,
-	                              TEST_SCRATCH_DIR "/network-scripts/",
-	                              &testfile);
+	_writer_new_connection (connection,
+	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
 
-	/* RES_OPTIONS is copied to both IPv4 and IPv6 settings */
-	nm_setting_ip_config_clear_dns_options (s_ip4, TRUE);
-	nm_setting_ip_config_add_dns_option (s_ip4, "debug");
-	nm_setting_ip_config_add_dns_option (s_ip4, "timeout:3");
-
-	nm_setting_ip_config_clear_dns_options (s_ip6, TRUE);
-	nm_setting_ip_config_add_dns_option (s_ip6, "debug");
-	nm_setting_ip_config_add_dns_option (s_ip6, "timeout:3");
-
 	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
 }
 
@@ -2674,7 +2686,7 @@ test_read_wifi_wep_passphrase (void)
 	g_assert (s_wsec);
 	g_assert_cmpstr (nm_setting_wireless_security_get_key_mgmt (s_wsec), ==, "none");
 	g_assert_cmpint (nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec), ==, 0);
-	g_assert_cmpint (nm_setting_wireless_security_get_wep_key_type (s_wsec), ==, NM_WEP_KEY_TYPE_PASSPHRASE);
+	g_assert_cmpint (nm_setting_wireless_security_get_wep_key_type (s_wsec), ==, NM_WEP_KEY_TYPE_UNKNOWN);
 	g_assert_cmpstr (nm_setting_wireless_security_get_wep_key (s_wsec, 0), ==, "foobar222blahblah");
 	g_assert (!nm_setting_wireless_security_get_wep_key (s_wsec, 1));
 	g_assert (!nm_setting_wireless_security_get_wep_key (s_wsec, 2));
@@ -4175,23 +4187,15 @@ test_write_wired_static (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection_FIXME (connection,
-	                              TEST_SCRATCH_DIR "/network-scripts/",
-	                              &testfile);
+	_writer_new_connection (connection,
+	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        &testfile);
 	route6file = utils_get_route6_path (testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
 
-	/* FIXME: currently DNS domains from IPv6 setting are stored in 'DOMAIN' key in ifcfg-file
-	 * However after re-reading they are dropped into IPv4 setting.
-	 * So, in order to comparison succeeded, move DNS domains back to IPv6 setting.
-	 */
 	reread_s_ip4 = nm_connection_get_setting_ip4_config (reread);
 	reread_s_ip6 = nm_connection_get_setting_ip6_config (reread);
-	nm_setting_ip_config_add_dns_search (reread_s_ip6, nm_setting_ip_config_get_dns_search (reread_s_ip4, 2));
-	nm_setting_ip_config_add_dns_search (reread_s_ip6, nm_setting_ip_config_get_dns_search (reread_s_ip4, 3));
-	nm_setting_ip_config_remove_dns_search (reread_s_ip4, 3);
-	nm_setting_ip_config_remove_dns_search (reread_s_ip4, 2);
 
 	g_assert_cmpint (nm_setting_ip_config_get_route_metric (reread_s_ip4), ==, 204);
 	g_assert_cmpint (nm_setting_ip_config_get_route_metric (reread_s_ip6), ==, 206);
@@ -4329,17 +4333,8 @@ test_write_wired_static_with_generic (void)
 	route6file = utils_get_route6_path (testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
-
-	/* FIXME: currently DNS domains from IPv6 setting are stored in 'DOMAIN' key in ifcfg-file
-	 * However after re-reading they are dropped into IPv4 setting.
-	 * So, in order to comparison succeeded, move DNS domains back to IPv6 setting.
-	 */
 	reread_s_ip4 = nm_connection_get_setting_ip4_config (reread);
 	reread_s_ip6 = nm_connection_get_setting_ip6_config (reread);
-	nm_setting_ip_config_add_dns_search (reread_s_ip6, nm_setting_ip_config_get_dns_search (reread_s_ip4, 2));
-	nm_setting_ip_config_add_dns_search (reread_s_ip6, nm_setting_ip_config_get_dns_search (reread_s_ip4, 3));
-	nm_setting_ip_config_remove_dns_search (reread_s_ip4, 3);
-	nm_setting_ip_config_remove_dns_search (reread_s_ip4, 2);
 
 	g_assert_cmpint (nm_setting_ip_config_get_route_metric (reread_s_ip4), ==, 204);
 	g_assert_cmpint (nm_setting_ip_config_get_route_metric (reread_s_ip6), ==, 206);
@@ -4748,6 +4743,7 @@ test_write_wired_static_routes (void)
 	NMIPAddress *addr;
 	NMIPRoute *route;
 	GError *error = NULL;
+	gboolean reread_same = FALSE;
 
 	connection = nm_simple_connection_new ();
 
@@ -4792,11 +4788,15 @@ test_write_wired_static_routes (void)
 
 	/* Write out routes */
 	route = nm_ip_route_new (AF_INET, "1.2.3.0", 24, "222.173.190.239", 0, &error);
+	nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, g_variant_new_uint32 (3455));
+	nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, g_variant_new_boolean (TRUE));
 	g_assert_no_error (error);
 	nm_setting_ip_config_add_route (s_ip4, route);
 	nm_ip_route_unref (route);
 
 	route = nm_ip_route_new (AF_INET, "3.2.1.0", 24, "202.254.186.190", 77, &error);
+	nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, g_variant_new_uint32 (30000));
+	nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, g_variant_new_boolean (FALSE));
 	g_assert_no_error (error);
 	nm_setting_ip_config_add_route (s_ip4, route);
 	nm_ip_route_unref (route);
@@ -4818,15 +4818,28 @@ test_write_wired_static_routes (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_reread (connection,
+	                               TEST_SCRATCH_DIR "/network-scripts/",
+	                               &testfile,
+	                               TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected",
+	                               &reread,
+	                               &reread_same);
+	/* ifcfg does not support setting onlink=0. It gets lost during write+re-read.
+	 * Assert that it's missing, and patch it to check whether the rest of the
+	 * connection equals. */
+	g_assert (!reread_same);
+	nmtst_assert_connection_verifies_without_normalization (reread);
+	s_ip4 = nm_connection_get_setting_ip4_config (reread);
+	g_assert (s_ip4);
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 2);
+	route = nm_setting_ip_config_get_route (s_ip4, 1);
+	g_assert (route);
+	g_assert (!nm_ip_route_get_attribute (route, NM_IP_ROUTE_ATTRIBUTE_ONLINK));
+	nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, g_variant_new_boolean (FALSE));
 
-	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
+	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
 
 	routefile = utils_get_route_path (testfile);
-
-	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
 }
 
 static void
@@ -5728,6 +5741,7 @@ test_write_wifi_wep_40_ascii (void)
 	g_object_set (s_wsec,
 	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none",
 	              NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, 2,
+	              NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_KEY,
 	              NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "shared",
 	              NULL);
 	nm_setting_wireless_security_set_wep_key (s_wsec, 0, "lorem");
@@ -5815,6 +5829,7 @@ test_write_wifi_wep_104_ascii (void)
 	g_object_set (s_wsec,
 	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none",
 	              NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, 0,
+	              NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN,
 	              NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
 	              NULL);
 	nm_setting_wireless_security_set_wep_key (s_wsec, 0, "LoremIpsumSit");
@@ -7368,6 +7383,7 @@ test_read_bridge_main (void)
 {
 	NMConnection *connection;
 	NMSettingBridge *s_bridge;
+	NMSettingWired *s_wired;
 	const char *mac;
 	char expected_mac_address[ETH_ALEN] = { 0x00, 0x16, 0x41, 0x11, 0x22, 0x33 };
 
@@ -7390,7 +7406,9 @@ test_read_bridge_main (void)
 	g_assert (!nm_setting_bridge_get_multicast_snooping (s_bridge));
 
 	/* MAC address */
-	mac = nm_setting_bridge_get_mac_address (s_bridge);
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert (s_wired);
+	mac = nm_setting_wired_get_cloned_mac_address (s_wired);
 	g_assert (mac);
 	g_assert (nm_utils_hwaddr_matches (mac, -1, expected_mac_address, ETH_ALEN));
 
@@ -7405,8 +7423,8 @@ test_write_bridge_main (void)
 	gs_unref_object NMConnection *reread = NULL;
 	NMSettingConnection *s_con;
 	NMSettingBridge *s_bridge;
-	NMSettingIPConfig *s_ip4;
-	NMSettingIPConfig *s_ip6;
+	NMSettingIPConfig *s_ip4, *s_ip6;
+	NMSettingWired *s_wired;
 	NMIPAddress *addr;
 	static const char *mac = "31:33:33:37:be:cd";
 	GError *error = NULL;
@@ -7458,6 +7476,10 @@ test_write_bridge_main (void)
 	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
 	              NULL);
 
+	/* Wired setting */
+	s_wired = (NMSettingWired *) nm_setting_wired_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
 	nm_connection_add_setting (connection, nm_setting_proxy_new ());
 
 	nmtst_assert_connection_verifies_without_normalization (connection);
@@ -8023,6 +8045,10 @@ test_write_bond_main (void)
 	s_bond = (NMSettingBond *) nm_setting_bond_new ();
 	nm_connection_add_setting (connection, NM_SETTING (s_bond));
 
+	nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY, "5");
+	nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_UPDELAY, "10");
+	nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MIIMON, "100");
+
 	/* IP4 setting */
 	s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
 	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
@@ -8669,7 +8695,7 @@ test_read_team_master (gconstpointer user_data)
 	NMConnection *connection;
 	NMSettingConnection *s_con;
 	NMSettingTeam *s_team;
-	const char *expected_config = "{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }";
+	const char *expected_config = "{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}";
 
 	connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL);
 
@@ -8722,7 +8748,7 @@ test_write_team_master (void)
 	NMSettingWired *s_wired;
 	NMSettingIPConfig *s_ip4;
 	NMSettingIPConfig *s_ip6;
-	const char *expected_config = "{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }";
+	const char *expected_config = "{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}";
 	shvarFile *f;
 
 	connection = nm_simple_connection_new ();
@@ -8791,7 +8817,7 @@ test_read_team_port (gconstpointer user_data)
 	NMConnection *connection;
 	NMSettingConnection *s_con;
 	NMSettingTeamPort *s_team_port;
-	const char *expected_config = "{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }";
+	const char *expected_config = "{\"p4p1\": {\"prio\": -10, \"sticky\": true}}";
 
 	connection = _connection_from_file (PATH_NAME, NULL, TYPE_ETHERNET, NULL);
 
@@ -8816,7 +8842,7 @@ test_write_team_port (void)
 	NMSettingConnection *s_con;
 	NMSettingTeamPort *s_team_port;
 	NMSettingWired *s_wired;
-	const char *expected_config = "{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }";
+	const char *expected_config = "{\"p4p1\": {\"prio\": -10, \"sticky\": true}}";
 	shvarFile *f;
 
 	connection = nm_simple_connection_new ();
@@ -8871,7 +8897,7 @@ test_write_team_infiniband_port (void)
 	NMSettingConnection *s_con;
 	NMSettingTeamPort *s_team_port;
 	NMSettingInfiniband *s_inf;
-	const char *expected_config = "{ \"inf1\": { \"prio\": -10, \"sticky\": true } }";
+	const char *expected_config = "{\"inf1\": {\"prio\": -10, \"sticky\": true}}";
 	shvarFile *f;
 
 	connection = nm_simple_connection_new ();
@@ -9232,7 +9258,9 @@ test_svUnescape (void)
 		V0 ("Bob outside LAN", NULL),
 		V1 ("x", "x"),
 		V1 ("'{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }'",
-	       "{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }"),
+		    "{ \"device\": \"team0\", \"link_watch\": { \"name\": \"ethtool\" } }"),
+		V1 ("'{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}'",
+		    "{\"device\": \"team0\", \"link_watch\": {\"name\": \"ethtool\"}}"),
 		V1 ("x\"\"b", "xb"),
 		V1 ("x\"c\"b", "xcb"),
 		V1 ("\"c\"b", "cb"),
diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection.c b/src/settings/plugins/ifnet/nms-ifnet-connection.c
index 5dbb124c..e7cd19cd 100644
--- a/src/settings/plugins/ifnet/nms-ifnet-connection.c
+++ b/src/settings/plugins/ifnet/nms-ifnet-connection.c
@@ -93,7 +93,7 @@ commit_changes (NMSettingsConnection *connection,
 	g_signal_emit (connection, signals[IFNET_CANCEL_MONITORS], 0);
 
 	if (priv->conn_name) {
-		success = ifnet_update_parsers_by_connection (NM_CONNECTION (connection),
+		success = ifnet_update_parsers_by_connection (new_connection,
 		                                              priv->conn_name,
 		                                              CONF_NET_FILE,
 		                                              WPA_SUPPLICANT_CONF,
@@ -102,7 +102,7 @@ commit_changes (NMSettingsConnection *connection,
 		                                              error);
 	} else {
 		added = TRUE;
-		success = ifnet_add_new_connection (NM_CONNECTION (connection),
+		success = ifnet_add_new_connection (new_connection,
 		                                    CONF_NET_FILE,
 		                                    WPA_SUPPLICANT_CONF,
 		                                    &new_name,
@@ -186,11 +186,14 @@ nm_ifnet_connection_new (NMConnection *source, const char *conn_name)
 	object = (GObject *) g_object_new (NM_TYPE_IFNET_CONNECTION, NULL);
 
 	NM_IFNET_CONNECTION_GET_PRIVATE ((NMIfnetConnection *) object)->conn_name = g_strdup (conn_name);
-	if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
-	                                              tmp,
-	                                              update_unsaved,
-	                                              NULL,
-	                                              NULL)) {
+	if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object),
+	                                    tmp,
+	                                    update_unsaved
+	                                      ? NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED
+	                                      : NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+	                                    NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+	                                    NULL,
+	                                    NULL)) {
 		g_object_unref (object);
 		return NULL;
 	}
diff --git a/src/settings/plugins/ifnet/nms-ifnet-plugin.c b/src/settings/plugins/ifnet/nms-ifnet-plugin.c
index 998b04b4..38d23f30 100644
--- a/src/settings/plugins/ifnet/nms-ifnet-plugin.c
+++ b/src/settings/plugins/ifnet/nms-ifnet-plugin.c
@@ -269,11 +269,12 @@ reload_connections (NMSettingsPlugin *config)
 				}
 			} else {
 				/* Update existing connection with new settings */
-				if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (old),
-				                                              NM_CONNECTION (new),
-				                                              FALSE,  /* don't set Unsaved */
-				                                              "ifnet-update",
-				                                              &error)) {
+				if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (old),
+				                                    NM_CONNECTION (new),
+				                                    NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+				                                    NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+				                                    "ifnet-update",
+				                                    &error)) {
 					/* Shouldn't ever get here as 'new' was verified by the reader already
 					 * and the UUID did not change. */
 					g_assert_not_reached ();
@@ -519,5 +520,5 @@ settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface)
 G_MODULE_EXPORT GObject *
 nm_settings_plugin_factory (void)
 {
-	return g_object_ref (settings_plugin_ifnet_get ());
+	return G_OBJECT (g_object_ref (settings_plugin_ifnet_get ()));
 }
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
index 0928772a..bfad9d15 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
@@ -139,10 +139,12 @@ bind_device_to_connection (SettingsPluginIfupdown *self,
 		g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, address, NULL);
 	}
 
-	nm_settings_connection_commit_changes (NM_SETTINGS_CONNECTION (exported),
-	                                       NULL,
-	                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
-	                                       NULL);
+	nm_settings_connection_update (NM_SETTINGS_CONNECTION (exported),
+	                               NULL,
+	                               NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
+	                               NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+	                               "ifupdown-new",
+	                               NULL);
 }
 
 static void
@@ -539,6 +541,6 @@ settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface)
 G_MODULE_EXPORT GObject *
 nm_settings_plugin_factory (void)
 {
-	return g_object_ref (settings_plugin_ifupdown_get ());
+	return G_OBJECT (g_object_ref (settings_plugin_ifupdown_get ()));
 }
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c
index 300aa9f7..5a6d8a76 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-connection.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-connection.c
@@ -65,7 +65,7 @@ commit_changes (NMSettingsConnection *connection,
 	nm_assert (out_reread_connection && !*out_reread_connection);
 	nm_assert (!out_logmsg_change || !*out_logmsg_change);
 
-	if (!nms_keyfile_writer_connection (new_connection ?: NM_CONNECTION (connection),
+	if (!nms_keyfile_writer_connection (new_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),
@@ -159,11 +159,14 @@ nms_keyfile_connection_new (NMConnection *source,
 	                                   NULL);
 
 	/* Update our settings with what was read from the file */
-	if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
-	                                              tmp,
-	                                              update_unsaved,
-	                                              NULL,
-	                                              error)) {
+	if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object),
+	                                    tmp,
+	                                    update_unsaved
+	                                      ? NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED
+	                                      : NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+	                                    NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+	                                    NULL,
+	                                    error)) {
 		g_object_unref (object);
 		object = NULL;
 	}
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index ee4db320..bc64b3ca 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -258,11 +258,12 @@ update_connection (NMSKeyfilePlugin *self,
 			else
 				_LOGI ("update and persist "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
 
-			if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (connection_by_uuid),
-			                                              NM_CONNECTION (connection_new),
-			                                              FALSE,  /* don't set Unsaved */
-			                                              "keyfile-update",
-			                                              &local)) {
+			if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid),
+			                                    NM_CONNECTION (connection_new),
+			                                    NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP,
+			                                    NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
+			                                    "keyfile-update",
+			                                    &local)) {
 				/* Shouldn't ever get here as 'connection_new' was verified by the reader already
 				 * and the UUID did not change. */
 				g_assert_not_reached ();
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_TC_Config b/src/settings/plugins/keyfile/tests/keyfiles/Test_TC_Config
new file mode 100644
index 00000000..14280eef
--- /dev/null
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_TC_Config
@@ -0,0 +1,11 @@
+
+[connection]
+id=Test TC Config
+uuid=32a51666-95ea-1337-b371-dcf5e54b1001
+type=802-3-ethernet
+
+[tc]
+qdisc.root=handle 1234 fq_codel
+qdisc.ffff:fff1=ingress
+tfilter.1234:=matchall action drop
+tfilter.ffff:=matchall action simple sdata Hello
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index 2584a722..f27efddd 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.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) 2008 - 2014 Red Hat, Inc.
+ * Copyright (C) 2008 - 2017 Red Hat, Inc.
  */
 
 #include "nm-default.h"
@@ -27,6 +27,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <sys/socket.h>
+#include <linux/pkt_sched.h>
 
 #include "nm-core-internal.h"
 
@@ -2624,6 +2625,125 @@ test_write_flags_property (void)
 /*****************************************************************************/
 
 static void
+test_read_tc_config (void)
+{
+	gs_unref_object NMConnection *connection = NULL;
+	NMSettingTCConfig *s_tc;
+	NMTCQdisc *qdisc1, *qdisc2;
+	NMTCAction *action1, *action2;
+	NMTCTfilter *tfilter1, *tfilter2;
+	gs_free_error GError *error = NULL;
+	gboolean success;
+
+	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_TC_Config", NULL);
+	g_assert (connection);
+	success = nm_connection_verify (connection, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	s_tc = nm_connection_get_setting_tc_config (connection);
+	g_assert (s_tc);
+
+	g_assert (nm_setting_tc_config_get_num_qdiscs (s_tc) == 2);
+
+	qdisc1 = nm_setting_tc_config_get_qdisc (s_tc, 0);
+	g_assert (qdisc1);
+	g_assert (g_strcmp0 (nm_tc_qdisc_get_kind (qdisc1), "fq_codel") == 0);
+	g_assert (nm_tc_qdisc_get_handle (qdisc1) == TC_H_MAKE (0x1234 << 16, 0x0000));
+	g_assert (nm_tc_qdisc_get_parent (qdisc1) == TC_H_ROOT);
+
+	qdisc2 = nm_setting_tc_config_get_qdisc (s_tc, 1);
+	g_assert (qdisc2);
+	g_assert (g_strcmp0 (nm_tc_qdisc_get_kind (qdisc2), "ingress") == 0);
+	g_assert (nm_tc_qdisc_get_handle (qdisc2) == TC_H_MAKE (TC_H_INGRESS, 0));
+	g_assert (nm_tc_qdisc_get_parent (qdisc2) == TC_H_INGRESS);
+
+	g_assert (nm_setting_tc_config_get_num_tfilters (s_tc) == 2);
+
+	tfilter1 = nm_setting_tc_config_get_tfilter (s_tc, 0);
+	g_assert (tfilter1);
+	g_assert (g_strcmp0 (nm_tc_tfilter_get_kind (tfilter1), "matchall") == 0);
+	g_assert (nm_tc_tfilter_get_handle (tfilter1) == TC_H_UNSPEC);
+	g_assert (nm_tc_tfilter_get_parent (tfilter1) == TC_H_MAKE (0x1234 << 16, 0x0000));
+
+	action1 = nm_tc_tfilter_get_action (tfilter1);
+	g_assert (action1);
+	g_assert (g_strcmp0 (nm_tc_action_get_kind (action1), "drop") == 0);
+
+	tfilter2 = nm_setting_tc_config_get_tfilter (s_tc, 1);
+	g_assert (tfilter2);
+	g_assert (g_strcmp0 (nm_tc_tfilter_get_kind (tfilter2), "matchall") == 0);
+	g_assert (nm_tc_tfilter_get_handle (tfilter2) == TC_H_UNSPEC);
+	g_assert (nm_tc_tfilter_get_parent (tfilter2) == TC_H_MAKE (TC_H_INGRESS, 0));
+
+	action2 = nm_tc_tfilter_get_action (tfilter2);
+	g_assert (action2);
+	g_assert (g_strcmp0 (nm_tc_action_get_kind (action2), "simple") == 0);
+	g_assert (g_strcmp0 (g_variant_get_bytestring (nm_tc_action_get_attribute (action2, "sdata")),
+	                     "Hello") == 0);
+}
+
+static void
+test_write_tc_config (void)
+{
+	gs_unref_object NMConnection *connection = NULL;
+	NMSetting *s_tc;
+	NMTCQdisc *qdisc1, *qdisc2;
+	NMTCTfilter *tfilter1, *tfilter2;
+	NMTCAction *action;
+	GError *error = NULL;
+
+	connection = nmtst_create_minimal_connection ("Test TC",
+	                                               NULL,
+	                                               NM_SETTING_WIRED_SETTING_NAME,
+	                                               NULL);
+	s_tc = nm_setting_tc_config_new ();
+
+	qdisc1 = nm_tc_qdisc_new ("fq_codel", TC_H_ROOT, &error);
+	nmtst_assert_success (qdisc1, error);
+	nm_tc_qdisc_set_handle (qdisc1, TC_H_MAKE (0x1234 << 16, 0x0000));
+	nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (s_tc), qdisc1);
+
+	qdisc2 = nm_tc_qdisc_new ("ingress", TC_H_INGRESS, &error);
+	nmtst_assert_success (qdisc2, error);
+	nm_tc_qdisc_set_handle (qdisc2, TC_H_MAKE (TC_H_INGRESS, 0));
+	nm_setting_tc_config_add_qdisc (NM_SETTING_TC_CONFIG (s_tc), qdisc2);
+
+	tfilter1 = nm_tc_tfilter_new ("matchall",
+	                              TC_H_MAKE (0x1234 << 16, 0x0000),
+	                              &error);
+	nmtst_assert_success (tfilter1, error);
+	action = nm_tc_action_new ("drop", &error);
+	nmtst_assert_success (action, error);
+	nm_tc_tfilter_set_action (tfilter1, action);
+	nm_tc_action_unref (action);
+	nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (s_tc), tfilter1);
+	nm_tc_tfilter_unref (tfilter1);
+
+	tfilter2 = nm_tc_tfilter_new ("matchall",
+	                              TC_H_MAKE (TC_H_INGRESS, 0),
+	                              &error);
+	nmtst_assert_success (tfilter2, error);
+	action = nm_tc_action_new ("simple", &error);
+	nmtst_assert_success (action, error);
+	nm_tc_action_set_attribute (action, "sdata", g_variant_new_bytestring ("Hello"));
+	nm_tc_tfilter_set_action (tfilter2, action);
+	nm_tc_action_unref (action);
+	nm_setting_tc_config_add_tfilter (NM_SETTING_TC_CONFIG (s_tc), tfilter2);
+	nm_tc_tfilter_unref (tfilter2);
+
+	nm_connection_add_setting (connection, s_tc);
+
+	nmtst_connection_normalize (connection);
+	write_test_connection_and_reread (connection, FALSE);
+
+	nm_tc_qdisc_unref (qdisc1);
+	nm_tc_qdisc_unref (qdisc2);
+}
+
+/*****************************************************************************/
+
+static void
 _escape_filename (const char *filename, gboolean would_be_ignored)
 {
 	gs_free char *esc = NULL;
@@ -2748,6 +2868,9 @@ int main (int argc, char **argv)
 	g_test_add_func ("/keyfile/test_read_flags_property", test_read_flags_property);
 	g_test_add_func ("/keyfile/test_write_flags_property", test_write_flags_property);
 
+	g_test_add_func ("/keyfile/test_read_tc_config", test_read_tc_config);
+	g_test_add_func ("/keyfile/test_write_tc_config", test_write_tc_config);
+
 	g_test_add_func ("/keyfile/test_nm_keyfile_plugin_utils_escape_filename", test_nm_keyfile_plugin_utils_escape_filename);
 
 	return g_test_run ();
diff --git a/src/tests/config/test-config.c b/src/tests/config/test-config.c
index 80e17d01..89b22a9f 100644
--- a/src/tests/config/test-config.c
+++ b/src/tests/config/test-config.c
@@ -318,6 +318,7 @@ test_config_global_dns (void)
 	g_object_unref (config);
 }
 
+#if WITH_CONCHECK
 static void
 test_config_connectivity_check (void)
 {
@@ -351,6 +352,7 @@ test_config_connectivity_check (void)
 
 	g_assert (remove (CONFIG_INTERN) == 0);
 }
+#endif
 
 static void
 test_config_no_auto_default (void)
@@ -368,7 +370,7 @@ test_config_no_auto_default (void)
 	g_assert_cmpint (nwrote, ==, 18);
 	nwrote = write (fd, "44:44:44:44:44:44\n", 18);
 	g_assert_cmpint (nwrote, ==, 18);
-	close (fd);
+	nm_close (fd);
 
 	config = setup_config (NULL, SRCDIR "/NetworkManager.conf", "", NULL, "/no/such/dir", "",
 	                       "--no-auto-default", state_file,
@@ -1053,7 +1055,9 @@ main (int argc, char **argv)
 
 	g_test_add_func ("/config/set-values", test_config_set_values);
 	g_test_add_func ("/config/global-dns", test_config_global_dns);
+#if WITH_CONCHECK
 	g_test_add_func ("/config/connectivity-check", test_config_connectivity_check);
+#endif
 
 	g_test_add_func ("/config/signal", test_config_signal);
 
diff --git a/src/tests/test-general-with-expect.c b/src/tests/test-general-with-expect.c
index c5d26163..2911d84d 100644
--- a/src/tests/test-general-with-expect.c
+++ b/src/tests/test-general-with-expect.c
@@ -184,7 +184,7 @@ test_nm_utils_kill_child_create_and_join_pgroup (void)
 
 	if (pgid == 0) {
 		/* child process... */
-		close (pipefd[0]);
+		nm_close (pipefd[0]);
 
 		err = setpgid (0, 0);
 		g_assert (err == 0);
@@ -192,16 +192,16 @@ test_nm_utils_kill_child_create_and_join_pgroup (void)
 		err = write (pipefd[1], &tmp, sizeof (tmp));
 		g_assert (err == sizeof (tmp));
 
-		close (pipefd[1]);
+		nm_close (pipefd[1]);
 		exit (0);
 	}
 
-	close (pipefd[1]);
+	nm_close (pipefd[1]);
 
 	err = read (pipefd[0], &tmp, sizeof (tmp));
 	g_assert (err == sizeof (tmp));
 
-	close (pipefd[0]);
+	nm_close (pipefd[0]);
 
 	err = setpgid (0, pgid);
 	g_assert (err == 0);
diff --git a/src/tests/test-general.c b/src/tests/test-general.c
index d36a26ef..bfa2ea73 100644
--- a/src/tests/test-general.c
+++ b/src/tests/test-general.c
@@ -359,7 +359,7 @@ _match_connection (GSList *connections,
 	}
 	list[i] = NULL;
 
-	return nm_utils_match_connection (list, original, device_has_carrier, default_v4_metric, default_v6_metric, NULL, NULL);
+	return nm_utils_match_connection (list, original, FALSE, device_has_carrier, default_v4_metric, default_v6_metric, NULL, NULL);
 }
 
 static void
diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c
index 649b443f..4c3c344e 100644
--- a/src/tests/test-ip4-config.c
+++ b/src/tests/test-ip4-config.c
@@ -38,6 +38,8 @@ build_test_config (void)
 	/* Build up the config to subtract */
 	config = nmtst_ip4_config_new (1);
 
+	nm_assert (NM_IP_CONFIG_CAST (config));
+
 	addr = *nmtst_platform_ip4_address ("192.168.1.10", "1.2.3.4", 24);
 	nm_ip4_config_add_address (config, &addr);
 
diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c
index 2436ea33..6c600c46 100644
--- a/src/vpn/nm-vpn-connection.c
+++ b/src/vpn/nm-vpn-connection.c
@@ -103,7 +103,7 @@ typedef struct {
 	gboolean service_can_persist;
 	gboolean connection_can_persist;
 
-	NMSettingsConnectionCallId secrets_id;
+	NMSettingsConnectionCallId *secrets_id;
 	SecretsReq secrets_idx;
 	char *username;
 
@@ -185,13 +185,13 @@ static NMSettingsConnection *_get_settings_connection (NMVpnConnection *self,
 
 static void get_secrets (NMVpnConnection *self,
                          SecretsReq secrets_idx,
-                         const char **hints);
+                         const char *const*hints);
 
 static guint32 get_route_table (NMVpnConnection *self, int addr_family, gboolean fallback_main);
 
 static void plugin_interactive_secrets_required (NMVpnConnection *self,
                                                  const char *message,
-                                                 const char **secrets);
+                                                 const char *const*secrets);
 
 static void _set_vpn_state (NMVpnConnection *self,
                             VpnState vpn_state,
@@ -1007,8 +1007,8 @@ print_vpn_config (NMVpnConnection *self)
 
 		if (priv->ip4_internal_gw)
 			_LOGI ("Data:   Internal Gateway: %s", nm_utils_inet4_ntop (priv->ip4_internal_gw, NULL));
-		_LOGI ("Data:   Internal Address: %s", nm_utils_inet4_ntop (address4->address, NULL));
-		_LOGI ("Data:   Internal Prefix: %d", address4->plen);
+		_LOGI ("Data:   Internal Address: %s", address4 ? nm_utils_inet4_ntop (address4->address, NULL) : "??");
+		_LOGI ("Data:   Internal Prefix: %d", address4 ? (int) address4->plen : -1);
 		_LOGI ("Data:   Internal Point-to-Point Address: %s", nm_utils_inet4_ntop (address4->peer_address, NULL));
 
 		nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, priv->ip4_config, &route) {
@@ -1563,9 +1563,12 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 
 	route_table = get_route_table (self, AF_INET, TRUE);
 	route_metric = nm_vpn_connection_get_ip4_route_metric (self);
+	s_ip = nm_connection_get_setting_ip4_config (_get_applied_connection (self));
 
-	if (   g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES, "b", &b)
-	    && b) {
+	if (nm_setting_ip_config_get_ignore_auto_routes (s_ip)) {
+		/* ignore VPN routes */
+	} else if (   g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES, "b", &b)
+	           && b) {
 		if (priv->ip4_config) {
 			NMDedupMultiIter ipconf_iter;
 			const NMPlatformIP4Route *route;
@@ -1616,7 +1619,6 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict)
 		never_default = b;
 
 	/* Merge in user overrides from the NMConnection's IPv4 setting */
-	s_ip = nm_connection_get_setting_ip4_config (_get_applied_connection (self));
 	nm_ip4_config_merge_setting (config,
 	                             s_ip,
 	                             route_table,
@@ -1756,9 +1758,12 @@ nm_vpn_connection_ip6_config_get (NMVpnConnection *self, GVariant *dict)
 
 	route_table = get_route_table (self, AF_INET6, TRUE);
 	route_metric = nm_vpn_connection_get_ip6_route_metric (self);
+	s_ip = nm_connection_get_setting_ip6_config (_get_applied_connection (self));
 
-	if (   g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES, "b", &b)
-	    && b) {
+	if (nm_setting_ip_config_get_ignore_auto_routes (s_ip)) {
+		/* Ignore VPN routes */
+	} else if (   g_variant_lookup (dict, NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES, "b", &b)
+	           && b) {
 		if (priv->ip6_config) {
 			NMDedupMultiIter ipconf_iter;
 			const NMPlatformIP6Route *route;
@@ -1806,7 +1811,6 @@ next:
 		never_default = b;
 
 	/* Merge in user overrides from the NMConnection's IPv6 setting */
-	s_ip = nm_connection_get_setting_ip6_config (_get_applied_connection (self));
 	nm_ip6_config_merge_setting (config,
 	                             s_ip,
 	                             route_table,
@@ -2043,7 +2047,7 @@ state_changed_cb (GDBusProxy *proxy,
 static void
 secrets_required_cb (GDBusProxy  *proxy,
                      const char  *message,
-                     const char **secrets,
+                     const char *const*secrets,
                      gpointer     user_data)
 {
 	NMVpnConnection *self = NM_VPN_CONNECTION (user_data);
@@ -2567,7 +2571,7 @@ plugin_new_secrets_cb (GDBusProxy *proxy, GAsyncResult *result, gpointer user_da
 
 static void
 get_secrets_cb (NMSettingsConnection *connection,
-                NMSettingsConnectionCallId call_id,
+                NMSettingsConnectionCallId *call_id,
                 const char *agent_username,
                 const char *setting_name,
                 GError *error,
@@ -2634,7 +2638,7 @@ get_secrets_cb (NMSettingsConnection *connection,
 static void
 get_secrets (NMVpnConnection *self,
              SecretsReq secrets_idx,
-             const char **hints)
+             const char *const*hints)
 {
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
 	NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE;
@@ -2679,12 +2683,13 @@ get_secrets (NMVpnConnection *self,
 static void
 plugin_interactive_secrets_required (NMVpnConnection *self,
                                      const char *message,
-                                     const char **secrets)
+                                     const char *const*secrets)
 {
 	NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self);
-	guint32 secrets_len = secrets ? g_strv_length ((char **) secrets) : 0;
-	char **hints;
-	guint32 i;
+	const gsize secrets_len = NM_PTRARRAY_LEN (secrets);
+	gsize i;
+	gs_free const char **hints = NULL;
+	gs_free char *message_hint = NULL;
 
 	_LOGI ("VPN plugin: requested secrets; state %s (%d)",
 	       vpn_state_to_string (priv->vpn_state), priv->vpn_state);
@@ -2696,14 +2701,17 @@ plugin_interactive_secrets_required (NMVpnConnection *self,
 	_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));
+	hints = g_new (const char *, secrets_len + 2);
 	for (i = 0; i < secrets_len; i++)
-		hints[i] = g_strdup (secrets[i]);
-	if (message)
-		hints[i] = g_strdup_printf ("x-vpn-message:%s", message);
+		hints[i] = secrets[i];
+	if (message) {
+		message_hint = g_strdup_printf ("x-vpn-message:%s", message);
+		hints[i++] = message_hint;
+	}
+	hints[i] = NULL;
+	nm_assert (i < secrets_len + 2);
 
-	get_secrets (self, SECRETS_REQ_INTERACTIVE, (const char **) hints);
-	g_strfreev (hints);
+	get_secrets (self, SECRETS_REQ_INTERACTIVE, hints);
 }
 
 /*****************************************************************************/