about summary refs log tree commit diff
path: root/src/settings/plugins/keyfile
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
committerMichael Biebl <biebl@debian.org>2014-07-06 02:16:10 +0200
commit33491bc4279481db8ae47213e34a6d695a0e8830 (patch)
tree097d2b0fdff3fae6885381ae5e57a182cd8cbbba /src/settings/plugins/keyfile
parent59c3714a494c3b3765657c0551ad82842d98a7d2 (diff)
Imported Upstream version 0.9.10.0 upstream/0.9.10.0
Diffstat (limited to 'src/settings/plugins/keyfile')
-rw-r--r--src/settings/plugins/keyfile/Makefile.am8
-rw-r--r--src/settings/plugins/keyfile/Makefile.in29
-rw-r--r--src/settings/plugins/keyfile/common.h4
-rw-r--r--src/settings/plugins/keyfile/nm-keyfile-connection.c38
-rw-r--r--src/settings/plugins/keyfile/nm-keyfile-connection.h4
-rw-r--r--src/settings/plugins/keyfile/plugin.c603
-rw-r--r--src/settings/plugins/keyfile/plugin.h2
-rw-r--r--src/settings/plugins/keyfile/reader.c407
-rw-r--r--src/settings/plugins/keyfile/tests/Makefile.am28
-rw-r--r--src/settings/plugins/keyfile/tests/Makefile.in237
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Makefile.am4
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Makefile.in25
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_MAC_IB_Old_Format13
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_MAC_Old_Format10
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_New_Wired_Group_Name13
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_New_Wireless_Group_Names16
-rw-r--r--src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection22
-rw-r--r--src/settings/plugins/keyfile/tests/test-keyfile.c545
-rw-r--r--src/settings/plugins/keyfile/utils.c176
-rw-r--r--src/settings/plugins/keyfile/utils.h49
-rw-r--r--src/settings/plugins/keyfile/writer.c337
21 files changed, 1842 insertions, 728 deletions
diff --git a/src/settings/plugins/keyfile/Makefile.am b/src/settings/plugins/keyfile/Makefile.am
index b0cd688a..cac97e7f 100644
--- a/src/settings/plugins/keyfile/Makefile.am
+++ b/src/settings/plugins/keyfile/Makefile.am
@@ -1,13 +1,21 @@
 SUBDIRS = . tests
 
+@GNOME_CODE_COVERAGE_RULES@
+
 AM_CPPFLAGS = \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/config \
+	-I$(top_srcdir)/src/logging \
 	-I$(top_srcdir)/src/settings \
 	-I$(top_srcdir)/include \
 	-I$(top_builddir)/include \
 	-I$(top_srcdir)/libnm-util \
 	-I$(top_builddir)/libnm-util \
+	-DG_LOG_DOMAIN=\""NetworkManager-keyfile"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(GLIB_CFLAGS) \
 	$(DBUS_CFLAGS) \
+	$(POLKIT_CFLAGS) \
 	-DNMCONFDIR=\"$(nmconfdir)\"
 
 noinst_LTLIBRARIES = \
diff --git a/src/settings/plugins/keyfile/Makefile.in b/src/settings/plugins/keyfile/Makefile.in
index 79dfcb42..8c4d6101 100644
--- a/src/settings/plugins/keyfile/Makefile.in
+++ b/src/settings/plugins/keyfile/Makefile.in
@@ -82,7 +82,8 @@ subdir = src/settings/plugins/keyfile
 DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
 	$(top_srcdir)/build-aux/depcomp
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_warnings.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -250,12 +251,15 @@ CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 DATADIRNAME = @DATADIRNAME@
 DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
+DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
 DBUS_LIBS = @DBUS_LIBS@
 DBUS_SYS_DIR = @DBUS_SYS_DIR@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
+DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DSYMUTIL = @DSYMUTIL@
@@ -280,7 +284,6 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LIBS = @GNUTLS_LIBS@
 GREP = @GREP@
 GTKDOC_CHECK = @GTKDOC_CHECK@
-GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
 GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
 GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
 GTKDOC_MKPDF = @GTKDOC_MKPDF@
@@ -325,12 +328,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNDP_CFLAGS = @LIBNDP_CFLAGS@
+LIBNDP_LIBS = @LIBNDP_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
+LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
@@ -347,6 +354,8 @@ MOC = @MOC@
 MSGFMT = @MSGFMT@
 MSGFMT_015 = @MSGFMT_015@
 MSGMERGE = @MSGMERGE@
+NEWT_CFLAGS = @NEWT_CFLAGS@
+NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
@@ -374,11 +383,16 @@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
 POLKIT_CFLAGS = @POLKIT_CFLAGS@
 POLKIT_LIBS = @POLKIT_LIBS@
 POSUB = @POSUB@
+PPPD_PATH = @PPPD_PATH@
 PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+PPPOE_PATH = @PPPOE_PATH@
 QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
 SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
@@ -393,6 +407,7 @@ UDEV_BASE_DIR = @UDEV_BASE_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
+VALGRIND_RULES = @VALGRIND_RULES@
 VAPIGEN = @VAPIGEN@
 VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
 VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
@@ -455,6 +470,7 @@ psdir = @psdir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
+subdirs = @subdirs@
 sysconfdir = @sysconfdir@
 systemdsystemunitdir = @systemdsystemunitdir@
 target_alias = @target_alias@
@@ -465,15 +481,22 @@ with_dhclient = @with_dhclient@
 with_dhcpcd = @with_dhcpcd@
 with_netconfig = @with_netconfig@
 with_resolvconf = @with_resolvconf@
+with_valgrind = @with_valgrind@
 SUBDIRS = . tests
 AM_CPPFLAGS = \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/config \
+	-I$(top_srcdir)/src/logging \
 	-I$(top_srcdir)/src/settings \
 	-I$(top_srcdir)/include \
 	-I$(top_builddir)/include \
 	-I$(top_srcdir)/libnm-util \
 	-I$(top_builddir)/libnm-util \
+	-DG_LOG_DOMAIN=\""NetworkManager-keyfile"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	$(GLIB_CFLAGS) \
 	$(DBUS_CFLAGS) \
+	$(POLKIT_CFLAGS) \
 	-DNMCONFDIR=\"$(nmconfdir)\"
 
 noinst_LTLIBRARIES = \
@@ -881,6 +904,8 @@ uninstall-am:
 	tags tags-am uninstall uninstall-am
 
 
+@GNOME_CODE_COVERAGE_RULES@
+
 install-data-hook:
 	$(mkinstalldirs) -m 0755 $(DESTDIR)$(keyfiledir)
 
diff --git a/src/settings/plugins/keyfile/common.h b/src/settings/plugins/keyfile/common.h
index d48c32f2..4faa5a84 100644
--- a/src/settings/plugins/keyfile/common.h
+++ b/src/settings/plugins/keyfile/common.h
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * (C) Copyright 2008 - 2010 Red Hat, Inc.
+ * (C) Copyright 2008 - 2013 Red Hat, Inc.
  */
 
 #ifndef __COMMON_H__
@@ -24,7 +24,7 @@
 #include <glib.h>
 
 #define KEYFILE_PLUGIN_NAME "keyfile"
-#define KEYFILE_PLUGIN_INFO "(c) 2007 - 2010 Red Hat, Inc.  To report bugs please use the NetworkManager mailing list."
+#define KEYFILE_PLUGIN_INFO "(c) 2007 - 2013 Red Hat, Inc.  To report bugs please use the NetworkManager mailing list."
 
 #define KEYFILE_DIR NMCONFDIR "/system-connections"
 
diff --git a/src/settings/plugins/keyfile/nm-keyfile-connection.c b/src/settings/plugins/keyfile/nm-keyfile-connection.c
index 6d72bbfe..4ef761c6 100644
--- a/src/settings/plugins/keyfile/nm-keyfile-connection.c
+++ b/src/settings/plugins/keyfile/nm-keyfile-connection.c
@@ -41,16 +41,17 @@ typedef struct {
 } NMKeyfileConnectionPrivate;
 
 NMKeyfileConnection *
-nm_keyfile_connection_new (const char *full_path,
-                           NMConnection *source,
+nm_keyfile_connection_new (NMConnection *source,
+                           const char *full_path,
                            GError **error)
 {
 	GObject *object;
 	NMKeyfileConnectionPrivate *priv;
 	NMConnection *tmp;
 	const char *uuid;
+	gboolean update_unsaved = TRUE;
 
-	g_return_val_if_fail (full_path != NULL, NULL);
+	g_assert (source || full_path);
 
 	/* If we're given a connection already, prefer that instead of re-reading */
 	if (source)
@@ -59,31 +60,33 @@ nm_keyfile_connection_new (const char *full_path,
 		tmp = nm_keyfile_plugin_connection_from_file (full_path, error);
 		if (!tmp)
 			return NULL;
+
+		uuid = nm_connection_get_uuid (NM_CONNECTION (tmp));
+		if (!uuid) {
+			g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
+			             "Connection in file %s had no UUID", full_path);
+			g_object_unref (tmp);
+			return NULL;
+		}
+
+		/* If we just read the connection from disk, it's clearly not Unsaved */
+		update_unsaved = FALSE;
 	}
 
 	object = (GObject *) g_object_new (NM_TYPE_KEYFILE_CONNECTION, NULL);
-	if (!object)
-		goto out;
 
 	priv = NM_KEYFILE_CONNECTION_GET_PRIVATE (object);
 	priv->path = g_strdup (full_path);
 
 	/* Update our settings with what was read from the file */
-	if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object), tmp, error)) {
-		g_object_unref (object);
-		object = NULL;
-		goto out;
-	}
-
-	uuid = nm_connection_get_uuid (NM_CONNECTION (object));
-	if (!uuid) {
-		g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
-		             "Connection in file %s had no UUID", full_path);
+	if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (object),
+	                                              tmp,
+	                                              update_unsaved,
+	                                              error)) {
 		g_object_unref (object);
 		object = NULL;
 	}
 
-out:
 	g_object_unref (tmp);
 	return (NMKeyfileConnection *) object;
 }
@@ -145,7 +148,8 @@ do_delete (NMSettingsConnection *connection,
 {
 	NMKeyfileConnectionPrivate *priv = NM_KEYFILE_CONNECTION_GET_PRIVATE (connection);
 
-	g_unlink (priv->path);
+	if (priv->path)
+		g_unlink (priv->path);
 
 	NM_SETTINGS_CONNECTION_CLASS (nm_keyfile_connection_parent_class)->delete (connection,
 	                                                                           callback,
diff --git a/src/settings/plugins/keyfile/nm-keyfile-connection.h b/src/settings/plugins/keyfile/nm-keyfile-connection.h
index e3bce11b..07dcafcb 100644
--- a/src/settings/plugins/keyfile/nm-keyfile-connection.h
+++ b/src/settings/plugins/keyfile/nm-keyfile-connection.h
@@ -43,8 +43,8 @@ typedef struct {
 
 GType nm_keyfile_connection_get_type (void);
 
-NMKeyfileConnection *nm_keyfile_connection_new (const char *filename,
-                                                NMConnection *source,
+NMKeyfileConnection *nm_keyfile_connection_new (NMConnection *source,
+                                                const char *filename,
                                                 GError **error);
 
 const char *nm_keyfile_connection_get_path (NMKeyfileConnection *self);
diff --git a/src/settings/plugins/keyfile/plugin.c b/src/settings/plugins/keyfile/plugin.c
index ef6613e6..10c83971 100644
--- a/src/settings/plugins/keyfile/plugin.c
+++ b/src/settings/plugins/keyfile/plugin.c
@@ -16,7 +16,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
  * Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2008 - 2012 Red Hat, Inc.
+ * Copyright (C) 2008 - 2013 Red Hat, Inc.
  */
 
 #include <config.h>
@@ -34,6 +34,9 @@
 #include <nm-connection.h>
 #include <nm-setting.h>
 #include <nm-setting-connection.h>
+#include <nm-utils.h>
+#include <nm-config.h>
+#include <nm-logging.h>
 
 #include "plugin.h"
 #include "nm-system-config-interface.h"
@@ -52,12 +55,13 @@ G_DEFINE_TYPE_EXTENDED (SCPluginKeyfile, sc_plugin_keyfile, G_TYPE_OBJECT, 0,
 #define SC_PLUGIN_KEYFILE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SC_TYPE_PLUGIN_KEYFILE, SCPluginKeyfilePrivate))
 
 typedef struct {
-	GHashTable *hash;
+	GHashTable *connections;  /* uuid::connection */
 
+	gboolean initialized;
 	GFileMonitor *monitor;
 	guint monitor_id;
 
-	char *conf_file;
+	const char *conf_file;
 	GFileMonitor *conf_file_monitor;
 	guint conf_file_monitor_id;
 
@@ -66,127 +70,132 @@ typedef struct {
 	gboolean disposed;
 } SCPluginKeyfilePrivate;
 
-static NMSettingsConnection *
-_internal_new_connection (SCPluginKeyfile *self,
-                          const char *full_path,
-                          NMConnection *source,
-                          GError **error)
+static void
+connection_removed_cb (NMSettingsConnection *obj, gpointer user_data)
 {
-	SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (self);
-	NMKeyfileConnection *connection;
+	g_hash_table_remove (SC_PLUGIN_KEYFILE_GET_PRIVATE (user_data)->connections,
+	                     nm_connection_get_uuid (NM_CONNECTION (obj)));
+}
 
-	g_return_val_if_fail (full_path != NULL, NULL);
+/* Monitoring */
 
-	connection = nm_keyfile_connection_new (full_path, source, error);
-	if (connection) {
-		g_hash_table_insert (priv->hash,
-		                     (gpointer) nm_keyfile_connection_get_path (connection),
-		                     connection);
-	}
+static void
+remove_connection (SCPluginKeyfile *self, NMKeyfileConnection *connection)
+{
+	gboolean removed;
+
+	g_return_if_fail (connection != NULL);
+
+	nm_log_info (LOGD_SETTINGS, "removed %s.", nm_keyfile_connection_get_path (connection));
 
-	return (NMSettingsConnection *) connection;
+	/* Removing from the hash table should drop the last reference */
+	g_object_ref (connection);
+	g_signal_handlers_disconnect_by_func (connection, connection_removed_cb, self);
+	removed = g_hash_table_remove (SC_PLUGIN_KEYFILE_GET_PRIVATE (self)->connections,
+	                               nm_connection_get_uuid (NM_CONNECTION (connection)));
+	nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection));
+	g_object_unref (connection);
+
+	g_return_if_fail (removed);
 }
 
 static void
-read_connections (NMSystemConfigInterface *config)
+update_connection (SCPluginKeyfile *self,
+                   NMKeyfileConnection *connection,
+                   const char *name)
 {
-	SCPluginKeyfile *self = SC_PLUGIN_KEYFILE (config);
-	GDir *dir;
+	NMKeyfileConnection *tmp;
 	GError *error = NULL;
-	const char *item;
 
-	dir = g_dir_open (KEYFILE_DIR, 0, &error);
-	if (!dir) {
-		PLUGIN_WARN (KEYFILE_PLUGIN_NAME, "Cannot read directory '%s': (%d) %s",
-		             KEYFILE_DIR,
-		             error ? error->code : -1,
-		             error && error->message ? error->message : "(unknown)");
+	tmp = nm_keyfile_connection_new (NULL, name, &error);
+	if (!tmp) {
+		/* Error; remove the connection */
+		nm_log_warn (LOGD_SETTINGS, "    error in connection %s: %s", name,
+		             (error && error->message) ? error->message : "(unknown)");
 		g_clear_error (&error);
+		remove_connection (self, connection);
 		return;
 	}
 
-	while ((item = g_dir_read_name (dir))) {
-		NMSettingsConnection *connection;
-		char *full_path;
-
-		if (nm_keyfile_plugin_utils_should_ignore_file (item))
-			continue;
-
-		full_path = g_build_filename (KEYFILE_DIR, item, NULL);
-		PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "parsing %s ... ", item);
-
-		connection = _internal_new_connection (self, full_path, NULL, &error);
-		if (connection) {
-			PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "    read connection '%s'",
-			              nm_connection_get_id (NM_CONNECTION (connection)));
-		} else {
-			PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "    error: %s",
-				          (error && error->message) ? error->message : "(unknown)");
+	if (!nm_connection_compare (NM_CONNECTION (connection),
+	                            NM_CONNECTION (tmp),
+	                            NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
+		                          NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
+		nm_log_info (LOGD_SETTINGS, "updating %s", name);
+		if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (connection),
+		                                              NM_CONNECTION (tmp),
+		                                              FALSE,  /* don't set Unsaved */
+		                                              &error)) {
+			/* Shouldn't ever get here as 'new' was verified by the reader already */
+			g_assert_no_error (error);
 		}
-		g_clear_error (&error);
-		g_free (full_path);
 	}
-	g_dir_close (dir);
+	g_object_unref (tmp);
 }
 
-static void
-update_connection_settings_commit_cb (NMSettingsConnection *orig, GError *error, gpointer user_data)
+static NMKeyfileConnection *
+find_by_path (SCPluginKeyfile *self, const char *path)
 {
-	if (error) {
-		g_warning ("%s: '%s' / '%s' invalid: %d",
-		       	   __func__,
-		       	   error ? g_type_name (nm_connection_lookup_setting_type_by_quark (error->domain)) : "(none)",
-		       	   (error && error->message) ? error->message : "(none)",
-		       	   error ? error->code : -1);
-		g_clear_error (&error);
+	SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (self);
+	GHashTableIter iter;
+	NMKeyfileConnection *candidate = NULL;
 
-		nm_settings_connection_signal_remove (orig);
-	}
-}
+	g_return_val_if_fail (path != NULL, NULL);
 
-static void
-update_connection_settings (NMKeyfileConnection *orig,
-                            NMKeyfileConnection *new)
-{
-	nm_settings_connection_replace_and_commit (NM_SETTINGS_CONNECTION (orig),
-	                                           NM_CONNECTION (new),
-	                                           update_connection_settings_commit_cb, NULL);
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
+		if (g_strcmp0 (path, nm_keyfile_connection_get_path (candidate)) == 0)
+			return candidate;
+	}
+	return NULL;
 }
 
-/* Monitoring */
-
 static void
-remove_connection (SCPluginKeyfile *self,
-                   NMKeyfileConnection *connection,
-                   const char *name)
-{
-	g_return_if_fail (connection != NULL);
-	g_return_if_fail (name != NULL);
-
-	/* Removing from the hash table should drop the last reference */
-	g_object_ref (connection);
-	g_hash_table_remove (SC_PLUGIN_KEYFILE_GET_PRIVATE (self)->hash, name);
-	nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection));
-	g_object_unref (connection);
-}
-
-static NMKeyfileConnection *
-find_by_uuid (SCPluginKeyfile *self, const char *uuid)
+new_connection (SCPluginKeyfile *self,
+                const char *name,
+                char **out_old_path)
 {
 	SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (self);
-	GHashTableIter iter;
-	gpointer data = NULL;
+	NMKeyfileConnection *tmp, *connection;
+	GError *error = NULL;
+	const char *uuid;
 
-	g_return_val_if_fail (uuid != NULL, NULL);
+	if (out_old_path)
+		*out_old_path = NULL;
 
-	g_hash_table_iter_init (&iter, priv->hash);
-	while (g_hash_table_iter_next (&iter, NULL, &data)) {
-		NMConnection *candidate = NM_CONNECTION (data);
+	tmp = nm_keyfile_connection_new (NULL, name, &error);
+	if (!tmp) {
+		nm_log_warn (LOGD_SETTINGS, "    error in connection %s: %s", name,
+		             (error && error->message) ? error->message : "(unknown)");
+		g_clear_error (&error);
+		return;
+	}
 
-		if (strcmp (uuid, nm_connection_get_uuid (candidate)) == 0)
-			return NM_KEYFILE_CONNECTION (candidate);
+	/* Connection renames will show as different paths but same UUID */
+	uuid = nm_connection_get_uuid (NM_CONNECTION (tmp));
+	connection = g_hash_table_lookup (priv->connections, uuid);
+	if (connection) {
+		nm_log_info (LOGD_SETTINGS, "rename %s -> %s", nm_keyfile_connection_get_path (connection), name);
+		if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (connection),
+		                                              NM_CONNECTION (tmp),
+		                                              FALSE,  /* don't set Unsaved */
+		                                              &error)) {
+			/* Shouldn't ever get here as 'tmp' was verified by the reader already */
+			g_assert_no_error (error);
+		}
+		g_object_unref (tmp);
+		if (out_old_path)
+			*out_old_path = g_strdup (nm_keyfile_connection_get_path (connection));
+		nm_keyfile_connection_set_path (connection, name);
+	} else {
+		nm_log_info (LOGD_SETTINGS, "new connection %s", name);
+		g_hash_table_insert (priv->connections, g_strdup (uuid), tmp);
+		g_signal_emit_by_name (self, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, tmp);
+
+		g_signal_connect (tmp, NM_SETTINGS_CONNECTION_REMOVED,
+		                  G_CALLBACK (connection_removed_cb),
+		                  self);
 	}
-	return NULL;
 }
 
 static void
