about summary refs log tree commit diff
path: root/src/settings
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2017-05-11 14:55:55 +0200
committerMichael Biebl <biebl@debian.org>2017-05-11 14:55:55 +0200
commitc333f062ddcba9b35330647bf6cbd0a07f2d786e (patch)
tree257c3a0c74c09f4ad2328eab5b932806405f0c1c /src/settings
parenta222e56e103f949b148a6942e385ccca2c26d9f3 (diff)
New upstream version 1.8.0 upstream/1.8.0
Diffstat (limited to 'src/settings')
-rw-r--r--src/settings/nm-agent-manager.c4
-rw-r--r--src/settings/nm-secret-agent.c2
-rw-r--r--src/settings/nm-secret-agent.h2
-rw-r--r--src/settings/nm-settings-connection.c229
-rw-r--r--src/settings/nm-settings-connection.h29
-rw-r--r--src/settings/nm-settings-plugin.h2
-rw-r--r--src/settings/nm-settings.c278
-rw-r--r--src/settings/nm-settings.h26
-rw-r--r--src/settings/plugins/ibft/nms-ibft-reader.h2
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c37
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c4
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c1429
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c134
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h6
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c1125
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h4
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c158
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h27
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected8
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected17
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected22
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected34
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected16
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected8
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected11
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected6
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected20
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected17
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected16
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected22
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem311
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3:14
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected41
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected13
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected12
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-eth-type7
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected12
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c572
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-connection-parser.c157
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-connection-parser.h2
-rw-r--r--src/settings/plugins/ifnet/nms-ifnet-net-utils.h4
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.h2
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.c90
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-connection.c16
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c13
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c28
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.h7
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.c94
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.h6
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection2
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c101
64 files changed, 3328 insertions, 1731 deletions
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c
index 0db20979..3d6b1cfb 100644
--- a/src/settings/nm-agent-manager.c
+++ b/src/settings/nm-agent-manager.c
@@ -103,7 +103,7 @@ NM_DEFINE_SINGLETON_GETTER (NMAgentManager, nm_agent_manager_get, NM_TYPE_AGENT_
                             nm_secret_agent_get_description (__agent)); \
             } else \
                 __prefix2[0] = '\0'; \
-            _nm_log ((level), (_NMLOG_DOMAIN), 0, \
+            _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
                      "%s%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      __prefix1, __prefix2 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
@@ -178,7 +178,7 @@ remove_agent (NMAgentManager *self, const char *owner)
 	while (g_hash_table_iter_next (&iter, &data, NULL))
 		request_remove_agent ((Request *) data, agent, &pending_reqs);
 
-	/* We cannot call request_next_agent() from from within hash iterating loop,
+	/* We cannot call request_next_agent() from within hash iterating loop,
 	 * because it may remove the request from the hash table, which invalidates
 	 * the iterator. So, only remove the agent from requests. And store the requests
 	 * that should be sent to other agent to a temporary list to proceed afterwards.
diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c
index 6ec96a32..b9ca34d0 100644
--- a/src/settings/nm-secret-agent.c
+++ b/src/settings/nm-secret-agent.c
@@ -87,7 +87,7 @@ G_DEFINE_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT)
                 g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", ""_NMLOG_PREFIX_NAME"", (self)); \
             else \
                 g_strlcpy (__prefix, _NMLOG_PREFIX_NAME, sizeof (__prefix)); \
-            _nm_log ((level), (_NMLOG_DOMAIN), 0, \
+            _nm_log ((level), (_NMLOG_DOMAIN), 0, NULL, NULL, \
                      "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                      __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
         } \
diff --git a/src/settings/nm-secret-agent.h b/src/settings/nm-secret-agent.h
index 0b6da136..54c5b398 100644
--- a/src/settings/nm-secret-agent.h
+++ b/src/settings/nm-secret-agent.h
@@ -21,7 +21,7 @@
 #ifndef __NETWORKMANAGER_SECRET_AGENT_H__
 #define __NETWORKMANAGER_SECRET_AGENT_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 #define NM_TYPE_SECRET_AGENT            (nm_secret_agent_get_type ())
 #define NM_SECRET_AGENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRET_AGENT, NMSecretAgent))
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index 81dcb698..0cb2920c 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -69,17 +69,24 @@ enum {
 static guint signals[LAST_SIGNAL] = { 0 };
 
 typedef struct _NMSettingsConnectionPrivate {
-	gboolean removed;
 
 	NMAgentManager *agent_mgr;
 	NMSessionMonitor *session_monitor;
 	gulong session_changed_id;
 
 	NMSettingsConnectionFlags flags;
-	gboolean ready;
+
+	bool removed:1;
+	bool ready:1;
+
+	/* Is this connection visible by some session? */
+	bool visible:1;
+
+	bool timestamp_set:1;
+
+	NMSettingsAutoconnectBlockedReason autoconnect_blocked_reason:3;
 
 	GSList *pending_auths; /* List of pending authentication requests */
-	gboolean visible; /* Is this connection is visible by some session? */
 
 	GSList *get_secret_requests;  /* in-progress secrets requests */
 
@@ -99,12 +106,10 @@ typedef struct _NMSettingsConnectionPrivate {
 	NMConnection *agent_secrets;
 
 	guint64 timestamp;   /* Up-to-date timestamp of connection use */
-	gboolean timestamp_set;
 	GHashTable *seen_bssids; /* Up-to-date BSSIDs that's been seen for the connection */
 
 	int autoconnect_retries;
 	gint32 autoconnect_retry_time;
-	NMDeviceStateReason autoconnect_blocked_reason;
 
 	char *filename;
 } NMSettingsConnectionPrivate;
@@ -126,14 +131,13 @@ G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_E
         if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
             char __prefix[128]; \
             const char *__p_prefix = _NMLOG_PREFIX_NAME; \
+            const char *__uuid = (self) ? nm_settings_connection_get_uuid (self) : NULL; \
             \
             if (self) { \
-                const char *__uuid = nm_settings_connection_get_uuid (self); \
-                \
                 g_snprintf (__prefix, sizeof (__prefix), "%s[%p%s%s]", _NMLOG_PREFIX_NAME, self, __uuid ? "," : "", __uuid ? __uuid : ""); \
                 __p_prefix = __prefix; \
             } \
-            _nm_log (__level, _NMLOG_DOMAIN, 0, \
+            _nm_log (__level, _NMLOG_DOMAIN, 0, NULL, __uuid, \
                      "%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
                      __p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
         } \
@@ -493,7 +497,7 @@ set_unsaved (NMSettingsConnection *self, gboolean now_unsaved)
 		else {
 			flags &= ~(NM_SETTINGS_CONNECTION_FLAGS_UNSAVED |
 			           NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED |
-			           NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED);
+			           NM_SETTINGS_CONNECTION_FLAGS_VOLATILE);
 		}
 		nm_settings_connection_set_flags_all (self, flags);
 	}
@@ -556,7 +560,7 @@ nm_settings_connection_replace_settings (NMSettingsConnection *self,
 	_LOGD ("replace settings from connection %p (%s)", new_connection, nm_connection_get_id (NM_CONNECTION (self)));
 
 	nm_settings_connection_set_flags (self,
-	                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED,
+	                                  NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED | NM_SETTINGS_CONNECTION_FLAGS_VOLATILE,
 	                                  FALSE);
 
 	/* Cache the just-updated system secrets in case something calls
@@ -1648,49 +1652,6 @@ con_update_cb (NMSettingsConnection *self,
 	update_complete (self, info, error);
 }
 
-static char *
-con_list_changed_props (NMConnection *old, NMConnection *new)
-{
-	gs_unref_hashtable GHashTable *diff = NULL;
-	GHashTable *setting_diff;
-	char *setting_name, *prop_name;
-	GHashTableIter iter, iter2;
-	gboolean same;
-	GString *str;
-
-	same = nm_connection_diff (old, new,
-	                           NM_SETTING_COMPARE_FLAG_EXACT |
-	                           NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT,
-	                           &diff);
-
-	if (same || !diff)
-		return NULL;
-
-	str = g_string_sized_new (32);
-	g_hash_table_iter_init (&iter, diff);
-
-	while (g_hash_table_iter_next (&iter,
-	                               (gpointer *) &setting_name,
-	                               (gpointer *) &setting_diff)) {
-		if (!setting_diff)
-			continue;
-
-		g_hash_table_iter_init (&iter2, setting_diff);
-
-		while (g_hash_table_iter_next (&iter2, (gpointer *) &prop_name, NULL)) {
-			g_string_append (str, setting_name);
-			g_string_append_c (str, '.');
-			g_string_append (str, prop_name);
-			g_string_append_c (str, ',');
-		}
-	}
-
-	if (str->len)
-		str->str[str->len - 1] = '\0';
-
-	return g_string_free (str, FALSE);
-}
-
 static void
 update_auth_cb (NMSettingsConnection *self,
                 GDBusMethodInvocation *context,
@@ -1706,6 +1667,17 @@ update_auth_cb (NMSettingsConnection *self,
 		return;
 	}
 
+	if (!info->new_settings) {
+		/* We're just calling Save(). Just commit the existing connection. */
+		if (info->save_to_disk) {
+			nm_settings_connection_commit_changes (self,
+			                                       NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION,
+			                                       con_update_cb,
+			                                       info);
+		}
+		return;
+	}
+
 	if (!any_secrets_present (info->new_settings)) {
 		/* If the new connection has no secrets, we do not want to remove all
 		 * secrets, rather we keep all the existing ones. Do that by merging
@@ -1720,8 +1692,17 @@ update_auth_cb (NMSettingsConnection *self,
 		update_agent_secrets_cache (self, info->new_settings);
 	}
 
-	if (nm_audit_manager_audit_enabled (nm_audit_manager_get ()))
-		info->audit_args = con_list_changed_props (NM_CONNECTION (self), info->new_settings);
+	if (nm_audit_manager_audit_enabled (nm_audit_manager_get ())) {
+		gs_unref_hashtable GHashTable *diff = NULL;
+		gboolean same;
+
+		same = nm_connection_diff (NM_CONNECTION (self), info->new_settings,
+		                           NM_SETTING_COMPARE_FLAG_EXACT |
+		                           NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT,
+		                           &diff);
+		if (!same && diff)
+			info->audit_args = nm_utils_format_con_diff_for_audit (diff);
+	}
 
 	if (info->save_to_disk) {
 		nm_settings_connection_replace_and_commit (self,
@@ -1855,11 +1836,7 @@ static void
 impl_settings_connection_save (NMSettingsConnection *self,
                                GDBusMethodInvocation *context)
 {
-	/* Do nothing if the connection is already synced with disk */
-	if (nm_settings_connection_get_unsaved (self))
-		settings_connection_update_helper (self, context, NULL, TRUE);
-	else
-		g_dbus_method_invocation_return_value (context, NULL);
+	settings_connection_update_helper (self, context, NULL, TRUE);
 }
 
 static void
@@ -2162,6 +2139,96 @@ nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConn
 
 /*****************************************************************************/
 
+static int
+_cmp_timestamp (NMSettingsConnection *a, NMSettingsConnection *b)
+{
+	gboolean a_has_ts, b_has_ts;
+	guint64 ats = 0, bts = 0;
+
+	nm_assert (NM_IS_SETTINGS_CONNECTION (a));
+	nm_assert (NM_IS_SETTINGS_CONNECTION (b));
+
+	a_has_ts = !!nm_settings_connection_get_timestamp (a, &ats);
+	b_has_ts = !!nm_settings_connection_get_timestamp (b, &bts);
+	if (a_has_ts != b_has_ts)
+		return a_has_ts ? -1 : 1;
+	if (a_has_ts && ats != bts)
+		return (ats > bts) ? -1 : 1;
+	return 0;
+}
+
+static int
+_cmp_last_resort (NMSettingsConnection *a, NMSettingsConnection *b)
+{
+	int c;
+
+	nm_assert (NM_IS_SETTINGS_CONNECTION (a));
+	nm_assert (NM_IS_SETTINGS_CONNECTION (b));
+
+	c = g_strcmp0 (nm_connection_get_uuid (NM_CONNECTION (a)),
+	               nm_connection_get_uuid (NM_CONNECTION (b)));
+	if (c)
+		return c;
+
+	/* hm, same UUID. Use their pointer value to give them a stable
+	 * order. */
+	return (a > b) ? -1 : 1;
+}
+
+/* sorting for "best" connections.
+ * The function sorts connections in descending timestamp order.
+ * That means an older connection (lower timestamp) goes after
+ * a newer one.
+ */
+int
+nm_settings_connection_cmp_timestamp (NMSettingsConnection *a, NMSettingsConnection *b)
+{
+	int c;
+
+	if (a == b)
+		return 0;
+	if (!a)
+		return 1;
+	if (!b)
+		return -1;
+
+	if ((c = _cmp_timestamp (a, b)))
+		return c;
+	if ((c = nm_utils_cmp_connection_by_autoconnect_priority (NM_CONNECTION (a), NM_CONNECTION (b))))
+		return c;
+	return _cmp_last_resort (a, b);
+}
+
+int
+nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data)
+{
+	return nm_settings_connection_cmp_timestamp (*((NMSettingsConnection **) pa),
+	                                             *((NMSettingsConnection **) pb));
+}
+
+int
+nm_settings_connection_cmp_autoconnect_priority (NMSettingsConnection *a, NMSettingsConnection *b)
+{
+	int c;
+
+	if (a == b)
+		return 0;
+	if ((c = nm_utils_cmp_connection_by_autoconnect_priority (NM_CONNECTION (a), NM_CONNECTION (b))))
+		return c;
+	if ((c = _cmp_timestamp (a, b)))
+		return c;
+	return _cmp_last_resort (a, b);
+}
+
+int
+nm_settings_connection_cmp_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data)
+{
+	return nm_settings_connection_cmp_autoconnect_priority (*((NMSettingsConnection **) pa),
+	                                                        *((NMSettingsConnection **) pb));
+}
+
+/*****************************************************************************/
+
 /**
  * nm_settings_connection_get_timestamp:
  * @self: the #NMSettingsConnection
@@ -2483,7 +2550,7 @@ nm_settings_connection_get_autoconnect_retries (NMSettingsConnection *self)
 		priv->autoconnect_retries = retries;
 	}
 
-	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retries;
+	return priv->autoconnect_retries;
 }
 
 void
@@ -2492,7 +2559,10 @@ nm_settings_connection_set_autoconnect_retries (NMSettingsConnection *self,
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
-	priv->autoconnect_retries = retries;
+	if (priv->autoconnect_retries != retries) {
+		_LOGT ("autoconnect-retries: set %d", retries);
+		priv->autoconnect_retries = retries;
+	}
 	if (retries)
 		priv->autoconnect_retry_time = 0;
 	else
@@ -2511,7 +2581,7 @@ nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self)
 	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_retry_time;
 }
 
-NMDeviceStateReason
+NMSettingsAutoconnectBlockedReason
 nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self)
 {
 	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason;
@@ -2519,8 +2589,12 @@ nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *sel
 
 void
 nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self,
-                                                       NMDeviceStateReason reason)
+                                                       NMSettingsAutoconnectBlockedReason reason)
 {
+	g_return_if_fail (NM_IN_SET (reason,
+	                             NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED,
+	                             NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED,
+	                             NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS));
 	NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->autoconnect_blocked_reason = reason;
 }
 
@@ -2533,7 +2607,7 @@ nm_settings_connection_can_autoconnect (NMSettingsConnection *self)
 
 	if (   !priv->visible
 	    || priv->autoconnect_retries == 0
-	    || priv->autoconnect_blocked_reason != NM_DEVICE_STATE_REASON_NONE)
+	    || priv->autoconnect_blocked_reason != NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED)
 		return FALSE;
 
 	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
@@ -2566,18 +2640,18 @@ nm_settings_connection_get_nm_generated (NMSettingsConnection *self)
 }
 
 /**
- * nm_settings_connection_get_nm_generated_assumed:
+ * nm_settings_connection_get_volatile:
  * @self: an #NMSettingsConnection
  *
- * Gets the "nm-generated-assumed" flag on @self.
+ * Gets the "volatile" flag on @self.
  *
- * The connection is a generated connection especially
- * generated for connection assumption.
+ * The connection is marked as volatile and will be removed when
+ * it disconnects.
  */
 gboolean
-nm_settings_connection_get_nm_generated_assumed (NMSettingsConnection *self)
+nm_settings_connection_get_volatile (NMSettingsConnection *self)
 {
-	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED);
+	return NM_FLAGS_HAS (nm_settings_connection_get_flags (self), NM_SETTINGS_CONNECTION_FLAGS_VOLATILE);
 }
 
 gboolean
@@ -2673,7 +2747,6 @@ nm_settings_connection_init (NMSettingsConnection *self)
 	priv->seen_bssids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
 	priv->autoconnect_retries = AUTOCONNECT_RETRIES_UNSET;
-	priv->autoconnect_blocked_reason = NM_DEVICE_STATE_REASON_NONE;
 
 	g_signal_connect (self, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), NULL);
 	g_signal_connect (self, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed_cb), NULL);
@@ -2772,13 +2845,8 @@ set_property (GObject *object, guint prop_id,
 	NMSettingsConnection *self = NM_SETTINGS_CONNECTION (object);
 
 	switch (prop_id) {
-	case PROP_READY:
-		nm_settings_connection_set_ready (self, g_value_get_boolean (value));
-		break;
-	case PROP_FLAGS:
-		nm_settings_connection_set_flags_all (self, g_value_get_uint (value));
-		break;
 	case PROP_FILENAME:
+		/* construct-only */
 		nm_settings_connection_set_filename (self, g_value_get_string (value));
 		break;
 	default:
@@ -2822,7 +2890,7 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
 	obj_properties[PROP_READY] =
 	     g_param_spec_boolean (NM_SETTINGS_CONNECTION_READY, "", "",
 	                           TRUE,
-	                           G_PARAM_READWRITE |
+	                           G_PARAM_READABLE |
 	                           G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_FLAGS] =
@@ -2830,13 +2898,14 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *class)
 	                        NM_SETTINGS_CONNECTION_FLAGS_NONE,
 	                        NM_SETTINGS_CONNECTION_FLAGS_ALL,
 	                        NM_SETTINGS_CONNECTION_FLAGS_NONE,
-	                        G_PARAM_READWRITE |
+	                        G_PARAM_READABLE |
 	                        G_PARAM_STATIC_STRINGS);
 
 	obj_properties[PROP_FILENAME] =
 	     g_param_spec_string (NM_SETTINGS_CONNECTION_FILENAME, "", "",
 	                          NULL,
 	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT_ONLY |
 	                          G_PARAM_STATIC_STRINGS);
 
 	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index c5ddd817..b449e2bd 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -25,7 +25,7 @@
 #include <net/ethernet.h>
 
 #include "nm-exported-object.h"
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 #define NM_TYPE_SETTINGS_CONNECTION            (nm_settings_connection_get_type ())
 #define NM_SETTINGS_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_CONNECTION, NMSettingsConnection))
@@ -58,9 +58,9 @@
  * @NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED: A connection is "nm-generated" if
  *  it was generated by NetworkManger. If the connection gets modified or saved
  *  by the user, the flag gets cleared. A nm-generated is implicitly unsaved.
- * @NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED: A special kind of "nm-generated"
- *  connection that was specifically created for connection assumption. "nm-generated-assumed"
- *  implies "nm-generated".
+ * @NM_SETTINGS_CONNECTION_FLAGS_VOLATILE: The connection will be deleted
+ *  when it disconnects. That is for in-memory connections (unsaved), which are
+ *  currently active but cleanup on disconnect.
  * @NM_SETTINGS_CONNECTION_FLAGS_ALL: special mask, for all known flags
  *
  * #NMSettingsConnection flags.
@@ -70,7 +70,7 @@ typedef enum
 	NM_SETTINGS_CONNECTION_FLAGS_NONE                               = 0x00,
 	NM_SETTINGS_CONNECTION_FLAGS_UNSAVED                            = 0x01,
 	NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED                       = 0x02,
-	NM_SETTINGS_CONNECTION_FLAGS_NM_GENERATED_ASSUMED               = 0x04,
+	NM_SETTINGS_CONNECTION_FLAGS_VOLATILE                           = 0x04,
 
 	__NM_SETTINGS_CONNECTION_FLAGS_LAST,
 	NM_SETTINGS_CONNECTION_FLAGS_ALL = ((__NM_SETTINGS_CONNECTION_FLAGS_LAST - 1) << 1) - 1,
@@ -82,6 +82,12 @@ typedef enum { /*< skip >*/
 	NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED                 = (1LL << 1),
 } NMSettingsConnectionCommitReason;
 
+typedef enum {
+	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_UNBLOCKED               = 0,
+	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_BLOCKED                 = 1,
+	NM_SETTINGS_AUTO_CONNECT_BLOCKED_REASON_NO_SECRETS              = 2,
+} NMSettingsAutoconnectBlockedReason;
+
 struct _NMSettingsConnectionCallId;
 typedef struct _NMSettingsConnectionCallId *NMSettingsConnectionCallId;
 
@@ -184,6 +190,11 @@ NMSettingsConnectionFlags nm_settings_connection_get_flags (NMSettingsConnection
 NMSettingsConnectionFlags nm_settings_connection_set_flags (NMSettingsConnection *self, NMSettingsConnectionFlags flags, gboolean set);
 NMSettingsConnectionFlags nm_settings_connection_set_flags_all (NMSettingsConnection *self, NMSettingsConnectionFlags flags);
 
+int nm_settings_connection_cmp_timestamp (NMSettingsConnection *ac, NMSettingsConnection *ab);
+int nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data);
+int nm_settings_connection_cmp_autoconnect_priority (NMSettingsConnection *a, NMSettingsConnection *b);
+int nm_settings_connection_cmp_autoconnect_priority_p_with_data (gconstpointer pa, gconstpointer pb, gpointer user_data);
+
 gboolean nm_settings_connection_get_timestamp (NMSettingsConnection *self,
                                                guint64 *out_timestamp);
 
@@ -210,14 +221,14 @@ void nm_settings_connection_reset_autoconnect_retries (NMSettingsConnection *sel
 
 gint32 nm_settings_connection_get_autoconnect_retry_time (NMSettingsConnection *self);
 
-NMDeviceStateReason nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self);
-void nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self,
-                                                            NMDeviceStateReason reason);
+NMSettingsAutoconnectBlockedReason nm_settings_connection_get_autoconnect_blocked_reason (NMSettingsConnection *self);
+void                               nm_settings_connection_set_autoconnect_blocked_reason (NMSettingsConnection *self,
+                                                                                          NMSettingsAutoconnectBlockedReason reason);
 
 gboolean nm_settings_connection_can_autoconnect (NMSettingsConnection *self);
 
 gboolean nm_settings_connection_get_nm_generated (NMSettingsConnection *self);
-gboolean nm_settings_connection_get_nm_generated_assumed (NMSettingsConnection *self);
+gboolean nm_settings_connection_get_volatile (NMSettingsConnection *self);
 
 gboolean nm_settings_connection_get_ready (NMSettingsConnection *self);
 void     nm_settings_connection_set_ready (NMSettingsConnection *self,
diff --git a/src/settings/nm-settings-plugin.h b/src/settings/nm-settings-plugin.h
index 8abd72ea..97bb7b65 100644
--- a/src/settings/nm-settings-plugin.h
+++ b/src/settings/nm-settings-plugin.h
@@ -22,7 +22,7 @@
 #ifndef __NETWORKMANAGER_SETTINGS_PLUGIN_H__
 #define __NETWORKMANAGER_SETTINGS_PLUGIN_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 /* Plugin's factory function that returns a GObject that implements
  * NMSettingsPlugin.
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 35790a12..afd1b084 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -104,7 +104,7 @@ EXPORT(nm_settings_connection_replace_and_commit)
 #define IFCFG_DIR                    SYSCONFDIR "/sysconfig/network"
 #define CONF_DHCP                    IFCFG_DIR "/dhcp"
 
-#define PLUGIN_MODULE_PATH      "plugin-module-path"
+static NM_CACHED_QUARK_FCN ("plugin-module-path", plugin_module_path_quark)
 
 #if (defined(HOSTNAME_PERSIST_SUSE) + defined(HOSTNAME_PERSIST_SLACKWARE) + defined(HOSTNAME_PERSIST_GENTOO)) > 1
 #error "Can only define one of HOSTNAME_PERSIST_*"
@@ -120,6 +120,9 @@ EXPORT(nm_settings_connection_replace_and_commit)
 #define HOSTNAME_FILE           HOSTNAME_FILE_DEFAULT
 #endif
 
+static NM_CACHED_QUARK_FCN ("default-wired-connection", _default_wired_connection_quark)
+static NM_CACHED_QUARK_FCN ("default-wired-device", _default_wired_device_quark)
+
 /*****************************************************************************/
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMSettings,
@@ -390,35 +393,6 @@ error:
 	g_clear_object (&subject);
 }
 
-static int
-connection_sort (gconstpointer pa, gconstpointer pb)
-{
-	NMConnection *a = NM_CONNECTION (pa);
-	NMSettingConnection *con_a;
-	NMConnection *b = NM_CONNECTION (pb);
-	NMSettingConnection *con_b;
-	guint64 ts_a = 0, ts_b = 0;
-	gboolean can_ac_a, can_ac_b;
-
-	con_a = nm_connection_get_setting_connection (a);
-	g_assert (con_a);
-	con_b = nm_connection_get_setting_connection (b);
-	g_assert (con_b);
-
-	can_ac_a = !!nm_setting_connection_get_autoconnect (con_a);
-	can_ac_b = !!nm_setting_connection_get_autoconnect (con_b);
-	if (can_ac_a != can_ac_b)
-		return can_ac_a ? -1 : 1;
-
-	nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pa), &ts_a);
-	nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (pb), &ts_b);
-	if (ts_a > ts_b)
-		return -1;
-	else if (ts_a == ts_b)
-		return 0;
-	return 1;
-}
-
 /**
  * nm_settings_get_connections:
  * @self: the #NMSettings
@@ -444,46 +418,98 @@ nm_settings_get_connections (NMSettings *self, guint *out_len)
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	if (priv->connections_cached_list) {
+	if (G_LIKELY (priv->connections_cached_list)) {
 		NM_SET_OUT (out_len, g_hash_table_size (priv->connections));
 		return priv->connections_cached_list;
 	}
 
 	l = g_hash_table_size (priv->connections);
 
-	v = g_new (NMSettingsConnection *, l + 1);
+	v = g_new (NMSettingsConnection *, (gsize) l + 1);
 
 	i = 0;
 	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &con))
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &con)) {
+		nm_assert (i < l);
 		v[i++] = con;
-	v[i] = NULL;
-
+	}
 	nm_assert (i == l);
+	v[i] = NULL;
 
 	NM_SET_OUT (out_len, l);
 	priv->connections_cached_list = v;
 	return v;
 }
 
+/**
+ * nm_settings_get_connections_clone:
+ * @self: the #NMSetting
+ * @out_len: (allow-none): optional output argument
+ * @func: caller-supplied function for filtering connections
+ * @func_data: caller-supplied data passed to @func
+ *
+ * Returns: (transfer container) (element-type NMSettingsConnection):
+ *   an NULL terminated array of #NMSettingsConnection objects that were
+ *   filtered by @func (or all connections if no filter was specified).
+ *   The order is arbitrary.
+ *   Caller is responsible for freeing the returned array with free(),
+ *   the contained values do not need to be unrefed.
+ */
+NMSettingsConnection **
+nm_settings_get_connections_clone (NMSettings *self,
+                                   guint *out_len,
+                                   NMSettingsConnectionFilterFunc func,
+                                   gpointer func_data)
+{
+	NMSettingsConnection *const*list_cached;
+	NMSettingsConnection **list;
+	guint len, i, j;
+
+	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
+
+	list_cached = nm_settings_get_connections (self, &len);
+
+#if NM_MORE_ASSERTS
+	nm_assert (list_cached);
+	for (i = 0; i < len; i++)
+		nm_assert (NM_IS_SETTINGS_CONNECTION (list_cached[i]));
+	nm_assert (!list_cached[i]);
+#endif
+
+	list = g_new (NMSettingsConnection *, ((gsize) len + 1));
+	if (func) {
+		for (i = 0, j = 0; i < len; i++) {
+			if (func (self, list_cached[i], func_data))
+				list[j++] = list_cached[i];
+		}
+		list[j] = NULL;
+		len = j;
+	} else
+		memcpy (list, list_cached, sizeof (list[0]) * ((gsize) len + 1));
+
+	NM_SET_OUT (out_len, len);
+	return list;
+}
+
 /* Returns a list of NMSettingsConnections.
  * The list is sorted in the order suitable for auto-connecting, i.e.
  * first go connections with autoconnect=yes and most recent timestamp.
- * Caller must free the list with g_slist_free().
+ * Caller must free the list with g_free(), but not the list items.
  */
-GSList *
-nm_settings_get_connections_sorted (NMSettings *self)
+NMSettingsConnection **
+nm_settings_get_connections_sorted (NMSettings *self, guint *out_len)
 {
-	GHashTableIter iter;
-	gpointer data = NULL;
-	GSList *list = NULL;
+	NMSettingsConnection **connections;
+	guint len;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
 
-	g_hash_table_iter_init (&iter, NM_SETTINGS_GET_PRIVATE (self)->connections);
-	while (g_hash_table_iter_next (&iter, NULL, &data))
-		list = g_slist_insert_sorted (list, data, connection_sort);
-	return list;
+	connections = nm_settings_get_connections_clone (self, &len, NULL, NULL);
+	if (len > 1)
+		g_qsort_with_data (connections, len, sizeof (NMSettingsConnection *), nm_settings_connection_cmp_autoconnect_priority_p_with_data, NULL);
+
+	NM_SET_OUT (out_len, len);
+	return connections;
 }
 
 NMSettingsConnection *
@@ -753,7 +779,7 @@ add_plugin (NMSettings *self, NMSettingsPlugin *plugin)
 	              NM_SETTINGS_PLUGIN_INFO, &pinfo,
 	              NULL);
 
-	path = g_object_get_data (G_OBJECT (plugin), PLUGIN_MODULE_PATH);
+	path = g_object_get_qdata (G_OBJECT (plugin), plugin_module_path_quark ());
 
 	_LOGI ("loaded plugin %s: %s%s%s%s", pname, pinfo,
 	       NM_PRINT_FMT_QUOTED (path, " (", path, ")", ""));
@@ -809,8 +835,8 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 	gboolean has_no_ibft;
 	gssize idx_no_ibft, idx_ibft;
 
-	idx_ibft    = _nm_utils_strv_find_first ((char **) plugins, -1, "ibft");
-	idx_no_ibft = _nm_utils_strv_find_first ((char **) plugins, -1, "no-ibft");
+	idx_ibft    = nm_utils_strv_find_first ((char **) plugins, -1, "ibft");
+	idx_no_ibft = nm_utils_strv_find_first ((char **) plugins, -1, "no-ibft");
 	has_no_ibft = idx_no_ibft >= 0 && idx_no_ibft > idx_ibft;
 #if WITH_SETTINGS_PLUGIN_IBFT
 	add_ibft = idx_no_ibft < 0 && idx_ibft < 0;
@@ -844,9 +870,9 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 			continue;
 		}
 
-		if (_nm_utils_strv_find_first ((char **) plugins,
-		                               iter - plugins,
-		                               pname) >= 0) {
+		if (nm_utils_strv_find_first ((char **) plugins,
+		                              iter - plugins,
+		                              pname) >= 0) {
 			/* the plugin is already mentioned in the list previously.
 			 * Don't load a duplicate. */
 			continue;
@@ -916,7 +942,7 @@ load_plugin:
 				break;
 			}
 
-			g_object_set_data_full (obj, PLUGIN_MODULE_PATH, path, g_free);
+			g_object_set_qdata_full (obj, plugin_module_path_quark (), path, g_free);
 			path = NULL;
 			if (add_plugin (self, NM_SETTINGS_PLUGIN (obj)))
 				list = g_slist_append (list, obj);
@@ -1659,6 +1685,28 @@ nm_settings_set_transient_hostname (NMSettings *self,
 	                   info);
 }
 
+gboolean
+nm_settings_get_transient_hostname (NMSettings *self, char **hostname)
+{
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	GVariant *v_hostname;
+
+	if (!priv->hostname.hostnamed_proxy)
+		return FALSE;
+
+	v_hostname = g_dbus_proxy_get_cached_property (priv->hostname.hostnamed_proxy,
+	                                               "Hostname");
+	if (!v_hostname) {
+		_LOGT ("transient hostname retrieval failed");
+		return FALSE;
+	}
+
+	*hostname = g_variant_dup_string (v_hostname, NULL);
+	g_variant_unref (v_hostname);
+
+	return TRUE;
+}
+
 static gboolean
 write_hostname (NMSettingsPrivate *priv, const char *hostname)
 {
@@ -1935,9 +1983,6 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 	return FALSE;
 }
 
-#define DEFAULT_WIRED_CONNECTION_TAG "default-wired-connection"
-#define DEFAULT_WIRED_DEVICE_TAG     "default-wired-device"
-
 static void default_wired_clear_tag (NMSettings *self,
                                      NMDevice *device,
                                      NMSettingsConnection *connection,
@@ -1953,7 +1998,7 @@ default_wired_connection_removed_cb (NMSettingsConnection *connection, NMSetting
 	 * wired device to the config file and don't create a new default wired
 	 * connection for that device again.
 	 */
-	device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG);
+	device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
 	if (device)
 		default_wired_clear_tag (self, device, connection, TRUE);
 }
@@ -1970,7 +2015,7 @@ default_wired_connection_updated_by_user_cb (NMSettingsConnection *connection, g
 	 * considered a default wired connection, and should no longer affect
 	 * the no-auto-default configuration option.
 	 */
-	device = g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG);
+	device = g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ());
 	if (device)
 		default_wired_clear_tag (self, device, connection, FALSE);
 }
@@ -1984,11 +2029,11 @@ default_wired_clear_tag (NMSettings *self,
 	g_return_if_fail (NM_IS_SETTINGS (self));
 	g_return_if_fail (NM_IS_DEVICE (device));
 	g_return_if_fail (NM_IS_CONNECTION (connection));
-	g_return_if_fail (device == g_object_get_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG));
-	g_return_if_fail (connection == g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG));
+	g_return_if_fail (device == g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ()));
+	g_return_if_fail (connection == g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ()));
 
-	g_object_set_data (G_OBJECT (connection), DEFAULT_WIRED_DEVICE_TAG, NULL);
-	g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, NULL);
+	g_object_set_qdata (G_OBJECT (connection), _default_wired_device_quark (), NULL);
+	g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL);
 
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_removed_cb), self);
 	g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
@@ -2015,7 +2060,7 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
 	 * ignore it.
 	 */
 	if (   !nm_device_get_managed (device, FALSE)
-	    || g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG)
+	    || g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ())
 	    || have_connection_for_device (self, device))
 		return;
 
@@ -2037,8 +2082,8 @@ device_realized (NMDevice *device, GParamSpec *pspec, NMSettings *self)
 		return;
 	}
 
-	g_object_set_data (G_OBJECT (added), DEFAULT_WIRED_DEVICE_TAG, device);
-	g_object_set_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG, added);
+	g_object_set_qdata (G_OBJECT (added), _default_wired_device_quark (), device);
+	g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), added);
 
 	g_signal_connect (added, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
 	                  G_CALLBACK (default_wired_connection_updated_by_user_cb), self);
@@ -2071,7 +2116,7 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin
 	                                      G_CALLBACK (device_realized),
 	                                      self);
 
-	connection = g_object_get_data (G_OBJECT (device), DEFAULT_WIRED_CONNECTION_TAG);
+	connection = g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ());
 	if (connection) {
 		default_wired_clear_tag (self, device, connection, FALSE);
 
@@ -2085,107 +2130,6 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin
 
 /*****************************************************************************/
 
-/* GCompareFunc helper for sorting "best" connections.
- * The function sorts connections in ascending timestamp order.
- * That means an older connection (lower timestamp) goes before
- * a newer one.
- */
-gint
-nm_settings_sort_connections (gconstpointer a, gconstpointer b)
-{
-	NMSettingsConnection *ac = (NMSettingsConnection *) a;
-	NMSettingsConnection *bc = (NMSettingsConnection *) b;
-	guint64 ats = 0, bts = 0;
-
-	if (ac == bc)
-		return 0;
-	if (!ac)
-		return -1;
-	if (!bc)
-		return 1;
-
-	/* In the future we may use connection priorities in addition to timestamps */
-	nm_settings_connection_get_timestamp (ac, &ats);
-	nm_settings_connection_get_timestamp (bc, &bts);
-
-	if (ats < bts)
-		return -1;
-	else if (ats > bts)
-		return 1;
-	return 0;
-}
-
-/**
- * nm_settings_get_best_connections:
- * @self: the #NMSetting
- * @max_requested: if non-zero, the maximum number of connections to return
- * @ctype1: an #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME) to
- *   filter connections against
- * @ctype2: a second #NMSetting base type (eg NM_SETTING_WIRELESS_SETTING_NAME)
- *   to filter connections against
- * @func: caller-supplied function for filtering connections
- * @func_data: caller-supplied data passed to @func
- *
- * Returns: a #GSList of #NMConnection objects in sorted order representing the
- *   "best" or highest-priority connections filtered by @ctype1 and/or @ctype2,
- *   and/or @func.  Caller is responsible for freeing the returned #GSList, but
- *   the contained values do not need to be unreffed.
- */
-GSList *
-nm_settings_get_best_connections (NMSettings *self,
-                                  guint max_requested,
-                                  const char *ctype1,
-                                  const char *ctype2,
-                                  NMConnectionFilterFunc func,
-                                  gpointer func_data)
-{
-	NMSettingsPrivate *priv;
-	GSList *sorted = NULL;
-	GHashTableIter iter;
-	NMSettingsConnection *connection;
-	guint added = 0;
-	guint64 oldest = 0;
-
-	g_return_val_if_fail (NM_IS_SETTINGS (self), NULL);
-
-	priv = NM_SETTINGS_GET_PRIVATE (self);
-
-	g_hash_table_iter_init (&iter, priv->connections);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) {
-		guint64 cur_ts = 0;
-
-		if (ctype1 && !nm_connection_is_type (NM_CONNECTION (connection), ctype1))
-			continue;
-		if (ctype2 && !nm_connection_is_type (NM_CONNECTION (connection), ctype2))
-			continue;
-		if (func && !func (self, NM_CONNECTION (connection), func_data))
-			continue;
-
-		/* Don't bother with a connection that's older than the oldest one in the list */
-		if (max_requested && added >= max_requested) {
-		    nm_settings_connection_get_timestamp (connection, &cur_ts);
-		    if (cur_ts <= oldest)
-				continue;
-		}
-
-		/* List is sorted with oldest first */
-		sorted = g_slist_insert_sorted (sorted, connection, nm_settings_sort_connections);
-		added++;
-
-		if (max_requested && added > max_requested) {
-			/* Over the limit, remove the oldest one */
-			sorted = g_slist_delete_link (sorted, sorted);
-			added--;
-		}
-
-		nm_settings_connection_get_timestamp (NM_SETTINGS_CONNECTION (sorted->data), &oldest);
-	}
-
-	return g_slist_reverse (sorted);
-}
-
-/*****************************************************************************/
-
 gboolean
 nm_settings_get_startup_complete (NMSettings *self)
 {
@@ -2221,7 +2165,7 @@ hostnamed_properties_changed (GDBusProxy *proxy,
 		g_free (priv->hostname.value);
 		priv->hostname.value = g_strdup (hostname);
 		_notify (self, PROP_HOSTNAME);
-		nm_dispatcher_call (DISPATCHER_ACTION_HOSTNAME, NULL, NULL, NULL, NULL, NULL, NULL);
+		nm_dispatcher_call_hostname (NULL, NULL, NULL);
 	}
 
 	g_variant_unref (v_hostname);
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index c8a7ebae..7110a12b 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -26,7 +26,7 @@
 #ifndef __NM_SETTINGS_H__
 #define __NM_SETTINGS_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 #include "nm-exported-object.h"
 
@@ -57,9 +57,9 @@
  *
  * Returns: %TRUE to allow the connection, %FALSE to ignore it
  */
-typedef gboolean (*NMConnectionFilterFunc) (NMSettings *settings,
-                                            NMConnection *connection,
-                                            gpointer func_data);
+typedef gboolean (*NMSettingsConnectionFilterFunc) (NMSettings *settings,
+                                                    NMSettingsConnection *connection,
+                                                    gpointer func_data);
 
 typedef struct _NMSettingsClass NMSettingsClass;
 
@@ -97,14 +97,13 @@ void nm_settings_add_connection_dbus (NMSettings *self,
 
 NMSettingsConnection *const* nm_settings_get_connections (NMSettings *settings, guint *out_len);
 
-GSList *nm_settings_get_connections_sorted (NMSettings *settings);
+NMSettingsConnection **nm_settings_get_connections_clone (NMSettings *self,
+                                                          guint *out_len,
+                                                          NMSettingsConnectionFilterFunc func,
+                                                          gpointer func_data);
 
-GSList *nm_settings_get_best_connections (NMSettings *self,
-                                          guint max_requested,
-                                          const char *ctype1,
-                                          const char *ctype2,
-                                          NMConnectionFilterFunc func,
-                                          gpointer func_data);
+NMSettingsConnection **nm_settings_get_connections_sorted (NMSettings *self,
+                                                           guint *out_len);
 
 NMSettingsConnection *nm_settings_add_connection (NMSettings *settings,
                                                   NMConnection *connection,
@@ -126,8 +125,6 @@ void nm_settings_device_added (NMSettings *self, NMDevice *device);
 
 void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting);
 
-gint nm_settings_sort_connections (gconstpointer a, gconstpointer b);
-
 gboolean nm_settings_get_startup_complete (NMSettings *self);
 
 void nm_settings_set_transient_hostname (NMSettings *self,
@@ -135,4 +132,7 @@ void nm_settings_set_transient_hostname (NMSettings *self,
                                          NMSettingsSetHostnameCb cb,
                                          gpointer user_data);
 
+gboolean nm_settings_get_transient_hostname (NMSettings *self,
+                                             char **hostname);
+
 #endif  /* __NM_SETTINGS_H__ */
diff --git a/src/settings/plugins/ibft/nms-ibft-reader.h b/src/settings/plugins/ibft/nms-ibft-reader.h
index e2be7b02..27500cc5 100644
--- a/src/settings/plugins/ibft/nms-ibft-reader.h
+++ b/src/settings/plugins/ibft/nms-ibft-reader.h
@@ -21,7 +21,7 @@
 #ifndef __NMS_IBFT_READER_H__
 #define __NMS_IBFT_READER_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 gboolean nms_ibft_reader_load_blocks (const char *iscsiadm_path,
                                       GSList **out_blocks,
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
index 33f4847e..b54f9549 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
@@ -336,43 +336,24 @@ commit_changes (NMSettingsConnection *connection,
                 gpointer user_data)
 {
 	GError *error = NULL;
-	NMConnection *reread;
-	gboolean same = FALSE, success = FALSE;
+	gboolean success = FALSE;
 	char *ifcfg_path = NULL;
 	const char *filename;
 
-	/* To ensure we don't rewrite files that are only changed from other
-	 * processes on-disk, read the existing connection back in and only rewrite
-	 * it if it's really changed.
-	 */
 	filename = nm_settings_connection_get_filename (connection);
 	if (filename) {
-		gs_free char *unhandled = NULL;
-
-		reread = connection_from_file (filename, &unhandled, NULL, NULL);
-		if (reread) {
-			same = nm_connection_compare (NM_CONNECTION (connection),
-			                              reread,
-			                              NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
-			                                 NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS);
-			g_object_unref (reread);
-
-			/* Don't bother writing anything out if in-memory and on-disk data are the same */
-			if (same) {
-				/* But chain up to parent to handle success - emits updated signal */
-				NM_SETTINGS_CONNECTION_CLASS (nm_ifcfg_connection_parent_class)->commit_changes (connection, commit_reason, callback, user_data);
-				return;
-			}
-		}
-
 		success = writer_update_connection (NM_CONNECTION (connection),
 		                                    IFCFG_DIR,
 		                                    filename,
+		                                    NULL,
+		                                    NULL,
 		                                    &error);
 	} else {
 		success = writer_new_connection (NM_CONNECTION (connection),
 		                                 IFCFG_DIR,
 		                                 &ifcfg_path,
+		                                 NULL,
+		                                 NULL,
 		                                 &error);
 		if (success) {
 			nm_settings_connection_set_filename (connection, ifcfg_path);
@@ -478,6 +459,11 @@ nm_ifcfg_connection_new (NMConnection *source,
 	if (out_ignore_error)
 		*out_ignore_error = FALSE;
 
+	if (full_path) {
+		/* The connection already is on the disk */
+		update_unsaved = FALSE;
+	}
+
 	/* If we're given a connection already, prefer that instead of re-reading */
 	if (source)
 		tmp = g_object_ref (source);
@@ -488,9 +474,6 @@ nm_ifcfg_connection_new (NMConnection *source,
 		                            out_ignore_error);
 		if (!tmp)
 			return NULL;
-
-		/* If we just read the connection from disk, it's clearly not Unsaved */
-		update_unsaved = FALSE;
 	}
 
 	if (unhandled_spec && g_str_has_prefix (unhandled_spec, "unmanaged:"))
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
index 414f593b..a3092e71 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -98,7 +98,7 @@ NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfcfg, settings_plugin_ifcfg_get, SETT
 #define _NMLOG_DOMAIN  LOGD_SETTINGS
 #define _NMLOG(level, ...) \
     G_STMT_START { \
-        nm_log ((level), (_NMLOG_DOMAIN), \
+        nm_log ((level), (_NMLOG_DOMAIN), NULL, NULL, \
                 "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                 "ifcfg-rh: " \
                 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
@@ -687,7 +687,7 @@ add_connection (NMSettingsPlugin *config,
 		return NULL;
 
 	if (save_to_disk) {
-		if (!writer_new_connection (connection, IFCFG_DIR, &path, error))
+		if (!writer_new_connection (connection, IFCFG_DIR, &path, NULL, NULL, error))
 			return NULL;
 	}
 	return NM_SETTINGS_CONNECTION (update_connection (self, connection, path, NULL, FALSE, NULL, error));
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index 46bd2de1..164f6844 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -48,6 +48,7 @@
 #include "nm-setting-bridge.h"
 #include "nm-setting-bridge-port.h"
 #include "nm-setting-dcb.h"
+#include "nm-setting-user.h"
 #include "nm-setting-proxy.h"
 #include "nm-setting-generic.h"
 #include "nm-core-internal.h"
@@ -66,7 +67,7 @@
 #define _NMLOG_PREFIX_NAME "ifcfg-rh"
 #define _NMLOG(level, ...) \
     G_STMT_START { \
-        nm_log ((level), (_NMLOG_DOMAIN), \
+        nm_log ((level), (_NMLOG_DOMAIN), NULL, NULL, \
                 "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                 _NMLOG_PREFIX_NAME": " \
                 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
@@ -97,14 +98,13 @@ make_connection_name (shvarFile *ifcfg,
 	char *full_name = NULL, *name;
 
 	/* If the ifcfg file already has a NAME, always use that */
-	name = svGetValueString (ifcfg, "NAME");
-	if (name && strlen (name))
+	name = svGetValueStr_cp (ifcfg, "NAME");
+	if (name)
 		return name;
 
 	/* Otherwise construct a new NAME */
-	g_free (name);
 	if (!prefix)
-		prefix = _("System");
+		prefix = "System";
 
 	/* For cosmetic reasons, if the suggested name is the same as
 	 * the ifcfg files name, don't use it.  Mainly for wifi so that
@@ -142,22 +142,18 @@ make_connection_setting (const char *file,
 	g_free (new_id);
 
 	/* Try for a UUID key before falling back to hashing the file name */
-	uuid = svGetValueString (ifcfg, "UUID");
-	if (!uuid || !strlen (uuid)) {
-		g_free (uuid);
+	uuid = svGetValueStr_cp (ifcfg, "UUID");
+	if (!uuid)
 		uuid = nm_utils_uuid_generate_from_string (svFileGetName (ifcfg), -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
-	}
-
-	stable_id = svGetValueString (ifcfg, "STABLE_ID");
 
 	g_object_set (s_con,
 	              NM_SETTING_CONNECTION_TYPE, type,
 	              NM_SETTING_CONNECTION_UUID, uuid,
-	              NM_SETTING_CONNECTION_STABLE_ID, stable_id,
+	              NM_SETTING_CONNECTION_STABLE_ID, svGetValue (ifcfg, "STABLE_ID", &stable_id),
 	              NULL);
 	g_free (uuid);
 
-	value = svGetValueString (ifcfg, "DEVICE");
+	value = svGetValueStr_cp (ifcfg, "DEVICE");
 	if (value) {
 		GError *error = NULL;
 
@@ -172,7 +168,7 @@ make_connection_setting (const char *file,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "LLDP");
+	value = svGetValueStr_cp (ifcfg, "LLDP");
 	if (!g_strcmp0 (value, "rx"))
 		lldp = NM_SETTING_CONNECTION_LLDP_ENABLE_RX;
 	else
@@ -196,7 +192,7 @@ make_connection_setting (const char *file,
 	              NM_SETTING_CONNECTION_LLDP, lldp,
 	              NULL);
 
-	value = svGetValueString (ifcfg, "USERS");
+	value = svGetValueStr_cp (ifcfg, "USERS");
 	if (value) {
 		char **items, **iter;
 
@@ -212,15 +208,11 @@ make_connection_setting (const char *file,
 	}
 
 
-	zone = svGetValueString (ifcfg, "ZONE");
-	if (!zone || !strlen (zone)) {
-		g_free (zone);
-		zone = NULL;
-	}
+	zone = svGetValueStr_cp (ifcfg, "ZONE");
 	g_object_set (s_con, NM_SETTING_CONNECTION_ZONE, zone, NULL);
 	g_free (zone);
 
-	value = svGetValueString (ifcfg, "SECONDARY_UUIDS");
+	value = svGetValueStr_cp (ifcfg, "SECONDARY_UUIDS");
 	if (value) {
 		char **items, **iter;
 
@@ -235,9 +227,9 @@ make_connection_setting (const char *file,
 		g_strfreev (items);
 	}
 
-	value = svGetValueString (ifcfg, "BRIDGE_UUID");
+	value = svGetValueStr_cp (ifcfg, "BRIDGE_UUID");
 	if (!value)
-		value = svGetValueString (ifcfg, "BRIDGE");
+		value = svGetValueStr_cp (ifcfg, "BRIDGE");
 	if (value) {
 		const char *old_value;
 
@@ -252,7 +244,7 @@ make_connection_setting (const char *file,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "GATEWAY_PING_TIMEOUT");
+	value = svGetValueStr_cp (ifcfg, "GATEWAY_PING_TIMEOUT");
 	if (value) {
 		gint64 tmp;
 
@@ -280,75 +272,82 @@ make_connection_setting (const char *file,
 static gboolean
 read_ip4_address (shvarFile *ifcfg,
                   const char *tag,
-                  char **out_addr,
+                  gboolean *out_has_key,
+                  guint32 *out_addr,
                   GError **error)
 {
-	char *value = NULL;
-
-	g_return_val_if_fail (ifcfg != NULL, FALSE);
-	g_return_val_if_fail (tag != NULL, FALSE);
-	g_return_val_if_fail (out_addr != NULL, FALSE);
-	g_return_val_if_fail (!error || !*error, FALSE);
+	gs_free char *value_to_free = NULL;
+	const char *value;
+	guint32 a;
 
-	*out_addr = NULL;
+	nm_assert (ifcfg);
+	nm_assert (tag);
+	nm_assert (!error || !*error);
 
-	value = svGetValueString (ifcfg, tag);
-	if (!value)
+	value = svGetValueStr (ifcfg, tag, &value_to_free);
+	if (!value) {
+		NM_SET_OUT (out_has_key, FALSE);
+		NM_SET_OUT (out_addr, 0);
 		return TRUE;
+	}
 
-	if (nm_utils_ipaddr_valid (AF_INET, value)) {
-		*out_addr = value;
-		return TRUE;
-	} else {
+	if (inet_pton (AF_INET, value, &a) != 1) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Invalid %s IP4 address '%s'", tag, value);
-		g_free (value);
 		return FALSE;
 	}
+
+	NM_SET_OUT (out_has_key, TRUE);
+	NM_SET_OUT (out_addr, a);
+	return TRUE;
 }
 
-static char *
-get_numbered_tag (char *tag_name, int which)
+static void
+_numbered_tag (char *buf, gsize buf_len, const char *tag_name, int which)
 {
-	if (which == -1)
-		return g_strdup (tag_name);
-	return g_strdup_printf ("%s%u", tag_name, which);
+	gsize l;
+
+	l = g_strlcpy (buf, tag_name, buf_len);
+	nm_assert (l < buf_len);
+	if (which != -1) {
+		buf_len -= l;
+		l = g_snprintf (&buf[l], buf_len, "%d", which);
+		nm_assert (l < buf_len);
+	}
 }
+#define numbered_tag(buf, tag_name, which) \
+	({ \
+		_nm_unused char *const _buf = (buf); \
+		\
+		/* some static assert trying to ensure that the buffer is statically allocated.
+		 * It disallows a buffer size of sizeof(gpointer) to catch that. */ \
+		G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) != sizeof (char *) && sizeof (buf) < G_MAXINT); \
+		_numbered_tag (buf, sizeof (buf), ""tag_name"", (which)); \
+		buf; \
+	})
 
 static gboolean
 is_any_ip4_address_defined (shvarFile *ifcfg, int *idx)
 {
-	int i, ignore, *ret_idx;;
+	int i, ignore, *ret_idx;
 
 	ret_idx = idx ? idx : &ignore;
 
 	for (i = -1; i <= 2; i++) {
-		char *tag;
-		char *value;
+		gs_free char *value = NULL;
+		char tag[256];
 
-		tag = get_numbered_tag ("IPADDR", i);
-		value = svGetValueString (ifcfg, tag);
-		g_free (tag);
-		if (value) {
-			g_free (value);
+		if (svGetValueStr (ifcfg, numbered_tag (tag, "IPADDR", i), &value)) {
 			*ret_idx = i;
 			return TRUE;
 		}
 
-		tag = get_numbered_tag ("PREFIX", i);
-		value = svGetValueString (ifcfg, tag);
-		g_free(tag);
-		if (value) {
-			g_free (value);
+		if (svGetValueStr (ifcfg, numbered_tag (tag, "PREFIX", i), &value)) {
 			*ret_idx = i;
 			return TRUE;
 		}
 
-		tag = get_numbered_tag ("NETMASK", i);
-		value = svGetValueString (ifcfg, tag);
-		g_free(tag);
-		if (value) {
-			g_free (value);
+		if (svGetValueStr (ifcfg, numbered_tag (tag, "NETMASK", i), &value)) {
 			*ret_idx = i;
 			return TRUE;
 		}
@@ -365,12 +364,14 @@ read_full_ip4_address (shvarFile *ifcfg,
                        char **out_gateway,
                        GError **error)
 {
-	char *ip_tag, *prefix_tag, *netmask_tag, *gw_tag;
-	char *ip = NULL;
+	char tag[256];
+	char prefix_tag[256];
+	guint32 ipaddr;
+	gs_free char *value = NULL;
 	int prefix = 0;
-	gboolean success = FALSE;
-	char *value;
-	guint32 tmp;
+	gboolean has_key;
+	guint32 a;
+	char inet_buf[NM_UTILS_INET_ADDRSTRLEN];
 
 	g_return_val_if_fail (which >= -1, FALSE);
 	g_return_val_if_fail (ifcfg != NULL, FALSE);
@@ -378,76 +379,186 @@ read_full_ip4_address (shvarFile *ifcfg,
 	g_return_val_if_fail (*out_address == NULL, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
-	ip_tag = get_numbered_tag ("IPADDR", which);
-	prefix_tag = get_numbered_tag ("PREFIX", which);
-	netmask_tag = get_numbered_tag ("NETMASK", which);
-	gw_tag = get_numbered_tag ("GATEWAY", which);
-
 	/* IP address */
-	if (!read_ip4_address (ifcfg, ip_tag, &ip, error))
-		goto done;
-	if (!ip) {
-		if (base_addr)
-			ip = g_strdup (nm_ip_address_get_address (base_addr));
-		else {
-			success = TRUE;
-			goto done;
-		}
+	if (!read_ip4_address (ifcfg,
+	                       numbered_tag (tag, "IPADDR", which),
+	                       &has_key, &ipaddr, error))
+		return FALSE;
+	if (!has_key) {
+		if (!base_addr)
+			return TRUE;
+		nm_ip_address_get_address_binary (base_addr, &ipaddr);
 	}
 
 	/* Gateway */
 	if (out_gateway && !*out_gateway) {
-		if (!read_ip4_address (ifcfg, gw_tag, out_gateway, error))
-			goto done;
+		if (!read_ip4_address (ifcfg,
+		                       numbered_tag (tag, "GATEWAY", which),
+		                       &has_key, &a, error))
+			return FALSE;
+		if (has_key)
+			*out_gateway = g_strdup (nm_utils_inet4_ntop (a, inet_buf));
 	}
 
 	/* Prefix */
-	value = svGetValueString (ifcfg, prefix_tag);
+	numbered_tag (prefix_tag, "PREFIX", which);
+	value = svGetValueStr_cp (ifcfg, prefix_tag);
 	if (value) {
 		prefix = _nm_utils_ascii_str_to_int64 (value, 10, 0, 32, -1);
 		if (prefix < 0) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid IP4 prefix '%s'", value);
-			g_free (value);
-			goto done;
+			return FALSE;
 		}
-		g_free (value);
 	} else {
 		/* Fall back to NETMASK if no PREFIX was specified */
-		if (!read_ip4_address (ifcfg, netmask_tag, &value, error))
-			goto done;
-		if (value) {
-			inet_pton (AF_INET, value, &tmp);
-			prefix = nm_utils_ip4_netmask_to_prefix (tmp);
-			g_free (value);
-		} else {
+		if (!read_ip4_address (ifcfg,
+		                       numbered_tag (tag, "NETMASK", which),
+		                       &has_key, &a, error))
+			return FALSE;
+		if (has_key)
+			prefix = nm_utils_ip4_netmask_to_prefix (a);
+		else {
 			if (base_addr)
 				prefix = nm_ip_address_get_prefix (base_addr);
 			else {
 				/* Try to autodetermine the prefix for the address' class */
-				if (inet_pton (AF_INET, ip, &tmp) == 1) {
-					prefix = nm_utils_ip4_get_default_prefix (tmp);
-
-					PARSE_WARNING ("missing %s, assuming %s/%d", prefix_tag, ip, prefix);
-				} else {
-					g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-					             "Missing IP4 prefix");
-					goto done;
-				}
+				prefix = nm_utils_ip4_get_default_prefix (ipaddr);
+				PARSE_WARNING ("missing %s, assuming %s/%d", prefix_tag, nm_utils_inet4_ntop (ipaddr, inet_buf), prefix);
 			}
 		}
 	}
 
-	*out_address = nm_ip_address_new (AF_INET, ip, prefix, error);
+	*out_address = nm_ip_address_new_binary (AF_INET, &ipaddr, prefix, error);
 	if (*out_address)
-		success = TRUE;
+		return TRUE;
 
-done:
-	g_free (ip);
-	g_free (ip_tag);
-	g_free (prefix_tag);
-	g_free (netmask_tag);
-	g_free (gw_tag);
+	return FALSE;
+}
+
+/*
+ * Use looser syntax to comprise all the possibilities.
+ * The validity must be checked after the match.
+ */
+#define IPV4_ADDR_REGEX "(?:[0-9]{1,3}\\.){3}[0-9]{1,3}"
+#define IPV6_ADDR_REGEX "[0-9A-Fa-f:.]+"
+
+/*
+ * NOTE: The regexes below don't describe all variants allowed by 'ip route add',
+ * namely destination IP without 'to' keyword is recognized just at line start.
+ */
+
+static gboolean
+parse_route_options (NMIPRoute *route, int family, const char *line, GError **error)
+{
+	GRegex *regex = NULL;
+	GMatchInfo *match_info = NULL;
+	gboolean success = FALSE;
+	static const char *metrics[] = { NM_IP_ROUTE_ATTRIBUTE_WINDOW, NM_IP_ROUTE_ATTRIBUTE_CWND,
+	                                 NM_IP_ROUTE_ATTRIBUTE_INITCWND, NM_IP_ROUTE_ATTRIBUTE_INITRWND,
+	                                 NM_IP_ROUTE_ATTRIBUTE_MTU, NULL };
+	char buffer[1024];
+	int i;
+
+	g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE);
+
+	for (i = 0; metrics[i]; i++) {
+		nm_sprintf_buf (buffer, "(?:\\s|^)%s\\s+(lock\\s+)?(\\d+)(?:$|\\s)", metrics[i]);
+		regex = g_regex_new (buffer, 0, 0, NULL);
+		g_regex_match (regex, line, 0, &match_info);
+		if (g_match_info_matches (match_info)) {
+			gs_free char *lock = g_match_info_fetch (match_info, 1);
+			gs_free char *str = g_match_info_fetch (match_info, 2);
+			gint64 num = _nm_utils_ascii_str_to_int64 (str, 10, 0, G_MAXUINT32, -1);
+
+			if (num == -1) {
+				g_match_info_free (match_info);
+				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				             "Invalid route %s '%s'", metrics[i], str);
+				goto out;
+			}
+
+			nm_ip_route_set_attribute (route, metrics[i],
+			                           g_variant_new_uint32 (num));
+			if (lock && lock[0]) {
+				nm_sprintf_buf (buffer, "lock-%s", metrics[i]);
+				nm_ip_route_set_attribute (route, buffer,
+				                           g_variant_new_boolean (TRUE));
+			}
+		}
+		g_clear_pointer (&regex, g_regex_unref);
+		g_clear_pointer (&match_info, g_match_info_free);
+	}
+
+	/* tos */
+	regex = g_regex_new ("(?:\\s|^)tos\\s+(\\S+)(?:$|\\s)", 0, 0, NULL);
+	g_regex_match (regex, line, 0, &match_info);
+	if (g_match_info_matches (match_info)) {
+		gs_free char *str = g_match_info_fetch (match_info, 1);
+		gint64 num = _nm_utils_ascii_str_to_int64 (str, 0, 0, G_MAXUINT8, -1);
+
+		if (num == -1) {
+			g_match_info_free (match_info);
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Invalid route %s '%s'", "tos", str);
+			goto out;
+		}
+		nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_TOS,
+		                           g_variant_new_byte ((guchar) num));
+	}
+	g_clear_pointer (&regex, g_regex_unref);
+	g_clear_pointer (&match_info, g_match_info_free);
+
+	/* from */
+	if (family == AF_INET6) {
+		regex = g_regex_new ("(?:\\s|^)from\\s+(" IPV6_ADDR_REGEX "(?:/\\d{1,3})?)(?:$|\\s)", 0, 0, NULL);
+		g_regex_match (regex, line, 0, &match_info);
+		if (g_match_info_matches (match_info)) {
+			gs_free char *str = g_match_info_fetch (match_info, 1);
+			gs_free_error GError *local_error = NULL;
+			GVariant *variant = g_variant_new_string (str);
+
+			if (!nm_ip_route_attribute_validate (NM_IP_ROUTE_ATTRIBUTE_FROM, variant, family, NULL, &local_error)) {
+				g_match_info_free (match_info);
+				g_variant_unref (variant);
+				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+				             "Invalid route from '%s': %s", str, local_error->message);
+				goto out;
+			}
+			nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_FROM, variant);
+		}
+		g_clear_pointer (&regex, g_regex_unref);
+		g_clear_pointer (&match_info, g_match_info_free);
+	}
+
+	if (family == AF_INET)
+		regex = g_regex_new ("(?:\\s|^)src\\s+(" IPV4_ADDR_REGEX ")(?:$|\\s)", 0, 0, NULL);
+	else
+		regex = g_regex_new ("(?:\\s|^)src\\s+(" IPV6_ADDR_REGEX ")(?:$|\\s)", 0, 0, NULL);
+	g_regex_match (regex, line, 0, &match_info);
+	if (g_match_info_matches (match_info)) {
+		gs_free char *str = g_match_info_fetch (match_info, 1);
+		gs_free_error GError *local_error = NULL;
+		GVariant *variant = g_variant_new_string (str);
+
+		if (!nm_ip_route_attribute_validate (NM_IP_ROUTE_ATTRIBUTE_SRC, variant, family,
+		                                     NULL, &local_error)) {
+			g_match_info_free (match_info);
+			g_variant_unref (variant);
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Invalid route src '%s': %s", str, local_error->message);
+			goto out;
+		}
+
+		nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_SRC, variant);
+	}
+	success = TRUE;
+
+out:
+	if (regex)
+		g_regex_unref (regex);
+	if (match_info)
+		g_match_info_free (match_info);
 
 	return success;
 }
@@ -459,87 +570,85 @@ read_one_ip4_route (shvarFile *ifcfg,
                     NMIPRoute **out_route,
                     GError **error)
 {
-	char *ip_tag, *netmask_tag, *gw_tag, *metric_tag, *value;
-	char *dest = NULL, *next_hop = NULL;
+	char tag[256];
+	char netmask_tag[256];
+	guint32 dest;
+	guint32 next_hop;
+	guint32 netmask;
+	gboolean has_key;
+	gs_free char *value = NULL;
 	gint64 prefix, metric;
-	gboolean success = FALSE;
+	char inet_buf[NM_UTILS_INET_ADDRSTRLEN];
 
 	g_return_val_if_fail (ifcfg != NULL, FALSE);
 	g_return_val_if_fail (out_route != NULL, FALSE);
 	g_return_val_if_fail (*out_route == NULL, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
 
-	ip_tag = g_strdup_printf ("ADDRESS%u", which);
-	netmask_tag = g_strdup_printf ("NETMASK%u", which);
-	gw_tag = g_strdup_printf ("GATEWAY%u", which);
-	metric_tag = g_strdup_printf ("METRIC%u", which);
-
 	/* Destination */
-	if (!read_ip4_address (ifcfg, ip_tag, &dest, error))
-		goto out;
-	if (!dest) {
-		/* Check whether IP is missing or 0.0.0.0 */
-		char *val;
-		val = svGetValueString (ifcfg, ip_tag);
-		if (!val) {
-			*out_route = NULL;
-			success = TRUE;  /* missing route = success */
-			goto out;
-		}
-		g_free (val);
+	if (!read_ip4_address (ifcfg,
+	                       numbered_tag (tag, "ADDRESS", which),
+	                       &has_key, &dest, error))
+		return FALSE;
+	if (!has_key) {
+		/* missing route = success */
+		*out_route = NULL;
+		return TRUE;
 	}
 
 	/* Next hop */
-	if (!read_ip4_address (ifcfg, gw_tag, &next_hop, error))
-		goto out;
+	if (!read_ip4_address (ifcfg,
+	                       numbered_tag (tag, "GATEWAY", which),
+	                       NULL, &next_hop, error))
+		return FALSE;
 	/* We don't make distinction between missing GATEWAY IP and 0.0.0.0 */
 
 	/* Prefix */
-	if (!read_ip4_address (ifcfg, netmask_tag, &value, error))
-		goto out;
-	if (value) {
-		guint32 netmask;
-
-		inet_pton (AF_INET, value, &netmask);
+	if (!read_ip4_address (ifcfg,
+	                       numbered_tag (netmask_tag, "NETMASK", which),
+	                       &has_key, &netmask, error))
+		return FALSE;
+	if (has_key) {
 		prefix = nm_utils_ip4_netmask_to_prefix (netmask);
-		g_free (value);
 		if (prefix == 0 || netmask != nm_utils_ip4_prefix_to_netmask (prefix)) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, nm_utils_inet4_ntop (netmask, NULL));
-			goto out;
+			             "Invalid IP4 netmask '%s' \"%s\"", netmask_tag, nm_utils_inet4_ntop (netmask, inet_buf));
+			return FALSE;
 		}
 	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IP4 route element '%s'", netmask_tag);
-		goto out;
+		return FALSE;
 	}
 
 	/* Metric */
-	value = svGetValueString (ifcfg, metric_tag);
+	nm_clear_g_free (&value);
+	value = svGetValueStr_cp (ifcfg, numbered_tag (tag, "METRIC", which));
 	if (value) {
 		metric = _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, -1);
 		if (metric < 0) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid IP4 route metric '%s'", value);
-			g_free (value);
-			goto out;
+			return FALSE;
 		}
-		g_free (value);
 	} else
 		metric = -1;
 
-	*out_route = nm_ip_route_new (AF_INET, dest, prefix, next_hop, metric, error);
-	if (*out_route)
-		success = TRUE;
+	*out_route = nm_ip_route_new_binary (AF_INET, &dest, prefix, &next_hop, metric, error);
+	if (!*out_route)
+		return FALSE;
 
-out:
-	g_free (dest);
-	g_free (next_hop);
-	g_free (ip_tag);
-	g_free (netmask_tag);
-	g_free (gw_tag);
-	g_free (metric_tag);
-	return success;
+	/* Options */
+	nm_clear_g_free (&value);
+	value = svGetValueStr_cp (ifcfg, numbered_tag (tag, "OPTIONS", which));
+	if (value) {
+		if (!parse_route_options (*out_route, AF_INET, value, error)) {
+			g_clear_pointer (out_route, nm_ip_route_unref);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
 }
 
 static gboolean
@@ -665,6 +774,12 @@ read_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError *
 		route = nm_ip_route_new (AF_INET, dest, prefix_int, next_hop, metric_int, error);
 		if (!route)
 			goto error;
+
+		if (!parse_route_options (route, AF_INET, *iter, error)) {
+			nm_ip_route_unref (route);
+			goto error;
+		}
+
 		if (!nm_setting_ip_config_add_route (s_ip4, route))
 			PARSE_WARNING ("duplicate IP4 route");
 		nm_ip_route_unref (route);
@@ -758,13 +873,6 @@ error:
 	return success;
 }
 
-/* IPv6 address is very complex to describe completely by a regular expression,
- * so don't try to, rather use looser syntax to comprise all possibilities
- * NOTE: The regexes below don't describe all variants allowed by 'ip route add',
- * namely destination IP without 'to' keyword is recognized just at line start.
- */
-#define IPV6_ADDR_REGEX "[0-9A-Fa-f:.]+"
-
 static gboolean
 read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error)
 {
@@ -786,6 +894,7 @@ read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error
 	const char *pattern_via = "via\\s+(" IPV6_ADDR_REGEX ")";       /* IPv6 of gateway */
 	const char *pattern_metric = "metric\\s+(\\d+)";                /* metric */
 
+
 	g_return_val_if_fail (filename != NULL, FALSE);
 	g_return_val_if_fail (s_ip6 != NULL, FALSE);
 	g_return_val_if_fail (!error || !*error, FALSE);
@@ -891,6 +1000,12 @@ read_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error
 		g_free (next_hop);
 		if (!route)
 			goto error;
+
+		if (!parse_route_options (route, AF_INET6, *iter, error)) {
+			nm_ip_route_unref (route);
+			goto error;
+		}
+
 		if (!nm_setting_ip_config_add_route (s_ip6, route))
 			PARSE_WARNING ("duplicate IP6 route");
 		nm_ip_route_unref (route);
@@ -910,13 +1025,61 @@ error:
 }
 
 static NMSetting *
+make_user_setting (shvarFile *ifcfg, GError **error)
+{
+	gboolean has_user_data = FALSE;
+	gs_unref_object NMSettingUser *s_user = NULL;
+	gs_unref_hashtable GHashTable *keys = NULL;
+	GHashTableIter iter;
+	const char *key;
+	nm_auto_free_gstring GString *str = NULL;
+
+	keys = svGetKeys (ifcfg);
+	if (!keys)
+		return NULL;
+
+	g_hash_table_iter_init (&iter, keys);
+	while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
+		const char *value;
+		gs_free char *value_to_free = NULL;
+
+		if (!g_str_has_prefix (key, "NM_USER_"))
+			continue;
+
+		value = svGetValue (ifcfg, key, &value_to_free);
+
+		if (!value)
+			continue;
+
+		if (!str)
+			str = g_string_sized_new (100);
+		else
+			g_string_set_size (str, 0);
+
+		if (!nms_ifcfg_rh_utils_user_key_decode (key + NM_STRLEN ("NM_USER_"), str))
+			continue;
+
+		if (!s_user)
+			s_user = NM_SETTING_USER (nm_setting_user_new ());
+
+		if (nm_setting_user_set_data (s_user, str->str,
+		                              value, NULL))
+			has_user_data = TRUE;
+	}
+
+	return has_user_data
+	       ? g_steal_pointer (&s_user)
+	       : NULL;
+}
+
+static NMSetting *
 make_proxy_setting (shvarFile *ifcfg, GError **error)
 {
 	NMSettingProxy *s_proxy = NULL;
 	char *value = NULL;
 	NMSettingProxyMethod method;
 
-	value = svGetValueString (ifcfg, "PROXY_METHOD");
+	value = svGetValueStr_cp (ifcfg, "PROXY_METHOD");
 	if (!value)
 		return NULL;
 
@@ -934,14 +1097,14 @@ make_proxy_setting (shvarFile *ifcfg, GError **error)
 		              NM_SETTING_PROXY_METHOD, (int) NM_SETTING_PROXY_METHOD_AUTO,
 		              NULL);
 
-		value = svGetValueString (ifcfg, "PAC_URL");
+		value = svGetValueStr_cp (ifcfg, "PAC_URL");
 		if (value) {
 			value = g_strstrip (value);
 			g_object_set (s_proxy, NM_SETTING_PROXY_PAC_URL, value, NULL);
 			g_free (value);
 		}
 
-		value = svGetValueString (ifcfg, "PAC_SCRIPT");
+		value = svGetValueStr_cp (ifcfg, "PAC_SCRIPT");
 		if (value) {
 			value = g_strstrip (value);
 			g_object_set (s_proxy, NM_SETTING_PROXY_PAC_SCRIPT, value, NULL);
@@ -956,7 +1119,7 @@ make_proxy_setting (shvarFile *ifcfg, GError **error)
 		break;
 	}
 
-	value = svGetValueString (ifcfg, "BROWSER_ONLY");
+	value = svGetValueStr_cp (ifcfg, "BROWSER_ONLY");
 	if (value) {
 		if (!g_ascii_strcasecmp (value, "yes"))
 			g_object_set (s_proxy, NM_SETTING_PROXY_BROWSER_ONLY, TRUE, NULL);
@@ -969,21 +1132,27 @@ make_proxy_setting (shvarFile *ifcfg, GError **error)
 static NMSetting *
 make_ip4_setting (shvarFile *ifcfg,
                   const char *network_file,
+                  gboolean *out_has_defroute,
                   GError **error)
 {
-	NMSettingIPConfig *s_ip4 = NULL;
+	gs_unref_object NMSettingIPConfig *s_ip4 = NULL;
+	gs_free char *route_path = NULL;
 	char *value = NULL;
-	char *route_path = NULL;
 	char *method;
 	gs_free char *dns_options_free = NULL;
 	const char *dns_options = NULL;
 	gs_free char *gateway = NULL;
-	gint32 i;
+	int i;
+	guint32 a;
+	gboolean has_key;
 	shvarFile *network_ifcfg;
 	shvarFile *route_ifcfg;
-	gboolean never_default = FALSE;
+	gboolean never_default;
 	gint64 timeout;
 	gint priority;
+	char inet_buf[NM_UTILS_INET_ADDRSTRLEN];
+
+	nm_assert (out_has_defroute && !*out_has_defroute);
 
 	s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
 
@@ -992,7 +1161,13 @@ make_ip4_setting (shvarFile *ifcfg,
 	 * specified is DEFROUTE=yes which means that this connection can be used
 	 * as a default route
 	 */
-	never_default = !svGetValueBoolean (ifcfg, "DEFROUTE", TRUE);
+	i = svGetValueBoolean (ifcfg, "DEFROUTE", -1);
+	if (i == -1)
+		never_default = FALSE;
+	else {
+		never_default = !i;
+		*out_has_defroute = TRUE;
+	}
 
 	/* Then check if GATEWAYDEV; it's global and overrides DEFROUTE */
 	network_ifcfg = svOpenFile (network_file, NULL);
@@ -1000,8 +1175,8 @@ make_ip4_setting (shvarFile *ifcfg,
 		char *gatewaydev;
 
 		/* Get the connection ifcfg device name and the global gateway device */
-		value = svGetValueString (ifcfg, "DEVICE");
-		gatewaydev = svGetValueString (network_ifcfg, "GATEWAYDEV");
+		value = svGetValueStr_cp (ifcfg, "DEVICE");
+		gatewaydev = svGetValueStr_cp (network_ifcfg, "GATEWAYDEV");
 		dns_options = svGetValue (network_ifcfg, "RES_OPTIONS", &dns_options_free);
 
 		/* If there was a global gateway device specified, then only connections
@@ -1015,7 +1190,7 @@ make_ip4_setting (shvarFile *ifcfg,
 		svCloseFile (network_ifcfg);
 	}
 
-	value = svGetValueString (ifcfg, "BOOTPROTO");
+	value = svGetValueStr_cp (ifcfg, "BOOTPROTO");
 
 	if (!value || !*value || !g_ascii_strcasecmp (value, "none")) {
 		if (is_any_ip4_address_defined (ifcfg, NULL))
@@ -1041,24 +1216,26 @@ make_ip4_setting (shvarFile *ifcfg,
 		              NULL);
 		/* 1 IP address is allowed for shared connections. Read it. */
 		if (is_any_ip4_address_defined (ifcfg, &idx)) {
+			guint32 gw;
 			NMIPAddress *addr = NULL;
 
 			if (!read_full_ip4_address (ifcfg, idx, NULL, &addr, NULL, error))
-				goto done;
-			if (!read_ip4_address (ifcfg, "GATEWAY", &gateway, error))
-				goto done;
+				return NULL;
+			if (!read_ip4_address (ifcfg, "GATEWAY", NULL, &gw, error))
+				return NULL;
 			(void) nm_setting_ip_config_add_address (s_ip4, addr);
 			nm_ip_address_unref (addr);
 			if (never_default)
 				PARSE_WARNING ("GATEWAY will be ignored when DEFROUTE is disabled");
+			gateway = g_strdup (nm_utils_inet4_ntop (gw, inet_buf));
 			g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL);
 		}
-		return NM_SETTING (s_ip4);
+		return g_steal_pointer (&s_ip4);
 	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Unknown BOOTPROTO '%s'", value);
 		g_free (value);
-		goto done;
+		return NULL;
 	}
 	g_free (value);
 
@@ -1073,32 +1250,34 @@ make_ip4_setting (shvarFile *ifcfg,
 	              NULL);
 
 	if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0)
-		return NM_SETTING (s_ip4);
+		return g_steal_pointer (&s_ip4);
 
 	/* Handle DHCP settings */
-	value = svGetValueString (ifcfg, "DHCP_HOSTNAME");
-	if (value && *value)
+	value = svGetValueStr_cp (ifcfg, "DHCP_HOSTNAME");
+	if (value) {
 		g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, value, NULL);
-	g_free (value);
+		g_free (value);
+	}
 
-	value = svGetValueString (ifcfg, "DHCP_FQDN");
-	if (value && *value) {
+	value = svGetValueStr_cp (ifcfg, "DHCP_FQDN");
+	if (value) {
 		g_object_set (s_ip4,
-			      NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, NULL,
-			      NM_SETTING_IP4_CONFIG_DHCP_FQDN, value,
-			      NULL);
+		              NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, NULL,
+		              NM_SETTING_IP4_CONFIG_DHCP_FQDN, value,
+		              NULL);
+		g_free (value);
 	}
-	g_free (value);
 
 	g_object_set (s_ip4,
 		      NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, svGetValueBoolean (ifcfg, "DHCP_SEND_HOSTNAME", TRUE),
 		      NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, svGetValueInt64 (ifcfg, "IPV4_DHCP_TIMEOUT", 10, 0, G_MAXINT32, 0),
 		      NULL);
 
-	value = svGetValueString (ifcfg, "DHCP_CLIENT_ID");
-	if (value && strlen (value))
+	value = svGetValueStr_cp (ifcfg, "DHCP_CLIENT_ID");
+	if (value) {
 		g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, value, NULL);
-	g_free (value);
+		g_free (value);
+	}
 
 	/* Read static IP addresses.
 	 * Read them even for AUTO method - in this case the addresses are
@@ -1111,7 +1290,7 @@ make_ip4_setting (shvarFile *ifcfg,
 		/* gateway will only be set if still unset. Hence, we don't leak gateway
 		 * here by calling read_full_ip4_address() repeatedly */
 		if (!read_full_ip4_address (ifcfg, i, NULL, &addr, &gateway, error))
-			goto done;
+			return NULL;
 
 		if (!addr) {
 			/* The first mandatory variable is 2-indexed (IPADDR2)
@@ -1132,16 +1311,17 @@ make_ip4_setting (shvarFile *ifcfg,
 		if (network_ifcfg) {
 			gboolean read_success;
 
-			read_success = read_ip4_address (network_ifcfg, "GATEWAY", &gateway, error);
+			read_success = read_ip4_address (network_ifcfg, "GATEWAY", &has_key, &a, error);
 			svCloseFile (network_ifcfg);
 			if (!read_success)
-				goto done;
-
-			if (gateway && nm_setting_ip_config_get_num_addresses (s_ip4) == 0) {
-				gs_free char *f = g_path_get_basename (svFileGetName (ifcfg));
-				PARSE_WARNING ("ignoring GATEWAY (/etc/sysconfig/network) for %s "
-				               "because the connection has no static addresses", f);
-				g_clear_pointer (&gateway, g_free);
+				return NULL;
+			if (has_key) {
+				if (nm_setting_ip_config_get_num_addresses (s_ip4) == 0) {
+					gs_free char *f = g_path_get_basename (svFileGetName (ifcfg));
+					PARSE_WARNING ("ignoring GATEWAY (/etc/sysconfig/network) for %s "
+					               "because the connection has no static addresses", f);
+				} else
+					gateway = g_strdup (nm_utils_inet4_ntop (a, inet_buf));
 			}
 		}
 	}
@@ -1154,10 +1334,10 @@ make_ip4_setting (shvarFile *ifcfg,
 	 * Pick up just IPv4 addresses (IPv6 addresses are taken by make_ip6_setting())
 	 */
 	for (i = 1; i <= 10; i++) {
-		char *tag;
+		char tag[256];
 
-		tag = g_strdup_printf ("DNS%u", i);
-		value = svGetValueString (ifcfg, tag);
+		numbered_tag (tag, "DNS", i);
+		value = svGetValueStr_cp (ifcfg, tag);
 		if (value) {
 			if (nm_utils_ipaddr_valid (AF_INET, value)) {
 				if (!nm_setting_ip_config_add_dns (s_ip4, value))
@@ -1166,19 +1346,16 @@ make_ip4_setting (shvarFile *ifcfg,
 				/* Ignore IPv6 addresses */
 			} else {
 				PARSE_WARNING ("invalid DNS server address %s", value);
-				g_free (tag);
 				g_free (value);
-				goto done;
+				return NULL;
 			}
 
 			g_free (value);
 		}
-
-		g_free (tag);
 	}
 
 	/* DNS searches */
-	value = svGetValueString (ifcfg, "DOMAIN");
+	value = svGetValueStr_cp (ifcfg, "DOMAIN");
 	if (value) {
 		char **searches = NULL;
 
@@ -1222,7 +1399,7 @@ make_ip4_setting (shvarFile *ifcfg,
 
 				if (!read_one_ip4_route (route_ifcfg, i, &route, error)) {
 					svCloseFile (route_ifcfg);
-					goto done;
+					return NULL;
 				}
 
 				if (!route)
@@ -1236,13 +1413,12 @@ make_ip4_setting (shvarFile *ifcfg,
 		}
 	} else {
 		if (!read_route_file_legacy (route_path, s_ip4, error))
-			goto done;
+			return NULL;
 	}
-	g_free (route_path);
 
 	/* Legacy value NM used for a while but is incorrect (rh #459370) */
 	if (!nm_setting_ip_config_get_num_dns_searches (s_ip4)) {
-		value = svGetValueString (ifcfg, "SEARCH");
+		value = svGetValueStr_cp (ifcfg, "SEARCH");
 		if (value) {
 			char **searches = NULL;
 
@@ -1266,16 +1442,11 @@ make_ip4_setting (shvarFile *ifcfg,
 	g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT,
 	              (gint) (timeout <= 0 ? timeout : timeout * 1000), NULL);
 
-	return NM_SETTING (s_ip4);
-
-done:
-	g_free (route_path);
-	g_object_unref (s_ip4);
-	return NULL;
+	return g_steal_pointer (&s_ip4);
 }
 
 static void
-read_aliases (NMSettingIPConfig *s_ip4, const char *filename)
+read_aliases (NMSettingIPConfig *s_ip4, gboolean read_defroute, const char *filename)
 {
 	GDir *dir;
 	char *dirname, *base;
@@ -1301,6 +1472,7 @@ read_aliases (NMSettingIPConfig *s_ip4, const char *filename)
 		gboolean ok;
 
 		while ((item = g_dir_read_name (dir))) {
+			gs_free char *gateway = NULL;
 			char *full_path, *device;
 			const char *p;
 
@@ -1329,7 +1501,7 @@ read_aliases (NMSettingIPConfig *s_ip4, const char *filename)
 				continue;
 			}
 
-			device = svGetValueString (parsed, "DEVICE");
+			device = svGetValueStr_cp (parsed, "DEVICE");
 			if (!device) {
 				PARSE_WARNING ("alias file '%s' has no DEVICE", full_path);
 				svCloseFile (parsed);
@@ -1347,12 +1519,30 @@ read_aliases (NMSettingIPConfig *s_ip4, const char *filename)
 			}
 
 			addr = NULL;
-			ok = read_full_ip4_address (parsed, -1, base_addr, &addr, NULL, &err);
-			svCloseFile (parsed);
+			ok = read_full_ip4_address (parsed, -1, base_addr, &addr,
+			                            read_defroute ? &gateway : NULL,
+			                            &err);
 			if (ok) {
 				nm_ip_address_set_attribute (addr, "label", g_variant_new_string (device));
 				if (!nm_setting_ip_config_add_address (s_ip4, addr))
 					PARSE_WARNING ("duplicate IP4 address in alias file %s", item);
+				if (nm_streq0 (nm_setting_ip_config_get_method (s_ip4), NM_SETTING_IP4_CONFIG_METHOD_DISABLED))
+					g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
+				if (read_defroute) {
+					int i;
+
+					if (gateway) {
+						g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway, NULL);
+						read_defroute = FALSE;
+					}
+					i = svGetValueBoolean (parsed, "DEFROUTE", -1);
+					if (i != -1) {
+						g_object_set (s_ip4,
+						              NM_SETTING_IP_CONFIG_NEVER_DEFAULT, (gboolean) !i,
+						              NULL);
+						read_defroute = FALSE;
+					}
+				}
 			} else {
 				PARSE_WARNING ("error reading IP4 address from alias file '%s': %s",
 				               full_path, err ? err->message : "no address");
@@ -1360,6 +1550,8 @@ read_aliases (NMSettingIPConfig *s_ip4, const char *filename)
 			}
 			nm_ip_address_unref (addr);
 
+			svCloseFile (parsed);
+
 			g_free (device);
 			g_free (full_path);
 		}
@@ -1416,9 +1608,9 @@ make_ip6_setting (shvarFile *ifcfg,
 		char *default_dev = NULL;
 
 		/* Get the connection ifcfg device name and the global default route device */
-		value = svGetValueString (ifcfg, "DEVICE");
-		ipv6_defaultgw = svGetValueString (network_ifcfg, "IPV6_DEFAULTGW");
-		ipv6_defaultdev = svGetValueString (network_ifcfg, "IPV6_DEFAULTDEV");
+		value = svGetValueStr_cp (ifcfg, "DEVICE");
+		ipv6_defaultgw = svGetValueStr_cp (network_ifcfg, "IPV6_DEFAULTGW");
+		ipv6_defaultdev = svGetValueStr_cp (network_ifcfg, "IPV6_DEFAULTDEV");
 		dns_options = svGetValue (network_ifcfg, "RES_OPTIONS", &dns_options_free);
 
 		if (ipv6_defaultgw) {
@@ -1443,7 +1635,7 @@ make_ip6_setting (shvarFile *ifcfg,
 
 	/* Find out method property */
 	/* Is IPV6 enabled? Set method to "ignored", when not enabled */
-	str_value = svGetValueString (ifcfg, "IPV6INIT");
+	str_value = svGetValueStr_cp (ifcfg, "IPV6INIT");
 	ipv6init = svGetValueBoolean (ifcfg, "IPV6INIT", FALSE);
 	if (!str_value) {
 		network_ifcfg = svOpenFile (network_file, NULL);
@@ -1458,7 +1650,7 @@ make_ip6_setting (shvarFile *ifcfg,
 		method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;  /* IPv6 is disabled */
 	else {
 		ipv6forwarding = svGetValueBoolean (ifcfg, "IPV6FORWARDING", FALSE);
-		str_value = svGetValueString (ifcfg, "IPV6_AUTOCONF");
+		str_value = svGetValueStr_cp (ifcfg, "IPV6_AUTOCONF");
 		dhcp6 = svGetValueBoolean (ifcfg, "DHCPV6C", FALSE);
 
 		if (!g_strcmp0 (str_value, "shared"))
@@ -1470,9 +1662,9 @@ make_ip6_setting (shvarFile *ifcfg,
 		else {
 			/* IPV6_AUTOCONF=no and no IPv6 address -> method 'link-local' */
 			g_free (str_value);
-			str_value = svGetValueString (ifcfg, "IPV6ADDR");
+			str_value = svGetValueStr_cp (ifcfg, "IPV6ADDR");
 			if (!str_value)
-				str_value = svGetValueString (ifcfg, "IPV6ADDR_SECONDARIES");
+				str_value = svGetValueStr_cp (ifcfg, "IPV6ADDR_SECONDARIES");
 
 			if (!str_value)
 				method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
@@ -1482,7 +1674,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	/* TODO - handle other methods */
 
 	/* Read IPv6 Privacy Extensions configuration */
-	str_value = svGetValueString (ifcfg, "IPV6_PRIVACY");
+	str_value = svGetValueStr_cp (ifcfg, "IPV6_PRIVACY");
 	if (str_value) {
 		ip6_privacy = svParseBoolean (str_value, FALSE);
 		if (!ip6_privacy)
@@ -1512,13 +1704,13 @@ make_ip6_setting (shvarFile *ifcfg,
 	if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0)
 		return NM_SETTING (s_ip6);
 
-	value = svGetValueString (ifcfg, "DHCPV6_HOSTNAME");
+	value = svGetValueStr_cp (ifcfg, "DHCPV6_HOSTNAME");
 	/* Use DHCP_HOSTNAME as fallback if it is in FQDN format and ipv6.method is
 	 * auto or dhcp: this is required to support old ifcfg files
 	 */
 	if (!value && (   !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)
 		       || !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP))) {
-		value = svGetValueString (ifcfg, "DHCP_HOSTNAME");
+		value = svGetValueStr_cp (ifcfg, "DHCP_HOSTNAME");
 		if (value && !strchr (value, '.'))
 			g_clear_pointer (&value, g_free);
 	}
@@ -1534,8 +1726,8 @@ make_ip6_setting (shvarFile *ifcfg,
 	 * added to the automatic ones. Note that this is not currently supported by
 	 * the legacy 'network' service (ifup-eth).
 	 */
-	ipv6addr = svGetValueString (ifcfg, "IPV6ADDR");
-	ipv6addr_secondaries = svGetValueString (ifcfg, "IPV6ADDR_SECONDARIES");
+	ipv6addr = svGetValueStr_cp (ifcfg, "IPV6ADDR");
+	ipv6addr_secondaries = svGetValueStr_cp (ifcfg, "IPV6ADDR_SECONDARIES");
 
 	value = g_strjoin (ipv6addr && ipv6addr_secondaries ? " " : NULL,
 	                   ipv6addr ? ipv6addr : "",
@@ -1562,12 +1754,12 @@ make_ip6_setting (shvarFile *ifcfg,
 
 	/* Gateway */
 	if (nm_setting_ip_config_get_num_addresses (s_ip6)) {
-		value = svGetValueString (ifcfg, "IPV6_DEFAULTGW");
+		value = svGetValueStr_cp (ifcfg, "IPV6_DEFAULTGW");
 		if (!value) {
 			/* If no gateway in the ifcfg, try global /etc/sysconfig/network instead */
 			network_ifcfg = svOpenFile (network_file, NULL);
 			if (network_ifcfg) {
-				value = svGetValueString (network_ifcfg, "IPV6_DEFAULTGW");
+				value = svGetValueStr_cp (network_ifcfg, "IPV6_DEFAULTGW");
 				svCloseFile (network_ifcfg);
 			}
 		}
@@ -1588,7 +1780,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	}
 
 	/* IPv6 addressing mode configuration */
-	str_value = svGetValueString (ifcfg, "IPV6_ADDR_GEN_MODE");
+	str_value = svGetValueStr_cp (ifcfg, "IPV6_ADDR_GEN_MODE");
 	if (str_value) {
 		if (nm_utils_enum_from_str (nm_setting_ip6_config_addr_gen_mode_get_type (), str_value,
 		                            (int *) &addr_gen_mode, NULL))
@@ -1604,7 +1796,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	}
 
 	/* IPv6 tokenized interface identifier */
-	str_value = svGetValueString (ifcfg, "IPV6_TOKEN");
+	str_value = svGetValueStr_cp (ifcfg, "IPV6_TOKEN");
 	if (str_value) {
 		g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, str_value, NULL);
 		g_free (str_value);
@@ -1614,13 +1806,13 @@ make_ip6_setting (shvarFile *ifcfg,
 	 * Pick up just IPv6 addresses (IPv4 addresses are taken by make_ip4_setting())
 	 */
 	for (i = 1; i <= 10; i++) {
-		char *tag;
+		char tag[256];
 
-		tag = g_strdup_printf ("DNS%u", i);
-		value = svGetValueString (ifcfg, tag);
+		numbered_tag (tag, "DNS", i);
+		value = svGetValueStr_cp (ifcfg, tag);
 		if (!value) {
-			g_free (tag);
-			break; /* all done */
+			/* all done */
+			break;
 		}
 
 		if (nm_utils_ipaddr_valid (AF_INET6, value)) {
@@ -1630,12 +1822,10 @@ make_ip6_setting (shvarFile *ifcfg,
 			/* Ignore IPv4 addresses */
 		} else {
 			PARSE_WARNING ("invalid DNS server address %s", value);
-			g_free (tag);
 			g_free (value);
 			goto error;
 		}
 
-		g_free (tag);
 		g_free (value);
 	}
 
@@ -1676,9 +1866,9 @@ check_if_bond_slave (shvarFile *ifcfg,
 {
 	char *value;
 
-	value = svGetValueString (ifcfg, "MASTER_UUID");
+	value = svGetValueStr_cp (ifcfg, "MASTER_UUID");
 	if (!value)
-		value = svGetValueString (ifcfg, "MASTER");
+		value = svGetValueStr_cp (ifcfg, "MASTER");
 
 	if (value) {
 		g_object_set (s_con, NM_SETTING_CONNECTION_MASTER, value, NULL);
@@ -1699,9 +1889,9 @@ check_if_team_slave (shvarFile *ifcfg,
 {
 	gs_free char *value = NULL;
 
-	value = svGetValueString (ifcfg, "TEAM_MASTER_UUID");
+	value = svGetValueStr_cp (ifcfg, "TEAM_MASTER_UUID");
 	if (!value)
-		value = svGetValueString (ifcfg, "TEAM_MASTER");
+		value = svGetValueStr_cp (ifcfg, "TEAM_MASTER");
 	if (!value)
 		return FALSE;
 
@@ -1777,7 +1967,7 @@ read_dcb_app (shvarFile *ifcfg,
 
 	/* Priority */
 	tmp = g_strdup_printf ("DCB_APP_%s_PRIORITY", app);
-	val = svGetValueString (ifcfg, tmp);
+	val = svGetValueStr_cp (ifcfg, tmp);
 	if (val) {
 		priority = _nm_utils_ascii_str_to_int64 (val, 0, 0, 7, -1);
 		if (priority < 0) {
@@ -1814,26 +2004,23 @@ read_dcb_bool_array (shvarFile *ifcfg,
                      DcbSetBoolFunc set_func,
                      GError **error)
 {
-	char *val;
-	gboolean success = FALSE;
+	gs_free char *val = NULL;
 	guint i;
 
-	val = svGetValueString (ifcfg, prop);
+	val = svGetValueStr_cp (ifcfg, prop);
 	if (!val)
 		return TRUE;
 
 	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
 		PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
-		success = TRUE;
-		goto out;
+		return TRUE;
 	}
 
-	val = g_strstrip (val);
 	if (strlen (val) != 8) {
 		PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, val);
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "boolean array must be 8 characters");
-		goto out;
+		return FALSE;
 	}
 
 	/* All characters must be either 0 or 1 */
@@ -1842,15 +2029,11 @@ read_dcb_bool_array (shvarFile *ifcfg,
 			PARSE_WARNING ("invalid %s value '%s': not all 0s and 1s", prop, val);
 			g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			                     "invalid boolean digit");
-			goto out;
+			return FALSE;
 		}
 		set_func (s_dcb, i, (val[i] == '1'));
 	}
-	success = TRUE;
-
-out:
-	g_free (val);
-	return success;
+	return TRUE;
 }
 
 typedef void (*DcbSetUintFunc) (NMSettingDcb *, guint, guint);
@@ -1865,26 +2048,23 @@ read_dcb_uint_array (shvarFile *ifcfg,
                      DcbSetUintFunc set_func,
                      GError **error)
 {
-	char *val;
-	gboolean success = FALSE;
+	gs_free char *val = NULL;
 	guint i;
 
-	val = svGetValueString (ifcfg, prop);
+	val = svGetValueStr_cp (ifcfg, prop);
 	if (!val)
 		return TRUE;
 
 	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
 		PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
-		success = TRUE;
-		goto out;
+		return TRUE;
 	}
 
-	val = g_strstrip (val);
 	if (strlen (val) != 8) {
 		PARSE_WARNING ("%s value '%s' must be 8 characters long", prop, val);
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "uint array must be 8 characters");
-		goto out;
+		return FALSE;
 	}
 
 	/* All characters must be either 0 - 7 or (optionally) f */
@@ -1898,14 +2078,11 @@ read_dcb_uint_array (shvarFile *ifcfg,
 			               prop, val, f_allowed ? " or 'f'" : "");
 			g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			                     "invalid uint digit");
-			goto out;
+			return FALSE;
 		}
 	}
-	success = TRUE;
 
-out:
-	g_free (val);
-	return success;
+	return TRUE;
 }
 
 static gboolean
@@ -1918,28 +2095,26 @@ read_dcb_percent_array (shvarFile *ifcfg,
                         DcbSetUintFunc set_func,
                         GError **error)
 {
-	char *val;
-	gboolean success = FALSE;
-	char **split = NULL, **iter;
+	gs_free char *val = NULL;
+	gs_strfreev char **split = NULL;
+	char **iter;
 	guint i, sum = 0;
 
-	val = svGetValueString (ifcfg, prop);
+	val = svGetValueStr_cp (ifcfg, prop);
 	if (!val)
 		return TRUE;
 
 	if (!(flags & NM_SETTING_DCB_FLAG_ENABLE)) {
 		PARSE_WARNING ("ignoring %s; %s is not enabled", prop, desc);
-		success = TRUE;
-		goto out;
+		return TRUE;
 	}
 
-	val = g_strstrip (val);
 	split = g_strsplit_set (val, ",", 0);
 	if (!split || (g_strv_length (split) != 8)) {
 		PARSE_WARNING ("invalid %s percentage list value '%s'", prop, val);
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "percent array must be 8 elements");
-		goto out;
+		return FALSE;
 	}
 
 	for (iter = split, i = 0; iter && *iter; iter++, i++) {
@@ -1950,7 +2125,7 @@ read_dcb_percent_array (shvarFile *ifcfg,
 			PARSE_WARNING ("invalid %s percentage value '%s'", prop, *iter);
 			g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			                     "invalid percent element");
-			goto out;
+			return FALSE;
 		}
 		set_func (s_dcb, i, (guint) tmp);
 		sum += (guint) tmp;
@@ -1960,16 +2135,10 @@ read_dcb_percent_array (shvarFile *ifcfg,
 		PARSE_WARNING ("%s percentages do not equal 100%%", prop);
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "invalid percentage sum");
-		goto out;
+		return FALSE;
 	}
 
-	success = TRUE;
-
-out:
-	if (split)
-		g_strfreev (split);
-	g_free (val);
-	return success;
+	return TRUE;
 }
 
 static gboolean
@@ -2001,7 +2170,7 @@ make_dcb_setting (shvarFile *ifcfg,
 		return FALSE;
 	}
 	if (nm_setting_dcb_get_app_fcoe_flags (s_dcb) & NM_SETTING_DCB_FLAG_ENABLE) {
-		val = svGetValueString (ifcfg, KEY_DCB_APP_FCOE_MODE);
+		val = svGetValueStr_cp (ifcfg, KEY_DCB_APP_FCOE_MODE);
 		if (val) {
 			if (strcmp (val, NM_SETTING_DCB_FCOE_MODE_FABRIC) == 0 ||
 			    strcmp (val, NM_SETTING_DCB_FCOE_MODE_VN2VN) == 0)
@@ -2138,11 +2307,9 @@ add_one_wep_key (shvarFile *ifcfg,
 	g_return_val_if_fail (key_idx <= 3, FALSE);
 	g_return_val_if_fail (s_wsec != NULL, FALSE);
 
-	value = svGetValueString (ifcfg, shvar_key);
-	if (!value || !strlen (value)) {
-		g_free (value);
+	value = svGetValueStr_cp (ifcfg, shvar_key);
+	if (!value)
 		return TRUE;
-	}
 
 	/* Validate keys */
 	if (passphrase) {
@@ -2195,9 +2362,10 @@ add_one_wep_key (shvarFile *ifcfg,
 		nm_setting_wireless_security_set_wep_key (s_wsec, key_idx, key);
 		g_free (key);
 		success = TRUE;
-	} else
+	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Invalid WEP key length.");
+	}
 
 out:
 	g_free (value);
@@ -2245,7 +2413,7 @@ read_secret_flags (shvarFile *ifcfg, const char *flags_key)
 	g_return_val_if_fail (flags_key[0] != '\0', NM_SETTING_SECRET_FLAG_NONE);
 	g_return_val_if_fail (g_str_has_suffix (flags_key, "_FLAGS"), NM_SETTING_SECRET_FLAG_NONE);
 
-	val = svGetValueString (ifcfg, flags_key);
+	val = svGetValueStr_cp (ifcfg, flags_key);
 	if (val) {
 		if (strstr (val, SECRET_FLAG_AGENT))
 			flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;
@@ -2264,7 +2432,7 @@ make_wep_setting (shvarFile *ifcfg,
                   const char *file,
                   GError **error)
 {
-	NMSettingWirelessSecurity *s_wsec;
+	gs_unref_object NMSettingWirelessSecurity *s_wsec = NULL;
 	char *value;
 	shvarFile *keys_ifcfg = NULL;
 	int default_key_idx = 0;
@@ -2274,14 +2442,14 @@ make_wep_setting (shvarFile *ifcfg,
 	s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
 	g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", NULL);
 
-	value = svGetValueString (ifcfg, "DEFAULTKEY");
+	value = svGetValueStr_cp (ifcfg, "DEFAULTKEY");
 	if (value) {
 		default_key_idx = _nm_utils_ascii_str_to_int64 (value, 0, 1, 4, 0);
 		if (default_key_idx == 0) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Invalid default WEP key '%s'", value);
 			g_free (value);
-			goto error;
+			return NULL;
 		}
 		has_default_key = TRUE;
 		default_key_idx--;  /* convert to [0...3] */
@@ -2296,21 +2464,21 @@ make_wep_setting (shvarFile *ifcfg,
 	/* Read keys in the ifcfg file if they are system-owned */
 	if (key_flags == NM_SETTING_SECRET_FLAG_NONE) {
 		if (!read_wep_keys (ifcfg, default_key_idx, s_wsec, error))
-			goto error;
+			return NULL;
 
 		/* Try to get keys from the "shadow" key file */
 		keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
 		if (keys_ifcfg) {
 			if (!read_wep_keys (keys_ifcfg, default_key_idx, s_wsec, error)) {
 				svCloseFile (keys_ifcfg);
-				goto error;
+				return NULL;
 			}
 			svCloseFile (keys_ifcfg);
 			g_assert (error == NULL || *error == NULL);
 		}
 	}
 
-	value = svGetValueString (ifcfg, "SECURITYMODE");
+	value = svGetValueStr_cp (ifcfg, "SECURITYMODE");
 	if (value) {
 		char *lcase;
 
@@ -2326,7 +2494,7 @@ make_wep_setting (shvarFile *ifcfg,
 			             "Invalid WEP authentication algorithm '%s'",
 			             lcase);
 			g_free (lcase);
-			goto error;
+			return NULL;
 		}
 		g_free (lcase);
 	}
@@ -2347,20 +2515,14 @@ make_wep_setting (shvarFile *ifcfg,
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "WEP Shared Key authentication is invalid for "
 			             "unencrypted connections.");
-			goto error;
+			return NULL;
 		}
 
 		/* Unencrypted */
-		g_object_unref (s_wsec);
-		s_wsec = NULL;
+		return NULL;
 	}
 
-	return (NMSetting *) s_wsec;
-
-error:
-	if (s_wsec)
-		g_object_unref (s_wsec);
-	return NULL;
+	return g_steal_pointer (&s_wsec);
 }
 
 static gboolean
@@ -2373,7 +2535,7 @@ fill_wpa_ciphers (shvarFile *ifcfg,
 	char **list = NULL, **iter;
 	int i = 0;
 
-	p = value = svGetValueString (ifcfg, group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE");
+	p = value = svGetValueStr_cp (ifcfg, group ? "CIPHER_GROUP" : "CIPHER_PAIRWISE");
 	if (!value)
 		return TRUE;
 
@@ -2443,13 +2605,13 @@ parse_wpa_psk (shvarFile *ifcfg,
 	/* Try to get keys from the "shadow" key file */
 	keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
 	if (keys_ifcfg) {
-		psk = svGetValueString (keys_ifcfg, "WPA_PSK");
+		psk = svGetValueStr_cp (keys_ifcfg, "WPA_PSK");
 		svCloseFile (keys_ifcfg);
 	}
 
 	/* Fall back to the original ifcfg */
 	if (!psk)
-		psk = svGetValueString (ifcfg, "WPA_PSK");
+		psk = svGetValueStr_cp (ifcfg, "WPA_PSK");
 
 	if (!psk)
 		return NULL;
@@ -2486,7 +2648,7 @@ eap_simple_reader (const char *eap_method,
 	NMSettingSecretFlags flags;
 	char *value;
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_IDENTITY");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY");
 	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IEEE_8021X_IDENTITY for EAP method '%s'.",
@@ -2501,10 +2663,10 @@ eap_simple_reader (const char *eap_method,
 
 	/* Only read the password if it's system-owned */
 	if (flags == NM_SETTING_SECRET_FLAG_NONE) {
-		value = svGetValueString (ifcfg, "IEEE_8021X_PASSWORD");
+		value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PASSWORD");
 		if (!value && keys) {
 			/* Try the lookaside keys file */
-			value = svGetValueString (keys, "IEEE_8021X_PASSWORD");
+			value = svGetValueStr_cp (keys, "IEEE_8021X_PASSWORD");
 		}
 
 		if (!value) {
@@ -2543,6 +2705,19 @@ get_full_file_path (const char *ifcfg_path, const char *file_path)
 	return ret;
 }
 
+static char *
+get_cert_value (const char *ifcfg_path, const char *value,
+                NMSetting8021xCKScheme *out_scheme)
+{
+	if (strncmp (value, "pkcs11:", 7) == 0) {
+		*out_scheme = NM_SETTING_802_1X_CK_SCHEME_PKCS11;
+		return g_strdup (value);
+	}
+
+	*out_scheme = NM_SETTING_802_1X_CK_SCHEME_PATH;
+	return get_full_file_path (ifcfg_path, value);
+}
+
 static gboolean
 eap_tls_reader (const char *eap_method,
                 shvarFile *ifcfg,
@@ -2551,48 +2726,58 @@ eap_tls_reader (const char *eap_method,
                 gboolean phase2,
                 GError **error)
 {
+	gs_free char *ca_cert = NULL;
+	gs_free char *privkey = NULL;
+	gs_free char *privkey_password = NULL;
 	char *value;
-	char *ca_cert = NULL;
-	char *real_path = NULL;
-	char *client_cert = NULL;
-	char *privkey = NULL;
-	char *privkey_password = NULL;
-	gboolean success = FALSE;
+	char *ca_cert_password = NULL;
+	char *client_cert_password = NULL;
 	NMSetting8021xCKFormat privkey_format = NM_SETTING_802_1X_CK_FORMAT_UNKNOWN;
 	const char *ca_cert_key = phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT";
-	const char *pk_pw_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD": "IEEE_8021X_PRIVATE_KEY_PASSWORD";
-	const char *pk_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY";
+	const char *ca_cert_pw_key = phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD";
+	const char *ca_cert_pw_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD;
+	const char *ca_cert_pw_flags_key = phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD_FLAGS" : "IEEE_8021X_CA_CERT_PASSWORD_FLAGS";
+	const char *ca_cert_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS : NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS;
 	const char *cli_cert_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT";
-	const char *pk_pw_flags_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS": "IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS";
+	const char *cli_cert_pw_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" : "IEEE_8021X_CLIENT_CERT_PASSWORD";
+	const char *cli_cert_pw_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD;
+	const char *cli_cert_pw_flags_key = phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD_FLAGS" : "IEEE_8021X_CLIENT_CERT_PASSWORD_FLAGS";
+	const char *cli_cert_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS;
+	const char *pk_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY";
+	const char *pk_pw_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD": "IEEE_8021X_PRIVATE_KEY_PASSWORD";
+	const char *pk_pw_flags_key = phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS" : "IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS";
 	const char *pk_pw_flags_prop = phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS;
 	NMSettingSecretFlags flags;
+	NMSetting8021xCKScheme scheme;
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_IDENTITY");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY");
 	if (value) {
 		g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL);
 		g_free (value);
 	}
 
-	ca_cert = svGetValueString (ifcfg, ca_cert_key);
+	ca_cert = svGetValueStr_cp (ifcfg, ca_cert_key);
 	if (ca_cert) {
-		real_path = get_full_file_path (svFileGetName (ifcfg), ca_cert);
+		gs_free char *real_cert_value = NULL;
+
+		real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme);
 		if (phase2) {
-			if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x,
-			                                           real_path,
-			                                           NM_SETTING_802_1X_CK_SCHEME_PATH,
-			                                           NULL,
-			                                           error))
-				goto done;
+			if (!nm_setting_802_1x_set_phase2_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
+				return FALSE;
 		} else {
-			if (!nm_setting_802_1x_set_ca_cert (s_8021x,
-			                                    real_path,
-			                                    NM_SETTING_802_1X_CK_SCHEME_PATH,
-			                                    NULL,
-			                                    error))
-				goto done;
+			if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
+				return FALSE;
+		}
+
+		if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
+			flags = read_secret_flags (ifcfg, ca_cert_pw_flags_key);
+			g_object_set (s_8021x, ca_cert_pw_flags_prop, flags, NULL);
+
+			if (flags == NM_SETTING_SECRET_FLAG_NONE) {
+				ca_cert_password = svGetValueStr_cp (ifcfg, ca_cert_pw_key);
+				g_object_set (s_8021x, ca_cert_pw_prop, ca_cert_password, NULL);
+			}
 		}
-		g_free (real_path);
-		real_path = NULL;
 	} else {
 		PARSE_WARNING ("missing %s for EAP method '%s'; this is insecure!",
 		               ca_cert_key, eap_method);
@@ -2605,10 +2790,10 @@ eap_tls_reader (const char *eap_method,
 	/* Read the private key password if it's system-owned */
 	if (flags == NM_SETTING_SECRET_FLAG_NONE) {
 		/* Private key password */
-		privkey_password = svGetValueString (ifcfg, pk_pw_key);
+		privkey_password = svGetValueStr_cp (ifcfg, pk_pw_key);
 		if (!privkey_password && keys) {
 			/* Try the lookaside keys file */
-			privkey_password = svGetValueString (keys, pk_pw_key);
+			privkey_password = svGetValueStr_cp (keys, pk_pw_key);
 		}
 
 		if (!privkey_password) {
@@ -2616,40 +2801,42 @@ eap_tls_reader (const char *eap_method,
 			             "Missing %s for EAP method '%s'.",
 			             pk_pw_key,
 			             eap_method);
-			goto done;
+			return FALSE;
 		}
 	}
 
 	/* The private key itself */
-	privkey = svGetValueString (ifcfg, pk_key);
+	privkey = svGetValueStr_cp (ifcfg, pk_key);
 	if (!privkey) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing %s for EAP method '%s'.",
 		             pk_key,
 		             eap_method);
-		goto done;
+		return FALSE;
 	}
 
-	real_path = get_full_file_path (svFileGetName (ifcfg), privkey);
-	if (phase2) {
-		if (!nm_setting_802_1x_set_phase2_private_key (s_8021x,
-		                                               real_path,
-		                                               privkey_password,
-		                                               NM_SETTING_802_1X_CK_SCHEME_PATH,
-		                                               &privkey_format,
-		                                               error))
-			goto done;
-	} else {
-		if (!nm_setting_802_1x_set_private_key (s_8021x,
-		                                        real_path,
-		                                        privkey_password,
-		                                        NM_SETTING_802_1X_CK_SCHEME_PATH,
-		                                        &privkey_format,
-		                                        error))
-			goto done;
+	{
+		gs_free char *real_cert_value = NULL;
+
+		real_cert_value = get_cert_value (svFileGetName (ifcfg), privkey, &scheme);
+		if (phase2) {
+			if (!nm_setting_802_1x_set_phase2_private_key (s_8021x,
+			                                               real_cert_value,
+			                                               privkey_password,
+			                                               scheme,
+			                                               &privkey_format,
+			                                               error))
+				return FALSE;
+		} else {
+			if (!nm_setting_802_1x_set_private_key (s_8021x,
+			                                        real_cert_value,
+			                                        privkey_password,
+			                                        scheme,
+			                                        &privkey_format,
+			                                        error))
+				return FALSE;
+		}
 	}
-	g_free (real_path);
-	real_path = NULL;
 
 	/* Only set the client certificate if the private key is not PKCS#12 format,
 	 * as NM (due to supplicant restrictions) requires.  If the key was PKCS#12,
@@ -2658,44 +2845,39 @@ eap_tls_reader (const char *eap_method,
 	 */
 	if (   privkey_format == NM_SETTING_802_1X_CK_FORMAT_RAW_KEY
 	    || privkey_format == NM_SETTING_802_1X_CK_FORMAT_X509) {
-		client_cert = svGetValueString (ifcfg, cli_cert_key);
+		gs_free char *real_cert_value = NULL;
+		gs_free char *client_cert = NULL;
+
+		client_cert = svGetValueStr_cp (ifcfg, cli_cert_key);
 		if (!client_cert) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Missing %s for EAP method '%s'.",
 			             cli_cert_key,
 			             eap_method);
-			goto done;
+			return FALSE;
 		}
 
-		real_path = get_full_file_path (svFileGetName (ifcfg), client_cert);
+		real_cert_value = get_cert_value (svFileGetName (ifcfg), client_cert, &scheme);
 		if (phase2) {
-			if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x,
-			                                               real_path,
-			                                               NM_SETTING_802_1X_CK_SCHEME_PATH,
-			                                               NULL,
-			                                               error))
-				goto done;
+			if (!nm_setting_802_1x_set_phase2_client_cert (s_8021x, real_cert_value, scheme, NULL, error))
+				return FALSE;
 		} else {
-			if (!nm_setting_802_1x_set_client_cert (s_8021x,
-			                                        real_path,
-			                                        NM_SETTING_802_1X_CK_SCHEME_PATH,
-			                                        NULL,
-			                                        error))
-				goto done;
+			if (!nm_setting_802_1x_set_client_cert (s_8021x, real_cert_value, scheme, NULL, error))
+				return FALSE;
 		}
-		g_free (real_path);
-		real_path = NULL;
-	}
 
-	success = TRUE;
+		if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
+			flags = read_secret_flags (ifcfg, cli_cert_pw_flags_key);
+			g_object_set (s_8021x, cli_cert_pw_flags_prop, flags, NULL);
 
-done:
-	g_free (real_path);
-	g_free (ca_cert);
-	g_free (client_cert);
-	g_free (privkey);
-	g_free (privkey_password);
-	return success;
+			if (flags == NM_SETTING_SECRET_FLAG_NONE) {
+				client_cert_password = svGetValueStr_cp (ifcfg, cli_cert_pw_key);
+				g_object_set (s_8021x, cli_cert_pw_prop, client_cert_password, NULL);
+			}
+		}
+	}
+
+	return TRUE;
 }
 
 static gboolean
@@ -2708,28 +2890,25 @@ eap_peap_reader (const char *eap_method,
 {
 	char *anon_ident = NULL;
 	char *ca_cert = NULL;
-	char *real_cert_path = NULL;
+	char *real_cert_value = NULL;
 	char *inner_auth = NULL;
 	char *peapver = NULL;
 	char *lower;
 	char **list = NULL, **iter;
 	gboolean success = FALSE;
+	NMSetting8021xCKScheme scheme;
 
-	ca_cert = svGetValueString (ifcfg, "IEEE_8021X_CA_CERT");
+	ca_cert = svGetValueStr_cp (ifcfg, "IEEE_8021X_CA_CERT");
 	if (ca_cert) {
-		real_cert_path = get_full_file_path (svFileGetName (ifcfg), ca_cert);
-		if (!nm_setting_802_1x_set_ca_cert (s_8021x,
-		                                    real_cert_path,
-		                                    NM_SETTING_802_1X_CK_SCHEME_PATH,
-		                                    NULL,
-		                                    error))
+		real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme);
+		if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
 			goto done;
 	} else {
 		PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
 		               eap_method);
 	}
 
-	peapver = svGetValueString (ifcfg, "IEEE_8021X_PEAP_VERSION");
+	peapver = svGetValueStr_cp (ifcfg, "IEEE_8021X_PEAP_VERSION");
 	if (peapver) {
 		if (!strcmp (peapver, "0"))
 			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPVER, "0", NULL);
@@ -2746,11 +2925,11 @@ eap_peap_reader (const char *eap_method,
 	if (svGetValueBoolean (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", FALSE))
 		g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_PEAPLABEL, "1", NULL);
 
-	anon_ident = svGetValueString (ifcfg, "IEEE_8021X_ANON_IDENTITY");
-	if (anon_ident && strlen (anon_ident))
+	anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY");
+	if (anon_ident)
 		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
 
-	inner_auth = svGetValueString (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
+	inner_auth = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
 	if (!inner_auth) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IEEE_8021X_INNER_AUTH_METHODS.");
@@ -2797,7 +2976,7 @@ done:
 		g_strfreev (list);
 	g_free (inner_auth);
 	g_free (peapver);
-	g_free (real_cert_path);
+	g_free (real_cert_value);
 	g_free (ca_cert);
 	g_free (anon_ident);
 	return success;
@@ -2814,30 +2993,27 @@ eap_ttls_reader (const char *eap_method,
 	gboolean success = FALSE;
 	char *anon_ident = NULL;
 	char *ca_cert = NULL;
-	char *real_cert_path = NULL;
+	char *real_cert_value = NULL;
 	char *inner_auth = NULL;
 	char *tmp;
 	char **list = NULL, **iter;
+	NMSetting8021xCKScheme scheme;
 
-	ca_cert = svGetValueString (ifcfg, "IEEE_8021X_CA_CERT");
+	ca_cert = svGetValueStr_cp (ifcfg, "IEEE_8021X_CA_CERT");
 	if (ca_cert) {
-		real_cert_path = get_full_file_path (svFileGetName (ifcfg), ca_cert);
-		if (!nm_setting_802_1x_set_ca_cert (s_8021x,
-		                                    real_cert_path,
-		                                    NM_SETTING_802_1X_CK_SCHEME_PATH,
-		                                    NULL,
-		                                    error))
+		real_cert_value = get_cert_value (svFileGetName (ifcfg), ca_cert, &scheme);
+		if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
 			goto done;
 	} else {
 		PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
 		               eap_method);
 	}
 
-	anon_ident = svGetValueString (ifcfg, "IEEE_8021X_ANON_IDENTITY");
-	if (anon_ident && strlen (anon_ident))
+	anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY");
+	if (anon_ident)
 		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
 
-	tmp = svGetValueString (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
+	tmp = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
 	if (!tmp) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IEEE_8021X_INNER_AUTH_METHODS.");
@@ -2885,7 +3061,7 @@ done:
 	if (list)
 		g_strfreev (list);
 	g_free (inner_auth);
-	g_free (real_cert_path);
+	g_free (real_cert_value);
 	g_free (ca_cert);
 	g_free (anon_ident);
 	return success;
@@ -2906,17 +3082,17 @@ eap_fast_reader (const char *eap_method,
 	char *fast_provisioning = NULL;
 	char *lower;
 	char **list = NULL, **iter;
-	const char* pac_prov_str;
+	const char *pac_prov_str;
 	gboolean allow_unauth = FALSE, allow_auth = FALSE;
 	gboolean success = FALSE;
 
-	pac_file = svGetValueString (ifcfg, "IEEE_8021X_PAC_FILE");
+	pac_file = svGetValueStr_cp (ifcfg, "IEEE_8021X_PAC_FILE");
 	if (pac_file) {
 		real_pac_path = get_full_file_path (svFileGetName (ifcfg), pac_file);
 		g_object_set (s_8021x, NM_SETTING_802_1X_PAC_FILE, real_pac_path, NULL);
 	}
 
-	fast_provisioning = svGetValueString (ifcfg, "IEEE_8021X_FAST_PROVISIONING");
+	fast_provisioning = svGetValueStr_cp (ifcfg, "IEEE_8021X_FAST_PROVISIONING");
 	if (fast_provisioning) {
 		list = g_strsplit_set (fast_provisioning, " \t", 0);
 		for (iter = list; iter && *iter; iter++) {
@@ -2944,11 +3120,11 @@ eap_fast_reader (const char *eap_method,
 		goto done;
 	}
 
-	anon_ident = svGetValueString (ifcfg, "IEEE_8021X_ANON_IDENTITY");
-	if (anon_ident && strlen (anon_ident))
+	anon_ident = svGetValueStr_cp (ifcfg, "IEEE_8021X_ANON_IDENTITY");
+	if (anon_ident)
 		g_object_set (s_8021x, NM_SETTING_802_1X_ANONYMOUS_IDENTITY, anon_ident, NULL);
 
-	inner_auth = svGetValueString (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
+	inner_auth = svGetValueStr_cp (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS");
 	if (!inner_auth) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IEEE_8021X_INNER_AUTH_METHODS.");
@@ -2998,12 +3174,12 @@ done:
 
 typedef struct {
 	const char *method;
-	gboolean (*reader)(const char *eap_method,
-	                   shvarFile *ifcfg,
-	                   shvarFile *keys,
-	                   NMSetting8021x *s_8021x,
-	                   gboolean phase2,
-	                   GError **error);
+	gboolean (*reader) (const char *eap_method,
+	                    shvarFile *ifcfg,
+	                    shvarFile *keys,
+	                    NMSetting8021x *s_8021x,
+	                    gboolean phase2,
+	                    GError **error);
 	gboolean wifi_phase2_only;
 } EAPReader;
 
@@ -3035,7 +3211,7 @@ read_8021x_list_value (shvarFile *ifcfg,
 	g_return_if_fail (ifcfg_var_name != NULL);
 	g_return_if_fail (prop_name != NULL);
 
-	value = svGetValueString (ifcfg, ifcfg_var_name);
+	value = svGetValueStr_cp (ifcfg, ifcfg_var_name);
 	if (!value)
 		return;
 
@@ -3053,12 +3229,13 @@ fill_8021x (shvarFile *ifcfg,
             gboolean wifi,
             GError **error)
 {
+	nm_auto_shvar_file_close shvarFile *keys = NULL;
 	NMSetting8021x *s_8021x;
-	shvarFile *keys = NULL;
 	char *value;
 	char **list = NULL, **iter;
+	gint64 timeout;
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_EAP_METHODS");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_EAP_METHODS");
 	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing IEEE_8021X_EAP_METHODS for key management '%s'",
@@ -3104,7 +3281,7 @@ fill_8021x (shvarFile *ifcfg,
 			found = TRUE;
 			break;
 
-		next:
+next:
 			eap++;
 		}
 
@@ -3119,37 +3296,54 @@ fill_8021x (shvarFile *ifcfg,
 		goto error;
 	}
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_SUBJECT_MATCH");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_SUBJECT_MATCH");
 	g_object_set (s_8021x, NM_SETTING_802_1X_SUBJECT_MATCH, value, NULL);
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH");
 	g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH, value, NULL);
 	g_free (value);
 
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS");
+	if (value) {
+		NMSetting8021xAuthFlags flags;
+		char *token;
+
+		if (nm_utils_enum_from_str (nm_setting_802_1x_auth_flags_get_type (), value,
+		                            (int *) &flags, &token)) {
+			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE1_AUTH_FLAGS, flags, NULL);
+		} else {
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Invalid IEEE_8021X_PHASE1_AUTH_FLAGS flag '%s'", token);
+			g_free (token);
+			g_free (value);
+			goto error;
+		}
+		g_free (value);
+	}
+
 	read_8021x_list_value (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES",
 	                       s_8021x, NM_SETTING_802_1X_ALTSUBJECT_MATCHES);
 	read_8021x_list_value (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES",
 	                       s_8021x, NM_SETTING_802_1X_PHASE2_ALTSUBJECT_MATCHES);
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH");
 	g_object_set (s_8021x, NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH, value, NULL);
 	g_free (value);
-	value = svGetValueString (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH");
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH");
 	g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, value, NULL);
 	g_free (value);
 
+	timeout = svGetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", 10, 0, G_MAXINT32, 0);
+	g_object_set (s_8021x, NM_SETTING_802_1X_AUTH_TIMEOUT, (gint32) timeout, NULL);
+
 	if (list)
 		g_strfreev (list);
-	if (keys)
-		svCloseFile (keys);
 	return s_8021x;
 
 error:
 	if (list)
 		g_strfreev (list);
-	if (keys)
-		svCloseFile (keys);
 	g_object_unref (s_8021x);
 	return NULL;
 }
@@ -3168,7 +3362,7 @@ make_wpa_setting (shvarFile *ifcfg,
 
 	wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
 
-	value = svGetValueString (ifcfg, "KEY_MGMT");
+	value = svGetValueStr_cp (ifcfg, "KEY_MGMT");
 	wpa_psk = !g_strcmp0 (value, "WPA-PSK");
 	wpa_eap = !g_strcmp0 (value, "WPA-EAP");
 	ieee8021x = !g_strcmp0 (value, "IEEE8021X");
@@ -3188,8 +3382,8 @@ make_wpa_setting (shvarFile *ifcfg,
 	} else {
 		char *allow_wpa, *allow_rsn;
 
-		allow_wpa = svGetValueString (ifcfg, "WPA_ALLOW_WPA");
-		allow_rsn = svGetValueString (ifcfg, "WPA_ALLOW_WPA2");
+		allow_wpa = svGetValueStr_cp (ifcfg, "WPA_ALLOW_WPA");
+		allow_rsn = svGetValueStr_cp (ifcfg, "WPA_ALLOW_WPA2");
 
 		if (allow_wpa && svGetValueBoolean (ifcfg, "WPA_ALLOW_WPA", TRUE))
 			nm_setting_wireless_security_add_proto (wsec, "wpa");
@@ -3243,7 +3437,7 @@ make_wpa_setting (shvarFile *ifcfg,
 
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "SECURITYMODE");
+	value = svGetValueStr_cp (ifcfg, "SECURITYMODE");
 	if (NM_IN_STRSET (value, NULL, "open"))
 		g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, value, NULL);
 
@@ -3269,12 +3463,12 @@ make_leap_setting (shvarFile *ifcfg,
 
 	wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
 
-	value = svGetValueString (ifcfg, "KEY_MGMT");
+	value = svGetValueStr_cp (ifcfg, "KEY_MGMT");
 	if (!value || strcmp (value, "IEEE8021X"))
 		goto error; /* Not LEAP */
 
 	g_free (value);
-	value = svGetValueString (ifcfg, "SECURITYMODE");
+	value = svGetValueStr_cp (ifcfg, "SECURITYMODE");
 	if (!value || strcasecmp (value, "leap"))
 		goto error; /* Not LEAP */
 
@@ -3285,12 +3479,12 @@ make_leap_setting (shvarFile *ifcfg,
 
 	/* Read LEAP password if it's system-owned */
 	if (flags == NM_SETTING_SECRET_FLAG_NONE) {
-		value = svGetValueString (ifcfg, "IEEE_8021X_PASSWORD");
+		value = svGetValueStr_cp (ifcfg, "IEEE_8021X_PASSWORD");
 		if (!value) {
 			/* Try to get keys from the "shadow" key file */
 			keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
 			if (keys_ifcfg) {
-				value = svGetValueString (keys_ifcfg, "IEEE_8021X_PASSWORD");
+				value = svGetValueStr_cp (keys_ifcfg, "IEEE_8021X_PASSWORD");
 				svCloseFile (keys_ifcfg);
 			}
 		}
@@ -3299,8 +3493,8 @@ make_leap_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "IEEE_8021X_IDENTITY");
-	if (!value || !strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "IEEE_8021X_IDENTITY");
+	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing LEAP identity");
 		goto error;
@@ -3392,25 +3586,25 @@ make_wireless_setting (shvarFile *ifcfg,
 
 	s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
 
-	value = svGetValueString (ifcfg, "HWADDR");
+	value = svGetValueStr_cp (ifcfg, "HWADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "MACADDR");
+	value = svGetValueStr_cp (ifcfg, "MACADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "GENERATE_MAC_ADDRESS_MASK");
+	value = svGetValueStr_cp (ifcfg, "GENERATE_MAC_ADDRESS_MASK");
 	g_object_set (s_wireless, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK, value, NULL);
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "HWADDR_BLACKLIST");
+	value = svGetValueStr_cp (ifcfg, "HWADDR_BLACKLIST");
 	if (value) {
 		char **strv;
 
@@ -3420,7 +3614,7 @@ make_wireless_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "ESSID");
+	value = svGetValueStr_cp (ifcfg, "ESSID");
 	if (value) {
 		gs_unref_bytes GBytes *bytes = NULL;
 		gsize ssid_len = 0;
@@ -3449,7 +3643,7 @@ make_wireless_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "MODE");
+	value = svGetValueStr_cp (ifcfg, "MODE");
 	if (value) {
 		char *lcase;
 		const char *mode = NULL;
@@ -3475,14 +3669,14 @@ make_wireless_setting (shvarFile *ifcfg,
 		g_object_set (s_wireless, NM_SETTING_WIRELESS_MODE, mode, NULL);
 	}
 
-	value = svGetValueString (ifcfg, "BSSID");
+	value = svGetValueStr_cp (ifcfg, "BSSID");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_wireless, NM_SETTING_WIRELESS_BSSID, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "CHANNEL");
+	value = svGetValueStr_cp (ifcfg, "CHANNEL");
 	if (value) {
 		errno = 0;
 		chan = _nm_utils_ascii_str_to_int64 (value, 10, 1, 196, 0);
@@ -3496,7 +3690,7 @@ make_wireless_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "BAND");
+	value = svGetValueStr_cp (ifcfg, "BAND");
 	if (value) {
 		if (!strcmp (value, "a")) {
 			if (chan && chan <= 14) {
@@ -3527,7 +3721,7 @@ make_wireless_setting (shvarFile *ifcfg,
 			g_object_set (s_wireless, NM_SETTING_WIRELESS_BAND, "bg", NULL);
 	}
 
-	value = svGetValueString (ifcfg, "MTU");
+	value = svGetValueStr_cp (ifcfg, "MTU");
 	if (value) {
 		int mtu;
 
@@ -3637,7 +3831,7 @@ wireless_connection_from_ifcfg (const char *file,
 		printable_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
 		                                        g_bytes_get_size (ssid));
 	} else
-		printable_ssid = g_strdup_printf ("unmanaged");
+		printable_ssid = g_strdup ("unmanaged");
 
 	mode = nm_setting_wireless_get_mode (NM_SETTING_WIRELESS (wireless_setting));
 	if (mode && !strcmp (mode, "adhoc"))
@@ -3856,7 +4050,7 @@ parse_ethtool_options (shvarFile *ifcfg, NMSettingWired *s_wired, const char *va
 	}
 
 	/* ETHTOOL_WAKE_ON_LAN = ignore overrides WoL settings in ETHTOOL_OPTS */
-	wol_value = svGetValueString (ifcfg, "ETHTOOL_WAKE_ON_LAN");
+	wol_value = svGetValueStr_cp (ifcfg, "ETHTOOL_WAKE_ON_LAN");
 	if (wol_value) {
 		if (strcmp (wol_value, "ignore") == 0)
 			wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE;
@@ -3891,7 +4085,7 @@ make_wired_setting (shvarFile *ifcfg,
 
 	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
 
-	value = svGetValueString (ifcfg, "MTU");
+	value = svGetValueStr_cp (ifcfg, "MTU");
 	if (value) {
 		int mtu;
 
@@ -3903,14 +4097,14 @@ make_wired_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "HWADDR");
+	value = svGetValueStr_cp (ifcfg, "HWADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "SUBCHANNELS");
+	value = svGetValueStr_cp (ifcfg, "SUBCHANNELS");
 	if (value) {
 		const char *p = value;
 		gboolean success = TRUE;
@@ -3941,28 +4135,29 @@ make_wired_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "PORTNAME");
-	if (value && strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "PORTNAME");
+	if (value) {
 		nm_setting_wired_add_s390_option (s_wired, "portname", value);
+		g_free (value);
 	}
-	g_free (value);
 
-	value = svGetValueString (ifcfg, "CTCPROT");
-	if (value && strlen (value))
+	value = svGetValueStr_cp (ifcfg, "CTCPROT");
+	if (value) {
 		nm_setting_wired_add_s390_option (s_wired, "ctcprot", value);
-	g_free (value);
+		g_free (value);
+	}
 
-	nettype = svGetValueString (ifcfg, "NETTYPE");
-	if (nettype && strlen (nettype)) {
+	nettype = svGetValueStr_cp (ifcfg, "NETTYPE");
+	if (nettype) {
 		if (!strcmp (nettype, "qeth") || !strcmp (nettype, "lcs") || !strcmp (nettype, "ctc"))
 			g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, nettype, NULL);
 		else
 			PARSE_WARNING ("unknown s390 NETTYPE '%s'", nettype);
+		g_free (nettype);
 	}
-	g_free (nettype);
 
-	value = svGetValueString (ifcfg, "OPTIONS");
-	if (value && strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "OPTIONS");
+	if (value) {
 		char **options, **iter;
 
 		iter = options = g_strsplit_set (value, " ", 0);
@@ -3979,21 +4174,21 @@ make_wired_setting (shvarFile *ifcfg,
 			iter++;
 		}
 		g_strfreev (options);
+		g_free (value);
 	}
-	g_free (value);
 
-	value = svGetValueString (ifcfg, "MACADDR");
+	value = svGetValueStr_cp (ifcfg, "MACADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "GENERATE_MAC_ADDRESS_MASK");
+	value = svGetValueStr_cp (ifcfg, "GENERATE_MAC_ADDRESS_MASK");
 	g_object_set (s_wired, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, value, NULL);
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "HWADDR_BLACKLIST");
+	value = svGetValueStr_cp (ifcfg, "HWADDR_BLACKLIST");
 	if (value) {
 		char **strv;
 
@@ -4003,7 +4198,7 @@ make_wired_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "KEY_MGMT");
+	value = svGetValueStr_cp (ifcfg, "KEY_MGMT");
 	if (value) {
 		if (!strcmp (value, "IEEE8021X")) {
 			*s_8021x = fill_8021x (ifcfg, file, value, FALSE, error);
@@ -4078,19 +4273,19 @@ parse_infiniband_p_key (shvarFile *ifcfg,
 	int id;
 	gboolean ret = FALSE;
 
-	device = svGetValueString (ifcfg, "DEVICE");
+	device = svGetValueStr_cp (ifcfg, "DEVICE");
 	if (!device) {
 		PARSE_WARNING ("InfiniBand connection specified PKEY but not DEVICE");
 		goto done;
 	}
 
-	physdev = svGetValueString (ifcfg, "PHYSDEV");
+	physdev = svGetValueStr_cp (ifcfg, "PHYSDEV");
 	if (!physdev) {
 		PARSE_WARNING ("InfiniBand connection specified PKEY but not PHYSDEV");
 		goto done;
 	}
 
-	pkey_id = svGetValueString (ifcfg, "PKEY_ID");
+	pkey_id = svGetValueStr_cp (ifcfg, "PKEY_ID");
 	if (!pkey_id) {
 		PARSE_WARNING ("InfiniBand connection specified PKEY but not PKEY_ID");
 		goto done;
@@ -4138,7 +4333,7 @@ make_infiniband_setting (shvarFile *ifcfg,
 
 	s_infiniband = NM_SETTING_INFINIBAND (nm_setting_infiniband_new ());
 
-	value = svGetValueString (ifcfg, "MTU");
+	value = svGetValueStr_cp (ifcfg, "MTU");
 	if (value) {
 		int mtu;
 
@@ -4150,7 +4345,7 @@ make_infiniband_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "HWADDR");
+	value = svGetValueStr_cp (ifcfg, "HWADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, value, NULL);
@@ -4248,17 +4443,17 @@ make_bond_setting (shvarFile *ifcfg,
 	NMSettingBond *s_bond;
 	char *value;
 
-	s_bond = NM_SETTING_BOND (nm_setting_bond_new ());
-
-	value = svGetValueString (ifcfg, "DEVICE");
-	if (!value || !strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "DEVICE");
+	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "mandatory DEVICE keyword missing");
-		goto error;
+		return NULL;
 	}
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "BONDING_OPTS");
+	s_bond = NM_SETTING_BOND (nm_setting_bond_new ());
+
+	value = svGetValueStr_cp (ifcfg, "BONDING_OPTS");
 	if (value) {
 		char **items, **iter;
 
@@ -4271,7 +4466,7 @@ make_bond_setting (shvarFile *ifcfg,
 				if (keys && *keys) {
 					key = *keys;
 					val = *(keys + 1);
-					if (val && strlen(key) && strlen(val))
+					if (val && key[0] && val[0])
 						handle_bond_option (s_bond, key, val);
 				}
 
@@ -4283,10 +4478,6 @@ make_bond_setting (shvarFile *ifcfg,
 	}
 
 	return (NMSetting *) s_bond;
-
-error:
-	g_object_unref (s_bond);
-	return NULL;
 }
 
 static NMConnection *
@@ -4342,7 +4533,7 @@ read_team_config (shvarFile *ifcfg, const char *key, GError **error)
 	gs_free char *value = NULL;
 	size_t l;
 
-	value = svGetValueString (ifcfg, key);
+	value = svGetValueStr_cp (ifcfg, key);
 	if (!value)
 		return NULL;
 
@@ -4370,29 +4561,26 @@ make_team_setting (shvarFile *ifcfg,
 	char *value;
 	GError *local_err = NULL;
 
-	s_team = NM_SETTING_TEAM (nm_setting_team_new ());
-
-	value = svGetValueString (ifcfg, "DEVICE");
-	if (!value || !strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "DEVICE");
+	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "mandatory DEVICE keyword missing");
-		goto error;
+		return NULL;
 	}
 	g_free (value);
 
 	value = read_team_config (ifcfg, "TEAM_CONFIG", &local_err);
 	if (local_err) {
 		g_propagate_error (error, local_err);
-		goto error;
+		return NULL;
 	}
+
+	s_team = NM_SETTING_TEAM (nm_setting_team_new ());
+
 	g_object_set (s_team, NM_SETTING_TEAM_CONFIG, value, NULL);
 	g_free (value);
 
 	return (NMSetting *) s_team;
-
-error:
-	g_object_unref (s_team);
-	return NULL;
 }
 
 static NMConnection *
@@ -4454,23 +4642,23 @@ handle_bridge_option (NMSetting *setting,
 	guint32 u = 0;
 
 	if (!strcmp (key, "priority")) {
-		if (stp == FALSE) {
+		if (stp == FALSE)
 			PARSE_WARNING ("'priority' invalid when STP is disabled");
-		} else if (get_uint (value, &u))
+		else if (get_uint (value, &u))
 			g_object_set (setting, NM_SETTING_BRIDGE_PRIORITY, u, NULL);
 		else
 			PARSE_WARNING ("invalid priority value '%s'", value);
 	} else if (!strcmp (key, "hello_time")) {
-		if (stp == FALSE) {
+		if (stp == FALSE)
 			PARSE_WARNING ("'hello_time' invalid when STP is disabled");
-		} else if (get_uint (value, &u))
+		else if (get_uint (value, &u))
 			g_object_set (setting, NM_SETTING_BRIDGE_HELLO_TIME, u, NULL);
 		else
 			PARSE_WARNING ("invalid hello_time value '%s'", value);
 	} else if (!strcmp (key, "max_age")) {
-		if (stp == FALSE) {
+		if (stp == FALSE)
 			PARSE_WARNING ("'max_age' invalid when STP is disabled");
-		} else if (get_uint (value, &u))
+		else if (get_uint (value, &u))
 			g_object_set (setting, NM_SETTING_BRIDGE_MAX_AGE, u, NULL);
 		else
 			PARSE_WARNING ("invalid max_age value '%s'", value);
@@ -4506,7 +4694,7 @@ handle_bridging_opts (NMSetting *setting,
 			if (keys && *keys) {
 				key = *keys;
 				val = *(keys + 1);
-				if (val && strlen(key) && strlen(val))
+				if (val && strlen (key) && strlen (val))
 					func (setting, stp, key, val);
 			}
 
@@ -4527,24 +4715,24 @@ make_bridge_setting (shvarFile *ifcfg,
 	gboolean stp = FALSE;
 	gboolean stp_set = FALSE;
 
-	s_bridge = NM_SETTING_BRIDGE (nm_setting_bridge_new ());
-
-	value = svGetValueString (ifcfg, "DEVICE");
-	if (!value || !strlen (value)) {
+	value = svGetValueStr_cp (ifcfg, "DEVICE");
+	if (!value) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "mandatory DEVICE keyword missing");
-		goto error;
+		return NULL;
 	}
 	g_free (value);
 
-	value = svGetValueString (ifcfg, "MACADDR");
+	s_bridge = NM_SETTING_BRIDGE (nm_setting_bridge_new ());
+
+	value = svGetValueStr_cp (ifcfg, "MACADDR");
 	if (value) {
 		value = g_strstrip (value);
 		g_object_set (s_bridge, NM_SETTING_BRIDGE_MAC_ADDRESS, value, NULL);
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "STP");
+	value = svGetValueStr_cp (ifcfg, "STP");
 	if (value) {
 		if (!strcasecmp (value, "on") || !strcasecmp (value, "yes")) {
 			g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, TRUE, NULL);
@@ -4563,7 +4751,7 @@ make_bridge_setting (shvarFile *ifcfg,
 		g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL);
 	}
 
-	value = svGetValueString (ifcfg, "DELAY");
+	value = svGetValueStr_cp (ifcfg, "DELAY");
 	if (value) {
 		if (stp) {
 			if (get_uint (value, &u))
@@ -4575,17 +4763,13 @@ make_bridge_setting (shvarFile *ifcfg,
 		g_free (value);
 	}
 
-	value = svGetValueString (ifcfg, "BRIDGING_OPTS");
+	value = svGetValueStr_cp (ifcfg, "BRIDGING_OPTS");
 	if (value) {
 		handle_bridging_opts (NM_SETTING (s_bridge), stp, value, handle_bridge_option);
 		g_free (value);
 	}
 
 	return (NMSetting *) s_bridge;
-
-error:
-	g_object_unref (s_bridge);
-	return NULL;
 }
 
 static NMConnection *
@@ -4616,7 +4800,7 @@ bridge_connection_from_ifcfg (const char *file,
 		g_object_unref (connection);
 		return NULL;
 	}
-	nm_connection_add_setting (connection, bridge_setting);	
+	nm_connection_add_setting (connection, bridge_setting);
 
 	return connection;
 }
@@ -4658,14 +4842,14 @@ make_bridge_port_setting (shvarFile *ifcfg)
 
 	g_return_val_if_fail (ifcfg != NULL, FALSE);
 
-	value = svGetValueString (ifcfg, "BRIDGE_UUID");
+	value = svGetValueStr_cp (ifcfg, "BRIDGE_UUID");
 	if (!value)
-		value = svGetValueString (ifcfg, "BRIDGE");
+		value = svGetValueStr_cp (ifcfg, "BRIDGE");
 	if (value) {
 		g_free (value);
 
 		s_port = nm_setting_bridge_port_new ();
-		value = svGetValueString (ifcfg, "BRIDGING_OPTS");
+		value = svGetValueStr_cp (ifcfg, "BRIDGING_OPTS");
 		if (value)
 			handle_bridging_opts (s_port, FALSE, value, handle_bridge_port_option);
 		g_free (value);
@@ -4742,7 +4926,7 @@ parse_prio_map_list (NMSettingVlan *s_vlan,
 	char *value;
 	gchar **list = NULL, **iter;
 
-	value = svGetValueString (ifcfg, key);
+	value = svGetValueStr_cp (ifcfg, key);
 	if (!value)
 		return;
 
@@ -4764,16 +4948,16 @@ make_vlan_setting (shvarFile *ifcfg,
                    const char *file,
                    GError **error)
 {
-	NMSettingVlan *s_vlan = NULL;
+	gs_unref_object NMSettingVlan *s_vlan = NULL;
+	gs_free char *parent = NULL;
+	gs_free char *iface_name = NULL;
 	char *value = NULL;
-	char *iface_name = NULL;
-	char *parent = NULL;
 	const char *p = NULL;
 	int vlan_id = -1;
 	guint32 vlan_flags = 0;
 	gint gvrp, reorder_hdr;
 
-	value = svGetValueString (ifcfg, "VLAN_ID");
+	value = svGetValueStr_cp (ifcfg, "VLAN_ID");
 	if (value) {
 		vlan_id = _nm_utils_ascii_str_to_int64 (value, 10, 0, 4095, -1);
 		if (vlan_id == -1) {
@@ -4786,7 +4970,7 @@ make_vlan_setting (shvarFile *ifcfg,
 	}
 
 	/* Need DEVICE if we don't have a separate VLAN_ID property */
-	iface_name = svGetValueString (ifcfg, "DEVICE");
+	iface_name = svGetValueStr_cp (ifcfg, "DEVICE");
 	if (!iface_name && vlan_id < 0) {
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "Missing DEVICE property; cannot determine VLAN ID.");
@@ -4796,7 +4980,7 @@ make_vlan_setting (shvarFile *ifcfg,
 	s_vlan = NM_SETTING_VLAN (nm_setting_vlan_new ());
 
 	/* Parent interface from PHYSDEV takes precedence if it exists */
-	parent = svGetValueString (ifcfg, "PHYSDEV");
+	parent = svGetValueStr_cp (ifcfg, "PHYSDEV");
 
 	if (iface_name) {
 		p = strchr (iface_name, '.');
@@ -4808,8 +4992,7 @@ make_vlan_setting (shvarFile *ifcfg,
 					/* Like initscripts, if no PHYSDEV and we get an obviously
 					 * invalid parent interface from DEVICE, fail.
 					 */
-					g_free (parent);
-					parent = NULL;
+					nm_clear_g_free (&parent);
 				}
 			}
 			p++;
@@ -4834,17 +5017,16 @@ make_vlan_setting (shvarFile *ifcfg,
 	if (vlan_id < 0) {
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "Failed to determine VLAN ID from DEVICE or VLAN_ID.");
-		goto error;
+		return NULL;
 	}
 	g_object_set (s_vlan, NM_SETTING_VLAN_ID, vlan_id, NULL);
 
 	if (parent == NULL) {
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                     "Failed to determine VLAN parent from DEVICE or PHYSDEV");
-		goto error;
+		return NULL;
 	}
 	g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, parent, NULL);
-	g_clear_pointer (&parent, g_free);
 
 	vlan_flags |= NM_VLAN_FLAG_REORDER_HEADERS;
 
@@ -4852,7 +5034,7 @@ make_vlan_setting (shvarFile *ifcfg,
 	if (gvrp > 0)
 		vlan_flags |= NM_VLAN_FLAG_GVRP;
 
-	value = svGetValueString (ifcfg, "VLAN_FLAGS");
+	value = svGetValueStr_cp (ifcfg, "VLAN_FLAGS");
 	if (value) {
 		gs_strfreev char **strv = NULL;
 		char **ptr;
@@ -4883,15 +5065,7 @@ make_vlan_setting (shvarFile *ifcfg,
 	parse_prio_map_list (s_vlan, ifcfg, "VLAN_INGRESS_PRIORITY_MAP", NM_VLAN_INGRESS_MAP);
 	parse_prio_map_list (s_vlan, ifcfg, "VLAN_EGRESS_PRIORITY_MAP", NM_VLAN_EGRESS_MAP);
 
-	g_free (iface_name);
-
-	return (NMSetting *) s_vlan;
-
-error:
-	g_free (parent);
-	g_free (iface_name);
-	g_object_unref (s_vlan);
-	return NULL;
+	return g_steal_pointer (&s_vlan);
 }
 
 static NMConnection *
@@ -4963,7 +5137,7 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg,
 	nm_connection_add_setting (connection, nm_setting_generic_new ());
 
 	/* Get a spec */
-	value = svGetValueString (ifcfg, "HWADDR");
+	value = svGetValueStr_cp (ifcfg, "HWADDR");
 	if (value) {
 		char *lower = g_ascii_strdown (value, -1);
 		*out_spec = g_strdup_printf ("%s:mac:%s", type, lower);
@@ -4972,14 +5146,14 @@ create_unhandled_connection (const char *filename, shvarFile *ifcfg,
 		return connection;
 	}
 
-	value = svGetValueString (ifcfg, "SUBCHANNELS");
+	value = svGetValueStr_cp (ifcfg, "SUBCHANNELS");
 	if (value) {
 		*out_spec = g_strdup_printf ("%s:s390-subchannels:%s", type, value);
 		g_free (value);
 		return connection;
 	}
 
-	value = svGetValueString (ifcfg, "DEVICE");
+	value = svGetValueStr_cp (ifcfg, "DEVICE");
 	if (value) {
 		*out_spec = g_strdup_printf ("%s:interface-name:%s", type, value);
 		g_free (value);
@@ -5008,11 +5182,9 @@ uuid_from_file (const char *filename)
 		return NULL;
 
 	/* Try for a UUID key before falling back to hashing the file name */
-	uuid = svGetValueString (ifcfg, "UUID");
-	if (!uuid || !strlen (uuid)) {
-		g_free (uuid);
+	uuid = svGetValueStr_cp (ifcfg, "UUID");
+	if (!uuid)
 		uuid = nm_utils_uuid_generate_from_string (svFileGetName (ifcfg), -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
-	}
 
 	svCloseFile (ifcfg);
 	return uuid;
@@ -5029,7 +5201,8 @@ check_dns_search_domains (shvarFile *ifcfg, NMSetting *s_ip4, NMSetting *s_ip6)
 	 */
 	if (!s_ip4 || nm_setting_ip_config_get_num_dns_searches (NM_SETTING_IP_CONFIG (s_ip4)) == 0) {
 		/* DNS searches */
-		char *value = svGetValueString (ifcfg, "DOMAIN");
+		char *value = svGetValueStr_cp (ifcfg, "DOMAIN");
+
 		if (value) {
 			char **searches = g_strsplit (value, " ", 0);
 			if (searches) {
@@ -5055,12 +5228,13 @@ connection_from_file_full (const char *filename,
                            GError **error,
                            gboolean *out_ignore_error)
 {
-	NMConnection *connection = NULL;
-	shvarFile *parsed;
+	nm_auto_shvar_file_close shvarFile *parsed = NULL;
+	gs_unref_object NMConnection *connection = NULL;
 	gs_free char *type = NULL;
 	char *devtype, *bootproto;
-	NMSetting *s_ip4, *s_ip6, *s_proxy, *s_port, *s_dcb = NULL;
+	NMSetting *s_ip4, *s_ip6, *s_proxy, *s_port, *s_dcb = NULL, *s_user;
 	const char *ifcfg_name = NULL;
+	gboolean has_ip4_defroute = FALSE;
 
 	g_return_val_if_fail (filename != NULL, NULL);
 	g_return_val_if_fail (out_unhandled && !*out_unhandled, NULL);
@@ -5082,30 +5256,38 @@ connection_from_file_full (const char *filename,
 
 	if (!svGetValueBoolean (parsed, "NM_CONTROLLED", TRUE)) {
 		connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled);
-		if (!connection)
+		if (!connection) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 			             "NM_CONTROLLED was false but device was not uniquely identified; device will be managed");
-		goto done;
+		}
+		return g_steal_pointer (&connection);
 	}
 
 	/* iBFT is handled by the iBFT settings plugin */
-	bootproto = svGetValueString (parsed, "BOOTPROTO");
+	bootproto = svGetValueStr_cp (parsed, "BOOTPROTO");
 	if (bootproto && !g_ascii_strcasecmp (bootproto, "ibft")) {
 		if (out_ignore_error)
 			*out_ignore_error = TRUE;
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Ignoring iBFT configuration");
 		g_free (bootproto);
-		goto done;
+		return NULL;
 	}
 	g_free (bootproto);
 
-	devtype = svGetValueString (parsed, "DEVICETYPE");
+	devtype = svGetValueStr_cp (parsed, "DEVICETYPE");
 	if (devtype) {
 		if (!strcasecmp (devtype, TYPE_TEAM))
 			type = g_strdup (TYPE_TEAM);
-		else if (!strcasecmp (devtype, TYPE_TEAM_PORT))
-			type = g_strdup (TYPE_ETHERNET);
+		else if (!strcasecmp (devtype, TYPE_TEAM_PORT)) {
+			gs_free char *device = NULL;
+
+			device = svGetValueStr_cp (parsed, "DEVICE");
+			if (device && is_vlan_device (device, parsed))
+				type = g_strdup (TYPE_VLAN);
+			else
+				type = g_strdup (TYPE_ETHERNET);
+		}
 		g_free (devtype);
 	}
 	if (!type) {
@@ -5114,33 +5296,32 @@ connection_from_file_full (const char *filename,
 		/* Team and TeamPort types are also accepted by the mere
 		 * presense of TEAM_CONFIG/TEAM_MASTER. They don't require
 		 * DEVICETYPE. */
-		t = svGetValueString (parsed, "TEAM_CONFIG");
+		t = svGetValueStr_cp (parsed, "TEAM_CONFIG");
 		if (t)
 			type = g_strdup (TYPE_TEAM);
 	}
 
 	if (!type)
-		type = svGetValueString (parsed, "TYPE");
+		type = svGetValueStr_cp (parsed, "TYPE");
 
 	if (!type) {
 		gs_free char *tmp = NULL;
 		char *device;
 
-		if ((tmp = svGetValueString (parsed, "IPV6TUNNELIPV4"))) {
+		if ((tmp = svGetValueStr_cp (parsed, "IPV6TUNNELIPV4"))) {
 			if (out_ignore_error)
 				*out_ignore_error = TRUE;
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Ignoring unsupported connection due to IPV6TUNNELIPV4");
-			goto done;
+			return NULL;
 		}
 
-		device = svGetValueString (parsed, "DEVICE");
+		device = svGetValueStr_cp (parsed, "DEVICE");
 		if (!device) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "File '%s' had neither TYPE nor DEVICE keys.", filename);
-			goto done;
+			return NULL;
 		}
-		g_assert (device[0]);
 
 		if (!strcmp (device, "lo")) {
 			if (out_ignore_error)
@@ -5148,7 +5329,7 @@ connection_from_file_full (const char *filename,
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Ignoring loopback device config.");
 			g_free (device);
-			goto done;
+			return NULL;
 		}
 
 		if (!test_type) {
@@ -5196,7 +5377,7 @@ connection_from_file_full (const char *filename,
 						g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 						             "Ignore script for unknown device type which has a matching %s script",
 						             p_path);
-						goto done;
+						return NULL;
 					}
 				}
 
@@ -5220,11 +5401,21 @@ connection_from_file_full (const char *filename,
 		}
 	}
 
+	if (nm_streq0 (type, TYPE_ETHERNET)) {
+		gs_free char *bond_options = NULL;
+
+		if (svGetValueStr (parsed, "BONDING_OPTS", &bond_options)) {
+			/* initscripts consider these as bond masters */
+			g_free (type);
+			type = g_strdup (TYPE_BOND);
+		}
+	}
+
 	if (svGetValueBoolean (parsed, "BONDING_MASTER", FALSE) &&
 	    strcasecmp (type, TYPE_BOND)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "BONDING_MASTER=yes key only allowed in TYPE=bond connections");
-		goto done;
+		return NULL;
 	}
 
 	/* Construct the connection */
@@ -5244,29 +5435,30 @@ connection_from_file_full (const char *filename,
 		connection = bridge_connection_from_ifcfg (filename, parsed, error);
 	else {
 		connection = create_unhandled_connection (filename, parsed, "unrecognized", out_unhandled);
-		if (!connection)
+		if (!connection) {
 			PARSE_WARNING ("connection type was unrecognized but device was not uniquely identified; device may be managed");
-		goto done;
+			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+			             "Failed to read unrecognized connection");
+		}
+		return g_steal_pointer (&connection);
 	}
 
 	if (!connection)
-		goto done;
+		return NULL;
 
 	s_ip6 = make_ip6_setting (parsed, network_file, error);
-	if (!s_ip6) {
-		g_object_unref (connection);
-		connection = NULL;
-		goto done;
-	} else
+	if (!s_ip6)
+		return NULL;
+	else
 		nm_connection_add_setting (connection, s_ip6);
 
-	s_ip4 = make_ip4_setting (parsed, network_file, error);
-	if (!s_ip4) {
-		g_object_unref (connection);
-		connection = NULL;
-		goto done;
-	} else {
-		read_aliases (NM_SETTING_IP_CONFIG (s_ip4), filename);
+	s_ip4 = make_ip4_setting (parsed, network_file, &has_ip4_defroute, error);
+	if (!s_ip4)
+		return NULL;
+	else {
+		read_aliases (NM_SETTING_IP_CONFIG (s_ip4),
+		              !has_ip4_defroute && !nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (s_ip4)),
+		              filename);
 		nm_connection_add_setting (connection, s_ip4);
 	}
 
@@ -5280,6 +5472,10 @@ connection_from_file_full (const char *filename,
 	if (s_proxy)
 		nm_connection_add_setting (connection, s_proxy);
 
+	s_user = make_user_setting (parsed, error);
+	if (s_user)
+		nm_connection_add_setting (connection, s_user);
+
 	/* Bridge port? */
 	s_port = make_bridge_port_setting (parsed);
 	if (s_port)
@@ -5290,22 +5486,15 @@ connection_from_file_full (const char *filename,
 	if (s_port)
 		nm_connection_add_setting (connection, s_port);
 
-	if (!make_dcb_setting (parsed, network_file, &s_dcb, error)) {
-		g_object_unref (connection);
-		connection = NULL;
-		goto done;
-	}
+	if (!make_dcb_setting (parsed, network_file, &s_dcb, error))
+		return NULL;
 	if (s_dcb)
 		nm_connection_add_setting (connection, s_dcb);
 
-	if (!nm_connection_normalize (connection, NULL, NULL, error)) {
-		g_object_unref (connection);
-		connection = NULL;
-	}
+	if (!nm_connection_normalize (connection, NULL, NULL, error))
+		return NULL;
 
-done:
-	svCloseFile (parsed);
-	return connection;
+	return g_steal_pointer (&connection);
 }
 
 NMConnection *
@@ -5348,7 +5537,7 @@ devtimeout_from_file (const char *filename)
 	if (!ifcfg)
 		return 0;
 
-	devtimeout_str = svGetValueString (ifcfg, "DEVTIMEOUT");
+	devtimeout_str = svGetValueStr_cp (ifcfg, "DEVTIMEOUT");
 	if (devtimeout_str) {
 		devtimeout = _nm_utils_ascii_str_to_int64 (devtimeout_str, 10, 0, G_MAXUINT, 0);
 		g_free (devtimeout_str);
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
index d1c00976..e82ef60c 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
@@ -45,8 +45,9 @@ check_rpm_temp_suffix (const char *path)
 
 	/* Matches *;[a-fA-F0-9]{8}; used by rpm */
 	ptr = strrchr (path, ';');
-	if (ptr && (strspn (ptr + 1, "abcdefABCDEF0123456789") == 8)
-	    && (! ptr[9]))
+	if (   ptr
+	    && strspn (ptr + 1, "abcdefABCDEF0123456789") == 8
+	    && !ptr[9])
 		return TRUE;
 	return FALSE;
 }
@@ -100,19 +101,20 @@ utils_should_ignore_file (const char *filename, gboolean only_ifcfg)
 }
 
 char *
-utils_cert_path (const char *parent, const char *suffix)
+utils_cert_path (const char *parent, const char *suffix, const char *extension)
 {
+	gs_free char *dir = NULL;
 	const char *name;
-	char *dir, *path;
 
-	g_return_val_if_fail (parent != NULL, NULL);
-	g_return_val_if_fail (suffix != NULL, NULL);
+	g_return_val_if_fail (parent, NULL);
+	g_return_val_if_fail (suffix, NULL);
+	g_return_val_if_fail (extension, NULL);
 
 	name = utils_get_ifcfg_name (parent, FALSE);
+	g_return_val_if_fail (name, NULL);
+
 	dir = g_path_get_dirname (parent);
-	path = g_strdup_printf ("%s/%s-%s", dir, name, suffix);
-	g_free (dir);
-	return path;
+	return g_strdup_printf ("%s/%s-%s.%s", dir, name, suffix, extension);
 }
 
 const char *
@@ -363,3 +365,117 @@ utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg)
 		return NULL;
 	return utils_get_ifcfg_path (path);
 }
+
+void
+nms_ifcfg_rh_utils_user_key_encode (const char *key, GString *str_buffer)
+{
+	gsize i;
+
+	nm_assert (key);
+	nm_assert (str_buffer);
+
+	for (i = 0; key[i]; i++) {
+		char ch = key[i];
+
+		/* we encode the key in only upper case letters, digits, and underscore.
+		 * As we expect lower-case letters to be more common, we encode lower-case
+		 * letters as upper case, and upper-case letters with a leading underscore. */
+
+		if (ch >= '0' && ch <= '9') {
+			g_string_append_c (str_buffer, ch);
+			continue;
+		}
+		if (ch >= 'a' && ch <= 'z') {
+			g_string_append_c (str_buffer, ch - 'a' + 'A');
+			continue;
+		}
+		if (ch == '.') {
+			g_string_append (str_buffer, "__");
+			continue;
+		}
+		if (ch >= 'A' && ch <= 'Z') {
+			g_string_append_c (str_buffer, '_');
+			g_string_append_c (str_buffer, ch);
+			continue;
+		}
+		g_string_append_printf (str_buffer, "_%03o", (unsigned) ch);
+	}
+}
+
+gboolean
+nms_ifcfg_rh_utils_user_key_decode (const char *name, GString *str_buffer)
+{
+	gsize i;
+
+	nm_assert (name);
+	nm_assert (str_buffer);
+
+	if (!name[0])
+		return FALSE;
+
+	for (i = 0; name[i]; ) {
+		char ch = name[i];
+
+		if (ch >= '0' && ch <= '9') {
+			g_string_append_c (str_buffer, ch);
+			i++;
+			continue;
+		}
+		if (ch >= 'A' && ch <= 'Z') {
+			g_string_append_c (str_buffer, ch - 'A' + 'a');
+			i++;
+			continue;
+		}
+
+		if (ch == '_') {
+			ch = name[i + 1];
+			if (ch == '_') {
+				g_string_append_c (str_buffer, '.');
+				i += 2;
+				continue;
+			}
+			if (ch >= 'A' && ch <= 'Z') {
+				g_string_append_c (str_buffer, ch);
+				i += 2;
+				continue;
+			}
+			if (ch >= '0' && ch <= '7') {
+				char ch2, ch3;
+				unsigned v;
+
+				ch2 = name[i + 2];
+				if (!(ch2 >= '0' && ch2 <= '7'))
+					return FALSE;
+
+				ch3 = name[i + 3];
+				if (!(ch3 >= '0' && ch3 <= '7'))
+					return FALSE;
+
+#define OCTAL_VALUE(ch) ((unsigned) ((ch) - '0'))
+				v = (OCTAL_VALUE (ch)  << 6) +
+				    (OCTAL_VALUE (ch2) << 3) +
+				     OCTAL_VALUE (ch3);
+				if (   v > 0xFF
+				    || v == 0)
+					return FALSE;
+				ch = (char) v;
+				if (   (ch >= 'A' && ch <= 'Z')
+				    || (ch >= '0' && ch <= '9')
+				    || (ch == '.')
+				    || (ch >= 'a' && ch <= 'z')) {
+					/* such characters are not expected to be encoded via
+					 * octal representation. The encoding is invalid. */
+					return FALSE;
+				}
+				g_string_append_c (str_buffer, ch);
+				i += 4;
+				continue;
+			}
+			return FALSE;
+		}
+
+		return FALSE;
+	}
+
+	return TRUE;
+}
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
index af0469e6..2b2c2755 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
@@ -31,7 +31,7 @@
 #define NM_IFCFG_CONNECTION_LOG_FMTD        "%s (%s,\"%s\",%p)"
 #define NM_IFCFG_CONNECTION_LOG_ARGD(con)   NM_IFCFG_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_connection_get_uuid ((NMConnection *) (con)), nm_connection_get_id ((NMConnection *) (con)), (con)
 
-char *utils_cert_path (const char *parent, const char *suffix);
+char *utils_cert_path (const char *parent, const char *suffix, const char *extension);
 
 const char *utils_get_ifcfg_name (const char *file, gboolean only_ifcfg);
 
@@ -54,5 +54,7 @@ gboolean utils_is_ifcfg_alias_file (const char *alias, const char *ifcfg);
 
 char *utils_detect_ifcfg_path (const char *path, gboolean only_ifcfg);
 
-#endif  /* _UTILS_H_ */
+void nms_ifcfg_rh_utils_user_key_encode (const char *key, GString *str_buffer);
+gboolean nms_ifcfg_rh_utils_user_key_decode (const char *name, GString *str_buffer);
 
+#endif  /* _UTILS_H_ */
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
index fa8013b0..d6f33c49 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -42,11 +42,13 @@
 #include "nm-setting-ip6-config.h"
 #include "nm-setting-pppoe.h"
 #include "nm-setting-vlan.h"
+#include "nm-setting-user.h"
 #include "nm-setting-team.h"
 #include "nm-setting-team-port.h"
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
+#include "nm-setting-metadata.h"
 
 #include "nms-ifcfg-rh-common.h"
 #include "nms-ifcfg-rh-reader.h"
@@ -59,7 +61,7 @@
 #define _NMLOG_PREFIX_NAME "ifcfg-rh"
 #define _NMLOG(level, ...) \
     G_STMT_START { \
-        nm_log ((level), (_NMLOG_DOMAIN), \
+        nm_log ((level), (_NMLOG_DOMAIN), NULL, NULL, \
                 "%s" _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
                 _NMLOG_PREFIX_NAME": " \
                 _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
@@ -99,7 +101,7 @@ save_secret_flags (shvarFile *ifcfg,
 		g_string_append (str, SECRET_FLAG_NOT_REQUIRED);
 	}
 
-	svSetValueString (ifcfg, key, str->len ? str->str : NULL);
+	svSetValueStr (ifcfg, key, str->len ? str->str : NULL);
 	g_string_free (str, TRUE);
 }
 
@@ -127,7 +129,7 @@ set_secret (shvarFile *ifcfg,
 
 	/* Only write the secret if it's system owned and supposed to be saved */
 	if (flags == NM_SETTING_SECRET_FLAG_NONE)
-		svSetValueString (keyfile, key, value);
+		svSetValueStr (keyfile, key, value);
 	else
 		svUnsetValue (keyfile, key);
 
@@ -143,112 +145,70 @@ set_secret (shvarFile *ifcfg,
 
 error:
 	/* Try setting the secret in the actual ifcfg */
-	svSetValueString (ifcfg, key, value);
+	svSetValueStr (ifcfg, key, value);
 }
 
-typedef struct ObjectType {
-	const char *setting_key;
-	NMSetting8021xCKScheme (*scheme_func)(NMSetting8021x *setting);
-	const char *           (*path_func)  (NMSetting8021x *setting);
-	GBytes *               (*blob_func)  (NMSetting8021x *setting);
-	const char *ifcfg_key;
-	const char *suffix;
-} ObjectType;
-
-static const ObjectType ca_type = {
-	NM_SETTING_802_1X_CA_CERT,
-	nm_setting_802_1x_get_ca_cert_scheme,
-	nm_setting_802_1x_get_ca_cert_path,
-	nm_setting_802_1x_get_ca_cert_blob,
-	"IEEE_8021X_CA_CERT",
-	"ca-cert.der"
-};
-
-static const ObjectType phase2_ca_type = {
-	NM_SETTING_802_1X_PHASE2_CA_CERT,
-	nm_setting_802_1x_get_phase2_ca_cert_scheme,
-	nm_setting_802_1x_get_phase2_ca_cert_path,
-	nm_setting_802_1x_get_phase2_ca_cert_blob,
-	"IEEE_8021X_INNER_CA_CERT",
-	"inner-ca-cert.der"
-};
-
-static const ObjectType client_type = {
-	NM_SETTING_802_1X_CLIENT_CERT,
-	nm_setting_802_1x_get_client_cert_scheme,
-	nm_setting_802_1x_get_client_cert_path,
-	nm_setting_802_1x_get_client_cert_blob,
-	"IEEE_8021X_CLIENT_CERT",
-	"client-cert.der"
-};
-
-static const ObjectType phase2_client_type = {
-	NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
-	nm_setting_802_1x_get_phase2_client_cert_scheme,
-	nm_setting_802_1x_get_phase2_client_cert_path,
-	nm_setting_802_1x_get_phase2_client_cert_blob,
-	"IEEE_8021X_INNER_CLIENT_CERT",
-	"inner-client-cert.der"
-};
-
-static const ObjectType pk_type = {
-	NM_SETTING_802_1X_PRIVATE_KEY,
-	nm_setting_802_1x_get_private_key_scheme,
-	nm_setting_802_1x_get_private_key_path,
-	nm_setting_802_1x_get_private_key_blob,
-	"IEEE_8021X_PRIVATE_KEY",
-	"private-key.pem"
-};
-
-static const ObjectType phase2_pk_type = {
-	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-	nm_setting_802_1x_get_phase2_private_key_scheme,
-	nm_setting_802_1x_get_phase2_private_key_path,
-	nm_setting_802_1x_get_phase2_private_key_blob,
-	"IEEE_8021X_INNER_PRIVATE_KEY",
-	"inner-private-key.pem"
-};
-
-static const ObjectType p12_type = {
-	NM_SETTING_802_1X_PRIVATE_KEY,
-	nm_setting_802_1x_get_private_key_scheme,
-	nm_setting_802_1x_get_private_key_path,
-	nm_setting_802_1x_get_private_key_blob,
-	"IEEE_8021X_PRIVATE_KEY",
-	"private-key.p12"
-};
-
-static const ObjectType phase2_p12_type = {
-	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-	nm_setting_802_1x_get_phase2_private_key_scheme,
-	nm_setting_802_1x_get_phase2_private_key_path,
-	nm_setting_802_1x_get_phase2_private_key_blob,
-	"IEEE_8021X_INNER_PRIVATE_KEY",
-	"inner-private-key.p12"
+typedef struct {
+	const NMSetting8021xSchemeVtable *vtable;
+	const char *ifcfg_rh_key;
+} Setting8021xSchemeVtable;
+
+static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = {
+	[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT],
+		.ifcfg_rh_key           = "IEEE_8021X_CA_CERT",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT],
+		.ifcfg_rh_key           = "IEEE_8021X_INNER_CA_CERT",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT],
+		.ifcfg_rh_key           = "IEEE_8021X_CLIENT_CERT",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT],
+		.ifcfg_rh_key           = "IEEE_8021X_INNER_CLIENT_CERT",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY],
+		.ifcfg_rh_key           = "IEEE_8021X_PRIVATE_KEY",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY],
+		.ifcfg_rh_key           = "IEEE_8021X_INNER_PRIVATE_KEY",
+	},
 };
 
 static gboolean
 write_object (NMSetting8021x *s_8021x,
               shvarFile *ifcfg,
-              const ObjectType *objtype,
+              const Setting8021xSchemeVtable *objtype,
               GError **error)
 {
 	NMSetting8021xCKScheme scheme;
-	const char *path = NULL;
+	const char *value = NULL;
 	GBytes *blob = NULL;
+	const char *password = NULL;
+	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+	char *secret_name, *secret_flags;
+	const char *extension;
 
 	g_return_val_if_fail (ifcfg != NULL, FALSE);
 	g_return_val_if_fail (objtype != NULL, FALSE);
 
-	scheme = (*(objtype->scheme_func))(s_8021x);
+	scheme = (*(objtype->vtable->scheme_func))(s_8021x);
 	switch (scheme) {
 	case NM_SETTING_802_1X_CK_SCHEME_UNKNOWN:
 		break;
 	case NM_SETTING_802_1X_CK_SCHEME_BLOB:
-		blob = (*(objtype->blob_func))(s_8021x);
+		blob = (*(objtype->vtable->blob_func))(s_8021x);
 		break;
 	case NM_SETTING_802_1X_CK_SCHEME_PATH:
-		path = (*(objtype->path_func))(s_8021x);
+		value = (*(objtype->vtable->path_func))(s_8021x);
+		break;
+	case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
+		value = (*(objtype->vtable->uri_func))(s_8021x);
 		break;
 	default:
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
@@ -256,10 +216,26 @@ write_object (NMSetting8021x *s_8021x,
 		return FALSE;
 	}
 
+	/* Set the password for certificate/private key. */
+	secret_name = g_strdup_printf ("%s_PASSWORD", objtype->ifcfg_rh_key);
+	secret_flags = g_strdup_printf ("%s_PASSWORD_FLAGS", objtype->ifcfg_rh_key);
+	password = (*(objtype->vtable->passwd_func))(s_8021x);
+	flags = (*(objtype->vtable->pwflag_func))(s_8021x);
+	set_secret (ifcfg, secret_name, password, secret_flags, flags);
+	g_free (secret_name);
+	g_free (secret_flags);
+
+	if (!objtype->vtable->format_func)
+		extension = "der";
+	else if (objtype->vtable->format_func (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
+		extension = "p12";
+	else
+		extension = "pem";
+
 	/* If certificate/private key wasn't sent, the connection may no longer be
 	 * 802.1x and thus we clear out the paths and certs.
 	 */
-	if (!path && !blob) {
+	if (!value && !blob) {
 		char *standard_file;
 		int ignored;
 
@@ -269,20 +245,20 @@ write_object (NMSetting8021x *s_8021x,
 		 * /etc/sysconfig/network-scripts/ca-cert-Test_Write_Wifi_WPA_EAP-TLS.der
 		 * will be deleted, but /etc/pki/tls/cert.pem will not.
 		 */
-		standard_file = utils_cert_path (svFileGetName (ifcfg), objtype->suffix);
+		standard_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension);
 		if (g_file_test (standard_file, G_FILE_TEST_EXISTS))
 			ignored = unlink (standard_file);
 		g_free (standard_file);
 
-		svUnsetValue (ifcfg, objtype->ifcfg_key);
+		svUnsetValue (ifcfg, objtype->ifcfg_rh_key);
 		return TRUE;
 	}
 
 	/* If the object path was specified, prefer that over any raw cert data that
 	 * may have been sent.
 	 */
-	if (path) {
-		svSetValueString (ifcfg, objtype->ifcfg_key, path);
+	if (value) {
+		svSetValueStr (ifcfg, objtype->ifcfg_rh_key, value);
 		return TRUE;
 	}
 
@@ -292,11 +268,11 @@ write_object (NMSetting8021x *s_8021x,
 		char *new_file;
 		GError *write_error = NULL;
 
-		new_file = utils_cert_path (svFileGetName (ifcfg), objtype->suffix);
+		new_file = utils_cert_path (svFileGetName (ifcfg), objtype->vtable->file_suffix, extension);
 		if (!new_file) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 			             "Could not create file path for %s / %s",
-			             NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key);
+			             NM_SETTING_802_1X_SETTING_NAME, objtype->vtable->setting_key);
 			return FALSE;
 		}
 
@@ -310,13 +286,13 @@ write_object (NMSetting8021x *s_8021x,
 		                                      0600,
 		                                      &write_error);
 		if (success) {
-			svSetValueString (ifcfg, objtype->ifcfg_key, new_file);
+			svSetValueStr (ifcfg, objtype->ifcfg_rh_key, new_file);
 			g_free (new_file);
 			return TRUE;
 		} else {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 			             "Could not write certificate/key for %s / %s: %s",
-			             NM_SETTING_802_1X_SETTING_NAME, objtype->setting_key,
+			             NM_SETTING_802_1X_SETTING_NAME, objtype->vtable->setting_key,
 			             (write_error && write_error->message) ? write_error->message : "(unknown)");
 			g_clear_error (&write_error);
 		}
@@ -332,71 +308,45 @@ write_8021x_certs (NMSetting8021x *s_8021x,
                    shvarFile *ifcfg,
                    GError **error)
 {
-	const char *password = NULL;
-	gboolean success = FALSE, is_pkcs12 = FALSE;
-	const ObjectType *otype = NULL;
-	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+	const Setting8021xSchemeVtable *otype = NULL;
 
 	/* CA certificate */
-	if (!write_object (s_8021x, ifcfg, phase2 ? &phase2_ca_type : &ca_type, error))
+	if (!write_object (s_8021x, ifcfg,
+	                   phase2
+	                       ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT]
+	                       : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT],
+	                   error))
 		return FALSE;
 
 	/* Private key */
-	if (phase2) {
-		otype = &phase2_pk_type;
-		if (nm_setting_802_1x_get_phase2_private_key_format (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
-			otype = &phase2_p12_type;
-			is_pkcs12 = TRUE;
-		}
-		password = nm_setting_802_1x_get_phase2_private_key_password (s_8021x);
-		flags = nm_setting_802_1x_get_phase2_private_key_password_flags (s_8021x);
-	} else {
-		otype = &pk_type;
-		if (nm_setting_802_1x_get_private_key_format (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
-			otype = &p12_type;
-			is_pkcs12 = TRUE;
-		}
-		password = nm_setting_802_1x_get_private_key_password (s_8021x);
-		flags = nm_setting_802_1x_get_private_key_password_flags (s_8021x);
-	}
+	if (phase2)
+		otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY];
+	else
+		otype = &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY];
 
 	/* Save the private key */
 	if (!write_object (s_8021x, ifcfg, otype, error))
-		goto out;
-
-	/* Private key password */
-	if (phase2) {
-		set_secret (ifcfg,
-		            "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD",
-		            password,
-		            "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD_FLAGS",
-		            flags);
-	} else {
-		set_secret (ifcfg,
-		            "IEEE_8021X_PRIVATE_KEY_PASSWORD",
-		            password,
-		            "IEEE_8021X_PRIVATE_KEY_PASSWORD_FLAGS",
-		            flags);
-	}
+		return FALSE;
 
 	/* Client certificate */
-	if (is_pkcs12) {
+	if (otype->vtable->format_func (s_8021x) == NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
 		/* Don't need a client certificate with PKCS#12 since the file is both
 		 * the client certificate and the private key in one file.
 		 */
-		svSetValueString (ifcfg,
-		                  phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT",
-		                  NULL);
+		svSetValueStr (ifcfg,
+		               phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT",
+		               NULL);
 	} else {
 		/* Save the client certificate */
-		if (!write_object (s_8021x, ifcfg, phase2 ? &phase2_client_type : &client_type, error))
-			goto out;
+		if (!write_object (s_8021x, ifcfg,
+		                   phase2
+		                       ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT]
+		                       : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT],
+		                   error))
+			return FALSE;
 	}
 
-	success = TRUE;
-
-out:
-	return success;
+	return TRUE;
 }
 
 static gboolean
@@ -406,12 +356,13 @@ write_8021x_setting (NMConnection *connection,
                      GError **error)
 {
 	NMSetting8021x *s_8021x;
+	NMSetting8021xAuthFlags auth_flags;
 	const char *value, *match;
 	char *tmp = NULL;
-	gboolean success = FALSE;
 	GString *phase2_auth;
 	GString *str;
 	guint32 i, num;
+	gint timeout;
 
 	s_8021x = nm_connection_get_setting_802_1x (connection);
 	if (!s_8021x) {
@@ -423,7 +374,7 @@ write_8021x_setting (NMConnection *connection,
 
 	/* If wired, write KEY_MGMT */
 	if (wired)
-		svSetValueString (ifcfg, "KEY_MGMT", "IEEE8021X");
+		svSetValueStr (ifcfg, "KEY_MGMT", "IEEE8021X");
 
 	/* EAP method */
 	if (nm_setting_802_1x_get_num_eap_methods (s_8021x)) {
@@ -431,14 +382,14 @@ write_8021x_setting (NMConnection *connection,
 		if (value)
 			tmp = g_ascii_strup (value, -1);
 	}
-	svSetValueString (ifcfg, "IEEE_8021X_EAP_METHODS", tmp);
+	svSetValueStr (ifcfg, "IEEE_8021X_EAP_METHODS", tmp);
 	g_free (tmp);
 
-	svSetValueString (ifcfg, "IEEE_8021X_IDENTITY",
-	                  nm_setting_802_1x_get_identity (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_IDENTITY",
+	               nm_setting_802_1x_get_identity (s_8021x));
 
-	svSetValueString (ifcfg, "IEEE_8021X_ANON_IDENTITY",
-	                  nm_setting_802_1x_get_anonymous_identity (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_ANON_IDENTITY",
+	               nm_setting_802_1x_get_anonymous_identity (s_8021x));
 
 	set_secret (ifcfg,
 	            "IEEE_8021X_PASSWORD",
@@ -450,30 +401,30 @@ write_8021x_setting (NMConnection *connection,
 	value = nm_setting_802_1x_get_phase1_peapver (s_8021x);
 	svUnsetValue (ifcfg, "IEEE_8021X_PEAP_VERSION");
 	if (value && (!strcmp (value, "0") || !strcmp (value, "1")))
-		svSetValueString (ifcfg, "IEEE_8021X_PEAP_VERSION", value);
+		svSetValueStr (ifcfg, "IEEE_8021X_PEAP_VERSION", value);
 
 	/* Force new PEAP label */
 	value = nm_setting_802_1x_get_phase1_peaplabel (s_8021x);
 	svUnsetValue (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL");
 	if (value && !strcmp (value, "1"))
-		svSetValueString (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", "yes");
+		svSetValueStr (ifcfg, "IEEE_8021X_PEAP_FORCE_NEW_LABEL", "yes");
 
 	/* PAC file */
 	value = nm_setting_802_1x_get_pac_file (s_8021x);
 	svUnsetValue (ifcfg, "IEEE_8021X_PAC_FILE");
 	if (value)
-		svSetValueString (ifcfg, "IEEE_8021X_PAC_FILE", value);
+		svSetValueStr (ifcfg, "IEEE_8021X_PAC_FILE", value);
 
 	/* FAST PAC provisioning */
 	value = nm_setting_802_1x_get_phase1_fast_provisioning (s_8021x);
 	svUnsetValue (ifcfg, "IEEE_8021X_FAST_PROVISIONING");
 	if (value) {
 		if (strcmp (value, "1") == 0)
-			svSetValueString (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth");
+			svSetValueStr (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth");
 		else if (strcmp (value, "2") == 0)
-			svSetValueString (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-auth");
+			svSetValueStr (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-auth");
 		else if (strcmp (value, "3") == 0)
-			svSetValueString (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth allow-auth");
+			svSetValueStr (ifcfg, "IEEE_8021X_FAST_PROVISIONING", "allow-unauth allow-auth");
 	}
 
 	/* Phase2 auth methods */
@@ -497,16 +448,27 @@ write_8021x_setting (NMConnection *connection,
 		g_free (tmp);
 	}
 
-	svSetValueString (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS",
-	                  phase2_auth->len ? phase2_auth->str : NULL);
+	auth_flags = nm_setting_802_1x_get_phase1_auth_flags (s_8021x);
+	if (auth_flags == NM_SETTING_802_1X_AUTH_FLAGS_NONE) {
+		svUnsetValue (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS");
+	} else {
+		gs_free char *flags_str = NULL;
+
+		flags_str = _nm_utils_enum_to_str_full (nm_setting_802_1x_auth_flags_get_type (),
+		                                        auth_flags, " ");
+		svSetValueStr (ifcfg, "IEEE_8021X_PHASE1_AUTH_FLAGS", flags_str);
+	}
+
+	svSetValueStr (ifcfg, "IEEE_8021X_INNER_AUTH_METHODS",
+	               phase2_auth->len ? phase2_auth->str : NULL);
 
 	g_string_free (phase2_auth, TRUE);
 
-	svSetValueString (ifcfg, "IEEE_8021X_SUBJECT_MATCH",
-	                  nm_setting_802_1x_get_subject_match (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_SUBJECT_MATCH",
+	               nm_setting_802_1x_get_subject_match (s_8021x));
 
-	svSetValueString (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH",
-	                  nm_setting_802_1x_get_phase2_subject_match (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_PHASE2_SUBJECT_MATCH",
+	               nm_setting_802_1x_get_phase2_subject_match (s_8021x));
 
 	svUnsetValue (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES");
 	str = g_string_new (NULL);
@@ -518,7 +480,7 @@ write_8021x_setting (NMConnection *connection,
 		g_string_append (str, match);
 	}
 	if (str->len > 0)
-		svSetValueString (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES", str->str);
+		svSetValueStr (ifcfg, "IEEE_8021X_ALTSUBJECT_MATCHES", str->str);
 	g_string_free (str, TRUE);
 
 	svUnsetValue (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES");
@@ -531,21 +493,28 @@ write_8021x_setting (NMConnection *connection,
 		g_string_append (str, match);
 	}
 	if (str->len > 0)
-		svSetValueString (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", str->str);
+		svSetValueStr (ifcfg, "IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES", str->str);
 	g_string_free (str, TRUE);
 
-	svSetValueString (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH",
-	                  nm_setting_802_1x_get_domain_suffix_match (s_8021x));
-	svSetValueString (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH",
-	                  nm_setting_802_1x_get_phase2_domain_suffix_match (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_DOMAIN_SUFFIX_MATCH",
+	               nm_setting_802_1x_get_domain_suffix_match (s_8021x));
+	svSetValueStr (ifcfg, "IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH",
+	               nm_setting_802_1x_get_phase2_domain_suffix_match (s_8021x));
 
-	success = write_8021x_certs (s_8021x, FALSE, ifcfg, error);
-	if (success) {
-		/* phase2/inner certs */
-		success = write_8021x_certs (s_8021x, TRUE, ifcfg, error);
-	}
+	timeout = nm_setting_802_1x_get_auth_timeout (s_8021x);
+	if (timeout > 0)
+		svSetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", timeout);
+	else
+		svUnsetValue (ifcfg, "IEEE_8021X_AUTH_TIMEOUT");
+
+	if (!write_8021x_certs (s_8021x, FALSE, ifcfg, error))
+		return FALSE;
 
-	return success;
+	/* phase2/inner certs */
+	if (!write_8021x_certs (s_8021x, TRUE, ifcfg, error))
+		return FALSE;
+
+	return TRUE;
 }
 
 static gboolean
@@ -582,27 +551,27 @@ write_wireless_security_setting (NMConnection *connection,
 		wep = TRUE;
 		*no_8021x = TRUE;
 	} else if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) {
-		svSetValueString (ifcfg, "KEY_MGMT", "WPA-PSK");
+		svSetValueStr (ifcfg, "KEY_MGMT", "WPA-PSK");
 		wpa = TRUE;
 		*no_8021x = TRUE;
 	} else if (!strcmp (key_mgmt, "ieee8021x")) {
-		svSetValueString (ifcfg, "KEY_MGMT", "IEEE8021X");
+		svSetValueStr (ifcfg, "KEY_MGMT", "IEEE8021X");
 		dynamic_wep = TRUE;
 	} else if (!strcmp (key_mgmt, "wpa-eap")) {
-		svSetValueString (ifcfg, "KEY_MGMT", "WPA-EAP");
+		svSetValueStr (ifcfg, "KEY_MGMT", "WPA-EAP");
 		wpa = TRUE;
 	}
 
 	svUnsetValue (ifcfg, "SECURITYMODE");
 	if (auth_alg) {
 		if (!strcmp (auth_alg, "shared"))
-			svSetValueString (ifcfg, "SECURITYMODE", "restricted");
+			svSetValueStr (ifcfg, "SECURITYMODE", "restricted");
 		else if (!strcmp (auth_alg, "open"))
-			svSetValueString (ifcfg, "SECURITYMODE", "open");
+			svSetValueStr (ifcfg, "SECURITYMODE", "open");
 		else if (!strcmp (auth_alg, "leap")) {
-			svSetValueString (ifcfg, "SECURITYMODE", "leap");
-			svSetValueString (ifcfg, "IEEE_8021X_IDENTITY",
-			                  nm_setting_wireless_security_get_leap_username (s_wsec));
+			svSetValueStr (ifcfg, "SECURITYMODE", "leap");
+			svSetValueStr (ifcfg, "IEEE_8021X_IDENTITY",
+			               nm_setting_wireless_security_get_leap_username (s_wsec));
 			set_secret (ifcfg,
 			            "IEEE_8021X_PASSWORD",
 			            nm_setting_wireless_security_get_leap_password (s_wsec),
@@ -632,7 +601,7 @@ write_wireless_security_setting (NMConnection *connection,
 	if (wep) {
 		/* Default WEP TX key index */
 		tmp = g_strdup_printf ("%d", nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec) + 1);
-		svSetValueString (ifcfg, "DEFAULTKEY", tmp);
+		svSetValueStr (ifcfg, "DEFAULTKEY", tmp);
 		g_free (tmp);
 
 		for (i = 0; i < 4; i++) {
@@ -688,9 +657,9 @@ write_wireless_security_setting (NMConnection *connection,
 	for (i = 0; i < num; i++) {
 		proto = nm_setting_wireless_security_get_proto (s_wsec, i);
 		if (proto && !strcmp (proto, "wpa"))
-			svSetValueString (ifcfg, "WPA_ALLOW_WPA", "yes");
+			svSetValueStr (ifcfg, "WPA_ALLOW_WPA", "yes");
 		else if (proto && !strcmp (proto, "rsn"))
-			svSetValueString (ifcfg, "WPA_ALLOW_WPA2", "yes");
+			svSetValueStr (ifcfg, "WPA_ALLOW_WPA2", "yes");
 	}
 
 	/* WPA Pairwise ciphers */
@@ -712,7 +681,7 @@ write_wireless_security_setting (NMConnection *connection,
 		}
 	}
 	if (strlen (str->str) && (dynamic_wep == FALSE))
-		svSetValueString (ifcfg, "CIPHER_PAIRWISE", str->str);
+		svSetValueStr (ifcfg, "CIPHER_PAIRWISE", str->str);
 	g_string_free (str, TRUE);
 
 	/* WPA Group ciphers */
@@ -728,7 +697,7 @@ write_wireless_security_setting (NMConnection *connection,
 		g_free (tmp);
 	}
 	if (strlen (str->str) && (dynamic_wep == FALSE))
-		svSetValueString (ifcfg, "CIPHER_GROUP", str->str);
+		svSetValueStr (ifcfg, "CIPHER_GROUP", str->str);
 	g_string_free (str, TRUE);
 
 	if (wpa)
@@ -768,13 +737,13 @@ write_wireless_setting (NMConnection *connection,
 	}
 
 	device_mac = nm_setting_wireless_get_mac_address (s_wireless);
-	svSetValueString (ifcfg, "HWADDR", device_mac);
+	svSetValueStr (ifcfg, "HWADDR", device_mac);
 
 	cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless);
-	svSetValueString (ifcfg, "MACADDR", cloned_mac);
+	svSetValueStr (ifcfg, "MACADDR", cloned_mac);
 
-	svSetValueString (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
-	                  nm_setting_wireless_get_generate_mac_address_mask (s_wireless));
+	svSetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
+	               nm_setting_wireless_get_generate_mac_address_mask (s_wireless));
 
 	svUnsetValue (ifcfg, "HWADDR_BLACKLIST");
 	macaddr_blacklist = nm_setting_wireless_get_mac_address_blacklist (s_wireless);
@@ -782,7 +751,7 @@ write_wireless_setting (NMConnection *connection,
 		char *blacklist_str;
 
 		blacklist_str = g_strjoinv (" ", (char **) macaddr_blacklist);
-		svSetValueString (ifcfg, "HWADDR_BLACKLIST", blacklist_str);
+		svSetValueStr (ifcfg, "HWADDR_BLACKLIST", blacklist_str);
 		g_free (blacklist_str);
 	}
 
@@ -790,7 +759,7 @@ write_wireless_setting (NMConnection *connection,
 	mtu = nm_setting_wireless_get_mtu (s_wireless);
 	if (mtu) {
 		tmp = g_strdup_printf ("%u", mtu);
-		svSetValueString (ifcfg, "MTU", tmp);
+		svSetValueStr (ifcfg, "MTU", tmp);
 		g_free (tmp);
 	}
 
@@ -838,7 +807,7 @@ write_wireless_setting (NMConnection *connection,
 		g_string_append (str, "0x");
 		for (i = 0; i < ssid_len; i++)
 			g_string_append_printf (str, "%02X", ssid_data[i]);
-		svSetValueString (ifcfg, "ESSID", str->str);
+		svSetValueStr (ifcfg, "ESSID", str->str);
 		g_string_free (str, TRUE);
 	} else {
 		char buf[33];
@@ -846,17 +815,17 @@ write_wireless_setting (NMConnection *connection,
 		nm_assert (ssid_len <= 32);
 		memcpy (buf, ssid_data, ssid_len);
 		buf[ssid_len] = '\0';
-		svSetValueString (ifcfg, "ESSID", buf);
+		svSetValueStr (ifcfg, "ESSID", buf);
 	}
 
 	mode = nm_setting_wireless_get_mode (s_wireless);
 	if (!mode || !strcmp (mode, "infrastructure")) {
-		svSetValueString (ifcfg, "MODE", "Managed");
+		svSetValueStr (ifcfg, "MODE", "Managed");
 	} else if (!strcmp (mode, "adhoc")) {
-		svSetValueString (ifcfg, "MODE", "Ad-Hoc");
+		svSetValueStr (ifcfg, "MODE", "Ad-Hoc");
 		adhoc = TRUE;
 	} else if (!strcmp (mode, "ap")) {
-		svSetValueString (ifcfg, "MODE", "Ap");
+		svSetValueStr (ifcfg, "MODE", "Ap");
 	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Invalid mode '%s' in '%s' setting",
@@ -869,15 +838,15 @@ write_wireless_setting (NMConnection *connection,
 	chan = nm_setting_wireless_get_channel (s_wireless);
 	if (chan) {
 		tmp = g_strdup_printf ("%u", chan);
-		svSetValueString (ifcfg, "CHANNEL", tmp);
+		svSetValueStr (ifcfg, "CHANNEL", tmp);
 		g_free (tmp);
 	} else {
 		/* Band only set if channel is not, since channel implies band */
-		svSetValueString (ifcfg, "BAND", nm_setting_wireless_get_band (s_wireless));
+		svSetValueStr (ifcfg, "BAND", nm_setting_wireless_get_band (s_wireless));
 	}
 
 	bssid = nm_setting_wireless_get_bssid (s_wireless);
-	svSetValueString (ifcfg, "BSSID", bssid);
+	svSetValueStr (ifcfg, "BSSID", bssid);
 
 	/* Ensure DEFAULTKEY and SECURITYMODE are cleared unless there's security;
 	 * otherwise there's no way to detect WEP vs. open when WEP keys aren't
@@ -923,17 +892,17 @@ write_wireless_setting (NMConnection *connection,
 		g_free (keys_path);
 	}
 
-	svSetValueString (ifcfg, "SSID_HIDDEN", nm_setting_wireless_get_hidden (s_wireless) ? "yes" : NULL);
+	svSetValueStr (ifcfg, "SSID_HIDDEN", nm_setting_wireless_get_hidden (s_wireless) ? "yes" : NULL);
 
 	switch (nm_setting_wireless_get_powersave (s_wireless)) {
 	case NM_SETTING_WIRELESS_POWERSAVE_IGNORE:
-		svSetValueString (ifcfg, "POWERSAVE", "ignore");
+		svSetValueStr (ifcfg, "POWERSAVE", "ignore");
 		break;
 	case NM_SETTING_WIRELESS_POWERSAVE_DISABLE:
-		svSetValueString (ifcfg, "POWERSAVE", "disable");
+		svSetValueStr (ifcfg, "POWERSAVE", "disable");
 		break;
 	case NM_SETTING_WIRELESS_POWERSAVE_ENABLE:
-		svSetValueString (ifcfg, "POWERSAVE", "enable");
+		svSetValueStr (ifcfg, "POWERSAVE", "enable");
 		break;
 	default:
 	case NM_SETTING_WIRELESS_POWERSAVE_DEFAULT:
@@ -943,18 +912,18 @@ write_wireless_setting (NMConnection *connection,
 
 	switch (nm_setting_wireless_get_mac_address_randomization (s_wireless)) {
 	case NM_SETTING_MAC_RANDOMIZATION_NEVER:
-		svSetValueString (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "never");
+		svSetValueStr (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "never");
 		break;
 	case NM_SETTING_MAC_RANDOMIZATION_ALWAYS:
-		svSetValueString (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "always");
+		svSetValueStr (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "always");
 		break;
 	case NM_SETTING_MAC_RANDOMIZATION_DEFAULT:
 	default:
-		svSetValueString (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "default");
+		svSetValueStr (ifcfg, "MAC_ADDRESS_RANDOMIZATION", "default");
 		break;
 	}
 
-	svSetValueString (ifcfg, "TYPE", TYPE_WIRELESS);
+	svSetValueStr (ifcfg, "TYPE", TYPE_WIRELESS);
 
 	return TRUE;
 }
@@ -976,13 +945,13 @@ write_infiniband_setting (NMConnection *connection, shvarFile *ifcfg, GError **e
 	}
 
 	mac = nm_setting_infiniband_get_mac_address (s_infiniband);
-	svSetValueString (ifcfg, "HWADDR", mac);
+	svSetValueStr (ifcfg, "HWADDR", mac);
 
 	svUnsetValue (ifcfg, "MTU");
 	mtu = nm_setting_infiniband_get_mtu (s_infiniband);
 	if (mtu) {
 		tmp = g_strdup_printf ("%u", mtu);
-		svSetValueString (ifcfg, "MTU", tmp);
+		svSetValueStr (ifcfg, "MTU", tmp);
 		g_free (tmp);
 	}
 
@@ -991,17 +960,17 @@ write_infiniband_setting (NMConnection *connection, shvarFile *ifcfg, GError **e
 
 	p_key = nm_setting_infiniband_get_p_key (s_infiniband);
 	if (p_key != -1) {
-		svSetValueString (ifcfg, "PKEY", "yes");
+		svSetValueStr (ifcfg, "PKEY", "yes");
 		tmp = g_strdup_printf ("%u", p_key);
-		svSetValueString (ifcfg, "PKEY_ID", tmp);
+		svSetValueStr (ifcfg, "PKEY_ID", tmp);
 		g_free (tmp);
 
 		parent = nm_setting_infiniband_get_parent (s_infiniband);
 		if (parent)
-			svSetValueString (ifcfg, "PHYSDEV", parent);
+			svSetValueStr (ifcfg, "PHYSDEV", parent);
 	}
 
-	svSetValueString (ifcfg, "TYPE", TYPE_INFINIBAND);
+	svSetValueStr (ifcfg, "TYPE", TYPE_INFINIBAND);
 
 	return TRUE;
 }
@@ -1029,13 +998,13 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	}
 
 	device_mac = nm_setting_wired_get_mac_address (s_wired);
-	svSetValueString (ifcfg, "HWADDR", device_mac);
+	svSetValueStr (ifcfg, "HWADDR", device_mac);
 
 	cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-	svSetValueString (ifcfg, "MACADDR", cloned_mac);
+	svSetValueStr (ifcfg, "MACADDR", cloned_mac);
 
-	svSetValueString (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
-	                  nm_setting_wired_get_generate_mac_address_mask (s_wired));
+	svSetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
+	               nm_setting_wired_get_generate_mac_address_mask (s_wired));
 
 	svUnsetValue (ifcfg, "HWADDR_BLACKLIST");
 	macaddr_blacklist = nm_setting_wired_get_mac_address_blacklist (s_wired);
@@ -1043,7 +1012,7 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		char *blacklist_str;
 
 		blacklist_str = g_strjoinv (" ", (char **) macaddr_blacklist);
-		svSetValueString (ifcfg, "HWADDR_BLACKLIST", blacklist_str);
+		svSetValueStr (ifcfg, "HWADDR_BLACKLIST", blacklist_str);
 		g_free (blacklist_str);
 	}
 
@@ -1051,7 +1020,7 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	mtu = nm_setting_wired_get_mtu (s_wired);
 	if (mtu) {
 		tmp = g_strdup_printf ("%u", mtu);
-		svSetValueString (ifcfg, "MTU", tmp);
+		svSetValueStr (ifcfg, "MTU", tmp);
 		g_free (tmp);
 	}
 
@@ -1061,30 +1030,30 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		int len = g_strv_length ((char **)s390_subchannels);
 
 		tmp = NULL;
-	    if (len == 2) {
-		    tmp = g_strdup_printf ("%s,%s", s390_subchannels[0], s390_subchannels[1]);
-	    } else if (len == 3) {
-		    tmp = g_strdup_printf ("%s,%s,%s", s390_subchannels[0], s390_subchannels[1],
-		                           s390_subchannels[2]);
+		if (len == 2) {
+			tmp = g_strdup_printf ("%s,%s", s390_subchannels[0], s390_subchannels[1]);
+		} else if (len == 3) {
+			tmp = g_strdup_printf ("%s,%s,%s", s390_subchannels[0], s390_subchannels[1],
+			                       s390_subchannels[2]);
 		}
-		svSetValueString (ifcfg, "SUBCHANNELS", tmp);
+		svSetValueStr (ifcfg, "SUBCHANNELS", tmp);
 		g_free (tmp);
 	}
 
 	svUnsetValue (ifcfg, "NETTYPE");
 	nettype = nm_setting_wired_get_s390_nettype (s_wired);
 	if (nettype)
-		svSetValueString (ifcfg, "NETTYPE", nettype);
+		svSetValueStr (ifcfg, "NETTYPE", nettype);
 
 	svUnsetValue (ifcfg, "PORTNAME");
 	portname = nm_setting_wired_get_s390_option_by_key (s_wired, "portname");
 	if (portname)
-		svSetValueString (ifcfg, "PORTNAME", portname);
+		svSetValueStr (ifcfg, "PORTNAME", portname);
 
 	svUnsetValue (ifcfg, "CTCPROT");
 	ctcprot = nm_setting_wired_get_s390_option_by_key (s_wired, "ctcprot");
 	if (ctcprot)
-		svSetValueString (ifcfg, "CTCPROT", ctcprot);
+		svSetValueStr (ifcfg, "CTCPROT", ctcprot);
 
 	svUnsetValue (ifcfg, "OPTIONS");
 	num_opts = nm_setting_wired_get_num_s390_options (s_wired);
@@ -1102,7 +1071,7 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 			g_string_append_printf (str, "%s=%s", s390_key, s390_val);
 		}
 		if (str->len)
-			svSetValueString (ifcfg, "OPTIONS", str->str);
+			svSetValueStr (ifcfg, "OPTIONS", str->str);
 		g_string_free (str, TRUE);
 	}
 
@@ -1166,12 +1135,12 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 			g_string_append_printf (str, "s sopass %s", wol_password);
 	}
 	if (str) {
-		svSetValueString (ifcfg, "ETHTOOL_OPTS", str->str);
+		svSetValueStr (ifcfg, "ETHTOOL_OPTS", str->str);
 		g_string_free (str, TRUE);
 	}
 	/* End ETHTOOL_OPT stuffing */
 
-	svSetValueString (ifcfg, "TYPE", TYPE_ETHERNET);
+	svSetValueStr (ifcfg, "TYPE", TYPE_ETHERNET);
 
 	return TRUE;
 }
@@ -1213,18 +1182,18 @@ write_wired_for_virtual (NMConnection *connection, shvarFile *ifcfg)
 		has_wired = TRUE;
 
 		device_mac = nm_setting_wired_get_mac_address (s_wired);
-		svSetValueString (ifcfg, "HWADDR", device_mac);
+		svSetValueStr (ifcfg, "HWADDR", device_mac);
 
 		cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-		svSetValueString (ifcfg, "MACADDR", cloned_mac);
+		svSetValueStr (ifcfg, "MACADDR", cloned_mac);
 
-		svSetValueString (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
-		                  nm_setting_wired_get_generate_mac_address_mask (s_wired));
+		svSetValueStr (ifcfg, "GENERATE_MAC_ADDRESS_MASK",
+		               nm_setting_wired_get_generate_mac_address_mask (s_wired));
 
 		mtu = nm_setting_wired_get_mtu (s_wired);
 		if (mtu) {
 			tmp = g_strdup_printf ("%u", mtu);
-			svSetValueString (ifcfg, "MTU", tmp);
+			svSetValueStr (ifcfg, "MTU", tmp);
 			g_free (tmp);
 		} else
 			svUnsetValue (ifcfg, "MTU");
@@ -1256,13 +1225,13 @@ write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 		return FALSE;
 	}
 
-	svSetValueString (ifcfg, "VLAN", "yes");
-	svSetValueString (ifcfg, "TYPE", TYPE_VLAN);
-	svSetValueString (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con));
-	svSetValueString (ifcfg, "PHYSDEV", nm_setting_vlan_get_parent (s_vlan));
+	svSetValueStr (ifcfg, "VLAN", "yes");
+	svSetValueStr (ifcfg, "TYPE", TYPE_VLAN);
+	svSetValueStr (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con));
+	svSetValueStr (ifcfg, "PHYSDEV", nm_setting_vlan_get_parent (s_vlan));
 
 	tmp = g_strdup_printf ("%d", nm_setting_vlan_get_id (s_vlan));
-	svSetValueString (ifcfg, "VLAN_ID", tmp);
+	svSetValueStr (ifcfg, "VLAN_ID", tmp);
 	g_free (tmp);
 
 	vlan_flags = nm_setting_vlan_get_flags (s_vlan);
@@ -1276,16 +1245,16 @@ write_vlan_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 	if (!NM_FLAGS_HAS (vlan_flags, NM_VLAN_FLAG_REORDER_HEADERS))
 		nm_utils_strbuf_append (&s_buf_ptr, &s_buf_len, "%sNO_REORDER_HDR", s_buf[0] ? "," : "");
 
-	svSetValueString (ifcfg, "VLAN_FLAGS", s_buf);
+	svSetValueStr (ifcfg, "VLAN_FLAGS", s_buf);
 
 	svSetValueBoolean (ifcfg, "MVRP", NM_FLAGS_HAS (vlan_flags, NM_VLAN_FLAG_MVRP));
 
 	tmp = vlan_priority_maplist_to_stringlist (s_vlan, NM_VLAN_INGRESS_MAP);
-	svSetValueString (ifcfg, "VLAN_INGRESS_PRIORITY_MAP", tmp);
+	svSetValueStr (ifcfg, "VLAN_INGRESS_PRIORITY_MAP", tmp);
 	g_free (tmp);
 
 	tmp = vlan_priority_maplist_to_stringlist (s_vlan, NM_VLAN_EGRESS_MAP);
-	svSetValueString (ifcfg, "VLAN_EGRESS_PRIORITY_MAP", tmp);
+	svSetValueStr (ifcfg, "VLAN_EGRESS_PRIORITY_MAP", tmp);
 	g_free (tmp);
 
 	svUnsetValue (ifcfg, "HWADDR");
@@ -1318,7 +1287,7 @@ write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wir
 		return FALSE;
 	}
 
-	svSetValueString (ifcfg, "DEVICE", iface);
+	svSetValueStr (ifcfg, "DEVICE", iface);
 	svUnsetValue (ifcfg, "BONDING_OPTS");
 
 	num_opts = nm_setting_bond_get_num_options (s_bond);
@@ -1338,13 +1307,13 @@ write_bonding_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wir
 		}
 
 		if (str->len)
-			svSetValueString (ifcfg, "BONDING_OPTS", str->str);
+			svSetValueStr (ifcfg, "BONDING_OPTS", str->str);
 
 		g_string_free (str, TRUE);
 	}
 
-	svSetValueString (ifcfg, "TYPE", TYPE_BOND);
-	svSetValueString (ifcfg, "BONDING_MASTER", "yes");
+	svSetValueStr (ifcfg, "TYPE", TYPE_BOND);
+	svSetValueStr (ifcfg, "BONDING_MASTER", "yes");
 
 	*wired = write_wired_for_virtual (connection, ifcfg);
 
@@ -1372,9 +1341,9 @@ write_team_setting (NMConnection *connection, shvarFile *ifcfg, gboolean *wired,
 		return FALSE;
 	}
 
-	svSetValueString (ifcfg, "DEVICE", iface);
+	svSetValueStr (ifcfg, "DEVICE", iface);
 	config = nm_setting_team_get_config (s_team);
-	svSetValueString (ifcfg, "TEAM_CONFIG", config);
+	svSetValueStr (ifcfg, "TEAM_CONFIG", config);
 
 	*wired = write_wired_for_virtual (connection, ifcfg);
 
@@ -1440,24 +1409,24 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error
 		return FALSE;
 	}
 
-	svSetValueString (ifcfg, "DEVICE", iface);
+	svSetValueStr (ifcfg, "DEVICE", iface);
 	svUnsetValue (ifcfg, "BRIDGING_OPTS");
 	svSetValueBoolean (ifcfg, "STP", FALSE);
 	svUnsetValue (ifcfg, "DELAY");
 
 	mac = nm_setting_bridge_get_mac_address (s_bridge);
-	svSetValueString (ifcfg, "MACADDR", mac);
+	svSetValueStr (ifcfg, "MACADDR", mac);
 
 	/* Bridge options */
 	opts = g_string_sized_new (32);
 
 	if (nm_setting_bridge_get_stp (s_bridge)) {
-		svSetValueString (ifcfg, "STP", "yes");
+		svSetValueStr (ifcfg, "STP", "yes");
 
 		i = nm_setting_bridge_get_forward_delay (s_bridge);
 		if (i != get_setting_default_uint (NM_SETTING (s_bridge), NM_SETTING_BRIDGE_FORWARD_DELAY)) {
 			s = g_strdup_printf ("%u", i);
-			svSetValueString (ifcfg, "DELAY", s);
+			svSetValueStr (ifcfg, "DELAY", s);
 			g_free (s);
 		}
 
@@ -1493,10 +1462,10 @@ write_bridge_setting (NMConnection *connection, shvarFile *ifcfg, GError **error
 	}
 
 	if (opts->len)
-		svSetValueString (ifcfg, "BRIDGING_OPTS", opts->str);
+		svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str);
 	g_string_free (opts, TRUE);
 
-	svSetValueString (ifcfg, "TYPE", TYPE_BRIDGE);
+	svSetValueStr (ifcfg, "TYPE", TYPE_BRIDGE);
 
 	return TRUE;
 }
@@ -1535,7 +1504,7 @@ write_bridge_port_setting (NMConnection *connection, shvarFile *ifcfg, GError **
 	}
 
 	if (opts->len)
-		svSetValueString (ifcfg, "BRIDGING_OPTS", opts->str);
+		svSetValueStr (ifcfg, "BRIDGING_OPTS", opts->str);
 	g_string_free (opts, TRUE);
 
 	return TRUE;
@@ -1552,7 +1521,7 @@ write_team_port_setting (NMConnection *connection, shvarFile *ifcfg, GError **er
 		return TRUE;
 
 	config = nm_setting_team_port_get_config (s_port);
-	svSetValueString (ifcfg, "TEAM_PORT_CONFIG", config);
+	svSetValueStr (ifcfg, "TEAM_PORT_CONFIG", config);
 
 	return TRUE;
 }
@@ -1563,13 +1532,13 @@ write_dcb_flags (shvarFile *ifcfg, const char *tag, NMSettingDcbFlags flags)
 	char prop[NM_STRLEN ("DCB_xxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyy")];
 
 	nm_sprintf_buf (prop, "DCB_%s_ENABLE", tag);
-	svSetValueString (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ENABLE) ? "yes" : NULL);
+	svSetValueStr (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ENABLE) ? "yes" : NULL);
 
 	nm_sprintf_buf (prop, "DCB_%s_ADVERTISE", tag);
-	svSetValueString (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ADVERTISE) ? "yes" : NULL);
+	svSetValueStr (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_ADVERTISE) ? "yes" : NULL);
 
 	nm_sprintf_buf (prop, "DCB_%s_WILLING", tag);
-	svSetValueString (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_WILLING) ? "yes" : NULL);
+	svSetValueStr (ifcfg, prop, (flags & NM_SETTING_DCB_FLAG_WILLING) ? "yes" : NULL);
 }
 
 static void
@@ -1609,7 +1578,7 @@ write_dcb_bool_array (shvarFile *ifcfg,
 	str[8] = 0;
 	for (i = 0; i < 8; i++)
 		str[i] = get_func (s_dcb, i) ? '1' : '0';
-	svSetValueString (ifcfg, key, str);
+	svSetValueStr (ifcfg, key, str);
 }
 
 typedef guint (*DcbGetUintFunc) (NMSettingDcb *, guint);
@@ -1639,7 +1608,7 @@ write_dcb_uint_array (shvarFile *ifcfg,
 		else
 			g_assert_not_reached ();
 	}
-	svSetValueString (ifcfg, key, str);
+	svSetValueStr (ifcfg, key, str);
 }
 
 static void
@@ -1663,7 +1632,7 @@ write_dcb_percent_array (shvarFile *ifcfg,
 			g_string_append_c (str, ',');
 		g_string_append_printf (str, "%d", get_func (s_dcb, i));
 	}
-	svSetValueString (ifcfg, key, str->str);
+	svSetValueStr (ifcfg, key, str->str);
 	g_string_free (str, TRUE);
 }
 
@@ -1707,13 +1676,13 @@ write_dcb_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		return TRUE;
 	}
 
-	svSetValueString (ifcfg, "DCB", "yes");
+	svSetValueStr (ifcfg, "DCB", "yes");
 
 	write_dcb_app (ifcfg, "APP_FCOE",
 	               nm_setting_dcb_get_app_fcoe_flags (s_dcb),
 	               nm_setting_dcb_get_app_fcoe_priority (s_dcb));
 	if (nm_setting_dcb_get_app_fcoe_flags (s_dcb) & NM_SETTING_DCB_FLAG_ENABLE)
-		svSetValueString (ifcfg, KEY_DCB_APP_FCOE_MODE, nm_setting_dcb_get_app_fcoe_mode (s_dcb));
+		svSetValueStr (ifcfg, KEY_DCB_APP_FCOE_MODE, nm_setting_dcb_get_app_fcoe_mode (s_dcb));
 	else
 		svUnsetValue (ifcfg, KEY_DCB_APP_FCOE_MODE);
 
@@ -1749,21 +1718,21 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	char *tmp;
 	gint i_int;
 
-	svSetValueString (ifcfg, "NAME", nm_setting_connection_get_id (s_con));
-	svSetValueString (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con));
-	svSetValueString (ifcfg, "STABLE_ID", nm_setting_connection_get_stable_id (s_con));
-	svSetValueString (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con));
+	svSetValueStr (ifcfg, "NAME", nm_setting_connection_get_id (s_con));
+	svSetValueStr (ifcfg, "UUID", nm_setting_connection_get_uuid (s_con));
+	svSetValueStr (ifcfg, "STABLE_ID", nm_setting_connection_get_stable_id (s_con));
+	svSetValueStr (ifcfg, "DEVICE", nm_setting_connection_get_interface_name (s_con));
 	svSetValueBoolean (ifcfg, "ONBOOT", nm_setting_connection_get_autoconnect (s_con));
 
 	i_int = nm_setting_connection_get_autoconnect_priority (s_con);
 	tmp = i_int != NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT
 	      ? g_strdup_printf ("%d", i_int) : NULL;
-	svSetValueString (ifcfg, "AUTOCONNECT_PRIORITY", tmp);
+	svSetValueStr (ifcfg, "AUTOCONNECT_PRIORITY", tmp);
 	g_free (tmp);
 
 	i_int = nm_setting_connection_get_autoconnect_retries (s_con);
 	tmp = i_int != -1 ? g_strdup_printf ("%d", i_int) : NULL;
-	svSetValueString (ifcfg, "AUTOCONNECT_RETRIES", tmp);
+	svSetValueStr (ifcfg, "AUTOCONNECT_RETRIES", tmp);
 	g_free (tmp);
 
 	/* Only save the value for master connections */
@@ -1773,9 +1742,9 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	    || !g_strcmp0 (type, NM_SETTING_BRIDGE_SETTING_NAME)) {
 		NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
 		autoconnect_slaves = nm_setting_connection_get_autoconnect_slaves (s_con);
-		svSetValueString (ifcfg, "AUTOCONNECT_SLAVES",
-		                  autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES ? "yes" :
-		                  autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO ? "no" : NULL);
+		svSetValueStr (ifcfg, "AUTOCONNECT_SLAVES",
+		               autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_YES ? "yes" :
+		               autoconnect_slaves == NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_NO ? "no" : NULL);
 	} else
 		svUnsetValue (ifcfg, "AUTOCONNECT_SLAVES");
 
@@ -1789,7 +1758,7 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	default:
 		tmp = NULL;
 	}
-	svSetValueString (ifcfg, "LLDP", tmp);
+	svSetValueStr (ifcfg, "LLDP", tmp);
 
 	/* Permissions */
 	svUnsetValue (ifcfg, "USERS");
@@ -1809,19 +1778,19 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 			if (nm_setting_connection_get_permission (s_con, i, NULL, &puser, NULL))
 				g_string_append (str, puser);
 		}
-		svSetValueString (ifcfg, "USERS", str->str);
+		svSetValueStr (ifcfg, "USERS", str->str);
 		g_string_free (str, TRUE);
 	}
 
-	svSetValueString (ifcfg, "ZONE", nm_setting_connection_get_zone(s_con));
+	svSetValueStr (ifcfg, "ZONE", nm_setting_connection_get_zone (s_con));
 
-	svSetValueString (ifcfg, "MASTER_UUID", NULL);
-	svSetValueString (ifcfg, "MASTER", NULL);
-	svSetValueString (ifcfg, "SLAVE", NULL);
-	svSetValueString (ifcfg, "BRIDGE_UUID", NULL);
-	svSetValueString (ifcfg, "BRIDGE", NULL);
-	svSetValueString (ifcfg, "TEAM_MASTER_UUID", NULL);
-	svSetValueString (ifcfg, "TEAM_MASTER", NULL);
+	svSetValueStr (ifcfg, "MASTER_UUID", NULL);
+	svSetValueStr (ifcfg, "MASTER", NULL);
+	svSetValueStr (ifcfg, "SLAVE", NULL);
+	svSetValueStr (ifcfg, "BRIDGE_UUID", NULL);
+	svSetValueStr (ifcfg, "BRIDGE", NULL);
+	svSetValueStr (ifcfg, "TEAM_MASTER_UUID", NULL);
+	svSetValueStr (ifcfg, "TEAM_MASTER", NULL);
 
 	master = nm_setting_connection_get_master (s_con);
 	if (master) {
@@ -1838,23 +1807,23 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 		}
 
 		if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BOND_SETTING_NAME)) {
-			svSetValueString (ifcfg, "MASTER_UUID", master);
-			svSetValueString (ifcfg, "MASTER", master_iface);
-			svSetValueString (ifcfg, "SLAVE", "yes");
+			svSetValueStr (ifcfg, "MASTER_UUID", master);
+			svSetValueStr (ifcfg, "MASTER", master_iface);
+			svSetValueStr (ifcfg, "SLAVE", "yes");
 		} else if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_BRIDGE_SETTING_NAME)) {
-			svSetValueString (ifcfg, "BRIDGE_UUID", master);
-			svSetValueString (ifcfg, "BRIDGE", master_iface);
+			svSetValueStr (ifcfg, "BRIDGE_UUID", master);
+			svSetValueStr (ifcfg, "BRIDGE", master_iface);
 		} else if (nm_setting_connection_is_slave_type (s_con, NM_SETTING_TEAM_SETTING_NAME)) {
-			svSetValueString (ifcfg, "TEAM_MASTER_UUID", master);
-			svSetValueString (ifcfg, "TEAM_MASTER", master_iface);
+			svSetValueStr (ifcfg, "TEAM_MASTER_UUID", master);
+			svSetValueStr (ifcfg, "TEAM_MASTER", master_iface);
 			svUnsetValue (ifcfg, "TYPE");
 		}
 	}
 
 	if (nm_streq0 (type, NM_SETTING_TEAM_SETTING_NAME))
-		svSetValueString (ifcfg, "DEVICETYPE", TYPE_TEAM);
+		svSetValueStr (ifcfg, "DEVICETYPE", TYPE_TEAM);
 	else if (master_iface && nm_setting_connection_is_slave_type (s_con, NM_SETTING_TEAM_SETTING_NAME))
-		svSetValueString (ifcfg, "DEVICETYPE", TYPE_TEAM_PORT);
+		svSetValueStr (ifcfg, "DEVICETYPE", TYPE_TEAM_PORT);
 	else
 		svUnsetValue (ifcfg, "DEVICETYPE");
 
@@ -1876,40 +1845,94 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 			if ((uuid = nm_setting_connection_get_secondary (s_con, i)) != NULL)
 				g_string_append (str, uuid);
 		}
-		svSetValueString (ifcfg, "SECONDARY_UUIDS", str->str);
+		svSetValueStr (ifcfg, "SECONDARY_UUIDS", str->str);
 		g_string_free (str, TRUE);
 	}
 
 	svUnsetValue (ifcfg, "GATEWAY_PING_TIMEOUT");
 	if (nm_setting_connection_get_gateway_ping_timeout (s_con)) {
 		tmp = g_strdup_printf ("%" G_GUINT32_FORMAT, nm_setting_connection_get_gateway_ping_timeout (s_con));
-		svSetValueString (ifcfg, "GATEWAY_PING_TIMEOUT", tmp);
+		svSetValueStr (ifcfg, "GATEWAY_PING_TIMEOUT", tmp);
 		g_free (tmp);
 	}
 
 	switch (nm_setting_connection_get_metered (s_con)) {
 	case NM_METERED_YES:
-		svSetValueString (ifcfg, "CONNECTION_METERED", "yes");
+		svSetValueStr (ifcfg, "CONNECTION_METERED", "yes");
 		break;
 	case NM_METERED_NO:
-		svSetValueString (ifcfg, "CONNECTION_METERED", "no");
+		svSetValueStr (ifcfg, "CONNECTION_METERED", "no");
 		break;
 	default:
 		svUnsetValue (ifcfg, "CONNECTION_METERED");
 	}
 }
 
+static char *
+get_route_attributes_string (NMIPRoute *route, int family)
+{
+	gs_strfreev char **names = NULL;
+	GVariant *attr, *lock;
+	GString *str;
+	int i;
+
+	names = nm_ip_route_get_attribute_names (route);
+	if (!names || !names[0])
+		return NULL;
+
+	str = g_string_new ("");
+
+	for (i = 0; names[i]; i++) {
+		attr = nm_ip_route_get_attribute (route, names[i]);
+
+		if (!nm_ip_route_attribute_validate (names[i], attr, family, NULL, NULL))
+			continue;
+
+		if (NM_IN_STRSET (names[i], NM_IP_ROUTE_ATTRIBUTE_WINDOW,
+		                            NM_IP_ROUTE_ATTRIBUTE_CWND,
+		                            NM_IP_ROUTE_ATTRIBUTE_INITCWND,
+		                            NM_IP_ROUTE_ATTRIBUTE_INITRWND,
+		                            NM_IP_ROUTE_ATTRIBUTE_MTU)) {
+			char lock_name[256];
+
+			nm_sprintf_buf (lock_name, "lock-%s", names[i]);
+			lock = nm_ip_route_get_attribute (route, lock_name);
+
+			g_string_append_printf (str,
+			                        "%s %s%u",
+			                        names[i],
+			                        (lock && g_variant_get_boolean (lock)) ? "lock " : "",
+			                        g_variant_get_uint32 (attr));
+		} else if (strstr (names[i], "lock-")) {
+			/* handled above */
+		} else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_TOS)) {
+			g_string_append_printf (str, "%s %u", names[i], (unsigned) g_variant_get_byte (attr));
+		} else if (   nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC)
+		           || nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_FROM)) {
+			char *arg = nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC) ? "src" : "from";
+
+			g_string_append_printf (str, "%s %s", arg, g_variant_get_string (attr, NULL));
+		} else {
+			_LOGW ("unknown route option '%s'", names[i]);
+			continue;
+		}
+		if (names[i + 1])
+			g_string_append_c (str, ' ');
+	}
+
+	return g_string_free (str, FALSE);
+}
+
 static gboolean
 write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError **error)
 {
 	const char *dest, *next_hop;
 	char **route_items;
-	char *route_contents;
+	gs_free char *route_contents = NULL;
 	NMIPRoute *route;
 	guint32 prefix;
 	gint64 metric;
 	guint32 i, num;
-	gboolean success = FALSE;
 
 	g_return_val_if_fail (filename != NULL, FALSE);
 	g_return_val_if_fail (s_ip4 != NULL, FALSE);
@@ -1922,8 +1945,10 @@ write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError
 		return TRUE;
 	}
 
-	route_items = g_malloc0 (sizeof (char*) * (num + 1));
+	route_items = g_malloc0 (sizeof (char *) * (num + 1));
 	for (i = 0; i < num; i++) {
+		gs_free char *options = NULL;
+
 		route = nm_setting_ip_config_get_route (s_ip4, i);
 
 		dest = nm_ip_route_get_dest (route);
@@ -1931,10 +1956,19 @@ write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError
 		next_hop = nm_ip_route_get_next_hop (route);
 		metric = nm_ip_route_get_metric (route);
 
-		if (metric == -1)
-			route_items[i] = g_strdup_printf ("%s/%u via %s\n", dest, prefix, next_hop);
-		else
-			route_items[i] = g_strdup_printf ("%s/%u via %s metric %u\n", dest, prefix, next_hop, (guint32) metric);
+		options = get_route_attributes_string (route, AF_INET);
+
+		if (metric == -1) {
+			route_items[i] = g_strdup_printf ("%s/%u via %s%s%s\n",
+			                                  dest, prefix, next_hop,
+			                                  options ? " " : "",
+			                                  options ?: "");
+		} else {
+			route_items[i] = g_strdup_printf ("%s/%u via %s metric %u%s%s\n",
+			                                  dest, prefix, next_hop, (guint32) metric,
+			                                  options ? " " : "",
+			                                  options ?: "");
+		}
 	}
 	route_items[num] = NULL;
 	route_contents = g_strjoinv (NULL, route_items);
@@ -1943,15 +1977,10 @@ write_route_file_legacy (const char *filename, NMSettingIPConfig *s_ip4, GError
 	if (!g_file_set_contents (filename, route_contents, -1, NULL)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Writing route file '%s' failed", filename);
-		goto error;
+		return FALSE;
 	}
 
-	success = TRUE;
-
-error:
-	g_free (route_contents);
-
-	return success;
+	return TRUE;
 }
 
 static gboolean
@@ -1972,19 +2001,19 @@ write_proxy_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	method = nm_setting_proxy_get_method (s_proxy);
 	switch (method) {
 	case NM_SETTING_PROXY_METHOD_AUTO:
-		svSetValueString (ifcfg, "PROXY_METHOD", "auto");
+		svSetValueStr (ifcfg, "PROXY_METHOD", "auto");
 
 		pac_url = nm_setting_proxy_get_pac_url (s_proxy);
 		if (pac_url)
-			svSetValueString (ifcfg, "PAC_URL", pac_url);
+			svSetValueStr (ifcfg, "PAC_URL", pac_url);
 
 		pac_script = nm_setting_proxy_get_pac_script (s_proxy);
 		if (pac_script)
-			svSetValueString (ifcfg, "PAC_SCRIPT", pac_script);
+			svSetValueStr (ifcfg, "PAC_SCRIPT", pac_script);
 
 		break;
 	case NM_SETTING_PROXY_METHOD_NONE:
-		svSetValueString (ifcfg, "PROXY_METHOD", "none");
+		svSetValueStr (ifcfg, "PROXY_METHOD", "none");
 		break;
 	}
 
@@ -1994,14 +2023,51 @@ write_proxy_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 }
 
 static gboolean
+write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
+{
+	NMSettingUser *s_user;
+	guint i, len;
+	const char *const*keys;
+
+	s_user = NM_SETTING_USER (nm_connection_get_setting (connection, NM_TYPE_SETTING_USER));
+
+	svUnsetValuesWithPrefix (ifcfg, "NM_USER_");
+
+	if (!s_user)
+		return TRUE;
+
+	keys = nm_setting_user_get_keys (s_user, &len);
+	if (len) {
+		nm_auto_free_gstring GString *str = g_string_sized_new (100);
+
+		for (i = 0; i < len; i++) {
+			const char *key = keys[i];
+
+			g_string_set_size (str, 0);
+			g_string_append (str, "NM_USER_");
+			nms_ifcfg_rh_utils_user_key_encode (key, str);
+			svSetValue (ifcfg,
+			            str->str,
+			            nm_setting_user_get_data (s_user, key));
+		}
+	}
+
+	return TRUE;
+}
+
+static gboolean
 write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 {
 	NMSettingIPConfig *s_ip4;
 	const char *value;
-	char *addr_key, *prefix_key, *netmask_key, *gw_key, *metric_key, *tmp;
+	char *tmp;
+	char addr_key[64];
+	char prefix_key[64];
+	char netmask_key[64];
+	char gw_key[64];
 	char *route_path = NULL;
-	gint32 j;
-	guint32 i, n, num;
+	gint j;
+	guint i, num, n;
 	gint64 route_metric;
 	gint priority;
 	int timeout;
@@ -2041,26 +2107,21 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		svUnsetValue (ifcfg, "BOOTPROTO");
 		for (j = -1; j < 256; j++) {
 			if (j == -1) {
-				addr_key = g_strdup ("IPADDR");
-				prefix_key = g_strdup ("PREFIX");
-				netmask_key = g_strdup ("NETMASK");
-				gw_key = g_strdup ("GATEWAY");
+				nm_sprintf_buf (addr_key, "IPADDR");
+				nm_sprintf_buf (prefix_key, "PREFIX");
+				nm_sprintf_buf (netmask_key, "NETMASK");
+				nm_sprintf_buf (gw_key, "GATEWAY");
 			} else {
-				addr_key = g_strdup_printf ("IPADDR%d", j);
-				prefix_key = g_strdup_printf ("PREFIX%d", j);
-				netmask_key = g_strdup_printf ("NETMASK%d", j);
-				gw_key = g_strdup_printf ("GATEWAY%d", j);
+				nm_sprintf_buf (addr_key, "IPADDR%d", (guint) j);
+				nm_sprintf_buf (prefix_key, "PREFIX%u",  (guint) j);
+				nm_sprintf_buf (netmask_key, "NETMASK%u",  (guint) j);
+				nm_sprintf_buf (gw_key, "GATEWAY%u",  (guint) j);
 			}
 
 			svUnsetValue (ifcfg, addr_key);
 			svUnsetValue (ifcfg, prefix_key);
 			svUnsetValue (ifcfg, netmask_key);
 			svUnsetValue (ifcfg, gw_key);
-
-			g_free (addr_key);
-			g_free (prefix_key);
-			g_free (netmask_key);
-			g_free (gw_key);
 		}
 
 		route_path = utils_get_route_path (svFileGetName (ifcfg));
@@ -2069,14 +2130,20 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		return TRUE;
 	}
 
+	num = nm_setting_ip_config_get_num_addresses (s_ip4);
+
 	if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO))
-		svSetValueString (ifcfg, "BOOTPROTO", "dhcp");
-	else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
-		svSetValueString (ifcfg, "BOOTPROTO", "none");
-	else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
-		svSetValueString (ifcfg, "BOOTPROTO", "autoip");
+		svSetValueStr (ifcfg, "BOOTPROTO", "dhcp");
+	else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+		/* Preserve the archaic form of "static" if there actually
+		 * is static configuration. */
+		if (g_strcmp0 (svGetValue (ifcfg, "BOOTPROTO", &tmp), "static") || !num)
+			svSetValueStr (ifcfg, "BOOTPROTO", "none");
+		g_free (tmp);
+	} else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL))
+		svSetValueStr (ifcfg, "BOOTPROTO", "autoip");
 	else if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
-		svSetValueString (ifcfg, "BOOTPROTO", "shared");
+		svSetValueStr (ifcfg, "BOOTPROTO", "shared");
 
 	/* Clear out un-numbered IP address fields */
 	svUnsetValue (ifcfg, "IPADDR");
@@ -2092,9 +2159,9 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	/* Write out IPADDR<n>, PREFIX<n>, GATEWAY<n> for current IP addresses
 	 * without labels. Unset obsolete NETMASK<n>.
 	 */
-	num = nm_setting_ip_config_get_num_addresses (s_ip4);
 	for (i = n = 0; i < num; i++) {
 		NMIPAddress *addr;
+		guint prefix;
 
 		addr = nm_setting_ip_config_get_address (s_ip4, i);
 
@@ -2112,66 +2179,62 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 			 * See https://bugzilla.redhat.com/show_bug.cgi?id=771673
 			 * and https://bugzilla.redhat.com/show_bug.cgi?id=1105770
 			 */
-			addr_key = g_strdup ("IPADDR");
-			prefix_key = g_strdup ("PREFIX");
-			netmask_key = g_strdup ("NETMASK");
-			gw_key = g_strdup ("GATEWAY");
+			nm_sprintf_buf (addr_key, "IPADDR");
+			nm_sprintf_buf (prefix_key, "PREFIX");
+			nm_sprintf_buf (netmask_key, "NETMASK");
+			nm_sprintf_buf (gw_key, "GATEWAY");
 		} else {
-			addr_key = g_strdup_printf ("IPADDR%d", n);
-			prefix_key = g_strdup_printf ("PREFIX%d", n);
-			netmask_key = g_strdup_printf ("NETMASK%d", n);
-			gw_key = g_strdup_printf ("GATEWAY%d", n);
+			nm_sprintf_buf (addr_key, "IPADDR%u", n);
+			nm_sprintf_buf (prefix_key, "PREFIX%u", n);
+			nm_sprintf_buf (netmask_key, "NETMASK%u", n);
+			nm_sprintf_buf (gw_key, "GATEWAY%u", n);
 		}
 
-		svSetValueString (ifcfg, addr_key, nm_ip_address_get_address (addr));
+		svSetValueStr (ifcfg, addr_key, nm_ip_address_get_address (addr));
 
-		tmp = g_strdup_printf ("%u", nm_ip_address_get_prefix (addr));
-		svSetValueString (ifcfg, prefix_key, tmp);
+		prefix = nm_ip_address_get_prefix (addr);
+		tmp = g_strdup_printf ("%u", prefix);
+		svSetValueStr (ifcfg, prefix_key, tmp);
 		g_free (tmp);
 
-		svUnsetValue (ifcfg, netmask_key);
-		svUnsetValue (ifcfg, gw_key);
+		/* If the legacy "NETMASK" is present, keep it. */
+		if (svGetValue (ifcfg, netmask_key, &tmp)) {
+			char buf[INET_ADDRSTRLEN];
 
-		g_free (addr_key);
-		g_free (prefix_key);
-		g_free (netmask_key);
-		g_free (gw_key);
+			g_free (tmp);
+			svSetValueStr (ifcfg, netmask_key, nm_utils_inet4_ntop (prefix, buf));
+		}
+
+		svUnsetValue (ifcfg, gw_key);
 		n++;
 	}
 
 	/* Clear remaining IPADDR<n..255>, etc */
-	for (; n < 256; n++) {
-		addr_key = g_strdup_printf ("IPADDR%d", n);
-		prefix_key = g_strdup_printf ("PREFIX%d", n);
-		netmask_key = g_strdup_printf ("NETMASK%d", n);
-		gw_key = g_strdup_printf ("GATEWAY%d", n);
+	for (i = n; i < 256; i++) {
+		nm_sprintf_buf (addr_key, "IPADDR%u", i);
+		nm_sprintf_buf (prefix_key, "PREFIX%u", i);
+		nm_sprintf_buf (netmask_key, "NETMASK%u", i);
+		nm_sprintf_buf (gw_key, "GATEWAY%u", i);
 
 		svUnsetValue (ifcfg, addr_key);
 		svUnsetValue (ifcfg, prefix_key);
 		svUnsetValue (ifcfg, netmask_key);
 		svUnsetValue (ifcfg, gw_key);
-
-		g_free (addr_key);
-		g_free (prefix_key);
-		g_free (netmask_key);
-		g_free (gw_key);
 	}
 
-	svSetValueString (ifcfg, "GATEWAY", nm_setting_ip_config_get_gateway (s_ip4));
+	svSetValueStr (ifcfg, "GATEWAY", nm_setting_ip_config_get_gateway (s_ip4));
 
 	num = nm_setting_ip_config_get_num_dns (s_ip4);
 	for (i = 0; i < 254; i++) {
 		const char *dns;
 
-		addr_key = g_strdup_printf ("DNS%d", i + 1);
-
+		nm_sprintf_buf (addr_key, "DNS%u", i + 1);
 		if (i >= num)
 			svUnsetValue (ifcfg, addr_key);
 		else {
 			dns = nm_setting_ip_config_get_dns (s_ip4, i);
-			svSetValueString (ifcfg, addr_key, dns);
+			svSetValueStr (ifcfg, addr_key, dns);
 		}
-		g_free (addr_key);
 	}
 
 	num = nm_setting_ip_config_get_num_dns_searches (s_ip4);
@@ -2182,7 +2245,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 				g_string_append_c (searches, ' ');
 			g_string_append (searches, nm_setting_ip_config_get_dns_search (s_ip4, i));
 		}
-		svSetValueString (ifcfg, "DOMAIN", searches->str);
+		svSetValueStr (ifcfg, "DOMAIN", searches->str);
 		g_string_free (searches, TRUE);
 	} else
 		svUnsetValue (ifcfg, "DOMAIN");
@@ -2191,37 +2254,37 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	svSetValueBoolean (ifcfg, "DEFROUTE", !nm_setting_ip_config_get_never_default (s_ip4));
 
 	/* Missing PEERDNS means TRUE, so write it only when is FALSE */
-	svSetValueString (ifcfg, "PEERDNS",
-		nm_setting_ip_config_get_ignore_auto_dns (s_ip4) ? "no" : NULL);
+	svSetValueStr (ifcfg, "PEERDNS",
+	               nm_setting_ip_config_get_ignore_auto_dns (s_ip4) ? "no" : NULL);
 	/* Missing PEERROUTES means TRUE, so write it only when is FALSE */
-	svSetValueString (ifcfg, "PEERROUTES",
-		nm_setting_ip_config_get_ignore_auto_routes (s_ip4) ? "no" : NULL);
+	svSetValueStr (ifcfg, "PEERROUTES",
+	               nm_setting_ip_config_get_ignore_auto_routes (s_ip4) ? "no" : NULL);
 
 	value = nm_setting_ip_config_get_dhcp_hostname (s_ip4);
-	svSetValueString (ifcfg, "DHCP_HOSTNAME", value);
+	svSetValueStr (ifcfg, "DHCP_HOSTNAME", value);
 
 	value = nm_setting_ip4_config_get_dhcp_fqdn (NM_SETTING_IP4_CONFIG (s_ip4));
-	svSetValueString (ifcfg, "DHCP_FQDN", value);
+	svSetValueStr (ifcfg, "DHCP_FQDN", value);
 
 	/* Missing DHCP_SEND_HOSTNAME means TRUE, and we prefer not write it explicitly
 	 * in that case, because it is NM-specific variable
 	 */
-	svSetValueString (ifcfg, "DHCP_SEND_HOSTNAME",
-			  nm_setting_ip_config_get_dhcp_send_hostname (s_ip4) ? NULL : "no");
+	svSetValueStr (ifcfg, "DHCP_SEND_HOSTNAME",
+	               nm_setting_ip_config_get_dhcp_send_hostname (s_ip4) ? NULL : "no");
 
 	value = nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4));
-	svSetValueString (ifcfg, "DHCP_CLIENT_ID", value);
+	svSetValueStr (ifcfg, "DHCP_CLIENT_ID", value);
 
 	timeout = nm_setting_ip_config_get_dhcp_timeout (s_ip4);
 	tmp = timeout ? g_strdup_printf ("%d", timeout) : NULL;
-	svSetValueString (ifcfg, "IPV4_DHCP_TIMEOUT", tmp);
+	svSetValueStr (ifcfg, "IPV4_DHCP_TIMEOUT", tmp);
 	g_free (tmp);
 
 	svSetValueBoolean (ifcfg, "IPV4_FAILURE_FATAL", !nm_setting_ip_config_get_may_fail (s_ip4));
 
 	route_metric = nm_setting_ip_config_get_route_metric (s_ip4);
 	tmp = route_metric != -1 ? g_strdup_printf ("%"G_GINT64_FORMAT, route_metric) : NULL;
-	svSetValueString (ifcfg, "IPV4_ROUTE_METRIC", tmp);
+	svSetValueStr (ifcfg, "IPV4_ROUTE_METRIC", tmp);
 	g_free (tmp);
 
 	/* Static routes - route-<name> file */
@@ -2250,28 +2313,34 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 			NMIPRoute *route;
 			guint32 netmask;
 			gint64 metric;
+			char metric_key[64];
+			char options_key[64];
 
-			addr_key = g_strdup_printf ("ADDRESS%d", i);
-			netmask_key = g_strdup_printf ("NETMASK%d", i);
-			gw_key = g_strdup_printf ("GATEWAY%d", i);
-			metric_key = g_strdup_printf ("METRIC%d", i);
+			nm_sprintf_buf (addr_key, "ADDRESS%u", i);
+			nm_sprintf_buf (netmask_key, "NETMASK%u", i);
+			nm_sprintf_buf (gw_key, "GATEWAY%u", i);
+			nm_sprintf_buf (metric_key, "METRIC%u", i);
+			nm_sprintf_buf (options_key, "OPTIONS%u", i);
 
 			if (i >= num) {
 				svUnsetValue (routefile, addr_key);
 				svUnsetValue (routefile, netmask_key);
 				svUnsetValue (routefile, gw_key);
 				svUnsetValue (routefile, metric_key);
+				svUnsetValue (routefile, options_key);
 			} else {
+				gs_free char *options = NULL;
+
 				route = nm_setting_ip_config_get_route (s_ip4, i);
 
-				svSetValueString (routefile, addr_key, nm_ip_route_get_dest (route));
+				svSetValueStr (routefile, addr_key, nm_ip_route_get_dest (route));
 
 				memset (buf, 0, sizeof (buf));
 				netmask = nm_utils_ip4_prefix_to_netmask (nm_ip_route_get_prefix (route));
 				inet_ntop (AF_INET, (const void *) &netmask, &buf[0], sizeof (buf));
-				svSetValueString (routefile, netmask_key, &buf[0]);
+				svSetValueStr (routefile, netmask_key, &buf[0]);
 
-				svSetValueString (routefile, gw_key, nm_ip_route_get_next_hop (route));
+				svSetValueStr (routefile, gw_key, nm_ip_route_get_next_hop (route));
 
 				memset (buf, 0, sizeof (buf));
 				metric = nm_ip_route_get_metric (route);
@@ -2279,15 +2348,14 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 					svUnsetValue (routefile, metric_key);
 				else {
 					tmp = g_strdup_printf ("%u", (guint32) metric);
-					svSetValueString (routefile, metric_key, tmp);
+					svSetValueStr (routefile, metric_key, tmp);
 					g_free (tmp);
 				}
-			}
 
-			g_free (addr_key);
-			g_free (netmask_key);
-			g_free (gw_key);
-			g_free (metric_key);
+				options = get_route_attributes_string (route, AF_INET);
+				if (options)
+					svSetValueStr (routefile, options_key, options);
+			}
 		}
 		if (!svWriteFile (routefile, 0644, error)) {
 			svCloseFile (routefile);
@@ -2305,7 +2373,7 @@ write_ip4_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	if (timeout < 0)
 		svUnsetValue (ifcfg, "ARPING_WAIT");
 	else if (timeout == 0)
-		svSetValueString (ifcfg, "ARPING_WAIT", "0");
+		svSetValueStr (ifcfg, "ARPING_WAIT", "0");
 	else {
 		/* Round the value up to next integer */
 		svSetValueInt64 (ifcfg, "ARPING_WAIT", (timeout - 1) / 1000 + 1);
@@ -2325,7 +2393,7 @@ write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path)
 {
 	NMSettingIPConfig *s_ip4;
 	gs_free char *base_ifcfg_dir = NULL, *base_ifcfg_name = NULL;
-	const char*base_name;
+	const char *base_name;
 	int i, num, base_ifcfg_name_len, base_name_len;
 	GDir *dir;
 
@@ -2392,13 +2460,13 @@ write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path)
 		ifcfg = svCreateFile (path);
 		g_free (path);
 
-		svSetValueString (ifcfg, "DEVICE", label);
+		svSetValueStr (ifcfg, "DEVICE", label);
 
 		addr = nm_setting_ip_config_get_address (s_ip4, i);
-		svSetValueString (ifcfg, "IPADDR", nm_ip_address_get_address (addr));
+		svSetValueStr (ifcfg, "IPADDR", nm_ip_address_get_address (addr));
 
 		tmp = g_strdup_printf ("%u", nm_ip_address_get_prefix (addr));
-		svSetValueString (ifcfg, "PREFIX", tmp);
+		svSetValueStr (ifcfg, "PREFIX", tmp);
 		g_free (tmp);
 
 		svWriteFile (ifcfg, 0644, NULL);
@@ -2409,16 +2477,13 @@ write_ip4_aliases (NMConnection *connection, char *base_ifcfg_path)
 static gboolean
 write_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **error)
 {
-	char **route_items;
-	char *route_contents;
+	nm_auto_free_gstring GString *contents = NULL;
 	NMIPRoute *route;
 	guint32 i, num;
-	gboolean success = FALSE;
 
-	g_return_val_if_fail (filename != NULL, FALSE);
-	g_return_val_if_fail (s_ip6 != NULL, FALSE);
-	g_return_val_if_fail (error != NULL, FALSE);
-	g_return_val_if_fail (*error == NULL, FALSE);
+	g_return_val_if_fail (filename, FALSE);
+	g_return_val_if_fail (s_ip6, FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
 
 	num = nm_setting_ip_config_get_num_routes (s_ip6);
 	if (num == 0) {
@@ -2426,38 +2491,39 @@ write_route6_file (const char *filename, NMSettingIPConfig *s_ip6, GError **erro
 		return TRUE;
 	}
 
-	route_items = g_malloc0 (sizeof (char*) * (num + 1));
+	contents = g_string_new ("");
 	for (i = 0; i < num; i++) {
+		gs_free char *options = NULL;
+
 		route = nm_setting_ip_config_get_route (s_ip6, i);
+		options = get_route_attributes_string (route, AF_INET6);
 
 		if (nm_ip_route_get_metric (route) == -1) {
-			route_items[i] = g_strdup_printf ("%s/%u via %s\n",
+			g_string_append_printf (contents, "%s/%u via %s%s%s",
 			                                  nm_ip_route_get_dest (route),
 			                                  nm_ip_route_get_prefix (route),
-			                                  nm_ip_route_get_next_hop (route));
+			                                  nm_ip_route_get_next_hop (route),
+			                                  options ? " " : "",
+			                                  options ?: "");
 		} else {
-			route_items[i] = g_strdup_printf ("%s/%u via %s metric %u\n",
+			g_string_append_printf (contents, "%s/%u via %s metric %u%s%s",
 			                                  nm_ip_route_get_dest (route),
 			                                  nm_ip_route_get_prefix (route),
 			                                  nm_ip_route_get_next_hop (route),
-			                                  (guint32) nm_ip_route_get_metric (route));
+			                                  (unsigned) nm_ip_route_get_metric (route),
+			                                  options ? " " : "",
+			                                  options ?: "");
 		}
+		g_string_append (contents, "\n");
 	}
-	route_items[num] = NULL;
-	route_contents = g_strjoinv (NULL, route_items);
-	g_strfreev (route_items);
 
-	if (!g_file_set_contents (filename, route_contents, -1, NULL)) {
+	if (!g_file_set_contents (filename, contents->str, -1, NULL)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Writing route6 file '%s' failed", filename);
-		goto error;
+		return FALSE;
 	}
 
-	success = TRUE;
-
-error:
-	g_free (route_contents);
-	return success;
+	return TRUE;
 }
 
 static void
@@ -2466,7 +2532,7 @@ write_ip6_setting_dhcp_hostname (NMSettingIPConfig *s_ip6, shvarFile *ifcfg)
 	const char *hostname;
 
 	hostname = nm_setting_ip_config_get_dhcp_hostname (s_ip6);
-	svSetValueString (ifcfg, "DHCPV6_HOSTNAME", hostname);
+	svSetValueStr (ifcfg, "DHCPV6_HOSTNAME", hostname);
 
 	/* Missing DHCPV6_SEND_HOSTNAME means TRUE, and we prefer not write it
 	 * explicitly in that case, because it is NM-specific variable
@@ -2474,7 +2540,7 @@ write_ip6_setting_dhcp_hostname (NMSettingIPConfig *s_ip6, shvarFile *ifcfg)
 	if (nm_setting_ip_config_get_dhcp_send_hostname (s_ip6))
 		svUnsetValue (ifcfg, "DHCPV6_SEND_HOSTNAME");
 	else
-		svSetValueString (ifcfg, "DHCPV6_SEND_HOSTNAME", "no");
+		svSetValueStr (ifcfg, "DHCPV6_SEND_HOSTNAME", "no");
 }
 
 static gboolean
@@ -2483,11 +2549,9 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	NMSettingIPConfig *s_ip6;
 	NMSettingIPConfig *s_ip4;
 	const char *value;
-	char *addr_key;
 	char *tmp;
-	guint32 i, num, num4;
+	guint i, num, num4;
 	gint priority;
-	GString *searches;
 	NMIPAddress *addr;
 	const char *dns;
 	gint64 route_metric;
@@ -2518,28 +2582,28 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	value = nm_setting_ip_config_get_method (s_ip6);
 	g_assert (value);
 	if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) {
-		svSetValueString (ifcfg, "IPV6INIT", "no");
+		svSetValueStr (ifcfg, "IPV6INIT", "no");
 		svUnsetValue (ifcfg, "DHCPV6C");
 		return TRUE;
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) {
-		svSetValueString (ifcfg, "IPV6INIT", "yes");
-		svSetValueString (ifcfg, "IPV6_AUTOCONF", "yes");
+		svSetValueStr (ifcfg, "IPV6INIT", "yes");
+		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "yes");
 		svUnsetValue (ifcfg, "DHCPV6C");
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) {
-		svSetValueString (ifcfg, "IPV6INIT", "yes");
-		svSetValueString (ifcfg, "IPV6_AUTOCONF", "no");
-		svSetValueString (ifcfg, "DHCPV6C", "yes");
+		svSetValueStr (ifcfg, "IPV6INIT", "yes");
+		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "no");
+		svSetValueStr (ifcfg, "DHCPV6C", "yes");
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
-		svSetValueString (ifcfg, "IPV6INIT", "yes");
-		svSetValueString (ifcfg, "IPV6_AUTOCONF", "no");
+		svSetValueStr (ifcfg, "IPV6INIT", "yes");
+		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "no");
 		svUnsetValue (ifcfg, "DHCPV6C");
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) {
-		svSetValueString (ifcfg, "IPV6INIT", "yes");
-		svSetValueString (ifcfg, "IPV6_AUTOCONF", "no");
+		svSetValueStr (ifcfg, "IPV6INIT", "yes");
+		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "no");
 		svUnsetValue (ifcfg, "DHCPV6C");
 	} else if (!strcmp (value, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) {
-		svSetValueString (ifcfg, "IPV6INIT", "yes");
-		svSetValueString (ifcfg, "IPV6_AUTOCONF", "shared");
+		svSetValueStr (ifcfg, "IPV6INIT", "yes");
+		svSetValueStr (ifcfg, "IPV6_AUTOCONF", "shared");
 		svUnsetValue (ifcfg, "DHCPV6C");
 	}
 
@@ -2563,9 +2627,9 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		                        nm_ip_address_get_address (addr),
 		                        nm_ip_address_get_prefix (addr));
 	}
-	svSetValueString (ifcfg, "IPV6ADDR", ip_str1->str);
-	svSetValueString (ifcfg, "IPV6ADDR_SECONDARIES", ip_str2->str);
-	svSetValueString (ifcfg, "IPV6_DEFAULTGW", nm_setting_ip_config_get_gateway (s_ip6));
+	svSetValueStr (ifcfg, "IPV6ADDR", ip_str1->str);
+	svSetValueStr (ifcfg, "IPV6ADDR_SECONDARIES", ip_str2->str);
+	svSetValueStr (ifcfg, "IPV6_DEFAULTGW", nm_setting_ip_config_get_gateway (s_ip6));
 	g_string_free (ip_str1, TRUE);
 	g_string_free (ip_str2, TRUE);
 
@@ -2574,68 +2638,68 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	num4 = s_ip4 ? nm_setting_ip_config_get_num_dns (s_ip4) : 0; /* from where to start with IPv6 entries */
 	num = nm_setting_ip_config_get_num_dns (s_ip6);
 	for (i = 0; i < 254; i++) {
-		addr_key = g_strdup_printf ("DNS%d", i + num4 + 1);
+		char addr_key[64];
+
+		nm_sprintf_buf (addr_key, "DNS%u", i + num4 + 1);
 
 		if (i >= num)
 			svUnsetValue (ifcfg, addr_key);
 		else {
 			dns = nm_setting_ip_config_get_dns (s_ip6, i);
-			svSetValueString (ifcfg, addr_key, dns);
+			svSetValueStr (ifcfg, addr_key, dns);
 		}
-		g_free (addr_key);
 	}
 
 	/* Write out DNS domains - 'DOMAIN' key is shared for both IPv4 and IPv6 domains */
 	num = nm_setting_ip_config_get_num_dns_searches (s_ip6);
 	if (num > 0) {
-		char *ip4_domains;
-		ip4_domains = svGetValueString (ifcfg, "DOMAIN");
-		searches = g_string_new (ip4_domains);
+		gs_free char *ip4_domains = NULL;
+		nm_auto_free_gstring GString *searches = NULL;
+
+		searches = g_string_new (svGetValueStr (ifcfg, "DOMAIN", &ip4_domains));
 		for (i = 0; i < num; i++) {
 			if (searches->len > 0)
 				g_string_append_c (searches, ' ');
 			g_string_append (searches, nm_setting_ip_config_get_dns_search (s_ip6, i));
 		}
-		svSetValueString (ifcfg, "DOMAIN", searches->str);
-		g_string_free (searches, TRUE);
-		g_free (ip4_domains);
+		svSetValueStr (ifcfg, "DOMAIN", searches->str);
 	}
 
 
 	/* handle IPV6_DEFROUTE */
 	/* IPV6_DEFROUTE has the opposite meaning from 'never-default' */
-	if (nm_setting_ip_config_get_never_default(s_ip6))
-		svSetValueString (ifcfg, "IPV6_DEFROUTE", "no");
+	if (nm_setting_ip_config_get_never_default (s_ip6))
+		svSetValueStr (ifcfg, "IPV6_DEFROUTE", "no");
 	else
-		svSetValueString (ifcfg, "IPV6_DEFROUTE", "yes");
+		svSetValueStr (ifcfg, "IPV6_DEFROUTE", "yes");
 
-	svSetValueString (ifcfg, "IPV6_PEERDNS",
-	                  nm_setting_ip_config_get_ignore_auto_dns (s_ip6) ? "no" : NULL);
+	svSetValueStr (ifcfg, "IPV6_PEERDNS",
+	               nm_setting_ip_config_get_ignore_auto_dns (s_ip6) ? "no" : NULL);
 
-	svSetValueString (ifcfg, "IPV6_PEERROUTES",
-	                  nm_setting_ip_config_get_ignore_auto_routes (s_ip6) ? "no" : NULL);
+	svSetValueStr (ifcfg, "IPV6_PEERROUTES",
+	               nm_setting_ip_config_get_ignore_auto_routes (s_ip6) ? "no" : NULL);
 
-	svSetValueString (ifcfg, "IPV6_FAILURE_FATAL",
-	                  nm_setting_ip_config_get_may_fail (s_ip6) ? "no" : "yes");
+	svSetValueStr (ifcfg, "IPV6_FAILURE_FATAL",
+	               nm_setting_ip_config_get_may_fail (s_ip6) ? "no" : "yes");
 
 	route_metric = nm_setting_ip_config_get_route_metric (s_ip6);
 	tmp = route_metric != -1 ? g_strdup_printf ("%"G_GINT64_FORMAT, route_metric) : NULL;
-	svSetValueString (ifcfg, "IPV6_ROUTE_METRIC", tmp);
+	svSetValueStr (ifcfg, "IPV6_ROUTE_METRIC", tmp);
 	g_free (tmp);
 
 	/* IPv6 Privacy Extensions */
 	svUnsetValue (ifcfg, "IPV6_PRIVACY");
 	svUnsetValue (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP");
-	switch (nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6))){
+	switch (nm_setting_ip6_config_get_ip6_privacy (NM_SETTING_IP6_CONFIG (s_ip6))) {
 	case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED:
-		svSetValueString (ifcfg, "IPV6_PRIVACY", "no");
+		svSetValueStr (ifcfg, "IPV6_PRIVACY", "no");
 	break;
 	case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR:
-		svSetValueString (ifcfg, "IPV6_PRIVACY", "rfc3041");
-		svSetValueString (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", "yes");
+		svSetValueStr (ifcfg, "IPV6_PRIVACY", "rfc3041");
+		svSetValueStr (ifcfg, "IPV6_PRIVACY_PREFER_PUBLIC_IP", "yes");
 	break;
 	case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR:
-		svSetValueString (ifcfg, "IPV6_PRIVACY", "rfc3041");
+		svSetValueStr (ifcfg, "IPV6_PRIVACY", "rfc3041");
 	break;
 	default:
 	break;
@@ -2646,7 +2710,7 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	if (addr_gen_mode != NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) {
 		tmp = nm_utils_enum_to_str (nm_setting_ip6_config_addr_gen_mode_get_type (),
 		                            addr_gen_mode);
-		svSetValueString (ifcfg, "IPV6_ADDR_GEN_MODE", tmp);
+		svSetValueStr (ifcfg, "IPV6_ADDR_GEN_MODE", tmp);
 		g_free (tmp);
 	} else {
 		svUnsetValue (ifcfg, "IPV6_ADDR_GEN_MODE");
@@ -2654,7 +2718,7 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 
 	/* IPv6 tokenized interface identifier */
 	value = nm_setting_ip6_config_get_token (NM_SETTING_IP6_CONFIG (s_ip6));
-	svSetValueString (ifcfg, "IPV6_TOKEN", value);
+	svSetValueStr (ifcfg, "IPV6_TOKEN", value);
 
 	priority = nm_setting_ip_config_get_dns_priority (s_ip6);
 	if (priority)
@@ -2667,17 +2731,14 @@ write_ip6_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	if (!route6_path) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Could not get route6 file path for '%s'", svFileGetName (ifcfg));
-		goto error;
+		return FALSE;
 	}
 	write_route6_file (route6_path, s_ip6, error);
 	g_free (route6_path);
 	if (error && *error)
-		goto error;
+		return FALSE;
 
 	return TRUE;
-
-error:
-	return FALSE;
 }
 
 static void
@@ -2762,18 +2823,20 @@ write_connection (NMConnection *connection,
                   const char *ifcfg_dir,
                   const char *filename,
                   char **out_filename,
+                  NMConnection **out_reread,
+                  gboolean *out_reread_same,
                   GError **error)
 {
 	NMSettingConnection *s_con;
-	gboolean success = FALSE;
-	shvarFile *ifcfg = NULL;
-	char *ifcfg_name = NULL;
+	nm_auto_shvar_file_close shvarFile *ifcfg = NULL;
+	gs_free char *ifcfg_name = NULL;
 	const char *type;
 	gboolean no_8021x = FALSE;
 	gboolean wired = FALSE;
 
 	nm_assert (NM_IS_CONNECTION (connection));
-	nm_assert (nm_connection_verify (connection, NULL));
+	nm_assert (_nm_connection_verify (connection, NULL) == NM_SETTING_VERIFY_SUCCESS);
+	nm_assert (!out_reread || !*out_reread);
 
 	if (!writer_can_write_connection (connection, error))
 		return FALSE;
@@ -2802,13 +2865,12 @@ write_connection (NMConnection *connection,
 		if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS)) {
 			guint32 idx = 0;
 
-			g_free (ifcfg_name);
+			nm_clear_g_free (&ifcfg_name);
 			while (idx++ < 500) {
 				ifcfg_name = g_strdup_printf ("%s/ifcfg-%s-%u", ifcfg_dir, escaped, idx);
 				if (g_file_test (ifcfg_name, G_FILE_TEST_EXISTS) == FALSE)
 					break;
-				g_free (ifcfg_name);
-				ifcfg_name = NULL;
+				nm_clear_g_free (&ifcfg_name);
 			}
 		}
 		g_free (escaped);
@@ -2826,7 +2888,7 @@ write_connection (NMConnection *connection,
 	if (!type) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Missing connection type!");
-		goto out;
+		return FALSE;
 	}
 
 	if (!strcmp (type, NM_SETTING_WIRED_SETTING_NAME)) {
@@ -2835,82 +2897,113 @@ write_connection (NMConnection *connection,
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 			             "Can't write connection type '%s'",
 			             NM_SETTING_PPPOE_SETTING_NAME);
-			goto out;
+			return FALSE;
 		}
 
 		if (!write_wired_setting (connection, ifcfg, error))
-			goto out;
+			return FALSE;
 		wired = TRUE;
 	} else if (!strcmp (type, NM_SETTING_VLAN_SETTING_NAME)) {
 		if (!write_vlan_setting (connection, ifcfg, &wired, error))
-			goto out;
+			return FALSE;
 	} else if (!strcmp (type, NM_SETTING_WIRELESS_SETTING_NAME)) {
 		if (!write_wireless_setting (connection, ifcfg, &no_8021x, error))
-			goto out;
+			return FALSE;
 	} else if (!strcmp (type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
 		if (!write_infiniband_setting (connection, ifcfg, error))
-			goto out;
+			return FALSE;
 	} else if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME)) {
 		if (!write_bonding_setting (connection, ifcfg, &wired, error))
-			goto out;
+			return FALSE;
 	} else if (!strcmp (type, NM_SETTING_TEAM_SETTING_NAME)) {
 		if (!write_team_setting (connection, ifcfg, &wired, error))
-			goto out;
+			return FALSE;
 	} else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)) {
 		if (!write_bridge_setting (connection, ifcfg, error))
-			goto out;
+			return FALSE;
 	} else {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Can't write connection type '%s'", type);
-		goto out;
+		return FALSE;
 	}
 
 	if (!no_8021x) {
 		if (!write_8021x_setting (connection, ifcfg, wired, error))
-			goto out;
+			return FALSE;
 	}
 
 	if (!write_bridge_port_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 
 	if (!write_team_port_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 
 	if (!write_dcb_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 
 	if (!write_proxy_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
+
+	if (!write_user_setting (connection, ifcfg, error))
+		return FALSE;
 
 	svUnsetValue (ifcfg, "DHCP_HOSTNAME");
 	svUnsetValue (ifcfg, "DHCP_FQDN");
 
 	if (!write_ip4_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 	write_ip4_aliases (connection, ifcfg_name);
 
 	if (!write_ip6_setting (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 
 	if (!write_res_options (connection, ifcfg, error))
-		goto out;
+		return FALSE;
 
 	write_connection_setting (s_con, ifcfg);
 
 	if (!svWriteFile (ifcfg, 0644, error))
-		goto out;
+		return FALSE;
+
+	if (out_reread || out_reread_same) {
+		gs_unref_object NMConnection *reread = NULL;
+		gs_free_error GError *local = NULL;
+		gs_free char *unhandled = NULL;
+		gboolean reread_same = FALSE;
+
+		reread = connection_from_file (ifcfg_name, &unhandled, &local, NULL);
+
+		if (unhandled) {
+			_LOGW ("failure to re-read the new connection from file \"%s\": %s",
+			       ifcfg_name, "connection is unhandled");
+			g_clear_object (&reread);
+		} else if (!reread) {
+			_LOGW ("failure to re-read the new connection from file \"%s\": %s",
+			       ifcfg_name, local ? local->message : "<unknown>");
+		} else {
+			if (out_reread_same) {
+				if (nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT))
+					reread_same = TRUE;
+
+				nm_assert (reread_same == nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT));
+				nm_assert (reread_same == ({
+				                                gs_unref_hashtable GHashTable *_settings = NULL;
+
+				                                (   nm_connection_diff (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT, &_settings)
+				                                 && !_settings);
+				                           }));
+			}
+		}
+
+		NM_SET_OUT (out_reread, g_steal_pointer (&reread));
+		NM_SET_OUT (out_reread_same, reread_same);
+	}
 
 	/* Only return the filename if this was a newly written ifcfg */
 	if (out_filename && !filename)
-		*out_filename = g_strdup (ifcfg_name);
-
-	success = TRUE;
+		*out_filename = g_steal_pointer (&ifcfg_name);
 
-out:
-	if (ifcfg)
-		svCloseFile (ifcfg);
-	g_free (ifcfg_name);
-	return success;
+	return TRUE;
 }
 
 gboolean
@@ -2942,15 +3035,19 @@ gboolean
 writer_new_connection (NMConnection *connection,
                        const char *ifcfg_dir,
                        char **out_filename,
+                       NMConnection **out_reread,
+                       gboolean *out_reread_same,
                        GError **error)
 {
-	return write_connection (connection, ifcfg_dir, NULL, out_filename, error);
+	return write_connection (connection, ifcfg_dir, NULL, out_filename, out_reread, out_reread_same, error);
 }
 
 gboolean
 writer_update_connection (NMConnection *connection,
                           const char *ifcfg_dir,
                           const char *filename,
+                          NMConnection **out_reread,
+                          gboolean *out_reread_same,
                           GError **error)
 {
 	if (utils_has_complex_routes (filename)) {
@@ -2959,6 +3056,6 @@ writer_update_connection (NMConnection *connection,
 		return FALSE;
 	}
 
-	return write_connection (connection, ifcfg_dir, filename, NULL, error);
+	return write_connection (connection, ifcfg_dir, filename, NULL, out_reread, out_reread_same, error);
 }
 
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h
index 2662f79b..9cd9513e 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.h
@@ -29,11 +29,15 @@ gboolean writer_can_write_connection (NMConnection *connection,
 gboolean writer_new_connection (NMConnection *connection,
                                 const char *ifcfg_dir,
                                 char **out_filename,
+                                NMConnection **out_reread,
+                                gboolean *out_reread_same,
                                 GError **error);
 
 gboolean writer_update_connection (NMConnection *connection,
                                    const char *ifcfg_dir,
                                    const char *filename,
+                                   NMConnection **out_reread,
+                                   gboolean *out_reread_same,
                                    GError **error);
 
 #endif /* _WRITER_H_ */
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 65bcc0d9..9fce5aa1 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -41,7 +41,7 @@
 
 /*****************************************************************************/
 
-typedef struct {
+struct _shvarLine {
 	/* There are three cases:
 	 *
 	 * 1) the line is not a valid variable assignment (that is, it doesn't
@@ -61,7 +61,9 @@ typedef struct {
 	char *line;
 	const char *key;
 	char *key_with_prefix;
-} shvarLine;
+};
+
+typedef struct _shvarLine shvarLine;
 
 struct _shvarFile {
 	char      *fileName;
@@ -220,14 +222,12 @@ svEscape (const char *s, char **to_free)
 	int newlen;
 	size_t i, j, slen;
 
-	slen = strlen (s);
-
-	for (i = 0; i < slen; i++) {
-		if (_char_req_escape (s[i]))
+	for (slen = 0; s[slen]; slen++) {
+		if (_char_req_escape (s[slen]))
 			mangle++;
-		else if (_char_req_quotes (s[i]))
+		else if (_char_req_quotes (s[slen]))
 			requires_quotes = TRUE;
-		else if (s[i] < ' ') {
+		else if (s[slen] < ' ') {
 			/* if the string contains newline we can only express it using ANSI C quotation
 			 * (as we don't support line continuation).
 			 * Additionally, ANSI control characters look odd with regular quotation, so handle
@@ -639,15 +639,19 @@ svFileGetName (const shvarFile *s)
 }
 
 void
-svFileSetName (shvarFile *s, const char *fileName)
+svFileSetName_test_only (shvarFile *s, const char *fileName)
 {
+	/* changing the file name is not supported for regular
+	 * operation. Only allowed to use in tests, othewise,
+	 * the filename is immutable. */
 	g_free (s->fileName);
 	s->fileName = g_strdup (fileName);
 }
 
 void
-svFileSetModified (shvarFile *s)
+svFileSetModified_test_only (shvarFile *s)
 {
+	/* marking a file as modified is only for testing. */
 	s->modified = TRUE;
 }
 
@@ -877,6 +881,30 @@ shlist_find (const GList *current, const char *key)
 
 /*****************************************************************************/
 
+GHashTable *
+svGetKeys (shvarFile *s)
+{
+	GHashTable *keys = NULL;
+	const GList *current;
+	const shvarLine *line;
+
+	nm_assert (s);
+
+	for (current = s->lineList; current; current = current->next) {
+		line = current->data;
+		if (line->key && line->line) {
+			/* we don't clone the keys. The keys are only valid
+			 * until @s gets modified. */
+			if (!keys)
+				keys = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+			g_hash_table_add (keys, (gpointer) line->key);
+		}
+	}
+	return keys;
+}
+
+/*****************************************************************************/
+
 static const char *
 _svGetValue (shvarFile *s, const char *key, char **to_free)
 {
@@ -895,39 +923,104 @@ _svGetValue (shvarFile *s, const char *key, char **to_free)
 	}
 	if (last) {
 		line = last->data;
-		if (line->line)
-			return svUnescape (line->line, to_free);
+		if (line->line) {
+			const char *v;
+
+			v = svUnescape (line->line, to_free);
+			if (!v) {
+				/* a wrongly quoted value is treated like the empty string.
+				 * See also svWriteFile(), which handles unparsable values
+				 * that way. */
+				nm_assert (!*to_free);
+				return "";
+			}
+			return v;
+		}
 	}
 	*to_free = NULL;
 	return NULL;
 }
 
+/* Returns the value for key. The value is either owned by @s
+ * or returned as to_free. This aims to avoid cloning the string.
+ *
+ * - like svGetValue_cp(), but avoids cloning the value if possible.
+ * - like svGetValueStr(), but does not ignore empty string values.
+ */
 const char *
 svGetValue (shvarFile *s, const char *key, char **to_free)
 {
-	g_return_val_if_fail (s != NULL, NULL);
-	g_return_val_if_fail (key != NULL, NULL);
+	g_return_val_if_fail (s, NULL);
+	g_return_val_if_fail (key, NULL);
 	g_return_val_if_fail (to_free, NULL);
 
 	return _svGetValue (s, key, to_free);
 }
 
-/* Get the value associated with the key, and leave the current pointer
- * pointing at the line containing the value.  The char* returned MUST
- * be freed by the caller.
+/* Returns the value for key. The value is either owned by @s
+ * or returned as to_free. This aims to avoid cloning the string.
+ *
+ * - like svGetValue(), but does not return an empty string.
+ * - like svGetValueStr_cp(), but avoids cloning the value if possible.
+ */
+const char *
+svGetValueStr (shvarFile *s, const char *key, char **to_free)
+{
+	const char *value;
+
+	g_return_val_if_fail (s, NULL);
+	g_return_val_if_fail (key, NULL);
+	g_return_val_if_fail (to_free, NULL);
+
+	value = _svGetValue (s, key, to_free);
+	if (!value || !value[0]) {
+		nm_assert (!*to_free);
+		return NULL;
+	}
+	return value;
+}
+
+/* Returns the value for key. The returned value must be freed
+ * by the caller.
+ *
+ * - like svGetValue(), but always returns a copy of the value.
+ * - like svGetValueStr_cp(), but does not ignore an empty string.
  */
 char *
-svGetValueString (shvarFile *s, const char *key)
+svGetValue_cp (shvarFile *s, const char *key)
 {
 	char *to_free;
 	const char *value;
 
+	g_return_val_if_fail (s, NULL);
+	g_return_val_if_fail (key, NULL);
+
 	value = _svGetValue (s, key, &to_free);
 	if (!value) {
 		nm_assert (!to_free);
 		return NULL;
 	}
-	if (!value[0]) {
+	return to_free ?: g_strdup (value);
+}
+
+/* Returns the value for key. The returned value must be freed
+ * by the caller.
+ * If the key is unset or the value an empty string, NULL is returned.
+ *
+ * - like svGetValueStr(), but always returns a copy of the value.
+ * - like svGetValue_cp(), but returns NULL instead of an empty string.
+ */
+char *
+svGetValueStr_cp (shvarFile *s, const char *key)
+{
+	char *to_free;
+	const char *value;
+
+	g_return_val_if_fail (s, NULL);
+	g_return_val_if_fail (key, NULL);
+
+	value = _svGetValue (s, key, &to_free);
+	if (!value || !value[0]) {
 		nm_assert (!to_free);
 		return NULL;
 	}
@@ -991,8 +1084,8 @@ svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 m
 
 /*****************************************************************************/
 
-/* Same as svSetValueString() but it preserves empty @value -- contrary to
- * svSetValueString() for which "" effectively means to remove the value. */
+/* Same as svSetValueStr() but it preserves empty @value -- contrary to
+ * svSetValueStr() for which "" effectively means to remove the value. */
 void
 svSetValue (shvarFile *s, const char *key, const char *value)
 {
@@ -1041,7 +1134,7 @@ svSetValue (shvarFile *s, const char *key, const char *value)
  * to the bottom of the file.
  */
 void
-svSetValueString (shvarFile *s, const char *key, const char *value)
+svSetValueStr (shvarFile *s, const char *key, const char *value)
 {
 	svSetValue (s, key, value && value[0] ? value : NULL);
 }
@@ -1066,6 +1159,27 @@ svUnsetValue (shvarFile *s, const char *key)
 	svSetValue (s, key, NULL);
 }
 
+void
+svUnsetValuesWithPrefix (shvarFile *s, const char *prefix)
+{
+	GList *current;
+
+	g_return_if_fail (s);
+	g_return_if_fail (prefix);
+
+	for (current = s->lineList; current; current = current->next) {
+		shvarLine *line = current->data;
+
+		ASSERT_shvarLine (line);
+		if (   line->key
+		    && g_str_has_prefix (line->key, prefix)) {
+			if (nm_clear_g_free (&line->line))
+				s->modified = TRUE;
+		}
+		ASSERT_shvarLine (line);
+	}
+}
+
 /*****************************************************************************/
 
 /* Write the current contents iff modified.  Returns FALSE on error
diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h
index a6498fce..9d8c2364 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/settings/plugins/ifcfg-rh/shvar.h
@@ -34,9 +34,9 @@
 typedef struct _shvarFile shvarFile;
 
 const char *svFileGetName (const shvarFile *s);
-void svFileSetName (shvarFile *s, const char *fileName);
 
-void svFileSetModified (shvarFile *s);
+void svFileSetName_test_only (shvarFile *s, const char *fileName);
+void svFileSetModified_test_only (shvarFile *s);
 
 /* Create the file <name>, return a shvarFile (never fails) */
 shvarFile *svCreateFile (const char *name);
@@ -49,10 +49,15 @@ shvarFile *svOpenFile (const char *name, GError **error);
  * be freed by the caller.
  */
 const char *svGetValue (shvarFile *s, const char *key, char **to_free);
-char *svGetValueString (shvarFile *s, const char *key);
+char *svGetValue_cp (shvarFile *s, const char *key);
+
+const char *svGetValueStr (shvarFile *s, const char *key, char **to_free);
+char *svGetValueStr_cp (shvarFile *s, const char *key);
 
 gint svParseBoolean (const char *value, gint def);
 
+GHashTable *svGetKeys (shvarFile *s);
+
 /* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true")
  * return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false")
  * return <def> otherwise
@@ -66,13 +71,15 @@ gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, g
  * the key=value pair after that line.  Otherwise, prepend the pair
  * to the top of the file.
  */
-void svSetValueString (shvarFile *s, const char *key, const char *value);
 void svSetValue (shvarFile *s, const char *key, const char *value);
+void svSetValueStr (shvarFile *s, const char *key, const char *value);
 void svSetValueBoolean (shvarFile *s, const char *key, gboolean value);
 void svSetValueInt64 (shvarFile *s, const char *key, gint64 value);
 
 void svUnsetValue (shvarFile *s, const char *key);
 
+void svUnsetValuesWithPrefix (shvarFile *s, const char *prefix);
+
 /* Write the current contents iff modified.  Returns FALSE on error
  * and TRUE on success.  Do not write if no values have been modified.
  * The mode argument is only used if creating the file, not if
@@ -87,4 +94,16 @@ void svCloseFile (shvarFile *s);
 const char *svEscape (const char *s, char **to_free);
 const char *svUnescape (const char *s, char **to_free);
 
+static inline void
+_nm_auto_shvar_file_close (shvarFile **p_s)
+{
+	if (*p_s) {
+		int errsv = errno;
+
+		svCloseFile (*p_s);
+		errno = errsv;
+	}
+}
+#define nm_auto_shvar_file_close nm_auto(_nm_auto_shvar_file_close)
+
 #endif /* _SHVAR_H */
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected
new file mode 100644
index 00000000..d81d9187
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-a.cexpected
@@ -0,0 +1,8 @@
+HWADDR=00:22:15:59:62:97
+TYPE=Ethernet
+BRIDGING_OPTS="priority=28 hairpin_mode=1"
+NAME="System test-bridge-component"
+UUID=${UUID}
+DEVICE=eth0
+ONBOOT=no
+BRIDGE=br0
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
new file mode 100644
index 00000000..d4785ff5
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-bridge-component-b.cexpected
@@ -0,0 +1,17 @@
+HWADDR=00:22:15:59:62:97
+TYPE=Ethernet
+BRIDGING_OPTS="priority=28 hairpin_mode=1"
+NAME="System test-bridge-component"
+UUID=${UUID}
+DEVICE=eth0
+ONBOOT=no
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected
new file mode 100644
index 00000000..59a6f79b
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected
@@ -0,0 +1,22 @@
+HWADDR=00:11:22:33:44:EE
+TYPE=Ethernet
+KEY_MGMT=IEEE8021X
+IEEE_8021X_EAP_METHODS=PEAP
+IEEE_8021X_IDENTITY="Jara Cimrman"
+IEEE_8021X_PASSWORD_FLAGS="user ask"
+IEEE_8021X_PEAP_VERSION=1
+IEEE_8021X_INNER_AUTH_METHODS=GTC
+IEEE_8021X_SUBJECT_MATCH=server1.yourdomain.tld
+IEEE_8021X_PHASE2_SUBJECT_MATCH=server2.yourdomain.tld
+IEEE_8021X_ALTSUBJECT_MATCHES="a.yourdomain.tld b.yourdomain.tld c.yourdomain.tld"
+IEEE_8021X_PHASE2_ALTSUBJECT_MATCHES="x.yourdomain.tld y.yourdomain.tld"
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="System test-wired-802-1X-subj-matches"
+UUID=${UUID}
+DEVICE=eth0
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected
new file mode 100644
index 00000000..a48be78a
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_User_1.cexpected
@@ -0,0 +1,34 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+NM_USER__M_Y___053=val=MY.+
+NM_USER__M_Y___055=val=MY.-
+NM_USER__M_Y___057=val=MY./
+NM_USER__M_Y__8_053_V=val=MY.8+V
+NM_USER__M_Y__8_055_V=val=MY.8-V
+NM_USER__M_Y__8_057_V=val=MY.8/V
+NM_USER__M_Y__8_075_V=val=MY.8=V
+NM_USER__M_Y__8_V=val=MY.8V
+NM_USER__M_Y__8_137_V=val=MY.8_V
+NM_USER__M_Y___075=val=MY.=
+NM_USER__M_Y___A_V=val=MY.AV
+NM_USER__M_Y___137=val=MY._
+NM_USER_MY___AV=val=my.Av
+NM_USER_MY___137V=val=my._v
+NM_USER_MY__KEYS__1=val=my.keys.1
+NM_USER_MY__OTHER___K_E_Y__42=val=my.other.KEY.42
+NM_USER_MY__V_053=val=my.v+
+NM_USER_MY__V_137_137AL3=val=my.v__al3
+NM_USER_MY__VAL1=
+NM_USER_MY__VAL2=val=my.val2
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test User 1"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected
new file mode 100644
index 00000000..3956003d
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected
@@ -0,0 +1,16 @@
+DEVICE=bond0
+BONDING_OPTS=mode=balance-rr
+TYPE=Bond
+BONDING_MASTER=yes
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=none
+IPADDR=1.1.1.3
+PREFIX=24
+GATEWAY=1.1.1.1
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write Bond Main"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected
new file mode 100644
index 00000000..c478db38
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected
@@ -0,0 +1,8 @@
+HWADDR=31:33:33:37:BE:CD
+MTU=1492
+TYPE=Ethernet
+BRIDGING_OPTS="priority=50 path_cost=33"
+NAME="Test Write Bridge Component"
+UUID=${UUID}
+ONBOOT=yes
+BRIDGE=br0
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected
new file mode 100644
index 00000000..80e96921
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected
@@ -0,0 +1,11 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write Permissions"
+UUID=${UUID}
+ONBOOT=yes
+USERS="blahblah foobar asdfasdf"
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected
new file mode 100644
index 00000000..716eedc2
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=auto
+PAC_URL=https://wpad.neverland.org/wpad.dat
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write Proxy Basic"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected
new file mode 100644
index 00000000..0b1deb80
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected
@@ -0,0 +1,6 @@
+TEAM_PORT_CONFIG="{ \"p4p1\": { \"prio\": -10, \"sticky\": true } }"
+NAME="Test Write Team Port"
+UUID=${UUID}
+ONBOOT=yes
+TEAM_MASTER=team0
+DEVICETYPE=TeamPort
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected
new file mode 100644
index 00000000..339f8107
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected
@@ -0,0 +1,20 @@
+VLAN=yes
+TYPE=Vlan
+PHYSDEV=eth0
+VLAN_ID=444
+REORDER_HDR=yes
+GVRP=no
+MVRP=no
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write VLAN reorder_hdr"
+UUID=${UUID}
+ONBOOT=no
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected
new file mode 100644
index 00000000..a95a58db
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected
@@ -0,0 +1,18 @@
+ESSID="Test SSID"
+MODE=Managed
+BAND=a
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi Band A"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected
new file mode 100644
index 00000000..026993b8
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected
@@ -0,0 +1,18 @@
+ESSID="Test SSID"
+MODE=Managed
+SSID_HIDDEN=yes
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi Hidden"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected
new file mode 100644
index 00000000..f3704f10
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_always.cexpected
@@ -0,0 +1,18 @@
+MACADDR=random
+ESSID="Test SSID"
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=always
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi MAC always"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected
new file mode 100644
index 00000000..005c6179
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_default.cexpected
@@ -0,0 +1,17 @@
+ESSID="Test SSID"
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi MAC default"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected
new file mode 100644
index 00000000..dff17ef2
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_missing.cexpected
@@ -0,0 +1,18 @@
+MACADDR=permanent
+ESSID="Test SSID"
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=never
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi MAC missing"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected
new file mode 100644
index 00000000..94274cf9
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_WiFi_MAC_never.cexpected
@@ -0,0 +1,18 @@
+MACADDR=permanent
+ESSID="Test SSID"
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=never
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write WiFi MAC never"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected
new file mode 100644
index 00000000..d3db19b2
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected
@@ -0,0 +1,16 @@
+ESSID=blahblah
+MODE=Managed
+KEY_MGMT=IEEE8021X
+SECURITYMODE=leap
+IEEE_8021X_IDENTITY="Bill Smith"
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write Wifi LEAP"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected
new file mode 100644
index 00000000..32db7262
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected
@@ -0,0 +1,15 @@
+ESSID=blahblah104
+MODE=Managed
+SECURITYMODE=open
+DEFAULTKEY=1
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write Wifi WEP 104 ASCII"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected
new file mode 100644
index 00000000..8f421cfb
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected
@@ -0,0 +1,15 @@
+ETHTOOL_OPTS="autoneg off speed 10 duplex half"
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write Wired Auto-Negotiate"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected
new file mode 100644
index 00000000..398a3017
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected
@@ -0,0 +1,15 @@
+ETHTOOL_OPTS="wol umgs sopass 00:00:00:11:22:33"
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME="Test Write Wired Wake-on-LAN"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected
new file mode 100644
index 00000000..60091b7b
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected
@@ -0,0 +1,22 @@
+VLAN=yes
+TYPE=Vlan
+DEVICE=vlan43
+PHYSDEV=eth9
+VLAN_ID=43
+REORDER_HDR=yes
+GVRP=yes
+VLAN_FLAGS=LOOSE_BINDING
+MVRP=no
+VLAN_INGRESS_PRIORITY_MAP=0:1,2:5
+VLAN_EGRESS_PRIORITY_MAP=3:1,12:3,14:7
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=none
+IPADDR=192.168.43.149
+PREFIX=24
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Vlan test-vlan-interface"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3
new file mode 100644
index 00000000..b7bdd781
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3
@@ -0,0 +1,11 @@
+TYPE=Ethernet
+DEVICE=aliasem0
+HWADDR=00:11:22:33:44:55
+BOOTPROTO=none
+ONBOOT=yes
+DNS1=4.2.2.1
+DNS2=4.2.2.2
+IPADDR=192.168.1.5
+PREFIX=24
+NETMASK=255.255.255.0
+IPV6INIT=no
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3:1 b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3:1
new file mode 100644
index 00000000..5e151875
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-aliasem3:1
@@ -0,0 +1,4 @@
+DEVICE=aliasem3:1
+IPADDR=192.168.1.6
+DEFROUTE=yes
+GATEWAY=192.168.1.1
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected
new file mode 100644
index 00000000..56e233cc
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected
@@ -0,0 +1,41 @@
+TYPE=Ethernet
+DCB=yes
+DCB_APP_FCOE_ENABLE=yes
+DCB_APP_FCOE_ADVERTISE=yes
+DCB_APP_FCOE_WILLING=yes
+DCB_APP_FCOE_PRIORITY=5
+DCB_APP_FCOE_MODE=fabric
+DCB_APP_ISCSI_ENABLE=yes
+DCB_APP_ISCSI_ADVERTISE=yes
+DCB_APP_ISCSI_WILLING=yes
+DCB_APP_ISCSI_PRIORITY=1
+DCB_APP_FIP_ENABLE=yes
+DCB_APP_FIP_ADVERTISE=yes
+DCB_APP_FIP_WILLING=yes
+DCB_APP_FIP_PRIORITY=3
+DCB_PFC_ENABLE=yes
+DCB_PFC_ADVERTISE=yes
+DCB_PFC_WILLING=yes
+DCB_PFC_UP=11010110
+DCB_PG_ENABLE=yes
+DCB_PG_ADVERTISE=yes
+DCB_PG_WILLING=yes
+DCB_PG_ID=4f6f173f
+DCB_PG_PCT=10,20,15,10,2,3,35,5
+DCB_PG_UPPCT=10,20,30,40,50,10,0,25
+DCB_PG_STRICT=10110001
+DCB_PG_UP2TC=34721056
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=yes
+IPV6_AUTOCONF=yes
+IPV6_DEFROUTE=yes
+IPV6_FAILURE_FATAL=no
+IPV6_ADDR_GEN_MODE=stable-privacy
+NAME=dcb-test
+UUID=${UUID}
+DEVICE=eth0
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected
new file mode 100644
index 00000000..ead3a047
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection.cexpected
@@ -0,0 +1,13 @@
+ESSID=blahblah
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="random wifi connection"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected
new file mode 100644
index 00000000..7bc1ae6c
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-random_wifi_connection_2.cexpected
@@ -0,0 +1,15 @@
+ESSID=SomeSSID
+MODE=Managed
+MAC_ADDRESS_RANDOMIZATION=default
+TYPE=Wireless
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="random wifi connection 2"
+UUID=${UUID}
+ONBOOT=yes
+USERS=superman
+DEFAULTKEY=1
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected
new file mode 100644
index 00000000..367d0ddb
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected
@@ -0,0 +1,12 @@
+VLAN=yes
+DEVICE=enp31s0f1-142
+PHYSDEV=enp31s0f1
+VLAN_ID=142
+REORDER_HDR=yes
+GVRP=no
+MVRP=no
+NAME=team-slave-enp31s0f1-142
+UUID=74f435bb-ede4-415a-9d48-f580b60eba04
+ONBOOT=no
+TEAM_MASTER=team142
+DEVICETYPE=TeamPort
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-eth-type b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-eth-type
new file mode 100644
index 00000000..8d295c76
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-bond-eth-type
@@ -0,0 +1,7 @@
+DEVICE=bond0
+NM_CONTROLLED=yes
+TYPE=Ethernet
+BONDING_OPTS="miimon=213 mode=4 lacp_rate=1"
+BONDING_MASTER=yes
+ONBOOT=yes
+BOOTPROTO=none
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected
new file mode 100644
index 00000000..a28c5c1c
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-static-routes-legacy.cexpected
@@ -0,0 +1,12 @@
+HWADDR=00:16:41:11:22:33
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME=test-static-routes-legacy
+UUID=ba60d05a-7898-820d-c2db-427a88f8f2a5
+DEVICE=eth0
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes
index ee2a32d8..10a63b67 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes
@@ -6,3 +6,4 @@ ADDRESS1=44.55.66.77
 NETMASK1=255.255.255.255
 GATEWAY1=192.168.1.7
 METRIC1=3
+OPTIONS1="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 window 30000 initcwnd lock 13 initrwnd 14"
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy
index cb7d42bd..3f02032a 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route-test-wired-static-routes-legacy
@@ -3,5 +3,5 @@
 
 21.31.41.0/24 via 9.9.9.9 metric 1
 	via 8.8.8.8 to 32.42.52.62
- 43.53.0.0/16 metric 3 via 7.7.7.7 dev eth2
+ 43.53.0.0/16 metric 3 via 7.7.7.7 dev eth2 cwnd 14 mtu lock 9000  initrwnd 20 window lock 10000 initcwnd lock 42 src 1.2.3.4
 
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual
index b3259ab7..8bdf0acf 100644
--- a/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/route6-test-wired-ipv6-manual
@@ -5,3 +5,5 @@ default via dead::beaf
 
 # routes without "via" are valid
 abbe::cafe/64 metric 777
+
+aaaa::cccc/64 from 1111::2222/48 via 3333::4444 src 5555::6666 mtu lock 1450 cwnd 13
diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index f467d864..babb068d 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -33,6 +33,7 @@
 #include "nm-utils.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-wired.h"
+#include "nm-setting-user.h"
 #include "nm-setting-wireless.h"
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-ip4-config.h"
@@ -81,27 +82,170 @@
 		shvarFile *const _f = (f); \
 		const char *const _key = (key); \
 		\
-		_val_string = svGetValueString (_f, _key); \
+		_val_string = svGetValueStr_cp (_f, _key); \
 		_val = svGetValue (_f, _key, &_to_free); \
 		g_assert_cmpstr (_val, ==, (expected_value)); \
 		g_assert (   (!_val_string && (!_val || !_val[0])) \
 		          || ( _val_string && nm_streq0 (_val, _val_string))); \
 	} G_STMT_END
 
-#define _writer_update_connection(connection, ifcfg_dir, filename) \
+static void
+_assert_reread_same (NMConnection *connection, NMConnection *reread)
+{
+	nmtst_assert_connection_verifies_without_normalization (reread);
+	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
+}
+
+static void
+_assert_reread_same_FIXME (NMConnection *connection, NMConnection *reread)
+{
+	gs_unref_object NMConnection *connection_normalized = NULL;
+	gs_unref_hashtable GHashTable *settings = NULL;
+
+	/* FIXME: these assertion failures should not happen as we expect
+	 * that re-reading a connection after write yields the same result.
+	 *
+	 * Needs investation and fixing. */
+	nmtst_assert_connection_verifies_without_normalization (reread);
+
+	connection_normalized = nmtst_connection_duplicate_and_normalize (connection);
+
+	g_assert (!nm_connection_compare (connection_normalized, reread, NM_SETTING_COMPARE_FLAG_EXACT));
+	g_assert (!nm_connection_diff (connection_normalized, reread, NM_SETTING_COMPARE_FLAG_EXACT, &settings));
+}
+
+/* dummy path for an "expected" file, meaning: don't check for expected
+ * written ifcfg file. */
+static const char const NO_EXPECTED[1];
+
+static void
+_assert_expected_content (NMConnection *connection, const char *filename, const char *expected)
+{
+	gs_free char *content_expectd = NULL;
+	gs_free char *content_written = NULL;
+	GError *error = NULL;
+	gsize len_expectd = 0;
+	gsize len_written = 0;
+	gboolean success;
+	const char *uuid = NULL;
+
+	g_assert (NM_IS_CONNECTION (connection));
+	g_assert (filename);
+	g_assert (g_file_test (filename, G_FILE_TEST_EXISTS));
+
+	g_assert (expected);
+	if (expected == NO_EXPECTED)
+		return;
+
+	success = g_file_get_contents (filename, &content_written, &len_written, &error);
+	nmtst_assert_success (success, error);
+
+	success = g_file_get_contents (expected, &content_expectd, &len_expectd, &error);
+	nmtst_assert_success (success, error);
+
+	{
+		gsize i, j;
+
+		for (i = 0; i < len_expectd; ) {
+			if (content_expectd[i] != '$') {
+				i++;
+				continue;
+			}
+			if (g_str_has_prefix (&content_expectd[i], "${UUID}")) {
+				GString *str;
+
+				if (!uuid) {
+					uuid = nm_connection_get_uuid (connection);
+					g_assert (uuid);
+				}
+
+				j = strlen (uuid);
+
+				str = g_string_new_len (content_expectd, len_expectd);
+				g_string_erase (str, i, NM_STRLEN ("${UUID}"));
+				g_string_insert_len (str, i, uuid, j);
+
+				g_free (content_expectd);
+				len_expectd = str->len;
+				content_expectd = g_string_free (str, FALSE);
+				i += j;
+				continue;
+			}
+
+			/* other '$' is not supported. If need be, support escaping of
+			 * '$' via '$$'. */
+			g_assert_not_reached ();
+		}
+	}
+
+	if (   len_expectd != len_written
+	    || memcmp (content_expectd, content_written, len_expectd) != 0) {
+		if (g_getenv ("NMTST_IFCFG_RH_UPDATE_EXPECTED")) {
+			if (uuid) {
+				gs_free char *search = g_strdup_printf ("UUID=%s\n", uuid);
+				const char *s;
+				gsize i;
+				GString *str;
+
+				s = content_written;
+				while (TRUE) {
+					s = strstr (s, search);
+					g_assert (s);
+					if (   s == content_written
+					    || s[-1] == '\n')
+						break;
+					s += strlen (search);
+				}
+
+				i = s - content_written;
+
+				str = g_string_new_len (content_written, len_written);
+				g_string_erase (str, i, strlen (search));
+				g_string_insert (str, i, "UUID=${UUID}\n");
+
+				len_written = str->len;
+				content_written = g_string_free (str, FALSE);
+			}
+			success = g_file_set_contents (expected, content_written, len_written, &error);
+			nmtst_assert_success (success, error);
+		} else {
+			g_error ("The content of \"%s\" (%zu) differs from \"%s\" (%zu). Set NMTST_IFCFG_RH_UPDATE_EXPECTED=yes to update the files inplace\n\n>>>%s<<<\n\n>>>%s<<<\n",
+			         filename, len_written,
+			         expected, len_expectd,
+			         content_written,
+			         content_expectd);
+		}
+	}
+}
+
+#define _writer_update_connection_reread(connection, ifcfg_dir, filename, expected, out_reread, out_reread_same) \
 	G_STMT_START { \
-		NMConnection *_connection = (connection); \
+		gs_unref_object NMConnection *_connection = nmtst_connection_duplicate_and_normalize (connection); \
+		NMConnection **_out_reread = (out_reread); \
+		gboolean *_out_reread_same = (out_reread_same); \
 		const char *_ifcfg_dir = (ifcfg_dir); \
 		const char *_filename = (filename); \
+		const char *_expected = (expected); \
 		GError *_error = NULL; \
 		gboolean _success; \
 		\
-		g_assert (NM_IS_CONNECTION (connection)); \
 		g_assert (_ifcfg_dir && _ifcfg_dir[0]); \
 		g_assert (_filename && _filename[0]); \
 		\
-		_success = writer_update_connection (_connection, _ifcfg_dir, _filename, &_error); \
+		_success = writer_update_connection (_connection, _ifcfg_dir, _filename, _out_reread, _out_reread_same, &_error); \
 		nmtst_assert_success (_success, _error); \
+		_assert_expected_content (_connection, _filename, _expected); \
+	} G_STMT_END
+
+#define _writer_update_connection(connection, ifcfg_dir, filename, expected) \
+	G_STMT_START { \
+		gs_unref_object NMConnection *_reread = NULL; \
+		NMConnection *_c = (connection); \
+		gboolean _reread_same = FALSE; \
+		\
+		_writer_update_connection_reread (_c, ifcfg_dir, filename, expected, &_reread, &_reread_same); \
+		_assert_reread_same (_c, _reread); \
+		g_assert (_reread_same); \
 	} G_STMT_END
 
 static NMConnection *
@@ -147,14 +291,19 @@ _connection_from_file_fail (const char *filename,
 }
 
 static void
-_writer_new_connection (NMConnection *connection,
-                        const char *ifcfg_dir,
-                        char **out_filename)
+_writer_new_connection_reread (NMConnection *connection,
+                               const char *ifcfg_dir,
+                               char **out_filename,
+                               const char *expected,
+                               NMConnection **out_reread,
+                               gboolean *out_reread_same)
 {
 	gboolean success;
 	GError *error = NULL;
 	char *filename = NULL;
 	gs_unref_object NMConnection *con_verified = NULL;
+	gs_unref_object NMConnection *reread_copy = NULL;
+	NMConnection **reread = out_reread ?: ((nmtst_get_rand_int () % 2) ? &reread_copy : NULL);
 
 	g_assert (NM_IS_CONNECTION (connection));
 	g_assert (ifcfg_dir);
@@ -164,14 +313,59 @@ _writer_new_connection (NMConnection *connection,
 	success = writer_new_connection (con_verified,
 	                                 ifcfg_dir,
 	                                 &filename,
+	                                 reread,
+	                                 out_reread_same,
 	                                 &error);
 	nmtst_assert_success (success, error);
 	g_assert (filename && filename[0]);
 
+	if (reread)
+		nmtst_assert_connection_verifies_without_normalization (*reread);
+
+	_assert_expected_content (con_verified, filename, expected);
+
 	if (out_filename)
 		*out_filename = filename;
 	else
 		g_free (filename);
+
+}
+
+static void
+_writer_new_connec_exp (NMConnection *connection,
+                        const char *ifcfg_dir,
+                        const char *expected,
+                        char **out_filename)
+{
+	gs_unref_object NMConnection *reread = NULL;
+	gboolean reread_same = FALSE;
+
+	_writer_new_connection_reread (connection, ifcfg_dir, out_filename, expected, &reread, &reread_same);
+	_assert_reread_same (connection, reread);
+	g_assert (reread_same);
+}
+
+static void
+_writer_new_connection (NMConnection *connection,
+                        const char *ifcfg_dir,
+                        char **out_filename)
+{
+	_writer_new_connec_exp (connection, ifcfg_dir, NO_EXPECTED, out_filename);
+}
+
+static void
+_writer_new_connection_FIXME (NMConnection *connection,
+                              const char *ifcfg_dir,
+                              char **out_filename)
+{
+	gs_unref_object NMConnection *reread = NULL;
+	gboolean reread_same = FALSE;
+
+	/* FIXME: this should not happen. Fix it to use _writer_new_connection() instead. */
+
+	_writer_new_connection_reread (connection, ifcfg_dir, out_filename, NO_EXPECTED, &reread, &reread_same);
+	_assert_reread_same_FIXME (connection, reread);
+	g_assert (!reread_same);
 }
 
 static void
@@ -179,6 +373,8 @@ _writer_new_connection_fail (NMConnection *connection,
                              const char *ifcfg_dir,
                              GError **error)
 {
+	gs_unref_object NMConnection *connection_normalized = NULL;
+	gs_unref_object NMConnection *reread = NULL;
 	gboolean success;
 	GError *local = NULL;
 	char *filename = NULL;
@@ -186,12 +382,17 @@ _writer_new_connection_fail (NMConnection *connection,
 	g_assert (NM_IS_CONNECTION (connection));
 	g_assert (ifcfg_dir);
 
-	success = writer_new_connection (connection,
+	connection_normalized = nmtst_connection_duplicate_and_normalize (connection);
+
+	success = writer_new_connection (connection_normalized,
 	                                 ifcfg_dir,
 	                                 &filename,
+	                                 &reread,
+	                                 NULL,
 	                                 &local);
 	nmtst_assert_no_success (success, local);
 	g_assert (!filename);
+	g_assert (!reread);
 
 	g_propagate_error (error, local);
 }
@@ -897,6 +1098,62 @@ test_read_wired_obsolete_gateway_n (void)
 }
 
 static void
+test_user_1 (void)
+{
+	nmtst_auto_unlinkfile char *testfile = NULL;
+	gs_unref_object NMConnection *connection = NULL;
+	gs_unref_object NMConnection *reread = NULL;
+	NMSettingUser *s_user;
+
+	connection = nmtst_create_minimal_connection ("Test User 1", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
+	s_user = NM_SETTING_USER (nm_setting_user_new ());
+
+#define _USER_SET_DATA(s_user, key, val) \
+	G_STMT_START { \
+		GError *_error = NULL; \
+		gboolean _success; \
+		\
+		_success = nm_setting_user_set_data ((s_user), (key), (val), &_error); \
+		nmtst_assert_success (_success, _error); \
+	} G_STMT_END
+
+#define _USER_SET_DATA_X(s_user, key) \
+	_USER_SET_DATA (s_user, key, "val="key"")
+
+	_USER_SET_DATA (s_user, "my.val1", "");
+	_USER_SET_DATA_X (s_user, "my.val2");
+	_USER_SET_DATA_X (s_user, "my.v__al3");
+	_USER_SET_DATA_X (s_user, "my._v");
+	_USER_SET_DATA_X (s_user, "my.v+");
+	_USER_SET_DATA_X (s_user, "my.Av");
+	_USER_SET_DATA_X (s_user, "MY.AV");
+	_USER_SET_DATA_X (s_user, "MY.8V");
+	_USER_SET_DATA_X (s_user, "MY.8-V");
+	_USER_SET_DATA_X (s_user, "MY.8_V");
+	_USER_SET_DATA_X (s_user, "MY.8+V");
+	_USER_SET_DATA_X (s_user, "MY.8/V");
+	_USER_SET_DATA_X (s_user, "MY.8=V");
+	_USER_SET_DATA_X (s_user, "MY.-");
+	_USER_SET_DATA_X (s_user, "MY._");
+	_USER_SET_DATA_X (s_user, "MY.+");
+	_USER_SET_DATA_X (s_user, "MY./");
+	_USER_SET_DATA_X (s_user, "MY.=");
+	_USER_SET_DATA_X (s_user, "my.keys.1");
+	_USER_SET_DATA_X (s_user, "my.other.KEY.42");
+
+	nm_connection_add_setting (connection, NM_SETTING (s_user));
+
+	_writer_new_connec_exp (connection,
+	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_User_1.cexpected",
+	                        &testfile);
+
+	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
+
+	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
+}
+
+static void
 test_read_wired_never_default (void)
 {
 	NMConnection *connection;
@@ -1033,6 +1290,15 @@ test_read_wired_static_routes (void)
 	g_assert_cmpint (nm_ip_route_get_prefix (ip4_route), ==, 32);
 	g_assert_cmpstr (nm_ip_route_get_next_hop (ip4_route), ==, "192.168.1.7");
 	g_assert_cmpint (nm_ip_route_get_metric (ip4_route), ==, 3);
+	nmtst_assert_route_attribute_byte (ip4_route, NM_IP_ROUTE_ATTRIBUTE_TOS, 0x28);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 30000);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 12);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITCWND, 13);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITRWND, 14);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000);
+	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE);
+	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, TRUE);
+	nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.1.1.1");
 
 	g_object_unref (connection);
 }
@@ -1094,6 +1360,14 @@ test_read_wired_static_routes_legacy (void)
 	g_assert_cmpint (nm_ip_route_get_prefix (ip4_route), ==, 16);
 	g_assert_cmpstr (nm_ip_route_get_next_hop (ip4_route), ==, "7.7.7.7");
 	g_assert_cmpint (nm_ip_route_get_metric (ip4_route), ==, 3);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 10000);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 14);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITCWND, 42);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITRWND, 20);
+	nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000);
+	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, TRUE);
+	nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE);
+	nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.2.3.4");
 
 	g_object_unref (connection);
 }
@@ -1234,7 +1508,7 @@ test_read_wired_ipv6_manual (void)
 	g_assert_cmpint (nm_ip_address_get_prefix (ip6_addr), ==, 96);
 
 	/* Routes */
-	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 2);
+	g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 3);
 	/* Route #1 */
 	ip6_route = nm_setting_ip_config_get_route (s_ip6, 0);
 	g_assert (ip6_route);
@@ -1249,6 +1523,17 @@ test_read_wired_ipv6_manual (void)
 	g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 64);
 	g_assert_cmpstr (nm_ip_route_get_next_hop (ip6_route), ==, NULL);
 	g_assert_cmpint (nm_ip_route_get_metric (ip6_route), ==, 777);
+	/* Route #3 */
+	ip6_route = nm_setting_ip_config_get_route (s_ip6, 2);
+	g_assert (ip6_route);
+	g_assert_cmpstr (nm_ip_route_get_dest (ip6_route), ==, "aaaa::cccc");
+	g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 64);
+	g_assert_cmpstr (nm_ip_route_get_next_hop (ip6_route), ==, "3333::4444");
+	nmtst_assert_route_attribute_uint32 (ip6_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 13);
+	nmtst_assert_route_attribute_uint32 (ip6_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 1450);
+	nmtst_assert_route_attribute_boolean (ip6_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE);
+	nmtst_assert_route_attribute_string (ip6_route, NM_IP_ROUTE_ATTRIBUTE_FROM, "1111::2222/48");
+	nmtst_assert_route_attribute_string (ip6_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "5555::6666");
 
 	/* DNS Addresses */
 	g_assert_cmpint (nm_setting_ip_config_get_num_dns (s_ip6), ==, 2);
@@ -1567,12 +1852,16 @@ test_read_write_802_1X_subj_matches (void)
 	g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 0), ==, "x.yourdomain.tld");
 	g_assert_cmpstr (nm_setting_802_1x_get_phase2_altsubject_match (s_8021x, 1), ==, "y.yourdomain.tld");
 
-	_writer_new_connection (connection,
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!");
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-System_test-wired-802-1X-subj-matches.cexpected",
 	                        &testfile);
+	g_test_assert_expected_messages ();
 
 	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
-	                       "*missing IEEE_8021X_CA_CERT*peap*");
+	                       "*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!");
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
 	g_test_assert_expected_messages ();
 
@@ -1624,24 +1913,38 @@ test_read_802_1x_ttls_eapgtc (void)
 }
 
 static void
-test_read_wired_aliases_good (void)
+test_read_wired_aliases_good (gconstpointer test_data)
 {
+	const int N = GPOINTER_TO_INT (test_data);
 	NMConnection *connection;
 	NMSettingConnection *s_con;
 	NMSettingIPConfig *s_ip4;
-	int expected_num_addresses = 4;
-	const char *expected_address[4] = { "192.168.1.5", "192.168.1.6", "192.168.1.9", "192.168.1.99" };
-	const char *expected_label[4] = { NULL, "aliasem0:1", "aliasem0:2", "aliasem0:99" };
+	int expected_num_addresses;
+	const char *expected_address_0[] = { "192.168.1.5", "192.168.1.6", "192.168.1.9", "192.168.1.99", NULL };
+	const char *expected_address_3[] = { "192.168.1.5", "192.168.1.6", NULL };
+	const char *expected_label_0[] = { NULL, "aliasem0:1", "aliasem0:2", "aliasem0:99", NULL, };
+	const char *expected_label_3[] = { NULL, "aliasem3:1", NULL, };
+	const char **expected_address;
+	const char **expected_label;
 	int i, j;
+	char path[256];
 
-	connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem0",
-	                                    NULL, TYPE_ETHERNET, NULL);
+	expected_address = N == 0 ? expected_address_0 : expected_address_3;
+	expected_label   = N == 0 ? expected_label_0   : expected_label_3;
+	expected_num_addresses = g_strv_length ((char **) expected_address);
+
+	nm_sprintf_buf (path, TEST_IFCFG_DIR "/network-scripts/ifcfg-aliasem%d", N);
+
+	connection = _connection_from_file (path, NULL, TYPE_ETHERNET, NULL);
 
 	/* ===== CONNECTION SETTING ===== */
 
 	s_con = nm_connection_get_setting_connection (connection);
 	g_assert (s_con);
-	g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "System aliasem0");
+	if (N == 0)
+		g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "System aliasem0");
+	else
+		g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "System aliasem3");
 
 	/* ===== IPv4 SETTING ===== */
 
@@ -1680,6 +1983,7 @@ test_read_wired_aliases_good (void)
 	}
 
 	/* Gateway */
+	g_assert (!nm_setting_ip_config_get_never_default (s_ip4));
 	g_assert_cmpstr (nm_setting_ip_config_get_gateway (s_ip4), ==, "192.168.1.1");
 
 	for (i = 0; i < expected_num_addresses; i++)
@@ -1803,8 +2107,9 @@ test_clear_master (void)
 	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, "bridge");
 
 	/* 2. write the connection to a new file */
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-System_test-bridge-component-a.cexpected",
 	                        &testfile);
 
 	/* 3. clear master and slave-type */
@@ -1816,10 +2121,13 @@ test_clear_master (void)
 	g_assert_cmpstr (nm_setting_connection_get_master (s_con), ==, NULL);
 	g_assert_cmpstr (nm_setting_connection_get_slave_type (s_con), ==, NULL);
 
+	nmtst_assert_connection_verifies_after_normalization (connection, 0, 0);
+
 	/* 4. update the connection on disk */
 	_writer_update_connection (connection,
 	                           TEST_SCRATCH_DIR "/network-scripts/",
-	                           testfile);
+	                           testfile,
+	                           TEST_IFCFG_DIR "/network-scripts/ifcfg-System_test-bridge-component-b.cexpected");
 	keyfile = utils_get_keys_path (testfile);
 	g_assert (!g_file_test (keyfile, G_FILE_TEST_EXISTS));
 
@@ -1902,9 +2210,9 @@ test_write_dns_options (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
 
@@ -3120,8 +3428,9 @@ test_write_wifi_hidden (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_WiFi_Hidden.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
@@ -3169,6 +3478,7 @@ test_write_wifi_mac_random (gconstpointer user_data)
 	const char *name, *write_expected;
 	gpointer value_p;
 	NMSettingMacRandomization value;
+	char cexpected[NM_STRLEN (TEST_IFCFG_DIR) + 100];
 
 	nmtst_test_data_unpack (user_data, &name, &value_p, &write_expected);
 	value = GPOINTER_TO_INT (value_p);
@@ -3203,8 +3513,9 @@ test_write_wifi_mac_random (gconstpointer user_data)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        nm_sprintf_buf (cexpected, TEST_IFCFG_DIR"/network-scripts/ifcfg-Test_Write_WiFi_MAC_%s.cexpected", name),
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
@@ -3255,12 +3566,13 @@ test_write_wired_wake_on_lan (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
-	val = svGetValueString (f, "ETHTOOL_OPTS");
+	val = svGetValueStr_cp (f, "ETHTOOL_OPTS");
 	g_assert (val);
 	g_assert (strstr (val, "wol"));
 	g_assert (strstr (val, "sopass 00:00:00:11:22:33"));
@@ -3290,12 +3602,13 @@ test_write_wired_auto_negotiate_off (void)
 	              NM_SETTING_WIRED_SPEED, 10,
 	              NULL);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
-	val = svGetValueString (f, "ETHTOOL_OPTS");
+	val = svGetValueStr_cp (f, "ETHTOOL_OPTS");
 	g_assert (val);
 	g_assert (strstr (val, "autoneg off"));
 	g_assert (strstr (val, "speed 10"));
@@ -3329,7 +3642,7 @@ test_write_wired_auto_negotiate_on (void)
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
-	val = svGetValueString (f, "ETHTOOL_OPTS");
+	val = svGetValueStr_cp (f, "ETHTOOL_OPTS");
 	g_assert (val);
 	g_assert (strstr (val, "autoneg on"));
 	g_assert (!strstr (val, "speed"));
@@ -3403,8 +3716,9 @@ test_write_wifi_band_a (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_WiFi_Band_A.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
@@ -3753,6 +4067,12 @@ test_write_wired_static (void)
 
 	route6 = nm_ip_route_new (AF_INET6, "::", 128, "2222:aaaa::9999", 1, &error);
 	g_assert_no_error (error);
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_TOS, g_variant_new_byte (0xb8));
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (100));
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1280));
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, g_variant_new_boolean (TRUE));
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_FROM, g_variant_new_string ("2222::bbbb/32"));
+	nm_ip_route_set_attribute (route6, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string ("::42"));
 	nm_setting_ip_config_add_route (s_ip6, route6);
 	nm_ip_route_unref (route6);
 
@@ -3766,9 +4086,9 @@ test_write_wired_static (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 	route6file = utils_get_route6_path (testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -4082,7 +4402,7 @@ test_write_wired_static_ip6_only_gw (gconstpointer user_data)
 	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
 
 	ifcfg = _svOpenFile (testfile);
-	written_ifcfg_gateway = svGetValueString (ifcfg, "IPV6_DEFAULTGW");
+	written_ifcfg_gateway = svGetValueStr_cp (ifcfg, "IPV6_DEFAULTGW");
 	svCloseFile (ifcfg);
 
 	/* access the gateway from the loaded connection. */
@@ -4147,8 +4467,9 @@ test_read_write_static_routes_legacy (void)
 	 * we can clean up after the written connection in both the original
 	 * source tree and for 'make distcheck'.
 	 */
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR_TMP,
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-test-static-routes-legacy.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -4436,9 +4757,9 @@ test_write_wired_8021x_tls (gconstpointer test_data)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
 
@@ -4486,15 +4807,15 @@ test_write_wired_8021x_tls (gconstpointer test_data)
 	}
 
 	/* Clean up created certs and keys */
-	tmp = utils_cert_path (testfile, "ca-cert.der");
+	tmp = utils_cert_path (testfile, "ca-cert", "der");
 	nmtst_file_unlink_if_exists (tmp);
 	g_free (tmp);
 
-	tmp = utils_cert_path (testfile, "client-cert.der");
+	tmp = utils_cert_path (testfile, "client-cert", "der");
 	nmtst_file_unlink_if_exists (tmp);
 	g_free (tmp);
 
-	tmp = utils_cert_path (testfile, "private-key.pem");
+	tmp = utils_cert_path (testfile, "private-key", "pem");
 	nmtst_file_unlink_if_exists (tmp);
 	g_free (tmp);
 }
@@ -4562,22 +4883,22 @@ test_write_wired_aliases (void)
 
 	/* Create some pre-existing alias files, to make sure they get overwritten / deleted. */
 	ifcfg = svCreateFile (TEST_SCRATCH_ALIAS_BASE ":2");
-	svSetValueString (ifcfg, "DEVICE", "alias0:2");
-	svSetValueString (ifcfg, "IPADDR", "192.168.1.2");
+	svSetValueStr (ifcfg, "DEVICE", "alias0:2");
+	svSetValueStr (ifcfg, "IPADDR", "192.168.1.2");
 	svWriteFile (ifcfg, 0644, NULL);
 	svCloseFile (ifcfg);
 	g_assert (g_file_test (TEST_SCRATCH_ALIAS_BASE ":2", G_FILE_TEST_EXISTS));
 
 	ifcfg = svCreateFile (TEST_SCRATCH_ALIAS_BASE ":5");
-	svSetValueString (ifcfg, "DEVICE", "alias0:5");
-	svSetValueString (ifcfg, "IPADDR", "192.168.1.5");
+	svSetValueStr (ifcfg, "DEVICE", "alias0:5");
+	svSetValueStr (ifcfg, "IPADDR", "192.168.1.5");
 	svWriteFile (ifcfg, 0644, NULL);
 	svCloseFile (ifcfg);
 	g_assert (g_file_test (TEST_SCRATCH_ALIAS_BASE ":5", G_FILE_TEST_EXISTS));
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 
 	/* Re-check the alias files */
 	g_assert (g_file_test (TEST_SCRATCH_ALIAS_BASE ":2", G_FILE_TEST_EXISTS));
@@ -5263,8 +5584,9 @@ test_write_wifi_wep_104_ascii (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
@@ -5347,8 +5669,9 @@ test_write_wifi_leap (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
@@ -5429,9 +5752,9 @@ test_write_wifi_leap_secret_flags (gconstpointer data)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
 
@@ -5701,6 +6024,11 @@ test_write_wifi_wpa_eap_tls (void)
 	nm_connection_add_setting (connection, NM_SETTING (s_8021x));
 
 	g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, "Bill Smith", NULL);
+	g_object_set (s_8021x,
+	              NM_SETTING_802_1X_PHASE1_AUTH_FLAGS,
+	              (guint) (NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE |
+	                       NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE),
+	              NULL);
 
 	nm_setting_802_1x_add_eap_method (s_8021x, "tls");
 
@@ -6080,7 +6408,8 @@ test_write_wifi_wpa_then_open (void)
 	/* Write it back out */
 	_writer_update_connection (connection,
 	                           TEST_SCRATCH_DIR "/network-scripts/",
-	                           testfile);
+	                           testfile,
+	                           TEST_IFCFG_DIR "/network-scripts/ifcfg-random_wifi_connection.cexpected");
 	keyfile = utils_get_keys_path (testfile);
 	g_assert (!g_file_test (keyfile, G_FILE_TEST_EXISTS));
 
@@ -6199,7 +6528,8 @@ test_write_wifi_wpa_then_wep_with_perms (void)
 	/* Write it back out */
 	_writer_update_connection (connection,
 	                           TEST_SCRATCH_DIR "/network-scripts/",
-	                           testfile);
+	                           testfile,
+	                           TEST_IFCFG_DIR "/network-scripts/ifcfg-random_wifi_connection_2.cexpected");
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
 
@@ -6498,8 +6828,9 @@ test_write_permissions (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Permissions.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -6572,9 +6903,9 @@ test_write_wifi_wep_agent_keys (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
-	                        TEST_SCRATCH_DIR "/network-scripts/",
-	                        &testfile);
+	_writer_new_connection_FIXME (connection,
+	                              TEST_SCRATCH_DIR "/network-scripts/",
+	                              &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_WIRELESS, NULL);
 
@@ -6953,8 +7284,9 @@ test_write_bridge_component (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -7198,8 +7530,9 @@ test_write_vlan (void)
 	connection = _connection_from_file (TEST_IFCFG_VLAN_INTERFACE,
 	                                    NULL, TYPE_VLAN, NULL);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected",
 	                        &testfile);
 }
 
@@ -7278,8 +7611,9 @@ test_write_vlan_reorder_hdr (void)
 	              NM_SETTING_VLAN_FLAGS, 1,
 	              NULL);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -7374,6 +7708,28 @@ test_read_bond_main (void)
 }
 
 static void
+test_read_bond_eth_type (void)
+{
+	NMConnection *connection;
+	NMSettingBond *s_bond;
+
+	connection = _connection_from_file (TEST_IFCFG_DIR "/network-scripts/ifcfg-test-bond-eth-type",
+	                                    NULL, TYPE_ETHERNET,NULL);
+
+	g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "bond0");
+
+	/* ===== Bonding SETTING ===== */
+
+	s_bond = nm_connection_get_setting_bond (connection);
+	g_assert (s_bond);
+
+	g_assert_cmpstr (nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MIIMON), ==, "213");
+	g_assert_cmpstr (nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_LACP_RATE), ==, "1");
+
+	g_object_unref (connection);
+}
+
+static void
 test_write_bond_main (void)
 {
 	nmtst_auto_unlinkfile char *testfile = NULL;
@@ -7436,8 +7792,9 @@ test_write_bond_main (void)
 
 	nmtst_assert_connection_verifies_without_normalization (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Bond_Main.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_BOND, NULL);
@@ -7830,8 +8187,9 @@ test_write_dcb_basic (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts//ifcfg-dcb-test.cexpected",
 	                        &testfile);
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
@@ -8228,8 +8586,9 @@ test_write_team_port (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Team_Port.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
@@ -8269,6 +8628,69 @@ test_read_team_port_empty_config (void)
 }
 
 static void
+test_team_reread_slave (void)
+{
+	nmtst_auto_unlinkfile char *testfile = NULL;
+	gs_unref_object NMConnection *connection_1 = NULL;
+	gs_unref_object NMConnection *connection_2 = NULL;
+	gs_unref_object NMConnection *reread = NULL;
+	gboolean reread_same = FALSE;
+	NMSettingConnection *s_con;
+
+	connection_1 = nmtst_create_connection_from_keyfile (
+	        "[connection]\n"
+	        "id=team-slave-enp31s0f1-142\n"
+	        "uuid=74f435bb-ede4-415a-9d48-f580b60eba04\n"
+	        "type=vlan\n"
+	        "autoconnect=false\n"
+	        "interface-name=enp31s0f1-142\n"
+	        "master=team142\n"
+	        "permissions=\n"
+	        "slave-type=team\n"
+	        "\n"
+	        "[vlan]\n"
+	        "egress-priority-map=\n"
+	        "flags=1\n"
+	        "id=142\n"
+	        "ingress-priority-map=\n"
+	        "parent=enp31s0f1\n"
+	        , "/test_team_reread_slave", NULL);
+
+	/* to double-check keyfile syntax, re-create the connection by hand. */
+	connection_2 = nmtst_create_minimal_connection ("team-slave-enp31s0f1-142", "74f435bb-ede4-415a-9d48-f580b60eba04", NM_SETTING_VLAN_SETTING_NAME, &s_con);
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, "enp31s0f1-142",
+	              NM_SETTING_CONNECTION_MASTER, "team142",
+	              NM_SETTING_CONNECTION_SLAVE_TYPE, "team",
+	              NULL);
+	g_object_set (nm_connection_get_setting_vlan (connection_2),
+	              NM_SETTING_VLAN_FLAGS, 1,
+	              NM_SETTING_VLAN_ID, 142,
+	              NM_SETTING_VLAN_PARENT, "enp31s0f1",
+	              NULL);
+	nm_connection_add_setting (connection_2, nm_setting_team_port_new ());
+	nmtst_connection_normalize (connection_2);
+
+	nmtst_assert_connection_equals (connection_1, FALSE, connection_2, FALSE);
+
+	_writer_new_connection_reread ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2,
+	                               TEST_SCRATCH_DIR "/network-scripts/",
+	                               &testfile,
+	                               TEST_IFCFG_DIR "/network-scripts/ifcfg-team-slave-enp31s0f1-142.cexpected",
+	                               &reread,
+	                               &reread_same);
+	_assert_reread_same ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, reread);
+	g_assert (reread_same);
+	g_clear_object (&reread);
+
+	reread = _connection_from_file (testfile, NULL, TYPE_VLAN,
+	                                NULL);
+	nmtst_assert_connection_equals ((nmtst_get_rand_int () % 2) ? connection_1 : connection_2, FALSE,
+	                                reread, FALSE);
+}
+
+static void
 test_read_proxy_basic (void)
 {
 	NMConnection *connection;
@@ -8327,8 +8749,9 @@ test_write_proxy_basic (void)
 
 	nmtst_assert_connection_verifies (connection);
 
-	_writer_new_connection (connection,
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR "/network-scripts/",
+	                        TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
@@ -8682,12 +9105,12 @@ test_write_unknown (gconstpointer test_data)
 
 	sv = _svOpenFile (testfile);
 
-	svFileSetName (sv, filename_tmp_1);
-	svFileSetModified (sv);
+	svFileSetName_test_only (sv, filename_tmp_1);
+	svFileSetModified_test_only (sv);
 
 	if (g_str_has_suffix (testfile, "ifcfg-test-write-unknown-4")) {
 		_svGetValue_check (sv, "NAME", "l4x");
-		_svGetValue_check (sv, "NAME2", NULL);
+		_svGetValue_check (sv, "NAME2", "");
 		_svGetValue_check (sv, "NAME3", "name3-value");
 
 		svSetValue (sv, "NAME", "set-by-test1");
@@ -8944,6 +9367,8 @@ int main (int argc, char **argv)
 	nmtst_add_test_func (TPATH "wired/read/manual/3", test_read_wired_ipv4_manual, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-ipv4-manual-3", "System test-wired-ipv4-manual-3");
 	nmtst_add_test_func (TPATH "wired/read/manual/4", test_read_wired_ipv4_manual, TEST_IFCFG_DIR "/network-scripts/ifcfg-test-wired-ipv4-manual-4", "System test-wired-ipv4-manual-4");
 
+	g_test_add_func (TPATH "user/1", test_user_1);
+
 	g_test_add_func (TPATH "wired/ipv6-manual", test_read_wired_ipv6_manual);
 
 	nmtst_add_test_func (TPATH "wired-ipv6-only/0", test_read_wired_ipv6_only, TEST_IFCFG_DIR"/network-scripts/ifcfg-test-wired-ipv6-only",   "System test-wired-ipv6-only");
@@ -8960,7 +9385,8 @@ int main (int argc, char **argv)
 
 	g_test_add_func (TPATH "802-1x/subj-matches", test_read_write_802_1X_subj_matches);
 	g_test_add_func (TPATH "802-1x/ttls-eapgtc", test_read_802_1x_ttls_eapgtc);
-	g_test_add_func (TPATH "wired/read/aliases", test_read_wired_aliases_good);
+	g_test_add_data_func (TPATH "wired/read/aliases/good/0", GINT_TO_POINTER (0), test_read_wired_aliases_good);
+	g_test_add_data_func (TPATH "wired/read/aliases/good/3", GINT_TO_POINTER (3), test_read_wired_aliases_good);
 	g_test_add_func (TPATH "wired/read/aliases/bad1", test_read_wired_aliases_bad_1);
 	g_test_add_func (TPATH "wired/read/aliases/bad2", test_read_wired_aliases_bad_2);
 	g_test_add_func (TPATH "wifi/read/open", test_read_wifi_open);
@@ -9112,6 +9538,7 @@ int main (int argc, char **argv)
 	g_test_add_data_func (TPATH "fcoe/write-vn2vn", (gpointer) NM_SETTING_DCB_FCOE_MODE_VN2VN, test_write_fcoe_mode);
 
 	g_test_add_func (TPATH "bond/read-master", test_read_bond_main);
+	g_test_add_func (TPATH "bond/read-master-eth-type", test_read_bond_eth_type);
 	g_test_add_func (TPATH "bond/read-slave", test_read_bond_slave);
 	g_test_add_func (TPATH "bond/read-slave-ib", test_read_bond_slave_ib);
 	g_test_add_func (TPATH "bond/write-master", test_write_bond_main);
@@ -9133,6 +9560,7 @@ int main (int argc, char **argv)
 	g_test_add_data_func (TPATH "team/read-port-2", TEST_IFCFG_DIR"/network-scripts/ifcfg-test-team-port-2", test_read_team_port);
 	g_test_add_func (TPATH "team/write-port", test_write_team_port);
 	g_test_add_func (TPATH "team/read-port-empty-config", test_read_team_port_empty_config);
+	g_test_add_func (TPATH "team/reread-slave", test_team_reread_slave);
 
 	g_test_add_func (TPATH "proxy/read-proxy-basic", test_read_proxy_basic);
 	g_test_add_func (TPATH "proxy/write-proxy-basic", test_write_proxy_basic);
diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c
index 84f2d3f4..c5129fea 100644
--- a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c
+++ b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.c
@@ -31,6 +31,7 @@
 #include "settings/nm-settings-plugin.h"
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
+#include "nm-setting-metadata.h"
 
 #include "nms-ifnet-net-utils.h"
 #include "nms-ifnet-wpa-parser.h"
@@ -1688,96 +1689,43 @@ error:
 	return NULL;
 }
 
-typedef NMSetting8021xCKScheme (*SchemeFunc) (NMSetting8021x * setting);
-typedef const char *(*PathFunc) (NMSetting8021x * setting);
-typedef GBytes *(*BlobFunc) (NMSetting8021x * setting);
-
-typedef struct ObjectType {
-	const char *setting_key;
-	SchemeFunc scheme_func;
-	PathFunc path_func;
-	BlobFunc blob_func;
-	const char *conn_name_key;
-	const char *suffix;
-} ObjectType;
-
-static const ObjectType ca_type = {
-	NM_SETTING_802_1X_CA_CERT,
-	nm_setting_802_1x_get_ca_cert_scheme,
-	nm_setting_802_1x_get_ca_cert_path,
-	nm_setting_802_1x_get_ca_cert_blob,
-	"ca_cert",
-	"ca-cert.der"
-};
-
-static const ObjectType phase2_ca_type = {
-	NM_SETTING_802_1X_PHASE2_CA_CERT,
-	nm_setting_802_1x_get_phase2_ca_cert_scheme,
-	nm_setting_802_1x_get_phase2_ca_cert_path,
-	nm_setting_802_1x_get_phase2_ca_cert_blob,
-	"ca_cert2",
-	"inner-ca-cert.der"
-};
-
-static const ObjectType client_type = {
-	NM_SETTING_802_1X_CLIENT_CERT,
-	nm_setting_802_1x_get_client_cert_scheme,
-	nm_setting_802_1x_get_client_cert_path,
-	nm_setting_802_1x_get_client_cert_blob,
-	"client_cert",
-	"client-cert.der"
-};
-
-static const ObjectType phase2_client_type = {
-	NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
-	nm_setting_802_1x_get_phase2_client_cert_scheme,
-	nm_setting_802_1x_get_phase2_client_cert_path,
-	nm_setting_802_1x_get_phase2_client_cert_blob,
-	"client_cert2",
-	"inner-client-cert.der"
-};
-
-static const ObjectType pk_type = {
-	NM_SETTING_802_1X_PRIVATE_KEY,
-	nm_setting_802_1x_get_private_key_scheme,
-	nm_setting_802_1x_get_private_key_path,
-	nm_setting_802_1x_get_private_key_blob,
-	"private_key",
-	"private-key.pem"
-};
-
-static const ObjectType phase2_pk_type = {
-	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-	nm_setting_802_1x_get_phase2_private_key_scheme,
-	nm_setting_802_1x_get_phase2_private_key_path,
-	nm_setting_802_1x_get_phase2_private_key_blob,
-	"private_key2",
-	"inner-private-key.pem"
-};
-
-static const ObjectType p12_type = {
-	NM_SETTING_802_1X_PRIVATE_KEY,
-	nm_setting_802_1x_get_private_key_scheme,
-	nm_setting_802_1x_get_private_key_path,
-	nm_setting_802_1x_get_private_key_blob,
-	"private_key",
-	"private-key.p12"
-};
-
-static const ObjectType phase2_p12_type = {
-	NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-	nm_setting_802_1x_get_phase2_private_key_scheme,
-	nm_setting_802_1x_get_phase2_private_key_path,
-	nm_setting_802_1x_get_phase2_private_key_blob,
-	"private_key2",
-	"inner-private-key.p12"
+typedef struct Setting8021xSchemeVtable {
+	const NMSetting8021xSchemeVtable *vtable;
+	const char *ifnet_key;
+} Setting8021xSchemeVtable;
+
+static const Setting8021xSchemeVtable setting_8021x_scheme_vtable[] = {
+	[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT],
+		.ifnet_key              = "ca_cert",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT],
+		.ifnet_key              = "ca_cert2",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT],
+		.ifnet_key              = "client_cert",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT],
+		.ifnet_key              = "client_cert2",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY],
+		.ifnet_key              = "private_key",
+	},
+	[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY] = {
+		.vtable                 = &nm_setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY],
+		.ifnet_key              = "private_key2",
+	},
 };
 
 static gboolean
 write_object (NMSetting8021x *s_8021x,
               const char *conn_name,
               GBytes *override_data,
-              const ObjectType *objtype,
+              const Setting8021xSchemeVtable *objtype,
               GError **error)
 {
 	NMSetting8021xCKScheme scheme;
@@ -1792,13 +1740,13 @@ write_object (NMSetting8021x *s_8021x,
 		 */
 		blob = override_data;
 	else {
-		scheme = (*(objtype->scheme_func)) (s_8021x);
+		scheme = (*(objtype->vtable->scheme_func)) (s_8021x);
 		switch (scheme) {
 		case NM_SETTING_802_1X_CK_SCHEME_BLOB:
-			blob = (*(objtype->blob_func)) (s_8021x);
+			blob = (*(objtype->vtable->blob_func)) (s_8021x);
 			break;
 		case NM_SETTING_802_1X_CK_SCHEME_PATH:
-			path = (*(objtype->path_func)) (s_8021x);
+			path = (*(objtype->vtable->path_func)) (s_8021x);
 			break;
 		default:
 			break;
@@ -1809,8 +1757,8 @@ write_object (NMSetting8021x *s_8021x,
 	 * may have been sent.
 	 */
 	if (path) {
-		wpa_set_data (conn_name, (gchar *) objtype->conn_name_key,
-			      (gchar *) path);
+		wpa_set_data (conn_name, (gchar *) objtype->ifnet_key,
+		              (gchar *) path);
 		return TRUE;
 	}
 
@@ -1828,17 +1776,16 @@ write_8021x_certs (NMSetting8021x *s_8021x,
                    GError **error)
 {
 	char *password = NULL;
-	const ObjectType *otype = NULL;
+	const Setting8021xSchemeVtable *otype = NULL;
 	gboolean is_pkcs12 = FALSE, success = FALSE;
 	GBytes *blob = NULL;
 	GBytes *enc_key = NULL;
 	gchar *generated_pw = NULL;
 
 	/* CA certificate */
-	if (phase2)
-		otype = &phase2_ca_type;
-	else
-		otype = &ca_type;
+	otype = phase2
+	        ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CA_CERT]
+	        : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CA_CERT];
 
 	if (!write_object (s_8021x, conn_name, NULL, otype, error))
 		return FALSE;
@@ -1864,14 +1811,13 @@ write_8021x_certs (NMSetting8021x *s_8021x,
 		    nm_setting_802_1x_get_private_key_password (s_8021x);
 	}
 
-	if (is_pkcs12)
-		otype = phase2 ? &phase2_p12_type : &p12_type;
-	else
-		otype = phase2 ? &phase2_pk_type : &pk_type;
+	otype = phase2
+	        ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_PRIVATE_KEY]
+	        : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PRIVATE_KEY];
 
-	if ((*(otype->scheme_func)) (s_8021x) ==
+	if ((*(otype->vtable->scheme_func)) (s_8021x) ==
 	    NM_SETTING_802_1X_CK_SCHEME_BLOB)
-		blob = (*(otype->blob_func)) (s_8021x);
+		blob = (*(otype->vtable->blob_func)) (s_8021x);
 
 	/* Only do the private key re-encrypt dance if we got the raw key data, which
 	 * by definition will be unencrypted.  If we're given a direct path to the
@@ -1883,7 +1829,7 @@ write_8021x_certs (NMSetting8021x *s_8021x,
 		/* Encrypt the unencrypted private key with the fake password */
 		tmp_enc_key =
 		    nm_utils_rsa_key_encrypt (g_bytes_get_data (blob, NULL), g_bytes_get_size (blob),
-					      password, &generated_pw, error);
+		                              password, &generated_pw, error);
 		if (!tmp_enc_key)
 			goto out;
 
@@ -1906,12 +1852,11 @@ write_8021x_certs (NMSetting8021x *s_8021x,
 	/* Client certificate */
 	if (is_pkcs12) {
 		wpa_set_data (conn_name,
-			      phase2 ? "client_cert2" : "client_cert", NULL);
+		              phase2 ? "client_cert2" : "client_cert", NULL);
 	} else {
-		if (phase2)
-			otype = &phase2_client_type;
-		else
-			otype = &client_type;
+		otype = phase2
+		        ? &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_PHASE2_CLIENT_CERT]
+		        : &setting_8021x_scheme_vtable[NM_SETTING_802_1X_SCHEME_TYPE_CLIENT_CERT];
 
 		/* Save the client certificate */
 		if (!write_object (s_8021x, conn_name, NULL, otype, error))
diff --git a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h
index e6c0e88c..51bc34b9 100644
--- a/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h
+++ b/src/settings/plugins/ifnet/nms-ifnet-connection-parser.h
@@ -22,7 +22,7 @@
 #ifndef _CONNECTION_PARSER_H
 #define _CONNECTION_PARSER_H
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 #include "nms-ifnet-net-parser.h"
 
diff --git a/src/settings/plugins/ifnet/nms-ifnet-net-utils.h b/src/settings/plugins/ifnet/nms-ifnet-net-utils.h
index 79d44642..cc273aa7 100644
--- a/src/settings/plugins/ifnet/nms-ifnet-net-utils.h
+++ b/src/settings/plugins/ifnet/nms-ifnet-net-utils.h
@@ -26,8 +26,8 @@
 
 #include <arpa/inet.h>
 
-#include <nm-setting-ip6-config.h>
-#include <nm-setting-ip4-config.h>
+#include "nm-setting-ip6-config.h"
+#include "nm-setting-ip4-config.h"
 
 #include "nms-ifnet-net-parser.h"
 
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
index 36e52e2c..6a86bf86 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.h
@@ -24,7 +24,7 @@
 #ifndef __PARSER_H__
 #define __PARSER_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 #include "nms-ifupdown-interface-parser.h"
 
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
index 211e5d57..189b6e69 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
@@ -28,8 +28,8 @@
 
 #include <string.h>
 #include <arpa/inet.h>
-#include <gudev/gudev.h>
 #include <gmodule.h>
+#include <libudev.h>
 
 #include "nm-setting-connection.h"
 #include "nm-dbus-interface.h"
@@ -42,6 +42,7 @@
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 #include "nm-config.h"
+#include "nm-utils/nm-udev-utils.h"
 
 #include "nms-ifupdown-interface-parser.h"
 #include "nms-ifupdown-connection.h"
@@ -62,7 +63,7 @@
 /*****************************************************************************/
 
 typedef struct {
-	GUdevClient *client;
+	NMUdevClient *udev_client;
 
 	GHashTable *connections;  /* /e/n/i block name :: NMIfupdownConnection */
 
@@ -103,21 +104,21 @@ NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfupdown, settings_plugin_ifupdown_get
 
 static void
 bind_device_to_connection (SettingsPluginIfupdown *self,
-                           GUdevDevice *device,
+                           struct udev_device *device,
                            NMIfupdownConnection *exported)
 {
 	NMSettingWired *s_wired;
 	NMSettingWireless *s_wifi;
 	const char *iface, *address;
 
-	iface = g_udev_device_get_name (device);
+	iface = udev_device_get_sysname (device);
 	if (!iface) {
 		nm_log_warn (LOGD_SETTINGS, "failed to get ifname for device.");
 		return;
 	}
 
-	address = g_udev_device_get_sysfs_attr (device, "address");
-	if (!address || !strlen (address)) {
+	address = udev_device_get_sysattr_value (device, "address");
+	if (!address || !address[0]) {
 		nm_log_warn (LOGD_SETTINGS, "failed to get MAC address for %s", iface);
 		return;
 	}
@@ -142,14 +143,14 @@ bind_device_to_connection (SettingsPluginIfupdown *self,
 }
 
 static void
-udev_device_added (SettingsPluginIfupdown *self, GUdevDevice *device)
+udev_device_added (SettingsPluginIfupdown *self, struct udev_device *device)
 {
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
 	const char *iface, *path;
 	NMIfupdownConnection *exported;
 
-	iface = g_udev_device_get_name (device);
-	path = g_udev_device_get_sysfs_path (device);
+	iface = udev_device_get_sysname (device);
+	path = udev_device_get_syspath (device);
 	if (!iface || !path)
 		return;
 
@@ -165,7 +166,7 @@ udev_device_added (SettingsPluginIfupdown *self, GUdevDevice *device)
 		return;
 	}
 
-	g_hash_table_insert (priv->kernel_ifaces, g_strdup (iface), g_object_ref (device));
+	g_hash_table_insert (priv->kernel_ifaces, g_strdup (iface), udev_device_ref (device));
 
 	if (exported)
 		bind_device_to_connection (self, device, exported);
@@ -175,13 +176,13 @@ udev_device_added (SettingsPluginIfupdown *self, GUdevDevice *device)
 }
 
 static void
-udev_device_removed (SettingsPluginIfupdown *self, GUdevDevice *device)
+udev_device_removed (SettingsPluginIfupdown *self, struct udev_device *device)
 {
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
 	const char *iface, *path;
 
-	iface = g_udev_device_get_name (device);
-	path = g_udev_device_get_sysfs_path (device);
+	iface = udev_device_get_sysname (device);
+	path = udev_device_get_syspath (device);
 	if (!iface || !path)
 		return;
 
@@ -195,13 +196,13 @@ udev_device_removed (SettingsPluginIfupdown *self, GUdevDevice *device)
 }
 
 static void
-udev_device_changed (SettingsPluginIfupdown *self, GUdevDevice *device)
+udev_device_changed (SettingsPluginIfupdown *self, struct udev_device *device)
 {
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
 	const char *iface, *path;
 
-	iface = g_udev_device_get_name (device);
-	path = g_udev_device_get_sysfs_path (device);
+	iface = udev_device_get_sysname (device);
+	path = udev_device_get_syspath (device);
 	if (!iface || !path)
 		return;
 
@@ -215,20 +216,21 @@ udev_device_changed (SettingsPluginIfupdown *self, GUdevDevice *device)
 }
 
 static void
-handle_uevent (GUdevClient *client,
-               const char *action,
-               GUdevDevice *device,
+handle_uevent (NMUdevClient *client,
+               struct udev_device *device,
                gpointer user_data)
 {
 	SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (user_data);
 	const char *subsys;
+	const char *action;
+
+	action = udev_device_get_action (device);
 
 	g_return_if_fail (action != NULL);
 
 	/* A bit paranoid */
-	subsys = g_udev_device_get_subsystem (device);
-	g_return_if_fail (subsys != NULL);
-	g_return_if_fail (strcmp (subsys, "net") == 0);
+	subsys = udev_device_get_subsystem (device);
+	g_return_if_fail (nm_streq0 (subsys, "net"));
 
 	if (!strcmp (action, "add"))
 		udev_device_added (self, device);
@@ -271,7 +273,7 @@ get_unmanaged_specs (NMSettingsPlugin *config)
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE ((SettingsPluginIfupdown *) config);
 	GSList *specs = NULL;
 	GHashTableIter iter;
-	GUdevDevice *device;
+	struct udev_device *device;
 	const char *iface;
 
 	if (!ALWAYS_UNMANAGE && !priv->unmanage_well_known)
@@ -284,7 +286,7 @@ get_unmanaged_specs (NMSettingsPlugin *config)
 	while (g_hash_table_iter_next (&iter, (gpointer) &iface, (gpointer) &device)) {
 		const char *address;
 
-		address = g_udev_device_get_sysfs_attr (device, "address");
+		address = udev_device_get_sysattr_value (device, "address");
 		if (address)
 			specs = g_slist_append (specs, g_strdup_printf ("mac:%s", address));
 		else
@@ -318,17 +320,23 @@ get_property (GObject *object, guint prop_id,
 /*****************************************************************************/
 
 static void
+_udev_device_unref (gpointer ptr)
+{
+	udev_device_unref (ptr);
+}
+
+static void
 init (NMSettingsPlugin *config)
 {
 	SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (config);
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
 	GHashTable *auto_ifaces;
 	if_block *block = NULL;
-	GList *keys, *iter;
+	struct udev_enumerate *enumerate;
+	struct udev_list_entry *keys;
 	GHashTableIter con_iter;
 	const char *block_name;
 	NMIfupdownConnection *connection;
-	const char *subsys[2] = { "net", NULL };
 
 	auto_ifaces = g_hash_table_new (g_str_hash, g_str_equal);
 
@@ -336,18 +344,15 @@ init (NMSettingsPlugin *config)
 		priv->connections = g_hash_table_new (g_str_hash, g_str_equal);
 
 	if(!priv->kernel_ifaces)
-		priv->kernel_ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+		priv->kernel_ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _udev_device_unref);
 
 	if(!priv->eni_ifaces)
 		priv->eni_ifaces = g_hash_table_new (g_str_hash, g_str_equal);
 
 	nm_log_info (LOGD_SETTINGS, "init!");
 
-	priv->client = g_udev_client_new (subsys);
-	if (!priv->client) {
-		nm_log_warn (LOGD_SETTINGS, "    error initializing libgudev");
-	} else
-		g_signal_connect (priv->client, "uevent", G_CALLBACK (handle_uevent), self);
+	priv->udev_client = nm_udev_client_new ((const char *[]) { "net", NULL },
+	                                        handle_uevent, self);
 
 	/* Read in all the interfaces */
 	ifparser_init (ENI_INTERFACES_FILE, 0);
@@ -445,12 +450,20 @@ init (NMSettingsPlugin *config)
 	nm_log_info (LOGD_SETTINGS, "management mode: %s", priv->unmanage_well_known ? "unmanaged" : "managed");
 
 	/* Add well-known interfaces */
-	keys = g_udev_client_query_by_subsystem (priv->client, "net");
-	for (iter = keys; iter; iter = g_list_next (iter)) {
-		udev_device_added (self, G_UDEV_DEVICE (iter->data));
-		g_object_unref (G_UDEV_DEVICE (iter->data));
+	enumerate = nm_udev_client_enumerate_new (priv->udev_client);
+	udev_enumerate_scan_devices (enumerate);
+	keys = udev_enumerate_get_list_entry (enumerate);
+	for (; keys; keys = udev_list_entry_get_next (keys)) {
+		struct udev_device *udevice;
+
+		udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate),
+		                                        udev_list_entry_get_name (keys));
+		if (udevice) {
+			udev_device_added (self, udevice);
+			udev_device_unref (udevice);
+		}
 	}
-	g_list_free (keys);
+	udev_enumerate_unref (enumerate);
 
 	/* Now if we're running in managed mode, let NM know there are new connections */
 	if (!priv->unmanage_well_known) {
@@ -483,7 +496,8 @@ dispose (GObject *object)
 
 	g_clear_pointer (&priv->kernel_ifaces, g_hash_table_destroy);
 	g_clear_pointer (&priv->eni_ifaces, g_hash_table_destroy);
-	g_clear_object (&priv->client);
+
+	priv->udev_client = nm_udev_client_unref (priv->udev_client);
 
 	G_OBJECT_CLASS (settings_plugin_ifupdown_parent_class)->dispose (object);
 }
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c
index ff654acf..bd07d263 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-connection.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-connection.c
@@ -58,12 +58,16 @@ commit_changes (NMSettingsConnection *connection,
 {
 	char *path = NULL;
 	GError *error = NULL;
+	gs_unref_object NMConnection *reread = NULL;
+	gboolean reread_same = FALSE;
 
 	if (!nms_keyfile_writer_connection (NM_CONNECTION (connection),
 	                                    nm_settings_connection_get_filename (connection),
 	                                    NM_FLAGS_ALL (commit_reason,   NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION
 	                                                                 | NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED),
 	                                    &path,
+	                                    &reread,
+	                                    &reread_same,
 	                                    &error)) {
 		callback (connection, error, user_data);
 		g_clear_error (&error);
@@ -89,6 +93,18 @@ commit_changes (NMSettingsConnection *connection,
 		             NMS_KEYFILE_CONNECTION_LOG_ARG (connection));
 	}
 
+	if (reread && !reread_same) {
+		gs_free_error GError *local = NULL;
+
+		if (!nm_settings_connection_replace_settings (connection, reread, FALSE, "update-during-write", &local)) {
+			nm_log_warn (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" after persisting connection failed: %s",
+			             NMS_KEYFILE_CONNECTION_LOG_ARG (connection), local->message);
+		} else {
+			nm_log_info (LOGD_SETTINGS, "keyfile: update "NMS_KEYFILE_CONNECTION_LOG_FMT" after persisting connection",
+			             NMS_KEYFILE_CONNECTION_LOG_ARG (connection));
+		}
+	}
+
 	g_free (path);
 
 	NM_SETTINGS_CONNECTION_CLASS (nms_keyfile_connection_parent_class)->commit_changes (connection,
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index 97306d66..4af80142 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -77,7 +77,7 @@ G_DEFINE_TYPE_EXTENDED (NMSKeyfilePlugin, nms_keyfile_plugin, G_TYPE_OBJECT, 0,
 #define _NMLOG_PREFIX_NAME      "keyfile"
 #define _NMLOG_DOMAIN           LOGD_SETTINGS
 #define _NMLOG(level, ...) \
-    nm_log ((level), _NMLOG_DOMAIN, \
+    nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
             "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
             _NMLOG_PREFIX_NAME": " \
             _NM_UTILS_MACRO_REST (__VA_ARGS__))
@@ -530,12 +530,19 @@ add_connection (NMSettingsPlugin *config,
 {
 	NMSKeyfilePlugin *self = NMS_KEYFILE_PLUGIN (config);
 	gs_free char *path = NULL;
+	gs_unref_object NMConnection *reread = NULL;
 
 	if (save_to_disk) {
-		if (!nms_keyfile_writer_connection (connection, NULL, FALSE, &path, error))
+		if (!nms_keyfile_writer_connection (connection,
+		                                    NULL,
+		                                    FALSE,
+		                                    &path,
+		                                    &reread,
+		                                    NULL,
+		                                    error))
 			return NULL;
 	}
-	return NM_SETTINGS_CONNECTION (update_connection (self, connection, path, NULL, FALSE, NULL, error));
+	return NM_SETTINGS_CONNECTION (update_connection (self, reread ?: connection, path, NULL, FALSE, NULL, error));
 }
 
 static GSList *
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index 39a01480..cb4b8379 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -56,6 +56,10 @@ _fmt_warn (const char *group, NMSetting *setting, const char *property_name, con
 		return message;
 }
 
+typedef struct {
+	bool verbose;
+} HandlerReadData;
+
 static gboolean
 _handler_read (GKeyFile *keyfile,
                NMConnection *connection,
@@ -64,11 +68,16 @@ _handler_read (GKeyFile *keyfile,
                void *user_data,
                GError **error)
 {
+	const HandlerReadData *handler_data = user_data;
+
 	if (type == NM_KEYFILE_READ_TYPE_WARN) {
 		NMKeyfileReadTypeDataWarn *warn_data = type_data;
 		NMLogLevel level;
 		char *message_free = NULL;
 
+		if (!handler_data->verbose)
+			return TRUE;
+
 		if (warn_data->severity > NM_KEYFILE_WARN_SEVERITY_WARN)
 			level = LOGL_ERR;
 		else if (warn_data->severity >= NM_KEYFILE_WARN_SEVERITY_WARN)
@@ -78,7 +87,9 @@ _handler_read (GKeyFile *keyfile,
 		else
 			level = LOGL_INFO;
 
-		nm_log (level, LOGD_SETTINGS, "keyfile: %s",
+		nm_log (level, LOGD_SETTINGS, NULL,
+		        nm_connection_get_uuid (connection),
+		        "keyfile: %s",
 		        _fmt_warn (warn_data->group, warn_data->setting,
 		                   warn_data->property_name, warn_data->message,
 		                   &message_free));
@@ -89,6 +100,19 @@ _handler_read (GKeyFile *keyfile,
 }
 
 NMConnection *
+nms_keyfile_reader_from_keyfile (GKeyFile *key_file,
+                                 const char *filename,
+                                 gboolean verbose,
+                                 GError **error)
+{
+	HandlerReadData data = {
+		.verbose = verbose,
+	};
+
+	return nm_keyfile_read (key_file, filename, NULL, _handler_read, &data, error);
+}
+
+NMConnection *
 nms_keyfile_reader_from_file (const char *filename, GError **error)
 {
 	GKeyFile *key_file;
@@ -122,7 +146,7 @@ nms_keyfile_reader_from_file (const char *filename, GError **error)
 	if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error))
 		goto out;
 
-	connection = nm_keyfile_read (key_file, filename, NULL, _handler_read, NULL, error);
+	connection = nms_keyfile_reader_from_keyfile (key_file, filename, TRUE, error);
 	if (!connection)
 		goto out;
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.h b/src/settings/plugins/keyfile/nms-keyfile-reader.h
index c52fea31..b60c1e69 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.h
@@ -22,7 +22,12 @@
 #ifndef __NMS_KEYFILE_READER_H__
 #define __NMS_KEYFILE_READER_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
+
+NMConnection *nms_keyfile_reader_from_keyfile (GKeyFile *key_file,
+                                               const char *filename,
+                                               gboolean verbose,
+                                               GError **error);
 
 NMConnection *nms_keyfile_reader_from_file (const char *filename, GError **error);
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c
index 95897db3..92ed2849 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -32,6 +32,7 @@
 #include "nm-keyfile-internal.h"
 
 #include "nms-keyfile-utils.h"
+#include "nms-keyfile-reader.h"
 
 /*****************************************************************************/
 
@@ -51,12 +52,12 @@ cert_writer (NMConnection *connection,
 	NMSetting8021xCKFormat format;
 	const char *path = NULL, *ext = "pem";
 
-	scheme = cert_data->scheme_func (cert_data->setting);
+	scheme = cert_data->vtable->scheme_func (cert_data->setting);
 	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
 		char *tmp = NULL;
 		const char *accepted_path = NULL;
 
-		path = cert_data->path_func (cert_data->setting);
+		path = cert_data->vtable->path_func (cert_data->setting);
 		g_assert (path);
 
 		if (g_str_has_prefix (path, info->keyfile_dir)) {
@@ -92,11 +93,11 @@ cert_writer (NMConnection *connection,
 
 		if (!accepted_path)
 			accepted_path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL);
-		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, accepted_path);
+		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key, accepted_path);
 		g_free (tmp);
 	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
-		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name,
-		                                 cert_data->uri_func (cert_data->setting));
+		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key,
+		                                 cert_data->vtable->uri_func (cert_data->setting));
 	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
 		GBytes *blob;
 		const guint8 *blob_data;
@@ -105,13 +106,13 @@ cert_writer (NMConnection *connection,
 		GError *local = NULL;
 		char *new_path;
 
-		blob = cert_data->blob_func (cert_data->setting);
+		blob = cert_data->vtable->blob_func (cert_data->setting);
 		g_assert (blob);
 		blob_data = g_bytes_get_data (blob, &blob_len);
 
-		if (cert_data->format_func) {
+		if (cert_data->vtable->format_func) {
 			/* Get the extension for a private key */
-			format = cert_data->format_func (cert_data->setting);
+			format = cert_data->vtable->format_func (cert_data->setting);
 			if (format == NM_SETTING_802_1X_CK_FORMAT_PKCS12)
 				ext = "p12";
 		} else {
@@ -124,17 +125,17 @@ cert_writer (NMConnection *connection,
 		 * from now on instead of pushing around the certificate data.
 		 */
 		new_path = g_strdup_printf ("%s/%s-%s.%s", info->keyfile_dir, nm_connection_get_uuid (connection),
-		                            cert_data->suffix, ext);
+		                            cert_data->vtable->file_suffix, ext);
 
 		success = nm_utils_file_set_contents (new_path, (const gchar *) blob_data,
 		                                      blob_len, 0600, &local);
 		if (success) {
 			/* Write the path value to the keyfile.
 			 * We know, that basename(new_path) starts with a UUID, hence no conflict with "data:;base64,"  */
-			nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->property_name, strrchr (new_path, '/') + 1);
+			nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key, strrchr (new_path, '/') + 1);
 		} else {
 			nm_log_warn (LOGD_SETTINGS, "keyfile: %s.%s: failed to write certificate to file %s: %s",
-			             setting_name, cert_data->property_name, new_path, local->message);
+			             setting_name, cert_data->vtable->setting_key, new_path, local->message);
 			g_error_free (local);
 		}
 		g_free (new_path);
@@ -174,9 +175,11 @@ _internal_write_connection (NMConnection *connection,
                             const char *existing_path,
                             gboolean force_rename,
                             char **out_path,
+                            NMConnection **out_reread,
+                            gboolean *out_reread_same,
                             GError **error)
 {
-	GKeyFile *key_file;
+	gs_unref_keyfile GKeyFile *key_file = NULL;
 	gs_free char *data = NULL;
 	gsize len;
 	gs_free char *path = NULL;
@@ -188,8 +191,15 @@ _internal_write_connection (NMConnection *connection,
 	g_return_val_if_fail (!out_path || !*out_path, FALSE);
 	g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE);
 
-	if (!nm_connection_verify (connection, error))
+	switch (_nm_connection_verify (connection, error)) {
+	case NM_SETTING_VERIFY_NORMALIZABLE:
+		nm_assert_not_reached ();
+		/* fall-through */
+	case NM_SETTING_VERIFY_SUCCESS:
+		break;
+	default:
 		g_return_val_if_reached (FALSE);
+	}
 
 	id = nm_connection_get_id (connection);
 	g_assert (id && *id);
@@ -200,7 +210,6 @@ _internal_write_connection (NMConnection *connection,
 	if (!key_file)
 		return FALSE;
 	data = g_key_file_to_data (key_file, &len, error);
-	g_key_file_unref (key_file);
 	if (!data)
 		return FALSE;
 
@@ -290,15 +299,48 @@ _internal_write_connection (NMConnection *connection,
 		path = NULL;
 	}
 
+	if (out_reread || out_reread_same)
+	{
+		gs_unref_object NMConnection *reread = NULL;
+		gboolean reread_same = FALSE;
+
+		reread = nms_keyfile_reader_from_keyfile (key_file, path, FALSE, NULL);
+
+		nm_assert (NM_IS_CONNECTION (reread));
+
+		if (   reread
+		    && !nm_connection_normalize (reread, NULL, NULL, NULL)) {
+			nm_assert_not_reached ();
+			g_clear_object (&reread);
+		}
+
+		if (reread && out_reread_same) {
+			reread_same = !!nm_connection_compare (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT);
+
+			nm_assert (reread_same == nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT));
+			nm_assert (reread_same == ({
+			                                gs_unref_hashtable GHashTable *_settings = NULL;
+
+			                                (   nm_connection_diff (reread, connection, NM_SETTING_COMPARE_FLAG_EXACT, &_settings)
+			                                 && !_settings);
+			                           }));
+		}
+
+		NM_SET_OUT (out_reread, g_steal_pointer (&reread));
+		NM_SET_OUT (out_reread_same, reread_same);
+	}
+
 	return TRUE;
 }
 
 gboolean
 nms_keyfile_writer_connection (NMConnection *connection,
-                          const char *existing_path,
-                          gboolean force_rename,
-                          char **out_path,
-                          GError **error)
+                               const char *existing_path,
+                               gboolean force_rename,
+                               char **out_path,
+                               NMConnection **out_reread,
+                               gboolean *out_reread_same,
+                               GError **error)
 {
 	return _internal_write_connection (connection,
 	                                   nms_keyfile_utils_get_path (),
@@ -306,16 +348,20 @@ nms_keyfile_writer_connection (NMConnection *connection,
 	                                   existing_path,
 	                                   force_rename,
 	                                   out_path,
+	                                   out_reread,
+	                                   out_reread_same,
 	                                   error);
 }
 
 gboolean
 nms_keyfile_writer_test_connection (NMConnection *connection,
-                               const char *keyfile_dir,
-                               uid_t owner_uid,
-                               pid_t owner_grp,
-                               char **out_path,
-                               GError **error)
+                                    const char *keyfile_dir,
+                                    uid_t owner_uid,
+                                    pid_t owner_grp,
+                                    char **out_path,
+                                    NMConnection **out_reread,
+                                    gboolean *out_reread_same,
+                                    GError **error)
 {
 	return _internal_write_connection (connection,
 	                                   keyfile_dir,
@@ -323,6 +369,8 @@ nms_keyfile_writer_test_connection (NMConnection *connection,
 	                                   NULL,
 	                                   FALSE,
 	                                   out_path,
+	                                   out_reread,
+	                                   out_reread_same,
 	                                   error);
 }
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.h b/src/settings/plugins/keyfile/nms-keyfile-writer.h
index 4f43455d..ac41dfa2 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.h
@@ -22,12 +22,14 @@
 #ifndef __NMS_KEYFILE_WRITER_H__
 #define __NMS_KEYFILE_WRITER_H__
 
-#include <nm-connection.h>
+#include "nm-connection.h"
 
 gboolean nms_keyfile_writer_connection (NMConnection *connection,
                                         const char *existing_path,
                                         gboolean force_rename,
                                         char **out_path,
+                                        NMConnection **out_reread,
+                                        gboolean *out_reread_same,
                                         GError **error);
 
 gboolean nms_keyfile_writer_test_connection (NMConnection *connection,
@@ -35,6 +37,8 @@ gboolean nms_keyfile_writer_test_connection (NMConnection *connection,
                                              uid_t owner_uid,
                                              pid_t owner_grp,
                                              char **out_path,
+                                             NMConnection **out_reread,
+                                             gboolean *out_reread_same,
                                              GError **error);
 
 #endif /* __NMS_KEYFILE_WRITER_H__ */
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
index de8373be..5cb4d726 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
@@ -34,6 +34,7 @@ routes8=1.1.1.8/18,0.0.0.0,
 routes9=1.1.1.9/19,0.0.0.0,0
 routes10=1.1.1.10/20,,0
 routes11=1.1.1.11/21,,21
+routes11_options=cwnd=10,lock-cwnd=true,mtu=1430,src=7.7.7.7
 ignore-auto-routes=false
 ignore-auto-dns=false
 
@@ -58,5 +59,6 @@ route3=6:7:8:9:0:1:2:3/126,,1
 route4=7:8:9:0:1:2:3:4/125/::,5
 route5=8:9:0:1:2:3:4:5/124,6
 route6=8:9:0:1:2:3:4:6/123,,
+route6_options=from=abce::/63
 ignore-auto-routes=false
 ignore-auto-dns=false
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index f1102bd3..d9da5317 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -113,13 +113,25 @@ assert_reread_and_unlink (NMConnection *connection, gboolean normalize_connectio
 }
 
 static void
-write_test_connection (NMConnection *connection, char **testfile)
+assert_reread_same (NMConnection *connection,
+                    NMConnection *reread)
+{
+	nmtst_assert_connection_verifies_without_normalization (reread);
+	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
+}
+
+static void
+write_test_connection_reread (NMConnection *connection,
+                              char **testfile,
+                              NMConnection **out_reread,
+                              gboolean *out_reread_same)
 {
 	uid_t owner_uid;
 	gid_t owner_grp;
 	gboolean success;
 	GError *error = NULL;
 	GError **p_error = (nmtst_get_rand_int () % 2) ? &error : NULL;
+	gs_unref_object NMConnection *connection_normalized = NULL;
 
 	g_assert (NM_IS_CONNECTION (connection));
 	g_assert (testfile && !*testfile);
@@ -127,13 +139,33 @@ write_test_connection (NMConnection *connection, char **testfile)
 	owner_uid = geteuid ();
 	owner_grp = getegid ();
 
-	success = nms_keyfile_writer_test_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, testfile, p_error);
+	connection_normalized = nmtst_connection_duplicate_and_normalize (connection);
+
+	success = nms_keyfile_writer_test_connection (connection_normalized,
+	                                              TEST_SCRATCH_DIR,
+	                                              owner_uid,
+	                                              owner_grp,
+	                                              testfile,
+	                                              out_reread,
+	                                              out_reread_same,
+	                                              p_error);
 	g_assert_no_error (error);
 	g_assert (success);
 	g_assert (*testfile && (*testfile)[0]);
 }
 
 static void
+write_test_connection (NMConnection *connection, char **testfile)
+{
+	gs_unref_object NMConnection *reread = NULL;
+	gboolean reread_same = FALSE;
+
+	write_test_connection_reread (connection, testfile, &reread, &reread_same);
+	assert_reread_same (connection, reread);
+	g_assert (reread_same);
+}
+
+static void
 write_test_connection_and_reread (NMConnection *connection, gboolean normalize_connection)
 {
 	gs_free char *testfile = NULL;
@@ -161,6 +193,27 @@ keyfile_load_from_file (const char *testfile)
 	return keyfile;
 }
 
+static void
+_setting_copy_property_gbytes (NMConnection *src, NMConnection *dst, const char *setting_name, const char *property_name)
+{
+	gs_unref_bytes GBytes *blob = NULL;
+	NMSetting *s_src;
+	NMSetting *s_dst;
+
+	g_assert (NM_IS_CONNECTION (src));
+	g_assert (NM_IS_CONNECTION (dst));
+	g_assert (setting_name);
+	g_assert (property_name);
+
+	s_src = nm_connection_get_setting_by_name (src, setting_name);
+	g_assert (NM_IS_SETTING (s_src));
+	s_dst = nm_connection_get_setting_by_name (dst, setting_name);
+	g_assert (NM_IS_SETTING (s_dst));
+
+	g_object_get (s_src, property_name, &blob, NULL);
+	g_object_set (s_dst, property_name, blob, NULL);
+}
+
 /*****************************************************************************/
 
 static void
@@ -171,6 +224,7 @@ test_read_valid_wired_connection (void)
 	NMSettingWired *s_wired;
 	NMSettingIPConfig *s_ip4;
 	NMSettingIPConfig *s_ip6;
+	NMIPRoute *route;
 	gs_free_error GError *error = NULL;
 	const char *mac;
 	char expected_mac_address[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
@@ -265,6 +319,15 @@ test_read_valid_wired_connection (void)
 	check_ip_route (s_ip4, 10, "1.1.1.10", 20, NULL, -1);
 	check_ip_route (s_ip4, 11, "1.1.1.11", 21, NULL, 21);
 
+	/* Route attributes */
+	route = nm_setting_ip_config_get_route (s_ip4, 11);
+	g_assert (route);
+
+	nmtst_assert_route_attribute_uint32  (route, NM_IP_ROUTE_ATTRIBUTE_CWND, 10);
+	nmtst_assert_route_attribute_uint32  (route, NM_IP_ROUTE_ATTRIBUTE_MTU, 1430);
+	nmtst_assert_route_attribute_boolean (route, NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, TRUE);
+	nmtst_assert_route_attribute_string  (route, NM_IP_ROUTE_ATTRIBUTE_SRC, "7.7.7.7");
+
 	/* ===== IPv6 SETTING ===== */
 	s_ip6 = nm_connection_get_setting_ip6_config (connection);
 	g_assert (s_ip6);
@@ -304,6 +367,11 @@ test_read_valid_wired_connection (void)
 	check_ip_route (s_ip6, 4, "7:8:9:0:1:2:3:4", 125, NULL, 5);
 	check_ip_route (s_ip6, 5, "8:9:0:1:2:3:4:5", 124, NULL, 6);
 	check_ip_route (s_ip6, 6, "8:9:0:1:2:3:4:6", 123, NULL, -1);
+
+	/* Route attributes */
+	route = nm_setting_ip_config_get_route (s_ip6, 6);
+	g_assert (route);
+	nmtst_assert_route_attribute_string (route, NM_IP_ROUTE_ATTRIBUTE_FROM, "abce::/63");
 }
 
 static void
@@ -349,6 +417,7 @@ test_write_wired_connection (void)
 	NMSettingWired *s_wired;
 	NMSettingIPConfig *s_ip4;
 	NMSettingIPConfig *s_ip6;
+	NMIPRoute *rt;
 	const char *mac = "99:88:77:66:55:44";
 	const char *dns1 = "4.2.2.1";
 	const char *dns2 = "4.2.2.2";
@@ -376,6 +445,7 @@ test_write_wired_connection (void)
 	const char *route6_4 = "5:6:7:8:9:0:1:2";
 	const char *route6_4_nh = "::";
 	guint64 timestamp = 0x12345678L;
+	GError *error = NULL;
 
 	connection = nm_simple_connection_new ();
 
@@ -420,7 +490,14 @@ test_write_wired_connection (void)
 	add_one_ip_route (s_ip4, route1, route1_nh, 24, 3);
 	add_one_ip_route (s_ip4, route2, route2_nh, 8, 1);
 	add_one_ip_route (s_ip4, route3, route3_nh, 7, -1);
-	add_one_ip_route (s_ip4, route4, route4_nh, 6, 4);
+
+	rt = nm_ip_route_new (AF_INET, route4, 6, route4_nh, 4, &error);
+	g_assert_no_error (error);
+	nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_CWND, g_variant_new_uint32 (10));
+	nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (1492));
+	nm_ip_route_set_attribute (rt, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string ("1.2.3.4"));
+	g_assert (nm_setting_ip_config_add_route (s_ip4, rt));
+	nm_ip_route_unref (rt);
 
 	/* DNS servers */
 	nm_setting_ip_config_add_dns (s_ip4, dns1);
@@ -1639,11 +1716,18 @@ test_write_wired_8021x_tls_connection_path (void)
 	gs_free_error GError *error = NULL;
 	gs_unref_keyfile GKeyFile *keyfile = NULL;
 	gboolean relative = FALSE;
+	gboolean reread_same = FALSE;
 
 	connection = create_wired_tls_connection (NM_SETTING_802_1X_CK_SCHEME_PATH);
 	g_assert (connection != NULL);
 
-	write_test_connection (connection, &testfile);
+	write_test_connection_reread (connection, &testfile, &reread, &reread_same);
+	nmtst_assert_connection_verifies_without_normalization (reread);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY);
+	assert_reread_same (connection, reread);
+	g_clear_object (&reread);
 
 	/* Read the connection back in and compare it to the one we just wrote out */
 	reread = nms_keyfile_reader_from_file (testfile, &error);
@@ -1716,6 +1800,7 @@ test_write_wired_8021x_tls_connection_blob (void)
 	char *new_client_cert;
 	char *new_priv_key;
 	const char *uuid;
+	gboolean reread_same = FALSE;
 	gs_free_error GError *error = NULL;
 	GBytes *password_raw = NULL;
 #define PASSWORD_RAW "password-raw\0test"
@@ -1733,7 +1818,13 @@ test_write_wired_8021x_tls_connection_blob (void)
 	              NULL);
 	g_bytes_unref (password_raw);
 
-	write_test_connection (connection, &testfile);
+	write_test_connection_reread (connection, &testfile, &reread, &reread_same);
+	nmtst_assert_connection_verifies_without_normalization (reread);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CA_CERT);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_CLIENT_CERT);
+	_setting_copy_property_gbytes (connection, reread, NM_SETTING_802_1X_SETTING_NAME, NM_SETTING_802_1X_PRIVATE_KEY);
+	assert_reread_same (connection, reread);
+	g_clear_object (&reread);
 
 	/* Check that the new certs got written out */
 	s_con = nm_connection_get_setting_connection (connection);