summary refs log tree commit diff
path: root/src/settings
diff options
context:
space:
mode:
Diffstat (limited to 'src/settings')
-rw-r--r--src/settings/nm-agent-manager.c20
-rw-r--r--src/settings/nm-secret-agent.c8
-rw-r--r--src/settings/nm-settings-connection.c233
-rw-r--r--src/settings/nm-settings-connection.h12
-rw-r--r--src/settings/nm-settings-plugin.c183
-rw-r--r--src/settings/nm-settings-plugin.h81
-rw-r--r--src/settings/nm-settings.c276
-rw-r--r--src/settings/nm-settings.h2
-rw-r--r--src/settings/plugins/ibft/meson.build7
-rw-r--r--src/settings/plugins/ibft/nms-ibft-plugin.c35
-rw-r--r--src/settings/plugins/ibft/nms-ibft-reader.c2
-rw-r--r--src/settings/plugins/ibft/tests/meson.build1
-rw-r--r--src/settings/plugins/ifcfg-rh/meson.build9
-rw-r--r--src/settings/plugins/ifcfg-rh/nm-inotify-helper.c4
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c6
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c91
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c1233
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c101
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h21
-rw-r--r--src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c355
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.c125
-rw-r--r--src/settings/plugins/ifcfg-rh/shvar.h24
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/meson.build1
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected2
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected11
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov19
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected18
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected15
-rw-r--r--src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c243
-rw-r--r--src/settings/plugins/ifupdown/meson.build4
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-connection.c33
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c303
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h50
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-parser.c452
-rw-r--r--src/settings/plugins/ifupdown/nms-ifupdown-plugin.c453
-rw-r--r--src/settings/plugins/ifupdown/tests/meson.build1
-rw-r--r--src/settings/plugins/ifupdown/tests/test-ifupdown.c255
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-connection.c12
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-plugin.c58
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-reader.c8
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-utils.h4
-rw-r--r--src/settings/plugins/keyfile/nms-keyfile-writer.c11
-rw-r--r--src/settings/plugins/keyfile/tests/meson.build1
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c55
45 files changed, 2696 insertions, 2144 deletions
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c
index 453136e4..953d3ce4 100644
--- a/src/settings/nm-agent-manager.c
+++ b/src/settings/nm-agent-manager.c
@@ -349,7 +349,7 @@ agent_register_permissions_done (NMAuthChain *chain,
 		priv->agent_version_id += 1;
 		sender = nm_secret_agent_get_dbus_owner (agent);
 		g_hash_table_insert (priv->agents, g_strdup (sender), agent);
-		_LOGD (agent, "agent registered");
+		_LOGI (agent, "agent registered");
 		g_dbus_method_invocation_return_value (context, NULL);
 
 		/* Signal an agent was registered */
@@ -642,7 +642,7 @@ req_complete_error (Request *req, GError *error)
 	req_complete (req, NULL, NULL, NULL, error);
 }
 
-static gint
+static int
 agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data)
 {
 	NMSecretAgent *a = (NMSecretAgent *)aa;
@@ -651,12 +651,14 @@ agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data)
 	NMSessionMonitor *sm;
 	gboolean a_active, b_active;
 	gulong a_pid, b_pid, requester;
+	guint64 a_start, b_start;
+
+	a_pid = nm_secret_agent_get_pid (a);
+	b_pid = nm_secret_agent_get_pid (b);
 
 	/* Prefer agents in the process the request came from */
 	if (nm_auth_subject_is_unix_process (req->subject)) {
 		requester = nm_auth_subject_get_unix_process_pid (req->subject);
-		a_pid = nm_secret_agent_get_pid (a);
-		b_pid = nm_secret_agent_get_pid (b);
 
 		if (a_pid != b_pid) {
 			if (a_pid == requester)
@@ -672,11 +674,17 @@ agent_compare_func (gconstpointer aa, gconstpointer bb, gpointer user_data)
 	b_active = nm_session_monitor_session_exists (sm, nm_secret_agent_get_owner_uid (b), TRUE);
 	if (a_active && !b_active)
 		return -1;
-	else if (a_active == b_active)
-		return 0;
 	else if (!a_active && b_active)
 		return 1;
 
+	/* Prefer agents launched later (this is essentially to ease agent debugging) */
+	a_start = nm_utils_get_start_time_for_pid (a_pid, NULL, NULL);
+	b_start = nm_utils_get_start_time_for_pid (b_pid, NULL, NULL);
+	if (a_start > b_start)
+		return -1;
+	else if (a_start < b_start)
+		return 1;
+
 	return 0;
 }
 
diff --git a/src/settings/nm-secret-agent.c b/src/settings/nm-secret-agent.c
index af6d7017..836ab21d 100644
--- a/src/settings/nm-secret-agent.c
+++ b/src/settings/nm-secret-agent.c
@@ -648,10 +648,10 @@ _on_disconnected_private_connection (NMDBusManager *mgr,
 
 static void
 _on_disconnected_name_owner_changed (GDBusConnection *connection,
-                                     const gchar      *sender_name,
-                                     const gchar      *object_path,
-                                     const gchar      *interface_name,
-                                     const gchar      *signal_name,
+                                     const char       *sender_name,
+                                     const char       *object_path,
+                                     const char       *interface_name,
+                                     const char       *signal_name,
                                      GVariant         *parameters,
                                      gpointer          user_data)
 {
diff --git a/src/settings/nm-settings-connection.c b/src/settings/nm-settings-connection.c
index c09f6804..1b2edc83 100644
--- a/src/settings/nm-settings-connection.c
+++ b/src/settings/nm-settings-connection.c
@@ -49,7 +49,26 @@
 
 /*****************************************************************************/
 
-static void nm_settings_connection_connection_interface_init (NMConnectionInterface *iface);
+NMConnection **
+nm_settings_connections_array_to_connections (NMSettingsConnection *const*connections,
+                                              gssize n_connections)
+{
+	NMConnection **arr;
+	gssize i;
+
+	if (n_connections < 0)
+		n_connections = NM_PTRARRAY_LEN (connections);
+	if (n_connections == 0)
+		return NULL;
+
+	arr = g_new (NMConnection *, n_connections + 1);
+	for (i = 0; i < n_connections; i++)
+		arr[i] = nm_settings_connection_get_connection (connections[i]);
+	arr[i] = NULL;
+	return arr;
+}
+
+/*****************************************************************************/
 
 NM_GOBJECT_PROPERTIES_DEFINE (NMSettingsConnection,
 	PROP_UNSAVED,
@@ -87,6 +106,8 @@ typedef struct _NMSettingsConnectionPrivate {
 
 	CList call_ids_lst_head; /* in-progress secrets requests */
 
+	NMConnection *connection;
+
 	/* Caches secrets from on-disk connections; were they not cached any
 	 * call to nm_connection_clear_secrets() wipes them out and we'd have
 	 * to re-read them from disk which defeats the purpose of having the
@@ -115,9 +136,7 @@ typedef struct _NMSettingsConnectionPrivate {
 
 } NMSettingsConnectionPrivate;
 
-G_DEFINE_TYPE_WITH_CODE (NMSettingsConnection, nm_settings_connection, NM_TYPE_DBUS_OBJECT,
-                         G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION, nm_settings_connection_connection_interface_init)
-                         )
+G_DEFINE_TYPE (NMSettingsConnection, nm_settings_connection, NM_TYPE_DBUS_OBJECT)
 
 #define NM_SETTINGS_CONNECTION_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMSettingsConnection, NM_IS_SETTINGS_CONNECTION)
 
@@ -152,6 +171,16 @@ static const NMDBusInterfaceInfoExtended interface_info_settings_connection;
 
 /*****************************************************************************/
 
+NMConnection *
+nm_settings_connection_get_connection (NMSettingsConnection *self)
+{
+	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
+
+	return NM_SETTINGS_CONNECTION_GET_PRIVATE (self)->connection;
+}
+
+/*****************************************************************************/
+
 gboolean
 nm_settings_connection_has_unmodified_applied_connection (NMSettingsConnection *self,
                                                           NMConnection *applied_connection,
@@ -163,7 +192,8 @@ nm_settings_connection_has_unmodified_applied_connection (NMSettingsConnection *
 	/* for convenience, we *always* ignore certain settings. */
 	compare_flags |= NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS | NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP;
 
-	return nm_connection_compare (NM_CONNECTION (self), applied_connection, compare_flags);
+	return nm_connection_compare (nm_settings_connection_get_connection (self),
+	                              applied_connection, compare_flags);
 }
 
 /*****************************************************************************/
@@ -332,8 +362,7 @@ nm_settings_connection_recheck_visibility (NMSettingsConnection *self)
 
 	priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
-	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
-	g_assert (s_con);
+	s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (self));
 
 	/* Check every user in the ACL for a session */
 	num = nm_setting_connection_get_num_permissions (s_con);
@@ -362,9 +391,9 @@ nm_settings_connection_recheck_visibility (NMSettingsConnection *self)
 }
 
 static void
-session_changed_cb (NMSessionMonitor *self, gpointer user_data)
+session_changed_cb (NMSessionMonitor *self, NMSettingsConnection *sett_conn)
 {
-	nm_settings_connection_recheck_visibility (NM_SETTINGS_CONNECTION (user_data));
+	nm_settings_connection_recheck_visibility (sett_conn);
 }
 
 /*****************************************************************************/
@@ -390,8 +419,7 @@ nm_settings_connection_check_permission (NMSettingsConnection *self,
 	                   NM_SETTINGS_CONNECTION_INT_FLAGS_VISIBLE))
 		return FALSE;
 
-	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
-	g_assert (s_con);
+	s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (self));
 
 	/* Check every user in the ACL for a session */
 	num = nm_setting_connection_get_num_permissions (s_con);
@@ -447,7 +475,7 @@ update_system_secrets_cache (NMSettingsConnection *self)
 
 	if (priv->system_secrets)
 		g_object_unref (priv->system_secrets);
-	priv->system_secrets = nm_simple_connection_new_clone (NM_CONNECTION (self));
+	priv->system_secrets = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
 
 	/* Clear out non-system-owned and not-saved secrets */
 	nm_connection_clear_secrets_with_flags (priv->system_secrets,
@@ -463,7 +491,8 @@ update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new)
 
 	if (priv->agent_secrets)
 		g_object_unref (priv->agent_secrets);
-	priv->agent_secrets = nm_simple_connection_new_clone (new ?: NM_CONNECTION(self));
+	priv->agent_secrets = nm_simple_connection_new_clone (   new
+	                                                      ?: nm_settings_connection_get_connection (self));
 
 	/* Clear out non-system-owned secrets */
 	nm_connection_clear_secrets_with_flags (priv->agent_secrets,
@@ -472,7 +501,7 @@ update_agent_secrets_cache (NMSettingsConnection *self, NMConnection *new)
 }
 
 static void
-secrets_cleared_cb (NMSettingsConnection *self)
+secrets_cleared_cb (NMConnection *connection, NMSettingsConnection *self)
 {
 	NMSettingsConnectionPrivate *priv = NM_SETTINGS_CONNECTION_GET_PRIVATE (self);
 
@@ -533,7 +562,7 @@ _emit_updated (NMSettingsConnection *self, gboolean by_user)
 }
 
 static void
-connection_changed_cb (NMSettingsConnection *self, gpointer unused)
+connection_changed_cb (NMConnection *connection, NMSettingsConnection *self)
 {
 	set_persist_mode (self, NM_SETTINGS_CONNECTION_PERSIST_MODE_UNSAVED);
 	_emit_updated (self, FALSE);
@@ -636,7 +665,7 @@ nm_settings_connection_update (NMSettingsConnection *self,
 
 	if (persist_mode == NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK) {
 		if (!klass->commit_changes (self,
-		                            new_connection ?: NM_CONNECTION (self),
+		                            new_connection ?: nm_settings_connection_get_connection (self),
 		                            commit_reason,
 		                            &reread_connection,
 		                            &logmsg_change,
@@ -655,30 +684,30 @@ nm_settings_connection_update (NMSettingsConnection *self,
 	/* Disconnect the changed signal to ensure we don't set Unsaved when
 	 * it's not required.
 	 */
-	g_signal_handlers_block_by_func (self, G_CALLBACK (connection_changed_cb), NULL);
+	g_signal_handlers_block_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
 
 	/* Do nothing if there's nothing to update */
 	if (   replace_connection
-	    && !nm_connection_compare (NM_CONNECTION (self),
+	    && !nm_connection_compare (nm_settings_connection_get_connection (self),
 	                               replace_connection,
 	                               NM_SETTING_COMPARE_FLAG_EXACT)) {
 		gs_unref_object NMConnection *simple = NULL;
 
 		if (log_diff_name) {
-			nm_utils_log_connection_diff (replace_connection, NM_CONNECTION (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ",
+			nm_utils_log_connection_diff (replace_connection, nm_settings_connection_get_connection (self), LOGL_DEBUG, LOGD_CORE, log_diff_name, "++ ",
 			                              nm_dbus_object_get_path (NM_DBUS_OBJECT (self)));
 		}
 
 		/* Make a copy of agent-owned secrets because they won't be present in
 		 * the connection returned by plugins, as plugins return only what was
 		 * reread from the file. */
-		simple = nm_simple_connection_new_clone (NM_CONNECTION (self));
+		simple = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
 		nm_connection_clear_secrets_with_flags (simple,
 		                                        secrets_filter_cb,
 		                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
 		con_agent_secrets = nm_connection_to_dbus (simple, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
 
-		nm_connection_replace_settings_from_connection (NM_CONNECTION (self), replace_connection);
+		nm_connection_replace_settings_from_connection (nm_settings_connection_get_connection (self), replace_connection);
 
 		replaced = TRUE;
 	}
@@ -701,12 +730,12 @@ nm_settings_connection_update (NMSettingsConnection *self,
 
 			dict = nm_connection_to_dbus (priv->agent_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
 			if (dict) {
-				(void) nm_connection_update_secrets (NM_CONNECTION (self), NULL, dict, NULL);
+				(void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, dict, NULL);
 				g_variant_unref (dict);
 			}
 		}
 		if (con_agent_secrets)
-			(void) nm_connection_update_secrets (NM_CONNECTION (self), NULL, con_agent_secrets, NULL);
+			(void) nm_connection_update_secrets (nm_settings_connection_get_connection (self), NULL, con_agent_secrets, NULL);
 	}
 
 	nm_settings_connection_recheck_visibility (self);
@@ -724,7 +753,7 @@ nm_settings_connection_update (NMSettingsConnection *self,
 	                                  NM_SETTINGS_CONNECTION_PERSIST_MODE_VOLATILE_DETACHED))
 		nm_settings_connection_set_filename (self, NULL);
 
-	g_signal_handlers_unblock_by_func (self, G_CALLBACK (connection_changed_cb), NULL);
+	g_signal_handlers_unblock_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
 
 	_emit_updated (self, TRUE);
 
@@ -800,7 +829,7 @@ nm_settings_connection_delete (NMSettingsConnection *self,
 	set_visible (self, FALSE);
 
 	/* Tell agents to remove secrets for this connection */
-	for_agents = nm_simple_connection_new_clone (NM_CONNECTION (self));
+	for_agents = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
 	nm_connection_clear_secrets (for_agents);
 	nm_agent_manager_delete_secrets (priv->agent_mgr,
 	                                 nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
@@ -920,7 +949,7 @@ get_cmp_flags (NMSettingsConnection *self, /* only needed for logging */
                gboolean *agent_had_system,
                ForEachSecretFlags *cmp_flags)
 {
-	gboolean is_self = (((NMConnection *) self) == connection);
+	gboolean is_self = (nm_settings_connection_get_connection (self) == connection);
 
 	g_return_if_fail (secrets);
 
@@ -1001,7 +1030,7 @@ nm_settings_connection_new_secrets (NMSettingsConnection *self,
 		return FALSE;
 	}
 
-	if (!nm_connection_update_secrets (NM_CONNECTION (self), setting_name, secrets, error))
+	if (!nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, secrets, error))
 		return FALSE;
 
 	update_system_secrets_cache (self);
@@ -1073,7 +1102,7 @@ get_secrets_done_cb (NMAgentManager *manager,
 		goto out;
 	}
 
-	if (!nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name)) {
+	if (!nm_connection_get_setting_by_name (nm_settings_connection_get_connection (self), setting_name)) {
 		g_set_error (&local, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
 		             "Connection didn't have requested setting '%s'.",
 		             setting_name);
@@ -1083,7 +1112,7 @@ get_secrets_done_cb (NMAgentManager *manager,
 
 	get_cmp_flags (self,
 	               call_id,
-	               NM_CONNECTION (self),
+	               nm_settings_connection_get_connection (self),
 	               agent_dbus_owner,
 	               agent_has_modify,
 	               setting_name,
@@ -1100,8 +1129,8 @@ get_secrets_done_cb (NMAgentManager *manager,
 		dict = nm_connection_to_dbus (priv->system_secrets, NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
 
 	/* Update the connection with our existing secrets from backing storage */
-	nm_connection_clear_secrets (NM_CONNECTION (self));
-	if (!dict || nm_connection_update_secrets (NM_CONNECTION (self), setting_name, dict, &local)) {
+	nm_connection_clear_secrets (nm_settings_connection_get_connection (self));
+	if (!dict || nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, dict, &local)) {
 		GVariant *filtered_secrets;
 
 		/* Update the connection with the agent's secrets; by this point if any
@@ -1109,8 +1138,8 @@ get_secrets_done_cb (NMAgentManager *manager,
 		 * will have been authenticated, so those secrets can replace the existing
 		 * system secrets.
 		 */
-		filtered_secrets = for_each_secret (NM_CONNECTION (self), secrets, TRUE, validate_secret_flags, &cmp_flags);
-		if (nm_connection_update_secrets (NM_CONNECTION (self), setting_name, filtered_secrets, &local)) {
+		filtered_secrets = for_each_secret (nm_settings_connection_get_connection (self), secrets, TRUE, validate_secret_flags, &cmp_flags);
+		if (nm_connection_update_secrets (nm_settings_connection_get_connection (self), setting_name, filtered_secrets, &local)) {
 			/* Now that all secrets are updated, copy and cache new secrets,
 			 * then save them to backing storage.
 			 */
@@ -1250,7 +1279,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	g_return_val_if_fail (NM_IS_SETTINGS_CONNECTION (self), NULL);
 	g_return_val_if_fail (   !applied_connection
 	                      || (   NM_IS_CONNECTION (applied_connection)
-	                          && (((NMConnection *) self) != applied_connection)), NULL);
+	                          && (nm_settings_connection_get_connection (self) != applied_connection)), NULL);
 
 	call_id = g_slice_new0 (NMSettingsConnectionCallId);
 	call_id->self = self;
@@ -1264,7 +1293,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 	c_list_link_tail (&priv->call_ids_lst_head, &call_id->call_ids_lst);
 
 	/* Make sure the request actually requests something we can return */
-	if (!nm_connection_get_setting_by_name (NM_CONNECTION (self), setting_name)) {
+	if (!nm_connection_get_setting_by_name (nm_settings_connection_get_connection (self), setting_name)) {
 		g_set_error (&local, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
 		             "Connection didn't have requested setting '%s'.",
 		             setting_name);
@@ -1295,7 +1324,7 @@ nm_settings_connection_get_secrets (NMSettingsConnection *self,
 
 	call_id_a = nm_agent_manager_get_secrets (priv->agent_mgr,
 	                                          nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
-	                                          NM_CONNECTION (self),
+	                                          nm_settings_connection_get_connection (self),
 	                                          subject,
 	                                          existing_secrets,
 	                                          setting_name,
@@ -1465,7 +1494,7 @@ auth_start (NMSettingsConnection *self,
 	nm_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
 	nm_assert (NM_IS_AUTH_SUBJECT (subject));
 
-	if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (self),
+	if (!nm_auth_is_subject_in_acl_set_error (nm_settings_connection_get_connection (self),
 	                                          subject,
 	                                          NM_SETTINGS_ERROR,
 	                                          NM_SETTINGS_ERROR_PERMISSION_DENIED,
@@ -1539,15 +1568,14 @@ get_settings_auth_cb (NMSettingsConnection *self,
 	if (error)
 		g_dbus_method_invocation_return_gerror (context, error);
 	else {
+		gs_unref_object NMConnection *dupl_con = NULL;
 		GVariant *settings;
-		NMConnection *dupl_con;
 		NMSettingConnection *s_con;
 		NMSettingWireless *s_wifi;
 		guint64 timestamp = 0;
-		char **bssids;
+		gs_free char **bssids = NULL;
 
-		dupl_con = nm_simple_connection_new_clone (NM_CONNECTION (self));
-		g_assert (dupl_con);
+		dupl_con = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
 
 		/* Timestamp is not updated in connection's 'timestamp' property,
 		 * because it would force updating the connection and in turn
@@ -1557,8 +1585,7 @@ get_settings_auth_cb (NMSettingsConnection *self,
 		 */
 		nm_settings_connection_get_timestamp (self, &timestamp);
 		if (timestamp) {
-			s_con = nm_connection_get_setting_connection (NM_CONNECTION (dupl_con));
-			g_assert (s_con);
+			s_con = nm_connection_get_setting_connection (dupl_con);
 			g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, timestamp, NULL);
 		}
 		/* Seen BSSIDs are not updated in 802-11-wireless 'seen-bssids' property
@@ -1566,20 +1593,17 @@ get_settings_auth_cb (NMSettingsConnection *self,
 		 * return settings too.
 		 */
 		bssids = nm_settings_connection_get_seen_bssids (self);
-		s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (dupl_con));
+		s_wifi = nm_connection_get_setting_wireless (dupl_con);
 		if (bssids && bssids[0] && s_wifi)
 			g_object_set (s_wifi, NM_SETTING_WIRELESS_SEEN_BSSIDS, bssids, NULL);
-		g_free (bssids);
 
 		/* Secrets should *never* be returned by the GetSettings method, they
 		 * get returned by the GetSecrets method which can be better
 		 * protected against leakage of secrets to unprivileged callers.
 		 */
-		settings = nm_connection_to_dbus (NM_CONNECTION (dupl_con), NM_CONNECTION_SERIALIZE_NO_SECRETS);
-		g_assert (settings);
+		settings = nm_connection_to_dbus (dupl_con, NM_CONNECTION_SERIALIZE_NO_SECRETS);
 		g_dbus_method_invocation_return_value (context,
 		                                       g_variant_new ("(@a{sa{sv}})", settings));
-		g_object_unref (dupl_con);
 	}
 }
 
@@ -1734,7 +1758,7 @@ update_auth_cb (NMSettingsConnection *self,
 			gs_unref_hashtable GHashTable *diff = NULL;
 			gboolean same;
 
-			same = nm_connection_diff (NM_CONNECTION (self), info->new_settings,
+			same = nm_connection_diff (nm_settings_connection_get_connection (self), info->new_settings,
 			                           NM_SETTING_COMPARE_FLAG_EXACT |
 			                           NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT,
 			                           &diff);
@@ -1745,7 +1769,7 @@ update_auth_cb (NMSettingsConnection *self,
 
 	commit_reason = NM_SETTINGS_CONNECTION_COMMIT_REASON_USER_ACTION;
 	if (   info->new_settings
-	    && !nm_streq0 (nm_connection_get_id (NM_CONNECTION (self)),
+	    && !nm_streq0 (nm_connection_get_id (nm_settings_connection_get_connection (self)),
 	                   nm_connection_get_id (info->new_settings)))
 		commit_reason |= NM_SETTINGS_CONNECTION_COMMIT_REASON_ID_CHANGED;
 
@@ -1791,7 +1815,7 @@ update_auth_cb (NMSettingsConnection *self,
 		 * as agent-owned secrets are the only ones we send back be saved.
 		 * Only send secrets to agents of the same UID that called update too.
 		 */
-		for_agent = nm_simple_connection_new_clone (NM_CONNECTION (self));
+		for_agent = nm_simple_connection_new_clone (nm_settings_connection_get_connection (self));
 		nm_connection_clear_secrets_with_flags (for_agent,
 		                                        secrets_filter_cb,
 		                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
@@ -1811,11 +1835,9 @@ get_update_modify_permission (NMConnection *old, NMConnection *new)
 	guint32 orig_num = 0, new_num = 0;
 
 	s_con = nm_connection_get_setting_connection (old);
-	g_assert (s_con);
 	orig_num = nm_setting_connection_get_num_permissions (s_con);
 
 	s_con = nm_connection_get_setting_connection (new);
-	g_assert (s_con);
 	new_num = nm_setting_connection_get_num_permissions (s_con);
 
 	/* If the caller is the only user in either connection's permissions, then
@@ -1848,7 +1870,7 @@ settings_connection_update (NMSettingsConnection *self,
 	 * the problem (ex a system settings plugin that can't write connections out)
 	 * instead of over D-Bus.
 	 */
-	if (!check_writable (NM_CONNECTION (self), &error))
+	if (!check_writable (nm_settings_connection_get_connection (self), &error))
 		goto error;
 
 	/* Check if the settings are valid first */
@@ -1879,7 +1901,7 @@ settings_connection_update (NMSettingsConnection *self,
 	 * that's sending the update request.  You can't make a connection
 	 * invisible to yourself.
 	 */
-	if (!nm_auth_is_subject_in_acl_set_error (tmp ?: NM_CONNECTION(self),
+	if (!nm_auth_is_subject_in_acl_set_error (tmp ?: nm_settings_connection_get_connection (self),
 	                                          subject,
 	                                          NM_SETTINGS_ERROR,
 	                                          NM_SETTINGS_ERROR_PERMISSION_DENIED,
@@ -1894,8 +1916,8 @@ settings_connection_update (NMSettingsConnection *self,
 	info->flags = flags;
 	info->new_settings = tmp;
 
-	permission = get_update_modify_permission (NM_CONNECTION (self),
-	                                           tmp ?: NM_CONNECTION(self));
+	permission = get_update_modify_permission (nm_settings_connection_get_connection (self),
+	                                           tmp ?: nm_settings_connection_get_connection (self));
 	auth_start (self, context, subject, permission, update_auth_cb, info);
 	return;
 
@@ -2066,8 +2088,7 @@ get_modify_permission_basic (NMSettingsConnection *self)
 	 * we use the 'modify.own' permission instead of 'modify.system'.  If the
 	 * request affects more than just the caller, require 'modify.system'.
 	 */
-	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
-	nm_assert (s_con);
+	s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (self));
 	if (nm_setting_connection_get_num_permissions (s_con) == 1)
 		return NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN;
 
@@ -2087,7 +2108,7 @@ impl_settings_connection_delete (NMDBusObject *obj,
 	gs_unref_object NMAuthSubject *subject = NULL;
 	GError *error = NULL;
 
-	if (!check_writable (NM_CONNECTION (self), &error))
+	if (!check_writable (nm_settings_connection_get_connection (self), &error))
 		goto err;
 
 	subject = _new_auth_subject (invocation, &error);
@@ -2122,7 +2143,7 @@ dbus_get_agent_secrets_cb (NMSettingsConnection *self,
 		 * secrets from backing storage and those returned from the agent
 		 * by the time we get here.
 		 */
-		dict = nm_connection_to_dbus (NM_CONNECTION (self), NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
+		dict = nm_connection_to_dbus (nm_settings_connection_get_connection (self), NM_CONNECTION_SERIALIZE_ONLY_SECRETS);
 		if (!dict)
 			dict = g_variant_new_array (G_VARIANT_TYPE ("{sa{sv}}"), NULL, 0);
 		g_dbus_method_invocation_return_value (context, g_variant_new ("(@a{sa{sv}})", dict));
@@ -2204,7 +2225,7 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
 	}
 
 	/* Clear secrets in connection and caches */
-	nm_connection_clear_secrets (NM_CONNECTION (self));
+	nm_connection_clear_secrets (nm_settings_connection_get_connection (self));
 	if (priv->system_secrets)
 		nm_connection_clear_secrets (priv->system_secrets);
 	if (priv->agent_secrets)
@@ -2213,7 +2234,7 @@ dbus_clear_secrets_auth_cb (NMSettingsConnection *self,
 	/* Tell agents to remove secrets for this connection */
 	nm_agent_manager_delete_secrets (priv->agent_mgr,
 	                                 nm_dbus_object_get_path (NM_DBUS_OBJECT (self)),
-	                                 NM_CONNECTION (self));
+	                                 nm_settings_connection_get_connection (self));
 
 	nm_settings_connection_update (self,
 	                               NULL,
@@ -2388,15 +2409,8 @@ _cmp_timestamp (NMSettingsConnection *a, NMSettingsConnection *b)
 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;
+	NM_CMP_DIRECT_STRCMP0 (nm_settings_connection_get_uuid (a),
+	                       nm_settings_connection_get_uuid (b));
 
 	/* hm, same UUID. Use their pointer value to give them a stable
 	 * order. */
@@ -2411,19 +2425,11 @@ _cmp_last_resort (NMSettingsConnection *a, NMSettingsConnection *b)
 int
 nm_settings_connection_cmp_timestamp (NMSettingsConnection *a, NMSettingsConnection *b)
 {
-	int c;
+	NM_CMP_SELF (a, b);
 
-	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;
+	NM_CMP_RETURN (_cmp_timestamp (a, b));
+	NM_CMP_RETURN (nm_utils_cmp_connection_by_autoconnect_priority (nm_settings_connection_get_connection (a),
+	                                                                nm_settings_connection_get_connection (b)));
 	return _cmp_last_resort (a, b);
 }
 
@@ -2437,14 +2443,11 @@ nm_settings_connection_cmp_timestamp_p_with_data (gconstpointer pa, gconstpointe
 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;
+	NM_CMP_RETURN (nm_utils_cmp_connection_by_autoconnect_priority (nm_settings_connection_get_connection (a),
+	                                                                nm_settings_connection_get_connection (b)));
+	NM_CMP_RETURN (_cmp_timestamp (a, b));
 	return _cmp_last_resort (a, b);
 }
 
@@ -2727,7 +2730,7 @@ nm_settings_connection_read_and_fill_seen_bssids (NMSettingsConnection *self)
 		 * seen-bssids list from the deprecated seen-bssids property of the
 		 * wifi setting.
 		 */
-		s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (self));
+		s_wifi = nm_connection_get_setting_wireless (nm_settings_connection_get_connection (self));
 		if (s_wifi) {
 			len = nm_setting_wireless_get_num_seen_bssids (s_wifi);
 			for (i = 0; i < len; i++) {
@@ -2747,7 +2750,7 @@ _autoconnect_retries_initial (NMSettingsConnection *self)
 	NMSettingConnection *s_con;
 	int retries = -1;
 
-	s_con = nm_connection_get_setting_connection ((NMConnection *) self);
+	s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (self));
 	if (s_con)
 		retries = nm_setting_connection_get_autoconnect_retries (s_con);
 
@@ -2959,13 +2962,19 @@ nm_settings_connection_get_filename (NMSettingsConnection *self)
 const char *
 nm_settings_connection_get_id (NMSettingsConnection *self)
 {
-	return nm_connection_get_id (NM_CONNECTION (self));
+	return nm_connection_get_id (nm_settings_connection_get_connection (self));
 }
 
 const char *
 nm_settings_connection_get_uuid (NMSettingsConnection *self)
 {
-	return nm_connection_get_uuid (NM_CONNECTION (self));
+	return nm_connection_get_uuid (nm_settings_connection_get_connection (self));
+}
+
+const char *
+nm_settings_connection_get_connection_type (NMSettingsConnection *self)
+{
+	return nm_connection_get_connection_type (nm_settings_connection_get_connection (self));
 }
 
 /*****************************************************************************/
@@ -2995,8 +3004,10 @@ nm_settings_connection_init (NMSettingsConnection *self)
 
 	priv->autoconnect_retries = AUTOCONNECT_RETRIES_UNSET;
 
-	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);
+	priv->connection = nm_simple_connection_new ();
+
+	g_signal_connect (priv->connection, NM_CONNECTION_SECRETS_CLEARED, G_CALLBACK (secrets_cleared_cb), self);
+	g_signal_connect (priv->connection, NM_CONNECTION_CHANGED, G_CALLBACK (connection_changed_cb), self);
 }
 
 static void
@@ -3027,26 +3038,32 @@ dispose (GObject *object)
 			_get_secrets_cancel (self, call_id, TRUE);
 	}
 
-	/* Disconnect handlers.
-	 * connection_changed_cb() has to be disconnected *before* nm_connection_clear_secrets(),
-	 * because nm_connection_clear_secrets() emits NM_CONNECTION_CHANGED signal.
-	 */
-	g_signal_handlers_disconnect_by_func (self, G_CALLBACK (secrets_cleared_cb), NULL);
-	g_signal_handlers_disconnect_by_func (self, G_CALLBACK (connection_changed_cb), NULL);
+	set_visible (self, FALSE);
+
+	if (priv->connection) {
+		/* Disconnect handlers.
+		 * connection_changed_cb() has to be disconnected *before* nm_connection_clear_secrets(),
+		 * because nm_connection_clear_secrets() emits NM_CONNECTION_CHANGED signal.
+		 */
+		g_signal_handlers_disconnect_by_func (priv->connection, G_CALLBACK (secrets_cleared_cb), self);
+		g_signal_handlers_disconnect_by_func (priv->connection, G_CALLBACK (connection_changed_cb), self);
+
+		/* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
+		nm_connection_clear_secrets (priv->connection);
+	}
 
-	nm_connection_clear_secrets (NM_CONNECTION (self));
 	g_clear_object (&priv->system_secrets);
 	g_clear_object (&priv->agent_secrets);
 
 	g_clear_pointer (&priv->seen_bssids, g_hash_table_destroy);
 
-	set_visible (self, FALSE);
-
 	nm_clear_g_signal_handler (priv->session_monitor, &priv->session_changed_id);
 	g_clear_object (&priv->session_monitor);
 
 	g_clear_object (&priv->agent_mgr);
 
+	g_clear_object (&priv->connection);
+
 	g_clear_pointer (&priv->filename, g_free);
 
 	G_OBJECT_CLASS (nm_settings_connection_parent_class)->dispose (object);
@@ -3264,9 +3281,3 @@ nm_settings_connection_class_init (NMSettingsConnectionClass *klass)
 	                  g_cclosure_marshal_VOID__VOID,
 	                  G_TYPE_NONE, 0);
 }
-
-static void
-nm_settings_connection_connection_interface_init (NMConnectionInterface *iface)
-{
-}
-
diff --git a/src/settings/nm-settings-connection.h b/src/settings/nm-settings-connection.h
index cd64f976..e796b716 100644
--- a/src/settings/nm-settings-connection.h
+++ b/src/settings/nm-settings-connection.h
@@ -139,6 +139,8 @@ struct _NMSettingsConnectionClass {
 
 GType nm_settings_connection_get_type (void);
 
+NMConnection *nm_settings_connection_get_connection (NMSettingsConnection *self);
+
 guint64 nm_settings_connection_get_last_secret_agent_version_id (NMSettingsConnection *self);
 
 gboolean nm_settings_connection_has_unmodified_applied_connection (NMSettingsConnection *self,
@@ -268,7 +270,13 @@ void        nm_settings_connection_set_filename (NMSettingsConnection *self,
                                                  const char *filename);
 const char *nm_settings_connection_get_filename (NMSettingsConnection *self);
 
-const char *nm_settings_connection_get_id   (NMSettingsConnection *connection);
-const char *nm_settings_connection_get_uuid (NMSettingsConnection *connection);
+const char *nm_settings_connection_get_id              (NMSettingsConnection *connection);
+const char *nm_settings_connection_get_uuid            (NMSettingsConnection *connection);
+const char *nm_settings_connection_get_connection_type (NMSettingsConnection *connection);
+
+/*****************************************************************************/
+
+NMConnection **nm_settings_connections_array_to_connections (NMSettingsConnection *const*connections,
+                                                             gssize n_connections);
 
 #endif /* __NETWORKMANAGER_SETTINGS_CONNECTION_H__ */
diff --git a/src/settings/nm-settings-plugin.c b/src/settings/nm-settings-plugin.c
index f6dd2cfb..e7275631 100644
--- a/src/settings/nm-settings-plugin.c
+++ b/src/settings/nm-settings-plugin.c
@@ -15,117 +15,94 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2007 - 2011 Red Hat, Inc.
+ * Copyright (C) 2007 - 2018 Red Hat, Inc.
  * Copyright (C) 2008 Novell, Inc.
  */
 
 #include "nm-default.h"
 
 #include "nm-settings-plugin.h"
+
 #include "nm-settings-connection.h"
 
-G_DEFINE_INTERFACE (NMSettingsPlugin, nm_settings_plugin, G_TYPE_OBJECT)
+/*****************************************************************************/
 
-static void
-nm_settings_plugin_default_init (NMSettingsPluginInterface *g_iface)
-{
-	GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
-	static gboolean initialized = FALSE;
-
-	if (initialized)
-		return;
-
-	/* Signals */
-	g_signal_new (NM_SETTINGS_PLUGIN_CONNECTION_ADDED,
-	              iface_type,
-	              G_SIGNAL_RUN_FIRST,
-	              G_STRUCT_OFFSET (NMSettingsPluginInterface, connection_added),
-	              NULL, NULL,
-	              g_cclosure_marshal_VOID__OBJECT,
-	              G_TYPE_NONE, 1,
-	              NM_TYPE_SETTINGS_CONNECTION);
-
-	g_signal_new (NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED,
-	              iface_type,
-	              G_SIGNAL_RUN_FIRST,
-	              G_STRUCT_OFFSET (NMSettingsPluginInterface, unmanaged_specs_changed),
-	              NULL, NULL,
-	              g_cclosure_marshal_VOID__VOID,
-	              G_TYPE_NONE, 0);
-
-	g_signal_new (NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED,
-	              iface_type,
-	              G_SIGNAL_RUN_FIRST,
-	              G_STRUCT_OFFSET (NMSettingsPluginInterface, unrecognized_specs_changed),
-	              NULL, NULL,
-	              g_cclosure_marshal_VOID__VOID,
-	              G_TYPE_NONE, 0);
-
-	initialized = TRUE;
-}
+enum {
+	CONNECTION_ADDED,
+	UNMANAGED_SPECS_CHANGED,
+	UNRECOGNIZED_SPECS_CHANGED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (NMSettingsPlugin, nm_settings_plugin, G_TYPE_OBJECT)
+
+/*****************************************************************************/
 
 void
-nm_settings_plugin_init (NMSettingsPlugin *config)
+nm_settings_plugin_initialize (NMSettingsPlugin *self)
 {
-	g_return_if_fail (config != NULL);
+	g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self));
 
-	if (NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->init)
-		NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->init (config);
+	if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->initialize)
+		NM_SETTINGS_PLUGIN_GET_CLASS (self)->initialize (self);
 }
 
 GSList *
-nm_settings_plugin_get_connections (NMSettingsPlugin *config)
+nm_settings_plugin_get_connections (NMSettingsPlugin *self)
 {
-	g_return_val_if_fail (config != NULL, NULL);
+	g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
 
-	if (NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->get_connections)
-		return NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->get_connections (config);
+	if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_connections)
+		return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_connections (self);
 	return NULL;
 }
 
 gboolean
-nm_settings_plugin_load_connection (NMSettingsPlugin *config,
+nm_settings_plugin_load_connection (NMSettingsPlugin *self,
                                     const char *filename)
 {
-	g_return_val_if_fail (config != NULL, FALSE);
+	g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), FALSE);
 
-	if (NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->load_connection)
-		return NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->load_connection (config, filename);
+	if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->load_connection)
+		return NM_SETTINGS_PLUGIN_GET_CLASS (self)->load_connection (self, filename);
 	return FALSE;
 }
 
 void
-nm_settings_plugin_reload_connections (NMSettingsPlugin *config)
+nm_settings_plugin_reload_connections (NMSettingsPlugin *self)
 {
-	g_return_if_fail (config != NULL);
+	g_return_if_fail (NM_IS_SETTINGS_PLUGIN (self));
 
-	if (NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->reload_connections)
-		NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->reload_connections (config);
+	if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->reload_connections)
+		NM_SETTINGS_PLUGIN_GET_CLASS (self)->reload_connections (self);
 }
 
 GSList *
-nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *config)
+nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *self)
 {
-	g_return_val_if_fail (config != NULL, NULL);
+	g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
 
-	if (NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->get_unmanaged_specs)
-		return NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->get_unmanaged_specs (config);
+	if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unmanaged_specs)
+		return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unmanaged_specs (self);
 	return NULL;
 }
 
 GSList *
-nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *config)
+nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *self)
 {
-	g_return_val_if_fail (config != NULL, NULL);
+	g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
 
-	if (NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->get_unrecognized_specs)
-		return NM_SETTINGS_PLUGIN_GET_INTERFACE (config)->get_unrecognized_specs (config);
+	if (NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unrecognized_specs)
+		return NM_SETTINGS_PLUGIN_GET_CLASS (self)->get_unrecognized_specs (self);
 	return NULL;
 }
 
 /**
  * nm_settings_plugin_add_connection:
- * @config: the #NMSettingsPlugin
+ * @self: the #NMSettingsPlugin
  * @connection: the source connection to create a plugin-specific
  * #NMSettingsConnection from
  * @save_to_disk: %TRUE to save the connection to disk immediately, %FALSE to
@@ -140,22 +117,88 @@ nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *config)
  * Returns: the new #NMSettingsConnection or %NULL
  */
 NMSettingsConnection *
-nm_settings_plugin_add_connection (NMSettingsPlugin *config,
+nm_settings_plugin_add_connection (NMSettingsPlugin *self,
                                    NMConnection *connection,
                                    gboolean save_to_disk,
                                    GError **error)
 {
-	NMSettingsPluginInterface *config_interface;
+	NMSettingsPluginClass *klass;
 
-	g_return_val_if_fail (config != NULL, NULL);
+	g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (self), NULL);
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 
-	config_interface = NM_SETTINGS_PLUGIN_GET_INTERFACE (config);
-	if (!config_interface->add_connection) {
+	klass = NM_SETTINGS_PLUGIN_GET_CLASS (self);
+	if (!klass->add_connection) {
 		g_set_error_literal (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_NOT_SUPPORTED,
 		                     "Plugin does not support adding connections");
 		return NULL;
 	}
 
-	return config_interface->add_connection (config, connection, save_to_disk, error);
+	return klass->add_connection (self, connection, save_to_disk, error);
+}
+
+/*****************************************************************************/
+
+void
+_nm_settings_plugin_emit_signal_connection_added (NMSettingsPlugin *self,
+                                                  NMSettingsConnection *sett_conn)
+{
+	nm_assert (NM_IS_SETTINGS_PLUGIN (self));
+	nm_assert (NM_IS_SETTINGS_CONNECTION (sett_conn));
+
+	g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn);
+}
+
+void
+_nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *self)
+{
+	nm_assert (NM_IS_SETTINGS_PLUGIN (self));
+
+	g_signal_emit (self, signals[UNMANAGED_SPECS_CHANGED], 0);
+}
+
+void
+_nm_settings_plugin_emit_signal_unrecognized_specs_changed (NMSettingsPlugin *self)
+{
+	nm_assert (NM_IS_SETTINGS_PLUGIN (self));
+
+	g_signal_emit (self, signals[UNRECOGNIZED_SPECS_CHANGED], 0);
+}
+
+/*****************************************************************************/
+
+static void
+nm_settings_plugin_init (NMSettingsPlugin *self)
+{
+}
+
+static void
+nm_settings_plugin_class_init (NMSettingsPluginClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	signals[CONNECTION_ADDED] =
+	    g_signal_new (NM_SETTINGS_PLUGIN_CONNECTION_ADDED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL,
+	                  g_cclosure_marshal_VOID__OBJECT,
+	                  G_TYPE_NONE, 1,
+	                  NM_TYPE_SETTINGS_CONNECTION);
+
+	signals[UNMANAGED_SPECS_CHANGED] =
+	    g_signal_new (NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL,
+	                  g_cclosure_marshal_VOID__VOID,
+	                  G_TYPE_NONE, 0);
+
+	signals[UNRECOGNIZED_SPECS_CHANGED] =
+	    g_signal_new (NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED,
+	                  G_OBJECT_CLASS_TYPE (object_class),
+	                  G_SIGNAL_RUN_FIRST,
+	                  0, NULL, NULL,
+	                  g_cclosure_marshal_VOID__VOID,
+	                  G_TYPE_NONE, 0);
 }
diff --git a/src/settings/nm-settings-plugin.h b/src/settings/nm-settings-plugin.h
index 6fba25e7..fdd48f2b 100644
--- a/src/settings/nm-settings-plugin.h
+++ b/src/settings/nm-settings-plugin.h
@@ -15,53 +15,52 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2007 - 2011 Red Hat, Inc.
+ * Copyright (C) 2007 - 2018 Red Hat, Inc.
  * Copyright (C) 2008 Novell, Inc.
  */
 
-#ifndef __NETWORKMANAGER_SETTINGS_PLUGIN_H__
-#define __NETWORKMANAGER_SETTINGS_PLUGIN_H__
+#ifndef __NM_SETTINGS_PLUGIN_H__
+#define __NM_SETTINGS_PLUGIN_H__
 
 #include "nm-connection.h"
 
-/* Plugin's factory function that returns a GObject that implements
- * NMSettingsPlugin.
- */
-GObject * nm_settings_plugin_factory (void);
-
 #define NM_TYPE_SETTINGS_PLUGIN               (nm_settings_plugin_get_type ())
 #define NM_SETTINGS_PLUGIN(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPlugin))
+#define NM_SETTINGS_PLUGIN_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPluginClass))
 #define NM_IS_SETTINGS_PLUGIN(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTINGS_PLUGIN))
-#define NM_SETTINGS_PLUGIN_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPluginInterface))
+#define NM_IS_SETTINGS_PLUGIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTINGS_PLUGIN))
+#define NM_SETTINGS_PLUGIN_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTINGS_PLUGIN, NMSettingsPluginClass))
 
-#define NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED "unmanaged-specs-changed"
+#define NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED    "unmanaged-specs-changed"
 #define NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED "unrecognized-specs-changed"
-#define NM_SETTINGS_PLUGIN_CONNECTION_ADDED "connection-added"
+#define NM_SETTINGS_PLUGIN_CONNECTION_ADDED           "connection-added"
 
-typedef struct _NMSettingsPlugin NMSettingsPlugin;
+typedef struct {
+	GObject parent;
+} NMSettingsPlugin;
 
 typedef struct {
-	GTypeInterface g_iface;
+	GObjectClass parent;
 
 	/* Called when the plugin is loaded to initialize it */
-	void     (*init) (NMSettingsPlugin *config);
+	void (*initialize) (NMSettingsPlugin *plugin);
 
 	/* Returns a GSList of NMSettingsConnection objects that represent
 	 * connections the plugin knows about.  The returned list is freed by the
 	 * system settings service.
 	 */
-	GSList * (*get_connections) (NMSettingsPlugin *config);
+	GSList * (*get_connections) (NMSettingsPlugin *plugin);
 
 	/* Requests that the plugin load/reload a single connection, if it
 	 * recognizes the filename. Returns success or failure.
 	 */
-	gboolean (*load_connection) (NMSettingsPlugin *config,
+	gboolean (*load_connection) (NMSettingsPlugin *plugin,
 	                             const char *filename);
 
 	/* Requests that the plugin reload all connection files from disk,
 	 * and emit signals reflecting new, changed, and removed connections.
 	 */
-	void (*reload_connections) (NMSettingsPlugin *config);
+	void (*reload_connections) (NMSettingsPlugin *plugin);
 
 	/*
 	 * Return a string list of specifications of devices which NetworkManager
@@ -72,7 +71,7 @@ typedef struct {
 	 * Each string in the list must be in one of the formats recognized by
 	 * nm_device_spec_match_list().
 	 */
-	GSList * (*get_unmanaged_specs) (NMSettingsPlugin *config);
+	GSList * (*get_unmanaged_specs) (NMSettingsPlugin *plugin);
 
 	/*
 	 * Return a string list of specifications of devices for which at least
@@ -84,7 +83,7 @@ typedef struct {
 	 * Each string in the list must be in one of the formats recognized by
 	 * nm_device_spec_match_list().
 	 */
-	GSList * (*get_unrecognized_specs) (NMSettingsPlugin *config);
+	GSList * (*get_unrecognized_specs) (NMSettingsPlugin *plugin);
 
 	/*
 	 * Initialize the plugin-specific connection and return a new
@@ -93,40 +92,42 @@ typedef struct {
 	 * storage if @save_to_disk is TRUE.  The returned object is owned by the
 	 * plugin and must be referenced by the owner if necessary.
 	 */
-	NMSettingsConnection * (*add_connection) (NMSettingsPlugin *config,
+	NMSettingsConnection * (*add_connection) (NMSettingsPlugin *plugin,
 	                                          NMConnection *connection,
 	                                          gboolean save_to_disk,
 	                                          GError **error);
+} NMSettingsPluginClass;
 
-	/* Signals */
-
-	/* Emitted when a new connection has been found by the plugin */
-	void (*connection_added)   (NMSettingsPlugin *config,
-	                            NMSettingsConnection *connection);
+GType nm_settings_plugin_get_type (void);
 
-	/* Emitted when the list of unmanaged device specifications changes */
-	void (*unmanaged_specs_changed) (NMSettingsPlugin *config);
+typedef NMSettingsPlugin *(*NMSettingsPluginFactoryFunc) (void);
 
-	/* Emitted when the list of devices with unrecognized connections changes */
-	void (*unrecognized_specs_changed) (NMSettingsPlugin *config);
-} NMSettingsPluginInterface;
+/* Plugin's factory function that returns a #NMSettingsPlugin */
+NMSettingsPlugin *nm_settings_plugin_factory (void);
 
-GType nm_settings_plugin_get_type (void);
+void nm_settings_plugin_initialize (NMSettingsPlugin *config);
 
-void nm_settings_plugin_init (NMSettingsPlugin *config);
+GSList *nm_settings_plugin_get_connections (NMSettingsPlugin *plugin);
 
-GSList *nm_settings_plugin_get_connections (NMSettingsPlugin *config);
-
-gboolean nm_settings_plugin_load_connection (NMSettingsPlugin *config,
+gboolean nm_settings_plugin_load_connection (NMSettingsPlugin *plugin,
                                              const char *filename);
-void nm_settings_plugin_reload_connections (NMSettingsPlugin *config);
+void nm_settings_plugin_reload_connections (NMSettingsPlugin *plugin);
 
-GSList *nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *config);
-GSList *nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *config);
+GSList *nm_settings_plugin_get_unmanaged_specs (NMSettingsPlugin *plugin);
+GSList *nm_settings_plugin_get_unrecognized_specs (NMSettingsPlugin *plugin);
 
-NMSettingsConnection *nm_settings_plugin_add_connection (NMSettingsPlugin *config,
+NMSettingsConnection *nm_settings_plugin_add_connection (NMSettingsPlugin *plugin,
                                                          NMConnection *connection,
                                                          gboolean save_to_disk,
                                                          GError **error);
 
-#endif /* __NETWORKMANAGER_SETTINGS_PLUGIN_H__ */
+/* internal API */
+
+void _nm_settings_plugin_emit_signal_connection_added (NMSettingsPlugin *plugin,
+                                                       NMSettingsConnection *sett_conn);
+
+void _nm_settings_plugin_emit_signal_unmanaged_specs_changed (NMSettingsPlugin *plugin);
+
+void _nm_settings_plugin_emit_signal_unrecognized_specs_changed (NMSettingsPlugin *plugin);
+
+#endif /* __NM_SETTINGS_PLUGIN_H__ */
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 5bb629dc..2253d56a 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -214,9 +214,9 @@ connection_ready_changed (NMSettingsConnection *conn,
 static void
 plugin_connection_added (NMSettingsPlugin *config,
                          NMSettingsConnection *connection,
-                         gpointer user_data)
+                         NMSettings *self)
 {
-	claim_connection (NM_SETTINGS (user_data), connection);
+	claim_connection (self, connection);
 }
 
 static void
@@ -237,7 +237,7 @@ load_connections (NMSettings *self)
 		// priority plugin.
 
 		for (elt = plugin_connections; elt; elt = g_slist_next (elt))
-			claim_connection (self, NM_SETTINGS_CONNECTION (elt->data));
+			claim_connection (self, elt->data);
 
 		g_slist_free (plugin_connections);
 
@@ -306,15 +306,15 @@ impl_settings_get_connection_by_uuid (NMDBusObject *obj,
                                       GVariant *parameters)
 {
 	NMSettings *self = NM_SETTINGS (obj);
-	NMSettingsConnection *connection = NULL;
+	NMSettingsConnection *sett_conn;
 	gs_unref_object NMAuthSubject *subject = NULL;
 	GError *error = NULL;
 	const char *uuid;
 
 	g_variant_get (parameters, "(&s)", &uuid);
 
-	connection = nm_settings_get_connection_by_uuid (self, uuid);
-	if (!connection) {
+	sett_conn = nm_settings_get_connection_by_uuid (self, uuid);
+	if (!sett_conn) {
 		error = g_error_new_literal (NM_SETTINGS_ERROR,
 		                             NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		                             "No connection with the UUID was found.");
@@ -329,7 +329,7 @@ impl_settings_get_connection_by_uuid (NMDBusObject *obj,
 		goto error;
 	}
 
-	if (!nm_auth_is_subject_in_acl_set_error (NM_CONNECTION (connection),
+	if (!nm_auth_is_subject_in_acl_set_error (nm_settings_connection_get_connection (sett_conn),
 	                                          subject,
 	                                          NM_SETTINGS_ERROR,
 	                                          NM_SETTINGS_ERROR_PERMISSION_DENIED,
@@ -338,7 +338,7 @@ impl_settings_get_connection_by_uuid (NMDBusObject *obj,
 
 	g_dbus_method_invocation_return_value (invocation,
 	                                       g_variant_new ("(o)",
-	                                                      nm_dbus_object_get_path (NM_DBUS_OBJECT (connection))));
+	                                                      nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn))));
 	return;
 
 error:
@@ -362,6 +362,7 @@ _clear_connections_cached_list (NMSettingsPrivate *priv)
 	        0xdeaddead,
 	        sizeof (NMSettingsConnection *) * (priv->connections_len + 1));
 #endif
+
 	nm_clear_g_free (&priv->connections_cached_list);
 }
 
@@ -480,8 +481,8 @@ nm_settings_get_connection_by_path (NMSettings *self, const char *path)
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	connection = (NMSettingsConnection *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
-	                                                                     path);
+	connection = nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (self)),
+	                                            path);
 	if (   !connection
 	    || !NM_IS_SETTINGS_CONNECTION (connection))
 		return NULL;
@@ -525,28 +526,6 @@ nm_settings_get_unmanaged_specs (NMSettings *self)
 	return priv->unmanaged_specs;
 }
 
-static NMSettingsPlugin *
-get_plugin (NMSettings *self, gboolean has_add_connection)
-{
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
-	GSList *iter;
-
-	g_return_val_if_fail (self != NULL, NULL);
-
-	/* Do any of the plugins support the given capability? */
-	for (iter = priv->plugins; iter; iter = iter->next) {
-		NMSettingsPlugin *plugin = NM_SETTINGS_PLUGIN (iter->data);
-
-		if (!has_add_connection)
-			return plugin;
-
-		if (NM_SETTINGS_PLUGIN_GET_INTERFACE (iter->data)->add_connection != NULL)
-			return plugin;
-	}
-
-	return NULL;
-}
-
 static gboolean
 find_spec (GSList *spec_list, const char *spec)
 {
@@ -607,67 +586,49 @@ unrecognized_specs_changed (NMSettingsPlugin *config,
 	              nm_settings_plugin_get_unrecognized_specs);
 }
 
-static gboolean
-add_plugin (NMSettings *self, NMSettingsPlugin *plugin)
+static void
+add_plugin (NMSettings *self, NMSettingsPlugin *plugin, const char *path)
 {
 	NMSettingsPrivate *priv;
-	const char *path;
 
-	g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
-	g_return_val_if_fail (NM_IS_SETTINGS_PLUGIN (plugin), FALSE);
+	nm_assert (NM_IS_SETTINGS (self));
+	nm_assert (NM_IS_SETTINGS_PLUGIN (plugin));
 
 	priv = NM_SETTINGS_GET_PRIVATE (self);
 
-	if (g_slist_find (priv->plugins, plugin)) {
-		/* don't add duplicates. */
-		return FALSE;
-	}
+	nm_assert (!g_slist_find (priv->plugins, plugin));
 
 	priv->plugins = g_slist_append (priv->plugins, g_object_ref (plugin));
-	nm_settings_plugin_init (plugin);
-
-
-	path = g_object_get_qdata (G_OBJECT (plugin), plugin_module_path_quark ());
-
-	_LOGI ("Loaded settings plugin: %s (%s)", G_OBJECT_TYPE_NAME (plugin), path ?: "internal");
-
-	return TRUE;
-}
-
-static gboolean
-plugin_loaded (GSList *list, const char *path)
-{
-	GSList *iter;
 
-	g_return_val_if_fail (path != NULL, TRUE);
+	nm_settings_plugin_initialize (plugin);
 
-	for (iter = list; iter; iter = g_slist_next (iter)) {
-		const char *list_path = g_object_get_qdata (G_OBJECT (iter->data),
-		                                            plugin_module_path_quark ());
-
-		if (g_strcmp0 (path, list_path) == 0)
-			return TRUE;
-	}
-
-	return FALSE;
+	_LOGI ("Loaded settings plugin: %s (%s%s%s)",
+	       G_OBJECT_TYPE_NAME (plugin),
+	       NM_PRINT_FMT_QUOTED (path, "\"", path, "\"", "internal"));
 }
 
 static gboolean
-load_plugin (NMSettings *self, GSList **list, const char *pname, GError **error)
+add_plugin_load_file (NMSettings *self, const char *pname, GError **error)
 {
+	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
 	gs_free char *full_name = NULL;
 	gs_free char *path = NULL;
-	gs_unref_object GObject *obj = NULL;
-	GModule *plugin;
-	GObject * (*factory_func) (void);
+	gs_unref_object NMSettingsPlugin *plugin = NULL;
+	GModule *module;
+	NMSettingsPluginFactoryFunc factory_func;
+	GSList *iter;
 	struct stat st;
 	int errsv;
 
 	full_name = g_strdup_printf ("nm-settings-plugin-%s", pname);
 	path = g_module_build_path (NMPLUGINDIR, full_name);
 
-	if (plugin_loaded (*list, path))
-		return TRUE;
+	for (iter = priv->plugins; iter; iter = iter->next) {
+		if (nm_streq0 (path,
+		               g_object_get_qdata (iter->data,
+		                                   plugin_module_path_quark ())))
+			return TRUE;
+	}
 
 	if (stat (path, &st) != 0) {
 		errsv = errno;
@@ -687,8 +648,8 @@ load_plugin (NMSettings *self, GSList **list, const char *pname, GError **error)
 		return TRUE;
 	}
 
-	plugin = g_module_open (path, G_MODULE_BIND_LOCAL);
-	if (!plugin) {
+	module = g_module_open (path, G_MODULE_BIND_LOCAL);
+	if (!module) {
 		_LOGW ("could not load plugin '%s' from file '%s': %s",
 		     pname, path, g_module_error ());
 		return TRUE;
@@ -696,48 +657,46 @@ load_plugin (NMSettings *self, GSList **list, const char *pname, GError **error)
 
 	/* errors after this point are fatal, because we loaded the shared library already. */
 
-	if (!g_module_symbol (plugin, "nm_settings_plugin_factory", (gpointer) (&factory_func))) {
+	if (!g_module_symbol (module, "nm_settings_plugin_factory", (gpointer) (&factory_func))) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
 		             "Could not find plugin '%s' factory function.",
 		             pname);
-		g_module_close (plugin);
+		g_module_close (module);
 		return FALSE;
 	}
 
 	/* after accessing the plugin we cannot unload it anymore, because the glib
 	 * types cannot be properly unregistered. */
-	g_module_make_resident (plugin);
+	g_module_make_resident (module);
 
-	obj = (*factory_func) ();
-	if (!obj || !NM_IS_SETTINGS_PLUGIN (obj)) {
+	plugin = (*factory_func) ();
+	if (!NM_IS_SETTINGS_PLUGIN (plugin)) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
-		             "Plugin '%s' returned invalid system config object.",
+		             "plugin '%s' returned invalid settings plugin",
 		             pname);
 		return FALSE;
 	}
 
-	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, g_steal_pointer (&obj));
-
+	add_plugin (self, NM_SETTINGS_PLUGIN (plugin), path);
+	g_object_set_qdata_full (G_OBJECT (plugin),
+	                         plugin_module_path_quark (),
+	                         g_steal_pointer (&path),
+	                         g_free);
 	return TRUE;
 }
 
 static void
-add_keyfile_plugin (NMSettings *self)
+add_plugin_keyfile (NMSettings *self)
 {
 	gs_unref_object NMSKeyfilePlugin *keyfile_plugin = NULL;
 
 	keyfile_plugin = nms_keyfile_plugin_new ();
-	if (!add_plugin (self, NM_SETTINGS_PLUGIN (keyfile_plugin)))
-		g_return_if_reached ();
+	add_plugin (self, NM_SETTINGS_PLUGIN (keyfile_plugin), NULL);
 }
 
 static gboolean
 load_plugins (NMSettings *self, const char **plugins, GError **error)
 {
-	GSList *list = NULL;
 	const char **iter;
 	gboolean keyfile_added = FALSE;
 	gboolean success = TRUE;
@@ -765,15 +724,15 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 			continue;
 		}
 
-		if (!strcmp (pname, "no-ibft"))
+		if (nm_streq (pname, "no-ibft"))
 			continue;
-		if (has_no_ibft && !strcmp (pname, "ibft"))
+		if (has_no_ibft && nm_streq (pname, "ibft"))
 			continue;
 
 		/* keyfile plugin is built-in now */
-		if (strcmp (pname, "keyfile") == 0) {
+		if (nm_streq (pname, "keyfile")) {
 			if (!keyfile_added) {
-				add_keyfile_plugin (self);
+				add_plugin_keyfile (self);
 				keyfile_added = TRUE;
 			}
 			continue;
@@ -787,27 +746,25 @@ load_plugins (NMSettings *self, const char **plugins, GError **error)
 			continue;
 		}
 
-		success = load_plugin (self, &list, pname, error);
+		success = add_plugin_load_file (self, pname, error);
 		if (!success)
 			break;
 
-		if (add_ibft && !strcmp (pname, "ifcfg-rh")) {
+		if (add_ibft && nm_streq (pname, "ifcfg-rh")) {
 			/* The plugin ibft is not explicitly mentioned but we just enabled "ifcfg-rh".
 			 * Enable "ibft" by default after "ifcfg-rh". */
 			pname = "ibft";
 			add_ibft = FALSE;
 
-			success = load_plugin (self, &list, "ibft", error);
+			success = add_plugin_load_file (self, "ibft", error);
 			if (!success)
 				break;
 		}
 	}
 
 	/* If keyfile plugin was not among configured plugins, add it as the last one */
-	if (!keyfile_added)
-		add_keyfile_plugin (self);
-
-	g_slist_free_full (list, g_object_unref);
+	if (!keyfile_added && success)
+		add_plugin_keyfile (self);
 
 	return success;
 }
@@ -931,29 +888,33 @@ openconnect_migrate_hack (NMConnection *connection)
 }
 
 static void
-claim_connection (NMSettings *self, NMSettingsConnection *connection)
+claim_connection (NMSettings *self, NMSettingsConnection *sett_conn)
 {
-	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	NMSettingsPrivate *priv;
 	GError *error = NULL;
 	const char *path;
 	NMSettingsConnection *existing;
 
-	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (connection));
-	g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (connection)));
+	g_return_if_fail (NM_IS_SETTINGS (self));
+	g_return_if_fail (NM_IS_SETTINGS_CONNECTION (sett_conn));
+	g_return_if_fail (!nm_dbus_object_is_exported (NM_DBUS_OBJECT (sett_conn)));
+
+	priv = NM_SETTINGS_GET_PRIVATE (self);
 
 	/* prevent duplicates */
-	if (!c_list_is_empty (&connection->_connections_lst)) {
-		nm_assert (c_list_contains (&priv->connections_lst_head, &connection->_connections_lst));
+	if (!c_list_is_empty (&sett_conn->_connections_lst)) {
+		nm_assert (c_list_contains (&priv->connections_lst_head, &sett_conn->_connections_lst));
 		return;
 	}
 
-	if (!nm_connection_normalize (NM_CONNECTION (connection), NULL, NULL, &error)) {
+	/* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
+	if (!nm_connection_normalize (nm_settings_connection_get_connection (sett_conn), NULL, NULL, &error)) {
 		_LOGW ("plugin provided invalid connection: %s", error->message);
 		g_error_free (error);
 		return;
 	}
 
-	existing = nm_settings_get_connection_by_uuid (self, nm_settings_connection_get_uuid (connection));
+	existing = nm_settings_get_connection_by_uuid (self, nm_settings_connection_get_uuid (sett_conn));
 	if (existing) {
 		/* Cannot add duplicate connections per UUID. Just return without action and
 		 * log a warning.
@@ -966,51 +927,56 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 		 * error out. That should not happen unless the admin misconfigured the system
 		 * to create conflicting connections. */
 		_LOGW ("plugin provided duplicate connection with UUID %s",
-		       nm_settings_connection_get_uuid (connection));
+		       nm_settings_connection_get_uuid (sett_conn));
 		return;
 	}
 
 	/* Read timestamp from look-aside file and put it into the connection's data */
-	nm_settings_connection_read_and_fill_timestamp (connection);
+	nm_settings_connection_read_and_fill_timestamp (sett_conn);
 
 	/* Read seen-bssids from look-aside file and put it into the connection's data */
-	nm_settings_connection_read_and_fill_seen_bssids (connection);
+	nm_settings_connection_read_and_fill_seen_bssids (sett_conn);
 
 	/* Ensure its initial visibility is up-to-date */
-	nm_settings_connection_recheck_visibility (connection);
+	nm_settings_connection_recheck_visibility (sett_conn);
 
 	/* Evil openconnect migration hack */
-	openconnect_migrate_hack (NM_CONNECTION (connection));
+	/* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
+	openconnect_migrate_hack (nm_settings_connection_get_connection (sett_conn));
 
 	/* This one unexports the connection, it needs to run late to give the active
 	 * connection a chance to deal with its reference to this settings connection. */
-	g_signal_connect_after (connection, NM_SETTINGS_CONNECTION_REMOVED,
+	g_signal_connect_after (sett_conn, NM_SETTINGS_CONNECTION_REMOVED,
 	                        G_CALLBACK (connection_removed), self);
-	g_signal_connect (connection, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
+	g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_UPDATED_INTERNAL,
 	                  G_CALLBACK (connection_updated), self);
-	g_signal_connect (connection, NM_SETTINGS_CONNECTION_FLAGS_CHANGED,
+	g_signal_connect (sett_conn, NM_SETTINGS_CONNECTION_FLAGS_CHANGED,
 	                  G_CALLBACK (connection_flags_changed),
 	                  self);
 	if (!priv->startup_complete) {
-		g_signal_connect (connection, "notify::" NM_SETTINGS_CONNECTION_READY,
+		g_signal_connect (sett_conn, "notify::" NM_SETTINGS_CONNECTION_READY,
 		                  G_CALLBACK (connection_ready_changed),
 		                  self);
 	}
 
 	_clear_connections_cached_list (priv);
 
-	g_object_ref (connection);
+	g_object_ref (sett_conn);
 	/* FIXME(shutdown): The NMSettings instance can't be disposed
 	 * while there is any exported connection. Ideally we should
 	 * unexport all connections on NMSettings' disposal, but for now
 	 * leak @self on termination when there are connections alive. */
 	g_object_ref (self);
 	priv->connections_len++;
-	c_list_link_tail (&priv->connections_lst_head, &connection->_connections_lst);
+	c_list_link_tail (&priv->connections_lst_head, &sett_conn->_connections_lst);
 
-	path = nm_dbus_object_export (NM_DBUS_OBJECT (connection));
+	path = nm_dbus_object_export (NM_DBUS_OBJECT (sett_conn));
 
-	nm_utils_log_connection_diff (NM_CONNECTION (connection), NULL, LOGL_DEBUG, LOGD_CORE, "new connection", "++ ",
+	nm_utils_log_connection_diff (nm_settings_connection_get_connection (sett_conn),
+	                              NULL,
+	                              LOGL_DEBUG,
+	                              LOGD_CORE,
+	                              "new connection", "++ ",
 	                              path);
 
 	/* Only emit the individual connection-added signal after connections
@@ -1021,13 +987,13 @@ claim_connection (NMSettings *self, NMSettingsConnection *connection)
 		                            &interface_info_settings,
 		                            &signal_info_new_connection,
 		                            "(o)",
-		                            nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)));
+		                            nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)));
 
-		g_signal_emit (self, signals[CONNECTION_ADDED], 0, connection);
+		g_signal_emit (self, signals[CONNECTION_ADDED], 0, sett_conn);
 		_notify (self, PROP_CONNECTIONS);
 	}
 
-	nm_settings_connection_added (connection);
+	nm_settings_connection_added (sett_conn);
 }
 
 static gboolean
@@ -1079,7 +1045,7 @@ nm_settings_add_connection (NMSettings *self,
 
 	/* Make sure a connection with this UUID doesn't already exist */
 	c_list_for_each_entry (candidate, &priv->connections_lst_head, _connections_lst) {
-		if (nm_streq0 (uuid, nm_connection_get_uuid (NM_CONNECTION (candidate)))) {
+		if (nm_streq0 (uuid, nm_settings_connection_get_uuid (candidate))) {
 			g_set_error_literal (error,
 			                     NM_SETTINGS_ERROR,
 			                     NM_SETTINGS_ERROR_UUID_EXISTS,
@@ -1113,8 +1079,13 @@ nm_settings_add_connection (NMSettings *self,
 
 		added = nm_settings_plugin_add_connection (plugin, connection, save_to_disk, &add_error);
 		if (added) {
-			if (secrets)
-				nm_connection_update_secrets (NM_CONNECTION (added), NULL, secrets, NULL);
+			if (secrets) {
+				/* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
+				nm_connection_update_secrets (nm_settings_connection_get_connection (added),
+				                              NULL,
+				                              secrets,
+				                              NULL);
+			}
 			claim_connection (self, added);
 			return added;
 		}
@@ -1132,7 +1103,7 @@ nm_settings_add_connection (NMSettings *self,
 
 static void
 send_agent_owned_secrets (NMSettings *self,
-                          NMSettingsConnection *connection,
+                          NMSettingsConnection *sett_conn,
                           NMAuthSubject *subject)
 {
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
@@ -1142,12 +1113,12 @@ send_agent_owned_secrets (NMSettings *self,
 	 * as agent-owned secrets are the only ones we send back to be saved.
 	 * Only send secrets to agents of the same UID that called update too.
 	 */
-	for_agent = nm_simple_connection_new_clone (NM_CONNECTION (connection));
+	for_agent = nm_simple_connection_new_clone (nm_settings_connection_get_connection (sett_conn));
 	nm_connection_clear_secrets_with_flags (for_agent,
 	                                        secrets_filter_cb,
 	                                        GUINT_TO_POINTER (NM_SETTING_SECRET_FLAG_AGENT_OWNED));
 	nm_agent_manager_save_secrets (priv->agent_mgr,
-	                               nm_dbus_object_get_path (NM_DBUS_OBJECT (connection)),
+	                               nm_dbus_object_get_path (NM_DBUS_OBJECT (sett_conn)),
 	                               for_agent,
 	                               subject);
 }
@@ -1190,7 +1161,8 @@ pk_add_cb (NMAuthChain *chain,
 	} else {
 		/* Authorized */
 		connection = nm_auth_chain_get_data (chain, "connection");
-		g_assert (connection);
+		nm_assert (connection);
+
 		save_to_disk = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "save-to-disk"));
 		added = nm_settings_add_connection (self, connection, save_to_disk, &error);
 	}
@@ -1281,14 +1253,6 @@ nm_settings_add_connection_dbus (NMSettings *self,
 		goto done;
 	}
 
-	/* Do any of the plugins support adding? */
-	if (!get_plugin (self, TRUE)) {
-		error = g_error_new_literal (NM_SETTINGS_ERROR,
-		                             NM_SETTINGS_ERROR_NOT_SUPPORTED,
-		                             "None of the registered plugins support add.");
-		goto done;
-	}
-
 	if (!nm_auth_is_subject_in_acl_set_error (connection,
 	                                          subject,
 	                                          NM_SETTINGS_ERROR,
@@ -1612,20 +1576,21 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 	NMSettingWired *s_wired;
 	const char *setting_hwaddr;
 	const char *perm_hw_addr;
-	NMSettingsConnection *connection;
+	NMSettingsConnection *sett_conn;
 
 	g_return_val_if_fail (NM_IS_SETTINGS (self), FALSE);
 
 	perm_hw_addr = nm_device_get_permanent_hw_address (device);
 
 	/* Find a wired connection locked to the given MAC address, if any */
-	c_list_for_each_entry (connection, &priv->connections_lst_head, _connections_lst) {
+	c_list_for_each_entry (sett_conn, &priv->connections_lst_head, _connections_lst) {
+		NMConnection *connection = nm_settings_connection_get_connection (sett_conn);
 		const char *ctype, *iface;
 
-		if (!nm_device_check_connection_compatible (device, NM_CONNECTION (connection)))
+		if (!nm_device_check_connection_compatible (device, connection, NULL))
 			continue;
 
-		s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
+		s_con = nm_connection_get_setting_connection (connection);
 
 		iface = nm_setting_connection_get_interface_name (s_con);
 		if (iface && strcmp (iface, nm_device_get_iface (device)) != 0)
@@ -1636,14 +1601,15 @@ have_connection_for_device (NMSettings *self, NMDevice *device)
 		    && strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME))
 			continue;
 
-		s_wired = nm_connection_get_setting_wired (NM_CONNECTION (connection));
+		s_wired = nm_connection_get_setting_wired (connection);
 
-		if (!s_wired && !strcmp (ctype, NM_SETTING_PPPOE_SETTING_NAME)) {
+		if (   !s_wired
+		    && nm_streq (ctype, NM_SETTING_PPPOE_SETTING_NAME)) {
 			/* No wired setting; therefore the PPPoE connection applies to any device */
 			return TRUE;
 		}
 
-		g_assert (s_wired != NULL);
+		nm_assert (s_wired);
 
 		setting_hwaddr = nm_setting_wired_get_mac_address (s_wired);
 		if (setting_hwaddr) {
@@ -1687,11 +1653,11 @@ default_wired_clear_tag (NMSettings *self,
                          NMSettingsConnection *connection,
                          gboolean add_to_no_auto_default)
 {
-	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_qdata (G_OBJECT (connection), _default_wired_device_quark ()));
-	g_return_if_fail (connection == g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ()));
+	nm_assert (NM_IS_SETTINGS (self));
+	nm_assert (NM_IS_DEVICE (device));
+	nm_assert (NM_IS_SETTINGS_CONNECTION (connection));
+	nm_assert (device == g_object_get_qdata (G_OBJECT (connection), _default_wired_device_quark ()));
+	nm_assert (connection == g_object_get_qdata (G_OBJECT (device), _default_wired_connection_quark ()));
 
 	g_object_set_qdata (G_OBJECT (connection), _default_wired_device_quark (), NULL);
 	g_object_set_qdata (G_OBJECT (device), _default_wired_connection_quark (), NULL);
@@ -1866,7 +1832,7 @@ get_property (GObject *object, guint prop_id,
 		                      : NULL);
 		break;
 	case PROP_CAN_MODIFY:
-		g_value_set_boolean (value, !!get_plugin (self, TRUE));
+		g_value_set_boolean (value, TRUE);
 		break;
 	case PROP_CONNECTIONS:
 		if (priv->connections_loaded) {
@@ -1930,6 +1896,7 @@ finalize (GObject *object)
 {
 	NMSettings *self = NM_SETTINGS (object);
 	NMSettingsPrivate *priv = NM_SETTINGS_GET_PRIVATE (self);
+	GSList *iter;
 
 	_clear_connections_cached_list (priv);
 
@@ -1938,7 +1905,12 @@ finalize (GObject *object)
 	g_slist_free_full (priv->unmanaged_specs, g_free);
 	g_slist_free_full (priv->unrecognized_specs, g_free);
 
-	g_slist_free_full (priv->plugins, g_object_unref);
+	while ((iter = priv->plugins)) {
+		gs_unref_object NMSettingsPlugin *plugin = iter->data;
+
+		priv->plugins = g_slist_delete_link (priv->plugins, iter);
+		g_signal_handlers_disconnect_by_data (plugin, self);
+	}
 
 	g_clear_object (&priv->agent_mgr);
 
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index 021512fd..38d8ad4e 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -85,7 +85,7 @@ void nm_settings_add_connection_dbus (NMSettings *self,
                                       NMSettingsAddCallback callback,
                                       gpointer user_data);
 
-NMSettingsConnection *const* nm_settings_get_connections (NMSettings *settings, guint *out_len);
+NMSettingsConnection *const*nm_settings_get_connections (NMSettings *settings, guint *out_len);
 
 NMSettingsConnection **nm_settings_get_connections_clone (NMSettings *self,
                                                           guint *out_len,
diff --git a/src/settings/plugins/ibft/meson.build b/src/settings/plugins/ibft/meson.build
index 1b6ff28a..c7dbe459 100644
--- a/src/settings/plugins/ibft/meson.build
+++ b/src/settings/plugins/ibft/meson.build
@@ -1,15 +1,9 @@
 name = 'nm-settings-plugin-ibft'
 
-cflags = [
-  '-DSBINDIR="@0@"'.format(nm_sbindir),
-  '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir)
-]
-
 libnms_ibft_core = static_library(
   'nms-ibft-core',
   'nms-ibft-reader.c',
   dependencies: nm_dep,
-  c_args: cflags
 )
 
 sources = files(
@@ -21,7 +15,6 @@ libnm_settings_plugin_ibft = shared_module(
   name,
   sources: sources,
   dependencies: nm_dep,
-  c_args: cflags,
   link_with: libnms_ibft_core,
   link_args: ldflags_linker_script_settings,
   link_depends: linker_script_settings,
diff --git a/src/settings/plugins/ibft/nms-ibft-plugin.c b/src/settings/plugins/ibft/nms-ibft-plugin.c
index 77ce208f..69dd3733 100644
--- a/src/settings/plugins/ibft/nms-ibft-plugin.c
+++ b/src/settings/plugins/ibft/nms-ibft-plugin.c
@@ -42,19 +42,15 @@ typedef struct {
 } NMSIbftPluginPrivate;
 
 struct _NMSIbftPlugin {
-	GObject parent;
+	NMSettingsPlugin parent;
 	NMSIbftPluginPrivate _priv;
 };
 
 struct _NMSIbftPluginClass {
-	GObjectClass parent;
+	NMSettingsPluginClass parent;
 };
 
-static void settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface);
-
-G_DEFINE_TYPE_EXTENDED (NMSIbftPlugin, nms_ibft_plugin, G_TYPE_OBJECT, 0,
-                        G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_PLUGIN,
-                                               settings_plugin_interface_init))
+G_DEFINE_TYPE (NMSIbftPlugin, nms_ibft_plugin, NM_TYPE_SETTINGS_PLUGIN);
 
 #define NMS_IBFT_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSIbftPlugin, NMS_IS_IBFT_PLUGIN)
 
@@ -84,9 +80,9 @@ read_connections (NMSIbftPlugin *self)
 		connection = nms_ibft_connection_new (iter->data, &error);
 		if (connection) {
 			nm_log_info (LOGD_SETTINGS, "ibft: read connection '%s'",
-			             nm_connection_get_id (NM_CONNECTION (connection)));
+			             nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (connection)));
 			g_hash_table_insert (priv->connections,
-			                     g_strdup (nm_connection_get_uuid (NM_CONNECTION (connection))),
+			                     g_strdup (nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection))),
 			                     connection);
 		} else {
 			nm_log_warn (LOGD_SETTINGS, "ibft: failed to read iscsiadm record: %s", error->message);
@@ -121,11 +117,6 @@ get_connections (NMSettingsPlugin *config)
 /*****************************************************************************/
 
 static void
-init (NMSettingsPlugin *config)
-{
-}
-
-static void
 nms_ibft_plugin_init (NMSIbftPlugin *self)
 {
 	NMSIbftPluginPrivate *priv = NMS_IBFT_PLUGIN_GET_PRIVATE (self);
@@ -148,24 +139,20 @@ dispose (GObject *object)
 }
 
 static void
-nms_ibft_plugin_class_init (NMSIbftPluginClass *req_class)
+nms_ibft_plugin_class_init (NMSIbftPluginClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass);
 
 	object_class->dispose = dispose;
-}
 
-static void
-settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface)
-{
-	plugin_iface->get_connections = get_connections;
-	plugin_iface->init = init;
+	plugin_class->get_connections = get_connections;
 }
 
 /*****************************************************************************/
 
-G_MODULE_EXPORT GObject *
+G_MODULE_EXPORT NMSettingsPlugin *
 nm_settings_plugin_factory (void)
 {
-	return G_OBJECT (g_object_ref (nms_ibft_plugin_get ()));
+	return NM_SETTINGS_PLUGIN (g_object_ref (nms_ibft_plugin_get ()));
 }
diff --git a/src/settings/plugins/ibft/nms-ibft-reader.c b/src/settings/plugins/ibft/nms-ibft-reader.c
index cf849e28..ac5824a1 100644
--- a/src/settings/plugins/ibft/nms-ibft-reader.c
+++ b/src/settings/plugins/ibft/nms-ibft-reader.c
@@ -98,7 +98,7 @@ nms_ibft_reader_load_blocks (const char *iscsiadm_path,
 	const char *envp[1] = { NULL };
 	GSList *blocks = NULL;
 	char *out = NULL, *err = NULL;
-	gint status = 0;
+	int status = 0;
 	char **lines = NULL, **iter;
 	GPtrArray *block_lines = NULL;
 	gboolean success = FALSE;
diff --git a/src/settings/plugins/ibft/tests/meson.build b/src/settings/plugins/ibft/tests/meson.build
index 59f49c31..8b5e143a 100644
--- a/src/settings/plugins/ibft/tests/meson.build
+++ b/src/settings/plugins/ibft/tests/meson.build
@@ -6,7 +6,6 @@ exe = executable(
   test_unit,
   test_unit + '.c',
   dependencies: test_nm_dep,
-  c_args: nm_build_cflags,
   link_with: libnms_ibft_core
 )
 
diff --git a/src/settings/plugins/ifcfg-rh/meson.build b/src/settings/plugins/ifcfg-rh/meson.build
index 964ce22d..5d9689a5 100644
--- a/src/settings/plugins/ifcfg-rh/meson.build
+++ b/src/settings/plugins/ifcfg-rh/meson.build
@@ -3,11 +3,6 @@ install_data(
   install_dir: dbus_conf_dir
 )
 
-cflags = [
-  '-DSBINDIR="@0@"'.format(nm_sbindir),
-  '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir)
-]
-
 name = 'nmdbus-ifcfg-rh'
 
 dbus_sources = gnome.gdbus_codegen(
@@ -21,7 +16,6 @@ libnmdbus_ifcfg_rh = static_library(
   name,
   sources: dbus_sources,
   dependencies: glib_dep,
-  c_args: cflags
 )
 
 sources = files(
@@ -33,7 +27,6 @@ sources = files(
 )
 
 deps = [
-  crypto_dep,
   nm_dep
 ]
 
@@ -41,7 +34,6 @@ libnms_ifcfg_rh_core = static_library(
   'nms-ifcfg-rh-core',
   sources: sources,
   dependencies: deps,
-  c_args: cflags
 )
 
 sources = [dbus_sources] + files('nms-ifcfg-rh-connection.c')
@@ -50,7 +42,6 @@ libnm_settings_plugin_ifcfg_rh = shared_module(
   'nm-settings-plugin-ifcfg-rh',
   sources: sources,
   dependencies: deps,
-  c_args: cflags,
   link_with: [libnms_ifcfg_rh_core],
   link_args: ldflags_linker_script_settings,
   link_depends: linker_script_settings,
diff --git a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c
index 2863df64..e7a74a1a 100644
--- a/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c
+++ b/src/settings/plugins/ifcfg-rh/nm-inotify-helper.c
@@ -120,8 +120,8 @@ inotify_event_handler (GIOChannel *channel, GIOCondition cond, gpointer user_dat
 	struct inotify_event evt;
 
 	/* read the notifications from the watch descriptor */
-	while (g_io_channel_read_chars (channel, (gchar *) &evt, sizeof (struct inotify_event), NULL, NULL) == G_IO_STATUS_NORMAL) {
-		gchar filename[PATH_MAX + 1];
+	while (g_io_channel_read_chars (channel, (char *) &evt, sizeof (struct inotify_event), NULL, NULL) == G_IO_STATUS_NORMAL) {
+		char filename[PATH_MAX + 1];
 
 		filename[0] = '\0';
 		if (evt.len > 0) {
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 6979fdaa..ca319ddc 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-connection.c
@@ -143,7 +143,7 @@ devtimeout_expired (gpointer user_data)
 	NMIfcfgConnectionPrivate *priv = NM_IFCFG_CONNECTION_GET_PRIVATE (self);
 
 	nm_log_info (LOGD_SETTINGS, "Device for connection '%s' did not appear before timeout",
-	             nm_connection_get_id (NM_CONNECTION (self)));
+	             nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (self)));
 
 	g_signal_handler_disconnect (NM_PLATFORM_GET, priv->devtimeout_link_changed_handler);
 	priv->devtimeout_link_changed_handler = 0;
@@ -163,7 +163,7 @@ nm_ifcfg_connection_check_devtimeout (NMIfcfgConnection *self)
 	guint devtimeout;
 	const NMPlatformLink *pllink;
 
-	s_con = nm_connection_get_setting_connection (NM_CONNECTION (self));
+	s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (self)));
 
 	if (!nm_setting_connection_get_autoconnect (s_con))
 		return;
@@ -186,7 +186,7 @@ nm_ifcfg_connection_check_devtimeout (NMIfcfgConnection *self)
 	nm_settings_connection_set_ready (NM_SETTINGS_CONNECTION (self), FALSE);
 
 	nm_log_info (LOGD_SETTINGS, "Waiting %u seconds for %s to appear for connection '%s'",
-	             devtimeout, ifname, nm_connection_get_id (NM_CONNECTION (self)));
+	             devtimeout, ifname, nm_settings_connection_get_id (NM_SETTINGS_CONNECTION (self)));
 
 	priv->devtimeout_link_changed_handler =
 	    g_signal_connect (NM_PLATFORM_GET, NM_PLATFORM_SIGNAL_LINK_CHANGED,
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 0fb77b9d..6cac8cb6 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-plugin.c
@@ -70,19 +70,15 @@ typedef struct {
 } SettingsPluginIfcfgPrivate;
 
 struct _SettingsPluginIfcfg {
-	GObject parent;
+	NMSettingsPlugin parent;
 	SettingsPluginIfcfgPrivate _priv;
 };
 
 struct _SettingsPluginIfcfgClass {
-	GObjectClass parent;
+	NMSettingsPluginClass parent;
 };
 
-static void settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface);
-
-G_DEFINE_TYPE_EXTENDED (SettingsPluginIfcfg, settings_plugin_ifcfg, G_TYPE_OBJECT, 0,
-                        G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_PLUGIN,
-                                               settings_plugin_interface_init))
+G_DEFINE_TYPE (SettingsPluginIfcfg, settings_plugin_ifcfg, NM_TYPE_SETTINGS_PLUGIN)
 
 #define SETTINGS_PLUGIN_IFCFG_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfcfg, SETTINGS_IS_PLUGIN_IFCFG)
 
@@ -139,7 +135,7 @@ static void
 connection_removed_cb (NMSettingsConnection *obj, gpointer user_data)
 {
 	g_hash_table_remove (SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) user_data)->connections,
-	                     nm_connection_get_uuid (NM_CONNECTION (obj)));
+	                     nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (obj)));
 }
 
 static void
@@ -157,16 +153,16 @@ remove_connection (SettingsPluginIfcfg *self, NMIfcfgConnection *connection)
 	unrecognized = !!nm_ifcfg_connection_get_unrecognized_spec (connection);
 
 	g_object_ref (connection);
-	g_hash_table_remove (priv->connections, nm_connection_get_uuid (NM_CONNECTION (connection)));
+	g_hash_table_remove (priv->connections, nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection)));
 	if (!unmanaged && !unrecognized)
 		nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection));
 	g_object_unref (connection);
 
 	/* Emit changes _after_ removing the connection */
 	if (unmanaged)
-		g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED);
+		_nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
 	if (unrecognized)
-		g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED);
+		_nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
 }
 
 static NMIfcfgConnection *
@@ -228,7 +224,7 @@ update_connection (SettingsPluginIfcfg *self,
 		return NULL;
 	}
 
-	uuid = nm_connection_get_uuid (NM_CONNECTION (connection_new));
+	uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new));
 	connection_by_uuid = g_hash_table_lookup (priv->connections, uuid);
 
 	if (   connection
@@ -285,12 +281,16 @@ update_connection (SettingsPluginIfcfg *self,
 
 		if (   !unmanaged_changed
 		    && !unrecognized_changed
-		    && nm_connection_compare (NM_CONNECTION (connection_by_uuid),
-		                              NM_CONNECTION (connection_new),
+		    && nm_connection_compare (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_by_uuid)),
+		                              nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
 		                              NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
 		                              NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
-			if (old_path && g_strcmp0 (old_path, full_path) != 0)
-				_LOGI ("rename \"%s\" to "NM_IFCFG_CONNECTION_LOG_FMT" without other changes", nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid)), NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
+			if (   old_path
+			    && !nm_streq0 (old_path, full_path)) {
+				_LOGI ("rename \"%s\" to "NM_IFCFG_CONNECTION_LOG_FMT" without other changes",
+				       nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid)),
+				       NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
+			}
 		} else {
 
 			/*******************************************************
@@ -299,7 +299,7 @@ update_connection (SettingsPluginIfcfg *self,
 
 			if (source)
 				_LOGI ("update "NM_IFCFG_CONNECTION_LOG_FMT" from %s", NM_IFCFG_CONNECTION_LOG_ARG (connection_new), NM_IFCFG_CONNECTION_LOG_PATH (old_path));
-			else if (!g_strcmp0 (old_path, nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_new))))
+			else if (nm_streq0 (old_path, nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_new))))
 				_LOGI ("update "NM_IFCFG_CONNECTION_LOG_FMT, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
 			else if (old_path)
 				_LOGI ("rename \"%s\" to "NM_IFCFG_CONNECTION_LOG_FMT, old_path, NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
@@ -312,7 +312,7 @@ update_connection (SettingsPluginIfcfg *self,
 			              NULL);
 
 			if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid),
-			                                    NM_CONNECTION (connection_new),
+			                                    nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
 			                                    NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
 			                                    NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
 			                                    "ifcfg-update",
@@ -338,25 +338,27 @@ update_connection (SettingsPluginIfcfg *self,
 					 * so add it back now.
 					 */
 					g_hash_table_insert (priv->connections,
-					                     g_strdup (nm_connection_get_uuid (NM_CONNECTION (connection_by_uuid))),
+					                     g_strdup (nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_by_uuid))),
 					                     connection_by_uuid /* we took reference above and pass it on */);
 				}
 			} else {
 				if (old_unmanaged /* && !new_unmanaged */) {
 					_LOGI ("Managing connection "NM_IFCFG_CONNECTION_LOG_FMT" and its device because NM_CONTROLLED was true.",
 					       NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
-					g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_CONNECTION_ADDED, connection_by_uuid);
+					_nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
+					                                                  NM_SETTINGS_CONNECTION (connection_by_uuid));
 				} else if (old_unrecognized /* && !new_unrecognized */) {
 					_LOGI ("Managing connection "NM_IFCFG_CONNECTION_LOG_FMT" because it is now a recognized type.",
 					       NM_IFCFG_CONNECTION_LOG_ARG (connection_new));
-					g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_CONNECTION_ADDED, connection_by_uuid);
+					_nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
+					                                                  NM_SETTINGS_CONNECTION (connection_by_uuid));
 				}
 			}
 
 			if (unmanaged_changed)
-				g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED);
+				_nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
 			if (unrecognized_changed)
-				g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED);
+				_nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
 		}
 		nm_settings_connection_set_filename (NM_SETTINGS_CONNECTION (connection_by_uuid), full_path);
 		g_object_unref (connection_new);
@@ -394,11 +396,13 @@ update_connection (SettingsPluginIfcfg *self,
 			/* Only raise the signal if we were called without source, i.e. if we read the connection from file.
 			 * Otherwise, we were called by add_connection() which does not expect the signal. */
 			if (nm_ifcfg_connection_get_unmanaged_spec (connection_new))
-				g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED);
+				_nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
 			else if (nm_ifcfg_connection_get_unrecognized_spec (connection_new))
-				g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_UNRECOGNIZED_SPECS_CHANGED);
-			else
-				g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_CONNECTION_ADDED, connection_new);
+				_nm_settings_plugin_emit_signal_unrecognized_specs_changed (NM_SETTINGS_PLUGIN (self));
+			else {
+				_nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
+				                                                  NM_SETTINGS_CONNECTION (connection_new));
+			}
 		}
 		return connection_new;
 	}
@@ -731,7 +735,7 @@ impl_ifcfgrh_get_ifcfg_details (SettingsPluginIfcfg *plugin,
 		return;
 	}
 
-	s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
+	s_con = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection)));
 	if (!s_con) {
 		g_dbus_method_invocation_return_error (context,
 		                                       NM_SETTINGS_ERROR,
@@ -998,11 +1002,6 @@ config_changed_cb (NMConfig *config,
 /*****************************************************************************/
 
 static void
-init (NMSettingsPlugin *config)
-{
-}
-
-static void
 settings_plugin_ifcfg_init (SettingsPluginIfcfg *plugin)
 {
 	SettingsPluginIfcfgPrivate *priv = SETTINGS_PLUGIN_IFCFG_GET_PRIVATE ((SettingsPluginIfcfg *) plugin);
@@ -1059,30 +1058,26 @@ dispose (GObject *object)
 }
 
 static void
-settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *req_class)
+settings_plugin_ifcfg_class_init (SettingsPluginIfcfgClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass);
 
 	object_class->constructed = constructed;
 	object_class->dispose = dispose;
-}
 
-static void
-settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface)
-{
-	plugin_iface->get_connections = get_connections;
-	plugin_iface->add_connection = add_connection;
-	plugin_iface->load_connection = load_connection;
-	plugin_iface->reload_connections = reload_connections;
-	plugin_iface->get_unmanaged_specs = get_unmanaged_specs;
-	plugin_iface->get_unrecognized_specs = get_unrecognized_specs;
-	plugin_iface->init = init;
+	plugin_class->get_connections = get_connections;
+	plugin_class->add_connection = add_connection;
+	plugin_class->load_connection = load_connection;
+	plugin_class->reload_connections = reload_connections;
+	plugin_class->get_unmanaged_specs = get_unmanaged_specs;
+	plugin_class->get_unrecognized_specs = get_unrecognized_specs;
 }
 
 /*****************************************************************************/
 
-G_MODULE_EXPORT GObject *
+G_MODULE_EXPORT NMSettingsPlugin *
 nm_settings_plugin_factory (void)
 {
-	return G_OBJECT (g_object_ref (settings_plugin_ifcfg_get ()));
+	return NM_SETTINGS_PLUGIN (g_object_ref (settings_plugin_ifcfg_get ()));
 }
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
index c1960846..09a37991 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c
@@ -33,6 +33,7 @@
 #include <sys/ioctl.h>
 #include <unistd.h>
 
+#include "nm-utils/nm-secret-utils.h"
 #include "nm-connection.h"
 #include "nm-dbus-interface.h"
 #include "nm-setting-connection.h"
@@ -41,6 +42,7 @@
 #include "nm-setting-ip6-config.h"
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless.h"
+#include "nm-setting-ethtool.h"
 #include "nm-setting-8021x.h"
 #include "nm-setting-bond.h"
 #include "nm-setting-team.h"
@@ -53,6 +55,7 @@
 #include "nm-setting-generic.h"
 #include "nm-core-internal.h"
 #include "nm-utils.h"
+#include "nm-ethtool-utils.h"
 
 #include "platform/nm-platform.h"
 #include "NetworkManagerUtils.h"
@@ -77,6 +80,210 @@
 
 /*****************************************************************************/
 
+static char *
+get_full_file_path (const char *ifcfg_path, const char *file_path)
+{
+	const char *base = file_path;
+	char *p, *ret, *dirname;
+
+	g_return_val_if_fail (ifcfg_path != NULL, NULL);
+	g_return_val_if_fail (file_path != NULL, NULL);
+
+	if (file_path[0] == '/')
+		return g_strdup (file_path);
+
+	p = strrchr (file_path, '/');
+	if (p)
+		base = p + 1;
+
+	dirname = g_path_get_dirname (ifcfg_path);
+	ret = g_build_path ("/", dirname, base, NULL);
+	g_free (dirname);
+	return ret;
+}
+
+/*****************************************************************************/
+
+static NMSettingSecretFlags
+_secret_read_ifcfg_flags (shvarFile *ifcfg, const char *flags_key)
+{
+	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+	gs_free char *val_free = NULL;
+	const char *val;
+
+	nm_assert (flags_key);
+	nm_assert (g_str_has_suffix (flags_key, "_FLAGS"));
+
+	val = svGetValueStr (ifcfg, flags_key, &val_free);
+	if (val) {
+		if (strstr (val, SECRET_FLAG_AGENT))
+			flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;
+		if (strstr (val, SECRET_FLAG_NOT_SAVED))
+			flags |= NM_SETTING_SECRET_FLAG_NOT_SAVED;
+		if (strstr (val, SECRET_FLAG_NOT_REQUIRED))
+			flags |= NM_SETTING_SECRET_FLAG_NOT_REQUIRED;
+	}
+	return flags;
+}
+
+static void
+_secret_read_ifcfg (shvarFile *ifcfg,
+                    shvarFile *keys_ifcfg,
+                    const char *name,
+                    char **value,
+                    NMSettingSecretFlags *flags)
+{
+	char flags_key[250];
+
+	nm_sprintf_buf (flags_key, "%s_FLAGS", name);
+
+	*flags = _secret_read_ifcfg_flags (ifcfg, flags_key);
+
+	if (*flags != NM_SETTING_SECRET_FLAG_NONE)
+		*value = NULL;
+	else {
+		*value = svGetValue_cp (ifcfg, name);
+		if (!*value && keys_ifcfg)
+			*value = svGetValue_cp (keys_ifcfg, name);
+	}
+}
+
+static void
+_secret_set_from_ifcfg (gpointer setting,
+                        shvarFile *ifcfg,
+                        shvarFile *keys_ifcfg,
+                        const char *ifcfg_key,
+                        const char *property_name)
+{
+	nm_auto_free_secret char *secret = NULL;
+	NMSettingSecretFlags flags;
+	char flags_key[250];
+
+	nm_assert (NM_IS_SETTING (setting));
+
+	_secret_read_ifcfg (ifcfg, keys_ifcfg, ifcfg_key, &secret, &flags);
+
+	g_object_set (setting,
+	              property_name,
+	              secret,
+	              nm_sprintf_buf (flags_key, "%s-flags", property_name),
+	              flags,
+	              NULL);
+}
+
+static gboolean
+_secret_password_raw_to_bytes (const char *ifcfg_key,
+                               const char *password_raw,
+                               GBytes **out_bytes,
+                               GError **error)
+{
+	nm_auto_free_secret_buf NMSecretBuf *secret = NULL;
+	gsize len;
+
+	if (!password_raw) {
+		NM_SET_OUT (out_bytes, NULL);
+		return TRUE;
+	}
+
+	if (password_raw[0] == '0' && password_raw[1] == 'x')
+		password_raw += 2;
+
+	secret = nm_secret_buf_new (strlen (password_raw) / 2 + 3);
+	if (!_nm_utils_str2bin_full (password_raw, FALSE, ":", secret->bin, secret->len, &len)) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "Invalid hex password in %s",
+		             ifcfg_key);
+		return FALSE;
+	}
+
+	NM_SET_OUT (out_bytes, nm_secret_buf_to_gbytes_take (g_steal_pointer (&secret), len));
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+static GBytes *
+_cert_get_cert_bytes (const char *ifcfg_path,
+                      const char *value,
+                      GError **error)
+{
+	gs_free char *path = NULL;
+
+	if (g_str_has_prefix (value, "pkcs11:"))
+		return _nm_setting_802_1x_cert_value_to_bytes (NM_SETTING_802_1X_CK_SCHEME_PKCS11, (guint8 *) value, -1, error);
+
+	path = get_full_file_path (ifcfg_path, value);
+	return _nm_setting_802_1x_cert_value_to_bytes (NM_SETTING_802_1X_CK_SCHEME_PATH, (guint8 *) path, -1, error);
+}
+
+static gboolean
+_cert_get_cert (shvarFile *ifcfg,
+                const char *ifcfg_key,
+                GBytes **out_cert,
+                NMSetting8021xCKScheme *out_scheme,
+                GError **error)
+{
+	nm_auto_free_secret char *val_free = NULL;
+	const char *val;
+	gs_unref_bytes GBytes *cert = NULL;
+	GError *local = NULL;
+	NMSetting8021xCKScheme scheme;
+
+	val = svGetValueStr (ifcfg, ifcfg_key, &val_free);
+	if (!val) {
+		NM_SET_OUT (out_cert, NULL);
+		NM_SET_OUT (out_scheme, NM_SETTING_802_1X_CK_SCHEME_UNKNOWN);
+		return TRUE;
+	}
+
+	cert = _cert_get_cert_bytes (svFileGetName (ifcfg), val, &local);
+	if (!cert)
+		goto err;
+
+	scheme = _nm_setting_802_1x_cert_get_scheme (cert, &local);
+	if (scheme == NM_SETTING_802_1X_CK_SCHEME_UNKNOWN)
+		goto err;
+
+	NM_SET_OUT (out_cert, g_steal_pointer (&cert));
+	NM_SET_OUT (out_scheme, scheme);
+	return TRUE;
+
+err:
+	g_set_error (error,
+	             NM_SETTINGS_ERROR,
+	             NM_SETTINGS_ERROR_INVALID_CONNECTION,
+	             "invalid certificate %s: %s",
+	             ifcfg_key,
+	             local->message);
+	g_error_free (local);
+	return FALSE;
+}
+
+static gboolean
+_cert_set_from_ifcfg (gpointer setting,
+                      shvarFile *ifcfg,
+                      const char *ifcfg_key,
+                      const char *property_name,
+                      GBytes **out_cert,
+                      GError **error)
+{
+	gs_unref_bytes GBytes *cert = NULL;
+
+	if (!_cert_get_cert (ifcfg,
+	                     ifcfg_key,
+	                     &cert,
+	                     NULL,
+	                     error))
+		return FALSE;
+
+	g_object_set (setting, property_name, cert, NULL);
+
+	NM_SET_OUT (out_cert, g_steal_pointer (&cert));
+	return TRUE;
+}
+
+/*****************************************************************************/
+
 static void
 check_if_bond_slave (shvarFile *ifcfg,
                      NMSettingConnection *s_con)
@@ -232,13 +439,16 @@ make_connection_setting (const char *file,
 	              NM_SETTING_CONNECTION_AUTOCONNECT,
 	              svGetValueBoolean (ifcfg, "ONBOOT", TRUE),
 	              NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY,
-	              (gint) svGetValueInt64 (ifcfg, "AUTOCONNECT_PRIORITY", 10,
+	              (int) svGetValueInt64 (ifcfg, "AUTOCONNECT_PRIORITY", 10,
 	                                      NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN,
 	                                      NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX,
 	                                      NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT),
 	              NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,
-	              (gint) svGetValueInt64 (ifcfg, "AUTOCONNECT_RETRIES", 10,
+	              (int) svGetValueInt64 (ifcfg, "AUTOCONNECT_RETRIES", 10,
 	                                      -1, G_MAXINT32, -1),
+	              NM_SETTING_CONNECTION_MULTI_CONNECT,
+	              (gint) svGetValueInt64 (ifcfg, "MULTI_CONNECT", 10,
+	                                      G_MININT32, G_MAXINT32, NM_CONNECTION_MULTI_CONNECT_DEFAULT),
 	              NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
 	              svGetValueBoolean (ifcfg, "AUTOCONNECT_SLAVES", NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT),
 	              NM_SETTING_CONNECTION_LLDP, lldp,
@@ -249,7 +459,7 @@ make_connection_setting (const char *file,
 	if (v) {
 		gs_free const char **items = NULL;
 
-		items = nm_utils_strsplit_set (v, " ");
+		items = nm_utils_strsplit_set (v, " ", FALSE);
 		for (iter = items; iter && *iter; iter++) {
 			if (!nm_setting_connection_add_permission (s_con, "user", *iter, NULL))
 				PARSE_WARNING ("invalid USERS item '%s'", *iter);
@@ -265,7 +475,7 @@ make_connection_setting (const char *file,
 	if (v) {
 		gs_free const char **items = NULL;
 
-		items = nm_utils_strsplit_set (v, " \t");
+		items = nm_utils_strsplit_set (v, " \t", FALSE);
 		for (iter = items; iter && *iter; iter++) {
 			if (!nm_setting_connection_add_secondary (s_con, *iter))
 				PARSE_WARNING ("secondary connection UUID '%s' already added", *iter);
@@ -335,7 +545,7 @@ make_connection_setting (const char *file,
 	}
 
 	vint64 = svGetValueInt64 (ifcfg, "AUTH_RETRIES", 10, -1, G_MAXINT32, -1);
-	g_object_set (s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (gint) vint64, NULL);
+	g_object_set (s_con, NM_SETTING_CONNECTION_AUTH_RETRIES, (int) vint64, NULL);
 
 	i_val = NM_SETTING_CONNECTION_MDNS_DEFAULT;
 	if (!svGetValueEnum (ifcfg, "MDNS",
@@ -344,6 +554,13 @@ make_connection_setting (const char *file,
 		PARSE_WARNING ("invalid MDNS setting");
 	g_object_set (s_con, NM_SETTING_CONNECTION_MDNS, i_val, NULL);
 
+	i_val = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
+	if (!svGetValueEnum (ifcfg, "LLMNR",
+	                     nm_setting_connection_llmnr_get_type (),
+	                     &i_val, NULL))
+		PARSE_WARNING ("invalid LLMNR setting");
+	g_object_set (s_con, NM_SETTING_CONNECTION_LLMNR, i_val, NULL);
+
 	return NM_SETTING (s_con);
 }
 
@@ -674,7 +891,7 @@ parse_route_line (const char *line,
 	 * Maybe later we want to support some form of quotation here.
 	 * Which of course, would be incompatible with initscripts.
 	 */
-	words_free = nm_utils_strsplit_set (line, " \t\n");
+	words_free = nm_utils_strsplit_set (line, " \t\n", FALSE);
 
 	words = words_free ?: NM_PTRARRAY_EMPTY (const char *);
 
@@ -1110,7 +1327,7 @@ parse_dns_options (NMSettingIPConfig *ip_config, const char *value)
 	if (!nm_setting_ip_config_has_dns_options (ip_config))
 		nm_setting_ip_config_clear_dns_options (ip_config, TRUE);
 
-	options = nm_utils_strsplit_set (value, " ");
+	options = nm_utils_strsplit_set (value, " ", FALSE);
 	if (options) {
 		for (item = options; *item; item++) {
 			if (!nm_setting_ip_config_add_dns_option (ip_config, *item))
@@ -1178,7 +1395,7 @@ make_user_setting (shvarFile *ifcfg)
 	const char *key;
 	nm_auto_free_gstring GString *str = NULL;
 
-	keys = svGetKeys (ifcfg);
+	keys = svGetKeys (ifcfg, SV_KEY_TYPE_USER);
 	if (!keys)
 		return NULL;
 
@@ -1187,9 +1404,6 @@ make_user_setting (shvarFile *ifcfg)
 		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)
@@ -1217,6 +1431,32 @@ make_user_setting (shvarFile *ifcfg)
 }
 
 static NMSetting *
+make_match_setting (shvarFile *ifcfg)
+{
+	NMSettingMatch *s_match = NULL;
+	gs_free const char **strv = NULL;
+	gs_free char *value = NULL;
+	const char *v;
+	gsize i;
+
+	v = svGetValueStr (ifcfg, "MATCH_INTERFACE_NAME", &value);
+	if (!v)
+		return NULL;
+
+	strv = nm_utils_strsplit_set (v, " \t", TRUE);
+	if (strv) {
+		for (i = 0; strv[i]; i++) {
+			if (!s_match)
+				s_match = (NMSettingMatch *) nm_setting_match_new ();
+			nm_setting_match_add_interface_name (s_match,
+			                                     _nm_utils_unescape_spaces ((char *) strv[i]));
+		}
+	}
+
+	return (NMSetting *) s_match;
+}
+
+static NMSetting *
 make_proxy_setting (shvarFile *ifcfg)
 {
 	NMSettingProxy *s_proxy = NULL;
@@ -1286,7 +1526,7 @@ make_ip4_setting (shvarFile *ifcfg,
 	shvarFile *route_ifcfg;
 	gboolean never_default;
 	gint64 timeout;
-	gint priority;
+	int priority;
 	char inet_buf[NM_UTILS_INET_ADDRSTRLEN];
 	const char *const *item;
 	guint32 route_table;
@@ -1483,7 +1723,7 @@ make_ip4_setting (shvarFile *ifcfg,
 		if (v) {
 			gs_free const char **searches = NULL;
 
-			searches = nm_utils_strsplit_set (v, " ");
+			searches = nm_utils_strsplit_set (v, " ", FALSE);
 			if (searches) {
 				for (item = searches; *item; item++) {
 					if (!nm_setting_ip_config_add_dns_search (s_ip4, *item))
@@ -1544,7 +1784,7 @@ make_ip4_setting (shvarFile *ifcfg,
 		if (v) {
 			gs_free const char **searches = NULL;
 
-			searches = nm_utils_strsplit_set (v, " ");
+			searches = nm_utils_strsplit_set (v, " ", FALSE);
 			if (searches) {
 				for (item = searches; *item; item++) {
 					if (!nm_setting_ip_config_add_dns_search (s_ip4, *item))
@@ -1561,7 +1801,7 @@ make_ip4_setting (shvarFile *ifcfg,
 		if (timeout > 0)
 			timeout *= 1000;
 	}
-	g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT, (gint) timeout, NULL);
+	g_object_set (s_ip4, NM_SETTING_IP_CONFIG_DAD_TIMEOUT, (int) timeout, NULL);
 
 	return g_steal_pointer (&s_ip4);
 }
@@ -1697,7 +1937,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	guint32 i;
 	int i_val;
 	GError *local = NULL;
-	gint priority;
+	int priority;
 	gboolean never_default = FALSE;
 	gboolean ip6_privacy = FALSE, ip6_privacy_prefer_public_ip;
 	NMSettingIP6ConfigPrivacy ip6_privacy_val;
@@ -1860,7 +2100,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	                   ipv6addr_secondaries ?: "",
 	                   NULL);
 
-	list = nm_utils_strsplit_set (value, " ");
+	list = nm_utils_strsplit_set (value, " ", FALSE);
 	for (iter = list, i = 0; iter && *iter; iter++, i++) {
 		NMIPAddress *addr = NULL;
 
@@ -1953,7 +2193,7 @@ make_ip6_setting (shvarFile *ifcfg,
 	if (v) {
 		gs_free const char **searches = NULL;
 
-		searches = nm_utils_strsplit_set (v, " ");
+		searches = nm_utils_strsplit_set (v, " ", FALSE);
 		if (searches) {
 			for (iter = searches; *iter; iter++) {
 				if (!nm_setting_ip_config_add_dns_search (s_ip6, *iter))
@@ -1982,6 +2222,68 @@ error:
 }
 
 static NMSetting *
+make_sriov_setting (shvarFile *ifcfg)
+{
+	gs_unref_hashtable GHashTable *keys = NULL;
+	gs_unref_ptrarray GPtrArray *vfs = NULL;
+	NMTernary autoprobe_drivers;
+	NMSettingSriov *s_sriov;
+	int total_vfs;
+
+	total_vfs = svGetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", 10, 0, G_MAXINT32, 0);
+	if (!total_vfs)
+		return NULL;
+
+	autoprobe_drivers = svGetValueInt64 (ifcfg,
+	                                     "SRIOV_AUTOPROBE_DRIVERS",
+	                                     10,
+	                                     NM_TERNARY_FALSE,
+	                                     NM_TERNARY_TRUE,
+	                                     NM_TERNARY_DEFAULT);
+
+	keys = svGetKeys (ifcfg, SV_KEY_TYPE_SRIOV_VF);
+	if (keys) {
+		GHashTableIter iter;
+		const char *key;
+
+		g_hash_table_iter_init (&iter, keys);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL)) {
+			gs_free_error GError *error = NULL;
+			gs_free char *value_to_free = NULL;
+			const char *value;
+			NMSriovVF *vf;
+
+			nm_assert (g_str_has_prefix (key, "SRIOV_VF"));
+
+			value = svGetValue (ifcfg, key, &value_to_free);
+			if (!value)
+				continue;
+
+			key += NM_STRLEN ("SRIOV_VF");
+
+			vf = _nm_utils_sriov_vf_from_strparts (key, value, &error);
+			if (!vf) {
+				PARSE_WARNING ("ignoring invalid SR-IOV VF '%s %s': %s",
+				               key, value, error->message);
+				continue;
+			}
+			if (!vfs)
+				vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
+			g_ptr_array_add (vfs, vf);
+		}
+	}
+
+	s_sriov = (NMSettingSriov *) nm_setting_sriov_new ();
+	g_object_set (s_sriov,
+	              NM_SETTING_SRIOV_TOTAL_VFS, total_vfs,
+	              NM_SETTING_SRIOV_VFS, vfs,
+	              NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, (int) autoprobe_drivers,
+	              NULL);
+
+	return (NMSetting *) s_sriov;
+}
+
+static NMSetting *
 make_tc_setting (shvarFile *ifcfg)
 {
 	NMSettingTCConfig *s_tc = NULL;
@@ -2241,7 +2543,7 @@ read_dcb_percent_array (shvarFile *ifcfg,
 		return TRUE;
 	}
 
-	split = nm_utils_strsplit_set (val, ",");
+	split = nm_utils_strsplit_set (val, ",", FALSE);
 	if (NM_PTRARRAY_LEN (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,
@@ -2518,30 +2820,6 @@ read_wep_keys (shvarFile *ifcfg,
 	return TRUE;
 }
 
-static NMSettingSecretFlags
-read_secret_flags (shvarFile *ifcfg, const char *flags_key)
-{
-	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
-	char *val;
-
-	g_return_val_if_fail (flags_key != NULL, NM_SETTING_SECRET_FLAG_NONE);
-	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 = svGetValueStr_cp (ifcfg, flags_key);
-	if (val) {
-		if (strstr (val, SECRET_FLAG_AGENT))
-			flags |= NM_SETTING_SECRET_FLAG_AGENT_OWNED;
-		if (strstr (val, SECRET_FLAG_NOT_SAVED))
-			flags |= NM_SETTING_SECRET_FLAG_NOT_SAVED;
-		if (strstr (val, SECRET_FLAG_NOT_REQUIRED))
-			flags |= NM_SETTING_SECRET_FLAG_NOT_REQUIRED;
-
-		g_free (val);
-	}
-	return flags;
-}
-
 static NMSetting *
 make_wep_setting (shvarFile *ifcfg,
                   const char *file,
@@ -2573,7 +2851,7 @@ make_wep_setting (shvarFile *ifcfg,
 	}
 
 	/* Read WEP key flags */
-	key_flags = read_secret_flags (ifcfg, "WEP_KEY_FLAGS");
+	key_flags = _secret_read_ifcfg_flags (ifcfg, "WEP_KEY_FLAGS");
 	g_object_set (s_wsec, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, key_flags, NULL);
 
 	/* Read keys in the ifcfg file if they are system-owned */
@@ -2677,7 +2955,7 @@ fill_wpa_ciphers (shvarFile *ifcfg,
 	if (!p)
 		return TRUE;
 
-	list = nm_utils_strsplit_set (p, " ");
+	list = nm_utils_strsplit_set (p, " ", FALSE);
 	for (iter = list; iter && *iter; iter++, i++) {
 		/* Ad-Hoc configurations cannot have pairwise ciphers, and can only
 		 * have one group cipher.  Ignore any additional group ciphers and
@@ -2772,254 +3050,118 @@ parse_wpa_psk (shvarFile *ifcfg,
 	return g_steal_pointer (&psk);
 }
 
-static void
-read_8021x_password (shvarFile *ifcfg, shvarFile *keys_ifcfg, const char *name,
-                     char **value, NMSettingSecretFlags *flags)
-{
-	gs_free char *flags_key = NULL;
-
-	*value = NULL;
-	flags_key = g_strdup_printf ("%s_FLAGS", name);
-	*flags = read_secret_flags (ifcfg, flags_key);
-
-	if (*flags == NM_SETTING_SECRET_FLAG_NONE) {
-		*value = svGetValueStr_cp (ifcfg, name);
-		if (!*value && keys_ifcfg)
-			*value = svGetValueStr_cp (keys_ifcfg, name);
-	}
-}
-
 static gboolean
 eap_simple_reader (const char *eap_method,
                    shvarFile *ifcfg,
-                   shvarFile *keys,
+                   shvarFile *keys_ifcfg,
                    NMSetting8021x *s_8021x,
                    gboolean phase2,
                    GError **error)
 {
 	NMSettingSecretFlags flags;
-	GBytes *bytes;
-	char *value;
+	gs_free char *identity_free = NULL;
+	nm_auto_free_secret char *password_raw_str = NULL;
+	gs_unref_bytes GBytes *password_raw_bytes = NULL;
 
-	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'.",
-		             eap_method);
-		return FALSE;
-	}
-	g_object_set (s_8021x, NM_SETTING_802_1X_IDENTITY, value, NULL);
-	nm_clear_g_free (&value);
+	g_object_set (s_8021x,
+	              NM_SETTING_802_1X_IDENTITY,
+	              svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free),
+	              NULL);
 
-	read_8021x_password (ifcfg, keys, "IEEE_8021X_PASSWORD", &value, &flags);
-	g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_FLAGS, flags, NULL);
-	if (value) {
-		g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD, value, NULL);
-		nm_clear_g_free (&value);
-	}
+	_secret_set_from_ifcfg (s_8021x,
+	                        ifcfg,
+	                        keys_ifcfg,
+	                        "IEEE_8021X_PASSWORD",
+	                        NM_SETTING_802_1X_PASSWORD);
+
+	_secret_read_ifcfg (ifcfg, keys_ifcfg, "IEEE_8021X_PASSWORD_RAW", &password_raw_str, &flags);
+	if (!_secret_password_raw_to_bytes ("IEEE_8021X_PASSWORD_RAW",
+	                                    password_raw_str,
+	                                    &password_raw_bytes,
+	                                    error))
+		return FALSE;
 
-	read_8021x_password (ifcfg, keys, "IEEE_8021X_PASSWORD_RAW", &value, &flags);
-	g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_RAW_FLAGS, flags, NULL);
-	if (value) {
-		bytes = nm_utils_hexstr2bin (value);
-		if (!bytes) {
-			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-			             "Invalid hex string '%s' in IEEE_8021X_PASSWORD_RAW.",
-			             value);
-			g_free (value);
-			return FALSE;
-		}
-		g_object_set (s_8021x, NM_SETTING_802_1X_PASSWORD_RAW, bytes, NULL);
-		g_bytes_unref (bytes);
-		nm_clear_g_free (&value);
-	}
+	g_object_set (s_8021x,
+	              NM_SETTING_802_1X_PASSWORD_RAW_FLAGS,
+	              flags,
+	              NM_SETTING_802_1X_PASSWORD_RAW,
+	              password_raw_bytes,
+	              NULL);
 
 	return TRUE;
 }
 
-static char *
-get_full_file_path (const char *ifcfg_path, const char *file_path)
-{
-	const char *base = file_path;
-	char *p, *ret, *dirname;
-
-	g_return_val_if_fail (ifcfg_path != NULL, NULL);
-	g_return_val_if_fail (file_path != NULL, NULL);
-
-	if (file_path[0] == '/')
-		return g_strdup (file_path);
-
-	p = strrchr (file_path, '/');
-	if (p)
-		base = p + 1;
-
-	dirname = g_path_get_dirname (ifcfg_path);
-	ret = g_build_path ("/", dirname, base, NULL);
-	g_free (dirname);
-	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,
-                shvarFile *keys,
+                shvarFile *keys_ifcfg,
                 NMSetting8021x *s_8021x,
                 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_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 *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 *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;
+	gs_unref_bytes GBytes *privkey = NULL;
+	gs_unref_bytes GBytes *client_cert = NULL;
+	gs_free char *identity_free = NULL;
 
-	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 = svGetValueStr_cp (ifcfg, ca_cert_key);
-	if (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_cert_value, scheme, NULL, error))
-				return FALSE;
-		} else {
-			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);
-			}
-		}
-	} else {
-		PARSE_WARNING ("missing %s for EAP method '%s'; this is insecure!",
-		               ca_cert_key, eap_method);
-	}
-
-	/* Read and set private key password flags */
-	flags = read_secret_flags (ifcfg, pk_pw_flags_key);
-	g_object_set (s_8021x, pk_pw_flags_prop, flags, NULL);
-
-	/* Read the private key password if it's system-owned */
-	if (flags == NM_SETTING_SECRET_FLAG_NONE) {
-		/* Private key password */
-		privkey_password = svGetValueStr_cp (ifcfg, pk_pw_key);
-		if (!privkey_password && keys) {
-			/* Try the lookaside keys file */
-			privkey_password = svGetValueStr_cp (keys, pk_pw_key);
-		}
-	}
+	g_object_set (s_8021x,
+	              NM_SETTING_802_1X_IDENTITY,
+	              svGetValueStr (ifcfg, "IEEE_8021X_IDENTITY", &identity_free),
+	              NULL);
 
-	/* The private key itself */
-	privkey = svGetValueStr_cp (ifcfg, pk_key);
+	if (!_cert_set_from_ifcfg (s_8021x,
+	                           ifcfg,
+	                           phase2 ? "IEEE_8021X_INNER_CA_CERT" : "IEEE_8021X_CA_CERT",
+	                           phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT : NM_SETTING_802_1X_CA_CERT,
+	                           NULL,
+	                           error))
+		return FALSE;
+	_secret_set_from_ifcfg (s_8021x,
+	                        ifcfg,
+	                        keys_ifcfg,
+	                        phase2 ? "IEEE_8021X_INNER_CA_CERT_PASSWORD" : "IEEE_8021X_CA_CERT_PASSWORD",
+	                        phase2 ? NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD : NM_SETTING_802_1X_CA_CERT_PASSWORD);
+
+	if (!_cert_set_from_ifcfg (s_8021x,
+	                           ifcfg,
+	                           phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY",
+	                           phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY : NM_SETTING_802_1X_PRIVATE_KEY,
+	                           &privkey,
+	                           error))
+		return FALSE;
+	_secret_set_from_ifcfg (s_8021x,
+	                        ifcfg,
+	                        keys_ifcfg,
+	                        phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD" : "IEEE_8021X_PRIVATE_KEY_PASSWORD",
+	                        phase2 ? NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD : NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
 	if (!privkey) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Missing %s for EAP method '%s'.",
-		             pk_key,
+		             phase2 ? "IEEE_8021X_INNER_PRIVATE_KEY" : "IEEE_8021X_PRIVATE_KEY",
 		             eap_method);
 		return FALSE;
 	}
 
-	{
-		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;
-		}
-	}
-
-	/* 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,
-	 * then nm_setting_802_1x_set_private_key() already set the client certificate
-	 * to the same value as the private key.
-	 */
-	if (privkey_format != NM_SETTING_802_1X_CK_FORMAT_PKCS12) {
-		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);
-			return FALSE;
-		}
-
-		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_cert_value, scheme, NULL, error))
-				return FALSE;
-		} else {
-			if (!nm_setting_802_1x_set_client_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, cli_cert_pw_flags_key);
-			g_object_set (s_8021x, cli_cert_pw_flags_prop, flags, NULL);
-
-			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);
-			}
-		}
+	if (!_cert_set_from_ifcfg (s_8021x,
+	                           ifcfg,
+	                           phase2 ? "IEEE_8021X_INNER_CLIENT_CERT" : "IEEE_8021X_CLIENT_CERT",
+	                           phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT : NM_SETTING_802_1X_CLIENT_CERT,
+	                           &client_cert,
+	                           error))
+		return FALSE;
+	/* FIXME: writer does not actually write IEEE_8021X_CLIENT_CERT_PASSWORD and other
+	 * certificate related passwords. It should, because otherwise persisting such profiles
+	 * to ifcfg looses information. As this currently only matters for PKCS11 URIs, it seems
+	 * a seldomly used feature so that it is not fixed yet. */
+	_secret_set_from_ifcfg (s_8021x,
+	                        ifcfg,
+	                        keys_ifcfg,
+	                        phase2 ? "IEEE_8021X_INNER_CLIENT_CERT_PASSWORD" : "IEEE_8021X_CLIENT_CERT_PASSWORD",
+	                        phase2 ? NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD : NM_SETTING_802_1X_CLIENT_CERT_PASSWORD);
+	if (!client_cert) {
+		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+		             "Missing certificate for EAP method '%s'.",
+		             eap_method);
+		return FALSE;
 	}
 
 	return TRUE;
@@ -3028,7 +3170,7 @@ eap_tls_reader (const char *eap_method,
 static gboolean
 eap_peap_reader (const char *eap_method,
                  shvarFile *ifcfg,
-                 shvarFile *keys,
+                 shvarFile *keys_ifcfg,
                  NMSetting8021x *s_8021x,
                  gboolean phase2,
                  GError **error)
@@ -3037,19 +3179,19 @@ eap_peap_reader (const char *eap_method,
 	const char *v;
 	gs_free const char **list = NULL;
 	const char *const *iter;
-	NMSetting8021xCKScheme scheme;
-
-	v = svGetValueStr (ifcfg, "IEEE_8021X_CA_CERT", &value);
-	if (v) {
-		gs_free char *real_cert_value = NULL;
 
-		real_cert_value = get_cert_value (svFileGetName (ifcfg), v, &scheme);
-		if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
-			return FALSE;
-	} else {
-		PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
-		               eap_method);
-	}
+	if (!_cert_set_from_ifcfg (s_8021x,
+	                           ifcfg,
+	                           "IEEE_8021X_CA_CERT",
+	                           NM_SETTING_802_1X_CA_CERT,
+	                           NULL,
+	                           error))
+		return FALSE;
+	_secret_set_from_ifcfg (s_8021x,
+	                        ifcfg,
+	                        keys_ifcfg,
+	                        "IEEE_8021X_CA_CERT_PASSWORD",
+	                        NM_SETTING_802_1X_CA_CERT_PASSWORD);
 
 	nm_clear_g_free (&value);
 	v = svGetValueStr (ifcfg, "IEEE_8021X_PEAP_VERSION", &value);
@@ -3083,16 +3225,16 @@ eap_peap_reader (const char *eap_method,
 	}
 
 	/* Handle options for the inner auth method */
-	list = nm_utils_strsplit_set (v, " ");
+	list = nm_utils_strsplit_set (v, " ", FALSE);
 	iter = list;
 	if (iter) {
 		if (NM_IN_STRSET (*iter, "MSCHAPV2",
 		                         "MD5",
 		                         "GTC")) {
-			if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
+			if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
 				return FALSE;
 		} else if (nm_streq (*iter, "TLS")) {
-			if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
+			if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
 				return FALSE;
 		} else {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -3121,7 +3263,7 @@ eap_peap_reader (const char *eap_method,
 static gboolean
 eap_ttls_reader (const char *eap_method,
                  shvarFile *ifcfg,
-                 shvarFile *keys,
+                 shvarFile *keys_ifcfg,
                  NMSetting8021x *s_8021x,
                  gboolean phase2,
                  GError **error)
@@ -3131,19 +3273,19 @@ eap_ttls_reader (const char *eap_method,
 	const char *v;
 	gs_free const char **list = NULL;
 	const char *const *iter;
-	NMSetting8021xCKScheme scheme;
-
-	v = svGetValueStr (ifcfg, "IEEE_8021X_CA_CERT", &value);
-	if (v) {
-		gs_free char *real_cert_value = NULL;
 
-		real_cert_value = get_cert_value (svFileGetName (ifcfg), v, &scheme);
-		if (!nm_setting_802_1x_set_ca_cert (s_8021x, real_cert_value, scheme, NULL, error))
-			return FALSE;
-	} else {
-		PARSE_WARNING ("missing IEEE_8021X_CA_CERT for EAP method '%s'; this is insecure!",
-		               eap_method);
-	}
+	if (!_cert_set_from_ifcfg (s_8021x,
+	                           ifcfg,
+	                           "IEEE_8021X_CA_CERT",
+	                           NM_SETTING_802_1X_CA_CERT,
+	                           NULL,
+	                           error))
+		return FALSE;
+	_secret_set_from_ifcfg (s_8021x,
+	                        ifcfg,
+	                        keys_ifcfg,
+	                        "IEEE_8021X_CA_CERT_PASSWORD",
+	                        NM_SETTING_802_1X_CA_CERT_PASSWORD);
 
 	nm_clear_g_free (&value);
 	v = svGetValueStr (ifcfg, "IEEE_8021X_ANON_IDENTITY", &value);
@@ -3161,24 +3303,24 @@ eap_ttls_reader (const char *eap_method,
 	inner_auth = g_ascii_strdown (v, -1);
 
 	/* Handle options for the inner auth method */
-	list = nm_utils_strsplit_set (inner_auth, " ");
+	list = nm_utils_strsplit_set (inner_auth, " ", FALSE);
 	iter = list;
 	if (iter) {
 		if (NM_IN_STRSET (*iter, "mschapv2",
 		                         "mschap",
 		                         "pap",
 		                         "chap")) {
-			if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
+			if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
 				return FALSE;
 			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTH, *iter, NULL);
 		} else if (nm_streq (*iter, "eap-tls")) {
-			if (!eap_tls_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
+			if (!eap_tls_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
 				return FALSE;
 			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, "tls", NULL);
 		} else if (NM_IN_STRSET (*iter, "eap-mschapv2",
 		                                "eap-md5",
 		                                "eap-gtc")) {
-			if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
+			if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
 				return FALSE;
 			g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_AUTHEAP, (*iter + NM_STRLEN ("eap-")), NULL);
 		} else {
@@ -3195,7 +3337,7 @@ eap_ttls_reader (const char *eap_method,
 static gboolean
 eap_fast_reader (const char *eap_method,
                  shvarFile *ifcfg,
-                 shvarFile *keys,
+                 shvarFile *keys_ifcfg,
                  NMSetting8021x *s_8021x,
                  gboolean phase2,
                  GError **error)
@@ -3222,7 +3364,7 @@ eap_fast_reader (const char *eap_method,
 	if (fast_provisioning) {
 		gs_free const char **list1 = NULL;
 
-		list1 = nm_utils_strsplit_set (fast_provisioning, " \t");
+		list1 = nm_utils_strsplit_set (fast_provisioning, " \t", FALSE);
 		for (iter = list1; iter && *iter; iter++) {
 			if (strcmp (*iter, "allow-unauth") == 0)
 				allow_unauth = TRUE;
@@ -3256,12 +3398,12 @@ eap_fast_reader (const char *eap_method,
 	}
 
 	/* Handle options for the inner auth method */
-	list = nm_utils_strsplit_set (inner_auth, " ");
+	list = nm_utils_strsplit_set (inner_auth, " ", FALSE);
 	iter = list;
 	if (iter) {
 		if (   !strcmp (*iter, "MSCHAPV2")
 		    || !strcmp (*iter, "GTC")) {
-			if (!eap_simple_reader (*iter, ifcfg, keys, s_8021x, TRUE, error))
+			if (!eap_simple_reader (*iter, ifcfg, keys_ifcfg, s_8021x, TRUE, error))
 				goto done;
 		} else {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -3296,7 +3438,7 @@ typedef struct {
 	const char *method;
 	gboolean (*reader) (const char *eap_method,
 	                    shvarFile *ifcfg,
-	                    shvarFile *keys,
+	                    shvarFile *keys_ifcfg,
 	                    NMSetting8021x *s_8021x,
 	                    gboolean phase2,
 	                    GError **error);
@@ -3336,7 +3478,7 @@ read_8021x_list_value (shvarFile *ifcfg,
 	if (!v)
 		return;
 
-	strv = nm_utils_strsplit_set (v, " \t");
+	strv = nm_utils_strsplit_set (v, " \t", FALSE);
 	if (strv)
 		g_object_set (setting, prop_name, strv, NULL);
 }
@@ -3348,7 +3490,7 @@ fill_8021x (shvarFile *ifcfg,
             gboolean wifi,
             GError **error)
 {
-	nm_auto_shvar_file_close shvarFile *keys = NULL;
+	nm_auto_shvar_file_close shvarFile *keys_ifcfg = NULL;
 	gs_unref_object NMSetting8021x *s_8021x = NULL;
 	gs_free char *value = NULL;
 	const char *v;
@@ -3365,12 +3507,12 @@ fill_8021x (shvarFile *ifcfg,
 		return NULL;
 	}
 
-	list = nm_utils_strsplit_set (v, " ");
+	list = nm_utils_strsplit_set (v, " ", FALSE);
 
 	s_8021x = (NMSetting8021x *) nm_setting_802_1x_new ();
 
-	/* Read in the lookaside keys file, if present */
-	keys = utils_get_keys_ifcfg (file, FALSE);
+	/* Read in the lookaside keys_ifcfg file, if present */
+	keys_ifcfg = utils_get_keys_ifcfg (file, FALSE);
 
 	/* Validate and handle each EAP method */
 	for (iter = list; iter && *iter; iter++) {
@@ -3394,7 +3536,7 @@ fill_8021x (shvarFile *ifcfg,
 			}
 
 			/* Parse EAP method specific options */
-			if (!(*eap->reader)(lower, ifcfg, keys, s_8021x, FALSE, error))
+			if (!(*eap->reader)(lower, ifcfg, keys_ifcfg, s_8021x, FALSE, error))
 				return NULL;
 
 			nm_setting_802_1x_add_eap_method (s_8021x, lower);
@@ -3444,7 +3586,7 @@ next:
 	g_object_set (s_8021x, NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH, v, NULL);
 
 	timeout = svGetValueInt64 (ifcfg, "IEEE_8021X_AUTH_TIMEOUT", 10, 0, G_MAXINT32, 0);
-	g_object_set (s_8021x, NM_SETTING_802_1X_AUTH_TIMEOUT, (gint) timeout, NULL);
+	g_object_set (s_8021x, NM_SETTING_802_1X_AUTH_TIMEOUT, (int) timeout, NULL);
 
 	return g_steal_pointer (&s_8021x);
 }
@@ -3510,7 +3652,7 @@ make_wpa_setting (shvarFile *ifcfg,
 	if (wpa_psk) {
 		NMSettingSecretFlags psk_flags;
 
-		psk_flags = read_secret_flags (ifcfg, "WPA_PSK_FLAGS");
+		psk_flags = _secret_read_ifcfg_flags (ifcfg, "WPA_PSK_FLAGS");
 		g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, psk_flags, NULL);
 
 		/* Read PSK if it's system-owned */
@@ -3597,7 +3739,7 @@ make_leap_setting (shvarFile *ifcfg,
 		return NULL; /* Not LEAP */
 	nm_clear_g_free (&value);
 
-	flags = read_secret_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS");
+	flags = _secret_read_ifcfg_flags (ifcfg, "IEEE_8021X_PASSWORD_FLAGS");
 	g_object_set (wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, flags, NULL);
 
 	/* Read LEAP password if it's system-owned */
@@ -3674,7 +3816,7 @@ transform_hwaddr_blacklist (const char *blacklist)
 	const char **strv;
 	gsize i, j;
 
-	strv = nm_utils_strsplit_set (blacklist, " \t");
+	strv = nm_utils_strsplit_set (blacklist, " \t", FALSE);
 	if (!strv)
 		return NULL;
 	for (i = 0, j = 0; strv[j]; j++) {
@@ -3923,7 +4065,7 @@ wireless_connection_from_ifcfg (const char *file,
 	NMSetting8021x *s_8021x = NULL;
 	GBytes *ssid;
 	NMSetting *security_setting = NULL;
-	char *printable_ssid = NULL;
+	gs_free char *ssid_utf8 = NULL;
 	const char *mode;
 	gboolean adhoc = FALSE;
 	GError *local = NULL;
@@ -3943,12 +4085,6 @@ wireless_connection_from_ifcfg (const char *file,
 	nm_connection_add_setting (connection, wireless_setting);
 
 	ssid = nm_setting_wireless_get_ssid (NM_SETTING_WIRELESS (wireless_setting));
-	if (ssid) {
-		printable_ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
-		                                        g_bytes_get_size (ssid));
-	} else
-		printable_ssid = g_strdup ("unmanaged");
-
 	mode = nm_setting_wireless_get_mode (NM_SETTING_WIRELESS (wireless_setting));
 	if (mode && !strcmp (mode, "adhoc"))
 		adhoc = TRUE;
@@ -3956,7 +4092,6 @@ wireless_connection_from_ifcfg (const char *file,
 	/* Wireless security */
 	security_setting = make_wireless_security_setting (ifcfg, file, ssid, adhoc, &s_8021x, &local);
 	if (local) {
-		g_free (printable_ssid);
 		g_object_unref (connection);
 		g_propagate_error (error, local);
 		return NULL;
@@ -3967,11 +4102,16 @@ wireless_connection_from_ifcfg (const char *file,
 			nm_connection_add_setting (connection, NM_SETTING (s_8021x));
 	}
 
+	if (ssid)
+		ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid);
+
 	/* Connection */
-	con_setting = make_connection_setting (file, ifcfg,
+	con_setting = make_connection_setting (file,
+	                                       ifcfg,
 	                                       NM_SETTING_WIRELESS_SETTING_NAME,
-	                                       printable_ssid, NULL);
-	g_free (printable_ssid);
+	                                       nm_str_not_empty (ssid_utf8) ?: "unmanaged",
+	                                       NULL);
+
 	if (!con_setting) {
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 		             "Failed to create connection setting.");
@@ -3984,199 +4124,253 @@ wireless_connection_from_ifcfg (const char *file,
 }
 
 static void
-parse_ethtool_option_autoneg (const char *value, gboolean *out_autoneg)
+parse_ethtool_option (const char *value,
+                      NMSettingWiredWakeOnLan *out_flags,
+                      char **out_password,
+                      gboolean *out_autoneg,
+                      guint32 *out_speed,
+                      const char **out_duplex,
+                      NMSettingEthtool **out_s_ethtool)
 {
-	if (!value) {
-		PARSE_WARNING ("Auto-negotiation option missing");
+	gs_free const char **words = NULL;
+	guint i;
+
+	words = nm_utils_strsplit_set (value, NULL, FALSE);
+	if (!words)
 		return;
-	}
 
-	if (g_str_equal (value, "off"))
-		*out_autoneg = FALSE;
-	else if (g_str_equal (value, "on"))
-		*out_autoneg = TRUE;
-	else
-		PARSE_WARNING ("Auto-negotiation unknown value: %s", value);
-}
+	if (words[0] && words[0][0] == '-') {
+		/* /sbin/ethtool $opts */
+		if (NM_IN_STRSET (words[0], "-K", "--features", "--offload")) {
+			if (!words[1]) {
+				/* first argument must be the interface name. This is invalid. */
+				return;
+			}
 
-static void
-parse_ethtool_option_speed (const char *value, guint32 *out_speed)
-{
-	if (!value) {
-		PARSE_WARNING ("Speed option missing");
-		return;
-	}
+			if (!*out_s_ethtool)
+				*out_s_ethtool = NM_SETTING_ETHTOOL (nm_setting_ethtool_new ());
 
-	*out_speed =  _nm_utils_ascii_str_to_int64 (value, 10, 0, G_MAXUINT32, 0);
-	if (errno)
-		PARSE_WARNING ("Speed value '%s' is invalid", value);
-}
+			for (i = 2; words[i]; ) {
+				const char *opt = words[i];
+				const char *opt_val = words[++i];
+				const NMEthtoolData *d = NULL;
+				NMTernary onoff = NM_TERNARY_DEFAULT;
 
-static void
-parse_ethtool_option_duplex (const char *value, const char **out_duplex)
-{
-	if (!value) {
-		PARSE_WARNING ("Duplex option missing");
-		return;
-	}
+				if (nm_streq0 (opt_val, "on"))
+					onoff = NM_TERNARY_TRUE;
+				else if (nm_streq0 (opt_val, "off"))
+					onoff = NM_TERNARY_FALSE;
 
-	if (g_str_equal (value, "half"))
-		*out_duplex = "half";
-	else if (g_str_equal (value, "full"))
-		*out_duplex = "full";
-	else
-		PARSE_WARNING ("Duplex unknown value: %s", value);
+				d = nms_ifcfg_rh_utils_get_ethtool_by_name (opt);
 
-}
+				if (!d) {
+					if (onoff != NM_TERNARY_DEFAULT) {
+						/* the next value is just the on/off argument. Skip it too. */
+						i++;
+					}
 
-static void
-parse_ethtool_option_wol (const char *value, NMSettingWiredWakeOnLan *out_flags)
-{
-	NMSettingWiredWakeOnLan wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_NONE;
+					/* silently ignore unsupported offloading features. */
+					continue;
+				}
 
-	if (!value) {
-		PARSE_WARNING ("Wake-on-LAN options missing");
+				i++;
+
+				if (onoff == NM_TERNARY_DEFAULT) {
+					PARSE_WARNING ("Expects on/off argument for feature '%s'", opt);
+					continue;
+				}
+
+				nm_setting_ethtool_set_feature (*out_s_ethtool,
+				                                d->optname,
+				                                onoff);
+			}
+		}
 		return;
 	}
 
-	for (; *value; value++) {
-		switch (*value) {
-		case 'p':
-			wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_PHY;
-			break;
-		case 'u':
-			wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST;
-			break;
-		case 'm':
-			wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST;
-			break;
-		case 'b':
-			wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST;
-			break;
-		case 'a':
-			wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_ARP;
-			break;
-		case 'g':
-			wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC;
-			break;
-		case 's':
-			break;
-		case 'd':
-			wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_NONE;
-			break;
-		default:
-			PARSE_WARNING ("unrecognized Wake-on-LAN option '%c'", *value);
+	/* /sbin/ethtool -s ${REALDEVICE} $opts */
+	for (i = 0; words[i]; ) {
+		const char *opt = words[i];
+		const char *opt_val = words[++i];
+
+		if (nm_streq (opt, "autoneg")) {
+			if (!opt_val) {
+				PARSE_WARNING ("Auto-negotiation option missing");
+				break;
+			}
+			i++;
+
+			if (nm_streq (opt_val, "off"))
+				*out_autoneg = FALSE;
+			else if (nm_streq (opt_val, "on"))
+				*out_autoneg = TRUE;
+			else
+				PARSE_WARNING ("Auto-negotiation unknown value: %s", opt_val);
+			continue;
 		}
-	}
 
-	*out_flags = wol_flags;
-}
+		if (nm_streq (opt, "speed")) {
+			guint32 speed;
 
-static void parse_ethtool_option_sopass (const char *value, char **out_password)
-{
-	if (!value) {
-		PARSE_WARNING ("Wake-on-LAN password missing");
-		return;
-	}
+			if (!opt_val) {
+				PARSE_WARNING ("Speed option missing");
+				break;
+			}
+			i++;
 
-	g_clear_pointer (out_password, g_free);
-	if (!nm_utils_hwaddr_valid (value, ETH_ALEN)) {
-		PARSE_WARNING ("Wake-on-LAN password '%s' is invalid", value);
-		return;
-	}
+			speed =  _nm_utils_ascii_str_to_int64 (opt_val, 10, 0, G_MAXUINT32, 0);
+			if (errno == 0)
+				*out_speed = speed;
+			else
+				PARSE_WARNING ("Speed value '%s' is invalid", opt_val);
+			continue;
+		}
 
-	*out_password = g_strdup (value);
-}
+		if (nm_streq (opt, "duplex")) {
+			if (!opt_val) {
+				PARSE_WARNING ("Duplex option missing");
+				break;
+			}
+			i++;
 
-static void
-parse_ethtool_option (const char *value,
-                      NMSettingWiredWakeOnLan *out_flags,
-                      char **out_password,
-                      gboolean *out_autoneg,
-                      guint32 *out_speed,
-                      const char **out_duplex)
-{
-	gs_free const char **words = NULL;
-	const char *const *iter;
-	const char *opt_val, *opt;
+			if (nm_streq (opt_val, "half"))
+				*out_duplex = "half";
+			else if (nm_streq (opt_val, "full"))
+				*out_duplex = "full";
+			else
+				PARSE_WARNING ("Duplex unknown value: %s", opt_val);
+			continue;
+		}
 
-	words = nm_utils_strsplit_set (value, "\t ");
-	if (!words)
-		return;
+		if (nm_streq (opt, "wol")) {
+			NMSettingWiredWakeOnLan wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_NONE;
 
-	iter = words;
-
-	while (iter[0]) {
-		opt = iter++[0];
-		opt_val = iter[0];
-
-		if (nm_streq (opt, "autoneg"))
-			parse_ethtool_option_autoneg (opt_val, out_autoneg);
-		else if (nm_streq (opt, "speed"))
-			parse_ethtool_option_speed (opt_val, out_speed);
-		else if (nm_streq (opt, "duplex"))
-			parse_ethtool_option_duplex (opt_val, out_duplex);
-		else if (nm_streq (opt, "wol"))
-			parse_ethtool_option_wol (opt_val, out_flags);
-		else if (nm_streq (opt, "sopass"))
-			parse_ethtool_option_sopass (opt_val, out_password);
-		else {
-			/* Silently skip unknown options */
+			if (!opt_val) {
+				PARSE_WARNING ("Wake-on-LAN options missing");
+				break;
+			}
+			i++;
+
+			for (; *opt_val; opt_val++) {
+				switch (*opt_val) {
+				case 'p':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_PHY;
+					break;
+				case 'u':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST;
+					break;
+				case 'm':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST;
+					break;
+				case 'b':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST;
+					break;
+				case 'a':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_ARP;
+					break;
+				case 'g':
+					wol_flags |= NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC;
+					break;
+				case 's':
+					break;
+				case 'd':
+					wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_NONE;
+					break;
+				default:
+					PARSE_WARNING ("unrecognized Wake-on-LAN option '%c'", *opt_val);
+				}
+			}
+
+			*out_flags = wol_flags;
 			continue;
 		}
 
-		if (iter[0])
-			iter++;
+		if (nm_streq (opt, "sopass")) {
+			if (!opt_val) {
+				PARSE_WARNING ("Wake-on-LAN password missing");
+				break;
+			}
+			i++;
+
+			if (nm_utils_hwaddr_valid (opt_val, ETH_ALEN)) {
+				g_clear_pointer (out_password, g_free);
+				*out_password = g_strdup (opt_val);
+			} else
+				PARSE_WARNING ("Wake-on-LAN password '%s' is invalid", opt_val);
+			continue;
+		}
+
+		/* Silently skip unknown options */
 	}
 }
 
 static void
-parse_ethtool_options (shvarFile *ifcfg, NMSettingWired *s_wired, const char *value)
+parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection)
 {
+	NMSettingWired *s_wired;
+	gs_unref_object NMSettingEthtool *s_ethtool = NULL;
 	NMSettingWiredWakeOnLan wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT;
-	gs_free char *wol_password = NULL, *wol_value = NULL;
-	gboolean ignore_wol_password = FALSE, autoneg = FALSE;
+	gs_free char *ethtool_opts_free = NULL;
+	const char *ethtool_opts;
+	gs_free char *wol_password = NULL;
+	gs_free char *wol_value_free = NULL;
+	const char *tmp;
+	gboolean autoneg = FALSE;
 	guint32 speed = 0;
 	const char *duplex = NULL;
 
-	if (value) {
-		gs_free const char **opts = NULL;
-		const char *const *iter;
-
+	ethtool_opts = svGetValue (ifcfg, "ETHTOOL_OPTS", &ethtool_opts_free);
+	if (ethtool_opts) {
 		/* WAKE_ON_LAN_IGNORE is inferred from a specified but empty ETHTOOL_OPTS */
-		if (!value[0])
+		if (!ethtool_opts[0])
 			wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE;
-
-		opts = nm_utils_strsplit_set (value, ";");
-		for (iter = opts; iter && iter[0]; iter++) {
-			/* in case of repeated wol_passwords, parse_ethtool_option()
-			 * will do the right thing and clear wol_password before resetting. */
-			parse_ethtool_option (iter[0], &wol_flags, &wol_password, &autoneg, &speed, &duplex);
+		else {
+			gs_free const char **opts = NULL;
+			const char *const *iter;
+
+			opts = nm_utils_strsplit_set (ethtool_opts, ";", FALSE);
+			for (iter = opts; iter && iter[0]; iter++) {
+				/* in case of repeated wol_passwords, parse_ethtool_option()
+				 * will do the right thing and clear wol_password before resetting. */
+				parse_ethtool_option (iter[0],
+				                      &wol_flags,
+				                      &wol_password,
+				                      &autoneg,
+				                      &speed,
+				                      &duplex,
+				                      &s_ethtool);
+			}
 		}
 	}
 
 	/* ETHTOOL_WAKE_ON_LAN = ignore overrides WoL settings in ETHTOOL_OPTS */
-	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;
-		else
-			PARSE_WARNING ("invalid ETHTOOL_WAKE_ON_LAN value '%s'", wol_value);
-	}
+	tmp = svGetValueStr (ifcfg, "ETHTOOL_WAKE_ON_LAN", &wol_value_free);
+	if (nm_streq0 (tmp, "ignore"))
+		wol_flags = NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE;
+	else if (tmp)
+		PARSE_WARNING ("invalid ETHTOOL_WAKE_ON_LAN value '%s'", tmp);
 
 	if (   wol_password
 	    && !NM_FLAGS_HAS (wol_flags, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC)) {
 		PARSE_WARNING ("Wake-on-LAN password not expected");
-		ignore_wol_password = TRUE;
+		nm_clear_g_free (&wol_password);
 	}
 
-	g_object_set (s_wired,
-	              NM_SETTING_WIRED_WAKE_ON_LAN, wol_flags,
-	              NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD, ignore_wol_password ? NULL : wol_password,
-	              NM_SETTING_WIRED_AUTO_NEGOTIATE, autoneg,
-	              NM_SETTING_WIRED_SPEED, speed,
-	              NM_SETTING_WIRED_DUPLEX, duplex,
-	              NULL);
+	s_wired = nm_connection_get_setting_wired (connection);
+	if (s_wired) {
+		g_object_set (s_wired,
+		              NM_SETTING_WIRED_WAKE_ON_LAN, wol_flags,
+		              NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD, wol_password,
+		              NM_SETTING_WIRED_AUTO_NEGOTIATE, autoneg,
+		              NM_SETTING_WIRED_SPEED, speed,
+		              NM_SETTING_WIRED_DUPLEX, duplex,
+		              NULL);
+	}
+
+	if (s_ethtool) {
+		nm_connection_add_setting (connection,
+		                           NM_SETTING (g_steal_pointer (&s_ethtool)));
+	}
 }
 
 static NMSetting *
@@ -4230,7 +4424,7 @@ make_wired_setting (shvarFile *ifcfg,
 			gs_free const char **chans = NULL;
 			guint32 num_chans;
 
-			chans = nm_utils_strsplit_set (value, ",");
+			chans = nm_utils_strsplit_set (value, ",", FALSE);
 			num_chans = NM_PTRARRAY_LEN (chans);
 			if (num_chans < 2 || num_chans > 3) {
 				PARSE_WARNING ("invalid SUBCHANNELS '%s' (%u channels, 2 or 3 expected)",
@@ -4318,10 +4512,6 @@ make_wired_setting (shvarFile *ifcfg,
 		nm_clear_g_free (&value);
 	}
 
-	parse_ethtool_options (ifcfg, s_wired,
-	                       svGetValue (ifcfg, "ETHTOOL_OPTS", &value));
-	nm_clear_g_free (&value);
-
 	return (NMSetting *) g_steal_pointer (&s_wired);
 }
 
@@ -4557,7 +4747,7 @@ make_bond_setting (shvarFile *ifcfg,
 		gs_free const char **items = NULL;
 		const char *const *iter;
 
-		items = nm_utils_strsplit_set (v, " ");
+		items = nm_utils_strsplit_set (v, " ", FALSE);
 		for (iter = items; iter && *iter; iter++) {
 			gs_strfreev char **keys = NULL;
 			const char *key, *val;
@@ -4835,7 +5025,7 @@ handle_bridging_opts (NMSetting *setting,
 	gs_free const char **items = NULL;
 	const char *const *iter;
 
-	items = nm_utils_strsplit_set (value, " ");
+	items = nm_utils_strsplit_set (value, " ", FALSE);
 	for (iter = items; iter && *iter; iter++) {
 		gs_strfreev char **keys = NULL;
 		const char *key, *val;
@@ -5055,7 +5245,7 @@ parse_prio_map_list (NMSettingVlan *s_vlan,
 	v = svGetValueStr (ifcfg, key, &value);
 	if (!v)
 		return;
-	list = nm_utils_strsplit_set (v, ",");
+	list = nm_utils_strsplit_set (v, ",", FALSE);
 
 	for (iter = list; iter && *iter; iter++) {
 		if (!strchr (*iter, ':'))
@@ -5077,7 +5267,7 @@ make_vlan_setting (shvarFile *ifcfg,
 	const char *v = NULL;
 	int vlan_id = -1;
 	guint32 vlan_flags = 0;
-	gint gvrp, reorder_hdr;
+	int gvrp, reorder_hdr;
 
 	v = svGetValueStr (ifcfg, "VLAN_ID", &value);
 	if (v) {
@@ -5160,7 +5350,7 @@ make_vlan_setting (shvarFile *ifcfg,
 		gs_free const char **strv = NULL;
 		const char *const *ptr;
 
-		strv = nm_utils_strsplit_set (v, ", ");
+		strv = nm_utils_strsplit_set (v, ", ", FALSE);
 		for (ptr = strv; ptr && *ptr; ptr++) {
 			if (nm_streq (*ptr, "GVRP") && gvrp == -1)
 				vlan_flags |= NM_VLAN_FLAG_GVRP;
@@ -5302,7 +5492,7 @@ check_dns_search_domains (shvarFile *ifcfg, NMSetting *s_ip4, NMSetting *s_ip6)
 			gs_free const char **searches = NULL;
 			const char *const *item;
 
-			searches = nm_utils_strsplit_set (v, " ");
+			searches = nm_utils_strsplit_set (v, " ", FALSE);
 			if (searches) {
 				for (item = searches; *item; item++) {
 					if (!nm_setting_ip_config_add_dns_search (NM_SETTING_IP_CONFIG (s_ip6), *item))
@@ -5321,12 +5511,13 @@ connection_from_file_full (const char *filename,
                            GError **error,
                            gboolean *out_ignore_error)
 {
-	nm_auto_shvar_file_close shvarFile *parsed = NULL;
+	nm_auto_shvar_file_close shvarFile *main_ifcfg = NULL;
 	nm_auto_shvar_file_close shvarFile *network_ifcfg = NULL;
 	gs_unref_object NMConnection *connection = NULL;
 	gs_free char *type = NULL;
 	char *devtype, *bootproto;
 	NMSetting *s_ip4, *s_ip6, *s_tc, *s_proxy, *s_port, *s_dcb = NULL, *s_user;
+	NMSetting *s_sriov, *s_match;
 	const char *ifcfg_name = NULL;
 	gboolean has_ip4_defroute = FALSE;
 	gboolean has_complex_routes_v4;
@@ -5348,14 +5539,14 @@ connection_from_file_full (const char *filename,
 		return NULL;
 	}
 
-	parsed = svOpenFile (filename, error);
-	if (!parsed)
+	main_ifcfg = svOpenFile (filename, error);
+	if (!main_ifcfg)
 		return NULL;
 
 	network_ifcfg = svOpenFile (network_file, NULL);
 
-	if (!svGetValueBoolean (parsed, "NM_CONTROLLED", TRUE)) {
-		connection = create_unhandled_connection (filename, parsed, "unmanaged", out_unhandled);
+	if (!svGetValueBoolean (main_ifcfg, "NM_CONTROLLED", TRUE)) {
+		connection = create_unhandled_connection (filename, main_ifcfg, "unmanaged", out_unhandled);
 		if (!connection) {
 			NM_SET_OUT (out_ignore_error, TRUE);
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
@@ -5365,7 +5556,7 @@ connection_from_file_full (const char *filename,
 	}
 
 	/* iBFT is handled by the iBFT settings plugin */
-	bootproto = svGetValueStr_cp (parsed, "BOOTPROTO");
+	bootproto = svGetValueStr_cp (main_ifcfg, "BOOTPROTO");
 	if (bootproto && !g_ascii_strcasecmp (bootproto, "ibft")) {
 		NM_SET_OUT (out_ignore_error, TRUE);
 		g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -5375,19 +5566,19 @@ connection_from_file_full (const char *filename,
 	}
 	g_free (bootproto);
 
-	devtype = svGetValueStr_cp (parsed, "DEVICETYPE");
+	devtype = svGetValueStr_cp (main_ifcfg, "DEVICETYPE");
 	if (devtype) {
 		if (!strcasecmp (devtype, TYPE_TEAM))
 			type = g_strdup (TYPE_TEAM);
 		else if (!strcasecmp (devtype, TYPE_TEAM_PORT)) {
 			gs_free char *device = NULL;
 
-			type = svGetValueStr_cp (parsed, "TYPE");
-			device = svGetValueStr_cp (parsed, "DEVICE");
+			type = svGetValueStr_cp (main_ifcfg, "TYPE");
+			device = svGetValueStr_cp (main_ifcfg, "DEVICE");
 
 			if (type) {
 				/* nothing to do */
-			} else if (device && is_vlan_device (device, parsed))
+			} else if (device && is_vlan_device (device, main_ifcfg))
 				type = g_strdup (TYPE_VLAN);
 			else
 				type = g_strdup (TYPE_ETHERNET);
@@ -5400,26 +5591,26 @@ connection_from_file_full (const char *filename,
 		/* Team and TeamPort types are also accepted by the mere
 		 * presence of TEAM_CONFIG/TEAM_MASTER. They don't require
 		 * DEVICETYPE. */
-		t = svGetValueStr_cp (parsed, "TEAM_CONFIG");
+		t = svGetValueStr_cp (main_ifcfg, "TEAM_CONFIG");
 		if (t)
 			type = g_strdup (TYPE_TEAM);
 	}
 
 	if (!type)
-		type = svGetValueStr_cp (parsed, "TYPE");
+		type = svGetValueStr_cp (main_ifcfg, "TYPE");
 
 	if (!type) {
 		gs_free char *tmp = NULL;
 		char *device;
 
-		if ((tmp = svGetValueStr_cp (parsed, "IPV6TUNNELIPV4"))) {
+		if ((tmp = svGetValueStr_cp (main_ifcfg, "IPV6TUNNELIPV4"))) {
 			NM_SET_OUT (out_ignore_error, TRUE);
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Ignoring unsupported connection due to IPV6TUNNELIPV4");
 			return NULL;
 		}
 
-		device = svGetValueStr_cp (parsed, "DEVICE");
+		device = svGetValueStr_cp (main_ifcfg, "DEVICE");
 		if (!device) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "File '%s' had neither TYPE nor DEVICE keys.", filename);
@@ -5435,11 +5626,11 @@ connection_from_file_full (const char *filename,
 		}
 
 		if (!test_type) {
-			if (is_bond_device (device, parsed))
+			if (is_bond_device (device, main_ifcfg))
 				type = g_strdup (TYPE_BOND);
-			else if (is_vlan_device (device, parsed))
+			else if (is_vlan_device (device, main_ifcfg))
 				type = g_strdup (TYPE_VLAN);
-			else if (is_wifi_device (device, parsed))
+			else if (is_wifi_device (device, main_ifcfg))
 				type = g_strdup (TYPE_WIRELESS);
 			else {
 				gs_free char *p_path = NULL;
@@ -5505,14 +5696,14 @@ 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)) {
+		if (svGetValueStr (main_ifcfg, "BONDING_OPTS", &bond_options)) {
 			/* initscripts consider these as bond masters */
 			g_free (type);
 			type = g_strdup (TYPE_BOND);
 		}
 	}
 
-	if (svGetValueBoolean (parsed, "BONDING_MASTER", FALSE) &&
+	if (svGetValueBoolean (main_ifcfg, "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");
@@ -5521,21 +5712,21 @@ connection_from_file_full (const char *filename,
 
 	/* Construct the connection */
 	if (!strcasecmp (type, TYPE_ETHERNET))
-		connection = wired_connection_from_ifcfg (filename, parsed, error);
+		connection = wired_connection_from_ifcfg (filename, main_ifcfg, error);
 	else if (!strcasecmp (type, TYPE_WIRELESS))
-		connection = wireless_connection_from_ifcfg (filename, parsed, error);
+		connection = wireless_connection_from_ifcfg (filename, main_ifcfg, error);
 	else if (!strcasecmp (type, TYPE_INFINIBAND))
-		connection = infiniband_connection_from_ifcfg (filename, parsed, error);
+		connection = infiniband_connection_from_ifcfg (filename, main_ifcfg, error);
 	else if (!strcasecmp (type, TYPE_BOND))
-		connection = bond_connection_from_ifcfg (filename, parsed, error);
+		connection = bond_connection_from_ifcfg (filename, main_ifcfg, error);
 	else if (!strcasecmp (type, TYPE_TEAM))
-		connection = team_connection_from_ifcfg (filename, parsed, error);
+		connection = team_connection_from_ifcfg (filename, main_ifcfg, error);
 	else if (!strcasecmp (type, TYPE_VLAN))
-		connection = vlan_connection_from_ifcfg (filename, parsed, error);
+		connection = vlan_connection_from_ifcfg (filename, main_ifcfg, error);
 	else if (!strcasecmp (type, TYPE_BRIDGE))
-		connection = bridge_connection_from_ifcfg (filename, parsed, error);
+		connection = bridge_connection_from_ifcfg (filename, main_ifcfg, error);
 	else {
-		connection = create_unhandled_connection (filename, parsed, "unrecognized", out_unhandled);
+		connection = create_unhandled_connection (filename, main_ifcfg, "unrecognized", out_unhandled);
 		if (!connection) {
 			PARSE_WARNING ("connection type was unrecognized but device was not uniquely identified; device may be managed");
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
@@ -5547,6 +5738,8 @@ connection_from_file_full (const char *filename,
 	if (!connection)
 		return NULL;
 
+	parse_ethtool_options (main_ifcfg, connection);
+
 	has_complex_routes_v4 = utils_has_complex_routes (filename, AF_INET);
 	has_complex_routes_v6 = utils_has_complex_routes (filename, AF_INET6);
 
@@ -5559,7 +5752,7 @@ connection_from_file_full (const char *filename,
 			PARSE_WARNING ("'rule-' and 'rule6-' files are present; you will need to use a dispatcher script to apply these routes");
 	}
 
-	s_ip6 = make_ip6_setting (parsed,
+	s_ip6 = make_ip6_setting (main_ifcfg,
 	                          network_ifcfg,
 	                          !has_complex_routes_v4 && !has_complex_routes_v6,
 	                          error);
@@ -5568,7 +5761,7 @@ connection_from_file_full (const char *filename,
 	else
 		nm_connection_add_setting (connection, s_ip6);
 
-	s_ip4 = make_ip4_setting (parsed,
+	s_ip4 = make_ip4_setting (main_ifcfg,
 	                          network_ifcfg,
 	                          !has_complex_routes_v4 && !has_complex_routes_v6,
 	                          &has_ip4_defroute,
@@ -5582,7 +5775,11 @@ connection_from_file_full (const char *filename,
 		nm_connection_add_setting (connection, s_ip4);
 	}
 
-	s_tc = make_tc_setting (parsed);
+	s_sriov = make_sriov_setting (main_ifcfg);
+	if (s_sriov)
+		nm_connection_add_setting (connection, s_sriov);
+
+	s_tc = make_tc_setting (main_ifcfg);
 	if (s_tc)
 		nm_connection_add_setting (connection, s_tc);
 
@@ -5590,27 +5787,31 @@ connection_from_file_full (const char *filename,
 	 * config fails for some reason, we read DOMAIN and put the
 	 * values into IPv6 config instead of IPv4.
 	 */
-	check_dns_search_domains (parsed, s_ip4, s_ip6);
+	check_dns_search_domains (main_ifcfg, s_ip4, s_ip6);
 
-	s_proxy = make_proxy_setting (parsed);
+	s_proxy = make_proxy_setting (main_ifcfg);
 	if (s_proxy)
 		nm_connection_add_setting (connection, s_proxy);
 
-	s_user = make_user_setting (parsed);
+	s_user = make_user_setting (main_ifcfg);
 	if (s_user)
 		nm_connection_add_setting (connection, s_user);
 
+	s_match = make_match_setting (main_ifcfg);
+	if (s_match)
+		nm_connection_add_setting (connection, s_match);
+
 	/* Bridge port? */
-	s_port = make_bridge_port_setting (parsed);
+	s_port = make_bridge_port_setting (main_ifcfg);
 	if (s_port)
 		nm_connection_add_setting (connection, s_port);
 
 	/* Team port? */
-	s_port = make_team_port_setting (parsed);
+	s_port = make_team_port_setting (main_ifcfg);
 	if (s_port)
 		nm_connection_add_setting (connection, s_port);
 
-	if (!make_dcb_setting (parsed, &s_dcb, error))
+	if (!make_dcb_setting (main_ifcfg, &s_dcb, error))
 		return NULL;
 	if (s_dcb)
 		nm_connection_add_setting (connection, s_dcb);
diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
index 862e640e..49096d26 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.c
@@ -470,3 +470,104 @@ nms_ifcfg_rh_utils_user_key_decode (const char *name, GString *str_buffer)
 
 	return TRUE;
 }
+
+/*****************************************************************************/
+
+const char *const _nm_ethtool_ifcfg_names[] = {
+#define ETHT_NAME(eid, ename) \
+[eid - _NM_ETHTOOL_ID_FEATURE_FIRST] = ""ename""
+	/* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_ESP_HW_OFFLOAD,               "esp-hw-offload"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_ESP_TX_CSUM_HW_OFFLOAD,       "esp-tx-csum-hw-offload"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_FCOE_MTU,                     "fcoe-mtu"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_GRO,                          "gro"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_GSO,                          "gso"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_HIGHDMA,                      "highdma"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_HW_TC_OFFLOAD,                "hw-tc-offload"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_L2_FWD_OFFLOAD,               "l2-fwd-offload"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_LOOPBACK,                     "loopback"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_LRO,                          "lro"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_NTUPLE,                       "ntuple"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RX,                           "rx"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RXHASH,                       "rxhash"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RXVLAN,                       "rxvlan"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RX_ALL,                       "rx-all"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RX_FCS,                       "rx-fcs"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RX_GRO_HW,                    "rx-gro-hw"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RX_UDP_TUNNEL_PORT_OFFLOAD,   "rx-udp_tunnel-port-offload"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RX_VLAN_FILTER,               "rx-vlan-filter"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_FILTER,          "rx-vlan-stag-filter"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_RX_VLAN_STAG_HW_PARSE,        "rx-vlan-stag-hw-parse"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_SG,                           "sg"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TLS_HW_RECORD,                "tls-hw-record"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TLS_HW_TX_OFFLOAD,            "tls-hw-tx-offload"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TSO,                          "tso"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX,                           "tx"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TXVLAN,                       "txvlan"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_FCOE_CRC,         "tx-checksum-fcoe-crc"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV4,             "tx-checksum-ipv4"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IPV6,             "tx-checksum-ipv6"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_IP_GENERIC,       "tx-checksum-ip-generic"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_CHECKSUM_SCTP,             "tx-checksum-sctp"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_ESP_SEGMENTATION,          "tx-esp-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_FCOE_SEGMENTATION,         "tx-fcoe-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_GRE_CSUM_SEGMENTATION,     "tx-gre-csum-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_GRE_SEGMENTATION,          "tx-gre-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_GSO_PARTIAL,               "tx-gso-partial"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_GSO_ROBUST,                "tx-gso-robust"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_IPXIP4_SEGMENTATION,       "tx-ipxip4-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_IPXIP6_SEGMENTATION,       "tx-ipxip6-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_NOCACHE_COPY,              "tx-nocache-copy"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER,            "tx-scatter-gather"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_SCATTER_GATHER_FRAGLIST,   "tx-scatter-gather-fraglist"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_SCTP_SEGMENTATION,         "tx-sctp-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_TCP6_SEGMENTATION,         "tx-tcp6-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_TCP_ECN_SEGMENTATION,      "tx-tcp-ecn-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_TCP_MANGLEID_SEGMENTATION, "tx-tcp-mangleid-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_TCP_SEGMENTATION,          "tx-tcp-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_UDP_SEGMENTATION,          "tx-udp-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_CSUM_SEGMENTATION, "tx-udp_tnl-csum-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_UDP_TNL_SEGMENTATION,      "tx-udp_tnl-segmentation"),
+	ETHT_NAME (NM_ETHTOOL_ID_FEATURE_TX_VLAN_STAG_HW_INSERT,       "tx-vlan-stag-hw-insert"),
+};
+
+const NMEthtoolData *
+nms_ifcfg_rh_utils_get_ethtool_by_name (const char *name)
+{
+	static const struct {
+		NMEthtoolID ethtool_id;
+		const char *kernel_name;
+	} kernel_names[] = {
+		{ NM_ETHTOOL_ID_FEATURE_GRO,    "rx-gro" },
+		{ NM_ETHTOOL_ID_FEATURE_GSO,    "tx-generic-segmentation" },
+		{ NM_ETHTOOL_ID_FEATURE_LRO,    "rx-lro" },
+		{ NM_ETHTOOL_ID_FEATURE_NTUPLE, "rx-ntuple-filter" },
+		{ NM_ETHTOOL_ID_FEATURE_RX,     "rx-checksum" },
+		{ NM_ETHTOOL_ID_FEATURE_RXHASH, "rx-hashing" },
+		{ NM_ETHTOOL_ID_FEATURE_RXVLAN, "rx-vlan-hw-parse" },
+		{ NM_ETHTOOL_ID_FEATURE_TXVLAN, "tx-vlan-hw-insert" },
+	};
+	guint i;
+
+	for (i = 0; i < G_N_ELEMENTS (_nm_ethtool_ifcfg_names); i++) {
+		if (nm_streq (name, _nm_ethtool_ifcfg_names[i]))
+			return nm_ethtool_data[i];
+	}
+
+	/* Option not found. Note that ethtool utility has built-in features and
+	 * NetworkManager's API follows the naming of these built-in features, whenever
+	 * they exist.
+	 * For example, NM's "ethtool.feature-ntuple" corresponds to ethtool utility's "ntuple"
+	 * feature. However the underlying kernel feature is called "rx-ntuple-filter" (as reported
+	 * for ETH_SS_FEATURES).
+	 *
+	 * With ethtool utility, whose command line we attempt to parse here, the user can also
+	 * specify the name of the underlying kernel feature directly. So, check whether that is
+	 * the case and if yes, map them to the corresponding NetworkManager's features. */
+	for (i = 0; i < G_N_ELEMENTS (kernel_names); i++) {
+		if (nm_streq (name, kernel_names[i].kernel_name))
+			return nm_ethtool_data[kernel_names[i].ethtool_id];
+	}
+
+	return NULL;
+}
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 3756af7c..84c22094 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-utils.h
@@ -22,14 +22,15 @@
 #define _UTILS_H_
 
 #include "nm-connection.h"
+#include "nm-ethtool-utils.h"
 
 #include "shvar.h"
 
 #define NM_IFCFG_CONNECTION_LOG_PATH(path)  ((path) ?: "in-memory")
 #define NM_IFCFG_CONNECTION_LOG_FMT         "%s (%s,\"%s\")"
-#define NM_IFCFG_CONNECTION_LOG_ARG(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))
+#define NM_IFCFG_CONNECTION_LOG_ARG(con)    NM_IFCFG_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con))
 #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)
+#define NM_IFCFG_CONNECTION_LOG_ARGD(con)   NM_IFCFG_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)), (con)
 
 char *utils_cert_path (const char *parent, const char *suffix, const char *extension);
 
@@ -80,4 +81,20 @@ _nms_ifcfg_rh_utils_numbered_tag (char *buf, gsize buf_len, const char *tag_name
 		_nms_ifcfg_rh_utils_numbered_tag (buf, sizeof (buf), ""tag_name"", (which)); \
 	})
 
+/*****************************************************************************/
+
+extern const char *const _nm_ethtool_ifcfg_names[_NM_ETHTOOL_ID_FEATURE_NUM];
+
+static inline const char *
+nms_ifcfg_rh_utils_get_ethtool_name (NMEthtoolID ethtool_id)
+{
+	nm_assert (ethtool_id >= _NM_ETHTOOL_ID_FEATURE_FIRST && ethtool_id <= _NM_ETHTOOL_ID_FEATURE_LAST);
+	nm_assert ((ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST) < G_N_ELEMENTS (_nm_ethtool_ifcfg_names));
+	nm_assert (_nm_ethtool_ifcfg_names[ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST]);
+
+	return _nm_ethtool_ifcfg_names[ethtool_id - _NM_ETHTOOL_ID_FEATURE_FIRST];
+}
+
+const NMEthtoolData *nms_ifcfg_rh_utils_get_ethtool_by_name (const char *name);
+
 #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 fdb2e10a..e1a2289e 100644
--- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
+++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c
@@ -33,10 +33,12 @@
 #include <stdio.h>
 
 #include "nm-utils/nm-enum-utils.h"
+#include "nm-utils/nm-io-utils.h"
 #include "nm-manager.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless.h"
+#include "nm-setting-ethtool.h"
 #include "nm-setting-8021x.h"
 #include "nm-setting-proxy.h"
 #include "nm-setting-ip4-config.h"
@@ -50,6 +52,7 @@
 #include "nm-core-internal.h"
 #include "NetworkManagerUtils.h"
 #include "nm-meta-setting.h"
+#include "nm-ethtool-utils.h"
 
 #include "nms-ifcfg-rh-common.h"
 #include "nms-ifcfg-rh-reader.h"
@@ -809,7 +812,7 @@ write_wireless_setting (NMConnection *connection,
 	const char *device_mac, *cloned_mac;
 	guint32 mtu, chan, i;
 	gboolean adhoc = FALSE, hex_ssid = FALSE;
-	const char * const *macaddr_blacklist;
+	const char *const*macaddr_blacklist;
 
 	s_wireless = nm_connection_get_setting_wireless (connection);
 	if (!s_wireless) {
@@ -1041,16 +1044,9 @@ static gboolean
 write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 {
 	NMSettingWired *s_wired;
-	const char *device_mac, *cloned_mac;
-	char *tmp;
-	const char *nettype, *portname, *ctcprot, *s390_key, *s390_val, *duplex;
-	guint32 mtu, num_opts, speed, i;
-	const char *const *s390_subchannels;
-	GString *str = NULL;
-	const char * const *macaddr_blacklist;
-	gboolean auto_negotiate;
-	NMSettingWiredWakeOnLan wol;
-	const char *wol_password;
+	const char *const*s390_subchannels;
+	guint32 mtu, num_opts, i;
+	const char *const*macaddr_blacklist;
 
 	s_wired = nm_connection_get_setting_wired (connection);
 	if (!s_wired) {
@@ -1059,144 +1055,211 @@ write_wired_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 		return FALSE;
 	}
 
-	device_mac = nm_setting_wired_get_mac_address (s_wired);
-	svSetValueStr (ifcfg, "HWADDR", device_mac);
+	svSetValueStr (ifcfg, "HWADDR",
+	               nm_setting_wired_get_mac_address (s_wired));
 
-	cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
-	svSetValueStr (ifcfg, "MACADDR", cloned_mac);
+	svSetValueStr (ifcfg, "MACADDR",
+	               nm_setting_wired_get_cloned_mac_address (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);
 	if (macaddr_blacklist[0]) {
-		char *blacklist_str;
+		gs_free char *blacklist_str = NULL;
 
 		blacklist_str = g_strjoinv (" ", (char **) macaddr_blacklist);
 		svSetValueStr (ifcfg, "HWADDR_BLACKLIST", blacklist_str);
-		g_free (blacklist_str);
-	}
+	} else
+		svUnsetValue (ifcfg, "HWADDR_BLACKLIST");
 
 	mtu = nm_setting_wired_get_mtu (s_wired);
 	svSetValueInt64_cond (ifcfg, "MTU", mtu != 0, mtu);
 
-	svUnsetValue (ifcfg, "SUBCHANNELS");
 	s390_subchannels = nm_setting_wired_get_s390_subchannels (s_wired);
-	if (s390_subchannels) {
-		int len = g_strv_length ((char **)s390_subchannels);
 
-		tmp = NULL;
+	{
+		gs_free char *tmp = NULL;
+		gsize len = NM_PTRARRAY_LEN (s390_subchannels);
+
 		if (len == 2) {
-			tmp = g_strdup_printf ("%s,%s", s390_subchannels[0], s390_subchannels[1]);
+			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],
+			tmp = g_strdup_printf ("%s,%s,%s",
+			                       s390_subchannels[0],
+			                       s390_subchannels[1],
 			                       s390_subchannels[2]);
 		}
+
 		svSetValueStr (ifcfg, "SUBCHANNELS", tmp);
-		g_free (tmp);
 	}
 
-	svUnsetValue (ifcfg, "NETTYPE");
-	nettype = nm_setting_wired_get_s390_nettype (s_wired);
-	if (nettype)
-		svSetValueStr (ifcfg, "NETTYPE", nettype);
+	svSetValueStr (ifcfg, "NETTYPE",
+	               nm_setting_wired_get_s390_nettype (s_wired));
 
-	svUnsetValue (ifcfg, "PORTNAME");
-	portname = nm_setting_wired_get_s390_option_by_key (s_wired, "portname");
-	if (portname)
-		svSetValueStr (ifcfg, "PORTNAME", portname);
+	svSetValueStr (ifcfg, "PORTNAME",
+	               nm_setting_wired_get_s390_option_by_key (s_wired, "portname"));
 
-	svUnsetValue (ifcfg, "CTCPROT");
-	ctcprot = nm_setting_wired_get_s390_option_by_key (s_wired, "ctcprot");
-	if (ctcprot)
-		svSetValueStr (ifcfg, "CTCPROT", ctcprot);
+	svSetValueStr (ifcfg, "CTCPROT",
+	               nm_setting_wired_get_s390_option_by_key (s_wired, "ctcprot"));
 
 	svUnsetValue (ifcfg, "OPTIONS");
 	num_opts = nm_setting_wired_get_num_s390_options (s_wired);
 	if (s390_subchannels && num_opts) {
-		str = g_string_sized_new (30);
+		nm_auto_free_gstring GString *tmp = NULL;
+
 		for (i = 0; i < num_opts; i++) {
+			const char *s390_key, *s390_val;
+
 			nm_setting_wired_get_s390_option (s_wired, i, &s390_key, &s390_val);
 
 			/* portname is handled separately */
 			if (!strcmp (s390_key, "portname") || !strcmp (s390_key, "ctcprot"))
 				continue;
 
-			if (str->len)
-				g_string_append_c (str, ' ');
-			g_string_append_printf (str, "%s=%s", s390_key, s390_val);
+			if (!tmp)
+				tmp = g_string_sized_new (30);
+			else
+				g_string_append_c (tmp, ' ');
+			g_string_append_printf (tmp, "%s=%s", s390_key, s390_val);
 		}
-		if (str->len)
-			svSetValueStr (ifcfg, "OPTIONS", str->str);
-		g_string_free (str, TRUE);
+		if (tmp)
+			svSetValueStr (ifcfg, "OPTIONS", tmp->str);
 	}
 
-	/* Stuff ETHTOOL_OPT with required options */
-	str = NULL;
-	auto_negotiate = nm_setting_wired_get_auto_negotiate (s_wired);
-	speed = nm_setting_wired_get_speed (s_wired);
-	duplex = nm_setting_wired_get_duplex (s_wired);
+	svSetValueStr (ifcfg, "TYPE", TYPE_ETHERNET);
 
-	/* autoneg off + speed 0 + duplex NULL, means we want NM
-	 * to skip link configuration which is default. So write
-	 * down link config only if we have auto-negotiate true or
-	 * a valid value for one among speed and duplex.
-	 */
-	if (auto_negotiate) {
-		str = g_string_sized_new (64);
-		g_string_printf (str, "autoneg on");
-	} else if (speed || duplex) {
-		str = g_string_sized_new (64);
-		g_string_printf (str, "autoneg off");
+	return TRUE;
+}
+
+static gboolean
+write_ethtool_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
+{
+	NMSettingWired *s_wired;
+	NMSettingEthtool *s_ethtool;
+	const char *duplex;
+	guint32 speed;
+	GString *str = NULL;
+	gboolean auto_negotiate;
+	NMSettingWiredWakeOnLan wol;
+	const char *wol_password;
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	s_ethtool = NM_SETTING_ETHTOOL (nm_connection_get_setting (connection, NM_TYPE_SETTING_ETHTOOL));
+
+	if (!s_wired && !s_ethtool) {
+		svUnsetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN");
+		svUnsetValue (ifcfg, "ETHTOOL_OPTS");
+		return TRUE;
 	}
-	if (speed)
-		g_string_append_printf (str, " speed %u", speed);
-	if (duplex)
-		g_string_append_printf (str, " duplex %s", duplex);
 
-	wol = nm_setting_wired_get_wake_on_lan (s_wired);
-	wol_password = nm_setting_wired_get_wake_on_lan_password (s_wired);
+	if (s_wired) {
+		auto_negotiate = nm_setting_wired_get_auto_negotiate (s_wired);
+		speed = nm_setting_wired_get_speed (s_wired);
+		duplex = nm_setting_wired_get_duplex (s_wired);
+
+		/* autoneg off + speed 0 + duplex NULL, means we want NM
+		 * to skip link configuration which is default. So write
+		 * down link config only if we have auto-negotiate true or
+		 * a valid value for one among speed and duplex.
+		 */
+		if (auto_negotiate) {
+			str = g_string_sized_new (64);
+			g_string_printf (str, "autoneg on");
+		} else if (speed || duplex) {
+			str = g_string_sized_new (64);
+			g_string_printf (str, "autoneg off");
+		}
+		if (speed)
+			g_string_append_printf (str, " speed %u", speed);
+		if (duplex)
+			g_string_append_printf (str, " duplex %s", duplex);
+
+		wol = nm_setting_wired_get_wake_on_lan (s_wired);
+		wol_password = nm_setting_wired_get_wake_on_lan_password (s_wired);
+
+		svSetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN",
+		              wol == NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE
+		            ? "ignore"
+		            : NULL);
+		if (!NM_IN_SET (wol, NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE,
+		                     NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT)) {
+			if (!str)
+				str = g_string_sized_new (30);
+			else
+				g_string_append (str, " ");
+
+			g_string_append (str, "wol ");
+
+			if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_PHY))
+				g_string_append (str, "p");
+			if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST))
+				g_string_append (str, "u");
+			if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST))
+				g_string_append (str, "m");
+			if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST))
+				g_string_append (str, "b");
+			if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ARP))
+				g_string_append (str, "a");
+			if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC))
+				g_string_append (str, "g");
+
+			if (!NM_FLAGS_ANY (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ALL))
+				g_string_append (str, "d");
+
+			if (wol_password && NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC))
+				g_string_append_printf (str, "s sopass %s", wol_password);
+		}
+	} else
+		svUnsetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN");
+
+	if (s_ethtool) {
+		NMEthtoolID ethtool_id;
+		NMSettingConnection *s_con;
+		const char *iface = NULL;
+
+		s_con = nm_connection_get_setting_connection (connection);
+		if (s_con) {
+			iface = nm_setting_connection_get_interface_name (s_con);
+			if (   iface
+			    && (   !iface[0]
+			        || !NM_STRCHAR_ALL (iface, ch,    (ch >= 'a' && ch <= 'z')
+			                                       || (ch >= 'A' && ch <= 'Z')
+			                                       || (ch >= '0' && ch <= '9')
+			                                       || NM_IN_SET (ch, '_'))))
+				iface = NULL;
+		}
 
-	if (wol == NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE)
-		svSetValue (ifcfg, "ETHTOOL_WAKE_ON_LAN", "ignore");
-	else if (wol == NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT) {
-		if (!str)
-			svUnsetValue (ifcfg, "ETHTOOL_OPTS");
-	} else {
 		if (!str)
 			str = g_string_sized_new (30);
 		else
-			g_string_append (str, " ");
+			g_string_append (str, " ; ");
+		g_string_append (str, "-K ");
+		g_string_append (str, iface ?: "net0");
 
-		g_string_append (str, "wol ");
+		for (ethtool_id = _NM_ETHTOOL_ID_FEATURE_FIRST; ethtool_id <= _NM_ETHTOOL_ID_FEATURE_LAST; ethtool_id++) {
+			const NMEthtoolData *ed = nm_ethtool_data[ethtool_id];
+			NMTernary val;
 
-		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_PHY))
-			g_string_append (str, "p");
-		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST))
-			g_string_append (str, "u");
-		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST))
-			g_string_append (str, "m");
-		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST))
-			g_string_append (str, "b");
-		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ARP))
-			g_string_append (str, "a");
-		if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC))
-			g_string_append (str, "g");
+			nm_assert (nms_ifcfg_rh_utils_get_ethtool_name (ethtool_id));
 
-		if (!NM_FLAGS_ANY (wol, NM_SETTING_WIRED_WAKE_ON_LAN_ALL))
-			g_string_append (str, "d");
+			val = nm_setting_ethtool_get_feature (s_ethtool, ed->optname);
+			if (val == NM_TERNARY_DEFAULT)
+				continue;
 
-		if (wol_password && NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC))
-			g_string_append_printf (str, "s sopass %s", wol_password);
+			g_string_append_c (str, ' ');
+			g_string_append (str, nms_ifcfg_rh_utils_get_ethtool_name (ethtool_id));
+			g_string_append (str, val == NM_TERNARY_TRUE ? " on" : " off");
+		}
 	}
+
 	if (str) {
 		svSetValueStr (ifcfg, "ETHTOOL_OPTS", str->str);
 		g_string_free (str, TRUE);
-	}
-	/* End ETHTOOL_OPT stuffing */
-
-	svSetValueStr (ifcfg, "TYPE", TYPE_ETHERNET);
+	} else
+		svUnsetValue (ifcfg, "ETHTOOL_OPTS");
 
 	return TRUE;
 }
@@ -1555,7 +1618,7 @@ static void
 write_dcb_app (shvarFile *ifcfg,
                const char *tag,
                NMSettingDcbFlags flags,
-               gint priority)
+               int priority)
 {
 	char prop[NM_STRLEN ("DCB_xxxxxxxxxxxxxxxxxxxxxxx_yyyyyyyyyyyyyyyyyyyy")];
 
@@ -1725,8 +1788,9 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	guint32 n, i;
 	GString *str;
 	const char *master, *master_iface = NULL, *type;
-	gint vint;
+	int vint;
 	NMSettingConnectionMdns mdns;
+	NMSettingConnectionLlmnr llmnr;
 	guint32 vuint32;
 	const char *tmp;
 
@@ -1746,6 +1810,11 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 	                      vint != -1,
 	                      vint);
 
+	vint = nm_setting_connection_get_multi_connect (s_con);
+	svSetValueInt64_cond (ifcfg, "MULTI_CONNECT",
+	                      vint != NM_CONNECTION_MULTI_CONNECT_DEFAULT,
+	                      vint);
+
 	/* Only save the value for master connections */
 	type = nm_setting_connection_get_connection_type (s_con);
 	if (_nm_connection_type_is_master (type)) {
@@ -1892,6 +1961,13 @@ write_connection_setting (NMSettingConnection *s_con, shvarFile *ifcfg)
 		                mdns);
 	} else
 		svUnsetValue (ifcfg, "MDNS");
+
+	llmnr = nm_setting_connection_get_llmnr (s_con);
+	if (llmnr != NM_SETTING_CONNECTION_LLMNR_DEFAULT) {
+		svSetValueEnum (ifcfg, "LLMNR", nm_setting_connection_llmnr_get_type (),
+		                llmnr);
+	} else
+		svUnsetValue (ifcfg, "LLMNR");
 }
 
 static char *
@@ -2135,6 +2211,45 @@ write_user_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	return TRUE;
 }
 
+static void
+write_sriov_setting (NMConnection *connection, shvarFile *ifcfg)
+{
+	NMSettingSriov *s_sriov;
+	guint i, num = 0;
+	NMTernary b;
+	NMSriovVF *vf;
+	char key[32];
+	char *str;
+
+	svUnsetAll (ifcfg, SV_KEY_TYPE_SRIOV_VF);
+
+	s_sriov = nm_connection_get_setting_sriov (connection);
+	if (s_sriov)
+		num = nm_setting_sriov_get_total_vfs (s_sriov);
+	if (num == 0) {
+		svUnsetValue (ifcfg, "SRIOV_TOTAL_VFS");
+		svUnsetValue (ifcfg, "SRIOV_AUTOPROBE_DRIVERS");
+		return;
+	}
+
+	svSetValueInt64 (ifcfg, "SRIOV_TOTAL_VFS", num);
+
+	b = nm_setting_sriov_get_autoprobe_drivers (s_sriov);
+	if (b != NM_TERNARY_DEFAULT)
+		svSetValueInt64 (ifcfg, "SRIOV_AUTOPROBE_DRIVERS", b);
+	else
+		svUnsetValue (ifcfg, "SRIOV_AUTOPROBE_DRIVERS");
+
+	num = nm_setting_sriov_get_num_vfs (s_sriov);
+	for (i = 0; i < num; i++) {
+		vf = nm_setting_sriov_get_vf (s_sriov, i);
+		nm_sprintf_buf (key, "SRIOV_VF%u", nm_sriov_vf_get_index (vf));
+		str = nm_utils_sriov_vf_to_str (vf, TRUE, NULL);
+		svSetValueStr (ifcfg, key, str);
+		g_free (str);
+	}
+}
+
 static gboolean
 write_tc_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 {
@@ -2179,6 +2294,38 @@ write_tc_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
 	return TRUE;
 }
 
+static gboolean
+write_match_setting (NMConnection *connection, shvarFile *ifcfg, GError **error)
+{
+	NMSettingMatch *s_match;
+	nm_auto_free_gstring GString *str = NULL;
+	guint i, num;
+
+	svUnsetValue (ifcfg, "MATCH_INTERFACE_NAME");
+
+	s_match = (NMSettingMatch *) nm_connection_get_setting (connection, NM_TYPE_SETTING_MATCH);
+	if (!s_match)
+		return TRUE;
+
+	num = nm_setting_match_get_num_interface_names (s_match);
+	for (i = 0; i < num; i++) {
+		gs_free char *to_free = NULL;
+		const char *name;
+
+		if (i == 0)
+			str = g_string_new ("");
+		else
+			g_string_append_c (str, ' ');
+		name = nm_setting_match_get_interface_name (s_match, i);
+		g_string_append (str, _nm_utils_escape_spaces (name, &to_free));
+	}
+
+	if (str)
+		svSetValueStr (ifcfg, "MATCH_INTERFACE_NAME", str->str);
+
+	return TRUE;
+}
+
 static void
 write_res_options (shvarFile *ifcfg, NMSettingIPConfig *s_ip, const char *var)
 {
@@ -2212,11 +2359,11 @@ write_ip4_setting (NMConnection *connection,
 	const char *value;
 	char *tmp;
 	char tag[64];
-	gint j;
+	int j;
 	guint i, num, n;
 	gint64 route_metric;
 	NMIPRouteTableSyncMode route_table;
-	gint priority;
+	int priority;
 	int timeout;
 	GString *searches;
 	const char *method = NULL;
@@ -2548,7 +2695,7 @@ write_ip6_setting (NMConnection *connection,
 	NMSettingIPConfig *s_ip4;
 	const char *value;
 	guint i, num, num4;
-	gint priority;
+	int priority;
 	NMIPAddress *addr;
 	const char *dns;
 	gint64 route_metric;
@@ -2918,9 +3065,17 @@ do_write_construct (NMConnection *connection,
 	if (!write_proxy_setting (connection, ifcfg, error))
 		return FALSE;
 
+	if (!write_ethtool_setting (connection, ifcfg, error))
+		return FALSE;
+
 	if (!write_user_setting (connection, ifcfg, error))
 		return FALSE;
 
+	if (!write_match_setting (connection, ifcfg, error))
+		return FALSE;
+
+	write_sriov_setting (connection, ifcfg);
+
 	if (!write_tc_setting (connection, ifcfg, error))
 		return FALSE;
 
diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c
index 9120b870..fe82fbdd 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.c
+++ b/src/settings/plugins/ifcfg-rh/shvar.c
@@ -39,6 +39,7 @@
 #include "nm-core-internal.h"
 #include "nm-core-utils.h"
 #include "nm-utils/nm-enum-utils.h"
+#include "nm-utils/nm-io-utils.h"
 #include "c-list/src/c-list.h"
 
 /*****************************************************************************/
@@ -90,8 +91,8 @@ struct _shvarFile {
  *
  * Returns: the parsed boolean value or @fallback.
  */
-gint
-svParseBoolean (const char *value, gint fallback)
+int
+svParseBoolean (const char *value, int fallback)
 {
 	if (!value)
 		return fallback;
@@ -148,8 +149,8 @@ static char *
 _escape_ansic (const char *source)
 {
 	const char *p;
-	gchar *dest;
-	gchar *q;
+	char *dest;
+	char *q;
 
 	nm_assert (source);
 
@@ -821,6 +822,7 @@ svOpenFileInternal (const char *name, gboolean create, GError **error)
 	if (nm_utils_fd_get_contents (closefd ? nm_steal_fd (&fd) : fd,
 	                              closefd,
 	                              10 * 1024 * 1024,
+	                              NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
 	                              &arena,
 	                              NULL,
 	                              &local) < 0) {
@@ -870,8 +872,61 @@ svCreateFile (const char *name)
 
 /*****************************************************************************/
 
+static gboolean
+_is_all_digits (const char *str)
+{
+	return    str[0]
+	       && NM_STRCHAR_ALL (str, ch, g_ascii_isdigit (ch));
+}
+
+#define IS_NUMBERED_TAG(key, tab_name) \
+	({ \
+		const char *_key = (key); \
+		\
+		(   (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \
+		 && _is_all_digits (&_key[NM_STRLEN (tab_name)])); \
+	})
+
+static gboolean
+_svKeyMatchesType (const char *key, SvKeyType match_key_type)
+{
+	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY))
+		return TRUE;
+
+	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) {
+		if (   IS_NUMBERED_TAG (key, "ADDRESS")
+		    || IS_NUMBERED_TAG (key, "NETMASK")
+		    || IS_NUMBERED_TAG (key, "GATEWAY")
+		    || IS_NUMBERED_TAG (key, "METRIC")
+		    || IS_NUMBERED_TAG (key, "OPTIONS"))
+			return TRUE;
+	}
+	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) {
+		if (   IS_NUMBERED_TAG (key, "IPADDR")
+		    || IS_NUMBERED_TAG (key, "PREFIX")
+		    || IS_NUMBERED_TAG (key, "NETMASK")
+		    || IS_NUMBERED_TAG (key, "GATEWAY"))
+			return TRUE;
+	}
+	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_USER)) {
+		if (g_str_has_prefix (key, "NM_USER_"))
+			return TRUE;
+	}
+	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_TC)) {
+		if (   IS_NUMBERED_TAG (key, "QDISC")
+		    || IS_NUMBERED_TAG (key, "FILTER"))
+			return TRUE;
+	}
+	if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_SRIOV_VF)) {
+		if (IS_NUMBERED_TAG (key, "SRIOV_VF"))
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
 GHashTable *
-svGetKeys (shvarFile *s)
+svGetKeys (shvarFile *s, SvKeyType match_key_type)
 {
 	GHashTable *keys = NULL;
 	CList *current;
@@ -881,7 +936,9 @@ svGetKeys (shvarFile *s)
 
 	c_list_for_each (current, &s->lst_head) {
 		line = c_list_entry (current, shvarLine, lst);
-		if (line->key && line->line) {
+		if (   line->key
+		    && line->line
+		    && _svKeyMatchesType (line->key, match_key_type)) {
 			/* we don't clone the keys. The keys are only valid
 			 * until @s gets modified. */
 			if (!keys)
@@ -1044,8 +1101,8 @@ svGetValueStr_cp (shvarFile *s, const char *key)
  *
  * Returns: the parsed boolean value or @fallback.
  */
-gint
-svGetValueBoolean (shvarFile *s, const char *key, gint fallback)
+int
+svGetValueBoolean (shvarFile *s, const char *key, int fallback)
 {
 	gs_free char *to_free = NULL;
 	const char *value;
@@ -1120,21 +1177,6 @@ svGetValueEnum (shvarFile *s, const char *key,
 
 /*****************************************************************************/
 
-static gboolean
-_is_all_digits (const char *str)
-{
-	return    str[0]
-	       && NM_STRCHAR_ALL (str, ch, g_ascii_isdigit (ch));
-}
-
-#define IS_NUMBERED_TAG(key, tab_name) \
-	({ \
-		const char *_key = (key); \
-		\
-		(   (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \
-		 && _is_all_digits (&_key[NM_STRLEN (tab_name)])); \
-	})
-
 gboolean
 svUnsetAll (shvarFile *s, SvKeyType match_key_type)
 {
@@ -1150,38 +1192,11 @@ svUnsetAll (shvarFile *s, SvKeyType match_key_type)
 		if (!line->key)
 			continue;
 
-		if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ANY))
-			goto do_clear;
-		if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTE_SVFORMAT)) {
-			if (   IS_NUMBERED_TAG (line->key, "ADDRESS")
-			    || IS_NUMBERED_TAG (line->key, "NETMASK")
-			    || IS_NUMBERED_TAG (line->key, "GATEWAY")
-			    || IS_NUMBERED_TAG (line->key, "METRIC")
-			    || IS_NUMBERED_TAG (line->key, "OPTIONS"))
-				goto do_clear;
-		}
-		if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_IP4_ADDRESS)) {
-			if (   IS_NUMBERED_TAG (line->key, "IPADDR")
-			    || IS_NUMBERED_TAG (line->key, "PREFIX")
-			    || IS_NUMBERED_TAG (line->key, "NETMASK")
-			    || IS_NUMBERED_TAG (line->key, "GATEWAY"))
-				goto do_clear;
-		}
-		if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_USER)) {
-			if (g_str_has_prefix (line->key, "NM_USER_"))
-				goto do_clear;
-		}
-		if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_TC)) {
-			if (   IS_NUMBERED_TAG (line->key, "QDISC")
-			    || IS_NUMBERED_TAG (line->key, "FILTER"))
-				goto do_clear;
-		}
-
-		continue;
-do_clear:
-		if (nm_clear_g_free (&line->line)) {
-			ASSERT_shvarLine (line);
-			changed = TRUE;
+		if (_svKeyMatchesType (line->key, match_key_type)) {
+			if (nm_clear_g_free (&line->line)) {
+				ASSERT_shvarLine (line);
+				changed = TRUE;
+			}
 		}
 	}
 
diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h
index dbc4d950..622bb474 100644
--- a/src/settings/plugins/ifcfg-rh/shvar.h
+++ b/src/settings/plugins/ifcfg-rh/shvar.h
@@ -33,6 +33,15 @@
 
 typedef struct _shvarFile shvarFile;
 
+typedef enum {
+	SV_KEY_TYPE_ANY                     = (1LL << 0),
+	SV_KEY_TYPE_ROUTE_SVFORMAT          = (1LL << 1),
+	SV_KEY_TYPE_IP4_ADDRESS             = (1LL << 2),
+	SV_KEY_TYPE_TC                      = (1LL << 3),
+	SV_KEY_TYPE_USER                    = (1LL << 4),
+	SV_KEY_TYPE_SRIOV_VF                = (1LL << 5),
+} SvKeyType;
+
 const char *svFileGetName (const shvarFile *s);
 
 void _nmtst_svFileSetName (shvarFile *s, const char *fileName);
@@ -56,15 +65,15 @@ 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);
+int svParseBoolean (const char *value, int def);
 
-GHashTable *svGetKeys (shvarFile *s);
+GHashTable *svGetKeys (shvarFile *s, SvKeyType match_key_type);
 
 /* 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
  */
-gint svGetValueBoolean (shvarFile *s, const char *key, gint def);
+int svGetValueBoolean (shvarFile *s, const char *key, int def);
 
 gint64 svGetValueInt64 (shvarFile *s, const char *key, guint base, gint64 min, gint64 max, gint64 fallback);
 
@@ -85,15 +94,6 @@ gboolean svSetValueInt64_cond (shvarFile *s, const char *key, gboolean do_set, g
 gboolean svSetValueEnum (shvarFile *s, const char *key, GType gtype, int value);
 
 gboolean svUnsetValue (shvarFile *s, const char *key);
-
-typedef enum {
-	SV_KEY_TYPE_ANY                     = (1LL << 0),
-	SV_KEY_TYPE_ROUTE_SVFORMAT          = (1LL << 1),
-	SV_KEY_TYPE_IP4_ADDRESS             = (1LL << 2),
-	SV_KEY_TYPE_TC                      = (1LL << 3),
-	SV_KEY_TYPE_USER                    = (1LL << 4),
-} SvKeyType;
-
 gboolean svUnsetAll (shvarFile *s, SvKeyType match_key_type);
 
 /* Write the current contents iff modified.  Returns FALSE on error
diff --git a/src/settings/plugins/ifcfg-rh/tests/meson.build b/src/settings/plugins/ifcfg-rh/tests/meson.build
index 3d72d892..0593f12d 100644
--- a/src/settings/plugins/ifcfg-rh/tests/meson.build
+++ b/src/settings/plugins/ifcfg-rh/tests/meson.build
@@ -6,7 +6,6 @@ exe = executable(
   test_unit,
   test_unit + '.c',
   dependencies: test_nm_dep,
-  c_args: nm_build_cflags,
   link_with: libnms_ifcfg_rh_core
 )
 
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
index 8f421cfb..214f5da5 100644
--- 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
@@ -1,7 +1,7 @@
-ETHTOOL_OPTS="autoneg off speed 10 duplex half"
 TYPE=Ethernet
 PROXY_METHOD=none
 BROWSER_ONLY=no
+ETHTOOL_OPTS="autoneg off speed 10 duplex half"
 BOOTPROTO=dhcp
 DEFROUTE=yes
 IPV4_FAILURE_FATAL=no
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
index 398a3017..de66dcdd 100644
--- 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
@@ -1,7 +1,7 @@
-ETHTOOL_OPTS="wol umgs sopass 00:00:00:11:22:33"
 TYPE=Ethernet
 PROXY_METHOD=none
 BROWSER_ONLY=no
+ETHTOOL_OPTS="wol umgs sopass 00:00:00:11:22:33"
 BOOTPROTO=dhcp
 DEFROUTE=yes
 IPV4_FAILURE_FATAL=no
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected
new file mode 100644
index 00000000..f0cd36cb
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_match.cexpected
@@ -0,0 +1,11 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+MATCH_INTERFACE_NAME="ens* eth\\ 1? !veth*"
+BOOTPROTO=dhcp
+DEFROUTE=yes
+IPV4_FAILURE_FATAL=no
+IPV6INIT=no
+NAME="Test Write Wired with Match setting"
+UUID=${UUID}
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov
new file mode 100644
index 00000000..142f56e4
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov
@@ -0,0 +1,19 @@
+TYPE=Ethernet
+DEVICE=eth0
+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
+GATEWAY=192.168.1.1
+IPV6INIT=no
+NAME=ethernet-sriov
+UUID=acc703b8-e751-44ce-b456-1550bdf2057e
+SRIOV_TOTAL_VFS=16
+SRIOV_AUTOPROBE_DRIVERS=0
+SRIOV_VF15="max-tx-rate=200 mac=01:23:45:67:89:ab vlans=2"
+SRIOV_VF12="trust=false min-tx-rate=100  vlans=1.200.ad"
+SRIOV_VF3="mac=55:44:33:22:11:00 spoof-check=true"
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected
new file mode 100644
index 00000000..c882c479
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test-sriov-write.cexpected
@@ -0,0 +1,18 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+SRIOV_TOTAL_VFS=64
+SRIOV_AUTOPROBE_DRIVERS=1
+SRIOV_VF2="mac=55:55:55:55:55:55 vlans=3.10.ad;10"
+SRIOV_VF19=spoof-check=true
+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 SR-IOV config"
+UUID=${UUID}
+DEVICE=eth0
+ONBOOT=yes
diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected
new file mode 100644
index 00000000..42608576
--- /dev/null
+++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-test_write_wired_auto_negotiate_on.cexpected
@@ -0,0 +1,15 @@
+TYPE=Ethernet
+PROXY_METHOD=none
+BROWSER_ONLY=no
+ETHTOOL_OPTS="autoneg on ; -K net0 rxvlan off tx on"
+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/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
index 1331391d..70693861 100644
--- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
+++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c
@@ -43,12 +43,14 @@
 #include "nm-setting-pppoe.h"
 #include "nm-setting-ppp.h"
 #include "nm-setting-vpn.h"
+#include "nm-setting-ethtool.h"
 #include "nm-setting-gsm.h"
 #include "nm-setting-cdma.h"
 #include "nm-setting-serial.h"
 #include "nm-setting-vlan.h"
 #include "nm-setting-dcb.h"
 #include "nm-core-internal.h"
+#include "nm-ethtool-utils.h"
 
 #include "NetworkManagerUtils.h"
 
@@ -185,7 +187,8 @@ _assert_expected_content (NMConnection *connection, const char *filename, const
 
 	if (   len_expectd != len_written
 	    || memcmp (content_expectd, content_written, len_expectd) != 0) {
-		if (g_getenv ("NMTST_IFCFG_RH_UPDATE_EXPECTED")) {
+		if (   g_getenv ("NMTST_IFCFG_RH_UPDATE_EXPECTED")
+		    || nm_streq0 (g_getenv ("NM_TEST_REGENERATE"), "1")) {
 			if (uuid) {
 				gs_free char *search = g_strdup_printf ("UUID=%s\n", uuid);
 				const char *s;
@@ -1898,10 +1901,8 @@ test_read_write_802_1X_subj_matches (void)
 	gs_unref_object NMConnection *reread = NULL;
 	NMSetting8021x *s_8021x;
 
-	NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT*peap*");
 	connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-802-1X-subj-matches",
 	                                    NULL, TYPE_ETHERNET, NULL);
-	g_test_assert_expected_messages ();
 
 	/* ===== 802.1x SETTING ===== */
 	s_8021x = nm_connection_get_setting_802_1x (connection);
@@ -1919,16 +1920,12 @@ 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");
 
-	NMTST_EXPECT_NM_WARN ("*missing IEEE_8021X_CA_CERT for EAP method 'peap'; this is insecure!");
 	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR,
 	                        TEST_IFCFG_DIR"/ifcfg-System_test-wired-802-1X-subj-matches.cexpected",
 	                        &testfile);
-	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_NM_WARN ("*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 ();
 
 	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
 
@@ -3117,8 +3114,7 @@ test_read_wifi_wpa_psk_hex (void)
 
 	ssid = nm_setting_wireless_get_ssid (s_wireless);
 	g_assert (ssid);
-	g_assert_cmpint (g_bytes_get_size (ssid), ==, strlen (expected_ssid));
-	g_assert (memcmp (g_bytes_get_data (ssid, NULL), expected_ssid, strlen (expected_ssid)) == 0);
+	g_assert (nm_utils_gbytes_equal_mem (ssid, expected_ssid, strlen (expected_ssid)));
 
 	/* ===== WIRELESS SECURITY SETTING ===== */
 
@@ -3725,6 +3721,7 @@ test_write_wired_auto_negotiate_on (void)
 	gs_unref_object NMConnection *connection = NULL;
 	gs_unref_object NMConnection *reread = NULL;
 	NMSettingWired *s_wired;
+	NMSettingEthtool *s_ethtool;
 	char *val;
 	shvarFile *f;
 
@@ -3734,8 +3731,14 @@ test_write_wired_auto_negotiate_on (void)
 	              NM_SETTING_WIRED_AUTO_NEGOTIATE, TRUE,
 	              NULL);
 
-	_writer_new_connection (connection,
+	s_ethtool = NM_SETTING_ETHTOOL (nm_setting_ethtool_new ());
+	nm_setting_ethtool_set_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_TX, NM_TERNARY_TRUE);
+	nm_setting_ethtool_set_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN, NM_TERNARY_FALSE);
+	nm_connection_add_setting (connection, NM_SETTING (s_ethtool));
+
+	_writer_new_connec_exp (connection,
 	                        TEST_SCRATCH_DIR,
+	                        TEST_IFCFG_DIR"/ifcfg-test_write_wired_auto_negotiate_on.cexpected",
 	                        &testfile);
 
 	f = _svOpenFile (testfile);
@@ -3749,7 +3752,15 @@ test_write_wired_auto_negotiate_on (void)
 
 	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
 
+	nmtst_assert_connection_verifies_without_normalization (reread);
+
 	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
+
+	s_ethtool = NM_SETTING_ETHTOOL (nm_connection_get_setting (reread, NM_TYPE_SETTING_ETHTOOL));
+	g_assert (s_ethtool);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_TX), ==, NM_TERNARY_TRUE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_RXVLAN), ==, NM_TERNARY_FALSE);
+	g_assert_cmpint (nm_setting_ethtool_get_feature (s_ethtool, NM_ETHTOOL_OPTNAME_FEATURE_TXVLAN), ==, NM_TERNARY_DEFAULT);
 }
 
 static void
@@ -4417,6 +4428,67 @@ test_write_wired_dhcp (void)
 }
 
 static void
+test_write_wired_match (void)
+{
+	nmtst_auto_unlinkfile char *testfile = NULL;
+	gs_unref_object NMConnection *connection = NULL;
+	gs_unref_object NMConnection *reread = NULL;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	NMSettingMatch *s_match;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+
+	connection = nm_simple_connection_new ();
+
+	/* Connection setting */
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test Write Wired with Match setting",
+	              NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+	              NULL);
+
+	/* Wired setting */
+	s_wired = (NMSettingWired *) nm_setting_wired_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+	/* IP4 setting */
+	s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+	g_object_set (s_ip4,
+	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+	              NULL);
+
+	/* IP6 setting */
+	s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
+
+	g_object_set (s_ip6,
+	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NULL);
+
+	/* Match setting */
+	s_match = (NMSettingMatch *) nm_setting_match_new ();
+	nm_setting_match_add_interface_name (s_match, "ens*");
+	nm_setting_match_add_interface_name (s_match, "eth 1?");
+	nm_setting_match_add_interface_name (s_match, "!veth*");
+	nm_connection_add_setting (connection, NM_SETTING (s_match));
+
+	nmtst_assert_connection_verifies (connection);
+	_writer_new_connec_exp (connection,
+	                        TEST_SCRATCH_DIR,
+	                        TEST_IFCFG_DIR"/ifcfg-Test_Write_Wired_match.cexpected",
+	                        &testfile);
+	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
+	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
+}
+
+static void
 test_write_wired_dhcp_plus_ip (void)
 {
 	nmtst_auto_unlinkfile char *testfile = NULL;
@@ -9632,6 +9704,153 @@ test_utils_ignore (void)
 	do_test_utils_ignored ("ignored-augtmp", "ifcfg-FooBar" AUGTMP_TAG, TRUE);
 }
 
+/*****************************************************************************/
+
+static void
+test_sriov_read (void)
+{
+	gs_unref_object NMConnection *connection = NULL;
+	NMSettingSriov *s_sriov;
+	NMSriovVF *vf;
+	GVariant *variant;
+	GError *error = NULL;
+	char *str;
+
+	connection = _connection_from_file (TEST_IFCFG_DIR "/ifcfg-test-sriov",
+	                                    NULL, TYPE_ETHERNET,NULL);
+
+	g_assert_cmpstr (nm_connection_get_interface_name (connection), ==, "eth0");
+
+	s_sriov = nm_connection_get_setting_sriov (connection);
+	g_assert (s_sriov);
+
+	g_assert_cmpint (nm_setting_sriov_get_total_vfs (s_sriov), ==, 16);
+	g_assert_cmpint (nm_setting_sriov_get_num_vfs (s_sriov), ==, 3);
+	g_assert_cmpint (nm_setting_sriov_get_autoprobe_drivers (s_sriov), ==, NM_TERNARY_FALSE);
+
+	/* VF 3 */
+	vf = nm_setting_sriov_get_vf (s_sriov, 0);
+	g_assert (vf);
+	g_assert_cmpint (nm_sriov_vf_get_index (vf), ==, 3);
+
+	variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_MAC);
+	g_assert (variant);
+	g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING));
+	g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "55:44:33:22:11:00");
+
+	variant = nm_sriov_vf_get_attribute (vf, NM_SRIOV_VF_ATTRIBUTE_SPOOF_CHECK);
+	g_assert (variant);
+	g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_BOOLEAN));
+	g_assert_cmpint (g_variant_get_boolean (variant), ==, TRUE);
+
+	/* VF 12 */
+	vf = nm_setting_sriov_get_vf (s_sriov, 1);
+	str = nm_utils_sriov_vf_to_str (vf, FALSE, &error);
+	g_assert_no_error (error);
+	g_assert_cmpstr (str, ==, "12 min-tx-rate=100 trust=false vlans=1.200.ad");
+	g_free (str);
+
+	/* VF 15 */
+	vf = nm_setting_sriov_get_vf (s_sriov, 2);
+	str = nm_utils_sriov_vf_to_str (vf, FALSE, &error);
+	g_assert_no_error (error);
+	g_assert_cmpstr (str, ==, "15 mac=01:23:45:67:89:ab max-tx-rate=200 vlans=2");
+	g_free (str);
+}
+
+static void
+test_sriov_write (void)
+{
+	nmtst_auto_unlinkfile char *testfile = NULL;
+	gs_unref_object NMConnection *connection = NULL;
+	gs_unref_object NMConnection *reread = NULL;
+	NMSettingConnection *s_con;
+	NMSettingIPConfig *s_ip4;
+	NMSettingIPConfig *s_ip6;
+	NMSettingWired *s_wired;
+	NMSettingSriov *s_sriov;
+	NMSriovVF *vf;
+	gs_unref_ptrarray GPtrArray *vfs = NULL;
+	NMIPAddress *addr;
+	GError *error = NULL;
+
+	connection = nm_simple_connection_new ();
+
+	/* Connection setting */
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test Write SR-IOV config",
+	              NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
+	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, "eth0",
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+	              NULL);
+
+	/* Wired setting */
+	s_wired = (NMSettingWired *) nm_setting_wired_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+	/* IP4 setting */
+	s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+
+	g_object_set (s_ip4,
+	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
+	              NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.1",
+	              NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
+	              NULL);
+
+	addr = nm_ip_address_new (AF_INET, "1.1.1.3", 24, &error);
+	g_assert_no_error (error);
+	nm_setting_ip_config_add_address (s_ip4, addr);
+	nm_ip_address_unref (addr);
+
+	/* IP6 setting */
+	s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
+
+	g_object_set (s_ip6,
+	              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+	              NULL);
+
+	/* SRIOV setting */
+	s_sriov = (NMSettingSriov *) nm_setting_sriov_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_sriov));
+
+	vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
+
+	vf = nm_utils_sriov_vf_from_str ("2 mac=55:55:55:55:55:55 vlans=3.10.ad;10", &error);
+	nmtst_assert_success (vf, error);
+	g_ptr_array_add (vfs, vf);
+
+	vf = nm_utils_sriov_vf_from_str ("19 spoof-check=true", &error);
+	nmtst_assert_success (vf, error);
+	g_ptr_array_add (vfs, vf);
+
+	g_object_set (s_sriov,
+	              NM_SETTING_SRIOV_TOTAL_VFS, 64,
+	              NM_SETTING_SRIOV_VFS, vfs,
+	              NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, NM_TERNARY_TRUE,
+	              NULL);
+
+	nm_connection_add_setting (connection, nm_setting_proxy_new ());
+
+	nmtst_assert_connection_verifies_without_normalization (connection);
+
+	_writer_new_connec_exp (connection,
+	                        TEST_SCRATCH_DIR,
+	                        TEST_IFCFG_DIR "/ifcfg-test-sriov-write.cexpected",
+	                        &testfile);
+
+	reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
+
+	nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
+}
+
+/*****************************************************************************/
+
 static void
 test_tc_read (void)
 {
@@ -9907,6 +10126,7 @@ int main (int argc, char **argv)
 	g_test_add_func (TPATH "wired/write/dhcp", test_write_wired_dhcp);
 	g_test_add_func (TPATH "wired/write-dhcp-plus-ip", test_write_wired_dhcp_plus_ip);
 	g_test_add_func (TPATH "wired/write/dhcp-8021x-peap-mschapv2", test_write_wired_dhcp_8021x_peap_mschapv2);
+	g_test_add_func (TPATH "wired/write/match", test_write_wired_match);
 
 #define _add_test_write_wired_8021x_tls(testpath, scheme, flags) \
 	nmtst_add_test_func (testpath, test_write_wired_8021x_tls, GINT_TO_POINTER (scheme), GINT_TO_POINTER (flags))
@@ -10033,6 +10253,9 @@ int main (int argc, char **argv)
 	g_test_add_func (TPATH "utils/path", test_utils_path);
 	g_test_add_func (TPATH "utils/ignore", test_utils_ignore);
 
+	g_test_add_func (TPATH "sriov/read", test_sriov_read);
+	g_test_add_func (TPATH "sriov/write", test_sriov_write);
+
 	g_test_add_func (TPATH "tc/read", test_tc_read);
 	g_test_add_func (TPATH "tc/write", test_tc_write);
 
diff --git a/src/settings/plugins/ifupdown/meson.build b/src/settings/plugins/ifupdown/meson.build
index fd028f4d..826c7458 100644
--- a/src/settings/plugins/ifupdown/meson.build
+++ b/src/settings/plugins/ifupdown/meson.build
@@ -8,13 +8,10 @@ deps = [
   nm_dep
 ]
 
-cflags = '-DSYSCONFDIR="@0@"'.format(nm_sysconfdir)
-
 libnms_ifupdown_core = static_library(
   'nms-ifupdown-core',
   sources: sources,
   dependencies: deps,
-  c_args: cflags
 )
 
 sources = files(
@@ -26,7 +23,6 @@ libnm_settings_plugin_ifupdown = shared_module(
   'nm-settings-plugin-ifupdown',
   sources: sources,
   dependencies: deps,
-  c_args: cflags,
   link_with: libnms_ifupdown_core,
   link_args: ldflags_linker_script_settings,
   link_depends: linker_script_settings,
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c b/src/settings/plugins/ifupdown/nms-ifupdown-connection.c
index c3d231fe..1b817044 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-connection.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-connection.c
@@ -49,10 +49,20 @@ G_DEFINE_TYPE (NMIfupdownConnection, nm_ifupdown_connection, NM_TYPE_SETTINGS_CO
 
 /*****************************************************************************/
 
+#define _NMLOG_PREFIX_NAME      "ifupdown"
+#define _NMLOG_DOMAIN           LOGD_SETTINGS
+#define _NMLOG(level, ...) \
+    nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
+            "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+            _NMLOG_PREFIX_NAME": " \
+            _NM_UTILS_MACRO_REST (__VA_ARGS__))
+
+/*****************************************************************************/
+
 static gboolean
 supports_secrets (NMSettingsConnection *connection, const char *setting_name)
 {
-	nm_log_info (LOGD_SETTINGS, "supports_secrets() for setting_name: '%s'", setting_name);
+	_LOGI ("supports_secrets() for setting_name: '%s'", setting_name);
 
 	return (strcmp (setting_name, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME) == 0);
 }
@@ -64,26 +74,27 @@ nm_ifupdown_connection_init (NMIfupdownConnection *connection)
 {
 }
 
-NMIfupdownConnection*
+NMIfupdownConnection *
 nm_ifupdown_connection_new (if_block *block)
 {
-	GObject *object;
+	NMIfupdownConnection *connection;
 	GError *error = NULL;
 
 	g_return_val_if_fail (block != NULL, NULL);
 
-	object = g_object_new (NM_TYPE_IFUPDOWN_CONNECTION, NULL);
+	connection = g_object_new (NM_TYPE_IFUPDOWN_CONNECTION, NULL);
 
-	if (!ifupdown_update_connection_from_if_block (NM_CONNECTION (object), block, &error)) {
-		nm_log_warn (LOGD_SETTINGS, "%s.%d - invalid connection read from /etc/network/interfaces: %s",
-		             __FILE__,
-		             __LINE__,
-		             error->message);
-		g_object_unref (object);
+	/* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
+	if (!ifupdown_update_connection_from_if_block (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection)),
+	                                               block,
+	                                               &error)) {
+		_LOGW ("invalid connection read from /etc/network/interfaces: %s",
+		       error->message);
+		g_object_unref (connection);
 		return NULL;
 	}
 
-	return (NMIfupdownConnection *) object;
+	return connection;
 }
 
 static void
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
index 94a65ecb..d926829f 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.c
@@ -32,78 +32,90 @@
 
 #include "nm-utils.h"
 
-if_block* first;
-if_block* last;
+/*****************************************************************************/
 
-if_data* last_data;
+static void _ifparser_source (if_parser *parser, const char *path, const char *en_dir, int quiet, int dir);
 
-void add_block(const char *type, const char* name)
+/*****************************************************************************/
+
+#define _NMLOG_PREFIX_NAME      "ifupdown"
+#define _NMLOG_DOMAIN           LOGD_SETTINGS
+#define _NMLOG(level, ...) \
+    nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
+            "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+            _NMLOG_PREFIX_NAME": " \
+            _NM_UTILS_MACRO_REST (__VA_ARGS__))
+
+/*****************************************************************************/
+
+static void
+add_block (if_parser *parser, const char *type, const char* name)
 {
-	if_block *ret = g_slice_new0 (struct _if_block);
-	ret->name = g_strdup(name);
-	ret->type = g_strdup(type);
-	if (first == NULL)
-		first = last = ret;
-	else
-	{
-		last->next = ret;
-		last = ret;
-	}
-	last_data = NULL;
+	if_block *ifb;
+	gsize l_type, l_name;
+
+	l_type = strlen (type) + 1;
+	l_name = strlen (name) + 1;
+
+	ifb = g_malloc (sizeof (if_block) + l_type + l_name);
+	memcpy ((char *) ifb->name, name, l_name);
+	ifb->type = &ifb->name[l_name];
+	memcpy ((char *) ifb->type, type, l_type);
+	c_list_init (&ifb->data_lst_head);
+	c_list_link_tail (&parser->block_lst_head, &ifb->block_lst);
 }
 
-void add_data(const char *key,const char *data)
+static void
+add_data (if_parser *parser, const char *key, const char *data)
 {
-	if_data *ret;
+	if_block *last_block;
+	if_data *ifd;
 	char *idx;
+	gsize l_key, l_data;
+
+	last_block = c_list_last_entry (&parser->block_lst_head, if_block, block_lst);
 
 	/* Check if there is a block where we can attach our data */
-	if (first == NULL)
+	if (!last_block)
 		return;
 
-	ret = g_slice_new0 (struct _if_data);
-	ret->key = g_strdup(key);
+	l_key = strlen (key) + 1;
+	l_data = strlen (data) + 1;
+
+	ifd = g_malloc (sizeof (if_data) + l_key + l_data);
+	memcpy ((char *) ifd->key, key, l_key);
+	ifd->data = &ifd->key[l_key];
+	memcpy ((char *) ifd->data, data, l_data);
 
 	/* Normalize keys. Convert '_' to '-', as ifupdown accepts both variants.
 	 * When querying keys via ifparser_getkey(), use '-'. */
-	while ((idx = strrchr(ret->key, '_'))) {
-		*idx = '-';
-	}
-	ret->data = g_strdup(data);
+	idx = (char *) ifd->key;
+	while ((idx = strchr (idx, '_')))
+		*(idx++) = '-';
 
-	if (last->info == NULL)
-	{
-		last->info = ret;
-		last_data = ret;
-	}
-	else
-	{
-		last_data->next = ret;
-		last_data = last_data->next;
-	}
+	c_list_link_tail (&last_block->data_lst_head, &ifd->data_lst);
 }
 
 /* join values in src with spaces into dst;  dst needs to be large enough */
-static char *join_values_with_spaces(char *dst, char **src)
+static char *
+join_values_with_spaces (char *dst, char **src)
 {
 	if (dst != NULL) {
 		*dst = '\0';
 		if (src != NULL && *src != NULL) {
-			strcat(dst, *src);
+			strcat (dst, *src);
 
 			for (src++; *src != NULL; src++) {
-				strcat(dst, " ");
-				strcat(dst, *src);
+				strcat (dst, " ");
+				strcat (dst, *src);
 			}
 		}
 	}
-	return(dst);
+	return (dst);
 }
 
-static void _ifparser_source (const char *path, const char *en_dir, int quiet, int dir);
-
 static void
-_recursive_ifparser (const char *eni_file, int quiet)
+_recursive_ifparser (if_parser *parser, const char *eni_file, int quiet)
 {
 	FILE *inp;
 	char line[255];
@@ -114,36 +126,35 @@ _recursive_ifparser (const char *eni_file, int quiet)
 	/* Check if interfaces file exists and open it */
 	if (!g_file_test (eni_file, G_FILE_TEST_EXISTS)) {
 		if (!quiet)
-			nm_log_warn (LOGD_SETTINGS, "interfaces file %s doesn't exist\n", eni_file);
+			_LOGW ("interfaces file %s doesn't exist\n", eni_file);
 		return;
 	}
 	inp = fopen (eni_file, "re");
 	if (inp == NULL) {
 		if (!quiet)
-			nm_log_warn (LOGD_SETTINGS, "Can't open %s\n", eni_file);
+			_LOGW ("Can't open %s\n", eni_file);
 		return;
 	}
 	if (!quiet)
-		nm_log_info (LOGD_SETTINGS, "      interface-parser: parsing file %s\n", eni_file);
+		_LOGI ("      interface-parser: parsing file %s\n", eni_file);
 
-	while (!feof(inp))
-	{
+	while (!feof (inp)) {
 		char *token[128]; /* 255 chars can only be split into 127 tokens */
 		char value[255];  /* large enough to join previously split tokens */
 		char *safeptr;
 		int toknum;
 		int len = 0;
 
-		char *ptr = fgets(line+offs, 255-offs, inp);
+		char *ptr = fgets (line+offs, 255-offs, inp);
 		if (ptr == NULL)
 			break;
 
-		len = strlen(line);
+		len = strlen (line);
 		/* skip over-long lines */
-		if (!feof(inp) && len > 0 &&  line[len-1] != '\n') {
+		if (!feof (inp) && len > 0 &&  line[len-1] != '\n') {
 			if (!skip_long_line) {
 				if (!quiet)
-					nm_log_warn (LOGD_SETTINGS, "Skipping over-long-line '%s...'\n", line);
+					_LOGW ("Skipping over-long-line '%s...'\n", line);
 			}
 			skip_long_line = 1;
 			continue;
@@ -170,9 +181,9 @@ _recursive_ifparser (const char *eni_file, int quiet)
 
 #define SPACES " \t"
 		/* tokenize input; */
-		for (toknum = 0, token[toknum] = strtok_r(line, SPACES, &safeptr);
+		for (toknum = 0, token[toknum] = strtok_r (line, SPACES, &safeptr);
 		     token[toknum] != NULL;
-		     toknum++, token[toknum] = strtok_r(NULL, SPACES, &safeptr))
+		     toknum++, token[toknum] = strtok_r (NULL, SPACES, &safeptr))
 			;
 
 		/* ignore comments and empty lines */
@@ -181,8 +192,8 @@ _recursive_ifparser (const char *eni_file, int quiet)
 
 		if (toknum < 2) {
 			if (!quiet) {
-				nm_log_warn (LOGD_SETTINGS, "Can't parse interface line '%s'\n",
-				             join_values_with_spaces(value, token));
+				_LOGW ("Can't parse interface line '%s'\n",
+				       join_values_with_spaces (value, token));
 			}
 			skip_to_block = 1;
 			continue;
@@ -193,71 +204,71 @@ _recursive_ifparser (const char *eni_file, int quiet)
 		 * Create a block for each of them except source and source-directory.  */
 
 		/* iface stanza takes at least 3 parameters */
-		if (strcmp(token[0], "iface") == 0) {
+		if (nm_streq (token[0], "iface")) {
 			if (toknum < 4) {
 				if (!quiet) {
-					nm_log_warn (LOGD_SETTINGS, "Can't parse iface line '%s'\n",
-					             join_values_with_spaces(value, token));
+					_LOGW ("Can't parse iface line '%s'\n",
+					       join_values_with_spaces (value, token));
 				}
 				continue;
 			}
-			add_block(token[0], token[1]);
+			add_block (parser, token[0], token[1]);
 			skip_to_block = 0;
-			add_data(token[2], join_values_with_spaces(value, token + 3));
+			add_data (parser, token[2], join_values_with_spaces (value, token + 3));
 		}
 		/* auto and allow-auto stanzas are equivalent,
 		 * both can take multiple interfaces as parameters: add one block for each */
-		else if (strcmp(token[0], "auto") == 0 ||
-			 strcmp(token[0], "allow-auto") == 0) {
+		else if (NM_IN_STRSET (token[0], "auto", "allow-auto")) {
 			int i;
+
 			for (i = 1; i < toknum; i++)
-				add_block("auto", token[i]);
+				add_block (parser, "auto", token[i]);
 			skip_to_block = 0;
 		}
-		else if (strcmp(token[0], "mapping") == 0) {
-			add_block(token[0], join_values_with_spaces(value, token + 1));
+		else if (nm_streq (token[0], "mapping")) {
+			add_block (parser, token[0], join_values_with_spaces (value, token + 1));
 			skip_to_block = 0;
 		}
 		/* allow-* can take multiple interfaces as parameters: add one block for each */
-		else if (strncmp(token[0],"allow-",6) == 0) {
+		else if (g_str_has_prefix (token[0], "allow-")) {
 			int i;
 			for (i = 1; i < toknum; i++)
-				add_block(token[0], token[i]);
+				add_block (parser, token[0], token[i]);
 			skip_to_block = 0;
 		}
 		/* source and source-directory stanzas take one or more paths as parameters */
-		else if (strcmp (token[0], "source") == 0 || strcmp (token[0], "source-directory") == 0) {
+		else if (NM_IN_STRSET (token[0], "source", "source-directory")) {
 			int i;
 			char *en_dir;
 
 			skip_to_block = 0;
 			en_dir = g_path_get_dirname (eni_file);
 			for (i = 1; i < toknum; ++i) {
-				if (strcmp (token[0], "source-directory") == 0)
-					_ifparser_source (token[i], en_dir, quiet, TRUE);
+				if (nm_streq (token[0], "source-directory"))
+					_ifparser_source (parser, token[i], en_dir, quiet, TRUE);
 				else
-					_ifparser_source (token[i], en_dir, quiet, FALSE);
+					_ifparser_source (parser, token[i], en_dir, quiet, FALSE);
 			}
 			g_free (en_dir);
 		}
 		else {
 			if (skip_to_block) {
 				if (!quiet) {
-					nm_log_warn (LOGD_SETTINGS, "ignoring out-of-block data '%s'\n",
-					             join_values_with_spaces(value, token));
+					_LOGW ("ignoring out-of-block data '%s'\n",
+					       join_values_with_spaces (value, token));
 				}
 			} else
-				add_data(token[0], join_values_with_spaces(value, token + 1));
+				add_data (parser, token[0], join_values_with_spaces (value, token + 1));
 		}
 	}
-	fclose(inp);
+	fclose (inp);
 
 	if (!quiet)
-		nm_log_info (LOGD_SETTINGS, "      interface-parser: finished parsing file %s\n", eni_file);
+		_LOGI ("      interface-parser: finished parsing file %s\n", eni_file);
 }
 
 static void
-_ifparser_source (const char *path, const char *en_dir, int quiet, int dir)
+_ifparser_source (if_parser *parser, const char *path, const char *en_dir, int quiet, int dir)
 {
 	char *abs_path;
 	const char *item;
@@ -272,132 +283,128 @@ _ifparser_source (const char *path, const char *en_dir, int quiet, int dir)
 		abs_path = g_build_filename (en_dir, path, NULL);
 
 	if (!quiet)
-		nm_log_info (LOGD_SETTINGS, "      interface-parser: source line includes interfaces file(s) %s\n", abs_path);
+		_LOGI ("      interface-parser: source line includes interfaces file(s) %s\n", abs_path);
 
 	/* ifupdown uses WRDE_NOCMD for wordexp. */
 	if (wordexp (abs_path, &we, WRDE_NOCMD)) {
 		if (!quiet)
-			nm_log_warn (LOGD_SETTINGS, "word expansion for %s failed\n", abs_path);
+			_LOGW ("word expansion for %s failed\n", abs_path);
 	} else {
 		for (i = 0; i < we.we_wordc; i++) {
 			if (dir) {
 				source_dir = g_dir_open (we.we_wordv[i], 0, &error);
 				if (!source_dir) {
 					if (!quiet) {
-						nm_log_warn (LOGD_SETTINGS, "Failed to open directory %s: %s",
-						             we.we_wordv[i], error->message);
+						_LOGW ("Failed to open directory %s: %s",
+						       we.we_wordv[i], error->message);
 					}
 					g_clear_error (&error);
 				} else {
 					while ((item = g_dir_read_name (source_dir)))
-						_ifparser_source (item, we.we_wordv[i], quiet, FALSE);
+						_ifparser_source (parser, item, we.we_wordv[i], quiet, FALSE);
 					g_dir_close (source_dir);
 				}
 			} else
-				_recursive_ifparser (we.we_wordv[i], quiet);
+				_recursive_ifparser (parser, we.we_wordv[i], quiet);
 		}
 		wordfree (&we);
 	}
 	g_free (abs_path);
 }
 
-void ifparser_init (const char *eni_file, int quiet)
+if_parser *
+ifparser_parse (const char *eni_file, int quiet)
 {
-	first = last = NULL;
-	_recursive_ifparser (eni_file, quiet);
+	if_parser *parser;
+
+	parser = g_slice_new (if_parser);
+	c_list_init (&parser->block_lst_head);
+	_recursive_ifparser (parser, eni_file, quiet);
+	return parser;
 }
 
-void _destroy_data(if_data *ifd)
+static void
+_destroy_data (if_data *ifd)
 {
-	if (ifd == NULL)
-		return;
-	_destroy_data(ifd->next);
-	g_free(ifd->key);
-	g_free(ifd->data);
-	g_slice_free(struct _if_data, ifd);
-	return;
+	c_list_unlink_stale (&ifd->data_lst);
+	g_free (ifd);
 }
 
-void _destroy_block(if_block* ifb)
+static void
+_destroy_block (if_block* ifb)
 {
-	if (ifb == NULL)
-		return;
-	_destroy_block(ifb->next);
-	_destroy_data(ifb->info);
-	g_free(ifb->name);
-	g_free(ifb->type);
-	g_slice_free(struct _if_block, ifb);
-	return;
+	if_data *ifd;
+
+	while ((ifd = c_list_first_entry (&ifb->data_lst_head, if_data, data_lst)))
+		_destroy_data (ifd);
+	c_list_unlink_stale (&ifb->block_lst);
+	g_free (ifb);
 }
 
-void ifparser_destroy(void)
+void
+ifparser_destroy (if_parser *parser)
 {
-	_destroy_block(first);
-	first = last = NULL;
+	if_block *ifb;
+
+	while ((ifb = c_list_first_entry (&parser->block_lst_head, if_block, block_lst)))
+		_destroy_block (ifb);
+	g_slice_free (if_parser, parser);
 }
 
-if_block *ifparser_getfirst(void)
+if_block *
+ifparser_getfirst (if_parser *parser)
 {
-	return first;
+	return c_list_first_entry (&parser->block_lst_head, if_block, block_lst);
 }
 
-int ifparser_get_num_blocks(void)
+guint
+ifparser_get_num_blocks (if_parser *parser)
 {
-	int i = 0;
-	if_block *iter = first;
-
-	while (iter) {
-		i++;
-		iter = iter->next;
-	}
-	return i;
+	return c_list_length (&parser->block_lst_head);
 }
 
-if_block *ifparser_getif(const char* iface)
+if_block *
+ifparser_getif (if_parser *parser, const char* iface)
 {
-	if_block *curr = first;
-	while(curr!=NULL)
-	{
-		if (strcmp(curr->type,"iface")==0 && strcmp(curr->name,iface)==0)
-			return curr;
-		curr = curr->next;
+	if_block *ifb;
+
+	c_list_for_each_entry (ifb, &parser->block_lst_head, block_lst) {
+		if (   nm_streq (ifb->type, "iface")
+		    && nm_streq (ifb->name, iface))
+			return ifb;
 	}
 	return NULL;
 }
 
-const char *ifparser_getkey(if_block* iface, const char *key)
+static if_data *
+ifparser_findkey (if_block* iface, const char *key)
 {
-	if_data *curr = iface->info;
-	while(curr!=NULL)
-	{
-		if (strcmp(curr->key,key)==0)
-			return curr->data;
-		curr = curr->next;
+	if_data *ifd;
+
+	c_list_for_each_entry (ifd, &iface->data_lst_head, data_lst) {
+		if (nm_streq (ifd->key, key))
+			return ifd;
 	}
 	return NULL;
 }
 
-gboolean
-ifparser_haskey(if_block* iface, const char *key)
+const char *
+ifparser_getkey (if_block* iface, const char *key)
 {
-	if_data *curr = iface->info;
+	if_data *ifd;
 
-	while (curr != NULL) {
-		if (strcmp (curr->key, key) == 0)
-			return TRUE;
-		curr = curr->next;
-	}
-	return FALSE;
+	ifd = ifparser_findkey (iface, key);
+	return ifd ? ifd->data : NULL;
 }
 
-int ifparser_get_num_info(if_block* iface)
+gboolean
+ifparser_haskey (if_block* iface, const char *key)
 {
-	int i = 0;
-	if_data *iter = iface->info;
+	return !!ifparser_findkey (iface, key);
+}
 
-	while (iter) {
-		i++;
-		iter = iter->next;
-	}
-	return i;
+guint
+ifparser_get_num_info (if_block* iface)
+{
+	return c_list_length (&iface->data_lst_head);
 }
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h
index 7e6c8e34..f367f626 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-interface-parser.h
@@ -23,33 +23,37 @@
 #ifndef _INTERFACE_PARSER_H
 #define _INTERFACE_PARSER_H
 
-typedef struct _if_data
-{
-	char *key;
-	char *data;
-	struct _if_data *next;
+#include "c-list/src/c-list.h"
+
+typedef struct {
+	CList data_lst;
+	const char *data;
+	const char key[];
 } if_data;
 
-typedef struct _if_block
-{
-	char *type;
-	char *name;
-	if_data *info;
-	struct _if_block *next;
+typedef struct {
+	CList block_lst;
+	CList data_lst_head;
+	const char *type;
+	const char name[];
 } if_block;
 
-void ifparser_init(const char *eni_file, int quiet);
-void ifparser_destroy(void);
+typedef struct {
+	CList block_lst_head;
+} if_parser;
+
+if_parser *ifparser_parse (const char *eni_file, int quiet);
+
+void ifparser_destroy (if_parser *parser);
+NM_AUTO_DEFINE_FCN0 (if_parser *, _nm_auto_ifparser, ifparser_destroy);
+#define nm_auto_ifparser nm_auto(_nm_auto_ifparser)
+
+if_block *ifparser_getif (if_parser *parser, const char* iface);
+if_block *ifparser_getfirst (if_parser *parser);
+const char *ifparser_getkey (if_block* iface, const char *key);
+gboolean ifparser_haskey (if_block* iface, const char *key);
 
-if_block *ifparser_getif(const char* iface);
-if_block *ifparser_getfirst(void);
-const char *ifparser_getkey(if_block* iface, const char *key);
-gboolean ifparser_haskey(if_block* iface, const char *key);
-int ifparser_get_num_blocks(void);
-int ifparser_get_num_info(if_block* iface);
+guint ifparser_get_num_blocks (if_parser *parser);
+guint ifparser_get_num_info (if_block* iface);
 
-void add_block(const char *type, const char* name);
-void add_data(const char *key,const char *data);
-void _destroy_data(if_data *ifd);
-void _destroy_block(if_block* ifb);
 #endif
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
index 884c6591..369fa70d 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-parser.c
@@ -37,42 +37,64 @@
 #include "nms-ifupdown-plugin.h"
 #include "nms-ifupdown-parser.h"
 
-static const gchar*
+/*****************************************************************************/
+
+#define _NMLOG_PREFIX_NAME      "ifupdown"
+#define _NMLOG_DOMAIN           LOGD_SETTINGS
+#define _NMLOG(level, ...) \
+    nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
+            "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+            _NMLOG_PREFIX_NAME": " \
+            _NM_UTILS_MACRO_REST (__VA_ARGS__))
+
+/*****************************************************************************/
+
+#define _str_has_prefix(val, prefix, require_suffix) \
+	({ \
+		const char *_val = (val); \
+		\
+		   (strncmp (_val, ""prefix"", NM_STRLEN (prefix)) == 0) \
+		&& (   !(require_suffix) \
+		    || _val[NM_STRLEN (prefix)] != '\0'); \
+	})
+
+static const char*
 _ifupdownplugin_guess_connection_type (if_block *block)
 {
-	if_data *curr = block->info;
-	const gchar* ret_type = NULL;
-	const gchar* value = ifparser_getkey(block, "inet");
-	if(value && !strcmp("ppp", value)) {
-		ret_type = NM_SETTING_PPP_SETTING_NAME;
-	}
+	const char *ret_type = NULL;
 
-	while(!ret_type && curr) {
-		if(!strncmp("wireless-", curr->key, strlen("wireless-")) ||
-		   !strncmp("wpa-", curr->key, strlen("wpa-"))) {
-			ret_type = NM_SETTING_WIRELESS_SETTING_NAME;
+	if(nm_streq0 (ifparser_getkey (block, "inet"), "ppp"))
+		ret_type = NM_SETTING_PPP_SETTING_NAME;
+	else {
+		if_data *ifb;
+
+		c_list_for_each_entry (ifb, &block->data_lst_head, data_lst) {
+			if (   _str_has_prefix (ifb->key, "wireless-", FALSE)
+			    || _str_has_prefix (ifb->key, "wpa-", FALSE)) {
+				ret_type = NM_SETTING_WIRELESS_SETTING_NAME;
+				break;
+			}
 		}
-		curr = curr->next;
+		if(!ret_type)
+			ret_type = NM_SETTING_WIRED_SETTING_NAME;
 	}
 
-	if(!ret_type)
-		ret_type = NM_SETTING_WIRED_SETTING_NAME;
-
-	nm_log_info (LOGD_SETTINGS, "guessed connection type (%s) = %s", block->name, ret_type);
+	_LOGI ("guessed connection type (%s) = %s", block->name, ret_type);
 	return ret_type;
 }
 
 struct _Mapping {
-	const gchar *domain;
+	const char *domain;
 	const gpointer target;
 };
 
 static gpointer
-map_by_mapping(struct _Mapping *mapping, const gchar *key)
+map_by_mapping (struct _Mapping *mapping, const char *key)
 {
 	struct _Mapping *curr = mapping;
-	while(curr->domain) {
-		if(!strcmp(curr->domain, key))
+
+	while (curr->domain) {
+		if (nm_streq (curr->domain, key))
 			return curr->target;
 		curr++;
 	}
@@ -80,14 +102,11 @@ map_by_mapping(struct _Mapping *mapping, const gchar *key)
 }
 
 static void
-update_wireless_setting_from_if_block(NMConnection *connection,
-							   if_block *block)
+update_wireless_setting_from_if_block (NMConnection *connection,
+                                       if_block *block)
 {
-	gint wpa_l= strlen("wpa-");
-	gint wireless_l= strlen("wireless-");
-
-	if_data *curr = block->info;
-	const gchar* value = ifparser_getkey (block, "inet");
+	if_data *curr;
+	const char *value = ifparser_getkey (block, "inet");
 	struct _Mapping mapping[] = {
 		{"ssid", "ssid"},
 		{"essid", "ssid"},
@@ -97,27 +116,26 @@ update_wireless_setting_from_if_block(NMConnection *connection,
 
 	NMSettingWireless *wireless_setting = NULL;
 
-	if(value && !strcmp("ppp", value)) {
+	if (nm_streq0 (value, "ppp"))
 		return;
-	}
 
-	nm_log_info (LOGD_SETTINGS, "update wireless settings (%s).", block->name);
-	wireless_setting = NM_SETTING_WIRELESS(nm_setting_wireless_new());
+	_LOGI ("update wireless settings (%s).", block->name);
+	wireless_setting = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
+
+	c_list_for_each_entry (curr, &block->data_lst_head, data_lst) {
+		if (_str_has_prefix (curr->key, "wireless-", TRUE)) {
+			const char* newkey = map_by_mapping (mapping, curr->key + NM_STRLEN ("wireless-"));
 
-	while(curr) {
-		if(strlen(curr->key) > wireless_l &&
-		   !strncmp("wireless-", curr->key, wireless_l)) {
-			const gchar* newkey = map_by_mapping(mapping, curr->key+wireless_l);
-			nm_log_info (LOGD_SETTINGS, "wireless setting key: %s='%s'", newkey, curr->data);
-			if(newkey && !strcmp("ssid", newkey)) {
+			_LOGI ("wireless setting key: %s='%s'", newkey, curr->data);
+			if (nm_streq0 (newkey, "ssid")) {
 				GBytes *ssid;
-				gint len = strlen(curr->data);
+				int len = strlen (curr->data);
 
 				ssid = g_bytes_new (curr->data, len);
 				g_object_set (wireless_setting, NM_SETTING_WIRELESS_SSID, ssid, NULL);
 				g_bytes_unref (ssid);
-				nm_log_info (LOGD_SETTINGS, "setting wireless ssid = %d", len);
-			} else if(newkey && !strcmp("mode", newkey)) {
+				_LOGI ("setting wireless ssid = %d", len);
+			} else if (nm_streq0 (newkey, "mode")) {
 				if (!g_ascii_strcasecmp (curr->data, "Managed") || !g_ascii_strcasecmp (curr->data, "Auto"))
 					g_object_set (wireless_setting, NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, NULL);
 				else if (!g_ascii_strcasecmp (curr->data, "Ad-Hoc"))
@@ -125,39 +143,37 @@ update_wireless_setting_from_if_block(NMConnection *connection,
 				else if (!g_ascii_strcasecmp (curr->data, "Master"))
 					g_object_set (wireless_setting, NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_AP, NULL);
 				else
-					nm_log_warn (LOGD_SETTINGS, "Invalid mode '%s' (not 'Ad-Hoc', 'Ap', 'Managed', or 'Auto')", curr->data);
+					_LOGW ("Invalid mode '%s' (not 'Ad-Hoc', 'Ap', 'Managed', or 'Auto')", curr->data);
 			} else {
-				g_object_set(wireless_setting,
-					   newkey, curr->data,
-					   NULL);
+				g_object_set (wireless_setting,
+				              newkey, curr->data,
+				              NULL);
 			}
-		} else if(strlen(curr->key) > wpa_l &&
-				!strncmp("wpa-", curr->key, wpa_l)) {
-			const gchar* newkey = map_by_mapping(mapping, curr->key+wpa_l);
+		} else if (_str_has_prefix (curr->key, "wpa-", TRUE)) {
+			const char* newkey = map_by_mapping (mapping, curr->key + NM_STRLEN ("wpa-"));
 
-			if(newkey && !strcmp("ssid", newkey)) {
+			if (nm_streq0 (newkey, "ssid")) {
 				GBytes *ssid;
-				gint len = strlen(curr->data);
+				int len = strlen (curr->data);
 
 				ssid = g_bytes_new (curr->data, len);
 				g_object_set (wireless_setting, NM_SETTING_WIRELESS_SSID, ssid, NULL);
 				g_bytes_unref (ssid);
-				nm_log_info (LOGD_SETTINGS, "setting wpa ssid = %d", len);
-			} else if(newkey) {
+				_LOGI ("setting wpa ssid = %d", len);
+			} else if (newkey) {
 
-				g_object_set(wireless_setting,
-						   newkey, curr->data,
-						   NULL);
-				nm_log_info (LOGD_SETTINGS, "setting wpa newkey(%s)=data(%s)", newkey, curr->data);
+				g_object_set (wireless_setting,
+				              newkey, curr->data,
+				              NULL);
+				_LOGI ("setting wpa newkey(%s)=data(%s)", newkey, curr->data);
 			}
 		}
-		curr = curr->next;
 	}
-	nm_connection_add_setting(connection, (NMSetting*) wireless_setting);
+	nm_connection_add_setting (connection, (NMSetting*) wireless_setting);
 }
 
-typedef gchar* (*IfupdownStrDupeFunc) (gpointer value, gpointer data);
-typedef gpointer (*IfupdownStrToTypeFunc) (const gchar* value);
+typedef char* (*IfupdownStrDupeFunc) (gconstpointer value, gpointer data);
+typedef gpointer (*IfupdownStrToTypeFunc) (const char* value);
 
 static char*
 normalize_dupe_wireless_key (gpointer value, gpointer data) {
@@ -177,7 +193,7 @@ normalize_dupe_wireless_key (gpointer value, gpointer data) {
 		result_cur += next - delim;
 		delim = next + 1;
 	}
-	if (*delim && strlen (valuec) > GPOINTER_TO_UINT(delim - valuec)) {
+	if (*delim && strlen (valuec) > GPOINTER_TO_UINT (delim - valuec)) {
 		strncpy (result_cur, delim, endc - delim);
 		result_cur += endc - delim;
 	}
@@ -187,12 +203,12 @@ normalize_dupe_wireless_key (gpointer value, gpointer data) {
 
 static char*
 normalize_dupe (gpointer value, gpointer data) {
-	return g_strdup(value);
+	return g_strdup (value);
 }
 
 static char*
 normalize_tolower (gpointer value, gpointer data) {
-	return g_ascii_strdown(value, -1);
+	return g_ascii_strdown (value, -1);
 }
 
 static char *normalize_psk (gpointer value, gpointer data)
@@ -203,25 +219,25 @@ static char *normalize_psk (gpointer value, gpointer data)
 }
 
 static gpointer
-string_to_gpointerint(const gchar* data)
+string_to_gpointerint (const char* data)
 {
-	gint result = (gint) strtol (data, NULL, 10);
-	return GINT_TO_POINTER(result);
+	int result = (int) strtol (data, NULL, 10);
+	return GINT_TO_POINTER (result);
 }
 
 static gpointer
-string_to_glist_of_strings(const gchar* data)
+string_to_glist_of_strings (const char* data)
 {
 	GSList *ret = NULL;
-	gchar *string = (gchar*) data;
-	while(string) {
-		gchar* next = NULL;
-		if( (next = strchr(string, ' '))  ||
-		    (next = strchr(string, '\t')) ||
-		    (next = strchr(string, '\0')) ) {
-
-			gchar *part = g_strndup(string, (next - string));
-			ret = g_slist_append(ret, part);
+	char *string = (char*) data;
+	while (string) {
+		char* next = NULL;
+		if ( (next = strchr (string, ' '))  ||
+		     (next = strchr (string, '\t')) ||
+		     (next = strchr (string, '\0')) ) {
+
+			char *part = g_strndup (string, (next - string));
+			ret = g_slist_append (ret, part);
 			if (*next)
 				string = next+1;
 			else
@@ -234,19 +250,17 @@ string_to_glist_of_strings(const gchar* data)
 }
 
 static void
-slist_free_all(gpointer slist)
+slist_free_all (gpointer slist)
 {
 	g_slist_free_full ((GSList *) slist, g_free);
 }
 
 static void
-update_wireless_security_setting_from_if_block(NMConnection *connection,
-									  if_block *block)
+update_wireless_security_setting_from_if_block (NMConnection *connection,
+                                                if_block *block)
 {
-	gint wpa_l= strlen("wpa-");
-	gint wireless_l= strlen("wireless-");
-	if_data *curr = block->info;
-	const gchar* value = ifparser_getkey (block, "inet");
+	if_data *curr;
+	const char* value = ifparser_getkey (block, "inet");
 	struct _Mapping mapping[] = {
 		{"psk", "psk"},
 		{"identity", "leap-username"},
@@ -302,101 +316,93 @@ update_wireless_security_setting_from_if_block(NMConnection *connection,
 	NMSettingWireless *s_wireless;
 	gboolean security = FALSE;
 
-	if(value && !strcmp("ppp", value)) {
+	if (nm_streq0 (value, "ppp"))
 		return;
-	}
-
-	s_wireless = nm_connection_get_setting_wireless(connection);
-	g_return_if_fail(s_wireless);
 
-	nm_log_info (LOGD_SETTINGS, "update wireless security settings (%s).", block->name);
-	wireless_security_setting =
-		NM_SETTING_WIRELESS_SECURITY(nm_setting_wireless_security_new());
+	s_wireless = nm_connection_get_setting_wireless (connection);
+	g_return_if_fail (s_wireless);
 
-	while(curr) {
-		if(strlen(curr->key) > wireless_l &&
-		   !strncmp("wireless-", curr->key, wireless_l)) {
+	_LOGI ("update wireless security settings (%s).", block->name);
+	wireless_security_setting = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
 
-			gchar *property_value = NULL;
+	c_list_for_each_entry (curr, &block->data_lst_head, data_lst) {
+		if (_str_has_prefix (curr->key, "wireless-", TRUE)) {
+			const char *key = curr->key + NM_STRLEN ("wireless-");
+			char *property_value = NULL;
 			gpointer typed_property_value = NULL;
-			const gchar* newkey = map_by_mapping(mapping, curr->key+wireless_l);
-			IfupdownStrDupeFunc dupe_func = map_by_mapping (dupe_mapping, curr->key+wireless_l);
-			IfupdownStrToTypeFunc type_map_func = map_by_mapping (type_mapping, curr->key+wireless_l);
-			GFreeFunc free_func = map_by_mapping (free_type_mapping, curr->key+wireless_l);
-			if(!newkey || !dupe_func)
+			const char* newkey = map_by_mapping (mapping, key);
+			IfupdownStrDupeFunc dupe_func = map_by_mapping (dupe_mapping, key);
+			IfupdownStrToTypeFunc type_map_func = map_by_mapping (type_mapping, key);
+			GFreeFunc free_func = map_by_mapping (free_type_mapping, key);
+			if (!newkey || !dupe_func)
 				goto next;
 
 			property_value = (*dupe_func) (curr->data, connection);
-			nm_log_info (LOGD_SETTINGS, "setting wireless security key: %s=%s",
-			             newkey, property_value);
+			_LOGI ("setting wireless security key: %s=%s",
+			       newkey, property_value);
 
 			if (type_map_func) {
 				errno = 0;
 				typed_property_value = (*type_map_func) (property_value);
-				if(errno)
+				if (errno)
 					goto wireless_next;
 			}
 
-			g_object_set(wireless_security_setting,
-					   newkey, typed_property_value ?: property_value,
-					   NULL);
+			g_object_set (wireless_security_setting,
+			              newkey, typed_property_value ?: property_value,
+			              NULL);
 			security = TRUE;
 
-		wireless_next:
-			g_free(property_value);
+wireless_next:
+			g_free (property_value);
 			if (typed_property_value && free_func)
 				(*free_func) (typed_property_value);
 
-		} else if(strlen(curr->key) > wpa_l &&
-				!strncmp("wpa-", curr->key, wpa_l)) {
-
-			gchar *property_value = NULL;
+		} else if (_str_has_prefix (curr->key, "wpa-", TRUE)) {
+			const char *key = curr->key + NM_STRLEN ("wpa-");
+			char *property_value = NULL;
 			gpointer typed_property_value = NULL;
-			const gchar* newkey = map_by_mapping(mapping, curr->key+wpa_l);
-			IfupdownStrDupeFunc dupe_func = map_by_mapping (dupe_mapping, curr->key+wpa_l);
-			IfupdownStrToTypeFunc type_map_func = map_by_mapping (type_mapping, curr->key+wpa_l);
-			GFreeFunc free_func = map_by_mapping (free_type_mapping, curr->key+wpa_l);
-			if(!newkey || !dupe_func)
+			const char* newkey = map_by_mapping (mapping, key);
+			IfupdownStrDupeFunc dupe_func = map_by_mapping (dupe_mapping, key);
+			IfupdownStrToTypeFunc type_map_func = map_by_mapping (type_mapping, key);
+			GFreeFunc free_func = map_by_mapping (free_type_mapping, key);
+			if (!newkey || !dupe_func)
 				goto next;
 
 			property_value = (*dupe_func) (curr->data, connection);
-			nm_log_info (LOGD_SETTINGS, "setting wpa security key: %s=%s",
-			             newkey,
-#ifdef DEBUG_SECRETS
-			             property_value
-#else /* DEBUG_SECRETS */
-			             !strcmp("key", newkey) ||
-			             !strcmp("leap-password", newkey) ||
-			             !strcmp("pin", newkey) ||
-			             !strcmp("psk", newkey) ||
-			             !strcmp("wep-key0", newkey) ||
-			             !strcmp("wep-key1", newkey) ||
-			             !strcmp("wep-key2", newkey) ||
-			             !strcmp("wep-key3", newkey) ||
-			             NULL ?
-			             "<omitted>" : property_value
-#endif /* DEBUG_SECRETS */
-			             );
+			_LOGI ("setting wpa security key: %s=%s",
+			       newkey,
+			       NM_IN_STRSET (newkey, "key",
+			                             "leap-password",
+			                             "pin",
+			                             "psk",
+			                             "wep-key0",
+			                             "wep-key1",
+			                             "wep-key2",
+			                             "wep-key3")
+			         ? "<omitted>"
+			         : property_value
+			       );
 
 			if (type_map_func) {
 				errno = 0;
 				typed_property_value = (*type_map_func) (property_value);
-				if(errno)
+				if (errno)
 					goto wpa_next;
 			}
 
-			g_object_set(wireless_security_setting,
-					   newkey, typed_property_value ?: property_value,
-					   NULL);
+			g_object_set (wireless_security_setting,
+			              newkey, typed_property_value ?: property_value,
+			              NULL);
 			security = TRUE;
 
-		wpa_next:
-			g_free(property_value);
+wpa_next:
+			g_free (property_value);
 			if (free_func && typed_property_value)
 				(*free_func) (typed_property_value);
 		}
-	next:
-		curr = curr->next;
+next:
+		;
 	}
 
 	if (security)
@@ -404,51 +410,52 @@ update_wireless_security_setting_from_if_block(NMConnection *connection,
 }
 
 static void
-update_wired_setting_from_if_block(NMConnection *connection,
-							if_block *block)
+update_wired_setting_from_if_block (NMConnection *connection,
+                                    if_block *block)
 {
 	NMSettingWired *s_wired = NULL;
-	s_wired = NM_SETTING_WIRED(nm_setting_wired_new());
-	nm_connection_add_setting(connection, NM_SETTING(s_wired));
+	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
+	nm_connection_add_setting (connection, NM_SETTING (s_wired));
 }
 
 static void
 ifupdown_ip4_add_dns (NMSettingIPConfig *s_ip4, const char *dns)
 {
 	guint32 addr;
-	char **list, **iter;
+	gs_strfreev char **list = NULL;
+	char **iter;
 
 	if (dns == NULL)
 		return;
 
 	list = g_strsplit_set (dns, " \t", -1);
 	for (iter = list; iter && *iter; iter++) {
-		g_strstrip (*iter);
-		if (g_ascii_isspace (*iter[0]))
+		if ((*iter)[0] == '\0')
 			continue;
 		if (!inet_pton (AF_INET, *iter, &addr)) {
-			nm_log_warn (LOGD_SETTINGS, "    ignoring invalid nameserver '%s'", *iter);
+			_LOGW ("    ignoring invalid nameserver '%s'", *iter);
 			continue;
 		}
 
 		if (!nm_setting_ip_config_add_dns (s_ip4, *iter))
-			nm_log_warn (LOGD_SETTINGS, "    duplicate DNS domain '%s'", *iter);
+			_LOGW ("    duplicate DNS domain '%s'", *iter);
 	}
-	g_strfreev (list);
 }
 
 static gboolean
-update_ip4_setting_from_if_block(NMConnection *connection,
-						   if_block *block,
-						   GError **error)
+update_ip4_setting_from_if_block (NMConnection *connection,
+                                  if_block *block,
+                                  GError **error)
 {
 
-	NMSettingIPConfig *s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new());
-	const char *type = ifparser_getkey(block, "inet");
-	gboolean is_static = type && !strcmp("static", type);
+	gs_unref_object NMSettingIPConfig *s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ());
+	const char *type = ifparser_getkey (block, "inet");
 
-	if (!is_static) {
-		g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
+	if (!nm_streq0 (type, "static")) {
+		g_object_set (s_ip4,
+		              NM_SETTING_IP_CONFIG_METHOD,
+		              NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+		              NULL);
 	} else {
 		guint32 tmp_mask;
 		NMIPAddress *addr;
@@ -458,7 +465,6 @@ update_ip4_setting_from_if_block(NMConnection *connection,
 		const char *nameserver_v;
 		const char *nameservers_v;
 		const char *search_v;
-		char **list, **iter;
 		guint32 netmask_int = 32;
 
 		/* Address */
@@ -466,7 +472,7 @@ update_ip4_setting_from_if_block(NMConnection *connection,
 		if (!address_v) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Missing IPv4 address");
-			goto error;
+			return FALSE;
 		}
 
 		/* mask/prefix */
@@ -476,8 +482,8 @@ update_ip4_setting_from_if_block(NMConnection *connection,
 				netmask_int = atoi (netmask_v);
 			} else if (!inet_pton (AF_INET, netmask_v, &tmp_mask)) {
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
-						   "Invalid IPv4 netmask '%s'", netmask_v);
-				goto error;
+				             "Invalid IPv4 netmask '%s'", netmask_v);
+				return FALSE;
 			} else {
 				netmask_int = nm_utils_ip4_netmask_to_prefix (tmp_mask);
 			}
@@ -486,13 +492,13 @@ update_ip4_setting_from_if_block(NMConnection *connection,
 		/* Add the new address to the setting */
 		addr = nm_ip_address_new (AF_INET, address_v, netmask_int, error);
 		if (!addr)
-			goto error;
+			return FALSE;
 
 		if (nm_setting_ip_config_add_address (s_ip4, addr)) {
-			nm_log_info (LOGD_SETTINGS, "addresses count: %d",
-			             nm_setting_ip_config_get_num_addresses (s_ip4));
+			_LOGI ("addresses count: %d",
+			       nm_setting_ip_config_get_num_addresses (s_ip4));
 		} else {
-			nm_log_info (LOGD_SETTINGS, "ignoring duplicate IP4 address");
+			_LOGI ("ignoring duplicate IP4 address");
 		}
 		nm_ip_address_unref (addr);
 
@@ -502,7 +508,7 @@ update_ip4_setting_from_if_block(NMConnection *connection,
 			if (!nm_utils_ipaddr_valid (AF_INET, gateway_v)) {
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 				             "Invalid IPv4 gateway '%s'", gateway_v);
-				goto error;
+				return FALSE;
 			}
 			if (!nm_setting_ip_config_get_gateway (s_ip4))
 				g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, gateway_v, NULL);
@@ -515,70 +521,67 @@ update_ip4_setting_from_if_block(NMConnection *connection,
 		ifupdown_ip4_add_dns (s_ip4, nameservers_v);
 
 		if (!nm_setting_ip_config_get_num_dns (s_ip4))
-			nm_log_info (LOGD_SETTINGS, "No dns-nameserver configured in /etc/network/interfaces");
+			_LOGI ("No dns-nameserver configured in /etc/network/interfaces");
 
 		/* DNS searches */
 		search_v = ifparser_getkey (block, "dns-search");
 		if (search_v) {
+			gs_strfreev char **list = NULL;
+			char **iter;
+
 			list = g_strsplit_set (search_v, " \t", -1);
 			for (iter = list; iter && *iter; iter++) {
-				g_strstrip (*iter);
-				if (g_ascii_isspace (*iter[0]))
+				if ((*iter)[0] == '\0')
 					continue;
 				if (!nm_setting_ip_config_add_dns_search (s_ip4, *iter))
-					nm_log_warn (LOGD_SETTINGS, "    duplicate DNS domain '%s'", *iter);
+					_LOGW ("    duplicate DNS domain '%s'", *iter);
 			}
-			g_strfreev (list);
 		}
 
 		g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
 	}
 
-	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+	nm_connection_add_setting (connection, NM_SETTING (g_steal_pointer (&s_ip4)));
 	return TRUE;
-
-error:
-	g_object_unref (s_ip4);
-	return FALSE;
 }
 
 static void
 ifupdown_ip6_add_dns (NMSettingIPConfig *s_ip6, const char *dns)
 {
 	struct in6_addr addr;
-	char **list, **iter;
+	gs_strfreev char **list = NULL;
+	char **iter;
 
 	if (dns == NULL)
 		return;
 
 	list = g_strsplit_set (dns, " \t", -1);
 	for (iter = list; iter && *iter; iter++) {
-		g_strstrip (*iter);
-		if (g_ascii_isspace (*iter[0]))
+		if ((*iter)[0] == '\0')
 			continue;
 		if (!inet_pton (AF_INET6, *iter, &addr)) {
-			nm_log_warn (LOGD_SETTINGS, "    ignoring invalid nameserver '%s'", *iter);
+			_LOGW ("    ignoring invalid nameserver '%s'", *iter);
 			continue;
 		}
 
 		if (!nm_setting_ip_config_add_dns (s_ip6, *iter))
-			nm_log_warn (LOGD_SETTINGS, "    duplicate DNS domain '%s'", *iter);
+			_LOGW ("    duplicate DNS domain '%s'", *iter);
 	}
-	g_strfreev (list);
 }
 
 static gboolean
-update_ip6_setting_from_if_block(NMConnection *connection,
-						   if_block *block,
-						   GError **error)
+update_ip6_setting_from_if_block (NMConnection *connection,
+                                  if_block *block,
+                                  GError **error)
 {
-	NMSettingIPConfig *s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new());
-	const char *type = ifparser_getkey(block, "inet6");
-	gboolean is_static = type && (!strcmp("static", type) ||
-							!strcmp("v4tunnel", type));
+	gs_unref_object NMSettingIPConfig *s_ip6 = NM_SETTING_IP_CONFIG (nm_setting_ip6_config_new ());
+	const char *type = ifparser_getkey (block, "inet6");
 
-	if (!is_static) {
-		g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL);
+	if (!NM_IN_STRSET (type, "static", "v4tunnel")) {
+		g_object_set (s_ip6,
+		              NM_SETTING_IP_CONFIG_METHOD,
+		              NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+		              NULL);
 	} else {
 		NMIPAddress *addr;
 		const char *address_v;
@@ -588,31 +591,30 @@ update_ip6_setting_from_if_block(NMConnection *connection,
 		const char *nameservers_v;
 		const char *search_v;
 		int prefix_int = 128;
-		char **list, **iter;
 
 		/* Address */
-		address_v = ifparser_getkey(block, "address");
+		address_v = ifparser_getkey (block, "address");
 		if (!address_v) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Missing IPv6 address");
-			goto error;
+			return FALSE;
 		}
 
 		/* Prefix */
-		prefix_v = ifparser_getkey(block, "netmask");
+		prefix_v = ifparser_getkey (block, "netmask");
 		if (prefix_v)
 			prefix_int = g_ascii_strtoll (prefix_v, NULL, 10);
 
 		/* Add the new address to the setting */
 		addr = nm_ip_address_new (AF_INET6, address_v, prefix_int, error);
 		if (!addr)
-			goto error;
+			return FALSE;
 
 		if (nm_setting_ip_config_add_address (s_ip6, addr)) {
-			nm_log_info (LOGD_SETTINGS, "addresses count: %d",
+			_LOGI ("addresses count: %d",
 			             nm_setting_ip_config_get_num_addresses (s_ip6));
 		} else {
-			nm_log_info (LOGD_SETTINGS, "ignoring duplicate IP6 address");
+			_LOGI ("ignoring duplicate IP6 address");
 		}
 		nm_ip_address_unref (addr);
 
@@ -622,33 +624,34 @@ update_ip6_setting_from_if_block(NMConnection *connection,
 			if (!nm_utils_ipaddr_valid (AF_INET6, gateway_v)) {
 				g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 				             "Invalid IPv6 gateway '%s'", gateway_v);
-				goto error;
+				return FALSE;
 			}
 			if (!nm_setting_ip_config_get_gateway (s_ip6))
 				g_object_set (s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, gateway_v, NULL);
 		}
 
-		nameserver_v = ifparser_getkey(block, "dns-nameserver");
+		nameserver_v = ifparser_getkey (block, "dns-nameserver");
 		ifupdown_ip6_add_dns (s_ip6, nameserver_v);
 
-		nameservers_v = ifparser_getkey(block, "dns-nameservers");
+		nameservers_v = ifparser_getkey (block, "dns-nameservers");
 		ifupdown_ip6_add_dns (s_ip6, nameservers_v);
 
 		if (!nm_setting_ip_config_get_num_dns (s_ip6))
-			nm_log_info (LOGD_SETTINGS, "No dns-nameserver configured in /etc/network/interfaces");
+			_LOGI ("No dns-nameserver configured in /etc/network/interfaces");
 
 		/* DNS searches */
 		search_v = ifparser_getkey (block, "dns-search");
 		if (search_v) {
+			gs_strfreev char **list = NULL;
+			char **iter;
+
 			list = g_strsplit_set (search_v, " \t", -1);
 			for (iter = list; iter && *iter; iter++) {
-				g_strstrip (*iter);
-				if (isblank (*iter[0]))
+				if ((*iter)[0] == '\0')
 					continue;
 				if (!nm_setting_ip_config_add_dns_search (s_ip6, *iter))
-					nm_log_warn (LOGD_SETTINGS, "    duplicate DNS domain '%s'", *iter);
+					_LOGW ("    duplicate DNS domain '%s'", *iter);
 			}
-			g_strfreev (list);
 		}
 
 		g_object_set (s_ip6,
@@ -656,12 +659,8 @@ update_ip6_setting_from_if_block(NMConnection *connection,
 		              NULL);
 	}
 
-	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
+	nm_connection_add_setting (connection, NM_SETTING (g_steal_pointer (&s_ip6)));
 	return TRUE;
-
-error:
-	g_object_unref (s_ip6);
-	return FALSE;
 }
 
 gboolean
@@ -669,25 +668,22 @@ ifupdown_update_connection_from_if_block (NMConnection *connection,
                                           if_block *block,
                                           GError **error)
 {
-	const char *type = NULL;
-	char *idstr = NULL;
-	char *uuid_base = NULL;
-	char *uuid = NULL;
+	const char *type;
+	gs_free char *idstr = NULL;
+	gs_free char *uuid = NULL;
 	NMSettingConnection *s_con;
 	gboolean success = FALSE;
 
 	s_con = nm_connection_get_setting_connection (connection);
-	if(!s_con) {
-		s_con = NM_SETTING_CONNECTION (nm_setting_connection_new());
-		g_assert (s_con);
+	if (!s_con) {
+		s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
 		nm_connection_add_setting (connection, NM_SETTING (s_con));
 	}
 
 	type = _ifupdownplugin_guess_connection_type (block);
 	idstr = g_strconcat ("Ifupdown (", block->name, ")", NULL);
-	uuid_base = idstr;
 
-	uuid = nm_utils_uuid_generate_from_string (uuid_base, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
+	uuid = nm_utils_uuid_generate_from_string (idstr, -1, NM_UTILS_UUID_TYPE_LEGACY, NULL);
 	g_object_set (s_con,
 	              NM_SETTING_CONNECTION_TYPE, type,
 	              NM_SETTING_CONNECTION_INTERFACE_NAME, block->name,
@@ -696,19 +692,18 @@ ifupdown_update_connection_from_if_block (NMConnection *connection,
 	              NM_SETTING_CONNECTION_READ_ONLY, TRUE,
 	              NM_SETTING_CONNECTION_AUTOCONNECT, FALSE,
 	              NULL);
-	g_free (uuid);
 
-	nm_log_info (LOGD_SETTINGS, "update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s",
-	             block->name, type, idstr, nm_setting_connection_get_uuid (s_con));
+	_LOGI ("update_connection_setting_from_if_block: name:%s, type:%s, id:%s, uuid: %s",
+	       block->name, type, idstr, nm_setting_connection_get_uuid (s_con));
 
-	if (!strcmp (NM_SETTING_WIRED_SETTING_NAME, type))
+	if (nm_streq (type, NM_SETTING_WIRED_SETTING_NAME))
 		update_wired_setting_from_if_block (connection, block);
-	else if (!strcmp (NM_SETTING_WIRELESS_SETTING_NAME, type)) {
+	else if (nm_streq (type, NM_SETTING_WIRELESS_SETTING_NAME)) {
 		update_wireless_setting_from_if_block (connection, block);
 		update_wireless_security_setting_from_if_block (connection, block);
 	}
 
-	if (ifparser_haskey(block, "inet6"))
+	if (ifparser_haskey (block, "inet6"))
 		success = update_ip6_setting_from_if_block (connection, block, error);
 	else
 		success = update_ip4_setting_from_if_block (connection, block, error);
@@ -716,6 +711,5 @@ ifupdown_update_connection_from_if_block (NMConnection *connection,
 	if (success == TRUE)
 		success = nm_connection_verify (connection, error);
 
-	g_free (idstr);
 	return success;
 }
diff --git a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
index 8bd72d02..88c651b8 100644
--- a/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
+++ b/src/settings/plugins/ifupdown/nms-ifupdown-plugin.c
@@ -29,7 +29,6 @@
 #include <string.h>
 #include <arpa/inet.h>
 #include <gmodule.h>
-#include <libudev.h>
 
 #include "nm-setting-connection.h"
 #include "nm-dbus-interface.h"
@@ -42,7 +41,6 @@
 #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"
@@ -52,43 +50,27 @@
 
 #define IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT TRUE
 
-/* #define ALWAYS_UNMANAGE TRUE */
-#ifndef ALWAYS_UNMANAGE
-#define ALWAYS_UNMANAGE FALSE
-#endif
-
 /*****************************************************************************/
 
 typedef struct {
-	NMUdevClient *udev_client;
-
-	GHashTable *connections;  /* /e/n/i block name :: NMIfupdownConnection */
-
-	/* Stores all blocks/interfaces read from /e/n/i regardless of whether
-	 * there is an NMIfupdownConnection for block.
+	/* Stores an entry for blocks/interfaces read from /e/n/i and (if exists)
+	 * the NMIfupdownConnection associated with the block.
 	 */
 	GHashTable *eni_ifaces;
 
-	/* Stores any network interfaces the kernel knows about */
-	GHashTable *kernel_ifaces;
-
-	gboolean unmanage_well_known;
+	bool ifupdown_managed;
 } SettingsPluginIfupdownPrivate;
 
 struct _SettingsPluginIfupdown {
-	GObject parent;
+	NMSettingsPlugin parent;
 	SettingsPluginIfupdownPrivate _priv;
 };
 
 struct _SettingsPluginIfupdownClass {
-	GObjectClass parent;
+	NMSettingsPluginClass parent;
 };
 
-static void settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface);
-
-G_DEFINE_TYPE_EXTENDED (SettingsPluginIfupdown, settings_plugin_ifupdown, G_TYPE_OBJECT, 0,
-                        G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_PLUGIN,
-                                               settings_plugin_interface_init))
+G_DEFINE_TYPE (SettingsPluginIfupdown, settings_plugin_ifupdown, NM_TYPE_SETTINGS_PLUGIN)
 
 #define SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, SettingsPluginIfupdown, SETTINGS_IS_PLUGIN_IFUPDOWN)
 
@@ -99,169 +81,41 @@ NM_DEFINE_SINGLETON_GETTER (SettingsPluginIfupdown, settings_plugin_ifupdown_get
 
 /*****************************************************************************/
 
-static void
-bind_device_to_connection (SettingsPluginIfupdown *self,
-                           struct udev_device *device,
-                           NMIfupdownConnection *exported)
-{
-	NMSettingWired *s_wired;
-	NMSettingWireless *s_wifi;
-	const char *iface, *address;
-
-	iface = udev_device_get_sysname (device);
-	if (!iface) {
-		nm_log_warn (LOGD_SETTINGS, "failed to get ifname for device.");
-		return;
-	}
-
-	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;
-	}
-
-	if (!nm_utils_hwaddr_valid (address, ETH_ALEN)) {
-		nm_log_warn (LOGD_SETTINGS, "failed to parse MAC address '%s' for %s",
-		             address, iface);
-		return;
-	}
-
-	s_wired = nm_connection_get_setting_wired (NM_CONNECTION (exported));
-	s_wifi = nm_connection_get_setting_wireless (NM_CONNECTION (exported));
-	if (s_wired) {
-		nm_log_info (LOGD_SETTINGS, "locking wired connection setting");
-		g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, address, NULL);
-	} else if (s_wifi) {
-		nm_log_info (LOGD_SETTINGS, "locking wireless connection setting");
-		g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS, address, NULL);
-	}
-
-	nm_settings_connection_update (NM_SETTINGS_CONNECTION (exported),
-	                               NULL,
-	                               NM_SETTINGS_CONNECTION_PERSIST_MODE_DISK,
-	                               NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
-	                               "ifupdown-new",
-	                               NULL);
-}
-
-static void
-udev_device_added (SettingsPluginIfupdown *self, struct udev_device *device)
-{
-	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
-	const char *iface, *path;
-	NMIfupdownConnection *exported;
-
-	iface = udev_device_get_sysname (device);
-	path = udev_device_get_syspath (device);
-	if (!iface || !path)
-		return;
-
-	nm_log_info (LOGD_SETTINGS, "devices added (path: %s, iface: %s)", path, iface);
-
-	/* if we have a configured connection for this particular iface
-	 * we want to either unmanage the device or lock it
-	 */
-	exported = g_hash_table_lookup (priv->connections, iface);
-	if (!exported && !g_hash_table_lookup (priv->eni_ifaces, iface)) {
-		nm_log_info (LOGD_SETTINGS, "device added (path: %s, iface: %s): no ifupdown configuration found.",
-		             path, iface);
-		return;
-	}
-
-	g_hash_table_insert (priv->kernel_ifaces, g_strdup (iface), udev_device_ref (device));
-
-	if (exported)
-		bind_device_to_connection (self, device, exported);
-
-	if (ALWAYS_UNMANAGE || priv->unmanage_well_known)
-		g_signal_emit_by_name (G_OBJECT (self), NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED);
-}
-
-static void
-udev_device_removed (SettingsPluginIfupdown *self, struct udev_device *device)
-{
-	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
-	const char *iface, *path;
-
-	iface = udev_device_get_sysname (device);
-	path = udev_device_get_syspath (device);
-	if (!iface || !path)
-		return;
-
-	nm_log_info (LOGD_SETTINGS, "devices removed (path: %s, iface: %s)", path, iface);
-
-	if (!g_hash_table_remove (priv->kernel_ifaces, iface))
-		return;
-
-	if (ALWAYS_UNMANAGE || priv->unmanage_well_known)
-		g_signal_emit_by_name (G_OBJECT (self), NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED);
-}
-
-static void
-udev_device_changed (SettingsPluginIfupdown *self, struct udev_device *device)
-{
-	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
-	const char *iface, *path;
-
-	iface = udev_device_get_sysname (device);
-	path = udev_device_get_syspath (device);
-	if (!iface || !path)
-		return;
-
-	nm_log_info (LOGD_SETTINGS, "device changed (path: %s, iface: %s)", path, iface);
-
-	if (!g_hash_table_lookup (priv->kernel_ifaces, iface))
-		return;
-
-	if (ALWAYS_UNMANAGE || priv->unmanage_well_known)
-		g_signal_emit_by_name (G_OBJECT (self), NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED);
-}
-
-static void
-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);
+#define _NMLOG_PREFIX_NAME      "ifupdown"
+#define _NMLOG_DOMAIN           LOGD_SETTINGS
+#define _NMLOG(level, ...) \
+    nm_log ((level), _NMLOG_DOMAIN, NULL, NULL, \
+            "%s" _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
+            _NMLOG_PREFIX_NAME": " \
+            _NM_UTILS_MACRO_REST (__VA_ARGS__))
 
-	/* A bit paranoid */
-	subsys = udev_device_get_subsystem (device);
-	g_return_if_fail (nm_streq0 (subsys, "net"));
-
-	if (!strcmp (action, "add"))
-		udev_device_added (self, device);
-	else if (!strcmp (action, "remove"))
-		udev_device_removed (self, device);
-	else if (!strcmp (action, "change"))
-		udev_device_changed (self, device);
-}
+/*****************************************************************************/
 
 /* Returns the plugins currently known list of connections.  The returned
  * list is freed by the system settings service.
  */
 static GSList*
-get_connections (NMSettingsPlugin *config)
+get_connections (NMSettingsPlugin *plugin)
 {
-	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE ((SettingsPluginIfupdown *) config);
-	GSList *connections;
-
-	nm_log_info (LOGD_SETTINGS, "(%d) ... get_connections.", GPOINTER_TO_UINT(config));
+	SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin);
+	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
+	GSList *list = NULL;
+	GHashTableIter iter;
+	void *value;
 
-	if(priv->unmanage_well_known) {
-		nm_log_info (LOGD_SETTINGS, "(%d) ... get_connections (managed=false): return empty list.", GPOINTER_TO_UINT(config));
+	if (!priv->ifupdown_managed) {
+		_LOGD ("get_connections: not connections due to managed=false");
 		return NULL;
 	}
 
-	connections = _nm_utils_hash_values_to_slist (priv->connections);
+	g_hash_table_iter_init (&iter, priv->eni_ifaces);
+	while (g_hash_table_iter_next (&iter, NULL, &value)) {
+		if (value)
+			list = g_slist_prepend (list, value);
+	}
 
-	nm_log_info (LOGD_SETTINGS, "(%d) connections count: %d", GPOINTER_TO_UINT(config), g_slist_length(connections));
-	return connections;
+	_LOGD ("get_connections: %u connections", g_slist_length (list));
+	return list;
 }
 
 /*
@@ -270,202 +124,178 @@ get_connections (NMSettingsPlugin *config)
  * each element must be allocated using g_malloc() or its variants.
  */
 static GSList*
-get_unmanaged_specs (NMSettingsPlugin *config)
+get_unmanaged_specs (NMSettingsPlugin *plugin)
 {
-	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE ((SettingsPluginIfupdown *) config);
+	SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin);
+	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
 	GSList *specs = NULL;
 	GHashTableIter iter;
-	struct udev_device *device;
 	const char *iface;
 
-	if (!ALWAYS_UNMANAGE && !priv->unmanage_well_known)
+	if (priv->ifupdown_managed)
 		return NULL;
 
-	nm_log_info (LOGD_SETTINGS, "get unmanaged devices count: %d",
-	             g_hash_table_size (priv->kernel_ifaces));
-
-	g_hash_table_iter_init (&iter, priv->kernel_ifaces);
-	while (g_hash_table_iter_next (&iter, (gpointer) &iface, (gpointer) &device)) {
-		const char *address;
+	_LOGD ("unmanaged-specs: unmanaged devices count %u",
+	       g_hash_table_size (priv->eni_ifaces));
 
-		address = udev_device_get_sysattr_value (device, "address");
-		if (address)
-			specs = g_slist_append (specs, g_strdup_printf ("mac:%s", address));
-		else
-			specs = g_slist_append (specs, g_strdup_printf ("interface-name:%s", iface));
-	}
+	g_hash_table_iter_init (&iter, priv->eni_ifaces);
+	while (g_hash_table_iter_next (&iter, (gpointer) &iface, NULL))
+		specs = g_slist_append (specs, g_strdup_printf ("interface-name:=%s", iface));
 	return specs;
 }
 
 /*****************************************************************************/
 
 static void
-_udev_device_unref (gpointer ptr)
+initialize (NMSettingsPlugin *plugin)
 {
-	udev_device_unref (ptr);
-}
-
-static void
-init (NMSettingsPlugin *config)
-{
-	SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (config);
+	SettingsPluginIfupdown *self = SETTINGS_PLUGIN_IFUPDOWN (plugin);
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
-	GHashTable *auto_ifaces;
-	if_block *block = NULL;
-	struct udev_enumerate *enumerate;
-	struct udev_list_entry *keys;
+	gs_unref_hashtable GHashTable *auto_ifaces = NULL;
+	nm_auto_ifparser if_parser *parser = NULL;
+	if_block *block;
 	GHashTableIter con_iter;
 	const char *block_name;
-	NMIfupdownConnection *connection;
-
-	auto_ifaces = g_hash_table_new (nm_str_hash, g_str_equal);
-
-	if(!priv->connections)
-		priv->connections = g_hash_table_new (nm_str_hash, g_str_equal);
-
-	if(!priv->kernel_ifaces)
-		priv->kernel_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, _udev_device_unref);
+	NMIfupdownConnection *conn;
 
-	if(!priv->eni_ifaces)
-		priv->eni_ifaces = g_hash_table_new (nm_str_hash, g_str_equal);
+	parser = ifparser_parse (ENI_INTERFACES_FILE, 0);
 
-	nm_log_info (LOGD_SETTINGS, "init!");
+	c_list_for_each_entry (block, &parser->block_lst_head, block_lst) {
 
-	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);
-	block = ifparser_getfirst ();
-	while (block) {
-		if(!strcmp ("auto", block->type) || !strcmp ("allow-hotplug", block->type))
-			g_hash_table_insert (auto_ifaces, block->name, GUINT_TO_POINTER (1));
-		else if (!strcmp ("iface", block->type)) {
-			NMIfupdownConnection *exported;
+		if (NM_IN_STRSET (block->type, "auto", "allow-hotplug")) {
+			if (!auto_ifaces)
+				auto_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
+			g_hash_table_add (auto_ifaces, g_strdup (block->name));
+			continue;
+		}
 
+		if (nm_streq (block->type, "iface")) {
 			/* Bridge configuration */
-			if(!strncmp ("br", block->name, 2)) {
+			if (g_str_has_prefix (block_name, "br")) {
 				/* Try to find bridge ports */
 				const char *ports = ifparser_getkey (block, "bridge-ports");
+
 				if (ports) {
-					int i;
+					guint i;
 					int state = 0;
-					char **port_ifaces;
+					gs_strfreev char **port_ifaces = NULL;
 
-					nm_log_info (LOGD_SETTINGS, "found bridge ports %s for %s", ports, block->name);
+					_LOGD ("parse: found bridge ports %s for %s", ports, block->name);
 
 					port_ifaces = g_strsplit_set (ports, " \t", -1);
-					for (i = 0; i < g_strv_length (port_ifaces); i++) {
-						char *token = port_ifaces[i];
+					for (i = 0; port_ifaces[i]; i++) {
+						const char *token = port_ifaces[i];
+
 						/* Skip crazy stuff like regex or all */
-						if (!strcmp ("all", token)) {
+						if (nm_streq (token, "all"))
 							continue;
-						}
+
 						/* Small SM to skip everything inside regex */
-						if (!strcmp ("regex", token)) {
+						if (nm_streq (token, "regex")) {
 							state++;
 							continue;
 						}
-						if (!strcmp ("noregex", token)) {
+						if (nm_streq (token, "noregex")) {
 							state--;
 							continue;
 						}
+						if (nm_streq (token, "none"))
+							continue;
 						if (state == 0 && strlen (token) > 0) {
-							nm_log_info (LOGD_SETTINGS, "adding bridge port %s to eni_ifaces", token);
-							g_hash_table_insert (priv->eni_ifaces, g_strdup (token), "known");
+							conn = g_hash_table_lookup (priv->eni_ifaces, block->name);
+							if (!conn) {
+								_LOGD ("parse: adding bridge port \"%s\"", token);
+								g_hash_table_insert (priv->eni_ifaces, g_strdup (token), NULL);
+							} else {
+								_LOGD ("parse: adding bridge port \"%s\" (have connection %s)", token,
+								       nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
+							}
 						}
 					}
-					g_strfreev (port_ifaces);
 				}
-				goto next;
+				continue;
 			}
 
 			/* Skip loopback configuration */
-			if(!strcmp ("lo", block->name)) {
-				goto next;
-			}
+			if (nm_streq (block->name, "lo"))
+				continue;
 
 			/* Remove any connection for this block that was previously found */
-			exported = g_hash_table_lookup (priv->connections, block->name);
-			if (exported) {
-				nm_log_info (LOGD_SETTINGS, "deleting %s from connections", block->name);
-				nm_settings_connection_delete (NM_SETTINGS_CONNECTION (exported), NULL);
-				g_hash_table_remove (priv->connections, block->name);
+			conn = g_hash_table_lookup (priv->eni_ifaces, block->name);
+			if (conn) {
+				_LOGD ("parse: replace connection \"%s\" (%s)",
+				       block->name,
+				       nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
+				nm_settings_connection_delete (NM_SETTINGS_CONNECTION (conn), NULL);
+				g_hash_table_remove (priv->eni_ifaces, block->name);
 			}
 
 			/* add the new connection */
-			exported = nm_ifupdown_connection_new (block);
-			if (exported) {
-				nm_log_info (LOGD_SETTINGS, "adding %s to connections", block->name);
-				g_hash_table_insert (priv->connections, block->name, exported);
+			conn = nm_ifupdown_connection_new (block);
+			if (conn) {
+				_LOGD ("parse: adding connection \"%s\" (%s)", block->name,
+				       nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
+			} else
+				_LOGD ("parse: adding place holder for connection \"%s\"", block->name);
+			g_hash_table_insert (priv->eni_ifaces, g_strdup (block->name), conn);
+			continue;
+		}
+
+		if (nm_streq (block->type, "mapping")) {
+			conn = g_hash_table_lookup (priv->eni_ifaces, block->name);
+			if (!conn) {
+				_LOGD ("parse: adding mapping \"%s\"", block->name);
+				g_hash_table_insert (priv->eni_ifaces, g_strdup (block->name), NULL);
+			} else {
+				_LOGD ("parse: adding mapping \"%s\" (have connection %s)", block->name,
+				       nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (conn)));
 			}
-			nm_log_info (LOGD_SETTINGS, "adding iface %s to eni_ifaces", block->name);
-			g_hash_table_insert (priv->eni_ifaces, block->name, "known");
-		} else if (!strcmp ("mapping", block->type)) {
-			g_hash_table_insert (priv->eni_ifaces, block->name, "known");
-			nm_log_info (LOGD_SETTINGS, "adding mapping %s to eni_ifaces", block->name);
+			continue;
 		}
-	next:
-		block = block->next;
 	}
 
 	/* Make 'auto' interfaces autoconnect=TRUE */
-	g_hash_table_iter_init (&con_iter, priv->connections);
-	while (g_hash_table_iter_next (&con_iter, (gpointer) &block_name, (gpointer) &connection)) {
+	g_hash_table_iter_init (&con_iter, priv->eni_ifaces);
+	while (g_hash_table_iter_next (&con_iter, (gpointer) &block_name, (gpointer) &conn)) {
 		NMSettingConnection *setting;
 
-		if (g_hash_table_lookup (auto_ifaces, block_name)) {
-			setting = nm_connection_get_setting_connection (NM_CONNECTION (connection));
-			g_object_set (setting, NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, NULL);
-			nm_log_info (LOGD_SETTINGS, "autoconnect");
-		}
+		if (   !conn
+		    || !auto_ifaces
+		    || !g_hash_table_contains (auto_ifaces, block_name))
+			continue;
+
+		/* FIXME(copy-on-write-connection): avoid modifying NMConnection instances and share them via copy-on-write. */
+		setting = nm_connection_get_setting_connection (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (conn)));
+		g_object_set (setting, NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, NULL);
 	}
-	g_hash_table_destroy (auto_ifaces);
 
 	/* Check the config file to find out whether to manage interfaces */
-	priv->unmanage_well_known = !nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
-	                                                               NM_CONFIG_KEYFILE_GROUP_IFUPDOWN,
-	                                                               NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED,
-	                                                               !IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT);
-	nm_log_info (LOGD_SETTINGS, "management mode: %s", priv->unmanage_well_known ? "unmanaged" : "managed");
-
-	/* Add well-known interfaces */
-	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);
-		}
-	}
-	udev_enumerate_unref (enumerate);
+	priv->ifupdown_managed = nm_config_data_get_value_boolean (NM_CONFIG_GET_DATA_ORIG,
+	                                                           NM_CONFIG_KEYFILE_GROUP_IFUPDOWN,
+	                                                           NM_CONFIG_KEYFILE_KEY_IFUPDOWN_MANAGED,
+	                                                           !IFUPDOWN_UNMANAGE_WELL_KNOWN_DEFAULT);
+	_LOGI ("management mode: %s", priv->ifupdown_managed ? "managed" : "unmanaged");
 
 	/* Now if we're running in managed mode, let NM know there are new connections */
-	if (!priv->unmanage_well_known) {
-		GList *con_list = g_hash_table_get_values (priv->connections);
-		GList *cl_iter;
-
-		for (cl_iter = con_list; cl_iter; cl_iter = g_list_next (cl_iter)) {
-			g_signal_emit_by_name (self,
-			                       NM_SETTINGS_PLUGIN_CONNECTION_ADDED,
-			                       NM_SETTINGS_CONNECTION (cl_iter->data));
+	if (priv->ifupdown_managed) {
+		GHashTableIter iter;
+
+		g_hash_table_iter_init (&iter, priv->eni_ifaces);
+		while (g_hash_table_iter_next (&iter, NULL, (gpointer *) conn)) {
+			_nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
+			                                                  NM_SETTINGS_CONNECTION (conn));
 		}
-		g_list_free (con_list);
 	}
-
-	nm_log_info (LOGD_SETTINGS, "end _init.");
 }
 
 /*****************************************************************************/
 
 static void
-settings_plugin_ifupdown_init (SettingsPluginIfupdown *plugin)
+settings_plugin_ifupdown_init (SettingsPluginIfupdown *self)
 {
+	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (self);
+
+	priv->eni_ifaces = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_object_unref);
 }
 
 static void
@@ -474,35 +304,28 @@ dispose (GObject *object)
 	SettingsPluginIfupdown *plugin = SETTINGS_PLUGIN_IFUPDOWN (object);
 	SettingsPluginIfupdownPrivate *priv = SETTINGS_PLUGIN_IFUPDOWN_GET_PRIVATE (plugin);
 
-	g_clear_pointer (&priv->kernel_ifaces, g_hash_table_destroy);
 	g_clear_pointer (&priv->eni_ifaces, g_hash_table_destroy);
 
-	priv->udev_client = nm_udev_client_unref (priv->udev_client);
-
 	G_OBJECT_CLASS (settings_plugin_ifupdown_parent_class)->dispose (object);
 }
 
 static void
-settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *req_class)
+settings_plugin_ifupdown_class_init (SettingsPluginIfupdownClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass);
 
 	object_class->dispose = dispose;
-}
 
-static void
-settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface)
-{
-	plugin_iface->init = init;
-	plugin_iface->get_connections = get_connections;
-	plugin_iface->get_unmanaged_specs = get_unmanaged_specs;
+	plugin_class->initialize          = initialize;
+	plugin_class->get_connections     = get_connections;
+	plugin_class->get_unmanaged_specs = get_unmanaged_specs;
 }
 
 /*****************************************************************************/
 
-G_MODULE_EXPORT GObject *
+G_MODULE_EXPORT NMSettingsPlugin *
 nm_settings_plugin_factory (void)
 {
-	return G_OBJECT (g_object_ref (settings_plugin_ifupdown_get ()));
+	return NM_SETTINGS_PLUGIN (g_object_ref (settings_plugin_ifupdown_get ()));
 }
-
diff --git a/src/settings/plugins/ifupdown/tests/meson.build b/src/settings/plugins/ifupdown/tests/meson.build
index ee3b6a34..5a2383d9 100644
--- a/src/settings/plugins/ifupdown/tests/meson.build
+++ b/src/settings/plugins/ifupdown/tests/meson.build
@@ -4,7 +4,6 @@ exe = executable(
   test_unit,
   test_unit + '.c',
   dependencies: test_nm_dep,
-  c_args: nm_build_cflags,
   link_with: libnms_ifupdown_core
 )
 
diff --git a/src/settings/plugins/ifupdown/tests/test-ifupdown.c b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
index 18bad65e..e96350c0 100644
--- a/src/settings/plugins/ifupdown/tests/test-ifupdown.c
+++ b/src/settings/plugins/ifupdown/tests/test-ifupdown.c
@@ -130,16 +130,15 @@ expected_free (Expected *e)
 }
 
 static void
-compare_expected_to_ifparser (Expected *e)
+compare_expected_to_ifparser (if_parser *parser, Expected *e)
 {
 	if_block *n;
 	GSList *biter, *kiter;
 
-	g_assert_cmpint (g_slist_length (e->blocks), ==, ifparser_get_num_blocks ());
+	g_assert_cmpint (g_slist_length (e->blocks), ==, ifparser_get_num_blocks (parser));
 
-	for (n = ifparser_getfirst (), biter = e->blocks;
-	     n && biter;
-	     n = n->next, biter = g_slist_next (biter)) {
+	biter = e->blocks;
+	c_list_for_each_entry (n, &parser->block_lst_head, block_lst) {
 		if_data *m;
 		ExpectedBlock *b = biter->data;
 
@@ -150,26 +149,31 @@ compare_expected_to_ifparser (Expected *e)
 
 		g_assert_cmpint (g_slist_length (b->keys), ==, ifparser_get_num_info (n));
 
-		for (m = n->info, kiter = b->keys;
-		     m && kiter;
-		     m = m->next, kiter = g_slist_next (kiter)) {
+		kiter = b->keys;
+		c_list_for_each_entry (m, &n->data_lst_head, data_lst) {
 			ExpectedKey *k = kiter->data;
 
 			g_assert (k->key && m->key);
 			g_assert_cmpstr (k->key, ==, m->key);
 			g_assert (k->data && m->data);
 			g_assert_cmpstr (k->data, ==, m->data);
+
+			kiter = g_slist_next (kiter);
 		}
+		g_assert (!kiter);
+
+		biter = g_slist_next (biter);
 	}
+	g_assert (!biter);
 }
 
 static void
-dump_blocks (void)
+dump_blocks (if_parser *parser)
 {
 	if_block *n;
 
 	g_message ("\n***************************************************");
-	for (n = ifparser_getfirst (); n != NULL; n = n->next) {
+	c_list_for_each_entry (n, &parser->block_lst_head, block_lst) {
 		if_data *m;
 
 		// each block start with its type & name
@@ -178,8 +182,8 @@ dump_blocks (void)
 
 		// each key-value pair within a block is indented & separated by a tab
 		// (single quotes used to show typ & name baoundaries)
-		for (m = n->info; m != NULL; m = m->next)
-			   g_print("\t'%s'\t'%s'\n", m->key, m->data);
+		c_list_for_each_entry (m, &n->data_lst_head, data_lst)
+			g_print("\t'%s'\t'%s'\n", m->key, m->data);
 
 		// blocks are separated by an empty line
 		g_print("\n");
@@ -187,21 +191,24 @@ dump_blocks (void)
 	g_message ("##################################################\n");
 }
 
-static void
-init_ifparser_with_file (const char *path, const char *file)
+static if_parser *
+init_ifparser_with_file (const char *file)
 {
-	char *tmp;
+	if_parser *parser;
+	gs_free char *tmp = NULL;
 
-	tmp = g_strdup_printf ("%s/%s", path, file);
-	ifparser_init (tmp, 1);
-	g_free (tmp);
+	tmp = g_strdup_printf ("%s/%s", TEST_DIR, file);
+	parser = ifparser_parse (tmp, 1);
+	g_assert (parser);
+	return parser;
 }
 
 static void
-test1_ignore_line_before_first_block (const char *path)
+test1_ignore_line_before_first_block (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test1");
 
 	e = expected_new ();
 	b = expected_block_new ("auto", "eth0");
@@ -210,35 +217,33 @@ test1_ignore_line_before_first_block (const char *path)
 	expected_add_block (e, b);
 	expected_block_add_key (b, expected_key_new ("inet", "dhcp"));
 
-	init_ifparser_with_file (path, "test1");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test2_wrapped_line (const char *path)
+test2_wrapped_line (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test2");
 
 	e = expected_new ();
 	b = expected_block_new ("auto", "lo");
 	expected_add_block (e, b);
 
-	init_ifparser_with_file (path, "test2");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test3_wrapped_multiline_multiarg (const char *path)
+test3_wrapped_multiline_multiarg (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test3");
 
 	e = expected_new ();
 	b = expected_block_new ("allow-hotplug", "eth0");
@@ -248,35 +253,33 @@ test3_wrapped_multiline_multiarg (const char *path)
 	b = expected_block_new ("allow-hotplug", "bnep0");
 	expected_add_block (e, b);
 
-	init_ifparser_with_file (path, "test3");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test4_allow_auto_is_auto (const char *path)
+test4_allow_auto_is_auto (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test4");
 
 	e = expected_new ();
 	b = expected_block_new ("auto", "eth0");
 	expected_add_block (e, b);
 
-	init_ifparser_with_file (path, "test4");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test5_allow_auto_multiarg (const char *path)
+test5_allow_auto_multiarg (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test5");
 
 	e = expected_new ();
 	b = expected_block_new ("allow-hotplug", "eth0");
@@ -284,52 +287,50 @@ test5_allow_auto_multiarg (const char *path)
 	b = expected_block_new ("allow-hotplug", "wlan0");
 	expected_add_block (e, b);
 
-	init_ifparser_with_file (path, "test5");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test6_mixed_whitespace (const char *path)
+test6_mixed_whitespace (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test6");
 
 	e = expected_new ();
 	b = expected_block_new ("iface", "lo");
 	expected_block_add_key (b, expected_key_new ("inet", "loopback"));
 	expected_add_block (e, b);
 
-	init_ifparser_with_file (path, "test6");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test7_long_line (const char *path)
+test7_long_line (void)
 {
-	init_ifparser_with_file (path, "test7");
-	g_assert_cmpint (ifparser_get_num_blocks (), ==, 0);
-	ifparser_destroy ();
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test7");
+
+	g_assert_cmpint (ifparser_get_num_blocks (parser), ==, 0);
 }
 
 static void
-test8_long_line_wrapped (const char *path)
+test8_long_line_wrapped (void)
 {
-	init_ifparser_with_file (path, "test8");
-	g_assert_cmpint (ifparser_get_num_blocks (), ==, 0);
-	ifparser_destroy ();
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test8");
+
+	g_assert_cmpint (ifparser_get_num_blocks (parser), ==, 0);
 }
 
 static void
-test9_wrapped_lines_in_block (const char *path)
+test9_wrapped_lines_in_block (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test9");
 
 	e = expected_new ();
 	b = expected_block_new ("iface", "eth0");
@@ -340,18 +341,17 @@ test9_wrapped_lines_in_block (const char *path)
 	expected_block_add_key (b, expected_key_new ("broadcast", "10.250.2.63"));
 	expected_block_add_key (b, expected_key_new ("gateway", "10.250.2.50"));
 
-	init_ifparser_with_file (path, "test9");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test11_complex_wrap (const char *path)
+test11_complex_wrap (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test11");
 
 	e = expected_new ();
 	b = expected_block_new ("iface", "pppoe");
@@ -359,18 +359,17 @@ test11_complex_wrap (const char *path)
 	expected_block_add_key (b, expected_key_new ("inet", "manual"));
 	expected_block_add_key (b, expected_key_new ("pre-up", "/sbin/ifconfig eth0 up"));
 
-	init_ifparser_with_file (path, "test11");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test12_complex_wrap_split_word (const char *path)
+test12_complex_wrap_split_word (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test12");
 
 	e = expected_new ();
 	b = expected_block_new ("iface", "pppoe");
@@ -378,36 +377,34 @@ test12_complex_wrap_split_word (const char *path)
 	expected_block_add_key (b, expected_key_new ("inet", "manual"));
 	expected_block_add_key (b, expected_key_new ("up", "ifup ppp0=dsl"));
 
-	init_ifparser_with_file (path, "test12");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test13_more_mixed_whitespace (const char *path)
+test13_more_mixed_whitespace (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test13");
 
 	e = expected_new ();
 	b = expected_block_new ("iface", "dsl");
 	expected_block_add_key (b, expected_key_new ("inet", "ppp"));
 	expected_add_block (e, b);
 
-	init_ifparser_with_file (path, "test13");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test14_mixed_whitespace_block_start (const char *path)
+test14_mixed_whitespace_block_start (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test14");
 
 	e = expected_new ();
 	b = expected_block_new ("iface", "wlan0");
@@ -420,47 +417,43 @@ test14_mixed_whitespace_block_start (const char *path)
 	expected_block_add_key (b, expected_key_new ("inet", "dhcp"));
 	expected_add_block (e, b);
 
-	init_ifparser_with_file (path, "test14");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test15_trailing_space (const char *path)
+test15_trailing_space (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test15");
 
 	e = expected_new ();
 	b = expected_block_new ("iface", "bnep0");
 	expected_block_add_key (b, expected_key_new ("inet", "static"));
 	expected_add_block (e, b);
 
-	init_ifparser_with_file (path, "test15");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test16_missing_newline (const char *path)
+test16_missing_newline (void)
 {
 	Expected *e;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test16");
 
 	e = expected_new ();
 	expected_add_block (e, expected_block_new ("mapping", "eth0"));
 
-	init_ifparser_with_file (path, "test16");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 static void
-test17_read_static_ipv4 (const char *path)
+test17_read_static_ipv4 (void)
 {
 	NMConnection *connection;
 	NMSettingConnection *s_con;
@@ -470,13 +463,13 @@ test17_read_static_ipv4 (const char *path)
 	gboolean success;
 	NMIPAddress *ip4_addr;
 	if_block *block = NULL;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test17-wired-static-verify-ip4");
 
-	init_ifparser_with_file (path, "test17-wired-static-verify-ip4");
-	block = ifparser_getfirst ();
+	block = ifparser_getfirst (parser);
 	connection = nm_simple_connection_new();
 	g_assert (connection);
 
-	ifupdown_update_connection_from_if_block(connection, block, &error);
+	ifupdown_update_connection_from_if_block (connection, block, &error);
 	g_assert_no_error (error);
 
 	success = nm_connection_verify (connection, &error);
@@ -511,12 +504,11 @@ test17_read_static_ipv4 (const char *path)
 	g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 0), ==, "example.com");
 	g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip4, 1), ==, "foo.example.com");
 
-	ifparser_destroy ();
 	g_object_unref (connection);
 }
 
 static void
-test18_read_static_ipv6 (const char *path)
+test18_read_static_ipv6 (void)
 {
 	NMConnection *connection;
 	NMSettingConnection *s_con;
@@ -526,12 +518,12 @@ test18_read_static_ipv6 (const char *path)
 	gboolean success;
 	NMIPAddress *ip6_addr;
 	if_block *block = NULL;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test18-wired-static-verify-ip6");
 
-	init_ifparser_with_file (path, "test18-wired-static-verify-ip6");
-	block = ifparser_getfirst ();
+	block = ifparser_getfirst (parser);
 	connection = nm_simple_connection_new();
 	g_assert (connection);
-	ifupdown_update_connection_from_if_block(connection, block, &error);
+	ifupdown_update_connection_from_if_block (connection, block, &error);
 	g_assert_no_error (error);
 
 	success = nm_connection_verify (connection, &error);
@@ -566,12 +558,11 @@ test18_read_static_ipv6 (const char *path)
 	g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 0), ==, "example.com");
 	g_assert_cmpstr (nm_setting_ip_config_get_dns_search (s_ip6, 1), ==, "foo.example.com");
 
-	ifparser_destroy ();
 	g_object_unref (connection);
 }
 
 static void
-test19_read_static_ipv4_plen (const char *path)
+test19_read_static_ipv4_plen (void)
 {
 	NMConnection *connection;
 	NMSettingIPConfig *s_ip4;
@@ -579,12 +570,12 @@ test19_read_static_ipv4_plen (const char *path)
 	NMIPAddress *ip4_addr;
 	if_block *block = NULL;
 	gboolean success;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test19-wired-static-verify-ip4-plen");
 
-	init_ifparser_with_file (path, "test19-wired-static-verify-ip4-plen");
-	block = ifparser_getfirst ();
+	block = ifparser_getfirst (parser);
 	connection = nm_simple_connection_new();
 	g_assert (connection);
-	ifupdown_update_connection_from_if_block(connection, block, &error);
+	ifupdown_update_connection_from_if_block (connection, block, &error);
 	g_assert_no_error (error);
 
 	success = nm_connection_verify (connection, &error);
@@ -601,15 +592,15 @@ test19_read_static_ipv4_plen (const char *path)
 	g_assert_cmpstr (nm_ip_address_get_address (ip4_addr), ==, "10.0.0.3");
 	g_assert_cmpint (nm_ip_address_get_prefix (ip4_addr), ==, 8);
 
-	ifparser_destroy ();
 	g_object_unref (connection);
 }
 
 static void
-test20_source_stanza (const char *path)
+test20_source_stanza (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test20-source-stanza");
 
 	e = expected_new ();
 
@@ -625,18 +616,17 @@ test20_source_stanza (const char *path)
 	expected_add_block (e, b);
 	expected_block_add_key (b, expected_key_new ("inet", "dhcp"));
 
-	init_ifparser_with_file (path, "test20-source-stanza");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
 static void
-test21_source_dir_stanza (const char *path)
+test21_source_dir_stanza (void)
 {
 	Expected *e;
 	ExpectedBlock *b;
+	nm_auto_ifparser if_parser *parser = init_ifparser_with_file ("test21-source-dir-stanza");
 
 	e = expected_new ();
 
@@ -646,10 +636,8 @@ test21_source_dir_stanza (const char *path)
 	expected_add_block (e, b);
 	expected_block_add_key (b, expected_key_new ("inet", "dhcp"));
 
-	init_ifparser_with_file (path, "test21-source-dir-stanza");
-	compare_expected_to_ifparser (e);
+	compare_expected_to_ifparser (parser, e);
 
-	ifparser_destroy ();
 	expected_free (e);
 }
 
@@ -660,49 +648,28 @@ main (int argc, char **argv)
 {
 	nmtst_init_assert_logging (&argc, &argv, "WARN", "DEFAULT");
 
-	if (0)
-		dump_blocks ();
-
-	g_test_add_data_func ("/ifupdate/ignore_line_before_first_block", TEST_DIR,
-	                      (GTestDataFunc) test1_ignore_line_before_first_block);
-	g_test_add_data_func ("/ifupdate/wrapped_line", TEST_DIR,
-	                      (GTestDataFunc) test2_wrapped_line);
-	g_test_add_data_func ("/ifupdate/wrapped_multiline_multiarg", TEST_DIR,
-	                      (GTestDataFunc) test3_wrapped_multiline_multiarg);
-	g_test_add_data_func ("/ifupdate/allow_auto_is_auto", TEST_DIR,
-	                      (GTestDataFunc) test4_allow_auto_is_auto);
-	g_test_add_data_func ("/ifupdate/allow_auto_multiarg", TEST_DIR,
-	                      (GTestDataFunc) test5_allow_auto_multiarg);
-	g_test_add_data_func ("/ifupdate/mixed_whitespace", TEST_DIR,
-	                      (GTestDataFunc) test6_mixed_whitespace);
-	g_test_add_data_func ("/ifupdate/long_line", TEST_DIR,
-	                      (GTestDataFunc) test7_long_line);
-	g_test_add_data_func ("/ifupdate/long_line_wrapped", TEST_DIR,
-	                      (GTestDataFunc) test8_long_line_wrapped);
-	g_test_add_data_func ("/ifupdate/wrapped_lines_in_block", TEST_DIR,
-	                      (GTestDataFunc) test9_wrapped_lines_in_block);
-	g_test_add_data_func ("/ifupdate/complex_wrap", TEST_DIR,
-	                      (GTestDataFunc) test11_complex_wrap);
-	g_test_add_data_func ("/ifupdate/complex_wrap_split_word", TEST_DIR,
-	                      (GTestDataFunc) test12_complex_wrap_split_word);
-	g_test_add_data_func ("/ifupdate/more_mixed_whitespace", TEST_DIR,
-	                      (GTestDataFunc) test13_more_mixed_whitespace);
-	g_test_add_data_func ("/ifupdate/mixed_whitespace_block_start", TEST_DIR,
-	                      (GTestDataFunc) test14_mixed_whitespace_block_start);
-	g_test_add_data_func ("/ifupdate/trailing_space", TEST_DIR,
-	                      (GTestDataFunc) test15_trailing_space);
-	g_test_add_data_func ("/ifupdate/missing_newline", TEST_DIR,
-	                      (GTestDataFunc) test16_missing_newline);
-	g_test_add_data_func ("/ifupdate/read_static_ipv4", TEST_DIR,
-	                      (GTestDataFunc) test17_read_static_ipv4);
-	g_test_add_data_func ("/ifupdate/read_static_ipv6", TEST_DIR,
-	                      (GTestDataFunc) test18_read_static_ipv6);
-	g_test_add_data_func ("/ifupdate/read_static_ipv4_plen", TEST_DIR,
-	                      (GTestDataFunc) test19_read_static_ipv4_plen);
-	g_test_add_data_func ("/ifupdate/source_stanza", TEST_DIR,
-	                      (GTestDataFunc) test20_source_stanza);
-	g_test_add_data_func ("/ifupdate/source_dir_stanza", TEST_DIR,
-	                      (GTestDataFunc) test21_source_dir_stanza);
+	(void) dump_blocks;
+
+	g_test_add_func ("/ifupdate/ignore_line_before_first_block", test1_ignore_line_before_first_block);
+	g_test_add_func ("/ifupdate/wrapped_line",                   test2_wrapped_line);
+	g_test_add_func ("/ifupdate/wrapped_multiline_multiarg",     test3_wrapped_multiline_multiarg);
+	g_test_add_func ("/ifupdate/allow_auto_is_auto",             test4_allow_auto_is_auto);
+	g_test_add_func ("/ifupdate/allow_auto_multiarg",            test5_allow_auto_multiarg);
+	g_test_add_func ("/ifupdate/mixed_whitespace",               test6_mixed_whitespace);
+	g_test_add_func ("/ifupdate/long_line",                      test7_long_line);
+	g_test_add_func ("/ifupdate/long_line_wrapped",              test8_long_line_wrapped);
+	g_test_add_func ("/ifupdate/wrapped_lines_in_block",         test9_wrapped_lines_in_block);
+	g_test_add_func ("/ifupdate/complex_wrap",                   test11_complex_wrap);
+	g_test_add_func ("/ifupdate/complex_wrap_split_word",        test12_complex_wrap_split_word);
+	g_test_add_func ("/ifupdate/more_mixed_whitespace",          test13_more_mixed_whitespace);
+	g_test_add_func ("/ifupdate/mixed_whitespace_block_start",   test14_mixed_whitespace_block_start);
+	g_test_add_func ("/ifupdate/trailing_space",                 test15_trailing_space);
+	g_test_add_func ("/ifupdate/missing_newline",                test16_missing_newline);
+	g_test_add_func ("/ifupdate/read_static_ipv4",               test17_read_static_ipv4);
+	g_test_add_func ("/ifupdate/read_static_ipv6",               test18_read_static_ipv6);
+	g_test_add_func ("/ifupdate/read_static_ipv4_plen",          test19_read_static_ipv4_plen);
+	g_test_add_func ("/ifupdate/source_stanza",                  test20_source_stanza);
+	g_test_add_func ("/ifupdate/source_dir_stanza",              test21_source_dir_stanza);
 
 	return g_test_run ();
 }
diff --git a/src/settings/plugins/keyfile/nms-keyfile-connection.c b/src/settings/plugins/keyfile/nms-keyfile-connection.c
index 5f72a9fa..64e94b26 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-connection.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-connection.c
@@ -75,9 +75,7 @@ commit_changes (NMSettingsConnection *connection,
 	                                    error))
 		return FALSE;
 
-	/* Update the filename if it changed */
-	if (   path
-	    && g_strcmp0 (path, nm_settings_connection_get_filename (connection)) != 0) {
+	if (!nm_streq0 (path, nm_settings_connection_get_filename (connection))) {
 		gs_free char *old_path = g_strdup (nm_settings_connection_get_filename (connection));
 
 		nm_settings_connection_set_filename (connection, path);
@@ -142,7 +140,7 @@ nms_keyfile_connection_new (NMConnection *source,
 		if (!tmp)
 			return NULL;
 
-		uuid = nm_connection_get_uuid (NM_CONNECTION (tmp));
+		uuid = nm_connection_get_uuid (tmp);
 		if (!uuid) {
 			g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
 			             "Connection in file %s had no UUID", full_path);
@@ -154,9 +152,9 @@ nms_keyfile_connection_new (NMConnection *source,
 		update_unsaved = FALSE;
 	}
 
-	object = (GObject *) g_object_new (NMS_TYPE_KEYFILE_CONNECTION,
-	                                   NM_SETTINGS_CONNECTION_FILENAME, full_path,
-	                                   NULL);
+	object = g_object_new (NMS_TYPE_KEYFILE_CONNECTION,
+	                       NM_SETTINGS_CONNECTION_FILENAME, full_path,
+	                       NULL);
 
 	/* Update our settings with what was read from the file */
 	if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (object),
diff --git a/src/settings/plugins/keyfile/nms-keyfile-plugin.c b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
index 3723db94..89b89467 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-plugin.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-plugin.c
@@ -56,19 +56,15 @@ typedef struct {
 } NMSKeyfilePluginPrivate;
 
 struct _NMSKeyfilePlugin {
-	GObject parent;
+	NMSettingsPlugin parent;
 	NMSKeyfilePluginPrivate _priv;
 };
 
 struct _NMSKeyfilePluginClass {
-	GObjectClass parent;
+	NMSettingsPluginClass parent;
 };
 
-static void settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface);
-
-G_DEFINE_TYPE_EXTENDED (NMSKeyfilePlugin, nms_keyfile_plugin, G_TYPE_OBJECT, 0,
-                        G_IMPLEMENT_INTERFACE (NM_TYPE_SETTINGS_PLUGIN,
-                                               settings_plugin_interface_init))
+G_DEFINE_TYPE (NMSKeyfilePlugin, nms_keyfile_plugin, NM_TYPE_SETTINGS_PLUGIN)
 
 #define NMS_KEYFILE_PLUGIN_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSKeyfilePlugin, NMS_IS_KEYFILE_PLUGIN)
 
@@ -85,10 +81,10 @@ G_DEFINE_TYPE_EXTENDED (NMSKeyfilePlugin, nms_keyfile_plugin, G_TYPE_OBJECT, 0,
 /*****************************************************************************/
 
 static void
-connection_removed_cb (NMSettingsConnection *obj, gpointer user_data)
+connection_removed_cb (NMSettingsConnection *sett_conn, NMSKeyfilePlugin *self)
 {
-	g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE ((NMSKeyfilePlugin *) user_data)->connections,
-	                     nm_connection_get_uuid (NM_CONNECTION (obj)));
+	g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE (self)->connections,
+	                     nm_settings_connection_get_uuid (sett_conn));
 }
 
 /* Monitoring */
@@ -106,7 +102,7 @@ remove_connection (NMSKeyfilePlugin *self, NMSKeyfileConnection *connection)
 	g_object_ref (connection);
 	g_signal_handlers_disconnect_by_func (connection, connection_removed_cb, self);
 	removed = g_hash_table_remove (NMS_KEYFILE_PLUGIN_GET_PRIVATE (self)->connections,
-	                               nm_connection_get_uuid (NM_CONNECTION (connection)));
+	                               nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection)));
 	nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection));
 	g_object_unref (connection);
 
@@ -197,7 +193,7 @@ update_connection (NMSKeyfilePlugin *self,
 		return NULL;
 	}
 
-	uuid = nm_connection_get_uuid (NM_CONNECTION (connection_new));
+	uuid = nm_settings_connection_get_uuid (NM_SETTINGS_CONNECTION (connection_new));
 	connection_by_uuid = g_hash_table_lookup (priv->connections, uuid);
 
 	if (   connection
@@ -240,8 +236,8 @@ update_connection (NMSKeyfilePlugin *self,
 
 		old_path = nm_settings_connection_get_filename (NM_SETTINGS_CONNECTION (connection_by_uuid));
 
-		if (nm_connection_compare (NM_CONNECTION (connection_by_uuid),
-		                           NM_CONNECTION (connection_new),
+		if (nm_connection_compare (nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_by_uuid)),
+		                           nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
 		                           NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
 		                           NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
 			/* Nothing to do... except updating the path. */
@@ -259,7 +255,7 @@ update_connection (NMSKeyfilePlugin *self,
 				_LOGI ("update and persist "NMS_KEYFILE_CONNECTION_LOG_FMT, NMS_KEYFILE_CONNECTION_LOG_ARG (connection_new));
 
 			if (!nm_settings_connection_update (NM_SETTINGS_CONNECTION (connection_by_uuid),
-			                                    NM_CONNECTION (connection_new),
+			                                    nm_settings_connection_get_connection (NM_SETTINGS_CONNECTION (connection_new)),
 			                                    NM_SETTINGS_CONNECTION_PERSIST_MODE_KEEP_SAVED,
 			                                    NM_SETTINGS_CONNECTION_COMMIT_REASON_NONE,
 			                                    "keyfile-update",
@@ -287,8 +283,10 @@ update_connection (NMSKeyfilePlugin *self,
 		if (!source) {
 			/* Only raise the signal if we were called without source, i.e. if we read the connection from file.
 			 * Otherwise, we were called by add_connection() which does not expect the signal. */
-			g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_CONNECTION_ADDED, connection_new);
+			_nm_settings_plugin_emit_signal_connection_added (NM_SETTINGS_PLUGIN (self),
+			                                                  NM_SETTINGS_CONNECTION (connection_new));
 		}
+
 		return connection_new;
 	}
 }
@@ -341,13 +339,14 @@ config_changed_cb (NMConfig *config,
                    NMConfigData *old_data,
                    NMSKeyfilePlugin *self)
 {
-	gs_free char *old_value = NULL, *new_value = NULL;
+	gs_free char *old_value = NULL;
+	gs_free char *new_value = NULL;
 
 	old_value = nm_config_data_get_value (old_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC);
 	new_value = nm_config_data_get_value (config_data, NM_CONFIG_KEYFILE_GROUP_KEYFILE, NM_CONFIG_KEYFILE_KEY_KEYFILE_UNMANAGED_DEVICES, NM_CONFIG_GET_VALUE_TYPE_SPEC);
 
-	if (g_strcmp0 (old_value, new_value) != 0)
-		g_signal_emit_by_name (self, NM_SETTINGS_PLUGIN_UNMANAGED_SPECS_CHANGED);
+	if (!nm_streq0 (old_value, new_value))
+		_nm_settings_plugin_emit_signal_unmanaged_specs_changed (NM_SETTINGS_PLUGIN (self));
 }
 
 static void
@@ -616,20 +615,17 @@ dispose (GObject *object)
 }
 
 static void
-nms_keyfile_plugin_class_init (NMSKeyfilePluginClass *req_class)
+nms_keyfile_plugin_class_init (NMSKeyfilePluginClass *klass)
 {
-	GObjectClass *object_class = G_OBJECT_CLASS (req_class);
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingsPluginClass *plugin_class = NM_SETTINGS_PLUGIN_CLASS (klass);
 
 	object_class->constructed = constructed;
-	object_class->dispose = dispose;
-}
+	object_class->dispose     = dispose;
 
-static void
-settings_plugin_interface_init (NMSettingsPluginInterface *plugin_iface)
-{
-	plugin_iface->get_connections = get_connections;
-	plugin_iface->load_connection = load_connection;
-	plugin_iface->reload_connections = reload_connections;
-	plugin_iface->add_connection = add_connection;
-	plugin_iface->get_unmanaged_specs = get_unmanaged_specs;
+	plugin_class->get_connections     = get_connections;
+	plugin_class->load_connection     = load_connection;
+	plugin_class->reload_connections  = reload_connections;
+	plugin_class->add_connection      = add_connection;
+	plugin_class->get_unmanaged_specs = get_unmanaged_specs;
 }
diff --git a/src/settings/plugins/keyfile/nms-keyfile-reader.c b/src/settings/plugins/keyfile/nms-keyfile-reader.c
index cb4b8379..71578686 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-reader.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-reader.c
@@ -115,7 +115,7 @@ nms_keyfile_reader_from_keyfile (GKeyFile *key_file,
 NMConnection *
 nms_keyfile_reader_from_file (const char *filename, GError **error)
 {
-	GKeyFile *key_file;
+	gs_unref_keyfile GKeyFile *key_file = NULL;
 	struct stat statbuf;
 	NMConnection *connection = NULL;
 	GError *verify_error = NULL;
@@ -144,11 +144,11 @@ nms_keyfile_reader_from_file (const char *filename, GError **error)
 
 	key_file = g_key_file_new ();
 	if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, error))
-		goto out;
+		return NULL;
 
 	connection = nms_keyfile_reader_from_keyfile (key_file, filename, TRUE, error);
 	if (!connection)
-		goto out;
+		return NULL;
 
 	/* Normalize and verify the connection */
 	if (!nm_connection_normalize (connection, NULL, NULL, &verify_error)) {
@@ -160,8 +160,6 @@ nms_keyfile_reader_from_file (const char *filename, GError **error)
 		connection = NULL;
 	}
 
-out:
-	g_key_file_free (key_file);
 	return connection;
 }
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-utils.h b/src/settings/plugins/keyfile/nms-keyfile-utils.h
index cd3f42b9..d5ddb8d5 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-utils.h
+++ b/src/settings/plugins/keyfile/nms-keyfile-utils.h
@@ -25,9 +25,9 @@
 
 #define NMS_KEYFILE_CONNECTION_LOG_PATH(path)  ((path) ?: "in-memory")
 #define NMS_KEYFILE_CONNECTION_LOG_FMT         "%s (%s,\"%s\")"
-#define NMS_KEYFILE_CONNECTION_LOG_ARG(con)    NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_connection_get_uuid ((NMConnection *) (con)), nm_connection_get_id ((NMConnection *) (con))
+#define NMS_KEYFILE_CONNECTION_LOG_ARG(con)    NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con))
 #define NMS_KEYFILE_CONNECTION_LOG_FMTD        "%s (%s,\"%s\",%p)"
-#define NMS_KEYFILE_CONNECTION_LOG_ARGD(con)   NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_connection_get_uuid ((NMConnection *) (con)), nm_connection_get_id ((NMConnection *) (con)), (con)
+#define NMS_KEYFILE_CONNECTION_LOG_ARGD(con)   NMS_KEYFILE_CONNECTION_LOG_PATH (nm_settings_connection_get_filename ((NMSettingsConnection *) (con))), nm_settings_connection_get_uuid ((NMSettingsConnection *) (con)), nm_settings_connection_get_id ((NMSettingsConnection *) (con)), (con)
 
 gboolean nms_keyfile_utils_should_ignore_file (const char *filename);
 
diff --git a/src/settings/plugins/keyfile/nms-keyfile-writer.c b/src/settings/plugins/keyfile/nms-keyfile-writer.c
index 270a217e..194b97d4 100644
--- a/src/settings/plugins/keyfile/nms-keyfile-writer.c
+++ b/src/settings/plugins/keyfile/nms-keyfile-writer.c
@@ -34,6 +34,8 @@
 #include "nms-keyfile-utils.h"
 #include "nms-keyfile-reader.h"
 
+#include "nm-utils/nm-io-utils.h"
+
 /*****************************************************************************/
 
 typedef struct {
@@ -127,7 +129,7 @@ cert_writer (NMConnection *connection,
 		new_path = g_strdup_printf ("%s/%s-%s.%s", info->keyfile_dir, nm_connection_get_uuid (connection),
 		                            cert_data->vtable->file_suffix, ext);
 
-		success = nm_utils_file_set_contents (new_path, (const gchar *) blob_data,
+		success = nm_utils_file_set_contents (new_path, (const char *) blob_data,
 		                                      blob_len, 0600, &local);
 		if (success) {
 			/* Write the path value to the keyfile.
@@ -297,11 +299,6 @@ _internal_write_connection (NMConnection *connection,
 		return FALSE;
 	}
 
-	if (out_path && g_strcmp0 (existing_path, path)) {
-		*out_path = path;  /* pass path out to caller */
-		path = NULL;
-	}
-
 	if (out_reread || out_reread_same)
 	{
 		gs_unref_object NMConnection *reread = NULL;
@@ -333,6 +330,8 @@ _internal_write_connection (NMConnection *connection,
 		NM_SET_OUT (out_reread_same, reread_same);
 	}
 
+	NM_SET_OUT (out_path, g_steal_pointer (&path));
+
 	return TRUE;
 }
 
diff --git a/src/settings/plugins/keyfile/tests/meson.build b/src/settings/plugins/keyfile/tests/meson.build
index 33aaa264..8b94b256 100644
--- a/src/settings/plugins/keyfile/tests/meson.build
+++ b/src/settings/plugins/keyfile/tests/meson.build
@@ -6,7 +6,6 @@ exe = executable(
   test_unit,
   test_unit + '.c',
   dependencies: test_nm_dep,
-  c_args: nm_build_cflags,
 )
 
 test(
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index a3045f7d..b4c6b1e2 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -960,7 +960,7 @@ test_write_intlist_ssid (void)
 	unsigned char tmpssid[] = { 65, 49, 50, 51, 0, 50, 50 };
 	gs_free_error GError *error = NULL;
 	gs_unref_keyfile GKeyFile *keyfile = NULL;
-	gint *intlist;
+	int *intlist;
 	gsize len = 0, i;
 
 	connection = nm_simple_connection_new ();
@@ -1018,27 +1018,20 @@ test_read_intlike_ssid (void)
 	gs_free_error GError *error = NULL;
 	gboolean success;
 	GBytes *ssid;
-	const guint8 *ssid_data;
-	gsize ssid_len;
 	const char *expected_ssid = "101";
 
 	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Intlike_SSID", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
+	nmtst_assert_success (connection, error);
 
 	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	nmtst_assert_success (success, error);
 
-	/* SSID */
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wifi);
 
 	ssid = nm_setting_wireless_get_ssid (s_wifi);
-	g_assert (ssid != NULL);
-	ssid_data = g_bytes_get_data (ssid, &ssid_len);
-	g_assert_cmpint (ssid_len, ==, strlen (expected_ssid));
-	g_assert_cmpint (memcmp (ssid_data, expected_ssid, strlen (expected_ssid)), ==, 0);
+	g_assert (ssid);
+	g_assert (nm_utils_gbytes_equal_mem (ssid, expected_ssid, strlen (expected_ssid)));
 }
 
 static void
@@ -1049,27 +1042,20 @@ test_read_intlike_ssid_2 (void)
 	gs_free_error GError *error = NULL;
 	gboolean success;
 	GBytes *ssid;
-	const guint8 *ssid_data;
-	gsize ssid_len;
 	const char *expected_ssid = "11;12;13;";
 
 	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR "/Test_Intlike_SSID_2", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
+	nmtst_assert_success (connection, error);
 
 	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	nmtst_assert_success (success, error);
 
-	/* SSID */
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wifi);
 
 	ssid = nm_setting_wireless_get_ssid (s_wifi);
-	g_assert (ssid != NULL);
-	ssid_data = g_bytes_get_data (ssid, &ssid_len);
-	g_assert_cmpint (ssid_len, ==, strlen (expected_ssid));
-	g_assert_cmpint (memcmp (ssid_data, expected_ssid, strlen (expected_ssid)), ==, 0);
+	g_assert (ssid);
+	g_assert (nm_utils_gbytes_equal_mem (ssid, expected_ssid, strlen (expected_ssid)));
 }
 
 static void
@@ -1787,7 +1773,8 @@ test_write_wired_8021x_tls_connection_blob (void)
 	const char *uuid;
 	gboolean reread_same = FALSE;
 	gs_free_error GError *error = NULL;
-	GBytes *password_raw = NULL;
+	GBytes *password_raw;
+
 #define PASSWORD_RAW "password-raw\0test"
 
 	connection = create_wired_tls_connection (NM_SETTING_802_1X_CK_SCHEME_BLOB);
@@ -1846,8 +1833,7 @@ test_write_wired_8021x_tls_connection_blob (void)
 
 	password_raw = nm_setting_802_1x_get_password_raw (s_8021x);
 	g_assert (password_raw);
-	g_assert (g_bytes_get_size (password_raw) == NM_STRLEN (PASSWORD_RAW));
-	g_assert (!memcmp (g_bytes_get_data (password_raw, NULL), PASSWORD_RAW, NM_STRLEN (PASSWORD_RAW)));
+	g_assert (nm_utils_gbytes_equal_mem (password_raw, PASSWORD_RAW, NM_STRLEN (PASSWORD_RAW)));
 
 	unlink (testfile);
 
@@ -2186,7 +2172,7 @@ test_write_new_wired_group_name (void)
 	gs_free char *testfile = NULL;
 	gs_free_error GError *error = NULL;
 	char *s;
-	gint mtu;
+	int mtu;
 
 	connection = nm_simple_connection_new ();
 	g_assert (connection);
@@ -2234,32 +2220,25 @@ test_read_new_wireless_group_names (void)
 	NMSettingWireless *s_wifi;
 	NMSettingWirelessSecurity *s_wsec;
 	GBytes *ssid;
-	const guint8 *ssid_data;
-	gsize ssid_len;
 	const char *expected_ssid = "foobar";
 	gs_free_error GError *error = NULL;
 	gboolean success;
 
 	connection = nms_keyfile_reader_from_file (TEST_KEYFILES_DIR"/Test_New_Wireless_Group_Names", &error);
-	g_assert_no_error (error);
-	g_assert (connection);
+	nmtst_assert_success (connection, error);
+
 	success = nm_connection_verify (connection, &error);
-	g_assert_no_error (error);
-	g_assert (success);
+	nmtst_assert_success (success, error);
 
-	/* Wifi setting */
 	s_wifi = nm_connection_get_setting_wireless (connection);
 	g_assert (s_wifi);
 
 	ssid = nm_setting_wireless_get_ssid (s_wifi);
 	g_assert (ssid);
-	ssid_data = g_bytes_get_data (ssid, &ssid_len);
-	g_assert_cmpint (ssid_len, ==, strlen (expected_ssid));
-	g_assert_cmpint (memcmp (ssid_data, expected_ssid, ssid_len), ==, 0);
+	g_assert (nm_utils_gbytes_equal_mem (ssid, expected_ssid, strlen (expected_ssid)));
 
 	g_assert_cmpstr (nm_setting_wireless_get_mode (s_wifi), ==, NM_SETTING_WIRELESS_MODE_INFRA);
 
-	/* Wifi security setting */
 	s_wsec = nm_connection_get_setting_wireless_security (connection);
 	g_assert (s_wsec);
 	g_assert_cmpstr (nm_setting_wireless_security_get_key_mgmt (s_wsec), ==, "wpa-psk");