@@ -198,10 +207,8 @@ dir_changed (GFileMonitor *monitor,
 {
 	NMSystemConfigInterface *config = NM_SYSTEM_CONFIG_INTERFACE (user_data);
 	SCPluginKeyfile *self = SC_PLUGIN_KEYFILE (config);
-	SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (self);
-	char *full_path;
 	NMKeyfileConnection *connection;
-	GError *error = NULL;
+	char *full_path;
 
 	full_path = g_file_get_path (file);
 	if (nm_keyfile_plugin_utils_should_ignore_file (full_path)) {
@@ -209,89 +216,19 @@ dir_changed (GFileMonitor *monitor,
 		return;
 	}
 
-	connection = g_hash_table_lookup (priv->hash, full_path);
+	connection = find_by_path (self, full_path);
 
 	switch (event_type) {
 	case G_FILE_MONITOR_EVENT_DELETED:
-		if (connection) {
-			PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "removed %s.", full_path);
-			remove_connection (SC_PLUGIN_KEYFILE (config), connection, full_path);
-		}
+		if (connection)
+			remove_connection (SC_PLUGIN_KEYFILE (config), connection);
 		break;
 	case G_FILE_MONITOR_EVENT_CREATED:
 	case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
-		if (connection) {
-			/* Update */
-			NMKeyfileConnection *tmp;
-
-			tmp = nm_keyfile_connection_new (full_path, NULL, &error);
-			if (tmp) {
-				if (!nm_connection_compare (NM_CONNECTION (connection),
-				                            NM_CONNECTION (tmp),
-				                            NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
-				                              NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)) {
-					PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "updating %s", full_path);
-					update_connection_settings (connection, tmp);
-				}
-				g_object_unref (tmp);
-			} else {
-				/* Error; remove the connection */
-				PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "    error: %s",
-						      (error && error->message) ? error->message : "(unknown)");
-				g_clear_error (&error);
-				remove_connection (SC_PLUGIN_KEYFILE (config), connection, full_path);
-			}
-		} else {
-			PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "updating %s", full_path);
-
-			/* New */
-			connection = nm_keyfile_connection_new (full_path, NULL, &error);
-			if (connection) {
-				NMKeyfileConnection *found = NULL;
-
-				/* Connection renames will show up as different files but with
-				 * the same UUID.  Try to find the original connection.
-				 * A connection rename is treated just like an update except
-				 * there's a bit more housekeeping with the hash table.
-				 */
-				found = find_by_uuid (self, nm_connection_get_uuid (NM_CONNECTION (connection)));
-				if (found) {
-					const char *old_path = nm_keyfile_connection_get_path (connection);
-
-					/* Removing from the hash table should drop the last reference,
-					 * but of course we want to keep the connection around.
-					 */
-					g_object_ref (found);
-					g_hash_table_remove (priv->hash, old_path);
-
-					/* Updating settings should update the NMKeyfileConnection's
-					 * filename property too.
-					 */
-					update_connection_settings (found, connection);
-					/* However, when connections are the same and only the filename changed
-					 * we need to update the path manually (commit_changes() is not called.
-					 */
-					nm_keyfile_connection_set_path (found, full_path);
-
-					/* Re-insert the connection back into the hash with the new filename */
-					g_hash_table_insert (priv->hash,
-					                     (gpointer) nm_keyfile_connection_get_path (found),
-					                     found);
-
-					/* Get rid of the temporary connection */
-					g_object_unref (connection);
-				} else {
-					g_hash_table_insert (priv->hash,
-					                     (gpointer) nm_keyfile_connection_get_path (connection),
-					                     connection);
-					g_signal_emit_by_name (config, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, connection);
-				}
-			} else {
-				PLUGIN_PRINT (KEYFILE_PLUGIN_NAME, "    error: %s",
-						      (error && error->message) ? error->message : "(unknown)");
-				g_clear_error (&error);
-			}
-		}
+		if (connection)
+			update_connection (SC_PLUGIN_KEYFILE (config), connection, full_path);
+		else
+			new_connection (SC_PLUGIN_KEYFILE (config), full_path, NULL);
 		break;
 	default:
 		break;
@@ -344,15 +281,15 @@ setup_monitoring (NMSystemConfigInterface *config)
 	GFile *file;
 	GFileMonitor *monitor;
 
-	priv->hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
-
-	file = g_file_new_for_path (KEYFILE_DIR);
-	monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
-	g_object_unref (file);
+	if (nm_config_get_monitor_connection_files (nm_config_get ())) {
+		file = g_file_new_for_path (KEYFILE_DIR);
+		monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+		g_object_unref (file);
 
-	if (monitor) {
-		priv->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (dir_changed), config);
-		priv->monitor = monitor;
+		if (monitor) {
+			priv->monitor_id = g_signal_connect (monitor, "changed", G_CALLBACK (dir_changed), config);
+			priv->monitor = monitor;
+		}
 	}
 
 	if (priv->conf_file) {
@@ -367,6 +304,69 @@ setup_monitoring (NMSystemConfigInterface *config)
 	}
 }
 
+static void
+read_connections (NMSystemConfigInterface *config)
+{
+	SCPluginKeyfile *self = SC_PLUGIN_KEYFILE (config);
+	SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (self);
+	GDir *dir;
+	GError *error = NULL;
+	const char *item;
+	GHashTable *oldconns;
+	GHashTableIter iter;
+	gpointer data;
+
+	dir = g_dir_open (KEYFILE_DIR, 0, &error);
+	if (!dir) {
+		nm_log_warn (LOGD_SETTINGS, "Cannot read directory '%s': (%d) %s",
+		             KEYFILE_DIR,
+		             error ? error->code : -1,
+		             error && error->message ? error->message : "(unknown)");
+		g_clear_error (&error);
+		return;
+	}
+
+	oldconns = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	g_hash_table_iter_init (&iter, priv->connections);
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
+		const char *con_path = nm_keyfile_connection_get_path (data);
+		if (con_path)
+			g_hash_table_insert (oldconns, g_strdup (con_path), data);
+	}
+
+	while ((item = g_dir_read_name (dir))) {
+		NMKeyfileConnection *connection;
+		char *full_path, *old_path;
+
+		if (nm_keyfile_plugin_utils_should_ignore_file (item))
+			continue;
+
+		full_path = g_build_filename (KEYFILE_DIR, item, NULL);
+
+		connection = g_hash_table_lookup (oldconns, full_path);
+		if (connection) {
+			g_hash_table_remove (oldconns, full_path);
+			update_connection (self, connection, full_path);
+		} else {
+			new_connection (self, full_path, &old_path);
+			if (old_path) {
+				g_hash_table_remove (oldconns, old_path);
+				g_free (old_path);
+			}
+		}
+
+		g_free (full_path);
+	}
+	g_dir_close (dir);
+
+	g_hash_table_iter_init (&iter, oldconns);
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
+		g_hash_table_iter_remove (&iter);
+		remove_connection (self, data);
+	}
+	g_hash_table_destroy (oldconns);
+}
+
 /* Plugin */
 
 static GSList *
@@ -377,34 +377,104 @@ get_connections (NMSystemConfigInterface *config)
 	gpointer data = NULL;
 	GSList *list = NULL;
 
-	if (!priv->hash) {
+	if (!priv->initialized) {
 		setup_monitoring (config);
 		read_connections (config);
+		priv->initialized = TRUE;
 	}
 
-	g_hash_table_iter_init (&iter, priv->hash);
+	g_hash_table_iter_init (&iter, priv->connections);
 	while (g_hash_table_iter_next (&iter, NULL, &data))
 		list = g_slist_prepend (list, data);
 	return list;
 }
 
