about summary refs log tree commit diff
path: root/src/settings
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2017-12-12 15:53:07 +0100
committerMichael Biebl <biebl@debian.org>2017-12-12 15:53:07 +0100
commitafcd268ea7b1149fbfb66bce4eca659b675da0a2 (patch)
treec3fca2203ad17434daf3ccf576582bd66aa41ab2 /src/settings
parent417f6015c3dc8c47cf27daa59f64e0e36c521b9c (diff)
New upstream version 1.10.2 upstream/1.10.2
Diffstat (limited to 'src/settings')
-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
40 files changed, 1643 insertions, 1207 deletions
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 ();