+static gboolean
+load_connection (NMSystemConfigInterface *config,
+                 const char *filename)
+{
+	SCPluginKeyfile *self = SC_PLUGIN_KEYFILE (config);
+	NMKeyfileConnection *connection;
+	int dir_len = strlen (KEYFILE_DIR);
+
+	if (   strncmp (filename, KEYFILE_DIR, dir_len) != 0
+	    || filename[dir_len] != '/'
+	    || strchr (filename + dir_len + 1, '/') != NULL)
+		return FALSE;
+
+	if (nm_keyfile_plugin_utils_should_ignore_file (filename + dir_len + 1))
+		return FALSE;
+
+	connection = find_by_path (self, filename);
+	if (connection)
+		update_connection (self, connection, filename);
+	else {
+		new_connection (self, filename, NULL);
+		connection = find_by_path (self, filename);
+	}
+
+	return (connection != NULL);
+}
+
+static void
+reload_connections (NMSystemConfigInterface *config)
+{
+	read_connections (config);
+}
+
 static NMSettingsConnection *
 add_connection (NMSystemConfigInterface *config,
                 NMConnection *connection,
+                gboolean save_to_disk,
                 GError **error)
 {
 	SCPluginKeyfile *self = SC_PLUGIN_KEYFILE (config);
+	SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (self);
 	NMSettingsConnection *added = NULL;
 	char *path = NULL;
 
-	/* Write it out first, then add the connection to our internal list */
-	if (nm_keyfile_plugin_write_connection (connection, NULL, &path, error)) {
-		added = _internal_new_connection (self, path, connection, error);
-		g_free (path);
+	if (save_to_disk) {
+		if (!nm_keyfile_plugin_write_connection (connection, NULL, &path, error))
+			return NULL;
 	}
+
+	added = (NMSettingsConnection *) nm_keyfile_connection_new (connection, path, error);
+	if (added) {
+		g_hash_table_insert (priv->connections,
+		                     g_strdup (nm_connection_get_uuid (NM_CONNECTION (added))),
+		                     added);
+		g_signal_connect (added, NM_SETTINGS_CONNECTION_REMOVED,
+		                  G_CALLBACK (connection_removed_cb),
+		                  self);
+	}
+	g_free (path);
 	return added;
 }
 
+static gboolean
+parse_key_file_allow_none (SCPluginKeyfilePrivate  *priv,
+                           GKeyFile                *key_file,
+                           GError                 **error)
+{
+	gboolean ret = FALSE;
+	GError *local_error = NULL;
+
+	if (!g_key_file_load_from_file (key_file, priv->conf_file, G_KEY_FILE_NONE, &local_error)) {
+		if (g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+			g_clear_error (&local_error);
+		else {
+			g_propagate_prefixed_error (error, local_error,
+			                            "Error parsing file '%s': ",
+			                            priv->conf_file);
+			goto out;
+		}
+	}
+	ret = TRUE;
+
+ out:
+	return ret;
+}
+
 static GSList *
 get_unmanaged_specs (NMSystemConfigInterface *config)
 {
@@ -412,48 +482,45 @@ get_unmanaged_specs (NMSystemConfigInterface *config)
 	GKeyFile *key_file;
 	GSList *specs = NULL;
 	GError *error = NULL;
+	char *str;
 
 	if (!priv->conf_file)
 		return NULL;
 
 	key_file = g_key_file_new ();
-	if (g_key_file_load_from_file (key_file, priv->conf_file, G_KEY_FILE_NONE, &error)) {
-		char *str;
-
-		str = g_key_file_get_value (key_file, "keyfile", "unmanaged-devices", NULL);
-		if (str) {
-			char **udis;
-			int i;
-
-			udis = g_strsplit (str, ";", -1);
-			g_free (str);
-
-			for (i = 0; udis[i] != NULL; i++) {
-				/* Verify unmanaged specification and add it to the list */
-				if (strlen (udis[i]) > 4 && !strncmp (udis[i], "mac:", 4) && ether_aton (udis[i] + 4)) {
-					char *p = udis[i];
-
-					/* To accept uppercase MACs in configuration file, we have to convert values to lowercase here.
-					 * Unmanaged MACs in specs are always in lowercase. */
-					while (*p) {
-				                *p = g_ascii_tolower (*p);
-				                p++;
-				        }
-					specs = g_slist_append (specs, udis[i]);
-				} else {
-					g_warning ("Error in file '%s': invalid unmanaged-devices entry: '%s'", priv->conf_file, udis[i]);
-					g_free (udis[i]);
-				}
-			}
+	if (!parse_key_file_allow_none (priv, key_file, &error))
+		goto out;
+
+	str = g_key_file_get_value (key_file, "keyfile", "unmanaged-devices", NULL);
+	if (str) {
+		char **udis;
+		int i;
+
+		udis = g_strsplit (str, ";", -1);
+		g_free (str);
 
-			g_free (udis); /* Yes, g_free, not g_strfreev because we need the strings in the list */
+		for (i = 0; udis[i] != NULL; i++) {
+			/* Verify unmanaged specification and add it to the list */
+			if (!strncmp (udis[i], "mac:", 4) && nm_utils_hwaddr_valid (udis[i] + 4)) {
+				specs = g_slist_append (specs, udis[i]);
+			} else if (!strncmp (udis[i], "interface-name:", 15) && nm_utils_iface_valid_name (udis[i] + 15)) {
+				specs = g_slist_append (specs, udis[i]);
+			} else {
+				nm_log_warn (LOGD_SETTINGS, "Error in file '%s': invalid unmanaged-devices entry: '%s'", priv->conf_file, udis[i]);
+				g_free (udis[i]);
+			}
 		}
-	} else {
-		g_warning ("Error parsing file '%s': %s", priv->conf_file, error->message);
-		g_error_free (error);
+
+		g_free (udis); /* Yes, g_free, not g_strfreev because we need the strings in the list */
 	}
 
-	g_key_file_free (key_file);
+ out:
+	if (error) {
+		nm_log_warn (LOGD_SETTINGS, "%s", error->message);
+		g_error_free (error);
+	}
+	if (key_file)
+		g_key_file_free (key_file);
 
 	return specs;
 }
@@ -470,14 +537,18 @@ plugin_get_hostname (SCPluginKeyfile *plugin)
 		return NULL;
 
 	key_file = g_key_file_new ();
-	if (g_key_file_load_from_file (key_file, priv->conf_file, G_KEY_FILE_NONE, &error))
-		hostname = g_key_file_get_value (key_file, "keyfile", "hostname", NULL);
-	else {
-		g_warning ("Error parsing file '%s': %s", priv->conf_file, error->message);
+	if (!parse_key_file_allow_none (priv, key_file, &error))
+		goto out;
+
+	hostname = g_key_file_get_value (key_file, "keyfile", "hostname", NULL);
+
+ out:
+	if (error) {
+		nm_log_warn (LOGD_SETTINGS, "%s", error->message);
 		g_error_free (error);
 	}
-
-	g_key_file_free (key_file);
+	if (key_file)
+		g_key_file_free (key_file);
 
 	return hostname;
 }
@@ -485,45 +556,49 @@ plugin_get_hostname (SCPluginKeyfile *plugin)
 static gboolean
 plugin_set_hostname (SCPluginKeyfile *plugin, const char *hostname)
 {
+	gboolean ret = FALSE;
 	SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (plugin);
-	GKeyFile *key_file;
+	GKeyFile *key_file = NULL;
 	GError *error = NULL;
-	gboolean result = FALSE;
+	char *data = NULL;
+	gsize len;
 
 	if (!priv->conf_file) {
-		g_warning ("Error saving hostname: no config file");
-		return FALSE;
+		g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+		             "Error saving hostname: no config file");
+		goto out;
 	}
 
+	g_free (priv->hostname);
+	priv->hostname = g_strdup (hostname);
+
 	key_file = g_key_file_new ();
-	if (g_key_file_load_from_file (key_file, priv->conf_file, G_KEY_FILE_NONE, &error)) {
-		char *data;
-		gsize len;
+	if (!parse_key_file_allow_none (priv, key_file, &error))
+		goto out;
 
-		g_key_file_set_string (key_file, "keyfile", "hostname", hostname);
+	g_key_file_set_string (key_file, "keyfile", "hostname", hostname);
 
-		data = g_key_file_to_data (key_file, &len, &error);
-		if (data) {
-			g_file_set_contents (priv->conf_file, data, len, &error);
-			g_free (data);
+	data = g_key_file_to_data (key_file, &len, &error);
+	if (!data)
+		goto out;
 
-			g_free (priv->hostname);
-			priv->hostname = g_strdup (hostname);
-			result = TRUE;
-		}
+	if (!g_file_set_contents (priv->conf_file, data, len, &error)) {
+		g_prefix_error (&error, "Error saving hostname: ");
+		goto out;
+	}
 
-		if (error) {
-			g_warning ("Error saving hostname: %s", error->message);
-			g_error_free (error);
-		}
-	} else {
-		g_warning ("Error parsing file '%s': %s", priv->conf_file, error->message);
+	ret = TRUE;
+
+ out:
+	if (error) {
+		nm_log_warn (LOGD_SETTINGS, "%s", error->message);
 		g_error_free (error);
 	}
+	g_free (data);
+	if (key_file)
+		g_key_file_free (key_file);
 
-	g_key_file_free (key_file);
-
-	return result;
+	return ret;
 }
 
 /* GObject */
@@ -531,6 +606,9 @@ plugin_set_hostname (SCPluginKeyfile *plugin, const char *hostname)
 static void
 sc_plugin_keyfile_init (SCPluginKeyfile *plugin)
 {
+	SCPluginKeyfilePrivate *priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (plugin);
+
+	priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 }
 
 static void
@@ -603,10 +681,11 @@ dispose (GObject *object)
 	}
 
 	g_free (priv->hostname);
-	g_free (priv->conf_file);
 
-	if (priv->hash)
-		g_hash_table_destroy (priv->hash);
+	if (priv->connections) {
+		g_hash_table_destroy (priv->connections);
+		priv->connections = NULL;
+	}
 
 out:
 	G_OBJECT_CLASS (sc_plugin_keyfile_parent_class)->dispose (object);
@@ -645,26 +724,26 @@ system_config_interface_init (NMSystemConfigInterface *system_config_interface_c
 {
 	/* interface implementation */
 	system_config_interface_class->get_connections = get_connections;
+	system_config_interface_class->load_connection = load_connection;
+	system_config_interface_class->reload_connections = reload_connections;
 	system_config_interface_class->add_connection = add_connection;
 	system_config_interface_class->get_unmanaged_specs = get_unmanaged_specs;
 }
 
 GObject *
-nm_settings_keyfile_plugin_new (const char *config_file)
+nm_settings_keyfile_plugin_new (void)
 {
 	static SCPluginKeyfile *singleton = NULL;
 	SCPluginKeyfilePrivate *priv;
 
 	if (!singleton) {
 		singleton = SC_PLUGIN_KEYFILE (g_object_new (SC_TYPE_PLUGIN_KEYFILE, NULL));
-		if (singleton) {
-			priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (singleton);
+		priv = SC_PLUGIN_KEYFILE_GET_PRIVATE (singleton);
 
-			priv->conf_file = g_strdup (config_file);
+		priv->conf_file = nm_config_get_path (nm_config_get ());
 
-			/* plugin_set_hostname() has to be called *after* priv->conf_file is set */
-			priv->hostname = plugin_get_hostname (singleton);
-		}
+		/* plugin_set_hostname() has to be called *after* priv->conf_file is set */
+		priv->hostname = plugin_get_hostname (singleton);
 	} else
 		g_object_ref (singleton);
 
diff --git a/src/settings/plugins/keyfile/plugin.h b/src/settings/plugins/keyfile/plugin.h
index 9b317333..5f437390 100644
--- a/src/settings/plugins/keyfile/plugin.h
+++ b/src/settings/plugins/keyfile/plugin.h
@@ -43,6 +43,6 @@ GType sc_plugin_keyfile_get_type (void);
 
 GQuark keyfile_plugin_error_quark (void);
 
-GObject *nm_settings_keyfile_plugin_new (const char *config_file);
+GObject *nm_settings_keyfile_plugin_new (void);
 
 #endif	/* _PLUGIN_H_ */
diff --git a/src/settings/plugins/keyfile/reader.c b/src/settings/plugins/keyfile/reader.c
index ec4661eb..735f8c28 100644
--- a/src/settings/plugins/keyfile/reader.c
+++ b/src/settings/plugins/keyfile/reader.c
@@ -41,9 +41,35 @@
 #include <string.h>
 
 #include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
 #include "nm-system-config-interface.h"
+#include "nm-logging.h"
 #include "reader.h"
 #include "common.h"
+#include "utils.h"
+
+/* Some setting properties also contain setting names, such as
+ * NMSettingConnection's 'type' property (which specifies the base type of the
+ * connection, e.g. ethernet or wifi) or 'slave-type' (specifies type of slave
+ * connection, e.g. bond or bridge). This function handles translating those
+ * properties' values to the real setting name if they are an alias.
+ */
+static void
+setting_alias_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+{
+	const char *setting_name = nm_setting_get_name (setting);
+	char *s;
+	const char *key_setting_name;
+
+	s = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
+	if (s) {
+		key_setting_name = nm_keyfile_plugin_get_setting_name_for_alias (s);
+		g_object_set (G_OBJECT (setting),
+		              key, key_setting_name ? key_setting_name : s,
+		              NULL);
+		g_free (s);
+	}
+}
 
 static gboolean
 read_array_of_uint (GKeyFile *file,
@@ -55,7 +81,7 @@ read_array_of_uint (GKeyFile *file,
 	int i;
 	gint *tmp;
 
-	tmp = g_key_file_get_integer_list (file, nm_setting_get_name (setting), key, &length, NULL);
+	tmp = nm_keyfile_plugin_kf_get_integer_list (file, nm_setting_get_name (setting), key, &length, NULL);
 	array = g_array_sized_new (FALSE, FALSE, sizeof (guint32), length);
 	g_return_val_if_fail (array != NULL, FALSE);
 
@@ -72,11 +98,19 @@ static gboolean
 get_one_int (const char *str, guint32 max_val, const char *key_name, guint32 *out)
 {
 	long tmp;
+	char *endptr;
+
+	if (!str || !str[0]) {
+		if (key_name)
+			nm_log_warn (LOGD_SETTINGS, "%s: ignoring missing number %s", __func__, key_name);
+		return FALSE;
+	}
 
 	errno = 0;
-	tmp = strtol (str, NULL, 10);
-	if (errno || (tmp < 0) || (tmp > max_val)) {
-		g_warning ("%s: ignoring invalid IP %s item '%s'", __func__, key_name, str);
+	tmp = strtol (str, &endptr, 10);
+	if (errno || (tmp < 0) || (tmp > max_val) || *endptr != 0) {
+		if (key_name)
+			nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid number %s '%s'", __func__, key_name, str);
 		return FALSE;
 	}
 
@@ -85,12 +119,13 @@ get_one_int (const char *str, guint32 max_val, const char *key_name, guint32 *ou
 }
 
 static gpointer
-build_ip4_address_or_route (const char *address_str, guint32 plen, const char *gateway_str, guint32 metric, gboolean route)
+build_ip4_address_or_route (const char *key_name, const char *address_str, guint32 plen, const char *gateway_str, const char *metric_str, gboolean route)
 {
 	GArray *result;
-	struct in_addr addr;
+	guint32 addr;
 	guint32 address = 0;
 	guint32 gateway = 0;
+	guint32 metric = 0;
 	int err;
 
 	g_return_val_if_fail (address_str, NULL);
@@ -98,23 +133,30 @@ build_ip4_address_or_route (const char *address_str, guint32 plen, const char *g
 	/* Address */
 	err = inet_pton (AF_INET, address_str, &addr);
 	if (err <= 0) {
-		g_warning ("%s: ignoring invalid IPv4 address '%s'", __func__, address_str);
+		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid IPv4 address '%s'", __func__, address_str);
 		return NULL;
 	}
-	address = addr.s_addr;
+	address = addr;
+
 	/* Gateway */
-	if (gateway_str) {
+	if (gateway_str && gateway_str[0]) {
 		err = inet_pton (AF_INET, gateway_str, &addr);
 		if (err <= 0) {
-			g_warning ("%s: ignoring invalid IPv4 gateway '%s'", __func__, gateway_str);
+			nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid IPv4 gateway '%s'", __func__, gateway_str);
 			return NULL;
 		}
-		gateway = addr.s_addr;
+		gateway = addr;
 	}
 	else
 		gateway = 0;
 
-	result = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3);
+	/* parse metric, default to 0 */
+	if (metric_str) {
+		if (!get_one_int (metric_str, G_MAXUINT32, key_name, &metric))
+			return NULL;
+	}
+
+	result = g_array_sized_new (FALSE, TRUE, sizeof (guint32), 3 + !!route);
 	g_array_append_val (result, address);
 	g_array_append_val (result, plen);
 	g_array_append_val (result, gateway);
@@ -125,13 +167,14 @@ build_ip4_address_or_route (const char *address_str, guint32 plen, const char *g
 }
 
 static gpointer
-build_ip6_address_or_route (const char *address_str, guint32 plen, const char *gateway_str, guint32 metric, gboolean route)
+build_ip6_address_or_route (const char *key_name, const char *address_str, guint32 plen, const char *gateway_str, const char *metric_str, gboolean route)
 {
 	GValueArray *result;
 	struct in6_addr addr;
 	GByteArray *address;
 	GByteArray *gateway;
-	GValue value = { 0, };
+	guint32 metric = 0;
+	GValue value = G_VALUE_INIT;
 	int err;
 
 	g_return_val_if_fail (address_str, NULL);
@@ -141,9 +184,8 @@ build_ip6_address_or_route (const char *address_str, guint32 plen, const char *g
 	/* add address */
 	err = inet_pton (AF_INET6, address_str, &addr);
 	if (err <= 0) {
-		g_warning ("%s: ignoring invalid IPv6 address '%s'", __func__, address_str);
-		g_value_array_free (result);
-		return NULL;
+		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid IPv6 address '%s'", __func__, address_str);
+		goto error_out;
 	}
 	address = g_byte_array_new ();
 	g_byte_array_append (address, (guint8 *) addr.s6_addr, 16);
@@ -159,15 +201,33 @@ build_ip6_address_or_route (const char *address_str, guint32 plen, const char *g
 	g_value_unset (&value);
 
 	/* add gateway */
-	if (gateway_str) {
+	if (gateway_str && gateway_str[0]) {
 		err = inet_pton (AF_INET6, gateway_str, &addr);
 		if (err <= 0) {
-			g_warning ("%s: ignoring invalid IPv6 gateway '%s'", __func__, gateway_str);
-			g_value_array_free (result);
-			return NULL;
+			/* Try workaround for routes written by broken keyfile writer.
+			 * Due to bug bgo#719851, an older version of writer would have
+			 * written "a:b:c:d::/plen,metric" if the gateway was ::, instead
+			 * of "a:b:c:d::/plen,,metric" or "a:b:c:d::/plen,::,metric"
+			 * Try workaround by interepeting gateway_str as metric to accept such
+			 * invalid routes. This broken syntax should not be not officially
+			 * supported.
+			 **/
+			if (route && !metric_str && get_one_int (gateway_str, G_MAXUINT32, NULL, &metric))
+				addr = in6addr_any;
+			else {
+				nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid IPv6 gateway '%s'", __func__, gateway_str);
+				goto error_out;
+			}
 		}
 	} else
-		memset (&addr, 0, 16);
+		addr = in6addr_any;
+
+	/* parse metric, default to 0 */
+	if (metric_str) {
+		if (!get_one_int (metric_str, G_MAXUINT32, key_name, &metric))
+			goto error_out;
+	}
+
 	gateway = g_byte_array_new ();
 	g_byte_array_append (gateway, (guint8 *) addr.s6_addr, 16);
 	g_value_init (&value, DBUS_TYPE_G_UCHAR_ARRAY);
@@ -184,6 +244,10 @@ build_ip6_address_or_route (const char *address_str, guint32 plen, const char *g
 	}
 
 	return result;
+
+error_out:
+	g_value_array_free (result);
+	return NULL;
 }
 
 /* On success, returns pointer to the zero-terminated field (original @current).
@@ -214,7 +278,8 @@ read_field (char **current, char **error, const char *characters, const char *de
 	}
 
 	/* fail on empty input */
-	g_return_val_if_fail (**current, NULL);
+	if (!**current)
+		return NULL;
 
 	/* remember beginning of input */
 	start = *current;
@@ -251,18 +316,18 @@ read_field (char **current, char **error, const char *characters, const char *de
  * address (DEPRECATED)
  * address/plen
  * address/gateway (DEPRECATED)
- * address/plen/gateway
+ * address/plen,gateway
  *
  * The following IPv4 and IPv6 route formats are supported:
  *
  * address/plen (NETWORK dev DEVICE)
- * address/plen/gateway (NETWORK via GATEWAY dev DEVICE)
- * address/plen//gateway (NETWORK dev DEVICE metric METRIC)
- * address/plen/gateway/metric (NETWORK via GATEWAY dev DEVICE metric METRIC)
+ * address/plen,gateway (NETWORK via GATEWAY dev DEVICE)
+ * address/plen,,metric (NETWORK dev DEVICE metric METRIC)
+ * address/plen,gateway,metric (NETWORK via GATEWAY dev DEVICE metric METRIC)
  *
  * For backward, forward and sideward compatibility, slash (/),
- * semicolon (;) and comma (,) are interchangable. The use of
- * slash in the above examples is therefore not significant.
+ * semicolon (;) and comma (,) are interchangable. The choice of
+ * separator in the above examples is therefore not significant.
  *
  * Leaving out the prefix length is discouraged and DEPRECATED. The
  * default value of IPv6 prefix length was 64 and has not been
@@ -279,19 +344,19 @@ read_one_ip_address_or_route (GKeyFile *file,
 	gboolean ipv6,
 	gboolean route)
 {
-	guint32 plen, metric;
+	guint32 plen;
 	gpointer result;
 	char *address_str, *plen_str, *gateway_str, *metric_str, *value, *current, *error;
 
-	current = value = g_key_file_get_string (file, setting_name, key_name, NULL);
+	current = value = nm_keyfile_plugin_kf_get_string (file, setting_name, key_name, NULL);
 	if (!value)
 		return NULL;
 
 	/* get address field */
 	address_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
 	if (error) {
-		g_warning ("keyfile: Unexpected character '%c' in '%s.%s' address (position %td of '%s').",
-			*error, setting_name, key_name, error - current, current);
+		nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' address (position %td of '%s').",
+		             *error, setting_name, key_name, error - current, current);
 		goto error;
 	}
 	/* get prefix length field (skippable) */
@@ -299,18 +364,18 @@ read_one_ip_address_or_route (GKeyFile *file,
 	/* get gateway field */
 	gateway_str = read_field (&current, &error, IP_ADDRESS_CHARS, DELIMITERS);
 	if (error) {
-		g_warning ("keyfile: Unexpected character '%c' in '%s.%s' %s (position %td of '%s').",
-			*error, setting_name, key_name,
-			plen_str ? "gateway" : "gateway or prefix length",
-			error - current, current);
+		nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' %s (position %td of '%s').",
+		             *error, setting_name, key_name,
+		             plen_str ? "gateway" : "gateway or prefix length",
+		             error - current, current);
 		goto error;
 	}
 	/* for routes, get metric */
 	if (route) {
 		metric_str = read_field (&current, &error, DIGITS, DELIMITERS);
 		if (error) {
-			g_warning ("keyfile: Unexpected character '%c' in '%s.%s' prefix length (position %td of '%s').",
-				*error, setting_name, key_name, error - current, current);
+			nm_log_warn (LOGD_SETTINGS, "keyfile: Unexpected character '%c' in '%s.%s' prefix length (position %td of '%s').",
+			             *error, setting_name, key_name, error - current, current);
 			goto error;
 		}
 	} else
@@ -319,13 +384,13 @@ read_one_ip_address_or_route (GKeyFile *file,
 		/* there is still some data */
 		if (*current) {
 			/* another field follows */
-			g_warning ("keyfile: %s.%s: Garbage at the and of the line: %s",
-				setting_name, key_name, current);
+			nm_log_warn (LOGD_SETTINGS, "keyfile: %s.%s: Garbage at the and of the line: %s",
+			             setting_name, key_name, current);
 			goto error;
 		} else {
 			/* semicolon at the end of input */
-			g_message ("keyfile: %s.%s: Deprecated semicolon at the end of value.",
-				setting_name, key_name);
+			nm_log_info (LOGD_SETTINGS, "keyfile: %s.%s: Deprecated semicolon at the end of value.",
+			             setting_name, key_name);
 		}
 	}
 
@@ -338,23 +403,13 @@ read_one_ip_address_or_route (GKeyFile *file,
 			plen = ipv6 ? 128 : 24;
 		else
 			plen = ipv6 ? 64 : 24;
-		g_warning ("keyfile: Missing prefix length in '%s.%s', defaulting to %d",
-			setting_name, key_name, plen);
+		nm_log_warn (LOGD_SETTINGS, "keyfile: Missing prefix length in '%s.%s', defaulting to %d",
+		             setting_name, key_name, plen);
 	}
 
-	/* parse metric, default to 0 */
-	metric = 0;
-	if (metric_str)
-		g_return_val_if_fail (get_one_int (metric_str, G_MAXUINT32,
-			key_name, &metric), NULL);
-
 	/* build the appropriate data structure for NetworkManager settings */
-	if (route)
-		g_debug ("keyfile: %s.%s: route %s/%d gateway %s metric %d", setting_name, key_name, address_str, plen, gateway_str, metric);
-	else
-		g_debug ("keyfile: %s.%s: address %s/%d gateway %s", setting_name, key_name, address_str, plen, gateway_str);
 	result = (ipv6 ? build_ip6_address_or_route : build_ip4_address_or_route) (
-		address_str, plen, gateway_str, metric, route);
+	    key_name, address_str, plen, gateway_str, metric_str, route);
 
 	g_free (value);
 	return result;
@@ -375,8 +430,10 @@ ip_address_or_route_parser (NMSetting *setting, const char *key, GKeyFile *keyfi
 	GPtrArray *list;
 	int i;
 
+	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
 	list = g_ptr_array_new_with_free_func (
 		ipv6 ? (GDestroyNotify) g_value_array_free : (GDestroyNotify) g_array_unref);
+	G_GNUC_END_IGNORE_DEPRECATIONS;
 
 	for (i = -1; i < 1000; i++) {
 		const char **key_basename;
@@ -415,21 +472,21 @@ ip4_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch
 	char **list, **iter;
 	int ret;
 
-	list = g_key_file_get_string_list (keyfile, setting_name, key, &length, NULL);
+	list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
 	if (!list || !g_strv_length (list))
 		return;
 
 	array = g_array_sized_new (FALSE, FALSE, sizeof (guint32), length);
 	for (iter = list; *iter; iter++) {
-		struct in_addr addr;
+		guint32 addr;
 
 		ret = inet_pton (AF_INET, *iter, &addr);
 		if (ret <= 0) {
-			g_warning ("%s: ignoring invalid DNS server address '%s'", __func__, *iter);
+			nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid DNS server address '%s'", __func__, *iter);
 			continue;
 		}
 
-		g_array_append_val (array, addr.s_addr);
+		g_array_append_val (array, addr);
 	}
 	g_strfreev (list);
 
@@ -448,7 +505,7 @@ ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch
 	char **list, **iter;
 	int ret;
 
-	list = g_key_file_get_string_list (keyfile, setting_name, key, &length, NULL);
+	list = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
 	if (!list || !g_strv_length (list))
 		return;
 
@@ -460,7 +517,7 @@ ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch
 
 		ret = inet_pton (AF_INET6, *iter, &addr);
 		if (ret <= 0) {
-			g_warning ("%s: ignoring invalid DNS server IPv6 address '%s'", __func__, *iter);
+			nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid DNS server IPv6 address '%s'", __func__, *iter);
 			continue;
 		}
 		byte_array = g_byte_array_new ();
@@ -477,46 +534,52 @@ ip6_dns_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const ch
 }
 
 static void
-mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path, gsize enforce_length)
 {
 	const char *setting_name = nm_setting_get_name (setting);
 	char *tmp_string = NULL, *p;
 	gint *tmp_list;
 	GByteArray *array = NULL;
 	gsize length;
-	int i, type;
 
-	p = tmp_string = g_key_file_get_string (keyfile, setting_name, key, NULL);
-	if (tmp_string) {
+	p = tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
+	if (tmp_string && tmp_string[0]) {
 		/* Look for enough ':' characters to signify a MAC address */
-		i = 0;
+		guint i = 0;
+
 		while (*p) {
 			if (*p == ':')
 				i++;
 			p++;
 		}
 
-		/* If we found enough it's probably a string-format MAC address */
-		type = nm_utils_hwaddr_type (i + 1);
-		if (type > 0)
-			array = nm_utils_hwaddr_atoba (tmp_string, type);
+		if (enforce_length == 0 || enforce_length == i+1) {
+			/* If we found enough it's probably a string-format MAC address */
+			array = g_byte_array_sized_new (i+1);
+			g_byte_array_set_size (array, i+1);
+			if (!nm_utils_hwaddr_aton_len (tmp_string, array->data, array->len)) {
+				g_byte_array_unref (array);
+				array = NULL;
+			}
+		}
 	}
 	g_free (tmp_string);
 
 	if (array == NULL) {
 		/* Old format; list of ints */
-		tmp_list = g_key_file_get_integer_list (keyfile, setting_name, key, &length, NULL);
-		type = nm_utils_hwaddr_type (length);
-		if (type < 0) {
+		tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL);
+		if (length > 0 && (enforce_length == 0 || enforce_length == length)) {
+			gsize i;
+
 			array = g_byte_array_sized_new (length);
 			for (i = 0; i < length; i++) {
 				int val = tmp_list[i];
 				const guint8 v = (guint8) (val & 0xFF);
 
 				if (val < 0 || val > 255) {
-					g_warning ("%s: %s / %s ignoring invalid byte element '%d' (not "
-							   " between 0 and 255 inclusive)", __func__, setting_name,
-							   key, val);
+					nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not "
+					             " between 0 and 255 inclusive)", __func__, setting_name,
+					             key, val);
 					g_byte_array_free (array, TRUE);
 					array = NULL;
 					break;
@@ -531,29 +594,41 @@ mac_address_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, cons
 		g_object_set (setting, key, array, NULL);
 		g_byte_array_free (array, TRUE);
 	} else {
-		g_warning ("%s: ignoring invalid MAC address for %s / %s",
-		           __func__, setting_name, key);
+		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid MAC address for %s / %s",
+		             __func__, setting_name, key);
 	}
 }
 
 static void
+mac_address_parser_ETHER (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+{
+	mac_address_parser (setting, key, keyfile, keyfile_path, ETH_ALEN);
+}
+
+static void
+mac_address_parser_INFINIBAND (NMSetting *setting, const char *key, GKeyFile *keyfile, const char *keyfile_path)
+{
+	mac_address_parser (setting, key, keyfile, keyfile_path, INFINIBAND_ALEN);
+}
+
+static void
 read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key)
 {
 	char **keys, **iter;
 	char *value;
 	const char *setting_name = nm_setting_get_name (setting);
 
-	keys = g_key_file_get_keys (file, setting_name, NULL, NULL);
+	keys = nm_keyfile_plugin_kf_get_keys (file, setting_name, NULL, NULL);
 	if (!keys || !*keys)
 		return;
 
 	for (iter = keys; *iter; iter++) {
-		value = g_key_file_get_string (file, setting_name, *iter, NULL);
+		value = nm_keyfile_plugin_kf_get_string (file, setting_name, *iter, NULL);
 		if (!value)
 			continue;
 
 		if (NM_IS_SETTING_VPN (setting)) {
-			if (strcmp (*iter, NM_SETTING_VPN_SERVICE_TYPE))
+			if (strcmp (*iter, NM_SETTING_VPN_SERVICE_TYPE) && strcmp (*iter, NM_SETTING_VPN_USER_NAME))
 				nm_setting_vpn_add_data_item (NM_SETTING_VPN (setting), *iter, value);
 		}
 		if (NM_IS_SETTING_BOND (setting)) {
@@ -596,7 +671,7 @@ get_uchar_array (GKeyFile *keyfile,
 	/* New format: just a string
 	 * Old format: integer list; e.g. 11;25;38;
 	 */
-	tmp_string = g_key_file_get_string (keyfile, setting_name, key, NULL);
+	tmp_string = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
 	if (tmp_string) {
 		GRegex *regex;
 		GMatchInfo *match_info;
@@ -621,16 +696,16 @@ get_uchar_array (GKeyFile *keyfile,
 
 	if (!array) {
 		/* Old format; list of ints */
-		tmp_list = g_key_file_get_integer_list (keyfile, setting_name, key, &length, NULL);
+		tmp_list = nm_keyfile_plugin_kf_get_integer_list (keyfile, setting_name, key, &length, NULL);
 		array = g_byte_array_sized_new (length);
 		for (i = 0; i < length; i++) {
 			int val = tmp_list[i];
 			unsigned char v = (unsigned char) (val & 0xFF);
 
 			if (val < 0 || val > 255) {
-				g_warning ("%s: %s / %s ignoring invalid byte element '%d' (not "
-					       " between 0 and 255 inclusive)", __func__, setting_name,
-					       key, val);
+				nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not "
+				             " between 0 and 255 inclusive)", __func__, setting_name,
+				             key, val);
 			} else
 				g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
 		}
@@ -655,8 +730,8 @@ ssid_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char
 		g_object_set (setting, key, array, NULL);
 		g_byte_array_free (array, TRUE);
 	} else {
-		g_warning ("%s: ignoring invalid SSID for %s / %s",
-		           __func__, setting_name, key);
+		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid SSID for %s / %s",
+		             __func__, setting_name, key);
 	}
 }
 
@@ -671,8 +746,8 @@ password_raw_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, con
 		g_object_set (setting, key, array, NULL);
 		g_byte_array_free (array, TRUE);
 	} else {
-		g_warning ("%s: ignoring invalid raw password for %s / %s",
-		           __func__, setting_name, key);
+		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid raw password for %s / %s",
+		             __func__, setting_name, key);
 	}
 }
 
@@ -773,7 +848,7 @@ handle_as_path (GByteArray *array,
 
 		/* Warn if the certificate didn't exist */
 		if (exists == FALSE)
-			PLUGIN_WARN (KEYFILE_PLUGIN_NAME, "   certificate or key %s does not exist", path);
+			nm_log_warn (LOGD_SETTINGS, "certificate or key %s does not exist", path);
 	}
 	g_free (path);
 
@@ -800,8 +875,8 @@ cert_parser (NMSetting *setting, const char *key, GKeyFile *keyfile, const char
 		if (success == FALSE)
 			g_object_set (setting, key, array, NULL);
 	} else {
-		g_warning ("%s: ignoring invalid key/cert value for %s / %s",
-		           __func__, setting_name, key);
+		nm_log_warn (LOGD_SETTINGS, "%s: ignoring invalid key/cert value for %s / %s",
+		             __func__, setting_name, key);
 	}
 
 	if (array)
@@ -822,6 +897,14 @@ typedef struct {
  * in struct in6_addr internally, but as string in keyfiles.
  */
 static KeyParser key_parsers[] = {
+	{ NM_SETTING_CONNECTION_SETTING_NAME,
+	  NM_SETTING_CONNECTION_TYPE,
+	  TRUE,
+	  setting_alias_parser },
+	{ NM_SETTING_BRIDGE_SETTING_NAME,
+	  NM_SETTING_BRIDGE_MAC_ADDRESS,
+	  TRUE,
+	  mac_address_parser_ETHER },
 	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
 	  NM_SETTING_IP4_CONFIG_ADDRESSES,
 	  FALSE,
@@ -849,35 +932,35 @@ static KeyParser key_parsers[] = {
 	{ NM_SETTING_WIRED_SETTING_NAME,
 	  NM_SETTING_WIRED_MAC_ADDRESS,
 	  TRUE,
-	  mac_address_parser },
+	  mac_address_parser_ETHER },
 	{ NM_SETTING_WIRED_SETTING_NAME,
 	  NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
 	  TRUE,
-	  mac_address_parser },
+	  mac_address_parser_ETHER },
 	{ NM_SETTING_WIRELESS_SETTING_NAME,
 	  NM_SETTING_WIRELESS_MAC_ADDRESS,
 	  TRUE,
-	  mac_address_parser },
+	  mac_address_parser_ETHER },
 	{ NM_SETTING_WIRELESS_SETTING_NAME,
 	  NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS,
 	  TRUE,
-	  mac_address_parser },
+	  mac_address_parser_ETHER },
 	{ NM_SETTING_WIRELESS_SETTING_NAME,
 	  NM_SETTING_WIRELESS_BSSID,
 	  TRUE,
-	  mac_address_parser },
+	  mac_address_parser_ETHER },
 	{ NM_SETTING_BLUETOOTH_SETTING_NAME,
 	  NM_SETTING_BLUETOOTH_BDADDR,
 	  TRUE,
-	  mac_address_parser },
+	  mac_address_parser_ETHER },
 	{ NM_SETTING_INFINIBAND_SETTING_NAME,
 	  NM_SETTING_INFINIBAND_MAC_ADDRESS,
 	  TRUE,
-	  mac_address_parser },
+	  mac_address_parser_INFINIBAND },
 	{ NM_SETTING_WIMAX_SETTING_NAME,
 	  NM_SETTING_WIMAX_MAC_ADDRESS,
 	  TRUE,
-	  mac_address_parser },
+	  mac_address_parser_ETHER },
 	{ NM_SETTING_WIRELESS_SETTING_NAME,
 	  NM_SETTING_WIRELESS_SSID,
 	  TRUE,
@@ -969,10 +1052,10 @@ read_one_setting_value (NMSetting *setting,
 	 * like IP addresses and routes where more than one value is actually
 	 * encoded by the setting property, this won't be true.
 	 */
-	if (check_for_key && !g_key_file_has_key (info->keyfile, setting_name, key, &err)) {
+	if (check_for_key && !nm_keyfile_plugin_kf_has_key (info->keyfile, setting_name, key, &err)) {
 		/* Key doesn't exist or an error ocurred, thus nothing to do. */
 		if (err) {
-			g_warning ("Error loading setting '%s' value: %s", setting_name, err->message);
+			nm_log_warn (LOGD_SETTINGS, "Error loading setting '%s' value: %s", setting_name, err->message);
 			g_error_free (err);
 		}
 		return;
@@ -981,7 +1064,7 @@ read_one_setting_value (NMSetting *setting,
 	/* If there's a custom parser for this key, handle that before the generic
 	 * parsers below.
 	 */
-	if (parser && parser->setting_name) {
+	if (parser->setting_name) {
 		(*parser->parser) (setting, key, info->keyfile, info->keyfile_path);
 		return;
 	}
@@ -991,39 +1074,39 @@ read_one_setting_value (NMSetting *setting,
 	if (type == G_TYPE_STRING) {
 		char *str_val;
 
-		str_val = g_key_file_get_string (info->keyfile, setting_name, key, NULL);
+		str_val = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
 		g_object_set (setting, key, str_val, NULL);
 		g_free (str_val);
 	} else if (type == G_TYPE_UINT) {
 		int int_val;
 
-		int_val = g_key_file_get_integer (info->keyfile, setting_name, key, NULL);
+		int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
 		if (int_val < 0)
-			g_warning ("Casting negative value (%i) to uint", int_val);
+			nm_log_warn (LOGD_SETTINGS, "Casting negative value (%i) to uint", int_val);
 		g_object_set (setting, key, int_val, NULL);
 	} else if (type == G_TYPE_INT) {
 		int int_val;
 
-		int_val = g_key_file_get_integer (info->keyfile, setting_name, key, NULL);
+		int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
 		g_object_set (setting, key, int_val, NULL);
 	} else if (type == G_TYPE_BOOLEAN) {
 		gboolean bool_val;
 
-		bool_val = g_key_file_get_boolean (info->keyfile, setting_name, key, NULL);
+		bool_val = nm_keyfile_plugin_kf_get_boolean (info->keyfile, setting_name, key, NULL);
 		g_object_set (setting, key, bool_val, NULL);
 	} else if (type == G_TYPE_CHAR) {
 		int int_val;
 
-		int_val = g_key_file_get_integer (info->keyfile, setting_name, key, NULL);
+		int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
 		if (int_val < G_MININT8 || int_val > G_MAXINT8)
-			g_warning ("Casting value (%i) to char", int_val);
+			nm_log_warn (LOGD_SETTINGS, "Casting value (%i) to char", int_val);
 
 		g_object_set (setting, key, int_val, NULL);
 	} else if (type == G_TYPE_UINT64) {
 		char *tmp_str;
 		guint64 uint_val;
 
-		tmp_str = g_key_file_get_value (info->keyfile, setting_name, key, NULL);
+		tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, NULL);
 		uint_val = g_ascii_strtoull (tmp_str, NULL, 10);
 		g_free (tmp_str);
 		g_object_set (setting, key, uint_val, NULL);
@@ -1033,7 +1116,7 @@ read_one_setting_value (NMSetting *setting,
 		gsize length;
 		int i;
 
-		tmp = g_key_file_get_integer_list (info->keyfile, setting_name, key, &length, NULL);
+		tmp = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL);
 
 		array = g_byte_array_sized_new (length);
 		for (i = 0; i < length; i++) {
@@ -1041,9 +1124,9 @@ read_one_setting_value (NMSetting *setting,
 			unsigned char v = (unsigned char) (val & 0xFF);
 
 			if (val < 0 || val > 255) {
-				g_warning ("%s: %s / %s ignoring invalid byte element '%d' (not "
-				           " between 0 and 255 inclusive)", __func__, setting_name,
-				           key, val);
+				nm_log_warn (LOGD_SETTINGS, "%s: %s / %s ignoring invalid byte element '%d' (not "
+				             " between 0 and 255 inclusive)", __func__, setting_name,
+				             key, val);
 			} else
 				g_byte_array_append (array, (const unsigned char *) &v, sizeof (v));
 		}
@@ -1057,7 +1140,7 @@ read_one_setting_value (NMSetting *setting,
 		int i;
 		GSList *list = NULL;
 
-		sa = g_key_file_get_string_list (info->keyfile, setting_name, key, &length, NULL);
+		sa = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL);
 		for (i = 0; i < length; i++)
 			list = g_slist_prepend (list, sa[i]);
 
@@ -1070,26 +1153,28 @@ read_one_setting_value (NMSetting *setting,
 		read_hash_of_string (info->keyfile, setting, key);
 	} else if (type == DBUS_TYPE_G_UINT_ARRAY) {
 		if (!read_array_of_uint (info->keyfile, setting, key)) {
-			g_warning ("Unhandled setting property type (read): '%s/%s' : '%s'",
-					 setting_name, key, G_VALUE_TYPE_NAME (value));
+			nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (read): '%s/%s' : '%s'",
+			             setting_name, key, G_VALUE_TYPE_NAME (value));
 		}
 	} else {
-		g_warning ("Unhandled setting property type (read): '%s/%s' : '%s'",
-				 setting_name, key, G_VALUE_TYPE_NAME (value));
+		nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (read): '%s/%s' : '%s'",
+		             setting_name, key, G_VALUE_TYPE_NAME (value));
 	}
 }
 
 static NMSetting *
-read_setting (GKeyFile *file, const char *keyfile_path, const char *setting_name)
+read_setting (GKeyFile *file, const char *keyfile_path, const char *group)
 {
 	NMSetting *setting;
 	ReadInfo info = { file, keyfile_path };
+	const char *alias;
 
-	setting = nm_connection_create_setting (setting_name);
+	alias = nm_keyfile_plugin_get_setting_name_for_alias (group);
+	setting = nm_connection_create_setting (alias ? alias : group);
 	if (setting)
 		nm_setting_enumerate_values (setting, read_one_setting_value, &info);
 	else
-		g_warning ("Invalid setting name '%s'", setting_name);
+		nm_log_warn (LOGD_SETTINGS, "Invalid setting name '%s'", group);
 
 	return setting;
 }
@@ -1099,11 +1184,11 @@ read_vpn_secrets (GKeyFile *file, NMSettingVPN *s_vpn)
 {
 	char **keys, **iter;
 
-	keys = g_key_file_get_keys (file, VPN_SECRETS_GROUP, NULL, NULL);
+	keys = nm_keyfile_plugin_kf_get_keys (file, VPN_SECRETS_GROUP, NULL, NULL);
 	for (iter = keys; *iter; iter++) {
 		char *secret;
 
-		secret = g_key_file_get_string (file, VPN_SECRETS_GROUP, *iter, NULL);
+		secret = nm_keyfile_plugin_kf_get_string (file, VPN_SECRETS_GROUP, *iter, NULL);
 		if (secret) {
 			nm_setting_vpn_add_secret (s_vpn, *iter, secret);
 			g_free (secret);
@@ -1112,12 +1197,36 @@ read_vpn_secrets (GKeyFile *file, NMSettingVPN *s_vpn)
 	g_strfreev (keys);
 }
 
+static void
+ensure_slave_setting (NMConnection *connection)
+{
+	NMSettingConnection *s_con = nm_connection_get_setting_connection (connection);
+	const char *slave_type;
+	GType slave_gtype = G_TYPE_INVALID;
+	NMSetting *setting;
+
+	slave_type = nm_setting_connection_get_slave_type (s_con);
+	if (!slave_type)
+		return;
+
+	if (g_strcmp0 (slave_type, NM_SETTING_BRIDGE_SETTING_NAME) == 0)
+		slave_gtype = NM_TYPE_SETTING_BRIDGE_PORT;
+	else if (g_strcmp0 (slave_type, NM_SETTING_TEAM_SETTING_NAME) == 0)
+		slave_gtype = NM_TYPE_SETTING_TEAM_PORT;
+
+	if (slave_gtype != G_TYPE_INVALID && !nm_connection_get_setting (connection, slave_gtype)) {
+		setting = (NMSetting *) g_object_new (slave_gtype, NULL);
+		g_assert (setting);
+		nm_connection_add_setting (connection, setting);
+	}
+}
+
 NMConnection *
 nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
 {
 	GKeyFile *key_file;
 	struct stat statbuf;
-	gboolean bad_owner, bad_permissions;
+	gboolean bad_permissions;
 	NMConnection *connection = NULL;
 	NMSettingConnection *s_con;
 	NMSetting *setting;
@@ -1134,13 +1243,12 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
 		return NULL;
 	}
 
-	bad_owner = getuid () != statbuf.st_uid;
 	bad_permissions = statbuf.st_mode & 0077;
 
-	if (bad_owner || bad_permissions) {
+	if (bad_permissions) {
 		g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,
-		             "File permissions (%o) or owner (%d) were insecure",
-		             statbuf.st_mode, statbuf.st_uid);
+		             "File permissions (%o) were insecure",
+		             statbuf.st_mode);
 		return NULL;
 	}
 
@@ -1163,26 +1271,31 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
 			nm_connection_add_setting (connection, setting);
 	}
 
-	/* Make sure that we have the base device type setting even if
-	 * the keyfile didn't include it, which can happen when the base
-	 * device type setting is all default values (like ethernet where
-	 * the MAC address isn't given, or VLAN when the VLAN ID is zero).
+	/* Make sure that we have the base device type and slave type settings
+	 * even if the keyfile didn't include it, which can happen when the
+	 * setting in question is all default values (like ethernet where
+	 * the MAC address isn't given, or VLAN when the VLAN ID is zero, or
+	 * bridge port with all default settings).
 	 */
 	s_con = nm_connection_get_setting_connection (connection);
 	if (s_con) {
 		ctype = nm_setting_connection_get_connection_type (s_con);
-		setting = nm_connection_get_setting_by_name (connection, ctype);
-		if (ctype && !setting) {
-			NMSetting *base_setting;
-			GType base_setting_type;
-
-			base_setting_type = nm_connection_lookup_setting_type (ctype);
-			if (base_setting_type != G_TYPE_INVALID) {
-				base_setting = (NMSetting *) g_object_new (base_setting_type, NULL);
-				g_assert (base_setting);
-				nm_connection_add_setting (connection, base_setting);
+		if (ctype) {
+			setting = nm_connection_get_setting_by_name (connection, ctype);
+			if (!setting) {
+				NMSetting *base_setting;
+				GType base_setting_type;
+
+				base_setting_type = nm_connection_lookup_setting_type (ctype);
+				if (base_setting_type != G_TYPE_INVALID) {
+					base_setting = (NMSetting *) g_object_new (base_setting_type, NULL);
+					g_assert (base_setting);
+					nm_connection_add_setting (connection, base_setting);
+				}
 			}
 		}
+
+		ensure_slave_setting (connection);
 	}
 
 	/* Handle vpn secrets after the 'vpn' setting was read */
@@ -1205,8 +1318,6 @@ nm_keyfile_plugin_connection_from_file (const char *filename, GError **error)
 		g_clear_error (&verify_error);
 		g_object_unref (connection);
 		connection = NULL;
-		g_warning ("Connection failed to verify: %s",
-			verify_error ? g_type_name (nm_connection_lookup_setting_type_by_quark (verify_error->domain)) : "(unknown)");
 	}
 
 out:
diff --git a/src/settings/plugins/keyfile/tests/Makefile.am b/src/settings/plugins/keyfile/tests/Makefile.am
index c2f5fe3d..fae6e4ea 100644
--- a/src/settings/plugins/keyfile/tests/Makefile.am
+++ b/src/settings/plugins/keyfile/tests/Makefile.am
@@ -2,7 +2,7 @@ if ENABLE_TESTS
 
 SUBDIRS=keyfiles
 
-noinst_PROGRAMS = test-keyfile
+@GNOME_CODE_COVERAGE_RULES@
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/include \
@@ -10,22 +10,36 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/libnm-util \
 	-I$(top_builddir)/libnm-util \
 	-I$(top_srcdir)/libnm-glib \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/logging \
+	-I$(top_srcdir)/src/settings \
 	-I$(srcdir)/../ \
 	$(GLIB_CFLAGS) \
 	$(DBUS_CFLAGS) \
+	$(POLKIT_CFLAGS) \
+	$(CODE_COVERAGE_CFLAGS) \
+	-DG_LOG_DOMAIN=\""NetworkManager-keyfile"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 	-DTEST_KEYFILES_DIR=\"$(abs_srcdir)/keyfiles\" \
-	-DTEST_SCRATCH_DIR=\"$(abs_builddir)/keyfiles\"
+	-DTEST_SCRATCH_DIR=\"$(abs_builddir)/keyfiles\" \
+	-DNMCONFDIR=\"nonexistent\"
+
+noinst_PROGRAMS = test-keyfile
 
 test_keyfile_SOURCES = \
-	test-keyfile.c
+	test-keyfile.c \
+	../reader.c \
+	../writer.c \
+	../errors.c \
+	../utils.c
 
 test_keyfile_LDADD = \
-	$(builddir)/../libkeyfile-io.la \
+	$(top_builddir)/src/libNetworkManager.la \
 	$(top_builddir)/libnm-glib/libnm-glib.la \
 	$(top_builddir)/libnm-util/libnm-util.la \
-	$(DBUS_LIBS)
+	$(DBUS_LIBS) \
+	$(CODE_COVERAGE_LDFLAGS)
 
-check-local: test-keyfile
-	$(abs_builddir)/test-keyfile
+TESTS = test-keyfile
 
 endif
diff --git a/src/settings/plugins/keyfile/tests/Makefile.in b/src/settings/plugins/keyfile/tests/Makefile.in
index 675a9fcf..f7b4b62a 100644
--- a/src/settings/plugins/keyfile/tests/Makefile.in
+++ b/src/settings/plugins/keyfile/tests/Makefile.in
@@ -79,11 +79,13 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 @ENABLE_TESTS_TRUE@noinst_PROGRAMS = test-keyfile$(EXEEXT)
+@ENABLE_TESTS_TRUE@TESTS = test-keyfile$(EXEEXT)
 subdir = src/settings/plugins/keyfile/tests
 DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
 	$(top_srcdir)/build-aux/depcomp
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_warnings.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -102,15 +104,18 @@ CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
 PROGRAMS = $(noinst_PROGRAMS)
-am__test_keyfile_SOURCES_DIST = test-keyfile.c
-@ENABLE_TESTS_TRUE@am_test_keyfile_OBJECTS = test-keyfile.$(OBJEXT)
+am__test_keyfile_SOURCES_DIST = test-keyfile.c ../reader.c ../writer.c \
+	../errors.c ../utils.c
+@ENABLE_TESTS_TRUE@am_test_keyfile_OBJECTS = test-keyfile.$(OBJEXT) \
+@ENABLE_TESTS_TRUE@	reader.$(OBJEXT) writer.$(OBJEXT) \
+@ENABLE_TESTS_TRUE@	errors.$(OBJEXT) utils.$(OBJEXT)
 test_keyfile_OBJECTS = $(am_test_keyfile_OBJECTS)
 am__DEPENDENCIES_1 =
 @ENABLE_TESTS_TRUE@test_keyfile_DEPENDENCIES =  \
-@ENABLE_TESTS_TRUE@	$(builddir)/../libkeyfile-io.la \
+@ENABLE_TESTS_TRUE@	$(top_builddir)/src/libNetworkManager.la \
 @ENABLE_TESTS_TRUE@	$(top_builddir)/libnm-glib/libnm-glib.la \
 @ENABLE_TESTS_TRUE@	$(top_builddir)/libnm-util/libnm-util.la \
-@ENABLE_TESTS_TRUE@	$(am__DEPENDENCIES_1)
+@ENABLE_TESTS_TRUE@	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
 am__v_lt_0 = --silent
@@ -191,6 +196,28 @@ am__define_uniq_tagged_files = \
   done | $(am__uniquify_input)`
 ETAGS = etags
 CTAGS = ctags
+am__tty_colors_dummy = \
+  mgn= red= grn= lgn= blu= brg= std=; \
+  am__color_tests=no
+am__tty_colors = { \
+  $(am__tty_colors_dummy); \
+  if test "X$(AM_COLOR_TESTS)" = Xno; then \
+    am__color_tests=no; \
+  elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+    am__color_tests=yes; \
+  elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+    am__color_tests=yes; \
+  fi; \
+  if test $$am__color_tests = yes; then \
+    red=''; \
+    grn=''; \
+    lgn=''; \
+    blu=''; \
+    mgn=''; \
+    brg=''; \
+    std=''; \
+  fi; \
+}
 DIST_SUBDIRS = keyfiles
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
 am__relativize = \
@@ -243,12 +270,15 @@ CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 DATADIRNAME = @DATADIRNAME@
 DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
+DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
 DBUS_LIBS = @DBUS_LIBS@
 DBUS_SYS_DIR = @DBUS_SYS_DIR@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
+DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DSYMUTIL = @DSYMUTIL@
@@ -273,7 +303,6 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LIBS = @GNUTLS_LIBS@
 GREP = @GREP@
 GTKDOC_CHECK = @GTKDOC_CHECK@
-GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
 GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
 GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
 GTKDOC_MKPDF = @GTKDOC_MKPDF@
@@ -318,12 +347,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNDP_CFLAGS = @LIBNDP_CFLAGS@
+LIBNDP_LIBS = @LIBNDP_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
+LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
@@ -340,6 +373,8 @@ MOC = @MOC@
 MSGFMT = @MSGFMT@
 MSGFMT_015 = @MSGFMT_015@
 MSGMERGE = @MSGMERGE@
+NEWT_CFLAGS = @NEWT_CFLAGS@
+NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
@@ -367,11 +402,16 @@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
 POLKIT_CFLAGS = @POLKIT_CFLAGS@
 POLKIT_LIBS = @POLKIT_LIBS@
 POSUB = @POSUB@
+PPPD_PATH = @PPPD_PATH@
 PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+PPPOE_PATH = @PPPOE_PATH@
 QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
 SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
@@ -386,6 +426,7 @@ UDEV_BASE_DIR = @UDEV_BASE_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
+VALGRIND_RULES = @VALGRIND_RULES@
 VAPIGEN = @VAPIGEN@
 VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
 VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
@@ -448,6 +489,7 @@ psdir = @psdir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
+subdirs = @subdirs@
 sysconfdir = @sysconfdir@
 systemdsystemunitdir = @systemdsystemunitdir@
 target_alias = @target_alias@
@@ -458,6 +500,7 @@ with_dhclient = @with_dhclient@
 with_dhcpcd = @with_dhcpcd@
 with_netconfig = @with_netconfig@
 with_resolvconf = @with_resolvconf@
+with_valgrind = @with_valgrind@
 @ENABLE_TESTS_TRUE@SUBDIRS = keyfiles
 @ENABLE_TESTS_TRUE@AM_CPPFLAGS = \
 @ENABLE_TESTS_TRUE@	-I$(top_srcdir)/include \
@@ -465,20 +508,33 @@ with_resolvconf = @with_resolvconf@
 @ENABLE_TESTS_TRUE@	-I$(top_srcdir)/libnm-util \
 @ENABLE_TESTS_TRUE@	-I$(top_builddir)/libnm-util \
 @ENABLE_TESTS_TRUE@	-I$(top_srcdir)/libnm-glib \
+@ENABLE_TESTS_TRUE@	-I$(top_srcdir)/src \
+@ENABLE_TESTS_TRUE@	-I$(top_srcdir)/src/logging \
+@ENABLE_TESTS_TRUE@	-I$(top_srcdir)/src/settings \
 @ENABLE_TESTS_TRUE@	-I$(srcdir)/../ \
 @ENABLE_TESTS_TRUE@	$(GLIB_CFLAGS) \
 @ENABLE_TESTS_TRUE@	$(DBUS_CFLAGS) \
+@ENABLE_TESTS_TRUE@	$(POLKIT_CFLAGS) \
+@ENABLE_TESTS_TRUE@	$(CODE_COVERAGE_CFLAGS) \
+@ENABLE_TESTS_TRUE@	-DG_LOG_DOMAIN=\""NetworkManager-keyfile"\" \
+@ENABLE_TESTS_TRUE@	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
 @ENABLE_TESTS_TRUE@	-DTEST_KEYFILES_DIR=\"$(abs_srcdir)/keyfiles\" \
-@ENABLE_TESTS_TRUE@	-DTEST_SCRATCH_DIR=\"$(abs_builddir)/keyfiles\"
+@ENABLE_TESTS_TRUE@	-DTEST_SCRATCH_DIR=\"$(abs_builddir)/keyfiles\" \
+@ENABLE_TESTS_TRUE@	-DNMCONFDIR=\"nonexistent\"
 
 @ENABLE_TESTS_TRUE@test_keyfile_SOURCES = \
-@ENABLE_TESTS_TRUE@	test-keyfile.c
+@ENABLE_TESTS_TRUE@	test-keyfile.c \
+@ENABLE_TESTS_TRUE@	../reader.c \
+@ENABLE_TESTS_TRUE@	../writer.c \
+@ENABLE_TESTS_TRUE@	../errors.c \
+@ENABLE_TESTS_TRUE@	../utils.c
 
 @ENABLE_TESTS_TRUE@test_keyfile_LDADD = \
-@ENABLE_TESTS_TRUE@	$(builddir)/../libkeyfile-io.la \
+@ENABLE_TESTS_TRUE@	$(top_builddir)/src/libNetworkManager.la \
 @ENABLE_TESTS_TRUE@	$(top_builddir)/libnm-glib/libnm-glib.la \
 @ENABLE_TESTS_TRUE@	$(top_builddir)/libnm-util/libnm-util.la \
-@ENABLE_TESTS_TRUE@	$(DBUS_LIBS)
+@ENABLE_TESTS_TRUE@	$(DBUS_LIBS) \
+@ENABLE_TESTS_TRUE@	$(CODE_COVERAGE_LDFLAGS)
 
 all: all-recursive
 
@@ -534,7 +590,11 @@ mostlyclean-compile:
 distclean-compile:
 	-rm -f *.tab.c
 
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errors.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reader.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-keyfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/writer.Po@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@@ -557,6 +617,62 @@ distclean-compile:
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
 
+reader.o: ../reader.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT reader.o -MD -MP -MF $(DEPDIR)/reader.Tpo -c -o reader.o `test -f '../reader.c' || echo '$(srcdir)/'`../reader.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/reader.Tpo $(DEPDIR)/reader.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../reader.c' object='reader.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o reader.o `test -f '../reader.c' || echo '$(srcdir)/'`../reader.c
+
+reader.obj: ../reader.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT reader.obj -MD -MP -MF $(DEPDIR)/reader.Tpo -c -o reader.obj `if test -f '../reader.c'; then $(CYGPATH_W) '../reader.c'; else $(CYGPATH_W) '$(srcdir)/../reader.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/reader.Tpo $(DEPDIR)/reader.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../reader.c' object='reader.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o reader.obj `if test -f '../reader.c'; then $(CYGPATH_W) '../reader.c'; else $(CYGPATH_W) '$(srcdir)/../reader.c'; fi`
+
+writer.o: ../writer.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT writer.o -MD -MP -MF $(DEPDIR)/writer.Tpo -c -o writer.o `test -f '../writer.c' || echo '$(srcdir)/'`../writer.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/writer.Tpo $(DEPDIR)/writer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../writer.c' object='writer.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o writer.o `test -f '../writer.c' || echo '$(srcdir)/'`../writer.c
+
+writer.obj: ../writer.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT writer.obj -MD -MP -MF $(DEPDIR)/writer.Tpo -c -o writer.obj `if test -f '../writer.c'; then $(CYGPATH_W) '../writer.c'; else $(CYGPATH_W) '$(srcdir)/../writer.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/writer.Tpo $(DEPDIR)/writer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../writer.c' object='writer.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o writer.obj `if test -f '../writer.c'; then $(CYGPATH_W) '../writer.c'; else $(CYGPATH_W) '$(srcdir)/../writer.c'; fi`
+
+errors.o: ../errors.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT errors.o -MD -MP -MF $(DEPDIR)/errors.Tpo -c -o errors.o `test -f '../errors.c' || echo '$(srcdir)/'`../errors.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/errors.Tpo $(DEPDIR)/errors.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../errors.c' object='errors.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o errors.o `test -f '../errors.c' || echo '$(srcdir)/'`../errors.c
+
+errors.obj: ../errors.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT errors.obj -MD -MP -MF $(DEPDIR)/errors.Tpo -c -o errors.obj `if test -f '../errors.c'; then $(CYGPATH_W) '../errors.c'; else $(CYGPATH_W) '$(srcdir)/../errors.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/errors.Tpo $(DEPDIR)/errors.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../errors.c' object='errors.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o errors.obj `if test -f '../errors.c'; then $(CYGPATH_W) '../errors.c'; else $(CYGPATH_W) '$(srcdir)/../errors.c'; fi`
+
+utils.o: ../utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils.o -MD -MP -MF $(DEPDIR)/utils.Tpo -c -o utils.o `test -f '../utils.c' || echo '$(srcdir)/'`../utils.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/utils.Tpo $(DEPDIR)/utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../utils.c' object='utils.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils.o `test -f '../utils.c' || echo '$(srcdir)/'`../utils.c
+
+utils.obj: ../utils.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils.obj -MD -MP -MF $(DEPDIR)/utils.Tpo -c -o utils.obj `if test -f '../utils.c'; then $(CYGPATH_W) '../utils.c'; else $(CYGPATH_W) '$(srcdir)/../utils.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/utils.Tpo $(DEPDIR)/utils.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='../utils.c' object='utils.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils.obj `if test -f '../utils.c'; then $(CYGPATH_W) '../utils.c'; else $(CYGPATH_W) '$(srcdir)/../utils.c'; fi`
+
 mostlyclean-libtool:
 	-rm -f *.lo
 
@@ -662,6 +778,99 @@ cscopelist-am: $(am__tagged_files)
 distclean-tags:
 	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
 
+check-TESTS: $(TESTS)
+	@failed=0; all=0; xfail=0; xpass=0; skip=0; \
+	srcdir=$(srcdir); export srcdir; \
+	list=' $(TESTS) '; \
+	$(am__tty_colors); \
+	if test -n "$$list"; then \
+	  for tst in $$list; do \
+	    if test -f ./$$tst; then dir=./; \
+	    elif test -f $$tst; then dir=; \
+	    else dir="$(srcdir)/"; fi; \
+	    if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+	      all=`expr $$all + 1`; \
+	      case " $(XFAIL_TESTS) " in \
+	      *[\ \	]$$tst[\ \	]*) \
+		xpass=`expr $$xpass + 1`; \
+		failed=`expr $$failed + 1`; \
+		col=$$red; res=XPASS; \
+	      ;; \
+	      *) \
+		col=$$grn; res=PASS; \
+	      ;; \
+	      esac; \
+	    elif test $$? -ne 77; then \
+	      all=`expr $$all + 1`; \
+	      case " $(XFAIL_TESTS) " in \
+	      *[\ \	]$$tst[\ \	]*) \
+		xfail=`expr $$xfail + 1`; \
+		col=$$lgn; res=XFAIL; \
+	      ;; \
+	      *) \
+		failed=`expr $$failed + 1`; \
+		col=$$red; res=FAIL; \
+	      ;; \
+	      esac; \
+	    else \
+	      skip=`expr $$skip + 1`; \
+	      col=$$blu; res=SKIP; \
+	    fi; \
+	    echo "$${col}$$res$${std}: $$tst"; \
+	  done; \
+	  if test "$$all" -eq 1; then \
+	    tests="test"; \
+	    All=""; \
+	  else \
+	    tests="tests"; \
+	    All="All "; \
+	  fi; \
+	  if test "$$failed" -eq 0; then \
+	    if test "$$xfail" -eq 0; then \
+	      banner="$$All$$all $$tests passed"; \
+	    else \
+	      if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+	      banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+	    fi; \
+	  else \
+	    if test "$$xpass" -eq 0; then \
+	      banner="$$failed of $$all $$tests failed"; \
+	    else \
+	      if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+	      banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+	    fi; \
+	  fi; \
+	  dashes="$$banner"; \
+	  skipped=""; \
+	  if test "$$skip" -ne 0; then \
+	    if test "$$skip" -eq 1; then \
+	      skipped="($$skip test was not run)"; \
+	    else \
+	      skipped="($$skip tests were not run)"; \
+	    fi; \
+	    test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+	      dashes="$$skipped"; \
+	  fi; \
+	  report=""; \
+	  if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+	    report="Please report to $(PACKAGE_BUGREPORT)"; \
+	    test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+	      dashes="$$report"; \
+	  fi; \
+	  dashes=`echo "$$dashes" | sed s/./=/g`; \
+	  if test "$$failed" -eq 0; then \
+	    col="$$grn"; \
+	  else \
+	    col="$$red"; \
+	  fi; \
+	  echo "$${col}$$dashes$${std}"; \
+	  echo "$${col}$$banner$${std}"; \
+	  test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+	  test -z "$$report" || echo "$${col}$$report$${std}"; \
+	  echo "$${col}$$dashes$${std}"; \
+	  test "$$failed" -eq 0; \
+	else :; fi
+
 distdir: $(DISTFILES)
 	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
 	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -717,9 +926,8 @@ distdir: $(DISTFILES)
 	      || exit 1; \
 	  fi; \
 	done
-@ENABLE_TESTS_FALSE@check-local:
 check-am: all-am
-	$(MAKE) $(AM_MAKEFLAGS) check-local
+	$(MAKE) $(AM_MAKEFLAGS) check-TESTS
 check: check-recursive
 all-am: Makefile $(PROGRAMS)
 installdirs: installdirs-recursive
@@ -828,7 +1036,7 @@ uninstall-am:
 .MAKE: $(am__recursive_targets) check-am install-am install-strip
 
 .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
-	check-am check-local clean clean-generic clean-libtool \
+	check-TESTS check-am clean clean-generic clean-libtool \
 	clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \
 	distclean-compile distclean-generic distclean-libtool \
 	distclean-tags distdir dvi dvi-am html html-am info info-am \
@@ -843,8 +1051,7 @@ uninstall-am:
 	uninstall-am
 
 
-@ENABLE_TESTS_TRUE@check-local: test-keyfile
-@ENABLE_TESTS_TRUE@	$(abs_builddir)/test-keyfile
+@ENABLE_TESTS_TRUE@@GNOME_CODE_COVERAGE_RULES@
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am
index bbad8e30..576164d2 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.am
@@ -3,6 +3,8 @@ KEYFILES = \
 	Test_GSM_Connection \
 	Test_Wireless_Connection \
 	Test_Wired_Connection_MAC_Case \
+	Test_MAC_Old_Format \
+	Test_MAC_IB_Old_Format \
 	Test_Wired_Connection_IP6 \
 	ATT_Data_Connect_BT \
 	ATT_Data_Connect_Plain \
@@ -17,6 +19,8 @@ KEYFILES = \
 	Test_InfiniBand_Connection \
 	Test_Bridge_Main \
 	Test_Bridge_Component \
+	Test_New_Wired_Group_Name \
+	Test_New_Wireless_Group_Names \
 	Test_Missing_Vlan_Setting
 
 CERTS = \
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
index 450269b4..05660e3e 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Makefile.in
@@ -80,7 +80,8 @@ host_triplet = @host@
 subdir = src/settings/plugins/keyfile/tests/keyfiles
 DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/m4/compiler_warnings.m4 \
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_warnings.m4 \
 	$(top_srcdir)/m4/gettext.m4 \
 	$(top_srcdir)/m4/gnome-code-coverage.m4 \
 	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -144,12 +145,15 @@ CXXFLAGS = @CXXFLAGS@
 CYGPATH_W = @CYGPATH_W@
 DATADIRNAME = @DATADIRNAME@
 DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
+DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
 DBUS_LIBS = @DBUS_LIBS@
 DBUS_SYS_DIR = @DBUS_SYS_DIR@
 DEFS = @DEFS@
 DEPDIR = @DEPDIR@
 DHCLIENT_PATH = @DHCLIENT_PATH@
 DHCPCD_PATH = @DHCPCD_PATH@
+DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
 DLLTOOL = @DLLTOOL@
 DNSMASQ_PATH = @DNSMASQ_PATH@
 DSYMUTIL = @DSYMUTIL@
@@ -174,7 +178,6 @@ GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
 GNUTLS_LIBS = @GNUTLS_LIBS@
 GREP = @GREP@
 GTKDOC_CHECK = @GTKDOC_CHECK@
-GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
 GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
 GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
 GTKDOC_MKPDF = @GTKDOC_MKPDF@
@@ -219,12 +222,16 @@ LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
 LIBICONV = @LIBICONV@
 LIBINTL = @LIBINTL@
 LIBM = @LIBM@
+LIBNDP_CFLAGS = @LIBNDP_CFLAGS@
+LIBNDP_LIBS = @LIBNDP_LIBS@
 LIBNL_CFLAGS = @LIBNL_CFLAGS@
 LIBNL_LIBS = @LIBNL_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
 LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
+LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
 LIBTOOL = @LIBTOOL@
 LIPO = @LIPO@
 LN_S = @LN_S@
@@ -241,6 +248,8 @@ MOC = @MOC@
 MSGFMT = @MSGFMT@
 MSGFMT_015 = @MSGFMT_015@
 MSGMERGE = @MSGMERGE@
+NEWT_CFLAGS = @NEWT_CFLAGS@
+NEWT_LIBS = @NEWT_LIBS@
 NM = @NM@
 NMEDIT = @NMEDIT@
 NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
@@ -268,11 +277,16 @@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
 POLKIT_CFLAGS = @POLKIT_CFLAGS@
 POLKIT_LIBS = @POLKIT_LIBS@
 POSUB = @POSUB@
+PPPD_PATH = @PPPD_PATH@
 PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+PPPOE_PATH = @PPPOE_PATH@
 QT_CFLAGS = @QT_CFLAGS@
 QT_LIBS = @QT_LIBS@
 RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
 SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
@@ -287,6 +301,7 @@ UDEV_BASE_DIR = @UDEV_BASE_DIR@
 USE_NLS = @USE_NLS@
 UUID_CFLAGS = @UUID_CFLAGS@
 UUID_LIBS = @UUID_LIBS@
+VALGRIND_RULES = @VALGRIND_RULES@
 VAPIGEN = @VAPIGEN@
 VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
 VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
@@ -349,6 +364,7 @@ psdir = @psdir@
 sbindir = @sbindir@
 sharedstatedir = @sharedstatedir@
 srcdir = @srcdir@
+subdirs = @subdirs@
 sysconfdir = @sysconfdir@
 systemdsystemunitdir = @systemdsystemunitdir@
 target_alias = @target_alias@
@@ -359,11 +375,14 @@ with_dhclient = @with_dhclient@
 with_dhcpcd = @with_dhcpcd@
 with_netconfig = @with_netconfig@
 with_resolvconf = @with_resolvconf@
+with_valgrind = @with_valgrind@
 KEYFILES = \
 	Test_Wired_Connection \
 	Test_GSM_Connection \
 	Test_Wireless_Connection \
 	Test_Wired_Connection_MAC_Case \
+	Test_MAC_Old_Format \
+	Test_MAC_IB_Old_Format \
 	Test_Wired_Connection_IP6 \
 	ATT_Data_Connect_BT \
 	ATT_Data_Connect_Plain \
@@ -378,6 +397,8 @@ KEYFILES = \
 	Test_InfiniBand_Connection \
 	Test_Bridge_Main \
 	Test_Bridge_Component \
+	Test_New_Wired_Group_Name \
+	Test_New_Wireless_Group_Names \
 	Test_Missing_Vlan_Setting
 
 CERTS = \
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_MAC_IB_Old_Format b/src/settings/plugins/keyfile/tests/keyfiles/Test_MAC_IB_Old_Format
new file mode 100644
index 00000000..b2bf9155
--- /dev/null
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_MAC_IB_Old_Format
@@ -0,0 +1,13 @@
+[connection]
+id=Test InfiniBand Connection
+uuid=5680a56d-c99f-45ad-a6dd-b44d5c398c12
+type=infiniband
+
+[infiniband]
+mac-address=0;17;34;51;68;85;102;119;136;153;1;18;35;52;69;86;103;120;137;144;
+transport-mode=datagram
+mtu=1400
+
+[ipv4]
+method=auto
+
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_MAC_Old_Format b/src/settings/plugins/keyfile/tests/keyfiles/Test_MAC_Old_Format
new file mode 100644
index 00000000..9427b16c
--- /dev/null
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_MAC_Old_Format
@@ -0,0 +1,10 @@
+[connection]
+id=Test MAC Old Format
+uuid=8980a26d-c99f-4aad-a6bd-b439bc348ca4
+type=802-3-ethernet
+
+[802-3-ethernet]
+mac-address=00:11:aa:BB:CC:55
+cloned-mac-address=00;22;170;187;204;254;
+mtu=1400
+
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_New_Wired_Group_Name b/src/settings/plugins/keyfile/tests/keyfiles/Test_New_Wired_Group_Name
new file mode 100644
index 00000000..f27cd4a3
--- /dev/null
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_New_Wired_Group_Name
@@ -0,0 +1,13 @@
+
+[connection]
+id=Test Wired Connection
+uuid=4e80a56d-c99f-4aad-a6dd-b449bc398c57
+type=ethernet
+
+[ethernet]
+mac-address=00:11:22:33:44:55
+mtu=1400
+
+[ipv4]
+method=auto
+
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_New_Wireless_Group_Names b/src/settings/plugins/keyfile/tests/keyfiles/Test_New_Wireless_Group_Names
new file mode 100644
index 00000000..d9ac5864
--- /dev/null
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_New_Wireless_Group_Names
@@ -0,0 +1,16 @@
+[connection]
+id=Test New Wireless Group Names
+uuid=2f962388-e5f3-45af-a62c-ac220b8f7baa
+type=wifi
+
+[wifi]
+ssid=foobar
+mode=infrastructure
+
+[wifi-security]
+key-mgmt=wpa-psk
+psk=s3cu4e passphrase
+
+[ipv4]
+method=auto
+
diff --git a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
index 203441ef..de8373be 100644
--- a/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
+++ b/src/settings/plugins/keyfile/tests/keyfiles/Test_Wired_Connection
@@ -20,8 +20,20 @@ addresses1=192.168.0.5;24;192.168.0.1;
 addresses2=1.2.3.4;16;1.2.1.1;
 address=2.3.4.5/24,2.3.4.6
 address3=3.4.5.6/16
+address4=4.5.6.7/1.2.3.4
+address5=5.6.7.8
 routes1=1.2.3.0/24,2.3.4.8,99
 route=5.6.7.8/32
+routes2=1.1.1.2/12,
+routes3=1.1.1.3/13,,
+routes4=1.1.1.4/14,2.2.2.4
+routes5=1.1.1.5/15,2.2.2.5,
+routes6=1.1.1.6/16,2.2.2.6,0
+routes7=1.1.1.7/17,0.0.0.0
+routes8=1.1.1.8/18,0.0.0.0,
+routes9=1.1.1.9/19,0.0.0.0,0
+routes10=1.1.1.10/20,,0
+routes11=1.1.1.11/21,,21
 ignore-auto-routes=false
 ignore-auto-dns=false
 
@@ -33,8 +45,18 @@ addresses1=abcd:1234:ffff::cdde/64
 addresses2=1:2:3:4:5:6:7:8/96
 address=2:3:4:5:6:7:8:9/64,2:3:4:5:1:2:3:4
 address3=3:4:5:6:7:8:9:0/128
+address4=3:4:5:6:7:8:9:14
+address5=3:4:5:6:7:8:9:15,
+address6=3:4:5:6:7:8:9:16,66
+address7=3:4:5:6:7:8:9:17,67,
+address8=3:4:5:6:7:8:9:18,68,::
+address9=3:4:5:6:7:8:9:19,69,1::09
 routes1=a:b:c:d::/64;f:e:d:c:1:2:3:4;99;
 route=d:e:f:0:1:2:3:4/64,f:e:d:c:1:2:3:4
 route2=8:7:6:5:4:3:2:1/128
+route3=6:7:8:9:0:1:2:3/126,,1
+route4=7:8:9:0:1:2:3:4/125/::,5
+route5=8:9:0:1:2:3:4:5/124,6
+route6=8:9:0:1:2:3:4:6/123,,
 ignore-auto-routes=false
 ignore-auto-dns=false
diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c
index 2f89c5f1..cc57c1c3 100644
--- a/src/settings/plugins/keyfile/tests/test-keyfile.c
+++ b/src/settings/plugins/keyfile/tests/test-keyfile.c
@@ -15,7 +15,7 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * Copyright (C) 2008 - 2011 Red Hat, Inc.
+ * Copyright (C) 2008 - 2014 Red Hat, Inc.
  */
 
 #include <stdio.h>
@@ -40,12 +40,13 @@
 #include <nm-setting-gsm.h>
 #include <nm-setting-8021x.h>
 #include <nm-setting-infiniband.h>
-
-#include "nm-test-helpers.h"
+#include <nm-logging.h>
 
 #include "reader.h"
 #include "writer.h"
 
+#include "nm-test-utils.h"
+
 #define TEST_WIRED_FILE    TEST_KEYFILES_DIR"/Test_Wired_Connection"
 #define TEST_WIRELESS_FILE TEST_KEYFILES_DIR"/Test_Wireless_Connection"
 
@@ -53,16 +54,15 @@ static void
 check_ip4_address (NMSettingIP4Config *config, int idx, const char *address_str, int plen, const char *gateway_str)
 {
 	NMIP4Address *ip4 = nm_setting_ip4_config_get_address (config, idx);
-	struct in_addr address;
-	struct in_addr gateway;
+	guint32 address, gateway;
 
 	g_assert (inet_pton (AF_INET, address_str, &address) == 1);
 	g_assert (inet_pton (AF_INET, gateway_str, &gateway) == 1);
 
 	g_assert (ip4);
-	g_assert (nm_ip4_address_get_address (ip4) == address.s_addr);
+	g_assert (nm_ip4_address_get_address (ip4) == address);
 	g_assert (nm_ip4_address_get_prefix (ip4) == plen);
-	g_assert (nm_ip4_address_get_gateway (ip4) == gateway.s_addr);
+	g_assert (nm_ip4_address_get_gateway (ip4) == gateway);
 }
 
 static void
@@ -86,16 +86,15 @@ check_ip4_route (NMSettingIP4Config *config, int idx, const char *destination_st
 		const char *nexthop_str, int metric)
 {
 	NMIP4Route *route = nm_setting_ip4_config_get_route (config, idx);
-	struct in_addr destination;
-	struct in_addr nexthop;
+	guint32 destination, nexthop;
 
 	g_assert (inet_pton (AF_INET, destination_str, &destination) == 1);
 	g_assert (inet_pton (AF_INET, nexthop_str, &nexthop) == 1);
 
 	g_assert (route);
-	g_assert (nm_ip4_route_get_dest (route) == destination.s_addr);
+	g_assert (nm_ip4_route_get_dest (route) == destination);
 	g_assert (nm_ip4_route_get_prefix (route) == plen);
-	g_assert (nm_ip4_route_get_next_hop (route) == nexthop.s_addr);
+	g_assert (nm_ip4_route_get_next_hop (route) == nexthop);
 	g_assert (nm_ip4_route_get_metric (route) == metric);
 }
 
@@ -135,7 +134,7 @@ test_read_valid_wired_connection (void)
 	guint64 timestamp;
 	const char *expected_dns1 = "4.2.2.1";
 	const char *expected_dns2 = "4.2.2.2";
-	struct in_addr addr;
+	guint32 addr;
 	struct in6_addr addr6;
 	const char *expected6_dns1 = "1111:dddd::aaaa";
 	const char *expected6_dns2 = "1::cafe";
@@ -143,7 +142,36 @@ test_read_valid_wired_connection (void)
 	const char *expected6_dnssearch2 = "redhat.com";
 	const char *expected6_dnssearch3 = "gnu.org";
 
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv4.addresses1*semicolon at the end*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv4.addresses2*semicolon at the end*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*Missing prefix length*ipv4.address4*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*Missing prefix length*ipv4.address5*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv4.routes2*semicolon at the end*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv4.routes3*semicolon at the end*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv4.routes5*semicolon at the end*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv4.routes8*semicolon at the end*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*Missing prefix length*ipv6.address4*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv6.address5*semicolon at the end*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*Missing prefix length*ipv6.address5*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv6.address7*semicolon at the end*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv6.routes1*semicolon at the end*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv6.route6*semicolon at the end*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_FILE, NULL);
+	g_test_assert_expected_messages ();
 	ASSERT (connection != NULL,
 			"connection-read", "failed to read %s", TEST_WIRED_FILE);
 
@@ -259,7 +287,7 @@ test_read_valid_wired_connection (void)
 	        TEST_WIRED_FILE,
 	        NM_SETTING_IP4_CONFIG_SETTING_NAME,
 	        NM_SETTING_IP4_CONFIG_DNS);
-	ASSERT (nm_setting_ip4_config_get_dns (s_ip4, 0) == addr.s_addr,
+	ASSERT (nm_setting_ip4_config_get_dns (s_ip4, 0) == addr,
 	        "connection-verify-wired", "failed to verify %s: unexpected %s / %s key value #1",
 	        TEST_WIRED_FILE,
 	        NM_SETTING_IP4_CONFIG_SETTING_NAME,
@@ -270,23 +298,35 @@ test_read_valid_wired_connection (void)
 	        TEST_WIRED_FILE,
 	        NM_SETTING_IP4_CONFIG_SETTING_NAME,
 	        NM_SETTING_IP4_CONFIG_DNS);
-	ASSERT (nm_setting_ip4_config_get_dns (s_ip4, 1) == addr.s_addr,
+	ASSERT (nm_setting_ip4_config_get_dns (s_ip4, 1) == addr,
 	        "connection-verify-wired", "failed to verify %s: unexpected %s / %s key value #2",
 	        TEST_WIRED_FILE,
 	        NM_SETTING_IP4_CONFIG_SETTING_NAME,
 	        NM_SETTING_IP4_CONFIG_DNS);
 
 	/* IPv4 addresses */
-	g_assert (nm_setting_ip4_config_get_num_addresses (s_ip4) == 4);
+	g_assert (nm_setting_ip4_config_get_num_addresses (s_ip4) == 6);
 	check_ip4_address (s_ip4, 0, "2.3.4.5", 24, "2.3.4.6");
 	check_ip4_address (s_ip4, 1, "192.168.0.5", 24, "192.168.0.1");
 	check_ip4_address (s_ip4, 2, "1.2.3.4", 16, "1.2.1.1");
 	check_ip4_address (s_ip4, 3, "3.4.5.6", 16, "0.0.0.0");
+	check_ip4_address (s_ip4, 4, "4.5.6.7", 24, "1.2.3.4");
+	check_ip4_address (s_ip4, 5, "5.6.7.8", 24, "0.0.0.0");
 
 	/* IPv4 routes */
-	g_assert (nm_setting_ip4_config_get_num_routes (s_ip4) == 2);
+	g_assert (nm_setting_ip4_config_get_num_routes (s_ip4) == 12);
 	check_ip4_route (s_ip4, 0, "5.6.7.8", 32, "0.0.0.0", 0);
 	check_ip4_route (s_ip4, 1, "1.2.3.0", 24, "2.3.4.8", 99);
+	check_ip4_route (s_ip4, 2, "1.1.1.2", 12, "0.0.0.0", 0);
+	check_ip4_route (s_ip4, 3, "1.1.1.3", 13, "0.0.0.0", 0);
+	check_ip4_route (s_ip4, 4, "1.1.1.4", 14, "2.2.2.4", 0);
+	check_ip4_route (s_ip4, 5, "1.1.1.5", 15, "2.2.2.5", 0);
+	check_ip4_route (s_ip4, 6, "1.1.1.6", 16, "2.2.2.6", 0);
+	check_ip4_route (s_ip4, 7, "1.1.1.7", 17, "0.0.0.0", 0);
+	check_ip4_route (s_ip4, 8, "1.1.1.8", 18, "0.0.0.0", 0);
+	check_ip4_route (s_ip4, 9, "1.1.1.9", 19, "0.0.0.0", 0);
+	check_ip4_route (s_ip4, 10, "1.1.1.10", 20, "0.0.0.0", 0);
+	check_ip4_route (s_ip4, 11, "1.1.1.11", 21, "0.0.0.0", 21);
 
 	/* ===== IPv6 SETTING ===== */
 
@@ -357,17 +397,27 @@ test_read_valid_wired_connection (void)
 	        NM_SETTING_IP6_CONFIG_DNS_SEARCH);
 
 	/* IPv6 addresses */
-	g_assert (nm_setting_ip6_config_get_num_addresses (s_ip6) == 4);
+	g_assert (nm_setting_ip6_config_get_num_addresses (s_ip6) == 10);
 	check_ip6_address (s_ip6, 0, "2:3:4:5:6:7:8:9", 64, "2:3:4:5:1:2:3:4");
 	check_ip6_address (s_ip6, 1, "abcd:1234:ffff::cdde", 64, "::");
 	check_ip6_address (s_ip6, 2, "1:2:3:4:5:6:7:8", 96, "::");
 	check_ip6_address (s_ip6, 3, "3:4:5:6:7:8:9:0", 128, "::");
+	check_ip6_address (s_ip6, 4, "3:4:5:6:7:8:9:14", 64, "::");
+	check_ip6_address (s_ip6, 5, "3:4:5:6:7:8:9:15", 64, "::");
+	check_ip6_address (s_ip6, 6, "3:4:5:6:7:8:9:16", 66, "::");
+	check_ip6_address (s_ip6, 7, "3:4:5:6:7:8:9:17", 67, "::");
+	check_ip6_address (s_ip6, 8, "3:4:5:6:7:8:9:18", 68, "::");
+	check_ip6_address (s_ip6, 9, "3:4:5:6:7:8:9:19", 69, "1::09");
 
 	/* Route #1 */
-	g_assert (nm_setting_ip6_config_get_num_routes (s_ip6) == 3);
+	g_assert (nm_setting_ip6_config_get_num_routes (s_ip6) == 7);
 	check_ip6_route (s_ip6, 0, "d:e:f:0:1:2:3:4", 64, "f:e:d:c:1:2:3:4", 0);
 	check_ip6_route (s_ip6, 1, "a:b:c:d::", 64, "f:e:d:c:1:2:3:4", 99);
 	check_ip6_route (s_ip6, 2, "8:7:6:5:4:3:2:1", 128, "::", 0);
+	check_ip6_route (s_ip6, 3, "6:7:8:9:0:1:2:3", 126, "::", 1);
+	check_ip6_route (s_ip6, 4, "7:8:9:0:1:2:3:4", 125, "::", 5);
+	check_ip6_route (s_ip6, 5, "8:9:0:1:2:3:4:5", 124, "::", 6);
+	check_ip6_route (s_ip6, 6, "8:9:0:1:2:3:4:6", 123, "::", 0);
 	g_object_unref (connection);
 }
 
@@ -377,17 +427,17 @@ add_one_ip4_address (NMSettingIP4Config *s_ip4,
                      const char *gw,
                      guint32 prefix)
 {
-	struct in_addr tmp;
+	guint32 tmp;
 	NMIP4Address *ip4_addr;
 
 	ip4_addr = nm_ip4_address_new ();
 	nm_ip4_address_set_prefix (ip4_addr, prefix);
 
 	inet_pton (AF_INET, addr, &tmp);
-	nm_ip4_address_set_address (ip4_addr, tmp.s_addr);
+	nm_ip4_address_set_address (ip4_addr, tmp);
 
 	inet_pton (AF_INET, gw, &tmp);
-	nm_ip4_address_set_gateway (ip4_addr, tmp.s_addr);
+	nm_ip4_address_set_gateway (ip4_addr, tmp);
 
 	nm_setting_ip4_config_add_address (s_ip4, ip4_addr);
 	nm_ip4_address_unref (ip4_addr);
@@ -400,7 +450,7 @@ add_one_ip4_route (NMSettingIP4Config *s_ip4,
                    guint32 prefix,
                    guint32 metric)
 {
-	struct in_addr addr;
+	guint32 addr;
 	NMIP4Route *route;
 
 	route = nm_ip4_route_new ();
@@ -408,10 +458,10 @@ add_one_ip4_route (NMSettingIP4Config *s_ip4,
 	nm_ip4_route_set_metric (route, metric);
 
 	inet_pton (AF_INET, dest, &addr);
-	nm_ip4_route_set_dest (route, addr.s_addr);
+	nm_ip4_route_set_dest (route, addr);
 
 	inet_pton (AF_INET, nh, &addr);
-	nm_ip4_route_set_next_hop (route, addr.s_addr);
+	nm_ip4_route_set_next_hop (route, addr);
 
 	nm_setting_ip4_config_add_route (s_ip4, route);
 	nm_ip4_route_unref (route);
@@ -483,7 +533,7 @@ test_write_wired_connection (void)
 	GError *error = NULL;
 	pid_t owner_grp;
 	uid_t owner_uid;
-	struct in_addr addr;
+	guint32 addr;
 	struct in6_addr addr6;
 	const char *dns1 = "4.2.2.1";
 	const char *dns2 = "4.2.2.2";
@@ -495,6 +545,10 @@ test_write_wired_connection (void)
 	const char *route1_nh = "10.10.10.1";
 	const char *route2 = "1.1.1.1";
 	const char *route2_nh = "1.2.1.1";
+	const char *route3 = "2.2.2.2";
+	const char *route3_nh = "0.0.0.0";
+	const char *route4 = "3.3.3.3";
+	const char *route4_nh = "0.0.0.0";
 	const char *dns6_1 = "1::cafe";
 	const char *dns6_2 = "2::cafe";
 	const char *address6_1 = "abcd::beef";
@@ -503,18 +557,17 @@ test_write_wired_connection (void)
 	const char *route6_1_nh = "8:7:6:5:4:3:2:1";
 	const char *route6_2 = "2001::1000";
 	const char *route6_2_nh = "2001::1111";
+	const char *route6_3 = "4:5:6:7:8:9:0:1";
+	const char *route6_3_nh = "::";
+	const char *route6_4 = "5:6:7:8:9:0:1:2";
+	const char *route6_4_nh = "::";
 	guint64 timestamp = 0x12345678L;
 
 	connection = nm_connection_new ();
-	ASSERT (connection != NULL,
-			"connection-write", "failed to allocate new connection");
 
 	/* Connection setting */
 
 	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
-	ASSERT (s_con != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_CONNECTION_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_con));
 
 	uuid = nm_utils_uuid_generate ();
@@ -530,9 +583,6 @@ test_write_wired_connection (void)
 	/* Wired setting */
 
 	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
-	ASSERT (s_wired != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_WIRED_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_wired));
 
 	mac = g_byte_array_sized_new (ETH_ALEN);
@@ -546,9 +596,6 @@ test_write_wired_connection (void)
 	/* IP4 setting */
 
 	s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ());
-	ASSERT (s_ip4 != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_IP4_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
 
 	g_object_set (s_ip4,
@@ -562,19 +609,18 @@ test_write_wired_connection (void)
 	/* Routes */
 	add_one_ip4_route (s_ip4, route1, route1_nh, 24, 3);
 	add_one_ip4_route (s_ip4, route2, route2_nh, 8, 1);
+	add_one_ip4_route (s_ip4, route3, route3_nh, 7, 0);
+	add_one_ip4_route (s_ip4, route4, route4_nh, 6, 4);
 
 	/* DNS servers */
 	inet_pton (AF_INET, dns1, &addr);
-	nm_setting_ip4_config_add_dns (s_ip4, addr.s_addr);
+	nm_setting_ip4_config_add_dns (s_ip4, addr);
 	inet_pton (AF_INET, dns2, &addr);
-	nm_setting_ip4_config_add_dns (s_ip4, addr.s_addr);
+	nm_setting_ip4_config_add_dns (s_ip4, addr);
 
 	/* IP6 setting */
 
 	s_ip6 = NM_SETTING_IP6_CONFIG (nm_setting_ip6_config_new ());
-	ASSERT (s_ip6 != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
 	g_object_set (s_ip6,
@@ -588,6 +634,8 @@ test_write_wired_connection (void)
 	/* Routes */
 	add_one_ip6_route (s_ip6, route6_1, route6_1_nh, 64, 3);
 	add_one_ip6_route (s_ip6, route6_2, route6_2_nh, 56, 1);
+	add_one_ip6_route (s_ip6, route6_3, route6_3_nh, 63, 5);
+	add_one_ip6_route (s_ip6, route6_4, route6_4_nh, 62, 0);
 
 	/* DNS servers */
 	inet_pton (AF_INET6, dns6_1, &addr6);
@@ -754,15 +802,10 @@ test_write_ip6_wired_connection (void)
 	const char *gw = "dcba::beef";
 
 	connection = nm_connection_new ();
-	ASSERT (connection != NULL,
-			"connection-write", "failed to allocate new connection");
 
 	/* Connection setting */
 
 	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
-	ASSERT (s_con != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_CONNECTION_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_con));
 
 	uuid = nm_utils_uuid_generate ();
@@ -777,17 +820,11 @@ test_write_ip6_wired_connection (void)
 	/* Wired setting */
 
 	s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
-	ASSERT (s_wired != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_WIRED_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_wired));
 
 	/* IP4 setting */
 
 	s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ());
-	ASSERT (s_ip4 != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_IP4_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
 
 	g_object_set (s_ip4,
@@ -797,9 +834,6 @@ test_write_ip6_wired_connection (void)
 	/* IP6 setting */
 
 	s_ip6 = NM_SETTING_IP6_CONFIG (nm_setting_ip6_config_new ());
-	ASSERT (s_ip6 != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
 	g_object_set (s_ip6,
@@ -857,7 +891,14 @@ test_read_wired_mac_case (void)
 	const char *expected_id = "Test Wired Connection MAC Case";
 	const char *expected_uuid = "4e80a56d-c99f-4aad-a6dd-b449bc398c57";
 
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv4.addresses1*semicolon at the end*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv4.addresses2*semicolon at the end*");
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_MESSAGE,
+	                       "*ipv6.routes1*semicolon at the end*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_MAC_CASE_FILE, NULL);
+	g_test_assert_expected_messages ();
 	ASSERT (connection != NULL,
 			"connection-read", "failed to read %s", TEST_WIRED_MAC_CASE_FILE);
 
@@ -927,6 +968,79 @@ test_read_wired_mac_case (void)
 	g_object_unref (connection);
 }
 
+#define TEST_MAC_OLD_FORMAT_FILE TEST_KEYFILES_DIR"/Test_MAC_Old_Format"
+
+static void
+test_read_mac_old_format (void)
+{
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	GError *error = NULL;
+	gboolean success;
+	const GByteArray *array;
+	char expected_mac[ETH_ALEN] = { 0x00, 0x11, 0xaa, 0xbb, 0xcc, 0x55 };
+	char expected_cloned_mac[ETH_ALEN] = { 0x00, 0x16, 0xaa, 0xbb, 0xcc, 0xfe };
+
+	connection = nm_keyfile_plugin_connection_from_file (TEST_MAC_OLD_FORMAT_FILE, &error);
+	g_assert_no_error (error);
+	g_assert (connection);
+
+	success = nm_connection_verify (connection, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert (s_wired);
+
+	/* MAC address */
+	array = nm_setting_wired_get_mac_address (s_wired);
+	g_assert (array);
+	g_assert_cmpint (array->len, ==, ETH_ALEN);
+	g_assert (memcmp (array->data, expected_mac, ETH_ALEN) == 0);
+
+	/* Cloned MAC address */
+	array = nm_setting_wired_get_cloned_mac_address (s_wired);
+	g_assert (array);
+	g_assert_cmpint (array->len, ==, ETH_ALEN);
+	g_assert (memcmp (array->data, expected_cloned_mac, ETH_ALEN) == 0);
+
+	g_object_unref (connection);
+}
+
+#define TEST_MAC_IB_OLD_FORMAT_FILE TEST_KEYFILES_DIR"/Test_MAC_IB_Old_Format"
+
+static void
+test_read_mac_ib_old_format (void)
+{
+	NMConnection *connection;
+	NMSettingInfiniband *s_ib;
+	GError *error = NULL;
+	gboolean success;
+	const GByteArray *array;
+	guint8 expected_mac[INFINIBAND_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+		0x77, 0x88, 0x99, 0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89,
+		0x90 };
+
+	connection = nm_keyfile_plugin_connection_from_file (TEST_MAC_IB_OLD_FORMAT_FILE, &error);
+	g_assert_no_error (error);
+	g_assert (connection);
+
+	success = nm_connection_verify (connection, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	s_ib = nm_connection_get_setting_infiniband (connection);
+	g_assert (s_ib);
+
+	/* MAC address */
+	array = nm_setting_infiniband_get_mac_address (s_ib);
+	g_assert (array);
+	g_assert_cmpint (array->len, ==, INFINIBAND_ALEN);
+	g_assert_cmpint (memcmp (array->data, expected_mac, sizeof (expected_mac)), ==, 0);
+
+	g_object_unref (connection);
+}
+
 static void
 test_read_valid_wireless_connection (void)
 {
@@ -1066,15 +1180,10 @@ test_write_wireless_connection (void)
 	guint64 timestamp = 0x12344433L;
 
 	connection = nm_connection_new ();
-	ASSERT (connection != NULL,
-	        "connection-write", "failed to allocate new connection");
 
 	/* Connection setting */
 
 	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
-	ASSERT (s_con != NULL,
-	        "connection-write", "failed to allocate new %s setting",
-	        NM_SETTING_CONNECTION_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_con));
 
 	uuid = nm_utils_uuid_generate ();
@@ -1090,9 +1199,6 @@ test_write_wireless_connection (void)
 	/* Wireless setting */
 
 	s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
-	ASSERT (s_wireless != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_WIRELESS_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_wireless));
 
 	bssid = g_byte_array_sized_new (ETH_ALEN);
@@ -1113,9 +1219,6 @@ test_write_wireless_connection (void)
 	/* IP4 setting */
 
 	s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ());
-	ASSERT (s_ip4 != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_IP4_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
 
 	g_object_set (s_ip4,
@@ -1125,9 +1228,6 @@ test_write_wireless_connection (void)
 	/* IP6 setting */
 
 	s_ip6 = NM_SETTING_IP6_CONFIG (nm_setting_ip6_config_new ());
-	ASSERT (s_ip6 != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_IP6_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip6));
 
 	g_object_set (s_ip6,
@@ -1217,15 +1317,10 @@ test_write_string_ssid (void)
 	GKeyFile *keyfile;
 
 	connection = nm_connection_new ();
-	ASSERT (connection != NULL,
-	        "connection-write", "failed to allocate new connection");
 
 	/* Connection setting */
 
 	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
-	ASSERT (s_con != NULL,
-	        "connection-write", "failed to allocate new %s setting",
-	        NM_SETTING_CONNECTION_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_con));
 
 	uuid = nm_utils_uuid_generate ();
@@ -1239,9 +1334,6 @@ test_write_string_ssid (void)
 	/* Wireless setting */
 
 	s_wireless = NM_SETTING_WIRELESS (nm_setting_wireless_new ());
-	ASSERT (s_wireless != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_WIRELESS_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_wireless));
 
 	ssid = g_byte_array_sized_new (sizeof (tmpssid));
@@ -1252,9 +1344,6 @@ test_write_string_ssid (void)
 	/* IP4 setting */
 
 	s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ());
-	ASSERT (s_ip4 != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_IP4_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
 
 	g_object_set (s_ip4,
@@ -1276,7 +1365,7 @@ test_write_string_ssid (void)
 	keyfile = g_key_file_new ();
 	ASSERT (g_key_file_load_from_file (keyfile, testfile, 0, NULL) == TRUE,
 	        "string-ssid-verify", "failed to load keyfile to verify");
-	tmp = g_key_file_get_string (keyfile, NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID, NULL);
+	tmp = g_key_file_get_string (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, NULL);
 	ASSERT (tmp, "string-ssid-verify", "failed to load 'ssid' key from file");
 	ASSERT (strlen (tmp) == sizeof (tmpssid),
 	        "string-ssid-verify", "reread SSID and expected were different sizes");
@@ -1398,7 +1487,7 @@ test_write_intlist_ssid (void)
 	g_assert_no_error (error);
 	g_assert (success);
 
-	intlist = g_key_file_get_integer_list (keyfile, NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID, &len, &error);
+	intlist = g_key_file_get_integer_list (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, &len, &error);
 	g_assert_no_error (error);
 	g_assert (intlist);
 	g_assert_cmpint (len, ==, sizeof (tmpssid));
@@ -1554,7 +1643,7 @@ test_write_intlike_ssid (void)
 	g_assert_no_error (error);
 	g_assert (success);
 
-	tmp = g_key_file_get_string (keyfile, NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID, &error);
+	tmp = g_key_file_get_string (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, &error);
 	g_assert_no_error (error);
 	g_assert (tmp);
 	g_assert_cmpstr (tmp, ==, "101");
@@ -1642,7 +1731,7 @@ test_write_intlike_ssid_2 (void)
 	g_assert_no_error (error);
 	g_assert (success);
 
-	tmp = g_key_file_get_string (keyfile, NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_SSID, &error);
+	tmp = g_key_file_get_string (keyfile, "wifi", NM_SETTING_WIRELESS_SSID, &error);
 	g_assert_no_error (error);
 	g_assert (tmp);
 	g_assert_cmpstr (tmp, ==, "11\\;12\\;13\\;");
@@ -1843,15 +1932,10 @@ test_write_bt_dun_connection (void)
 	guint64 timestamp = 0x12344433L;
 
 	connection = nm_connection_new ();
-	ASSERT (connection != NULL,
-	        "connection-write", "failed to allocate new connection");
 
 	/* Connection setting */
 
 	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
-	ASSERT (s_con != NULL,
-	        "connection-write", "failed to allocate new %s setting",
-	        NM_SETTING_CONNECTION_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_con));
 
 	uuid = nm_utils_uuid_generate ();
@@ -1867,9 +1951,6 @@ test_write_bt_dun_connection (void)
 	/* Bluetooth setting */
 
 	s_bt = NM_SETTING_BLUETOOTH (nm_setting_bluetooth_new ());
-	ASSERT (s_bt != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_BLUETOOTH_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_bt));
 
 	bdaddr = g_byte_array_sized_new (ETH_ALEN);
@@ -1885,9 +1966,6 @@ test_write_bt_dun_connection (void)
 	/* IP4 setting */
 
 	s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ());
-	ASSERT (s_ip4 != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_IP4_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
 
 	g_object_set (s_ip4,
@@ -1896,9 +1974,6 @@ test_write_bt_dun_connection (void)
 
 	/* GSM setting */
 	s_gsm = NM_SETTING_GSM (nm_setting_gsm_new ());
-	ASSERT (s_gsm != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_GSM_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_gsm));
 
 	g_object_set (s_gsm,
@@ -2103,15 +2178,10 @@ test_write_gsm_connection (void)
 	guint64 timestamp = 0x12344433L;
 
 	connection = nm_connection_new ();
-	ASSERT (connection != NULL,
-	        "connection-write", "failed to allocate new connection");
 
 	/* Connection setting */
 
 	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
-	ASSERT (s_con != NULL,
-	        "connection-write", "failed to allocate new %s setting",
-	        NM_SETTING_CONNECTION_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_con));
 
 	uuid = nm_utils_uuid_generate ();
@@ -2127,9 +2197,6 @@ test_write_gsm_connection (void)
 	/* IP4 setting */
 
 	s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_ip4_config_new ());
-	ASSERT (s_ip4 != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_IP4_CONFIG_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_ip4));
 
 	g_object_set (s_ip4,
@@ -2138,9 +2205,6 @@ test_write_gsm_connection (void)
 
 	/* GSM setting */
 	s_gsm = NM_SETTING_GSM (nm_setting_gsm_new ());
-	ASSERT (s_gsm != NULL,
-			"connection-write", "failed to allocate new %s setting",
-			NM_SETTING_GSM_SETTING_NAME);
 	nm_connection_add_setting (connection, NM_SETTING (s_gsm));
 
 	g_object_set (s_gsm,
@@ -2228,7 +2292,10 @@ test_read_wired_8021x_tls_blob_connection (void)
 	g_assert_cmpint (nm_setting_802_1x_get_ca_cert_scheme (s_8021x), ==, NM_SETTING_802_1X_CK_SCHEME_BLOB);
 
 	/* Make sure it's not a path, since it's a blob */
+	g_test_expect_message ("libnm-util", G_LOG_LEVEL_CRITICAL,
+	                       "*assertion*scheme == NM_SETTING_802_1X_CK_SCHEME_PATH*");
 	tmp = nm_setting_802_1x_get_ca_cert_path (s_8021x);
+	g_test_assert_expected_messages ();
 	g_assert (tmp == NULL);
 
 	/* Validate the path */
@@ -2258,7 +2325,10 @@ test_read_wired_8021x_tls_bad_path_connection (void)
 	char *tmp2;
 	gboolean success;
 
+	g_test_expect_message ("NetworkManager", G_LOG_LEVEL_WARNING,
+	                       "*does not exist*");
 	connection = nm_keyfile_plugin_connection_from_file (TEST_WIRED_TLS_PATH_MISSING_FILE, &error);
+	g_test_assert_expected_messages ();
 	if (connection == NULL) {
 		g_assert (error);
 		g_warning ("Failed to read %s: %s", TEST_WIRED_TLS_PATH_MISSING_FILE, error->message);
@@ -2894,6 +2964,7 @@ test_write_bridge_main (void)
 	              NM_SETTING_CONNECTION_UUID, uuid,
 	              NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
 	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_BRIDGE_SETTING_NAME,
+	              NM_SETTING_CONNECTION_INTERFACE_NAME, "br0",
 	              NULL);
 	g_free (uuid);
 
@@ -3076,6 +3147,247 @@ test_write_bridge_component (void)
 }
 
 static void
+test_read_new_wired_group_name (void)
+{
+	NMConnection *connection;
+	NMSettingWired *s_wired;
+	const GByteArray *array;
+	guint8 expected_mac[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };
+	GError *error = NULL;
+	gboolean success;
+
+	connection = nm_keyfile_plugin_connection_from_file (TEST_KEYFILES_DIR"/Test_New_Wired_Group_Name", &error);
+	g_assert_no_error (error);
+	g_assert (connection);
+	success = nm_connection_verify (connection, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	/* Wired setting */
+	s_wired = nm_connection_get_setting_wired (connection);
+	g_assert (s_wired);
+	g_assert_cmpint (nm_setting_wired_get_mtu (s_wired), ==, 1400);
+
+	array = nm_setting_wired_get_mac_address (s_wired);
+	g_assert (array);
+	g_assert_cmpint (array->len, ==, ETH_ALEN);
+	g_assert_cmpint (memcmp (array->data, expected_mac, sizeof (expected_mac)), ==, 0);
+
+	g_object_unref (connection);
+}
+
+static void
+test_write_new_wired_group_name (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWired *s_wired;
+	char *uuid;
+	gboolean success;
+	NMConnection *reread;
+	char *testfile = NULL;
+	GError *error = NULL;
+	pid_t owner_grp;
+	uid_t owner_uid;
+	GKeyFile *kf;
+	char *s;
+	gint mtu;
+
+	connection = nm_connection_new ();
+	g_assert (connection);
+
+	/* Connection setting */
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	g_assert (s_con);
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test Write Wired New Group Name",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+
+	/* Wired setting */
+	s_wired = (NMSettingWired *) nm_setting_wired_new ();
+	g_assert (s_wired);
+	g_object_set (s_wired, NM_SETTING_WIRED_MTU, 1400, NULL);
+	nm_connection_add_setting (connection, NM_SETTING (s_wired));
+
+	/* Write out the connection */
+	owner_uid = geteuid ();
+	owner_grp = getegid ();
+	success = nm_keyfile_plugin_write_test_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, &testfile, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+	g_assert (testfile);
+
+	/* Read the connection back in and compare it to the one we just wrote out */
+	reread = nm_keyfile_plugin_connection_from_file (testfile, &error);
+	g_assert_no_error (error);
+	g_assert (reread);
+	g_assert (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT));
+
+	/* Look at the keyfile itself to ensure we wrote out the new group names and type */
+	kf = g_key_file_new ();
+	success = g_key_file_load_from_file (kf, testfile, G_KEY_FILE_NONE, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	s = g_key_file_get_string (kf, NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE, &error);
+	g_assert_no_error (error);
+	g_assert_cmpstr (s, ==, "ethernet");
+	g_free (s);
+
+	mtu = g_key_file_get_integer (kf, "ethernet", NM_SETTING_WIRED_MTU, &error);
+	g_assert_no_error (error);
+	g_assert_cmpint (mtu, ==, 1400);
+
+	unlink (testfile);
+	g_free (testfile);
+
+	g_object_unref (reread);
+	g_object_unref (connection);
+}
+
+static void
+test_read_new_wireless_group_names (void)
+{
+	NMConnection *connection;
+	NMSettingWireless *s_wifi;
+	NMSettingWirelessSecurity *s_wsec;
+	const GByteArray *array;
+	const char *expected_ssid = "foobar";
+	GError *error = NULL;
+	gboolean success;
+
+	connection = nm_keyfile_plugin_connection_from_file (TEST_KEYFILES_DIR"/Test_New_Wireless_Group_Names", &error);
+	g_assert_no_error (error);
+	g_assert (connection);
+	success = nm_connection_verify (connection, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	/* Wifi setting */
+	s_wifi = nm_connection_get_setting_wireless (connection);
+	g_assert (s_wifi);
+
+	array = nm_setting_wireless_get_ssid (s_wifi);
+	g_assert (array);
+	g_assert_cmpint (array->len, ==, strlen (expected_ssid));
+	g_assert_cmpint (memcmp (array->data, expected_ssid, array->len), ==, 0);
+
+	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");
+	g_assert_cmpstr (nm_setting_wireless_security_get_psk (s_wsec), ==, "s3cu4e passphrase");
+
+	g_object_unref (connection);
+}
+
+static void
+test_write_new_wireless_group_names (void)
+{
+	NMConnection *connection;
+	NMSettingConnection *s_con;
+	NMSettingWireless *s_wifi;
+	NMSettingWirelessSecurity *s_wsec;
+	char *uuid;
+	GByteArray *ssid;
+	unsigned char tmpssid[] = { 0x31, 0x33, 0x33, 0x37 };
+	const char *expected_psk = "asdfasdfasdfa12315";
+	gboolean success;
+	NMConnection *reread;
+	char *testfile = NULL;
+	GError *error = NULL;
+	pid_t owner_grp;
+	uid_t owner_uid;
+	GKeyFile *kf;
+	char *s;
+
+	connection = nm_connection_new ();
+
+	/* Connection setting */
+
+	s_con = (NMSettingConnection *) nm_setting_connection_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+	uuid = nm_utils_uuid_generate ();
+	g_object_set (s_con,
+	              NM_SETTING_CONNECTION_ID, "Test Write New Wireless Group Names",
+	              NM_SETTING_CONNECTION_UUID, uuid,
+	              NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRELESS_SETTING_NAME,
+	              NULL);
+	g_free (uuid);
+
+	/* WiFi setting */
+	s_wifi = (NMSettingWireless *) nm_setting_wireless_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_wifi));
+
+	ssid = g_byte_array_sized_new (sizeof (tmpssid));
+	g_byte_array_append (ssid, &tmpssid[0], sizeof (tmpssid));
+	g_object_set (s_wifi,
+	              NM_SETTING_WIRELESS_SSID, ssid,
+	              NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
+	              NULL);
+	g_byte_array_free (ssid, TRUE);
+
+	/* WiFi security setting */
+	s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new ();
+	nm_connection_add_setting (connection, NM_SETTING (s_wsec));
+	g_object_set (s_wsec,
+	              NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk",
+	              NM_SETTING_WIRELESS_SECURITY_PSK, expected_psk,
+	              NULL);
+
+	/* Write out the connection */
+	owner_uid = geteuid ();
+	owner_grp = getegid ();
+	success = nm_keyfile_plugin_write_test_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, &testfile, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	g_assert (testfile);
+
+	/* Read the connection back in and compare it to the one we just wrote out */
+	reread = nm_keyfile_plugin_connection_from_file (testfile, &error);
+	g_assert_no_error (error);
+	g_assert (reread);
+	g_assert (nm_connection_compare (connection, reread, NM_SETTING_COMPARE_FLAG_EXACT));
+
+	/* Look at the keyfile itself to ensure we wrote out the new group names and type */
+	kf = g_key_file_new ();
+	success = g_key_file_load_from_file (kf, testfile, G_KEY_FILE_NONE, &error);
+	g_assert_no_error (error);
+	g_assert (success);
+
+	s = g_key_file_get_string (kf, NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_TYPE, &error);
+	g_assert_no_error (error);
+	g_assert_cmpstr (s, ==, "wifi");
+	g_free (s);
+
+	s = g_key_file_get_string (kf, "wifi", NM_SETTING_WIRELESS_MODE, &error);
+	g_assert_no_error (error);
+	g_assert_cmpstr (s, ==, NM_SETTING_WIRELESS_MODE_INFRA);
+	g_free (s);
+
+	s = g_key_file_get_string (kf, "wifi-security", NM_SETTING_WIRELESS_SECURITY_PSK, &error);
+	g_assert_no_error (error);
+	g_assert_cmpstr (s, ==, expected_psk);
+	g_free (s);
+
+	unlink (testfile);
+	g_free (testfile);
+
+	g_object_unref (reread);
+	g_object_unref (connection);
+}
+
+static void
 test_read_missing_vlan_setting (void)
 {
 	NMConnection *connection;
@@ -3098,15 +3410,13 @@ test_read_missing_vlan_setting (void)
 	g_object_unref (connection);
 }
 
+NMTST_DEFINE ();
+
 int main (int argc, char **argv)
 {
-	GError *error = NULL;
 	char *base;
 
-	g_type_init ();
-
-	if (!nm_utils_init (&error))
-		FAIL ("nm-utils-init", "failed to initialize libnm-util: %s", error->message);
+	nmtst_init_assert_logging (&argc, &argv);
 
 	/* The tests */
 	test_read_valid_wired_connection ();
@@ -3116,6 +3426,8 @@ int main (int argc, char **argv)
 	test_write_ip6_wired_connection ();
 
 	test_read_wired_mac_case ();
+	test_read_mac_old_format ();
+	test_read_mac_ib_old_format ();
 
 	test_read_valid_wireless_connection ();
 	test_write_wireless_connection ();
@@ -3154,6 +3466,11 @@ int main (int argc, char **argv)
 	test_read_bridge_component ();
 	test_write_bridge_component ();
 
+	test_read_new_wired_group_name ();
+	test_write_new_wired_group_name ();
+	test_read_new_wireless_group_names ();
+	test_write_new_wireless_group_names ();
+
 	test_read_missing_vlan_setting ();
 
 	base = g_path_get_basename (argv[0]);
diff --git a/src/settings/plugins/keyfile/utils.c b/src/settings/plugins/keyfile/utils.c
index 8db5d524..e2bfc38c 100644
--- a/src/settings/plugins/keyfile/utils.c
+++ b/src/settings/plugins/keyfile/utils.c
@@ -22,6 +22,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include "utils.h"
+#include <nm-setting-wired.h>
+#include <nm-setting-wireless.h>
+#include <nm-setting-wireless-security.h>
 
 
 static const char temp_letters[] =
@@ -104,3 +107,176 @@ nm_keyfile_plugin_utils_should_ignore_file (const char *filename)
 	return ignore;
 }
 
+typedef struct {
+	const char *setting;
+	const char *alias;
+} SettingAlias;
+
+static const SettingAlias alias_list[] = {
+	{ NM_SETTING_WIRED_SETTING_NAME, "ethernet" },
+	{ NM_SETTING_WIRELESS_SETTING_NAME, "wifi" },
+	{ NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, "wifi-security" },
+};
+
+const char *
+nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name)
+{
+	guint i;
+
+	g_return_val_if_fail (setting_name != NULL, NULL);
+
+	for (i = 0; i < G_N_ELEMENTS (alias_list); i++) {
+		if (strcmp (setting_name, alias_list[i].setting) == 0)
+			return alias_list[i].alias;
+	}
+	return NULL;
+}
+
+const char *
+nm_keyfile_plugin_get_setting_name_for_alias (const char *alias)
+{
+	guint i;
+
+	g_return_val_if_fail (alias != NULL, NULL);
+
+	for (i = 0; i < G_N_ELEMENTS (alias_list); i++) {
+		if (strcmp (alias, alias_list[i].alias) == 0)
+			return alias_list[i].setting;
+	}
+	return NULL;
+}
+
+/**********************************************************************/
+
+/* List helpers */
+#define DEFINE_KF_LIST_WRAPPER(stype, get_ctype, set_ctype) \
+get_ctype \
+nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \
+                                         const char *group, \
+                                         const char *key, \
+                                         gsize *out_length, \
+                                         GError **error) \
+{ \
+	get_ctype list; \
+	const char *alias; \
+	GError *local = NULL; \
+ \
+	list = g_key_file_get_##stype##_list (kf, group, key, out_length, &local); \
+	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \
+		alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
+		if (alias) { \
+			g_clear_error (&local); \
+			list = g_key_file_get_##stype##_list (kf, alias, key, out_length, &local); \
+		} \
+	} \
+	if (local) \
+		g_propagate_error (error, local); \
+	return list; \
+} \
+ \
+void \
+nm_keyfile_plugin_kf_set_##stype##_list (GKeyFile *kf, \
+                                         const char *group, \
+                                         const char *key, \
+                                         set_ctype list[], \
+                                         gsize length) \
+{ \
+	const char *alias; \
+ \
+	alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
+	g_key_file_set_##stype##_list (kf, alias ? alias : group, key, list, length); \
+}
+
+DEFINE_KF_LIST_WRAPPER(integer, gint*, gint);
+DEFINE_KF_LIST_WRAPPER(string, gchar **, const gchar* const);
+
+/* Single value helpers */
+#define DEFINE_KF_WRAPPER(stype, get_ctype, set_ctype) \
+get_ctype \
+nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \
+                                  const char *group, \
+                                  const char *key, \
+                                  GError **error) \
+{ \
+	get_ctype val; \
+	const char *alias; \
+	GError *local = NULL; \
+ \
+	val = g_key_file_get_##stype (kf, group, key, &local); \
+	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \
+		alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
+		if (alias) { \
+			g_clear_error (&local); \
+			val = g_key_file_get_##stype (kf, alias, key, &local); \
+		} \
+	} \
+	if (local) \
+		g_propagate_error (error, local); \
+	return val; \
+} \
+ \
+void \
+nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
+                                  const char *group, \
+                                  const char *key, \
+                                  set_ctype value) \
+{ \
+	const char *alias; \
+ \
+	alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
+	g_key_file_set_##stype (kf, alias ? alias : group, key, value); \
+}
+
+DEFINE_KF_WRAPPER(string, gchar*, const gchar*);
+DEFINE_KF_WRAPPER(integer, gint, gint);
+DEFINE_KF_WRAPPER(boolean, gboolean, gboolean);
+DEFINE_KF_WRAPPER(value, gchar*, const gchar*);
+
+
+gchar **
+nm_keyfile_plugin_kf_get_keys (GKeyFile *kf,
+                               const char *group,
+                               gsize *out_length,
+                               GError **error)
+{
+	gchar **keys;
+	const char *alias;
+	GError *local = NULL;
+
+	keys = g_key_file_get_keys (kf, group, out_length, &local);
+	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+		alias = nm_keyfile_plugin_get_alias_for_setting_name (group);
+		if (alias) {
+			g_clear_error (&local);
+			keys = g_key_file_get_keys (kf, alias, out_length, &local);
+		}
+	}
+	if (local)
+		g_propagate_error (error, local);
+	return keys;
+}
+
+gboolean
+nm_keyfile_plugin_kf_has_key (GKeyFile *kf,
+                              const char *group,
+                              const char *key,
+                              GError **error)
+{
+	gboolean has;
+	const char *alias;
+	GError *local = NULL;
+
+	has = g_key_file_has_key (kf, group, key, &local);
+	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
+		alias = nm_keyfile_plugin_get_alias_for_setting_name (group);
+		if (alias) {
+			g_clear_error (&local);
+			has = g_key_file_has_key (kf, alias, key, &local);
+		}
+	}
+	if (local)
+		g_propagate_error (error, local);
+	return has;
+}
+
+
diff --git a/src/settings/plugins/keyfile/utils.h b/src/settings/plugins/keyfile/utils.h
index 68e6e56f..9a2e485b 100644
--- a/src/settings/plugins/keyfile/utils.h
+++ b/src/settings/plugins/keyfile/utils.h
@@ -26,5 +26,54 @@
 
 gboolean nm_keyfile_plugin_utils_should_ignore_file (const char *filename);
 
+const char *nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name);
+
+const char *nm_keyfile_plugin_get_setting_name_for_alias (const char *alias);
+
+/*********************************************************/
+
+/* List helpers */
+#define DEFINE_KF_LIST_WRAPPER_PROTO(stype, get_ctype, set_ctype) \
+get_ctype nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \
+                                                   const char *group, \
+                                                   const char *key, \
+                                                   gsize *out_length, \
+                                                   GError **error); \
+\
+void nm_keyfile_plugin_kf_set_##stype##_list  (GKeyFile *kf, \
+                                               const char *group, \
+                                               const char *key, \
+                                               set_ctype list[], \
+                                               gsize length);
+DEFINE_KF_LIST_WRAPPER_PROTO(integer, gint*, gint)
+DEFINE_KF_LIST_WRAPPER_PROTO(string, gchar**, const gchar* const)
+
+/* Single-value helpers */
+#define DEFINE_KF_WRAPPER_PROTO(stype, get_ctype, set_ctype) \
+get_ctype nm_keyfile_plugin_kf_get_##stype (GKeyFile *kf, \
+                                            const char *group, \
+                                            const char *key, \
+                                            GError **error); \
+\
+void nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
+                                       const char *group, \
+                                       const char *key, \
+                                       set_ctype value);
+DEFINE_KF_WRAPPER_PROTO(string, gchar*, const gchar*)
+DEFINE_KF_WRAPPER_PROTO(integer, gint, gint)
+DEFINE_KF_WRAPPER_PROTO(boolean, gboolean, gboolean)
+DEFINE_KF_WRAPPER_PROTO(value, gchar*, const gchar*)
+
+/* Misc */
+gchar ** nm_keyfile_plugin_kf_get_keys    (GKeyFile *kf,
+                                           const char *group,
+                                           gsize *out_length,
+                                           GError **error);
+
+gboolean nm_keyfile_plugin_kf_has_key     (GKeyFile *kf,
+                                           const char *group,
+                                           const char *key,
+                                           GError **error);
+
 #endif  /* _UTILS_H_ */
 
diff --git a/src/settings/plugins/keyfile/writer.c b/src/settings/plugins/keyfile/writer.c
index 38061a5d..5f3ebe25 100644
--- a/src/settings/plugins/keyfile/writer.c
+++ b/src/settings/plugins/keyfile/writer.c
@@ -44,8 +44,35 @@
 
 #include "nm-dbus-glib-types.h"
 #include "nm-glib-compat.h"
+#include "nm-logging.h"
 #include "writer.h"
 #include "common.h"
+#include "utils.h"
+
+/* Some setting properties also contain setting names, such as
+ * NMSettingConnection's 'type' property (which specifies the base type of the
+ * connection, eg ethernet or wifi) or the 802-11-wireless setting's
+ * 'security' property which specifies whether or not the AP requires
+ * encrpytion.  This function handles translating those properties' values
+ * from the real setting name to the more-readable alias.
+ */
+static void
+setting_alias_writer (GKeyFile *file,
+                      const char *keyfile_dir,
+                      const char *uuid,
+                      NMSetting *setting,
+                      const char *key,
+                      const GValue *value)
+{
+	const char *str, *alias;
+
+	str = g_value_get_string (value);
+	alias = nm_keyfile_plugin_get_alias_for_setting_name (str);
+	nm_keyfile_plugin_kf_set_string (file,
+	                                 nm_setting_get_name (setting),
+	                                 key,
+	                                 alias ? alias : str);
+}
 
 static gboolean
 write_array_of_uint (GKeyFile *file,
@@ -65,7 +92,7 @@ write_array_of_uint (GKeyFile *file,
 	for (i = 0; i < array->len; i++)
 		tmp_array[i] = g_array_index (array, int, i);
 
-	g_key_file_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len);
+	nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len);
 	g_free (tmp_array);
 	return TRUE;
 }
@@ -91,71 +118,63 @@ ip4_dns_writer (GKeyFile *file,
 	list = g_new0 (char *, array->len + 1);
 
 	for (i = 0; i < array->len; i++) {
-		char buf[INET_ADDRSTRLEN + 1];
-		struct in_addr addr;
-
-		addr.s_addr = g_array_index (array, guint32, i);
-		if (!inet_ntop (AF_INET, &addr, buf, sizeof (buf))) {
-			g_warning ("%s: error converting IP4 address 0x%X",
-			           __func__, ntohl (addr.s_addr));
-		} else
-			list[num++] = g_strdup (buf);
+		char *buf = g_new (char, INET_ADDRSTRLEN);
+		guint32 addr;
+
+		addr = g_array_index (array, guint32, i);
+		nm_utils_inet4_ntop (addr, buf);
+		list[num++] = buf;
 	}
 
-	g_key_file_set_string_list (file, nm_setting_get_name (setting), key, (const char **) list, num);
+	nm_keyfile_plugin_kf_set_string_list (file, nm_setting_get_name (setting), key, (const char **) list, num);
 	g_strfreev (list);
 }
 
 static void
 write_ip4_values (GKeyFile *file,
                   const char *setting_name,
-                  const char *key,
                   GPtrArray *array,
-                  guint32 tuple_len,
-                  guint32 addr1_pos,
-                  guint32 addr2_pos)
+                  gboolean is_route)
 {
 	GString *output;
-	int i, j;
+	int i;
+	guint32 addr, gw, plen, metric;
+	char key_name[30], *key_name_idx;
 
-	for (i = 0, j = 0; i < array->len; i++, j++) {
-		GArray *tuple = g_ptr_array_index (array, i);
-		gboolean success = TRUE;
-		char *key_name;
-		int k;
+	if (!array->len)
+		return;
 
-		output = g_string_new ("");
+	strcpy (key_name, is_route ? "route" : "address");
+	key_name_idx = key_name + strlen (key_name);
 
-		for (k = 0; k < tuple_len; k++) {
-			if (k == addr1_pos || k == addr2_pos) {
-				char buf[INET_ADDRSTRLEN + 1];
-				struct in_addr addr;
-
-				/* IP addresses */
-				addr.s_addr = g_array_index (tuple, guint32, k);
-				if (!inet_ntop (AF_INET, &addr, buf, sizeof (buf))) {
-					g_warning ("%s: error converting IP4 address 0x%X",
-					           __func__, ntohl (addr.s_addr));
-					success = FALSE;
-					break;
-				} else {
-					g_string_append_printf (output, "%s%s", k == 0 ? "" : ",", buf);
-				}
-			} else {
-				/* prefix, metric */
-				g_string_append_printf (output, "%c%d", k == 1 ? '/' : ',', g_array_index (tuple, guint32, k));
-			}
-		}
+	output = g_string_sized_new (2*INET_ADDRSTRLEN + 10);
+	for (i = 0; i < array->len; i++) {
+		GArray *tuple = g_ptr_array_index (array, i);
 
-		if (success) {
-			key_name = g_strdup_printf ("%s%d", key, j + 1);
-			g_key_file_set_string (file, setting_name, key_name, output->str);
-			g_free (key_name);
+		addr = g_array_index (tuple, guint32, 0);
+		plen = g_array_index (tuple, guint32, 1);
+		gw = g_array_index (tuple, guint32, 2);
+		metric = is_route ? g_array_index (tuple, guint32, 3) : 0;
+
+		g_string_set_size (output, 0);
+		g_string_append_printf (output, "%s/%u",
+		                        nm_utils_inet4_ntop (addr, NULL),
+		                        (unsigned) plen);
+		if (metric || gw) {
+			/* Older versions of the plugin do not support the form
+			 * "a.b.c.d/plen,,metric", so, we always have to write the
+			 * gateway, even if it's 0.0.0.0.
+			 * The current version support reading of the above form. */
+			g_string_append_c (output, ',');
+			g_string_append (output, nm_utils_inet4_ntop (gw, NULL));
+			if (metric)
+				g_string_append_printf (output, ",%lu", (unsigned long) metric);
 		}
 
-		g_string_free (output, TRUE);
-
+		sprintf (key_name_idx, "%d", i + 1);
+		nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str);
 	}
+	g_string_free (output, TRUE);
 }
 
 static void
@@ -173,7 +192,18 @@ ip4_addr_writer (GKeyFile *file,
 
 	array = (GPtrArray *) g_value_get_boxed (value);
 	if (array && array->len)
-		write_ip4_values (file, setting_name, "address", array, 3, 0, 2);
+		write_ip4_values (file, setting_name, array, FALSE);
+}
+
+static void
+ip4_addr_label_writer (GKeyFile *file,
+                       const char *keyfile_dir,
+                       const char *uuid,
+                       NMSetting *setting,
+                       const char *key,
+                       const GValue *value)
+{
+	/* skip */
 }
 
 static void
@@ -191,7 +221,7 @@ ip4_route_writer (GKeyFile *file,
 
 	array = (GPtrArray *) g_value_get_boxed (value);
 	if (array && array->len)
-		write_ip4_values (file, setting_name, "route", array, 4, 0, 2);
+		write_ip4_values (file, setting_name, array, TRUE);
 }
 
 static void
@@ -216,90 +246,63 @@ ip6_dns_writer (GKeyFile *file,
 	list = g_new0 (char *, array->len + 1);
 
 	for (i = 0; i < array->len; i++) {
-		char buf[INET6_ADDRSTRLEN];
+		char *buf = g_new (char, INET6_ADDRSTRLEN);
 
 		byte_array = g_ptr_array_index (array, i);
-		if (!inet_ntop (AF_INET6, (struct in6_addr *) byte_array->data, buf, sizeof (buf))) {
-			int j;
-			GString *ip6_str = g_string_new (NULL);
-			g_string_append_printf (ip6_str, "%02X", byte_array->data[0]);
-			for (j = 1; j < 16; j++)
-				g_string_append_printf (ip6_str, " %02X", byte_array->data[j]);
-			g_warning ("%s: error converting IP6 address %s",
-			           __func__, ip6_str->str);
-			g_string_free (ip6_str, TRUE);
-		} else
-			list[num++] = g_strdup (buf);
+		nm_utils_inet6_ntop ((const struct in6_addr *) byte_array->data, buf);
+		list[num++] = buf;
 	}
 
-	g_key_file_set_string_list (file, nm_setting_get_name (setting), key, (const char **) list, num);
+	nm_keyfile_plugin_kf_set_string_list (file, nm_setting_get_name (setting), key, (const char **) list, num);
 	g_strfreev (list);
 }
 
-static gboolean
+static void
 ip6_array_to_addr (GValueArray *values,
                    guint32 idx,
                    char *buf,
-                   size_t buflen,
-                   gboolean *out_is_unspec)
+                   struct in6_addr *out_addr)
 {
 	GByteArray *byte_array;
 	GValue *addr_val;
-	struct in6_addr *addr;
-
-	g_return_val_if_fail (buflen >= INET6_ADDRSTRLEN, FALSE);
+	const struct in6_addr *addr;
 
 	addr_val = g_value_array_get_nth (values, idx);
 	byte_array = g_value_get_boxed (addr_val);
-	addr = (struct in6_addr *) byte_array->data;
+	addr = (const struct in6_addr *) byte_array->data;
 
-	if (out_is_unspec && IN6_IS_ADDR_UNSPECIFIED (addr))
-		*out_is_unspec = TRUE;
+	nm_utils_inet6_ntop (addr, buf);
 
-	errno = 0;
-	if (!inet_ntop (AF_INET6, addr, buf, buflen)) {
-		GString *ip6_str = g_string_sized_new (INET6_ADDRSTRLEN + 10);
-
-		/* error converting the address */
-		g_string_append_printf (ip6_str, "%02X", byte_array->data[0]);
-		for (idx = 1; idx < 16; idx++)
-			g_string_append_printf (ip6_str, " %02X", byte_array->data[idx]);
-		g_warning ("%s: error %d converting IP6 address %s",
-		           __func__, errno, ip6_str->str);
-		g_string_free (ip6_str, TRUE);
-		return FALSE;
-	}
-
-	return TRUE;
+	if (out_addr)
+		*out_addr = *addr;
 }
 
 static char *
-ip6_array_to_addr_prefix (GValueArray *values)
+ip6_array_to_addr_prefix (GValueArray *values, gboolean force_write_gateway)
 {
 	GValue *prefix_val;
 	char *ret = NULL;
 	GString *ip6_str;
-	char buf[INET6_ADDRSTRLEN + 1];
-	gboolean is_unspec = FALSE;
+	char buf[INET6_ADDRSTRLEN];
+	struct in6_addr addr;
 
 	/* address */
-	if (ip6_array_to_addr (values, 0, buf, sizeof (buf), NULL)) {
-		/* Enough space for the address, '/', and the prefix */
-		ip6_str = g_string_sized_new ((INET6_ADDRSTRLEN * 2) + 5);
-
-		/* prefix */
-		g_string_append (ip6_str, buf);
-		prefix_val = g_value_array_get_nth (values, 1);
-		g_string_append_printf (ip6_str, "/%u", g_value_get_uint (prefix_val));
-
-		if (ip6_array_to_addr (values, 2, buf, sizeof (buf), &is_unspec)) {
-			if (!is_unspec)
-				g_string_append_printf (ip6_str, ",%s", buf);
-		}
+	ip6_array_to_addr (values, 0, buf, NULL);
 
-		ret = ip6_str->str;
-		g_string_free (ip6_str, FALSE);
-	}
+	/* Enough space for the address, '/', and the prefix */
+	ip6_str = g_string_sized_new ((INET6_ADDRSTRLEN * 2) + 5);
+
+	/* prefix */
+	g_string_append (ip6_str, buf);
+	prefix_val = g_value_array_get_nth (values, 1);
+	g_string_append_printf (ip6_str, "/%u", g_value_get_uint (prefix_val));
+
+	ip6_array_to_addr (values, 2, buf, &addr);
+	if (force_write_gateway || !IN6_IS_ADDR_UNSPECIFIED (&addr))
+		g_string_append_printf (ip6_str, ",%s", buf);
+
+	ret = ip6_str->str;
+	g_string_free (ip6_str, FALSE);
 
 	return ret;
 }
@@ -327,20 +330,18 @@ ip6_addr_writer (GKeyFile *file,
 		char *key_name, *ip6_addr;
 
 		if (values->n_values != 3) {
-			g_warning ("%s: error writing IP6 address %d (address array length "
-			           "%d is not 3)",
-			           __func__, i, values->n_values);
+			nm_log_warn (LOGD_SETTINGS, "%s: error writing IP6 address %d (address array "
+			             "length %d is not 3)", __func__, i, values->n_values);
 			continue;
 		}
 
-		ip6_addr = ip6_array_to_addr_prefix (values);
-		if (ip6_addr) {
-			/* Write it out */
-			key_name = g_strdup_printf ("address%d", j++);
-			g_key_file_set_string (file, setting_name, key_name, ip6_addr);
-			g_free (key_name);
-			g_free (ip6_addr);
-		}
+		/* we allow omitting the gateway if it's :: */
+		ip6_addr = ip6_array_to_addr_prefix (values, FALSE);
+		/* Write it out */
+		key_name = g_strdup_printf ("address%d", j++);
+		nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, ip6_addr);
+		g_free (key_name);
+		g_free (ip6_addr);
 	}
 }
 
@@ -366,21 +367,35 @@ ip6_route_writer (GKeyFile *file,
 	for (i = 0, j = 1; i < array->len; i++) {
 		GValueArray *values = g_ptr_array_index (array, i);
 		char *key_name;
-		guint32 int_val;
+		char *addr_str;
+		guint metric;
 
 		output = g_string_new ("");
 
-		/* Address, prefix and next hop*/
-		g_string_append (output, ip6_array_to_addr_prefix (values));
-
 		/* Metric */
 		value = g_value_array_get_nth (values, 3);
-		int_val = g_value_get_uint (value);
-		g_string_append_printf (output, ",%d", int_val);
+		metric = g_value_get_uint (value);
+
+		/* Address, prefix and next hop
+		 * We allow omitting the gateway ::, if we also omit the metric
+		 * and force writing of the gateway, if we add a non zero metric.
+		 * The current version of the reader also supports the syntax
+		 * "a:b:c::/plen,,metric" for a gateway ::.
+		 * As older versions of the plugin, cannot read this form,
+		 * we always write the gateway, whenever we also write the metric.
+		 * But if possible, we omit them both (",::,0") or only the metric
+		 * (",0").
+		 **/
+		addr_str = ip6_array_to_addr_prefix (values, metric != 0);
+		g_string_append (output, addr_str);
+		g_free (addr_str);
+
+		if (metric != 0)
+			g_string_append_printf (output, ",%u", metric);
 
 		/* Write it out */
 		key_name = g_strdup_printf ("route%d", j++);
-		g_key_file_set_string (file, setting_name, key_name, output->str);
+		nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str);
 		g_free (key_name);
 
 		g_string_free (output, TRUE);
@@ -399,23 +414,15 @@ mac_address_writer (GKeyFile *file,
 	GByteArray *array;
 	const char *setting_name = nm_setting_get_name (setting);
 	char *mac;
-	int type;
 
 	g_return_if_fail (G_VALUE_HOLDS (value, DBUS_TYPE_G_UCHAR_ARRAY));
 
 	array = (GByteArray *) g_value_get_boxed (value);
-	if (!array)
-		return;
-
-	type = nm_utils_hwaddr_type (array->len);
-	if (type < 0) {
-		g_warning ("%s: invalid %s / %s MAC address length %d",
-		           __func__, setting_name, key, array->len);
+	if (!array || !array->len)
 		return;
-	}
 
-	mac = nm_utils_hwaddr_ntoa (array->data, type);
-	g_key_file_set_string (file, setting_name, key, mac);
+	mac = nm_utils_hwaddr_ntoa_len (array->data, array->len);
+	nm_keyfile_plugin_kf_set_string (file, setting_name, key, mac);
 	g_free (mac);
 }
 
@@ -453,7 +460,7 @@ write_hash_of_string (GKeyFile *file,
 		}
 
 		if (write_item)
-			g_key_file_set_string (file, group_name, property, data);
+			nm_keyfile_plugin_kf_set_string (file, group_name, property, data);
 	}
 }
 
@@ -505,13 +512,13 @@ ssid_writer (GKeyFile *file,
 				ssid[j++] = array->data[i];
 			}
 		}
-		g_key_file_set_string (file, setting_name, key, ssid);
+		nm_keyfile_plugin_kf_set_string (file, setting_name, key, ssid);
 		g_free (ssid);
 	} else {
 		tmp_array = g_new (gint, array->len);
 		for (i = 0; i < array->len; i++)
 			tmp_array[i] = (int) array->data[i];
-		g_key_file_set_integer_list (file, setting_name, key, tmp_array, array->len);
+		nm_keyfile_plugin_kf_set_integer_list (file, setting_name, key, tmp_array, array->len);
 		g_free (tmp_array);
 	}
 }
@@ -537,7 +544,7 @@ password_raw_writer (GKeyFile *file,
 	tmp_array = g_new (gint, array->len);
 	for (i = 0; i < array->len; i++)
 		tmp_array[i] = (int) array->data[i];
-	g_key_file_set_integer_list (file, setting_name, key, tmp_array, array->len);
+	nm_keyfile_plugin_kf_set_integer_list (file, setting_name, key, tmp_array, array->len);
 	g_free (tmp_array);
 }
 
@@ -686,7 +693,10 @@ cert_writer (GKeyFile *file,
 			break;
 		}
 	}
-	g_return_if_fail (objtype != NULL);
+	if (!objtype) {
+		g_return_if_fail (objtype);
+		return;
+	}
 
 	scheme = objtype->scheme_func (NM_SETTING_802_1X (setting));
 	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
@@ -702,7 +712,7 @@ cert_writer (GKeyFile *file,
 				path++;
 		}
 
-		g_key_file_set_string (file, setting_name, key, path);
+		nm_keyfile_plugin_kf_set_string (file, setting_name, key, path);
 	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
 		const GByteArray *blob;
 		gboolean success;
@@ -732,9 +742,10 @@ cert_writer (GKeyFile *file,
 		success = write_cert_key_file (new_path, blob, &error);
 		if (success) {
 			/* Write the path value to the keyfile */
-			g_key_file_set_string (file, setting_name, key, new_path);
+			nm_keyfile_plugin_kf_set_string (file, setting_name, key, new_path);
 		} else {
-			g_warning ("Failed to write certificate/key %s: %s", new_path, error->message);
+			nm_log_warn (LOGD_SETTINGS, "Failed to write certificate/key %s: %s",
+			             new_path, error->message);
 			g_error_free (error);
 		}
 		g_free (new_path);
@@ -760,9 +771,18 @@ typedef struct {
  * in struct in6_addr internally, but as string in keyfiles.
  */
 static KeyWriter key_writers[] = {
+	{ NM_SETTING_CONNECTION_SETTING_NAME,
+	  NM_SETTING_CONNECTION_TYPE,
+	  setting_alias_writer },
+	{ NM_SETTING_BRIDGE_SETTING_NAME,
+	  NM_SETTING_BRIDGE_MAC_ADDRESS,
+	  mac_address_writer },
 	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
 	  NM_SETTING_IP4_CONFIG_ADDRESSES,
 	  ip4_addr_writer },
+	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	  "address-labels",
+	  ip4_addr_label_writer },
 	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
 	  NM_SETTING_IP6_CONFIG_ADDRESSES,
 	  ip6_addr_writer },
@@ -873,7 +893,7 @@ write_setting_value (NMSetting *setting,
 	 * the secret flags there are in a third-level hash in the 'secrets'
 	 * property.
 	 */
-	if (pspec->flags & NM_SETTING_PARAM_SECRET && !NM_IS_SETTING_VPN (setting)) {
+	if (pspec && (pspec->flags & NM_SETTING_PARAM_SECRET) && !NM_IS_SETTING_VPN (setting)) {
 		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
 
 		nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
@@ -895,21 +915,21 @@ write_setting_value (NMSetting *setting,
 
 		str = g_value_get_string (value);
 		if (str)
-			g_key_file_set_string (info->keyfile, setting_name, key, str);
+			nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str);
 	} else if (type == G_TYPE_UINT)
-		g_key_file_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value));
+		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value));
 	else if (type == G_TYPE_INT)
-		g_key_file_set_integer (info->keyfile, setting_name, key, g_value_get_int (value));
+		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, g_value_get_int (value));
 	else if (type == G_TYPE_UINT64) {
 		char *numstr;
 
 		numstr = g_strdup_printf ("%" G_GUINT64_FORMAT, g_value_get_uint64 (value));
-		g_key_file_set_value (info->keyfile, setting_name, key, numstr);
+		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
 		g_free (numstr);
 	} else if (type == G_TYPE_BOOLEAN) {
-		g_key_file_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value));
+		nm_keyfile_plugin_kf_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value));
 	} else if (type == G_TYPE_CHAR) {
-		g_key_file_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value));
+		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value));
 	} else if (type == DBUS_TYPE_G_UCHAR_ARRAY) {
 		GByteArray *array;
 
@@ -922,7 +942,7 @@ write_setting_value (NMSetting *setting,
 			for (i = 0; i < array->len; i++)
 				tmp_array[i] = (int) array->data[i];
 
-			g_key_file_set_integer_list (info->keyfile, setting_name, key, tmp_array, array->len);
+			nm_keyfile_plugin_kf_set_integer_list (info->keyfile, setting_name, key, tmp_array, array->len);
 			g_free (tmp_array);
 		}
 	} else if (type == DBUS_TYPE_G_LIST_OF_STRING) {
@@ -938,19 +958,19 @@ write_setting_value (NMSetting *setting,
 			for (iter = list; iter; iter = iter->next)
 				array[i++] = iter->data;
 
-			g_key_file_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, i);
+			nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, i);
 			g_free (array);
 		}
 	} else if (type == DBUS_TYPE_G_MAP_OF_STRING) {
 		write_hash_of_string (info->keyfile, setting, key, value);
 	} else if (type == DBUS_TYPE_G_UINT_ARRAY) {
 		if (!write_array_of_uint (info->keyfile, setting, key, value)) {
-			g_warning ("Unhandled setting property type (write) '%s/%s' : '%s'", 
-					 setting_name, key, g_type_name (type));
+			nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (write) '%s/%s' : '%s'", 
+			             setting_name, key, g_type_name (type));
 		}
 	} else {
-		g_warning ("Unhandled setting property type (write) '%s/%s' : '%s'", 
-				 setting_name, key, g_type_name (type));
+		nm_log_warn (LOGD_SETTINGS, "Unhandled setting property type (write) '%s/%s' : '%s'", 
+		             setting_name, key, g_type_name (type));
 	}
 }
 
@@ -995,6 +1015,11 @@ _internal_write_connection (NMConnection *connection,
 	if (out_path)
 		g_return_val_if_fail (*out_path == NULL, FALSE);
 
+	if (!nm_connection_verify (connection, error)) {
+		g_return_val_if_reached (FALSE);
+		return FALSE;
+	}
+
 	id = nm_connection_get_id (connection);
 	if (!id) {
 		g_set_error (error, KEYFILE_PLUGIN_ERROR, 0,