summary refs log tree commit diff
path: root/libnm-core
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-core')
-rw-r--r--libnm-core/crypto.h4
-rw-r--r--libnm-core/meson.build191
-rw-r--r--libnm-core/nm-connection-private.h4
-rw-r--r--libnm-core/nm-connection.c12
-rw-r--r--libnm-core/nm-connection.h2
-rw-r--r--libnm-core/nm-core-enum-types.c.template86
-rw-r--r--libnm-core/nm-core-enum-types.h.template21
-rw-r--r--libnm-core/nm-core-internal.h18
-rw-r--r--libnm-core/nm-core-types-internal.h13
-rw-r--r--libnm-core/nm-dbus-interface.h48
-rw-r--r--libnm-core/nm-dbus-types.xml49
-rw-r--r--libnm-core/nm-dbus-utils.c10
-rw-r--r--libnm-core/nm-json.c11
-rw-r--r--libnm-core/nm-json.h3
-rw-r--r--libnm-core/nm-keyfile-internal.h4
-rw-r--r--libnm-core/nm-keyfile-reader.c (renamed from libnm-core/nm-keyfile.c)1942
-rw-r--r--libnm-core/nm-keyfile-utils.c2
-rw-r--r--libnm-core/nm-keyfile-utils.h4
-rw-r--r--libnm-core/nm-keyfile-writer.c882
-rw-r--r--libnm-core/nm-property-compare.h4
-rw-r--r--libnm-core/nm-setting-8021x.c85
-rw-r--r--libnm-core/nm-setting-bond.c31
-rw-r--r--libnm-core/nm-setting-bridge.c2
-rw-r--r--libnm-core/nm-setting-connection.c90
-rw-r--r--libnm-core/nm-setting-connection.h21
-rw-r--r--libnm-core/nm-setting-dcb.c24
-rw-r--r--libnm-core/nm-setting-ip-config.c89
-rw-r--r--libnm-core/nm-setting-ip-config.h2
-rw-r--r--libnm-core/nm-setting-ip-tunnel.c60
-rw-r--r--libnm-core/nm-setting-ip-tunnel.h30
-rw-r--r--libnm-core/nm-setting-ip4-config.c30
-rw-r--r--libnm-core/nm-setting-ip6-config.c14
-rw-r--r--libnm-core/nm-setting-macvlan.c8
-rw-r--r--libnm-core/nm-setting-ovs-patch.c8
-rw-r--r--libnm-core/nm-setting-private.h6
-rw-r--r--libnm-core/nm-setting-tc-config.c112
-rw-r--r--libnm-core/nm-setting-tc-config.h90
-rw-r--r--libnm-core/nm-setting-team-port.c37
-rw-r--r--libnm-core/nm-setting-team-port.h22
-rw-r--r--libnm-core/nm-setting-team.c115
-rw-r--r--libnm-core/nm-setting-team.h80
-rw-r--r--libnm-core/nm-setting-tun.c4
-rw-r--r--libnm-core/nm-setting-user.c25
-rw-r--r--libnm-core/nm-setting-vlan.h4
-rw-r--r--libnm-core/nm-setting-vpn.c130
-rw-r--r--libnm-core/nm-setting-vpn.h8
-rw-r--r--libnm-core/nm-setting-wired.c6
-rw-r--r--libnm-core/nm-setting-wireless-security.c57
-rw-r--r--libnm-core/nm-setting-wireless-security.h26
-rw-r--r--libnm-core/nm-setting.c34
-rw-r--r--libnm-core/nm-simple-connection.c7
-rw-r--r--libnm-core/nm-utils-private.h4
-rw-r--r--libnm-core/nm-utils.c188
-rw-r--r--libnm-core/nm-utils.h12
-rw-r--r--libnm-core/nm-version.h22
-rw-r--r--libnm-core/nm-vpn-editor-plugin.h10
-rw-r--r--libnm-core/nm-vpn-plugin-info.h4
-rw-r--r--libnm-core/tests/meson.build51
-rw-r--r--libnm-core/tests/nm-core-tests-enum-types.c.template35
-rw-r--r--libnm-core/tests/nm-core-tests-enum-types.h.template21
-rw-r--r--libnm-core/tests/test-general.c363
-rw-r--r--libnm-core/tests/test-keyfile.c30
-rw-r--r--libnm-core/tests/test-secrets.c3
-rw-r--r--libnm-core/tests/test-setting.c15
64 files changed, 2255 insertions, 3070 deletions
diff --git a/libnm-core/crypto.h b/libnm-core/crypto.h
index e89f0919..8c4cfefe 100644
--- a/libnm-core/crypto.h
+++ b/libnm-core/crypto.h
@@ -24,10 +24,6 @@
 #ifndef __CRYPTO_H__
 #define __CRYPTO_H__
 
-#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-#error Cannot use this header.
-#endif
-
 #define MD5_HASH_LEN 20
 #define CIPHER_DES_EDE3_CBC "DES-EDE3-CBC"
 #define CIPHER_DES_CBC "DES-CBC"
diff --git a/libnm-core/meson.build b/libnm-core/meson.build
deleted file mode 100644
index bba84406..00000000
--- a/libnm-core/meson.build
+++ /dev/null
@@ -1,191 +0,0 @@
-libnm_core_inc = include_directories('.')
-
-libnm_core_headers = files(
-  'nm-connection.h',
-  'nm-core-types.h',
-  'nm-dbus-interface.h',
-  'nm-errors.h',
-  'nm-setting-8021x.h',
-  'nm-setting-adsl.h',
-  'nm-setting-bluetooth.h',
-  'nm-setting-bond.h',
-  'nm-setting-bridge-port.h',
-  'nm-setting-bridge.h',
-  'nm-setting-cdma.h',
-  'nm-setting-connection.h',
-  'nm-setting-dcb.h',
-  'nm-setting-dummy.h',
-  'nm-setting-generic.h',
-  'nm-setting-gsm.h',
-  'nm-setting-infiniband.h',
-  'nm-setting-ip-config.h',
-  'nm-setting-ip-tunnel.h',
-  'nm-setting-ip4-config.h',
-  'nm-setting-ip6-config.h',
-  'nm-setting-macsec.h',
-  'nm-setting-macvlan.h',
-  'nm-setting-olpc-mesh.h',
-  'nm-setting-ovs-bridge.h',
-  'nm-setting-ovs-interface.h',
-  'nm-setting-ovs-patch.h',
-  'nm-setting-ovs-port.h',
-  'nm-setting-ppp.h',
-  'nm-setting-pppoe.h',
-  'nm-setting-proxy.h',
-  'nm-setting-serial.h',
-  'nm-setting-tc-config.h',
-  'nm-setting-team-port.h',
-  'nm-setting-team.h',
-  'nm-setting-tun.h',
-  'nm-setting-user.h',
-  'nm-setting-vlan.h',
-  'nm-setting-vpn.h',
-  'nm-setting-vxlan.h',
-  'nm-setting-wimax.h',
-  'nm-setting-wired.h',
-  'nm-setting-wireless-security.h',
-  'nm-setting-wireless.h',
-  'nm-setting.h',
-  'nm-simple-connection.h',
-  'nm-utils.h',
-  'nm-version.h',
-  'nm-vpn-dbus-interface.h',
-  'nm-vpn-editor-plugin.h',
-  'nm-vpn-plugin-info.h'
-)
-
-libnm_core_settings_sources = files(
-  'nm-setting-8021x.c',
-  'nm-setting-adsl.c',
-  'nm-setting-bluetooth.c',
-  'nm-setting-bond.c',
-  'nm-setting-bridge-port.c',
-  'nm-setting-bridge.c',
-  'nm-setting-cdma.c',
-  'nm-setting-connection.c',
-  'nm-setting-dcb.c',
-  'nm-setting-dummy.c',
-  'nm-setting-generic.c',
-  'nm-setting-gsm.c',
-  'nm-setting-infiniband.c',
-  'nm-setting-ip-config.c',
-  'nm-setting-ip-tunnel.c',
-  'nm-setting-ip4-config.c',
-  'nm-setting-ip6-config.c',
-  'nm-setting-macsec.c',
-  'nm-setting-macvlan.c',
-  'nm-setting-olpc-mesh.c',
-  'nm-setting-ovs-bridge.c',
-  'nm-setting-ovs-interface.c',
-  'nm-setting-ovs-patch.c',
-  'nm-setting-ovs-port.c',
-  'nm-setting-ppp.c',
-  'nm-setting-pppoe.c',
-  'nm-setting-proxy.c',
-  'nm-setting-serial.c',
-  'nm-setting-tc-config.c',
-  'nm-setting-team-port.c',
-  'nm-setting-team.c',
-  'nm-setting-tun.c',
-  'nm-setting-user.c',
-  'nm-setting-vlan.c',
-  'nm-setting-vpn.c',
-  'nm-setting-vxlan.c',
-  'nm-setting-wimax.c',
-  'nm-setting-wired.c',
-  'nm-setting-wireless-security.c',
-  'nm-setting-wireless.c'
-)
-
-libnm_core_sources = libnm_core_settings_sources + files(
-  'crypto.c',
-  'crypto_' + crypto + '.c',
-  'nm-connection.c',
-  'nm-dbus-utils.c',
-  'nm-errors.c',
-  'nm-keyfile.c',
-  'nm-keyfile-utils.c',
-  'nm-property-compare.c',
-  'nm-setting.c',
-  'nm-simple-connection.c',
-  'nm-utils.c',
-  'nm-vpn-editor-plugin.c',
-  'nm-vpn-plugin-info.c'
-)
-
-enum_headers = libnm_core_headers + [version_header]
-enum_types = 'nm-core-enum-types'
-
-libnm_core_enum = gnome.mkenums(
-  enum_types,
-  sources: enum_headers,
-  identifier_prefix: nm_id_prefix,
-  c_template: enum_types + '.c.template',
-  h_template: enum_types + '.h.template',
-  install_header: true,
-  install_dir: libnm_pkgincludedir
-)
-
-deps = [
-  crypto_dep,
-  dl_dep,
-  libudev_dep,
-  shared_dep,
-  uuid_dep
-]
-
-cflags = [
-  '-DG_LOG_DOMAIN="@0@"'.format(libnm_name),
-  '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIBNM_CORE',
-  '-DLIBEXECDIR="@0@"'.format(nm_libexecdir),
-  '-DLOCALEDIR="@0@"'.format(nm_localedir),
-  '-DNMCONFDIR="@0@"'.format(nm_pkgconfdir),
-  '-DNMLIBDIR="@0@"'.format(nm_pkglibdir),
-  '-DNMPLUGINDIR="@0@"'.format(nm_pkglibdir)
-]
-
-if enable_json_validation
-  libnm_core_sources += files('nm-json.c')
-  deps += jansson_dep
-endif
-
-libnm_core = static_library(
-  'nm-core',
-  sources: libnm_core_sources + libnm_core_enum + shared_sources + [version_header],
-  dependencies: deps,
-  c_args: cflags
-)
-
-nm_core_dep = declare_dependency(
-  sources: libnm_core_enum[1],
-  include_directories: libnm_core_inc,
-  dependencies: shared_dep
-)
-
-enums_to_docbook = join_paths(meson.source_root(), 'tools', 'enums-to-docbook.pl')
-
-docbooks = [
-  ['nm-dbus-types', 'nm-dbus-interface.h', 'NetworkManager D-Bus API Types'],
-  ['nm-vpn-dbus-types', 'nm-vpn-dbus-interface.h', 'VPN Plugin D-Bus API Types']
-]
-
-foreach docbook: docbooks
-  output = docbook[0] + '.xml'
-
-  xml = custom_target(
-    output,
-    input: docbook[1],
-    output: output,
-    capture: true,
-    command: [perl, enums_to_docbook, docbook[0], docbook[2], '@INPUT@'],
-    # FIXME: gtkdoc does not depend directly on this.
-    #        https://github.com/mesonbuild/meson/pull/2806
-    build_by_default: true
-  )
-
-  content_files += xml.full_path()
-endforeach
-
-if enable_tests
-  subdir('tests')
-endif
diff --git a/libnm-core/nm-connection-private.h b/libnm-core/nm-connection-private.h
index 3ff7c57d..ee2d7264 100644
--- a/libnm-core/nm-connection-private.h
+++ b/libnm-core/nm-connection-private.h
@@ -21,10 +21,6 @@
 #ifndef __NM_CONNECTION_PRIVATE_H__
 #define __NM_CONNECTION_PRIVATE_H__
 
-#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-#error Cannot use this header.
-#endif
-
 #include "nm-setting.h"
 #include "nm-connection.h"
 
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
index 868fb109..f70be09f 100644
--- a/libnm-core/nm-connection.c
+++ b/libnm-core/nm-connection.c
@@ -587,7 +587,7 @@ nm_connection_diff (NMConnection *a,
 	if (a == b)
 		return TRUE;
 
-	diffs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
+	diffs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
 
 	/* Diff A to B, then B to A to capture keys in B that aren't in A */
 	if (diff_one_connection (a, b, flags, FALSE, diffs))
@@ -959,7 +959,7 @@ _normalize_infiniband_mtu (NMConnection *self, GHashTable *parameters)
 				max_mtu = 65520;
 
 			if (max_mtu && nm_setting_infiniband_get_mtu (s_infini) > max_mtu) {
-				g_object_set (s_infini, NM_SETTING_INFINIBAND_MTU, (guint) max_mtu, NULL);
+				g_object_set (s_infini, NM_SETTING_INFINIBAND_MTU, max_mtu, NULL);
 				return TRUE;
 			}
 		}
@@ -1370,7 +1370,7 @@ nm_connection_verify_secrets (NMConnection *connection, GError **error)
  * @parameters: (allow-none) (element-type utf8 gpointer): a #GHashTable with
  * normalization parameters to allow customization of the normalization by providing
  * specific arguments. Unknown arguments will be ignored and the default will be
- * used. The keys must be strings compared with g_str_equal() function.
+ * used. The keys must be strings, hashed by g_str_hash() and g_str_equal() functions.
  * The values are opaque and depend on the parameter name.
  * @modified: (out) (allow-none): outputs whether any settings were modified.
  * @error: location to store error, or %NULL. Contains the reason,
@@ -2447,7 +2447,7 @@ nm_connection_get_setting_ovs_patch (NMConnection *connection)
 {
 	return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_PATCH);
 }
-
+ 
 /**
  * nm_connection_get_setting_ovs_port:
  * @connection: the #NMConnection
@@ -2530,7 +2530,7 @@ nm_connection_get_setting_serial (NMConnection *connection)
  *
  * Returns: (transfer none): an #NMSettingTCConfig if the connection contains one, otherwise %NULL
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMSettingTCConfig *
 nm_connection_get_setting_tc_config (NMConnection *connection)
@@ -2724,7 +2724,7 @@ nm_connection_get_private (NMConnection *connection)
 		                         priv, (GDestroyNotify) nm_connection_private_free);
 
 		priv->self = connection;
-		priv->settings = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref);
+		priv->settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
 	}
 
 	return priv;
diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h
index 6d23cfdb..95e11f1f 100644
--- a/libnm-core/nm-connection.h
+++ b/libnm-core/nm-connection.h
@@ -229,7 +229,7 @@ NMSettingPppoe *           nm_connection_get_setting_pppoe             (NMConnec
 NM_AVAILABLE_IN_1_6
 NMSettingProxy *           nm_connection_get_setting_proxy             (NMConnection *connection);
 NMSettingSerial *          nm_connection_get_setting_serial            (NMConnection *connection);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMSettingTCConfig *        nm_connection_get_setting_tc_config         (NMConnection *connection);
 NMSettingTun *             nm_connection_get_setting_tun               (NMConnection *connection);
 NMSettingVpn *             nm_connection_get_setting_vpn               (NMConnection *connection);
diff --git a/libnm-core/nm-core-enum-types.c.template b/libnm-core/nm-core-enum-types.c.template
deleted file mode 100644
index 9c08f526..00000000
--- a/libnm-core/nm-core-enum-types.c.template
+++ /dev/null
@@ -1,86 +0,0 @@
-/*** BEGIN file-header ***/
-#include "config.h"
-
-#include "nm-core-enum-types.h"
-#include "nm-default.h"
-
-#include "nm-version-macros.h"
-#include "nm-connection.h"
-#include "nm-core-types.h"
-#include "nm-dbus-interface.h"
-#include "nm-errors.h"
-#include "nm-setting-8021x.h"
-#include "nm-setting-adsl.h"
-#include "nm-setting-bluetooth.h"
-#include "nm-setting-bond.h"
-#include "nm-setting-bridge-port.h"
-#include "nm-setting-bridge.h"
-#include "nm-setting-cdma.h"
-#include "nm-setting-connection.h"
-#include "nm-setting-dcb.h"
-#include "nm-setting-dummy.h"
-#include "nm-setting-generic.h"
-#include "nm-setting-gsm.h"
-#include "nm-setting-infiniband.h"
-#include "nm-setting-ip-config.h"
-#include "nm-setting-ip-tunnel.h"
-#include "nm-setting-ip4-config.h"
-#include "nm-setting-ip6-config.h"
-#include "nm-setting-macsec.h"
-#include "nm-setting-macvlan.h"
-#include "nm-setting-olpc-mesh.h"
-#include "nm-setting-ovs-bridge.h"
-#include "nm-setting-ovs-interface.h"
-#include "nm-setting-ovs-patch.h"
-#include "nm-setting-ovs-port.h"
-#include "nm-setting-ppp.h"
-#include "nm-setting-pppoe.h"
-#include "nm-setting-proxy.h"
-#include "nm-setting-serial.h"
-#include "nm-setting-team-port.h"
-#include "nm-setting-team.h"
-#include "nm-setting-tun.h"
-#include "nm-setting-user.h"
-#include "nm-setting-vlan.h"
-#include "nm-setting-vpn.h"
-#include "nm-setting-vxlan.h"
-#include "nm-setting-wimax.h"
-#include "nm-setting-wired.h"
-#include "nm-setting-wireless-security.h"
-#include "nm-setting-wireless.h"
-#include "nm-setting.h"
-#include "nm-simple-connection.h"
-#include "nm-utils.h"
-#include "nm-version.h"
-#include "nm-vpn-dbus-interface.h"
-#include "nm-vpn-editor-plugin.h"
-#include "nm-vpn-plugin-info.h"
-/*** END file-header ***/
-
-/*** BEGIN value-header ***/
-GType
-@enum_name@_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      static const G@Type@Value values[] = {
-/*** END value-header ***/
-
-/*** BEGIN value-production ***/
-        { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
-/*** END value-production ***/
-
-/*** BEGIN value-tail ***/
-        { 0, NULL, NULL }
-      };
-      GType g_define_type_id =
-        g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
-
-/*** END value-tail ***/
diff --git a/libnm-core/nm-core-enum-types.h.template b/libnm-core/nm-core-enum-types.h.template
deleted file mode 100644
index 0f1be1e1..00000000
--- a/libnm-core/nm-core-enum-types.h.template
+++ /dev/null
@@ -1,21 +0,0 @@
-/*** BEGIN file-header ***/
-#ifndef __NM_CORE_ENUM_TYPES_H__
-#define __NM_CORE_ENUM_TYPES_H__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-/*** END file-header ***/
-
-/*** BEGIN enumeration-production ***/
-GType @enum_name@_get_type (void) G_GNUC_CONST;
-#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
-
-/*** END enumeration-production ***/
-
-/*** BEGIN file-tail ***/
-G_END_DECLS
-
-#endif /* __NM_CORE_ENUM_TYPES_H__ */
-/*** END file-tail ***/
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index 7260ebfd..15e5dc2a 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -31,9 +31,6 @@
  * statically against libnm-core. This basically means libnm-core, libnm, NetworkManager
  * and some test programs.
  **/
-#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL)
-#error Cannot use this header.
-#endif
 
 
 #include "nm-connection.h"
@@ -219,8 +216,6 @@ GHashTable *_nm_utils_copy_strdict (GHashTable *strdict);
 
 typedef gpointer (*NMUtilsCopyFunc) (gpointer);
 
-const char **_nm_ip_address_get_attribute_names (const NMIPAddress *addr, gboolean sorted, guint *out_length);
-
 gboolean _nm_ip_route_attribute_validate_all (const NMIPRoute *route);
 const char **_nm_ip_route_get_attribute_names (const NMIPRoute *route, gboolean sorted, guint *out_length);
 GHashTable *_nm_ip_route_get_attributes_direct (NMIPRoute *route);
@@ -496,17 +491,4 @@ gboolean _nm_utils_string_append_tc_tfilter_rest (GString *string,
 
 /*****************************************************************************/
 
-static inline gboolean
-_nm_connection_type_is_master (const char *type)
-{
-	return (NM_IN_STRSET (type,
-	                      NM_SETTING_BOND_SETTING_NAME,
-	                      NM_SETTING_BRIDGE_SETTING_NAME,
-	                      NM_SETTING_TEAM_SETTING_NAME,
-	                      NM_SETTING_OVS_BRIDGE_SETTING_NAME,
-	                      NM_SETTING_OVS_PORT_SETTING_NAME));
-}
-
-/*****************************************************************************/
-
 #endif
diff --git a/libnm-core/nm-core-types-internal.h b/libnm-core/nm-core-types-internal.h
index 4d43aaf4..442a10a3 100644
--- a/libnm-core/nm-core-types-internal.h
+++ b/libnm-core/nm-core-types-internal.h
@@ -22,22 +22,9 @@
 #ifndef NM_CORE_TYPES_INTERNAL_H
 #define NM_CORE_TYPES_INTERNAL_H
 
-#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL)
-#error Cannot use this header.
-#endif
-
 typedef struct {
 	guint32 from;
 	guint32 to;
 } NMVlanQosMapping;
 
-#define _NM_IP_TUNNEL_FLAG_ALL_IP6TNL \
-	( NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT \
-	| NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS \
-	| NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL \
-	| NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV \
-	| NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY \
-	| NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK \
-	)
-
 #endif /* NM_CORE_TYPES_INTERNAL_H */
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index 6c1e97fe..33e4850c 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -76,7 +76,6 @@
 #define NM_DBUS_INTERFACE_DEVICE_GRE           NM_DBUS_INTERFACE_DEVICE ".Gre"
 #define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL     NM_DBUS_INTERFACE_DEVICE ".IPTunnel"
 #define NM_DBUS_INTERFACE_DEVICE_STATISTICS    NM_DBUS_INTERFACE_DEVICE ".Statistics"
-#define NM_DBUS_INTERFACE_CHECKPOINT           NM_DBUS_INTERFACE ".Checkpoint"
 
 #define NM_DBUS_INTERFACE_SETTINGS        "org.freedesktop.NetworkManager.Settings"
 #define NM_DBUS_PATH_SETTINGS             "/org/freedesktop/NetworkManager/Settings"
@@ -842,29 +841,16 @@ typedef enum {
  *   delete any new connection added after the checkpoint (Since: 1.6)
  * @NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES: upon rollback,
  *   disconnect any new device appeared after the checkpoint (Since: 1.6)
- * @NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING: by default, creating
- *   a checkpoint fails if there are already existing checkoints that
- *   reference the same devices. With this flag, creation of such
- *   checkpoints is allowed, however, if an older checkpoint
- *   that references overlapping devices gets rolled back, it will
- *   automatically destroy this checkpoint during rollback. This
- *   allows to create several overlapping checkpoints in parallel,
- *   and rollback to them at will. With the special case that
- *   rolling back to an older checkpoint will invalidate all
- *   overlapping younger checkpoints. This opts-in that the
- *   checkpoint can be automatically destroyed by the rollback
- *   of an older checkpoint. (Since: 1.12)
  *
  * The flags for CheckpointCreate call
  *
- * Since: 1.4 (gi flags generated since 1.12)
+ * Since: 1.4
  */
-typedef enum { /*< flags >*/
+typedef enum { /*< skip >*/
 	NM_CHECKPOINT_CREATE_FLAG_NONE                        = 0,
 	NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL                 = 0x01,
 	NM_CHECKPOINT_CREATE_FLAG_DELETE_NEW_CONNECTIONS      = 0x02,
 	NM_CHECKPOINT_CREATE_FLAG_DISCONNECT_NEW_DEVICES      = 0x04,
-	NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING           = 0x08,
 } NMCheckpointCreateFlags;
 
 /**
@@ -886,34 +872,6 @@ typedef enum { /*< skip >*/
 } NMRollbackResult;
 
 /**
- * NMSettingsConnectionFlags:
- * @NM_SETTINGS_CONNECTION_FLAG_NONE: an alias for numeric zero, no flags set.
- * @NM_SETTINGS_CONNECTION_FLAG_UNSAVED: the connection is not saved to disk.
- *   That either means, that the connection is in-memory only and currently
- *   is not backed by a file. Or, that the connection is backed by a file,
- *   but has modifications in-memory that were not persisted to disk.
- * @NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED: A connection is "nm-generated" if
- *  it was generated by NetworkManger. If the connection gets modified or saved
- *  by the user, the flag gets cleared. A nm-generated is also unsaved
- *  and has no backing file as it is in-memory only.
- * @NM_SETTINGS_CONNECTION_FLAG_VOLATILE: The connection will be deleted
- *  when it disconnects. That is for in-memory connections (unsaved), which are
- *  currently active but deleted on disconnect. Volatile connections are
- *  always unsaved, but they are also no backing file on disk and are entirely
- *  in-memory only.
- *
- * Flags describing the current activation state.
- *
- * Since: 1.12
- **/
-typedef enum { /*< flags >*/
-	NM_SETTINGS_CONNECTION_FLAG_NONE                       = 0,
-	NM_SETTINGS_CONNECTION_FLAG_UNSAVED                    = 0x01,
-	NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED               = 0x02,
-	NM_SETTINGS_CONNECTION_FLAG_VOLATILE                   = 0x04,
-} NMSettingsConnectionFlags;
-
-/**
  * NMActivationStateFlags:
  * @NM_ACTIVATION_STATE_FLAG_NONE: an alias for numeric zero, no flags set.
  * @NM_ACTIVATION_STATE_FLAG_IS_MASTER: the device is a master.
@@ -971,7 +929,7 @@ typedef enum { /*< flags >*/
  *   right away. Setting this flag, disables autoconnect until the connection
  *   is manually activated.
  *
- * Since: 1.12
+ * Since: 1.10.2
  */
 typedef enum { /*< flags >*/
 	NM_SETTINGS_UPDATE2_FLAG_NONE                       = 0,
diff --git a/libnm-core/nm-dbus-types.xml b/libnm-core/nm-dbus-types.xml
index cdb2021e..b8c5d84f 100644
--- a/libnm-core/nm-dbus-types.xml
+++ b/libnm-core/nm-dbus-types.xml
@@ -1483,7 +1483,7 @@
     <indexterm zone="NMCheckpointCreateFlags">
       <primary>NMCheckpointCreateFlags</primary>
     </indexterm>
-    <para><para>The flags for CheckpointCreate call</para><para>Since: 1.4 (gi flags generated since 1.12)</para><para></para></para>
+    <para><para>The flags for CheckpointCreate call</para><para>Since: 1.4</para><para></para></para>
     <refsect3 role="enum_members">
       <title>Values</title>
       <informaltable role="enum_members_table" pgwide="1" frame="none">
@@ -1512,11 +1512,6 @@
               <entry role="enum_member_value"><para>= <literal>0x04</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>upon rollback, disconnect any new device appeared after the checkpoint (Since: 1.6)</para><para></para></entry>
             </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_CHECKPOINT_CREATE_FLAG_ALLOW_OVERLAPPING</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x08</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>by default, creating a checkpoint fails if there are already existing checkoints that reference the same devices. With this flag, creation of such checkpoints is allowed, however, if an older checkpoint that references overlapping devices gets rolled back, it will automatically destroy this checkpoint during rollback. This allows to create several overlapping checkpoints in parallel, and rollback to them at will. With the special case that rolling back to an older checkpoint will invalidate all overlapping younger checkpoints. This opts-in that the checkpoint can be automatically destroyed by the rollback of an older checkpoint. (Since: 1.12)</para><para></para></entry>
-            </row>
           </tbody>
         </tgroup>
       </informaltable>
@@ -1563,46 +1558,6 @@
     </refsect3>
   </refsect2>
 
-  <refsect2 id="NMSettingsConnectionFlags" role="enum">
-    <title>enum NMSettingsConnectionFlags</title>
-    <indexterm zone="NMSettingsConnectionFlags">
-      <primary>NMSettingsConnectionFlags</primary>
-    </indexterm>
-    <para><para>Flags describing the current activation state.</para><para>Since: 1.12</para><para></para></para>
-    <refsect3 role="enum_members">
-      <title>Values</title>
-      <informaltable role="enum_members_table" pgwide="1" frame="none">
-        <tgroup cols="4">
-          <colspec colname="enum_members_name" colwidth="300px" />
-          <colspec colname="enum_members_value" colwidth="100px"/>
-          <colspec colname="enum_members_description" />
-          <tbody>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_CONNECTION_FLAG_NONE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an alias for numeric zero, no flags set.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_CONNECTION_FLAG_UNSAVED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x01</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the connection is not saved to disk. That either means, that the connection is in-memory only and currently is not backed by a file. Or, that the connection is backed by a file, but has modifications in-memory that were not persisted to disk.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_CONNECTION_FLAG_NM_GENERATED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x02</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>A connection is "nm-generated" if it was generated by NetworkManger. If the connection gets modified or saved by the user, the flag gets cleared. A nm-generated is also unsaved and has no backing file as it is in-memory only.</para><para></para></entry>
-            </row>
-            <row role="constant">
-              <entry role="enum_member_name"><para>NM_SETTINGS_CONNECTION_FLAG_VOLATILE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>0x04</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The connection will be deleted when it disconnects. That is for in-memory connections (unsaved), which are currently active but deleted on disconnect. Volatile connections are always unsaved, but they are also no backing file on disk and are entirely in-memory only.</para><para></para></entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </informaltable>
-    </refsect3>
-  </refsect2>
-
   <refsect2 id="NMActivationStateFlags" role="enum">
     <title>enum NMActivationStateFlags</title>
     <indexterm zone="NMActivationStateFlags">
@@ -1663,7 +1618,7 @@
     <indexterm zone="NMSettingsUpdate2Flags">
       <primary>NMSettingsUpdate2Flags</primary>
     </indexterm>
-    <para><para>Since: 1.12</para><para></para></para>
+    <para><para>Since: 1.10.2</para><para></para></para>
     <refsect3 role="enum_members">
       <title>Values</title>
       <informaltable role="enum_members_table" pgwide="1" frame="none">
diff --git a/libnm-core/nm-dbus-utils.c b/libnm-core/nm-dbus-utils.c
index ea7bf783..79020b2d 100644
--- a/libnm-core/nm-dbus-utils.c
+++ b/libnm-core/nm-dbus-utils.c
@@ -176,13 +176,11 @@ _nm_dbus_signal_connect_data (GDBusProxy *proxy,
 
 
 static void
-typecheck_response (GVariant **response,
-                    const GVariantType *reply_type,
-                    GError **error)
+typecheck_response (GVariant           **response,
+                    const GVariantType  *reply_type,
+                    GError             **error)
 {
-	if (   *response
-	    && reply_type
-	    && !g_variant_is_of_type (*response, reply_type)) {
+	if (*response && reply_type && !g_variant_is_of_type (*response, reply_type)) {
 		/* This is the same error code that g_dbus_connection_call() returns if
 		 * @reply_type doesn't match.
 		 */
diff --git a/libnm-core/nm-json.c b/libnm-core/nm-json.c
index aa181a4a..f9042b1f 100644
--- a/libnm-core/nm-json.c
+++ b/libnm-core/nm-json.c
@@ -44,6 +44,7 @@ void *_nm_jansson_json_true;
 void *_nm_jansson_json_object_size;
 void *_nm_jansson_json_object_set_new;
 void *_nm_jansson_json_object_iter;
+void *_nm_jansson_json_object_iter_at;
 void *_nm_jansson_json_integer_value;
 void *_nm_jansson_json_string_value;
 
@@ -79,6 +80,7 @@ bind_symbols (void *handle)
 	TRY_BIND_SYMBOL (json_object_size);
 	TRY_BIND_SYMBOL (json_object_set_new);
 	TRY_BIND_SYMBOL (json_object_iter);
+	TRY_BIND_SYMBOL (json_object_iter_at);
 	TRY_BIND_SYMBOL (json_integer_value);
 	TRY_BIND_SYMBOL (json_string_value);
 
@@ -94,7 +96,6 @@ nm_jansson_load (void)
 		MISSING,
 	} state = UNKNOWN;
 	void *handle;
-	int mode;
 
 	if (G_LIKELY (state != UNKNOWN))
 		goto out;
@@ -103,13 +104,7 @@ nm_jansson_load (void)
 	if (!bind_symbols (RTLD_DEFAULT))
 		goto out;
 
-	mode = RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE | RTLD_DEEPBIND;
-#if defined (ASAN_BUILD)
-	/* Address sanitizer is incompatible with RTLD_DEEPBIND. */
-	mode &= ~RTLD_DEEPBIND;
-#endif
-	handle = dlopen (JANSSON_SONAME, mode);
-
+	handle = dlopen (JANSSON_SONAME, RTLD_LAZY | RTLD_LOCAL | RTLD_NODELETE | RTLD_DEEPBIND);
 	if (!handle)
 		goto out;
 
diff --git a/libnm-core/nm-json.h b/libnm-core/nm-json.h
index 560b827a..513b8d36 100644
--- a/libnm-core/nm-json.h
+++ b/libnm-core/nm-json.h
@@ -42,10 +42,9 @@ gboolean nm_jansson_load (void);
 #define json_object_size        (*_nm_jansson_json_object_size)
 #define json_object_set_new     (*_nm_jansson_json_object_set_new)
 #define json_object_iter        (*_nm_jansson_json_object_iter)
+#define json_object_iter_at     (*_nm_jansson_json_object_iter_at)
 #define json_integer_value      (*_nm_jansson_json_integer_value)
 #define json_string_value       (*_nm_jansson_json_string_value)
-
-#include "nm-utils/nm-jansson.h"
 #endif
 
 #endif /* __NM_JSON_H__ */
diff --git a/libnm-core/nm-keyfile-internal.h b/libnm-core/nm-keyfile-internal.h
index 157b9fe8..5b709c02 100644
--- a/libnm-core/nm-keyfile-internal.h
+++ b/libnm-core/nm-keyfile-internal.h
@@ -22,10 +22,6 @@
 #ifndef __NM_KEYFILE_INTERNAL_H__
 #define __NM_KEYFILE_INTERNAL_H__
 
-#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_INTERNAL)
-#error Cannot use this header.
-#endif
-
 #include <sys/types.h>
 
 #include "nm-connection.h"
diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile-reader.c
index 40bf89da..20b5a3fc 100644
--- a/libnm-core/nm-keyfile.c
+++ b/libnm-core/nm-keyfile-reader.c
@@ -25,7 +25,6 @@
 
 #include <errno.h>
 #include <stdlib.h>
-#include <stdio.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <sys/types.h>
@@ -39,8 +38,6 @@
 
 #include "nm-setting-user.h"
 
-/*****************************************************************************/
-
 typedef struct {
 	NMConnection *connection;
 	GKeyFile *keyfile;
@@ -52,15 +49,6 @@ typedef struct {
 	NMSetting *setting;
 } KeyfileReaderInfo;
 
-typedef struct {
-	NMConnection *connection;
-	GKeyFile *keyfile;
-	GError *error;
-	NMKeyfileWriteHandler handler;
-	void *user_data;
-} KeyfileWriterInfo;
-
-/*****************************************************************************/
 
 static void
 _handle_warn (KeyfileReaderInfo *info,
@@ -444,8 +432,8 @@ read_one_ip_address_or_route (KeyfileReaderInfo *info,
 		                        address_str, plen, property_name);
 		if (!result)
 			return NULL;
-		if (gateway_str)
-			NM_SET_OUT (out_gateway, g_strdup (gateway_str));
+		if (out_gateway && gateway_str)
+			*out_gateway = g_strdup (gateway_str);
 	}
 
 #undef VALUE_ORIG
@@ -478,240 +466,146 @@ fill_route_attributes (GKeyFile *kf, NMIPRoute *route, const char *setting, cons
 	}
 }
 
-typedef struct {
-	const char *s_key;
-	gint32 key_idx;
-	gint8 key_type;
-} IPAddrRouteBuildListData;
-
-static int
-_ip_addrroute_build_lst_data_cmp (gconstpointer p_a, gconstpointer p_b, gpointer user_data)
+static void
+ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
-	const IPAddrRouteBuildListData *a = p_a;
-	const IPAddrRouteBuildListData *b = p_b;
-
-	NM_CMP_FIELD (a, b, key_idx);
-	NM_CMP_FIELD (a, b, key_type);
-	NM_CMP_FIELD_STR (a, b, s_key);
-	return 0;
-}
+	const char *setting_name = nm_setting_get_name (setting);
+	gboolean ipv6 = !strcmp (setting_name, "ipv6");
+	gboolean routes = !strcmp (key, "routes");
+	static const char *key_names_routes[] = { "route", "routes", NULL };
+	static const char *key_names_addresses[] = { "address", "addresses", NULL };
+	const char **key_names = routes ? key_names_routes : key_names_addresses;
+	char *gateway = NULL;
+	GPtrArray *list;
+	GDestroyNotify free_func;
+	int i;
 
-static gboolean
-ip_addrroute_match_key_w_name_ (const char *key,
-                                const char *base_name,
-                                gsize base_name_l,
-                                gint32 *out_key_idx)
-{
-	gint64 v;
+	if (routes)
+		free_func = (GDestroyNotify) nm_ip_route_unref;
+	else
+		free_func = (GDestroyNotify) nm_ip_address_unref;
+	list = g_ptr_array_new_with_free_func (free_func);
 
-	/* some very strict parsing. */
+	for (i = -1; i < 1000; i++) {
+		const char **key_basename;
 
-	/* the key must start with base_name. */
-	if (strncmp (key, base_name, base_name_l) != 0)
-		return FALSE;
+		for (key_basename = key_names; *key_basename; key_basename++) {
+			char *key_name;
+			gpointer item;
+			char options_key[128];
 
-	key += base_name_l;
-	if (key[0] == '\0') {
-		/* if key is identical to base_name, that's good. */
-		NM_SET_OUT (out_key_idx, -1);
-		return TRUE;
-	}
+			/* -1 means no suffix */
+			if (i >= 0)
+				key_name = g_strdup_printf ("%s%d", *key_basename, i);
+			else
+				key_name = g_strdup (*key_basename);
+
+			item = read_one_ip_address_or_route (info, key, setting_name, key_name, ipv6, routes,
+			                                     gateway ? NULL : &gateway, setting);
+			if (item && routes) {
+				nm_sprintf_buf (options_key, "%s_options", key_name);
+				fill_route_attributes (info->keyfile, item, setting_name, options_key, ipv6 ? AF_INET6 : AF_INET);
+			}
 
-	/* if base_name is followed by a zero, then it must be
-	 * only a zero, nothing else. */
-	if (key[0] == '0') {
-		if (key[1] != '\0')
-			return FALSE;
-		NM_SET_OUT (out_key_idx, 0);
-		return TRUE;
-	}
+			g_free (key_name);
 
-	/* otherwise, it can only be followed by a non-zero decimal. */
-	if (!(key[0] >= '1' && key[0] <= '9'))
-		return FALSE;
-	/* and all remaining chars must be decimals too. */
-	if (!NM_STRCHAR_ALL (&key[1], ch, g_ascii_isdigit (ch)))
-		return FALSE;
+			if (info->error) {
+				g_ptr_array_unref (list);
+				g_free (gateway);
+				return;
+			}
+			if (item)
+				g_ptr_array_add (list, item);
 
-	/* and it must be convertible to a (positive) int. */
-	v = _nm_utils_ascii_str_to_int64 (key, 10, 0, G_MAXINT32, -1);
-	if (v < 0)
-		return FALSE;
+		}
+	}
 
-	/* good */
-	NM_SET_OUT (out_key_idx, v);
-	return TRUE;
-}
+	if (list->len >= 1)
+		g_object_set (setting, key, list, NULL);
 
-static gboolean
-ip_addrroute_match_key (const char *key,
-                        gboolean is_routes,
-                        gint32 *out_key_idx,
-                        gint8 *out_key_type)
-{
-#define ip_addrroute_match_key_w_name(key, base_name, out_key_idx) \
-	ip_addrroute_match_key_w_name_ (key, base_name, NM_STRLEN (base_name), out_key_idx)
-
-	if (is_routes) {
-		if (ip_addrroute_match_key_w_name (key, "route", out_key_idx))
-			NM_SET_OUT (out_key_type, 0);
-		else if (ip_addrroute_match_key_w_name (key, "routes", out_key_idx))
-			NM_SET_OUT (out_key_type, 1);
-		else
-			return FALSE;
-	} else {
-		if (ip_addrroute_match_key_w_name (key, "address", out_key_idx))
-			NM_SET_OUT (out_key_type, 0);
-		else if (ip_addrroute_match_key_w_name (key, "addresses", out_key_idx))
-			NM_SET_OUT (out_key_type, 1);
-		else
-			return FALSE;
+	if (gateway) {
+		g_object_set (setting, "gateway", gateway, NULL);
+		g_free (gateway);
 	}
-	return TRUE;
+
+	g_ptr_array_unref (list);
 }
 
 static void
-ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *setting_key)
+ip4_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
 	const char *setting_name = nm_setting_get_name (setting);
-	gboolean is_ipv6 = nm_streq (setting_name, "ipv6");
-	gboolean is_routes = nm_streq (setting_key, "routes");
-	gs_free char *gateway = NULL;
-	gs_unref_ptrarray GPtrArray *list = NULL;
-	gs_strfreev char **keys = NULL;
-	gsize i_keys, keys_len;
-	gs_free IPAddrRouteBuildListData *build_list = NULL;
-	gsize i_build_list, build_list_len = 0;
-
-
-	keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &keys_len, NULL);
+	GPtrArray *array;
+	gsize length;
+	char **list, **iter;
+	int ret;
 
-	if (keys_len == 0)
+	list = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL);
+	if (!list || !g_strv_length (list))
 		return;
 
-	/* first create a list of all relevant keys, and sort them. */
-	for (i_keys = 0; i_keys < keys_len; i_keys++) {
-		const char *s_key = keys[i_keys];
-		gint32 key_idx;
-		gint8 key_type;
-
-		if (!ip_addrroute_match_key (s_key, is_routes, &key_idx, &key_type))
-			continue;
-
-		if (G_UNLIKELY (!build_list))
-			build_list = g_new (IPAddrRouteBuildListData, keys_len - i_keys);
+	array = g_ptr_array_sized_new (length + 1);
+	for (iter = list; *iter; iter++) {
+		guint32 addr;
 
-		build_list[build_list_len].s_key = s_key;
-		build_list[build_list_len].key_idx = key_idx;
-		build_list[build_list_len].key_type = key_type;
-		build_list_len++;
-	}
-
-	if (build_list_len == 0)
-		return;
-
-	g_qsort_with_data (build_list,
-	                   build_list_len,
-	                   sizeof (IPAddrRouteBuildListData),
-	                   _ip_addrroute_build_lst_data_cmp,
-	                   NULL);
-
-	list = g_ptr_array_new_with_free_func (is_routes
-	                                       ? (GDestroyNotify) nm_ip_route_unref
-	                                       : (GDestroyNotify) nm_ip_address_unref);
-
-	for (i_build_list = 0; i_build_list < build_list_len; i_build_list++) {
-		const IPAddrRouteBuildListData *build_data = &build_list[i_build_list];
-		gpointer item;
-
-		if (   i_build_list + 1 < build_list_len
-		    && build_data->key_idx == build_data[1].key_idx
-		    && build_data->key_type == build_data[1].key_type
-		    && nm_streq (build_data->s_key, build_data[1].s_key)) {
-			/* the keyfile contains duplicate keys, which are both returned
-			 * by g_key_file_get_keys() (WHY??).
-			 *
-			 * Skip the earlier one. */
+		ret = inet_pton (AF_INET, *iter, &addr);
+		if (ret <= 0) {
+			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			                  _("ignoring invalid DNS server IPv4 address '%s'"),
+			                  *iter)) {
+				g_ptr_array_unref (array);
+				g_strfreev (list);
+				return;
+			}
 			continue;
 		}
 
-		item = read_one_ip_address_or_route (info,
-		                                     setting_key,
-		                                     setting_name,
-		                                     build_data->s_key,
-		                                     is_ipv6,
-		                                     is_routes,
-		                                     gateway ? NULL : &gateway,
-		                                     setting);
-		if (item && is_routes) {
-			char options_key[128];
-
-			nm_sprintf_buf (options_key, "%s_options", build_data->s_key);
-			fill_route_attributes (info->keyfile,
-			                       item,
-			                       setting_name,
-			                       options_key,
-			                       is_ipv6 ? AF_INET6 : AF_INET);
-		}
-
-		if (info->error)
-			return;
-
-		if (item)
-			g_ptr_array_add (list, item);
+		g_ptr_array_add (array, *iter);
 	}
+	g_ptr_array_add (array, NULL);
 
-	if (list->len >= 1)
-		g_object_set (setting, setting_key, list, NULL);
-
-	if (gateway)
-		g_object_set (setting, "gateway", gateway, NULL);
+	g_object_set (setting, key, array->pdata, NULL);
+	g_ptr_array_unref (array);
+	g_strfreev (list);
 }
 
 static void
-ip_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+ip6_dns_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
-	int addr_family;
-	gs_strfreev char **list = NULL;
-	gsize i, n, length;
-
-	nm_assert (NM_IS_SETTING_IP4_CONFIG (setting) || NM_IS_SETTING_IP6_CONFIG (setting));
-
-	list = nm_keyfile_plugin_kf_get_string_list (info->keyfile,
-	                                             nm_setting_get_name (setting),
-	                                             key,
-	                                             &length,
-	                                             NULL);
-	nm_assert (length == NM_PTRARRAY_LEN (list));
-	if (length == 0)
+	const char *setting_name = nm_setting_get_name (setting);
+	GPtrArray *array = NULL;
+	gsize length;
+	char **list, **iter;
+	int ret;
+
+	list = nm_keyfile_plugin_kf_get_string_list (info->keyfile, setting_name, key, &length, NULL);
+	if (!list || !g_strv_length (list))
 		return;
 
-	addr_family = NM_IS_SETTING_IP4_CONFIG (setting) ? AF_INET : AF_INET6;
+	array = g_ptr_array_sized_new (length + 1);
 
-	n = 0;
-	for (i = 0; i < length; i++) {
-		NMIPAddr addr;
+	for (iter = list; *iter; iter++) {
+		struct in6_addr addr;
 
-		if (inet_pton (addr_family, list[i], &addr) <= 0) {
+		ret = inet_pton (AF_INET6, *iter, &addr);
+		if (ret <= 0) {
 			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-			                  _("ignoring invalid DNS server IPv%c address '%s'"),
-			                  nm_utils_addr_family_to_char (addr_family),
-			                  list[i])) {
-				do {
-					nm_clear_g_free (&list[i]);
-				} while (++i < length);
+			                  _("ignoring invalid DNS server IPv6 address '%s'"),
+			                  *iter)) {
+				g_ptr_array_unref (array);
+				g_strfreev (list);
 				return;
 			}
-			nm_clear_g_free (&list[i]);
 			continue;
 		}
 
-		if (n != i)
-			list[n] = g_steal_pointer (&list[i]);
-		n++;
+		g_ptr_array_add (array, *iter);
 	}
+	g_ptr_array_add (array, NULL);
 
-	g_object_set (setting, key, list, NULL);
+	g_object_set (setting, key, array->pdata, NULL);
+	g_ptr_array_unref (array);
+	g_strfreev (list);
 }
 
 static void
@@ -870,7 +764,7 @@ read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key)
 	if (NM_IS_SETTING_USER (setting)) {
 		gs_unref_hashtable GHashTable *data = NULL;
 
-		data = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
+		data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 		for (iter = (const char *const*) keys; *iter; iter++) {
 			gs_free char *to_free = NULL;
 			char *value = NULL;
@@ -1096,6 +990,116 @@ has_cert_ext (const char *path)
 	return FALSE;
 }
 
+static gboolean
+handle_as_scheme (KeyfileReaderInfo *info, GBytes *bytes, NMSetting *setting, const char *key)
+{
+	const char *data;
+	gsize data_len, bin_len;
+
+	data = g_bytes_get_data (bytes, &data_len);
+
+	g_return_val_if_fail (data && data_len > 0, FALSE);
+
+	/* to be a scheme, @data must be a zero terminated string, which is counted by @data_len */
+	if (data[data_len - 1] != '\0')
+		return FALSE;
+	data_len--;
+
+	/* It's the PATH scheme, can just set plain data.
+	 * In this case, @data_len includes */
+	if (   data_len >= NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)
+	    && g_str_has_prefix (data, NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)) {
+		if (nm_setting_802_1x_check_cert_scheme (data, data_len + 1, NULL) == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+			const char *path = &data[NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)];
+			gs_free char *path_free = NULL;
+
+			if (path[0] != '/') {
+				/* we want to read absolute paths because we use keyfile as exchange
+				 * between different processes which might not have the same cwd. */
+				path = path_free = get_cert_path (info->base_dir, (const guint8 *) path,
+				                                  data_len - NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH));
+			}
+
+			g_object_set (setting, key, bytes, NULL);
+			if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
+				handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE,
+				             _("certificate or key file '%s' does not exist"),
+				             path);
+			}
+		} else {
+			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			             _("invalid key/cert value path \"%s\""), data);
+		}
+		return TRUE;
+	}
+	if (   data_len >= NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PKCS11)
+	    && g_str_has_prefix (data, NM_KEYFILE_CERT_SCHEME_PREFIX_PKCS11)) {
+		if (nm_setting_802_1x_check_cert_scheme (data, data_len + 1, NULL) == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
+			g_object_set (setting, key, bytes, NULL);
+		} else {
+			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			             _("invalid PKCS#11 URI \"%s\""), data);
+		}
+		return TRUE;
+	}
+	if (   data_len > NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB)
+	    && g_str_has_prefix (data, NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB)) {
+		const char *cdata = data + NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB);
+		guchar *bin;
+		GBytes *bytes2;
+		gsize i;
+		gboolean valid_base64;
+
+		data_len -= NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB);
+
+		/* Let's be strict here. We expect valid base64, no funny stuff!!
+		 * We didn't write such invalid data ourselfes and refuse to read it as blob. */
+		if ((valid_base64 = (data_len % 4 == 0))) {
+			for (i = 0; i < data_len; i++) {
+				char c = cdata[i];
+
+				if (!(   (c >= 'a' && c <= 'z')
+				      || (c >= 'A' && c <= 'Z')
+				      || (c >= '0' && c <= '9')
+				      || (c == '+' || c == '/'))) {
+					if (c != '=' || i < data_len - 2)
+						valid_base64 = FALSE;
+					else {
+						for (; i < data_len; i++) {
+							if (cdata[i] != '=')
+								valid_base64 = FALSE;
+						}
+					}
+					break;
+				}
+			}
+		}
+		if (!valid_base64) {
+			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			             _("invalid key/cert value data:;base64, is not base64"));
+			return TRUE;
+		}
+
+		bin = g_base64_decode (cdata, &bin_len);
+
+		g_return_val_if_fail (bin_len > 0, FALSE);
+		if (nm_setting_802_1x_check_cert_scheme (bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+			/* The blob probably starts with "file://". Setting the cert data will confuse NMSetting8021x.
+			 * In fact this is a limitation of NMSetting8021x which does not support setting blobs that start
+			 * with file://. Just warn and return TRUE to signal that we ~handled~ the setting. */
+			g_free (bin);
+			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			             _("invalid key/cert value data:;base64,file://"));
+		} else {
+			bytes2 = g_bytes_new_take (bin, bin_len);
+			g_object_set (setting, key, bytes2, NULL);
+			g_bytes_unref (bytes2);
+		}
+		return TRUE;
+	}
+	return FALSE;
+}
+
 char *
 nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
                                            gconstpointer pdata,
@@ -1105,8 +1109,9 @@ nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
 {
 	const char *data = pdata;
 	gboolean exists = FALSE;
+	gboolean success = FALSE;
 	gsize validate_len;
-	gs_free char *path = NULL;
+	char *path;
 	GByteArray *tmp;
 
 	g_return_val_if_fail (base_dir && base_dir[0] == '/', NULL);
@@ -1138,187 +1143,106 @@ nm_keyfile_detect_unqualified_path_scheme (const char *base_dir,
 	if (   !memchr (data, '/', data_len)
 	    && !has_cert_ext (path)) {
 		if (!consider_exists)
-			return NULL;
+			goto out;
 		exists = g_file_test (path, G_FILE_TEST_EXISTS);
 		if (!exists)
-			return NULL;
+			goto out;
 	} else if (out_exists)
 		exists = g_file_test (path, G_FILE_TEST_EXISTS);
 
-	/* Construct the proper value as required for the PATH scheme.
-	 *
-	 * When returning TRUE, we must also be sure that @data_len does not look like
-	 * the deprecated format of list of integers. With this implementation that is the
-	 * case, as long as @consider_exists is FALSE. */
+	/* Construct the proper value as required for the PATH scheme */
 	tmp = g_byte_array_sized_new (strlen (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) + strlen (path) + 1);
 	g_byte_array_append (tmp, (const guint8 *) NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, strlen (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH));
 	g_byte_array_append (tmp, (const guint8 *) path, strlen (path) + 1);
-	if (nm_setting_802_1x_check_cert_scheme (tmp->data, tmp->len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH) {
+	if (nm_setting_802_1x_check_cert_scheme (tmp->data, tmp->len, NULL) == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+		g_free (path);
+		path = (char *) g_byte_array_free (tmp, FALSE);
+		/* when returning TRUE, we must also be sure that @data_len does not look like
+		 * the deprecated format of list of integers. With this implementation that is the
+		 * case, as long as @consider_exists is FALSE. */
+		success = TRUE;
+	} else
 		g_byte_array_unref (tmp);
+
+out:
+	if (!success) {
+		g_free (path);
 		return NULL;
 	}
-	g_free (path);
-	path = (char *) g_byte_array_free (tmp, FALSE);
-
-	NM_SET_OUT (out_exists, exists);
-	return g_steal_pointer (&path);
+	if (out_exists)
+		*out_exists = exists;
+	return path;
 }
 
-#define HAS_SCHEME_PREFIX(bin, bin_len, scheme) \
-	({ \
-		const char *const _bin = (bin); \
-		const gsize _bin_len = (bin_len); \
-		\
-		nm_assert (_bin && _bin_len > 0); \
-		\
-		(   _bin_len > NM_STRLEN (scheme) + 1 \
-		 && _bin[_bin_len - 1] == '\0' \
-		 && memcmp (_bin, scheme, NM_STRLEN (scheme)) == 0); \
-	})
-
-static void
-cert_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+static gboolean
+handle_as_path (KeyfileReaderInfo *info,
+                GBytes *bytes,
+                NMSetting *setting,
+                const char *key)
 {
-	const char *setting_name = nm_setting_get_name (setting);
-	gs_unref_bytes GBytes *bytes = NULL;
-	const char *bin = NULL;
-	gsize bin_len = 0;
+	const guint8 *data;
+	gsize data_len;
 	char *path;
-	gboolean path_exists;
-
-	bytes = get_bytes (info, setting_name, key, TRUE, FALSE);
-	if (bytes)
-		bin = g_bytes_get_data (bytes, &bin_len);
-	if (bin_len == 0) {
-		if (!info->error) {
-			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-			             _("invalid key/cert value"));
-		}
-		return;
-	}
-
-	if (HAS_SCHEME_PREFIX (bin, bin_len, NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)) {
-		const char *path2 = &bin[NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH)];
-		gs_free char *path2_free = NULL;
-
-		if (nm_setting_802_1x_check_cert_scheme (bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PATH) {
-			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-			             _("invalid key/cert value path \"%s\""), bin);
-			return;
-		}
-
-		g_object_set (setting, key, bytes, NULL);
+	gboolean exists = FALSE;
+	GBytes *val;
 
-		if (path2[0] != '/') {
-			/* we want to read absolute paths because we use keyfile as exchange
-			 * between different processes which might not have the same cwd. */
-			path2_free = get_cert_path (info->base_dir, (const guint8 *) path2,
-			                            bin_len - NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH) - 1);
-			path2 = path2_free;
-		}
+	data = g_bytes_get_data (bytes, &data_len);
 
-		if (!g_file_test (path2, G_FILE_TEST_EXISTS)) {
-			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE,
-			             _("certificate or key file '%s' does not exist"),
-			             path2);
-		}
-		return;
-	}
+	path = nm_keyfile_detect_unqualified_path_scheme (info->base_dir, data, data_len, TRUE, &exists);
+	if (!path)
+		return FALSE;
 
-	if (HAS_SCHEME_PREFIX (bin, bin_len, NM_KEYFILE_CERT_SCHEME_PREFIX_PKCS11)) {
-		if (nm_setting_802_1x_check_cert_scheme (bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
-			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-			             _("invalid PKCS#11 URI \"%s\""), bin);
-			return;
-		}
+	/* Construct the proper value as required for the PATH scheme */
+	val = g_bytes_new_take (path, strlen (path) + 1);
+	g_object_set (setting, key, val, NULL);
 
-		g_object_set (setting, key, bytes, NULL);
-		return;
+	/* Warn if the certificate didn't exist */
+	if (!exists) {
+		handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE,
+		             _("certificate or key file '%s' does not exist"),
+		             path);
 	}
+	g_bytes_unref (val);
 
-	if (HAS_SCHEME_PREFIX (bin, bin_len, NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB)) {
-		const char *cdata = bin + NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB);
-		gsize cdata_len = bin_len - NM_STRLEN (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB) - 1;
-		gs_free guchar *bin_decoded = NULL;
-		gsize bin_decoded_len = 0;
-		gsize i;
-		gboolean valid_base64;
-		gs_unref_bytes GBytes *val = NULL;
-
-		/* Let's be strict here. We expect valid base64, no funny stuff!!
-		 * We didn't write such invalid data ourselfes and refuse to read it as blob. */
-		if ((valid_base64 = (cdata_len % 4 == 0))) {
-			for (i = 0; i < cdata_len; i++) {
-				char c = cdata[i];
+	return TRUE;
+}
 
-				if (!(   (c >= 'a' && c <= 'z')
-				      || (c >= 'A' && c <= 'Z')
-				      || (c >= '0' && c <= '9')
-				      || (c == '+' || c == '/'))) {
-					if (c != '=' || i < cdata_len - 2)
-						valid_base64 = FALSE;
-					else {
-						for (; i < cdata_len; i++) {
-							if (cdata[i] != '=')
-								valid_base64 = FALSE;
-						}
-					}
-					break;
-				}
-			}
-		}
-		if (valid_base64)
-			bin_decoded = g_base64_decode (cdata, &bin_decoded_len);
+static void
+cert_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
+{
+	const char *setting_name = nm_setting_get_name (setting);
+	gs_unref_bytes GBytes *bytes = NULL;
+	gsize bin_len;
+	const char *bin;
 
-		if (bin_decoded_len == 0) {
-			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-			             _("invalid key/cert value data:;base64, is not base64"));
+	bytes = get_bytes (info, setting_name, key, TRUE, FALSE);
+	if (bytes) {
+		/* Try as a path + scheme (ie, starts with "file://") */
+		if (handle_as_scheme (info, bytes, setting, key))
 			return;
-		}
-
-		if (nm_setting_802_1x_check_cert_scheme (bin_decoded, bin_decoded_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-			/* The blob probably starts with "file://". Setting the cert data will confuse NMSetting8021x.
-			 * In fact this is a limitation of NMSetting8021x which does not support setting blobs that start
-			 * with file://. Just warn and return TRUE to signal that we ~handled~ the setting. */
-			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-			             _("invalid key/cert value data:;base64,file://"));
+		if (info->error)
 			return;
-		}
 
-		val = g_bytes_new_take (g_steal_pointer (&bin_decoded), bin_decoded_len);
-		g_object_set (setting, key, val, NULL);
-		return;
-	}
-
-	/* If not, it might be a plain path */
-	path = nm_keyfile_detect_unqualified_path_scheme (info->base_dir, bin, bin_len, TRUE, &path_exists);
-	if (path) {
-		gs_unref_bytes GBytes *val = NULL;
-
-		/* Construct the proper value as required for the PATH scheme */
-		val = g_bytes_new_take (path, strlen (path) + 1);
-		g_object_set (setting, key, val, NULL);
-
-		/* Warn if the certificate didn't exist */
-		if (!path_exists) {
-			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_INFO_MISSING_FILE,
-			             _("certificate or key file '%s' does not exist"),
-			             path);
-		}
-		return;
-	}
+		/* If not, it might be a plain path */
+		if (handle_as_path (info, bytes, setting, key))
+			return;
+		if (info->error)
+			return;
 
-	if (nm_setting_802_1x_check_cert_scheme (bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-		/* The blob probably starts with "file://" but contains invalid characters for a path.
-		 * Setting the cert data will confuse NMSetting8021x.
-		 * In fact, NMSetting8021x does not support setting such binary data, so just warn and
-		 * continue. */
+		bin = g_bytes_get_data (bytes, &bin_len);
+		if (nm_setting_802_1x_check_cert_scheme (bin, bin_len, NULL) != NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+			/* The blob probably starts with "file://" but contains invalid characters for a path.
+			 * Setting the cert data will confuse NMSetting8021x.
+			 * In fact, NMSetting8021x does not support setting such binary data, so just warn and
+			 * continue. */
+			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			             _("invalid key/cert value is not a valid blob"));
+		} else
+			g_object_set (setting, key, bytes, NULL);
+	} else if (!info->error) {
 		handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-		             _("invalid key/cert value is not a valid blob"));
-		return;
+		             _("invalid key/cert value"));
 	}
-
-	g_object_set (setting, key, bytes, NULL);
 }
 
 static void
@@ -1486,927 +1410,149 @@ tfilter_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 	g_ptr_array_unref (tfilters);
 }
 
-/*****************************************************************************/
-
-/* 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
- * encryption.  This function handles translating those properties' values
- * from the real setting name to the more-readable alias.
- */
-static void
-setting_alias_writer (KeyfileWriterInfo *info,
-                      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 (info->keyfile,
-	                                 nm_setting_get_name (setting),
-	                                 key,
-	                                 alias ? alias : str);
-}
-
-static void
-write_array_of_uint (GKeyFile *file,
-                     NMSetting *setting,
-                     const char *key,
-                     const GValue *value)
-{
-	GArray *array;
-	guint i;
-	gs_free int *tmp_array = NULL;
-
-	array = (GArray *) g_value_get_boxed (value);
-	if (!array || !array->len)
-		return;
-
-	g_return_if_fail (g_array_get_element_size (array) == sizeof (guint));
-
-	tmp_array = g_new (gint, array->len);
-	for (i = 0; i < array->len; i++) {
-		guint v = g_array_index (array, guint, i);
-
-		if (v > G_MAXINT)
-			g_return_if_reached ();
-		tmp_array[i] = (int) v;
-	}
-
-	nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len);
-}
-
-static void
-dns_writer (KeyfileWriterInfo *info,
-            NMSetting *setting,
-            const char *key,
-            const GValue *value)
-{
-	char **list;
-
-	list = g_value_get_boxed (value);
-	if (list && list[0]) {
-		nm_keyfile_plugin_kf_set_string_list (info->keyfile, nm_setting_get_name (setting), key,
-		                                      (const char **) list, g_strv_length (list));
-	}
-}
-
-static void
-ip6_addr_gen_mode_writer (KeyfileWriterInfo *info,
-                          NMSetting *setting,
-                          const char *key,
-                          const GValue *value)
-{
-	NMSettingIP6ConfigAddrGenMode addr_gen_mode;
-	gs_free char *str = NULL;
-
-	addr_gen_mode = (NMSettingIP6ConfigAddrGenMode) g_value_get_int (value);
-	str = nm_utils_enum_to_str (nm_setting_ip6_config_addr_gen_mode_get_type (),
-	                            addr_gen_mode);
-	nm_keyfile_plugin_kf_set_string (info->keyfile,
-	                                 nm_setting_get_name (setting),
-	                                 key,
-	                                 str);
-}
-
-static void
-write_ip_values (GKeyFile *file,
-                 const char *setting_name,
-                 GPtrArray *array,
-                 const char *gateway,
-                 gboolean is_route)
-{
-	GString *output;
-	int family, i;
-	const char *addr, *gw;
-	guint32 plen;
-	char key_name[64], *key_name_idx;
-
-	if (!array->len)
-		return;
-
-	family = !strcmp (setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME) ? AF_INET : AF_INET6;
-
-	strcpy (key_name, is_route ? "route" : "address");
-	key_name_idx = key_name + strlen (key_name);
-
-	output = g_string_sized_new (2*INET_ADDRSTRLEN + 10);
-	for (i = 0; i < array->len; i++) {
-		gint64 metric = -1;
-
-		if (is_route) {
-			NMIPRoute *route = array->pdata[i];
-
-			addr = nm_ip_route_get_dest (route);
-			plen = nm_ip_route_get_prefix (route);
-			gw = nm_ip_route_get_next_hop (route);
-			metric = nm_ip_route_get_metric (route);
-		} else {
-			NMIPAddress *address = array->pdata[i];
-
-			addr = nm_ip_address_get_address (address);
-			plen = nm_ip_address_get_prefix (address);
-			gw = i == 0 ? gateway : NULL;
-		}
-
-		g_string_set_size (output, 0);
-		g_string_append_printf (output, "%s/%u", addr, plen);
-		if (   metric != -1
-		    || 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 there isn't one.
-			 * The current version supports reading of the above form.
-			 */
-			if (!gw) {
-				if (family == AF_INET)
-					gw = "0.0.0.0";
-				else
-					gw = "::";
-			}
-
-			g_string_append_printf (output, ",%s", gw);
-			if (is_route && metric != -1)
-				g_string_append_printf (output, ",%lu", (unsigned long) metric);
-		}
-
-		sprintf (key_name_idx, "%d", i + 1);
-		nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str);
-
-		if (is_route) {
-			gs_free char *attributes = NULL;
-			GHashTable *hash;
-
-			hash = _nm_ip_route_get_attributes_direct (array->pdata[i]);
-			attributes = nm_utils_format_variant_attributes (hash, ',', '=');
-			if (attributes) {
-				g_strlcat (key_name, "_options", sizeof (key_name));
-				nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, attributes);
-			}
-		}
-	}
-	g_string_free (output, TRUE);
-}
-
-static void
-addr_writer (KeyfileWriterInfo *info,
-             NMSetting *setting,
-             const char *key,
-             const GValue *value)
-{
-	GPtrArray *array;
-	const char *setting_name = nm_setting_get_name (setting);
-	const char *gateway = nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (setting));
-
-	array = (GPtrArray *) g_value_get_boxed (value);
-	if (array && array->len)
-		write_ip_values (info->keyfile, setting_name, array, gateway, FALSE);
-}
-
-static void
-route_writer (KeyfileWriterInfo *info,
-              NMSetting *setting,
-              const char *key,
-              const GValue *value)
-{
-	GPtrArray *array;
-	const char *setting_name = nm_setting_get_name (setting);
-
-	array = (GPtrArray *) g_value_get_boxed (value);
-	if (array && array->len)
-		write_ip_values (info->keyfile, setting_name, array, NULL, TRUE);
-}
-
-static void
-qdisc_writer (KeyfileWriterInfo *info,
-              NMSetting *setting,
-              const char *key,
-              const GValue *value)
-{
-	gsize i;
-	GPtrArray *array;
-
-	array = (GPtrArray *) g_value_get_boxed (value);
-	if (!array || !array->len)
-		return;
-
-	for (i = 0; i < array->len; i++) {
-		NMTCQdisc *qdisc = array->pdata[i];
-		GString *key_name = g_string_sized_new (16);
-		GString *value_str = g_string_sized_new (60);
-
-		g_string_append (key_name, "qdisc.");
-		_nm_utils_string_append_tc_parent (key_name, NULL,
-		                                   nm_tc_qdisc_get_parent (qdisc));
-		_nm_utils_string_append_tc_qdisc_rest (value_str, qdisc);
-
-		nm_keyfile_plugin_kf_set_string (info->keyfile,
-		                                 NM_SETTING_TC_CONFIG_SETTING_NAME,
-		                                 key_name->str,
-		                                 value_str->str);
-
-		g_string_free (key_name, TRUE);
-		g_string_free (value_str, TRUE);
-	}
-}
-
-static void
-tfilter_writer (KeyfileWriterInfo *info,
-              NMSetting *setting,
-              const char *key,
-              const GValue *value)
-{
-	gsize i;
-	GPtrArray *array;
-
-	array = (GPtrArray *) g_value_get_boxed (value);
-	if (!array || !array->len)
-		return;
-
-	for (i = 0; i < array->len; i++) {
-		NMTCTfilter *tfilter = array->pdata[i];
-		GString *key_name = g_string_sized_new (16);
-		GString *value_str = g_string_sized_new (60);
-
-		g_string_append (key_name, "tfilter.");
-		_nm_utils_string_append_tc_parent (key_name, NULL,
-		                                   nm_tc_tfilter_get_parent (tfilter));
-		_nm_utils_string_append_tc_tfilter_rest (value_str, tfilter, NULL);
-
-		nm_keyfile_plugin_kf_set_string (info->keyfile,
-		                                 NM_SETTING_TC_CONFIG_SETTING_NAME,
-		                                 key_name->str,
-		                                 value_str->str);
-
-		g_string_free (key_name, TRUE);
-		g_string_free (value_str, TRUE);
-	}
-}
-
-static void
-write_hash_of_string (GKeyFile *file,
-                      NMSetting *setting,
-                      const char *key,
-                      const GValue *value)
-{
-	GHashTable *hash;
-	const char *group_name = nm_setting_get_name (setting);
-	gboolean vpn_secrets = FALSE;
-	gs_free const char **keys = NULL;
-	guint i, l;
-
-	/* Write VPN secrets out to a different group to keep them separate */
-	if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
-		group_name = NM_KEYFILE_GROUP_VPN_SECRETS;
-		vpn_secrets = TRUE;
-	}
-
-	hash = g_value_get_boxed (value);
-
-	keys = nm_utils_strdict_get_keys (hash, TRUE, &l);
-	for (i = 0; i < l; i++) {
-		const char *property, *data;
-		gboolean write_item = TRUE;
-
-		property = keys[i];
-
-		/* Handle VPN secrets specially; they are nested in the property's hash;
-		 * we don't want to write them if the secret is not saved, not required,
-		 * or owned by a user's secret agent.
-		 */
-		if (vpn_secrets) {
-			NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-
-			nm_setting_get_secret_flags (setting, property, &secret_flags, NULL);
-			if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
-				write_item = FALSE;
-		}
-
-		if (write_item) {
-			gs_free char *to_free = NULL;
-
-			data = g_hash_table_lookup (hash, property);
-			nm_keyfile_plugin_kf_set_string (file, group_name,
-			                                 nm_keyfile_key_encode (property, &to_free),
-			                                 data);
-		}
-	}
-}
-
-static void
-ssid_writer (KeyfileWriterInfo *info,
-             NMSetting *setting,
-             const char *key,
-             const GValue *value)
-{
-	GBytes *bytes;
-	const guint8 *ssid_data;
-	gsize ssid_len;
-	const char *setting_name = nm_setting_get_name (setting);
-	gboolean new_format = TRUE;
-	gsize semicolons = 0;
-	gsize i;
-
-	g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
-
-	bytes = g_value_get_boxed (value);
-	if (!bytes)
-		return;
-	ssid_data = g_bytes_get_data (bytes, &ssid_len);
-	if (!ssid_data || !ssid_len) {
-		nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, "");
-		return;
-	}
-
-	/* Check whether each byte is printable.  If not, we have to use an
-	 * integer list, otherwise we can just use a string.
-	 */
-	for (i = 0; i < ssid_len; i++) {
-		const char c = ssid_data[i];
-
-		if (!g_ascii_isprint (c)) {
-			new_format = FALSE;
-			break;
-		}
-		if (c == ';')
-			semicolons++;
-	}
-
-	if (new_format) {
-		gs_free char *ssid = NULL;
-
-		if (semicolons == 0)
-			ssid = g_strndup ((char *) ssid_data, ssid_len);
-		else {
-			/* Escape semicolons with backslashes to make strings
-			 * containing ';', such as '16;17;' unambiguous */
-			gsize j = 0;
-
-			ssid = g_malloc (ssid_len + semicolons + 1);
-			for (i = 0; i < ssid_len; i++) {
-				if (ssid_data[i] == ';')
-					ssid[j++] = '\\';
-				ssid[j++] = ssid_data[i];
-			}
-			ssid[j] = '\0';
-		}
-		nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, ssid);
-	} else
-		nm_keyfile_plugin_kf_set_integer_list_uint8 (info->keyfile, setting_name, key, ssid_data, ssid_len);
-}
-
-static void
-password_raw_writer (KeyfileWriterInfo *info,
-                     NMSetting *setting,
-                     const char *key,
-                     const GValue *value)
-{
-	const char *setting_name = nm_setting_get_name (setting);
-	GBytes *array;
-	gsize len;
-	const guint8 *data;
-
-	g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
-
-	array = (GBytes *) g_value_get_boxed (value);
-	if (!array)
-		return;
-	data = g_bytes_get_data (array, &len);
-	if (!data)
-		len = 0;
-	nm_keyfile_plugin_kf_set_integer_list_uint8 (info->keyfile, setting_name, key, data, len);
-}
-
-/*****************************************************************************/
-
-static void
-cert_writer_default (NMConnection *connection,
-                     GKeyFile *file,
-                     NMKeyfileWriteTypeDataCert *cert_data)
-{
-	const char *setting_name = nm_setting_get_name (NM_SETTING (cert_data->setting));
-	NMSetting8021xCKScheme scheme;
-
-	scheme = cert_data->vtable->scheme_func (cert_data->setting);
-	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
-		const char *path;
-		char *path_free = NULL, *tmp;
-		gs_free char *base_dir = NULL;
-
-		path = cert_data->vtable->path_func (cert_data->setting);
-		g_assert (path);
-
-		/* If the path is relative, make it an absolute path.
-		 * Relative paths make a keyfile not easily usable in another
-		 * context. */
-		if (path[0] && path[0] != '/') {
-			base_dir = g_get_current_dir ();
-			path = path_free = g_strconcat (base_dir, "/", path, NULL);
-		} else
-			base_dir = g_path_get_dirname (path);
-
-		/* path cannot start with "file://" or "data:;base64,", because it is an absolute path.
-		 * Still, make sure that a prefix-less path will be recognized. This can happen
-		 * for example if the path is longer then 500 chars. */
-		tmp = nm_keyfile_detect_unqualified_path_scheme (base_dir, path, -1, FALSE, NULL);
-		if (tmp)
-			g_clear_pointer (&tmp, g_free);
-		else
-			path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL);
-
-		/* Path contains at least a '/', hence it cannot be recognized as the old
-		 * binary format consisting of a list of integers. */
-
-		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key, path);
-		g_free (tmp);
-		g_free (path_free);
-	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
-		GBytes *blob;
-		const guint8 *blob_data;
-		gsize blob_len;
-		char *blob_base64, *val;
-
-		blob = cert_data->vtable->blob_func (cert_data->setting);
-		g_assert (blob);
-		blob_data = g_bytes_get_data (blob, &blob_len);
-
-		blob_base64 = g_base64_encode (blob_data, blob_len);
-		val = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB, blob_base64, NULL);
-
-		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key, val);
-		g_free (val);
-		g_free (blob_base64);
-	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
-		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key,
-		                                 cert_data->vtable->uri_func (cert_data->setting));
-	} else {
-		/* scheme_func() returns UNKNOWN in all other cases. The only valid case
-		 * where a scheme is allowed to be UNKNOWN, is unsetting the value. In this
-		 * case, we don't expect the writer to be called, because the default value
-		 * will not be serialized.
-		 * The only other reason for the scheme to be UNKNOWN is an invalid cert.
-		 * But our connection verifies, so that cannot happen either. */
-		g_return_if_reached ();
-	}
-}
-
-static void
-cert_writer (KeyfileWriterInfo *info,
-             NMSetting *setting,
-             const char *key,
-             const GValue *value)
-{
-	const NMSetting8021xSchemeVtable *objtype = NULL;
-	guint i;
-	NMKeyfileWriteTypeDataCert type_data = { 0 };
-
-	for (i = 0; nm_setting_8021x_scheme_vtable[i].setting_key; i++) {
-		if (g_strcmp0 (nm_setting_8021x_scheme_vtable[i].setting_key, key) == 0) {
-			objtype = &nm_setting_8021x_scheme_vtable[i];
-			break;
-		}
-	}
-	if (!objtype)
-		g_return_if_reached ();
-
-	type_data.setting = NM_SETTING_802_1X (setting);
-	type_data.vtable = objtype;
-
-	if (info->handler) {
-		if (info->handler (info->connection,
-		                   info->keyfile,
-		                   NM_KEYFILE_WRITE_TYPE_CERT,
-		                   &type_data,
-		                   info->user_data,
-		                   &info->error))
-			return;
-		if (info->error)
-			return;
-	}
-
-	cert_writer_default (info->connection, info->keyfile, &type_data);
-}
-
-/*****************************************************************************/
-
-typedef struct {
-	const char *property_name;
-	void (*parser) (KeyfileReaderInfo *info,
-	                NMSetting *setting,
-	                const char *key);
-	void (*writer) (KeyfileWriterInfo *info,
-	                NMSetting *setting,
-	                const char *key,
-	                const GValue *value);
-	bool parser_skip;
-	bool parser_no_check_key:1;
-	bool writer_skip:1;
-
-	/* usually, we skip to write values that have their
-	 * default value. By setting this flag to TRUE, also
-	 * default values are written. */
-	bool writer_persist_default:1;
-} ParseInfoProperty;
-
-#define PARSE_INFO_PROPERTY(_property_name, ...) \
-	(&((const ParseInfoProperty) { \
-		.property_name = _property_name, \
-		__VA_ARGS__ \
-	}))
-
-#define PARSE_INFO_PROPERTIES(...) \
-	.properties = ((const ParseInfoProperty*const[]) { \
-		__VA_ARGS__ \
-		NULL, \
-	})
-
 typedef struct {
 	const char *setting_name;
-	const ParseInfoProperty*const*properties;
-} ParseInfoSetting;
-
-#define PARSE_INFO_SETTING(_setting_name, ...) \
-	{ \
-		.setting_name = _setting_name, \
-		__VA_ARGS__ \
-	}
-
-static const ParseInfoSetting parse_infos[] = {
-	PARSE_INFO_SETTING (NM_SETTING_WIRELESS_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_WIRELESS_BSSID,
-				.parser        = mac_address_parser_ETHER,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS,
-				.parser        = mac_address_parser_ETHER_cloned,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_WIRELESS_MAC_ADDRESS,
-				.parser        = mac_address_parser_ETHER,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_WIRELESS_SSID,
-				.parser        = ssid_parser,
-				.writer        = ssid_writer,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_802_1X_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_802_1X_CA_CERT,
-				.parser        = cert_parser,
-				.writer        = cert_writer,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_802_1X_CLIENT_CERT,
-				.parser        = cert_parser,
-				.writer        = cert_writer,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_802_1X_PASSWORD_RAW,
-				.parser        = password_raw_parser,
-				.writer        = password_raw_writer,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_802_1X_PHASE2_CA_CERT,
-				.parser        = cert_parser,
-				.writer        = cert_writer,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
-				.parser        = cert_parser,
-				.writer        = cert_writer,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-				.parser        = cert_parser,
-				.writer        = cert_writer,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_802_1X_PRIVATE_KEY,
-				.parser        = cert_parser,
-				.writer        = cert_writer,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_WIRED_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
-				.parser        = mac_address_parser_ETHER_cloned,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_WIRED_MAC_ADDRESS,
-				.parser        = mac_address_parser_ETHER,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_BLUETOOTH_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_BLUETOOTH_BDADDR,
-				.parser        = mac_address_parser_ETHER,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_BOND_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_BOND_OPTIONS,
-				.parser_no_check_key = TRUE,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_BRIDGE_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_BRIDGE_MAC_ADDRESS,
-				.parser        = mac_address_parser_ETHER,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_CONNECTION_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_CONNECTION_READ_ONLY,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_CONNECTION_TYPE,
-				.parser        = setting_alias_parser,
-				.writer        = setting_alias_writer,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_INFINIBAND_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_INFINIBAND_MAC_ADDRESS,
-				.parser        = mac_address_parser_INFINIBAND,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_IP4_CONFIG_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ADDRESSES,
-				.parser_no_check_key = TRUE,
-				.parser        = ip_address_or_route_parser,
-				.writer        = addr_writer,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_DNS,
-				.parser_no_check_key = TRUE,
-				.parser        = ip_dns_parser,
-				.writer        = dns_writer,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_GATEWAY,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ROUTES,
-				.parser_no_check_key = TRUE,
-				.parser        = ip_address_or_route_parser,
-				.writer        = route_writer,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_IP6_CONFIG_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
-				.parser_no_check_key = TRUE,
-				.parser        = ip6_addr_gen_mode_parser,
-				.writer        = ip6_addr_gen_mode_writer,
-				.writer_persist_default = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ADDRESSES,
-				.parser_no_check_key = TRUE,
-				.parser        = ip_address_or_route_parser,
-				.writer        = addr_writer,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_DNS,
-				.parser_no_check_key = TRUE,
-				.parser        = ip_dns_parser,
-				.writer        = dns_writer,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_GATEWAY,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_IP_CONFIG_ROUTES,
-				.parser_no_check_key = TRUE,
-				.parser        = ip_address_or_route_parser,
-				.writer        = route_writer,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_SERIAL_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_SERIAL_PARITY,
-				.parser        = parity_parser,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_TC_CONFIG_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_TC_CONFIG_QDISCS,
-				.parser_no_check_key = TRUE,
-				.parser        = qdisc_parser,
-				.writer        = qdisc_writer,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TC_CONFIG_TFILTERS,
-				.parser_no_check_key = TRUE,
-				.parser        = tfilter_parser,
-				.writer        = tfilter_writer,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_TEAM_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_CONFIG,
-				.parser        = team_config_parser,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_LINK_WATCHERS,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_MCAST_REJOIN_COUNT,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_ACTIVE,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_FAST_RATE,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_MIN_PORTS,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_SYS_PRIO,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_TX_BALANCER,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_RUNNER_TX_HASH,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_TEAM_PORT_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_CONFIG,
-				.parser        = team_config_parser,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_LACP_KEY,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_LACP_PRIO,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_LINK_WATCHERS,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_PRIO,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_QUEUE_ID,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_TEAM_PORT_STICKY,
-				.parser_skip   = TRUE,
-				.writer_skip   = TRUE,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_USER_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_USER_DATA,
-				.parser_no_check_key = TRUE,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_VLAN_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_VLAN_FLAGS,
-				.writer_persist_default = TRUE,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_VPN_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_VPN_DATA,
-				.parser_no_check_key = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_VPN_PERSISTENT,
-				.parser_no_check_key = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_VPN_SECRETS,
-				.parser_no_check_key = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_VPN_SERVICE_TYPE,
-				.parser_no_check_key = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_VPN_TIMEOUT,
-				.parser_no_check_key = TRUE,
-			),
-			PARSE_INFO_PROPERTY (NM_SETTING_VPN_USER_NAME,
-				.parser_no_check_key = TRUE,
-			),
-		),
-	),
-	PARSE_INFO_SETTING (NM_SETTING_WIMAX_SETTING_NAME,
-		PARSE_INFO_PROPERTIES (
-			PARSE_INFO_PROPERTY (NM_SETTING_WIMAX_MAC_ADDRESS,
-				.parser        = mac_address_parser_ETHER,
-			),
-		),
-	),
+	const char *key;
+	gboolean check_for_key;
+	void (*parser) (KeyfileReaderInfo *info, NMSetting *setting, const char *key);
+} KeyParser;
+
+/* A table of keys that require further parsing/conversion because they are
+ * stored in a format that can't be automatically read using the key's type.
+ * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
+ * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored
+ * 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_IP_CONFIG_ADDRESSES,
+	  FALSE,
+	  ip_address_or_route_parser },
+	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_ADDRESSES,
+	  FALSE,
+	  ip_address_or_route_parser },
+	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_ROUTES,
+	  FALSE,
+	  ip_address_or_route_parser },
+	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_ROUTES,
+	  FALSE,
+	  ip_address_or_route_parser },
+	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_DNS,
+	  FALSE,
+	  ip4_dns_parser },
+	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_DNS,
+	  FALSE,
+	  ip6_dns_parser },
+	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
+	  FALSE,
+	  ip6_addr_gen_mode_parser },
+	{ NM_SETTING_WIRED_SETTING_NAME,
+	  NM_SETTING_WIRED_MAC_ADDRESS,
+	  TRUE,
+	  mac_address_parser_ETHER },
+	{ NM_SETTING_WIRED_SETTING_NAME,
+	  NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
+	  TRUE,
+	  mac_address_parser_ETHER_cloned },
+	{ NM_SETTING_WIRELESS_SETTING_NAME,
+	  NM_SETTING_WIRELESS_MAC_ADDRESS,
+	  TRUE,
+	  mac_address_parser_ETHER },
+	{ NM_SETTING_WIRELESS_SETTING_NAME,
+	  NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS,
+	  TRUE,
+	  mac_address_parser_ETHER_cloned },
+	{ NM_SETTING_WIRELESS_SETTING_NAME,
+	  NM_SETTING_WIRELESS_BSSID,
+	  TRUE,
+	  mac_address_parser_ETHER },
+	{ NM_SETTING_BLUETOOTH_SETTING_NAME,
+	  NM_SETTING_BLUETOOTH_BDADDR,
+	  TRUE,
+	  mac_address_parser_ETHER },
+	{ NM_SETTING_INFINIBAND_SETTING_NAME,
+	  NM_SETTING_INFINIBAND_MAC_ADDRESS,
+	  TRUE,
+	  mac_address_parser_INFINIBAND },
+	{ NM_SETTING_WIMAX_SETTING_NAME,
+	  NM_SETTING_WIMAX_MAC_ADDRESS,
+	  TRUE,
+	  mac_address_parser_ETHER },
+	{ NM_SETTING_WIRELESS_SETTING_NAME,
+	  NM_SETTING_WIRELESS_SSID,
+	  TRUE,
+	  ssid_parser },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_PASSWORD_RAW,
+	  TRUE,
+	  password_raw_parser },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_CA_CERT,
+	  TRUE,
+	  cert_parser },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_CLIENT_CERT,
+	  TRUE,
+	  cert_parser },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_PRIVATE_KEY,
+	  TRUE,
+	  cert_parser },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_PHASE2_CA_CERT,
+	  TRUE,
+	  cert_parser },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
+	  TRUE,
+	  cert_parser },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
+	  TRUE,
+	  cert_parser },
+	{ NM_SETTING_SERIAL_SETTING_NAME,
+	  NM_SETTING_SERIAL_PARITY,
+	  TRUE,
+	  parity_parser },
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_CONFIG,
+	  TRUE,
+	  team_config_parser },
+	{ NM_SETTING_TEAM_PORT_SETTING_NAME,
+	  NM_SETTING_TEAM_CONFIG,
+	  TRUE,
+	  team_config_parser },
+        { NM_SETTING_TC_CONFIG_SETTING_NAME,
+          NM_SETTING_TC_CONFIG_QDISCS,
+	  FALSE,
+          qdisc_parser },
+        { NM_SETTING_TC_CONFIG_SETTING_NAME,
+          NM_SETTING_TC_CONFIG_TFILTERS,
+	  FALSE,
+          tfilter_parser },
+	{ NULL, NULL, FALSE }
 };
 
-static const ParseInfoProperty *
-_parse_info_find (const char *setting_name, const char *property_name)
+static void
+set_default_for_missing_key (NMSetting *setting, const char *property)
 {
-	gssize idx;
-
-#if NM_MORE_ASSERTS > 5
-	{
-		guint i, j;
-
-		for (i = 0; i < G_N_ELEMENTS (parse_infos); i++) {
-			const ParseInfoSetting *pis = &parse_infos[i];
-
-			g_assert (pis->setting_name);
-			if (   i > 0
-				&& strcmp (pis[-1].setting_name, pis->setting_name) >= 0)
-				g_error ("Wrong order at index #%d: \"%s\" before \"%s\"", i - 1, pis[-1].setting_name, pis->setting_name);
-			g_assert (pis->properties);
-			g_assert (pis->properties[0]);
-			for (j = 0; pis->properties[j]; j++) {
-				const ParseInfoProperty *pip0;
-				const ParseInfoProperty *pip = pis->properties[j];
-
-				g_assert (pip->property_name);
-				if (   j > 0
-				    && (pip0 = pis->properties[j - 1])
-				    && strcmp (pip0->property_name, pip->property_name) >= 0)
-					g_error ("Wrong order at index #%d.%d: \"%s.%s\" before \"%s.%s\"", i, j - 1, pis->setting_name, pip0->property_name, pis->setting_name, pip->property_name);
-			}
-		}
-	}
-#endif
-
-	G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (ParseInfoSetting, setting_name) == 0);
-	idx = _nm_utils_array_find_binary_search (parse_infos,
-	                                          sizeof (ParseInfoSetting),
-	                                          G_N_ELEMENTS (parse_infos),
-	                                          &setting_name,
-	                                          nm_strcmp_p_with_data,
-	                                          NULL);
-	if (idx >= 0) {
-		const ParseInfoSetting *pis = &parse_infos[idx];
-
-		nm_assert (nm_streq (pis->setting_name, setting_name));
-		idx = _nm_utils_ptrarray_find_binary_search ((gconstpointer *) pis->properties,
-		                                             NM_PTRARRAY_LEN (pis->properties),
-		                                             &property_name,
-		                                             nm_strcmp_p_with_data,
-		                                             NULL,
-		                                             NULL,
-		                                             NULL);
-		if (idx >= 0)
-			return pis->properties[idx];
-	}
-
-	return NULL;
+	/* Set a value different from the default value of the property's spec */
 }
 
-/*****************************************************************************/
-
 static void
 read_one_setting_value (NMSetting *setting,
                         const char *key,
@@ -2420,54 +1566,85 @@ read_one_setting_value (NMSetting *setting,
 	int errsv;
 	GType type;
 	gs_free_error GError *err = NULL;
-	const ParseInfoProperty *pip;
+	gboolean check_for_key = TRUE;
+	KeyParser *parser = &key_parsers[0];
 
 	if (info->error)
 		return;
 
+	/* Property is not writable */
 	if (!(flags & G_PARAM_WRITABLE))
 		return;
 
-	setting_name = nm_setting_get_name (setting);
-
-	pip = _parse_info_find (setting_name, key);
+	/* Setting name gets picked up from the keyfile's section name instead */
+	if (!strcmp (key, NM_SETTING_NAME))
+		return;
 
-	if (   !pip
-	    && nm_streq (key, NM_SETTING_NAME))
+	/* Don't read the NMSettingConnection object's 'read-only' property */
+	if (   NM_IS_SETTING_CONNECTION (setting)
+	    && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
 		return;
 
-	if (pip && pip->parser_skip)
+	if (   (   NM_IS_SETTING_TEAM (setting)
+	        || NM_IS_SETTING_TEAM_PORT (setting))
+	    && !NM_IN_STRSET (key, NM_SETTING_TEAM_CONFIG)) {
+		/* silently ignore all team properties (except "config"). */
 		return;
+	}
+
+	setting_name = nm_setting_get_name (setting);
+
+	/* Look through the list of handlers for non-standard format key values */
+	while (parser->setting_name) {
+		if (!strcmp (parser->setting_name, setting_name) && !strcmp (parser->key, key)) {
+			check_for_key = parser->check_for_key;
+			break;
+		}
+		parser++;
+	}
+
+	if (NM_IS_SETTING_VPN (setting))
+		check_for_key = FALSE;
+	else if (NM_IS_SETTING_USER (setting))
+		check_for_key = FALSE;
+	else if (NM_IS_SETTING_BOND (setting))
+		check_for_key = FALSE;
 
 	/* Check for the exact key in the GKeyFile if required.  Most setting
 	 * properties map 1:1 to a key in the GKeyFile, but for those properties
 	 * like IP addresses and routes where more than one value is actually
 	 * encoded by the setting property, this won't be true.
 	 */
-	if (   (!pip || !pip->parser_no_check_key)
-	    && !nm_keyfile_plugin_kf_has_key (keyfile, setting_name, key, &err)) {
+	if (check_for_key && !nm_keyfile_plugin_kf_has_key (keyfile, setting_name, key, &err)) {
 		/* Key doesn't exist or an error ocurred, thus nothing to do. */
 		if (err) {
 			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
 			                  _("error loading setting value: %s"),
 			                  err->message))
-				return;
+				goto out_error;
 		}
+
+		/* Allow default values different than in property spec */
+		set_default_for_missing_key (setting, key);
 		return;
 	}
 
-	if (pip && pip->parser) {
-		pip->parser (info, setting, key);
+	/* If there's a custom parser for this key, handle that before the generic
+	 * parsers below.
+	 */
+	if (parser->setting_name) {
+		(*parser->parser) (info, setting, key);
 		return;
 	}
 
 	type = G_VALUE_TYPE (value);
 
 	if (type == G_TYPE_STRING) {
-		gs_free char *str_val = NULL;
+		char *str_val;
 
 		str_val = nm_keyfile_plugin_kf_get_string (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;
 
@@ -2476,7 +1653,7 @@ read_one_setting_value (NMSetting *setting,
 			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
 			                  _("invalid negative value (%i)"),
 			                  int_val))
-				return;
+				goto out_error;
 		}
 		g_object_set (setting, key, int_val, NULL);
 	} else if (type == G_TYPE_INT) {
@@ -2497,16 +1674,17 @@ read_one_setting_value (NMSetting *setting,
 			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
 			                  _("invalid char value (%i)"),
 			                  int_val))
-				return;
+				goto out_error;
 		}
 
 		g_object_set (setting, key, int_val, NULL);
 	} else if (type == G_TYPE_UINT64) {
-		gs_free char *tmp_str = NULL;
+		char *tmp_str;
 		guint64 uint_val;
 
 		tmp_str = nm_keyfile_plugin_kf_get_value (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);
 	} else if (type == G_TYPE_INT64) {
 		gs_free char *tmp_str = NULL;
@@ -2519,7 +1697,7 @@ read_one_setting_value (NMSetting *setting,
 			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
 			                  _("invalid int64 value (%s)"),
 			                  tmp_str))
-				return;
+				goto out_error;
 		} else
 			g_object_set (setting, key, int_val, NULL);
 	} else if (type == G_TYPE_BYTES) {
@@ -2544,7 +1722,7 @@ read_one_setting_value (NMSetting *setting,
 				                     val)) {
 					g_byte_array_unref (array);
 					g_free (tmp);
-					return;
+					goto out_error;
 				}
 				already_warned = TRUE;
 			} else
@@ -2578,7 +1756,7 @@ read_one_setting_value (NMSetting *setting,
 				if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
 				                  _("too large FLAGS property '%s' (%llu)"),
 				                  G_VALUE_TYPE_NAME (value), (unsigned long long) uint_val))
-					return;
+					goto out_error;
 			}
 		}
 	} else if (G_VALUE_HOLDS_ENUM (value)) {
@@ -2591,8 +1769,10 @@ read_one_setting_value (NMSetting *setting,
 		if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
 		                 _("unhandled setting property type '%s'"),
 		                 G_VALUE_TYPE_NAME (value)))
-			return;
+			goto out_error;
 	}
+out_error:
+	return;
 }
 
 static NMSetting *
@@ -2627,8 +1807,7 @@ read_setting (KeyfileReaderInfo *info)
 static void
 read_vpn_secrets (KeyfileReaderInfo *info, NMSettingVpn *s_vpn)
 {
-	gs_strfreev char **keys = NULL;
-	char **iter;
+	char **keys, **iter;
 
 	keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, NM_KEYFILE_GROUP_VPN_SECRETS, NULL, NULL);
 	for (iter = keys; *iter; iter++) {
@@ -2640,6 +1819,7 @@ read_vpn_secrets (KeyfileReaderInfo *info, NMSettingVpn *s_vpn)
 			g_free (secret);
 		}
 	}
+	g_strfreev (keys);
 }
 
 /**
@@ -2673,7 +1853,7 @@ nm_keyfile_read (GKeyFile *keyfile,
                  void *user_data,
                  GError **error)
 {
-	gs_unref_object NMConnection *connection = NULL;
+	NMConnection *connection = NULL;
 	NMSettingConnection *s_con;
 	NMSetting *setting;
 	gchar **groups;
@@ -2707,6 +1887,8 @@ nm_keyfile_read (GKeyFile *keyfile,
 	info.user_data = user_data;
 
 	groups = g_key_file_get_groups (keyfile, &length);
+	if (!groups)
+		length = 0;
 	for (i = 0; i < length; i++) {
 		/* Only read out secrets when needed */
 		if (!strcmp (groups[i], NM_KEYFILE_GROUP_VPN_SECRETS)) {
@@ -2717,10 +1899,8 @@ nm_keyfile_read (GKeyFile *keyfile,
 		info.group = groups[i];
 		setting = read_setting (&info);
 		info.group = NULL;
-		if (info.error) {
-			g_propagate_error (error, info.error);
-			return NULL;
-		}
+		if (info.error)
+			goto out_error;
 		if (setting)
 			nm_connection_add_setting (connection, setting);
 	}
@@ -2735,19 +1915,21 @@ nm_keyfile_read (GKeyFile *keyfile,
 	/* Make sure that we have 'id' even if not explictly specified in the keyfile */
 	if (   keyfile_name
 	    && !nm_setting_connection_get_id (s_con)) {
-		gs_free char *base_name = NULL;
+		char *base_name;
 
 		base_name = g_path_get_basename (keyfile_name);
 		g_object_set (s_con, NM_SETTING_CONNECTION_ID, base_name, NULL);
+		g_free (base_name);
 	}
 
 	/* Make sure that we have 'uuid' even if not explictly specified in the keyfile */
 	if (   keyfile_name
 	    && !nm_setting_connection_get_uuid (s_con)) {
-		gs_free char *hashed_uuid = NULL;
+		char *hashed_uuid;
 
 		hashed_uuid = _nm_utils_uuid_generate_from_strings ("keyfile", keyfile_name, NULL);
 		g_object_set (s_con, NM_SETTING_CONNECTION_UUID, hashed_uuid, NULL);
+		g_free (hashed_uuid);
 	}
 
 	/* Make sure that we have 'interface-name' even if it was specified in the
@@ -2774,152 +1956,14 @@ nm_keyfile_read (GKeyFile *keyfile,
 		s_vpn = nm_connection_get_setting_vpn (connection);
 		if (s_vpn) {
 			read_vpn_secrets (&info, s_vpn);
-			if (info.error) {
-				g_propagate_error (error, info.error);
-				return NULL;
-			}
+			if (info.error)
+				goto out_error;
 		}
 	}
 
-	return g_steal_pointer (&connection);
-}
-
-/*****************************************************************************/
-
-static void
-write_setting_value (NMSetting *setting,
-                     const char *key,
-                     const GValue *value,
-                     GParamFlags flag,
-                     gpointer user_data)
-{
-	KeyfileWriterInfo *info = user_data;
-	const char *setting_name;
-	GType type;
-	const ParseInfoProperty *pip;
-	GParamSpec *pspec;
-
-	if (info->error)
-		return;
-
-	setting_name = nm_setting_get_name (setting);
-
-	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
-	nm_assert (pspec);
-
-	pip = _parse_info_find (setting_name, key);
-
-	if (   !pip
-	    && nm_streq (key, NM_SETTING_NAME))
-		return;
-
-	if (pip && pip->writer_skip)
-		return;
-
-	/* Don't write secrets that are owned by user secret agents or aren't
-	 * supposed to be saved.  VPN secrets are handled specially though since
-	 * 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)) {
-		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-
-		if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
-			g_assert_not_reached ();
-		if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
-			return;
-	}
-
-	if (   (!pip || !pip->writer_persist_default)
-	    && g_param_value_defaults (pspec, (GValue *) value)) {
-		nm_assert (!g_key_file_has_key (info->keyfile, setting_name, key, NULL));
-		return;
-	}
-
-	if (pip && pip->writer) {
-		pip->writer (info, setting, key, value);
-		return;
-	}
-
-	type = G_VALUE_TYPE (value);
-	if (type == G_TYPE_STRING) {
-		const char *str;
-
-		str = g_value_get_string (value);
-		if (str)
-			nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str);
-	} else if (type == G_TYPE_UINT)
-		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value));
-	else if (type == G_TYPE_INT)
-		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, g_value_get_int (value));
-	else if (type == G_TYPE_UINT64) {
-		char numstr[30];
-
-		nm_sprintf_buf (numstr, "%" G_GUINT64_FORMAT, g_value_get_uint64 (value));
-		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
-	} else if (type == G_TYPE_INT64) {
-		char numstr[30];
-
-		nm_sprintf_buf (numstr, "%" G_GINT64_FORMAT, g_value_get_int64 (value));
-		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
-	} else if (type == G_TYPE_BOOLEAN) {
-		nm_keyfile_plugin_kf_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value));
-	} else if (type == G_TYPE_CHAR) {
-		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value));
-	} else if (type == G_TYPE_BYTES) {
-		GBytes *bytes;
-		const guint8 *data;
-		gsize len = 0;
-
-		bytes = g_value_get_boxed (value);
-		data = bytes ? g_bytes_get_data (bytes, &len) : NULL;
-
-		if (data != NULL && len > 0)
-			nm_keyfile_plugin_kf_set_integer_list_uint8 (info->keyfile, setting_name, key, data, len);
-	} else if (type == G_TYPE_STRV) {
-		char **array;
-
-		array = (char **) g_value_get_boxed (value);
-		nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, g_strv_length (array));
-	} else if (type == G_TYPE_HASH_TABLE) {
-		write_hash_of_string (info->keyfile, setting, key, value);
-	} else if (type == G_TYPE_ARRAY) {
-		write_array_of_uint (info->keyfile, setting, key, value);
-	} else if (G_VALUE_HOLDS_FLAGS (value)) {
-		/* Flags are guint but GKeyFile has no uint reader, just uint64 */
-		nm_keyfile_plugin_kf_set_uint64 (info->keyfile, setting_name, key, (guint64) g_value_get_flags (value));
-	} else if (G_VALUE_HOLDS_ENUM (value))
-		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (gint) g_value_get_enum (value));
-	else
-		g_warn_if_reached ();
-}
-
-GKeyFile *
-nm_keyfile_write (NMConnection *connection,
-                  NMKeyfileWriteHandler handler,
-                  void *user_data,
-                  GError **error)
-{
-	KeyfileWriterInfo info = { 0 };
-
-	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
-	g_return_val_if_fail (!error || !*error, NULL);
-
-	if (!nm_connection_verify (connection, error))
-		return NULL;
-
-	info.connection = connection;
-	info.keyfile = g_key_file_new ();
-	info.error = NULL;
-	info.handler = handler;
-	info.user_data = user_data;
-	nm_connection_for_each_setting_value (connection, write_setting_value, &info);
-
-	if (info.error) {
-		g_propagate_error (error, info.error);
-		g_key_file_unref (info.keyfile);
-		return NULL;
-	}
-	return info.keyfile;
+	return connection;
+out_error:
+	g_propagate_error (error, info.error);
+	g_free (connection);
+	return NULL;
 }
diff --git a/libnm-core/nm-keyfile-utils.c b/libnm-core/nm-keyfile-utils.c
index dc24a5e9..88583319 100644
--- a/libnm-core/nm-keyfile-utils.c
+++ b/libnm-core/nm-keyfile-utils.c
@@ -195,7 +195,7 @@ nm_keyfile_plugin_kf_get_keys (GKeyFile *kf,
 		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, error ? &local : NULL);
+			keys = g_key_file_get_keys (kf, alias, out_length, &local);
 		}
 	}
 	if (local)
diff --git a/libnm-core/nm-keyfile-utils.h b/libnm-core/nm-keyfile-utils.h
index 832ad0b4..1f63af8c 100644
--- a/libnm-core/nm-keyfile-utils.h
+++ b/libnm-core/nm-keyfile-utils.h
@@ -21,9 +21,7 @@
 #ifndef __NM_KEYFILE_UTILS_H__
 #define __NM_KEYFILE_UTILS_H__
 
-#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-#error Cannot use this header.
-#endif
+#include <glib.h>
 
 #define NM_KEYFILE_GROUP_VPN_SECRETS "vpn-secrets"
 
diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c
new file mode 100644
index 00000000..0333b644
--- /dev/null
+++ b/libnm-core/nm-keyfile-writer.c
@@ -0,0 +1,882 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager system settings service - keyfile plugin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * 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 Novell, Inc.
+ * Copyright (C) 2008 - 2017 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-keyfile-internal.h"
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "nm-core-internal.h"
+#include "nm-keyfile-utils.h"
+
+typedef struct {
+	NMConnection *connection;
+	GKeyFile *keyfile;
+	GError *error;
+	NMKeyfileWriteHandler handler;
+	void *user_data;
+} KeyfileWriterInfo;
+
+
+/* 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
+ * encryption.  This function handles translating those properties' values
+ * from the real setting name to the more-readable alias.
+ */
+static void
+setting_alias_writer (KeyfileWriterInfo *info,
+                      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 (info->keyfile,
+	                                 nm_setting_get_name (setting),
+	                                 key,
+	                                 alias ? alias : str);
+}
+
+static void
+write_array_of_uint (GKeyFile *file,
+                     NMSetting *setting,
+                     const char *key,
+                     const GValue *value)
+{
+	GArray *array;
+	guint i;
+	gs_free int *tmp_array = NULL;
+
+	array = (GArray *) g_value_get_boxed (value);
+	if (!array || !array->len)
+		return;
+
+	g_return_if_fail (g_array_get_element_size (array) == sizeof (guint));
+
+	tmp_array = g_new (gint, array->len);
+	for (i = 0; i < array->len; i++) {
+		guint v = g_array_index (array, guint, i);
+
+		if (v > G_MAXINT)
+			g_return_if_reached ();
+		tmp_array[i] = (int) v;
+	}
+
+	nm_keyfile_plugin_kf_set_integer_list (file, nm_setting_get_name (setting), key, tmp_array, array->len);
+}
+
+static void
+dns_writer (KeyfileWriterInfo *info,
+            NMSetting *setting,
+            const char *key,
+            const GValue *value)
+{
+	char **list;
+
+	list = g_value_get_boxed (value);
+	if (list && list[0]) {
+		nm_keyfile_plugin_kf_set_string_list (info->keyfile, nm_setting_get_name (setting), key,
+		                                      (const char **) list, g_strv_length (list));
+	}
+}
+
+static void
+ip6_addr_gen_mode_writer (KeyfileWriterInfo *info,
+                          NMSetting *setting,
+                          const char *key,
+                          const GValue *value)
+{
+	NMSettingIP6ConfigAddrGenMode addr_gen_mode;
+	gs_free char *str = NULL;
+
+	addr_gen_mode = (NMSettingIP6ConfigAddrGenMode) g_value_get_int (value);
+	str = nm_utils_enum_to_str (nm_setting_ip6_config_addr_gen_mode_get_type (),
+	                            addr_gen_mode);
+	nm_keyfile_plugin_kf_set_string (info->keyfile,
+	                                 nm_setting_get_name (setting),
+	                                 key,
+	                                 str);
+}
+
+static void
+write_ip_values (GKeyFile *file,
+                 const char *setting_name,
+                 GPtrArray *array,
+                 const char *gateway,
+                 gboolean is_route)
+{
+	GString *output;
+	int family, i;
+	const char *addr, *gw;
+	guint32 plen;
+	char key_name[64], *key_name_idx;
+
+	if (!array->len)
+		return;
+
+	family = !strcmp (setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME) ? AF_INET : AF_INET6;
+
+	strcpy (key_name, is_route ? "route" : "address");
+	key_name_idx = key_name + strlen (key_name);
+
+	output = g_string_sized_new (2*INET_ADDRSTRLEN + 10);
+	for (i = 0; i < array->len; i++) {
+		gint64 metric = -1;
+
+		if (is_route) {
+			NMIPRoute *route = array->pdata[i];
+
+			addr = nm_ip_route_get_dest (route);
+			plen = nm_ip_route_get_prefix (route);
+			gw = nm_ip_route_get_next_hop (route);
+			metric = nm_ip_route_get_metric (route);
+		} else {
+			NMIPAddress *address = array->pdata[i];
+
+			addr = nm_ip_address_get_address (address);
+			plen = nm_ip_address_get_prefix (address);
+			gw = i == 0 ? gateway : NULL;
+		}
+
+		g_string_set_size (output, 0);
+		g_string_append_printf (output, "%s/%u", addr, plen);
+		if (   metric != -1
+		    || 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 there isn't one.
+			 * The current version supports reading of the above form.
+			 */
+			if (!gw) {
+				if (family == AF_INET)
+					gw = "0.0.0.0";
+				else
+					gw = "::";
+			}
+
+			g_string_append_printf (output, ",%s", gw);
+			if (is_route && metric != -1)
+				g_string_append_printf (output, ",%lu", (unsigned long) metric);
+		}
+
+		sprintf (key_name_idx, "%d", i + 1);
+		nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str);
+
+		if (is_route) {
+			gs_free char *attributes = NULL;
+			GHashTable *hash;
+
+			hash = _nm_ip_route_get_attributes_direct (array->pdata[i]);
+			attributes = nm_utils_format_variant_attributes (hash, ',', '=');
+			if (attributes) {
+				g_strlcat (key_name, "_options", sizeof (key_name));
+				nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, attributes);
+			}
+		}
+	}
+	g_string_free (output, TRUE);
+}
+
+static void
+addr_writer (KeyfileWriterInfo *info,
+             NMSetting *setting,
+             const char *key,
+             const GValue *value)
+{
+	GPtrArray *array;
+	const char *setting_name = nm_setting_get_name (setting);
+	const char *gateway = nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (setting));
+
+	array = (GPtrArray *) g_value_get_boxed (value);
+	if (array && array->len)
+		write_ip_values (info->keyfile, setting_name, array, gateway, FALSE);
+}
+
+static void
+ip4_addr_label_writer (KeyfileWriterInfo *info,
+                       NMSetting *setting,
+                       const char *key,
+                       const GValue *value)
+{
+	/* skip */
+}
+
+static void
+gateway_writer (KeyfileWriterInfo *info,
+                NMSetting *setting,
+                const char *key,
+                const GValue *value)
+{
+	/* skip */
+}
+
+static void
+route_writer (KeyfileWriterInfo *info,
+              NMSetting *setting,
+              const char *key,
+              const GValue *value)
+{
+	GPtrArray *array;
+	const char *setting_name = nm_setting_get_name (setting);
+
+	array = (GPtrArray *) g_value_get_boxed (value);
+	if (array && array->len)
+		write_ip_values (info->keyfile, setting_name, array, NULL, TRUE);
+}
+
+static void
+qdisc_writer (KeyfileWriterInfo *info,
+              NMSetting *setting,
+              const char *key,
+              const GValue *value)
+{
+	gsize i;
+	GPtrArray *array;
+
+	array = (GPtrArray *) g_value_get_boxed (value);
+	if (!array || !array->len)
+		return;
+
+	for (i = 0; i < array->len; i++) {
+		NMTCQdisc *qdisc = array->pdata[i];
+		GString *key_name = g_string_sized_new (16);
+		GString *value_str = g_string_sized_new (60);
+
+		g_string_append (key_name, "qdisc.");
+		_nm_utils_string_append_tc_parent (key_name, NULL,
+		                                   nm_tc_qdisc_get_parent (qdisc));
+		_nm_utils_string_append_tc_qdisc_rest (value_str, qdisc);
+
+		nm_keyfile_plugin_kf_set_string (info->keyfile,
+		                                 NM_SETTING_TC_CONFIG_SETTING_NAME,
+		                                 key_name->str,
+		                                 value_str->str);
+
+		g_string_free (key_name, TRUE);
+		g_string_free (value_str, TRUE);
+	}
+}
+
+static void
+tfilter_writer (KeyfileWriterInfo *info,
+              NMSetting *setting,
+              const char *key,
+              const GValue *value)
+{
+	gsize i;
+	GPtrArray *array;
+
+	array = (GPtrArray *) g_value_get_boxed (value);
+	if (!array || !array->len)
+		return;
+
+	for (i = 0; i < array->len; i++) {
+		NMTCTfilter *tfilter = array->pdata[i];
+		GString *key_name = g_string_sized_new (16);
+		GString *value_str = g_string_sized_new (60);
+
+		g_string_append (key_name, "tfilter.");
+		_nm_utils_string_append_tc_parent (key_name, NULL,
+		                                   nm_tc_tfilter_get_parent (tfilter));
+		_nm_utils_string_append_tc_tfilter_rest (value_str, tfilter, NULL);
+
+		nm_keyfile_plugin_kf_set_string (info->keyfile,
+		                                 NM_SETTING_TC_CONFIG_SETTING_NAME,
+		                                 key_name->str,
+		                                 value_str->str);
+
+		g_string_free (key_name, TRUE);
+		g_string_free (value_str, TRUE);
+	}
+}
+
+static void
+write_hash_of_string (GKeyFile *file,
+                      NMSetting *setting,
+                      const char *key,
+                      const GValue *value)
+{
+	GHashTable *hash;
+	const char *group_name = nm_setting_get_name (setting);
+	gboolean vpn_secrets = FALSE;
+	gs_free const char **keys = NULL;
+	guint i, l;
+
+	/* Write VPN secrets out to a different group to keep them separate */
+	if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
+		group_name = NM_KEYFILE_GROUP_VPN_SECRETS;
+		vpn_secrets = TRUE;
+	}
+
+	hash = g_value_get_boxed (value);
+	keys = (const char **) g_hash_table_get_keys_as_array (hash, &l);
+	if (!keys)
+		return;
+
+	g_qsort_with_data (keys, l, sizeof (const char *), nm_strcmp_p_with_data, NULL);
+
+	for (i = 0; keys[i]; i++) {
+		const char *property, *data;
+		gboolean write_item = TRUE;
+
+		property = keys[i];
+
+		/* Handle VPN secrets specially; they are nested in the property's hash;
+		 * we don't want to write them if the secret is not saved, not required,
+		 * or owned by a user's secret agent.
+		 */
+		if (vpn_secrets) {
+			NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+			nm_setting_get_secret_flags (setting, property, &secret_flags, NULL);
+			if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
+				write_item = FALSE;
+		}
+
+		if (write_item) {
+			gs_free char *to_free = NULL;
+
+			data = g_hash_table_lookup (hash, property);
+			nm_keyfile_plugin_kf_set_string (file, group_name,
+			                                 nm_keyfile_key_encode (property, &to_free),
+			                                 data);
+		}
+	}
+}
+
+static void
+ssid_writer (KeyfileWriterInfo *info,
+             NMSetting *setting,
+             const char *key,
+             const GValue *value)
+{
+	GBytes *bytes;
+	const guint8 *ssid_data;
+	gsize ssid_len;
+	const char *setting_name = nm_setting_get_name (setting);
+	gboolean new_format = TRUE;
+	gsize semicolons = 0;
+	gsize i;
+
+	g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
+
+	bytes = g_value_get_boxed (value);
+	if (!bytes)
+		return;
+	ssid_data = g_bytes_get_data (bytes, &ssid_len);
+	if (!ssid_data || !ssid_len) {
+		nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, "");
+		return;
+	}
+
+	/* Check whether each byte is printable.  If not, we have to use an
+	 * integer list, otherwise we can just use a string.
+	 */
+	for (i = 0; i < ssid_len; i++) {
+		const char c = ssid_data[i];
+
+		if (!g_ascii_isprint (c)) {
+			new_format = FALSE;
+			break;
+		}
+		if (c == ';')
+			semicolons++;
+	}
+
+	if (new_format) {
+		gs_free char *ssid = NULL;
+
+		if (semicolons == 0)
+			ssid = g_strndup ((char *) ssid_data, ssid_len);
+		else {
+			/* Escape semicolons with backslashes to make strings
+			 * containing ';', such as '16;17;' unambiguous */
+			gsize j = 0;
+
+			ssid = g_malloc (ssid_len + semicolons + 1);
+			for (i = 0; i < ssid_len; i++) {
+				if (ssid_data[i] == ';')
+					ssid[j++] = '\\';
+				ssid[j++] = ssid_data[i];
+			}
+			ssid[j] = '\0';
+		}
+		nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, ssid);
+	} else
+		nm_keyfile_plugin_kf_set_integer_list_uint8 (info->keyfile, setting_name, key, ssid_data, ssid_len);
+}
+
+static void
+password_raw_writer (KeyfileWriterInfo *info,
+                     NMSetting *setting,
+                     const char *key,
+                     const GValue *value)
+{
+	const char *setting_name = nm_setting_get_name (setting);
+	GBytes *array;
+	gsize len;
+	const guint8 *data;
+
+	g_return_if_fail (G_VALUE_HOLDS (value, G_TYPE_BYTES));
+
+	array = (GBytes *) g_value_get_boxed (value);
+	if (!array)
+		return;
+	data = g_bytes_get_data (array, &len);
+	if (!data)
+		len = 0;
+	nm_keyfile_plugin_kf_set_integer_list_uint8 (info->keyfile, setting_name, key, data, len);
+}
+
+/*****************************************************************************/
+
+static void
+cert_writer_default (NMConnection *connection,
+                     GKeyFile *file,
+                     NMKeyfileWriteTypeDataCert *cert_data)
+{
+	const char *setting_name = nm_setting_get_name (NM_SETTING (cert_data->setting));
+	NMSetting8021xCKScheme scheme;
+
+	scheme = cert_data->vtable->scheme_func (cert_data->setting);
+	if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH) {
+		const char *path;
+		char *path_free = NULL, *tmp;
+		gs_free char *base_dir = NULL;
+
+		path = cert_data->vtable->path_func (cert_data->setting);
+		g_assert (path);
+
+		/* If the path is relative, make it an absolute path.
+		 * Relative paths make a keyfile not easily usable in another
+		 * context. */
+		if (path[0] && path[0] != '/') {
+			base_dir = g_get_current_dir ();
+			path = path_free = g_strconcat (base_dir, "/", path, NULL);
+		} else
+			base_dir = g_path_get_dirname (path);
+
+		/* path cannot start with "file://" or "data:;base64,", because it is an absolute path.
+		 * Still, make sure that a prefix-less path will be recognized. This can happen
+		 * for example if the path is longer then 500 chars. */
+		tmp = nm_keyfile_detect_unqualified_path_scheme (base_dir, path, -1, FALSE, NULL);
+		if (tmp)
+			g_clear_pointer (&tmp, g_free);
+		else
+			path = tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL);
+
+		/* Path contains at least a '/', hence it cannot be recognized as the old
+		 * binary format consisting of a list of integers. */
+
+		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key, path);
+		g_free (tmp);
+		g_free (path_free);
+	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
+		GBytes *blob;
+		const guint8 *blob_data;
+		gsize blob_len;
+		char *blob_base64, *val;
+
+		blob = cert_data->vtable->blob_func (cert_data->setting);
+		g_assert (blob);
+		blob_data = g_bytes_get_data (blob, &blob_len);
+
+		blob_base64 = g_base64_encode (blob_data, blob_len);
+		val = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_BLOB, blob_base64, NULL);
+
+		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key, val);
+		g_free (val);
+		g_free (blob_base64);
+	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11) {
+		nm_keyfile_plugin_kf_set_string (file, setting_name, cert_data->vtable->setting_key,
+		                                 cert_data->vtable->uri_func (cert_data->setting));
+	} else {
+		/* scheme_func() returns UNKNOWN in all other cases. The only valid case
+		 * where a scheme is allowed to be UNKNOWN, is unsetting the value. In this
+		 * case, we don't expect the writer to be called, because the default value
+		 * will not be serialized.
+		 * The only other reason for the scheme to be UNKNOWN is an invalid cert.
+		 * But our connection verifies, so that cannot happen either. */
+		g_return_if_reached ();
+	}
+}
+
+static void
+cert_writer (KeyfileWriterInfo *info,
+             NMSetting *setting,
+             const char *key,
+             const GValue *value)
+{
+	const NMSetting8021xSchemeVtable *objtype = NULL;
+	guint i;
+	NMKeyfileWriteTypeDataCert type_data = { 0 };
+
+	for (i = 0; nm_setting_8021x_scheme_vtable[i].setting_key; i++) {
+		if (g_strcmp0 (nm_setting_8021x_scheme_vtable[i].setting_key, key) == 0) {
+			objtype = &nm_setting_8021x_scheme_vtable[i];
+			break;
+		}
+	}
+	if (!objtype)
+		g_return_if_reached ();
+
+	type_data.setting = NM_SETTING_802_1X (setting);
+	type_data.vtable = objtype;
+
+	if (info->handler) {
+		if (info->handler (info->connection,
+		                   info->keyfile,
+		                   NM_KEYFILE_WRITE_TYPE_CERT,
+		                   &type_data,
+		                   info->user_data,
+		                   &info->error))
+			return;
+		if (info->error)
+			return;
+	}
+
+	cert_writer_default (info->connection, info->keyfile, &type_data);
+}
+
+static void
+null_writer (KeyfileWriterInfo *info,
+             NMSetting *setting,
+             const char *key,
+             const GValue *value)
+{
+	/* skip */
+}
+
+/*****************************************************************************/
+
+typedef struct {
+	const char *setting_name;
+	const char *key;
+	void (*writer) (KeyfileWriterInfo *info,
+	                NMSetting *setting,
+	                const char *key,
+	                const GValue *value);
+} KeyWriter;
+
+/* A table of keys that require further parsing/conversion because they are
+ * stored in a format that can't be automatically read using the key's type.
+ * i.e. IPv4 addresses, which are stored in NetworkManager as guint32, but are
+ * stored in keyfiles as strings, eg "10.1.1.2" or IPv6 addresses stored 
+ * 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_IP4_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_ADDRESSES,
+	  addr_writer },
+	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	  "address-labels",
+	  ip4_addr_label_writer },
+	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_ADDRESSES,
+	  addr_writer },
+	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_GATEWAY,
+	  gateway_writer },
+	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_GATEWAY,
+	  gateway_writer },
+	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_ROUTES,
+	  route_writer },
+	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_ROUTES,
+	  route_writer },
+	{ NM_SETTING_IP4_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_DNS,
+	  dns_writer },
+	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP_CONFIG_DNS,
+	  dns_writer },
+	{ NM_SETTING_IP6_CONFIG_SETTING_NAME,
+	  NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE,
+	  ip6_addr_gen_mode_writer },
+	{ NM_SETTING_WIRELESS_SETTING_NAME,
+	  NM_SETTING_WIRELESS_SSID,
+	  ssid_writer },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_PASSWORD_RAW,
+	  password_raw_writer },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_CA_CERT,
+	  cert_writer },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_CLIENT_CERT,
+	  cert_writer },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_PRIVATE_KEY,
+	  cert_writer },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_PHASE2_CA_CERT,
+	  cert_writer },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
+	  cert_writer },
+	{ NM_SETTING_802_1X_SETTING_NAME,
+	  NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
+	  cert_writer },
+	{ NM_SETTING_TC_CONFIG_SETTING_NAME,
+	  NM_SETTING_TC_CONFIG_QDISCS,
+	  qdisc_writer },
+	{ NM_SETTING_TC_CONFIG_SETTING_NAME,
+	  NM_SETTING_TC_CONFIG_TFILTERS,
+	  tfilter_writer },
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_NOTIFY_PEERS_COUNT,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_MCAST_REJOIN_COUNT,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_RUNNER,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_RUNNER_HWADDR_POLICY,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_RUNNER_TX_HASH,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_RUNNER_TX_BALANCER,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_RUNNER_ACTIVE,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_RUNNER_FAST_RATE,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_RUNNER_SYS_PRIO,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_RUNNER_MIN_PORTS,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY,
+	  null_writer},
+	{ NM_SETTING_TEAM_SETTING_NAME,
+	  NM_SETTING_TEAM_LINK_WATCHERS,
+	  null_writer},
+	{ NM_SETTING_TEAM_PORT_SETTING_NAME,
+	  NM_SETTING_TEAM_PORT_QUEUE_ID,
+	  null_writer},
+	{ NM_SETTING_TEAM_PORT_SETTING_NAME,
+	  NM_SETTING_TEAM_PORT_PRIO,
+	  null_writer},
+	{ NM_SETTING_TEAM_PORT_SETTING_NAME,
+	  NM_SETTING_TEAM_PORT_STICKY,
+	  null_writer},
+	{ NM_SETTING_TEAM_PORT_SETTING_NAME,
+	  NM_SETTING_TEAM_PORT_LACP_PRIO,
+	  null_writer},
+	{ NM_SETTING_TEAM_PORT_SETTING_NAME,
+	  NM_SETTING_TEAM_PORT_LACP_KEY,
+	  null_writer},
+	{ NM_SETTING_TEAM_PORT_SETTING_NAME,
+	  NM_SETTING_TEAM_PORT_LINK_WATCHERS,
+	  null_writer},
+	{ NULL, NULL, NULL }
+};
+
+static gboolean
+can_omit_default_value (NMSetting *setting, const char *property)
+{
+	if (NM_IS_SETTING_VLAN (setting)) {
+		if (!strcmp (property, NM_SETTING_VLAN_FLAGS))
+			return FALSE;
+	} else if (NM_IS_SETTING_IP6_CONFIG (setting)) {
+		if (!strcmp (property, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE))
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void
+write_setting_value (NMSetting *setting,
+                     const char *key,
+                     const GValue *value,
+                     GParamFlags flag,
+                     gpointer user_data)
+{
+	KeyfileWriterInfo *info = user_data;
+	const char *setting_name;
+	GType type = G_VALUE_TYPE (value);
+	KeyWriter *writer = &key_writers[0];
+	GParamSpec *pspec;
+
+	if (info->error)
+		return;
+
+	/* Setting name gets picked up from the keyfile's section name instead */
+	if (!strcmp (key, NM_SETTING_NAME))
+		return;
+
+	/* Don't write the NMSettingConnection object's 'read-only' property */
+	if (   NM_IS_SETTING_CONNECTION (setting)
+	    && !strcmp (key, NM_SETTING_CONNECTION_READ_ONLY))
+		return;
+
+	setting_name = nm_setting_get_name (setting);
+
+	/* If the value is the default value, remove the item from the keyfile */
+	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
+	if (pspec) {
+		if (   can_omit_default_value (setting, key)
+		    && g_param_value_defaults (pspec, (GValue *) value)) {
+			g_key_file_remove_key (info->keyfile, setting_name, key, NULL);
+			return;
+		}
+	}
+
+	/* Don't write secrets that are owned by user secret agents or aren't
+	 * supposed to be saved.  VPN secrets are handled specially though since
+	 * the secret flags there are in a third-level hash in the 'secrets'
+	 * property.
+	 */
+	if (pspec && (pspec->flags & NM_SETTING_PARAM_SECRET) && !NM_IS_SETTING_VPN (setting)) {
+		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+		if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
+			g_assert_not_reached ();
+		if (secret_flags != NM_SETTING_SECRET_FLAG_NONE)
+			return;
+	}
+
+	/* Look through the list of handlers for non-standard format key values */
+	while (writer->setting_name) {
+		if (!strcmp (writer->setting_name, setting_name) && !strcmp (writer->key, key)) {
+			(*writer->writer) (info, setting, key, value);
+			return;
+		}
+		writer++;
+	}
+
+	if (type == G_TYPE_STRING) {
+		const char *str;
+
+		str = g_value_get_string (value);
+		if (str)
+			nm_keyfile_plugin_kf_set_string (info->keyfile, setting_name, key, str);
+	} else if (type == G_TYPE_UINT)
+		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_uint (value));
+	else if (type == G_TYPE_INT)
+		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));
+		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
+		g_free (numstr);
+	} else if (type == G_TYPE_INT64) {
+		char *numstr;
+
+		numstr = g_strdup_printf ("%" G_GINT64_FORMAT, g_value_get_int64 (value));
+		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
+		g_free (numstr);
+	} else if (type == G_TYPE_BOOLEAN) {
+		nm_keyfile_plugin_kf_set_boolean (info->keyfile, setting_name, key, g_value_get_boolean (value));
+	} else if (type == G_TYPE_CHAR) {
+		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value));
+	} else if (type == G_TYPE_BYTES) {
+		GBytes *bytes;
+		const guint8 *data;
+		gsize len = 0;
+
+		bytes = g_value_get_boxed (value);
+		data = bytes ? g_bytes_get_data (bytes, &len) : NULL;
+
+		if (data != NULL && len > 0)
+			nm_keyfile_plugin_kf_set_integer_list_uint8 (info->keyfile, setting_name, key, data, len);
+	} else if (type == G_TYPE_STRV) {
+		char **array;
+
+		array = (char **) g_value_get_boxed (value);
+		nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const gchar **const) array, g_strv_length (array));
+	} else if (type == G_TYPE_HASH_TABLE) {
+		write_hash_of_string (info->keyfile, setting, key, value);
+	} else if (type == G_TYPE_ARRAY) {
+		write_array_of_uint (info->keyfile, setting, key, value);
+	} else if (G_VALUE_HOLDS_FLAGS (value)) {
+		/* Flags are guint but GKeyFile has no uint reader, just uint64 */
+		nm_keyfile_plugin_kf_set_uint64 (info->keyfile, setting_name, key, (guint64) g_value_get_flags (value));
+	} else if (G_VALUE_HOLDS_ENUM (value))
+		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (gint) g_value_get_enum (value));
+	else
+		g_warn_if_reached ();
+}
+
+GKeyFile *
+nm_keyfile_write (NMConnection *connection,
+                  NMKeyfileWriteHandler handler,
+                  void *user_data,
+                  GError **error)
+{
+	KeyfileWriterInfo info = { 0 };
+
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
+	g_return_val_if_fail (!error || !*error, NULL);
+
+	if (!nm_connection_verify (connection, error))
+		return NULL;
+
+	info.connection = connection;
+	info.keyfile = g_key_file_new ();
+	info.error = NULL;
+	info.handler = handler;
+	info.user_data = user_data;
+	nm_connection_for_each_setting_value (connection, write_setting_value, &info);
+
+	if (info.error) {
+		g_propagate_error (error, info.error);
+		g_key_file_unref (info.keyfile);
+		return NULL;
+	}
+	return info.keyfile;
+}
+
diff --git a/libnm-core/nm-property-compare.h b/libnm-core/nm-property-compare.h
index da2aaf2c..b859989f 100644
--- a/libnm-core/nm-property-compare.h
+++ b/libnm-core/nm-property-compare.h
@@ -23,10 +23,6 @@
 #ifndef __NM_PROPERTY_COMPARE_H__
 #define __NM_PROPERTY_COMPARE_H__
 
-#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-#error Cannot use this header.
-#endif
-
 int nm_property_compare (GVariant *value1, GVariant *value2);
 
 #endif /* __NM_PROPERTY_COMPARE_H__ */
diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c
index 98e12ca8..af195211 100644
--- a/libnm-core/nm-setting-8021x.c
+++ b/libnm-core/nm-setting-8021x.c
@@ -2247,7 +2247,7 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting,
 {
 	NMSetting8021xPrivate *priv;
 	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	gboolean password_changed = FALSE;
+	gboolean key_cleared = FALSE, password_cleared = FALSE;
 	GError *local_err = NULL;
 
 	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
@@ -2281,35 +2281,39 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting,
 
 	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
 
+	/* Clear out any previous private key data */
+	if (priv->private_key) {
+		g_bytes_unref (priv->private_key);
+		priv->private_key = NULL;
+		key_cleared = TRUE;
+	}
+
+	if (priv->private_key_password) {
+		g_free (priv->private_key_password);
+		priv->private_key_password = NULL;
+		password_cleared = TRUE;
+	}
+
 	if (value == NULL) {
-		if (priv->private_key) {
-			g_clear_pointer (&priv->private_key, g_bytes_unref);
+		if (key_cleared)
 			g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY);
-		}
-		if (nm_clear_g_free (&priv->private_key_password))
+		if (password_cleared)
 			g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
 		return TRUE;
 	}
 
-	/* this makes password self-assignment safe. */
-	if (!nm_streq0 (priv->private_key_password, password)) {
-		g_free (priv->private_key_password);
-		priv->private_key_password = g_strdup (password);
-		password_changed = TRUE;
-	}
-
-	g_bytes_unref (priv->private_key);
+	priv->private_key_password = g_strdup (password);
 	if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
 		/* FIXME: potential race after verifying the private key above */
 		/* FIXME: ensure blob doesn't start with file:// */
 		priv->private_key = file_to_secure_bytes (value);
-		nm_assert (priv->private_key);
+		g_assert (priv->private_key);
 	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
 		priv->private_key = path_to_scheme_value (value);
-	else {
-		nm_assert (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11);
+	else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11)
 		priv->private_key = g_bytes_new (value, strlen (value) + 1);
-	}
+	else
+		g_assert_not_reached ();
 
 	/* As required by NM and wpa_supplicant, set the client-cert
 	 * property to the same PKCS#12 data.
@@ -2322,10 +2326,11 @@ nm_setting_802_1x_set_private_key (NMSetting8021x *setting,
 	}
 
 	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY);
-	if (password_changed)
+	if (password_cleared || password)
 		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD);
 
-	NM_SET_OUT (out_format, (NMSetting8021xCKFormat) format);
+	if (out_format)
+		*out_format = (NMSetting8021xCKFormat) format;
 	return priv->private_key != NULL;
 }
 
@@ -2589,7 +2594,7 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting,
 {
 	NMSetting8021xPrivate *priv;
 	NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
-	gboolean password_changed = FALSE;
+	gboolean key_cleared = FALSE, password_cleared = FALSE;
 	GError *local_err = NULL;
 
 	g_return_val_if_fail (NM_IS_SETTING_802_1X (setting), FALSE);
@@ -2623,34 +2628,39 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting,
 
 	priv = NM_SETTING_802_1X_GET_PRIVATE (setting);
 
+	/* Clear out any previous private key data */
+	if (priv->phase2_private_key) {
+		g_bytes_unref (priv->phase2_private_key);
+		priv->phase2_private_key = NULL;
+		key_cleared = TRUE;
+	}
+
+	if (priv->phase2_private_key_password) {
+		g_free (priv->phase2_private_key_password);
+		priv->phase2_private_key_password = NULL;
+		password_cleared = TRUE;
+	}
+
 	if (value == NULL) {
-		if (priv->phase2_private_key) {
-			g_clear_pointer (&priv->phase2_private_key, g_bytes_unref);
+		if (key_cleared)
 			g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
-		}
-		if (nm_clear_g_free (&priv->phase2_private_key_password))
+		if (password_cleared)
 			g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD);
 		return TRUE;
 	}
 
-	/* this makes password self-assignment safe. */
-	if (!nm_streq0 (priv->phase2_private_key_password, password)) {
-		g_free (priv->phase2_private_key_password);
-		priv->phase2_private_key_password = g_strdup (password);
-		password_changed = TRUE;
-	}
-
+	priv->phase2_private_key_password = g_strdup (password);
 	if (scheme == NM_SETTING_802_1X_CK_SCHEME_BLOB) {
 		/* FIXME: potential race after verifying the private key above */
 		/* FIXME: ensure blob doesn't start with file:// */
 		priv->phase2_private_key = file_to_secure_bytes (value);
-		nm_assert (priv->phase2_private_key);
+		g_assert (priv->phase2_private_key);
 	} else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PATH)
 		priv->phase2_private_key = path_to_scheme_value (value);
-	else {
-		nm_assert (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11);
+	else if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11)
 		priv->phase2_private_key = g_bytes_new (value, strlen (value) + 1);
-	}
+	else
+		g_assert_not_reached ();
 
 	/* As required by NM and wpa_supplicant, set the client-cert
 	 * property to the same PKCS#12 data.
@@ -2664,10 +2674,11 @@ nm_setting_802_1x_set_phase2_private_key (NMSetting8021x *setting,
 	}
 
 	g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY);
-	if (password_changed)
+	if (password_cleared || password)
 		g_object_notify (G_OBJECT (setting), NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD);
 
-	NM_SET_OUT (out_format, (NMSetting8021xCKFormat) format);
+	if (out_format)
+		*out_format = (NMSetting8021xCKFormat) format;
 	return priv->phase2_private_key != NULL;
 }
 
diff --git a/libnm-core/nm-setting-bond.c b/libnm-core/nm-setting-bond.c
index 9b4b3641..a44972cf 100644
--- a/libnm-core/nm-setting-bond.c
+++ b/libnm-core/nm-setting-bond.c
@@ -159,7 +159,9 @@ nm_setting_bond_get_option (NMSettingBond *setting,
                             const char **out_value)
 {
 	NMSettingBondPrivate *priv;
-	guint len;
+	guint i, len;
+	GHashTableIter iter;
+	const char *key, *value;
 
 	g_return_val_if_fail (NM_IS_SETTING_BOND (setting), FALSE);
 
@@ -169,8 +171,23 @@ nm_setting_bond_get_option (NMSettingBond *setting,
 	if (idx >= len)
 		return FALSE;
 
-	if (!G_UNLIKELY (priv->options_idx_cache))
-		priv->options_idx_cache = nm_utils_named_values_from_str_dict (priv->options, NULL);
+	if (!G_UNLIKELY (priv->options_idx_cache)) {
+		NMUtilsNamedValue *options;
+
+		i = 0;
+		options = g_new (NMUtilsNamedValue, len);
+		g_hash_table_iter_init (&iter, priv->options);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) {
+			options[i].name = key;
+			options[i].value_str = value;
+			i++;
+		}
+		nm_assert (i == len);
+
+		g_qsort_with_data (options, len, sizeof (options[0]),
+		                   nm_utils_named_entry_cmp_with_data, NULL);
+		priv->options_idx_cache = options;
+	}
 
 	NM_SET_OUT (out_name, priv->options_idx_cache[idx].name);
 	NM_SET_OUT (out_value, priv->options_idx_cache[idx].value_str);
@@ -537,7 +554,7 @@ _nm_setting_bond_option_supported (const char *option, NMBondMode mode)
 
 	for (i = 0; i < G_N_ELEMENTS (bond_unsupp_modes); i++) {
 		if (nm_streq (option, bond_unsupp_modes[i].option))
-			return !NM_FLAGS_ANY (bond_unsupp_modes[i].unsupp_modes, BIT (mode));
+		    return !NM_FLAGS_HAS (bond_unsupp_modes[i].unsupp_modes, BIT (mode));
 	}
 
 	return TRUE;
@@ -930,7 +947,7 @@ nm_setting_bond_init (NMSettingBond *setting)
 {
 	NMSettingBondPrivate *priv = NM_SETTING_BOND_GET_PRIVATE (setting);
 
-	priv->options = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
+	priv->options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
 	/* Default values: */
 	nm_setting_bond_add_option (setting, NM_SETTING_BOND_OPTION_MODE, "balance-rr");
@@ -977,11 +994,13 @@ nm_setting_bond_class_init (NMSettingBondClass *setting_class)
 
 	/* Properties */
 	/**
-	 * NMSettingBond:options: (type GHashTable(utf8,utf8)):
+	 * NMSettingBond:options:
 	 *
 	 * Dictionary of key/value pairs of bonding options.  Both keys and values
 	 * must be strings. Option names must contain only alphanumeric characters
 	 * (ie, [a-zA-Z0-9]).
+	 *
+	 * Type: GHashTable(utf8,utf8)
 	 **/
 	/* ---ifcfg-rh---
 	 * property: options
diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c
index 112b499f..152cdf65 100644
--- a/libnm-core/nm-setting-bridge.c
+++ b/libnm-core/nm-setting-bridge.c
@@ -418,8 +418,6 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
 	 * "ethernet.cloned-mac-address" anyway overwrites the MAC address of
 	 * the bridge later while activating the bridge. Hence, this property
 	 * is deprecated.
-	 *
-	 * Deprecated: 1.12: Use the ethernet.cloned-mac-address property instead.
 	 **/
 	/* ---keyfile---
 	 * property: mac-address
diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c
index 40153c0f..24c65e7c 100644
--- a/libnm-core/nm-setting-connection.c
+++ b/libnm-core/nm-setting-connection.c
@@ -81,7 +81,6 @@ typedef struct {
 	NMMetered metered;
 	NMSettingConnectionLldp lldp;
 	gint auth_retries;
-	int mdns;
 } NMSettingConnectionPrivate;
 
 enum {
@@ -104,7 +103,6 @@ enum {
 	PROP_GATEWAY_PING_TIMEOUT,
 	PROP_METERED,
 	PROP_LLDP,
-	PROP_MDNS,
 	PROP_STABLE_ID,
 	PROP_AUTH_RETRIES,
 
@@ -864,23 +862,6 @@ nm_setting_connection_get_lldp (NMSettingConnection *setting)
 	return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->lldp;
 }
 
-/**
- * nm_setting_connection_get_mdns:
- * @setting: the #NMSettingConnection
- *
- * Returns: the #NMSettingConnection:mdns property of the setting.
- *
- * Since: 1.12
- **/
-NMSettingConnectionMdns
-nm_setting_connection_get_mdns (NMSettingConnection *setting)
-{
-	g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting),
-	                      NM_SETTING_CONNECTION_MDNS_DEFAULT);
-
-	return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->mdns;
-}
-
 static void
 _set_error_missing_base_setting (GError **error, const char *type)
 {
@@ -1076,17 +1057,6 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	if (   priv->mdns < NM_SETTING_CONNECTION_MDNS_DEFAULT
-	    || priv->mdns > NM_SETTING_CONNECTION_MDNS_RESOLVE) {
-		g_set_error (error,
-		             NM_CONNECTION_ERROR,
-		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		             _("mdns value %d is not valid"), priv->mdns);
-		g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME,
-		                NM_SETTING_CONNECTION_MDNS);
-		return FALSE;
-	}
-
 	/* *** errors above here should be always fatal, below NORMALIZABLE_ERROR *** */
 
 	if (!priv->uuid) {
@@ -1244,9 +1214,6 @@ compare_property (NMSetting *setting,
 static void
 nm_setting_connection_init (NMSettingConnection *setting)
 {
-	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
-
-	priv->mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT;
 }
 
 static void
@@ -1365,9 +1332,6 @@ set_property (GObject *object, guint prop_id,
 	case PROP_AUTH_RETRIES:
 		priv->auth_retries = g_value_get_int (value);
 		break;
-	case PROP_MDNS:
-		priv->mdns = g_value_get_int (value);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1456,9 +1420,6 @@ get_property (GObject *object, guint prop_id,
 	case PROP_AUTH_RETRIES:
 		g_value_set_int (value, priv->auth_retries);
 		break;
-	case PROP_MDNS:
-		g_value_set_int (value, priv->mdns);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1541,12 +1502,10 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 	 * The stable-id is used for generating IPv6 stable private addresses
 	 * with ipv6.addr-gen-mode=stable-privacy. It is also used to seed the
 	 * generated cloned MAC address for ethernet.cloned-mac-address=stable
-	 * and wifi.cloned-mac-address=stable. It is also used as DHCP client
-	 * identifier with ipv4.dhcp-client-id=stable.
-	 *
-	 * Note that also the interface name of the activating connection and a
-	 * per-host secret key is included into the address generation so that the
-	 * same stable-id on different hosts/devices yields different addresses.
+	 * and wifi.cloned-mac-address=stable. Note that also the interface name
+	 * of the activating connection and a per-host secret key is included
+	 * into the address generation so that the same stable-id on different
+	 * hosts/devices yields different addresses.
 	 *
 	 * If the value is unset, an ID unique for the connection is used.
 	 * Specifying a stable-id allows multiple connections to generate the
@@ -1682,10 +1641,6 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 	 * NetworkManager when the resources for the connection are available.
 	 * %TRUE to automatically activate the connection, %FALSE to require manual
 	 * intervention to activate the connection.
-	 *
-	 * Note that autoconnect is not implemented for VPN profiles. See
-	 * #NMSettingConnection:secondaries as an alternative to automatically
-	 * connect VPN profiles.
 	 **/
 	/* ---ifcfg-rh---
 	 * property: autoconnect
@@ -1878,9 +1833,7 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 	 *
 	 * Whether or not slaves of this connection should be automatically brought up
 	 * when NetworkManager activates this connection. This only has a real effect
-	 * for master connections. The properties #NMSettingConnection:autoconnect,
-	 * #NMSettingConnection:autoconnect-priority and #NMSettingConnection:autoconnect-retries
-	 * are unrelated to this setting.
+	 * for master connections.
 	 * The permitted values are: 0: leave slave connections untouched,
 	 * 1: activate all the slave connections with this connection, -1: default.
 	 * If -1 (default) is set, global connection.autoconnect-slaves is read to
@@ -2029,37 +1982,4 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
 		                   G_PARAM_CONSTRUCT |
 		                   NM_SETTING_PARAM_FUZZY_IGNORE |
 		                   G_PARAM_STATIC_STRINGS));
-
-	/**
-	 * NMSettingConnection:mdns:
-	 *
-	 * Whether mDNS is enabled for the connection.
-	 *
-	 * The permitted values are: yes: register hostname and resolving
-	 * for the connection, no: disable mDNS for the interface, resolve:
-	 * do not register hostname but allow resolving of mDNS host names.
-	 * When updating this property on a currently activated connection,
-	 * the change takes effect immediately.
-	 *
-	 * This feature requires a plugin which supports mDNS. One such
-	 * plugin is dns-systemd-resolved.
-	 *
-	 * Since: 1.12
-	 **/
-	/* ---ifcfg-rh---
-	 * property: mdns
-	 * variable: CONNECTION_MDNS(+)
-	 * values: yes,no,resolve
-	 * default: missing variable means global default
-	 * description: Whether or not mDNS is enabled for the connection
-	 * example: CONNECTION_MDNS=yes
-	 * ---end---
-	 */
-	g_object_class_install_property
-		(object_class, PROP_MDNS,
-		 g_param_spec_int (NM_SETTING_CONNECTION_MDNS, "", "",
-		                   G_MININT32, G_MAXINT32,
-		                   NM_SETTING_CONNECTION_MDNS_DEFAULT,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
 }
diff --git a/libnm-core/nm-setting-connection.h b/libnm-core/nm-setting-connection.h
index cf083a48..02a7f5b1 100644
--- a/libnm-core/nm-setting-connection.h
+++ b/libnm-core/nm-setting-connection.h
@@ -64,7 +64,6 @@ G_BEGIN_DECLS
 #define NM_SETTING_CONNECTION_METERED        "metered"
 #define NM_SETTING_CONNECTION_LLDP           "lldp"
 #define NM_SETTING_CONNECTION_AUTH_RETRIES   "auth-retries"
-#define NM_SETTING_CONNECTION_MDNS           "mdns"
 
 /* Types for property values */
 /**
@@ -99,24 +98,6 @@ typedef enum {
 } NMSettingConnectionLldp;
 
 /**
- * NMSettingConnectionMdns:
- * @NM_SETTING_CONNECTION_MDNS_DEFAULT: default value
- * @NM_SETTING_CONNECTION_MDNS_NO: disable mDNS
- * @NM_SETTING_CONNECTION_MDNS_RESOLVE: support only resolving, do not register hostname
- * @NM_SETTING_CONNECTION_MDNS_YES: enable mDNS
- *
- * #NMSettingConnectionMdns values indicate whether mDNS should be enabled.
- *
- * Since: 1.12
- */
-typedef enum {
-	NM_SETTING_CONNECTION_MDNS_DEFAULT      = -1,
-	NM_SETTING_CONNECTION_MDNS_NO           = 0,
-	NM_SETTING_CONNECTION_MDNS_RESOLVE      = 1,
-	NM_SETTING_CONNECTION_MDNS_YES          = 2,
-} NMSettingConnectionMdns;
-
-/**
  * NMSettingConnection:
  *
  * General Connection Profile Settings
@@ -189,8 +170,6 @@ NMSettingConnectionLldp nm_setting_connection_get_lldp (NMSettingConnection *set
 NM_AVAILABLE_IN_1_10
 gint        nm_setting_connection_get_auth_retries     (NMSettingConnection *setting);
 
-NM_AVAILABLE_IN_1_12
-NMSettingConnectionMdns   nm_setting_connection_get_mdns (NMSettingConnection *setting);
 G_END_DECLS
 
 #endif /* __NM_SETTING_CONNECTION_H__ */
diff --git a/libnm-core/nm-setting-dcb.c b/libnm-core/nm-setting-dcb.c
index 491d9151..24d461a1 100644
--- a/libnm-core/nm-setting-dcb.c
+++ b/libnm-core/nm-setting-dcb.c
@@ -1099,11 +1099,13 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 		                     G_PARAM_STATIC_STRINGS));
 
 	/**
-	 * NMSettingDcb:priority-flow-control: (type GArray(gboolean))
+	 * NMSettingDcb:priority-flow-control:
 	 *
 	 * An array of 8 boolean values, where the array index corresponds to the User
 	 * Priority (0 - 7) and the value indicates whether or not the corresponding
 	 * priority should transmit priority pause.
+	 *
+	 * Element-type: gboolean
 	 **/
 	/* ---ifcfg-rh---
 	 * property: priority-flow-control
@@ -1147,11 +1149,13 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 		                     G_PARAM_STATIC_STRINGS));
 
 	/**
-	 * NMSettingDcb:priority-group-id: (type GArray(guint))
+	 * NMSettingDcb:priority-group-id:
 	 *
 	 * An array of 8 uint values, where the array index corresponds to the User
 	 * Priority (0 - 7) and the value indicates the Priority Group ID.  Allowed
 	 * Priority Group ID values are 0 - 7 or 15 for the unrestricted group.
+	 *
+	 * Element-type: guint
 	 **/
 	/* ---ifcfg-rh---
 	 * property: priority-group-id
@@ -1173,12 +1177,14 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 	                                      _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
-	 * NMSettingDcb:priority-group-bandwidth: (type GArray(guint))
+	 * NMSettingDcb:priority-group-bandwidth:
 	 *
 	 * An array of 8 uint values, where the array index corresponds to the
 	 * Priority Group ID (0 - 7) and the value indicates the percentage of link
 	 * bandwidth allocated to that group.  Allowed values are 0 - 100, and the
 	 * sum of all values must total 100 percents.
+	 *
+	 * Element-type: guint
 	 **/
 	/* ---ifcfg-rh---
 	 * property: priority-group-bandwidth
@@ -1199,13 +1205,15 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 	                                      _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
-	 * NMSettingDcb:priority-bandwidth: (type GArray(guint))
+	 * NMSettingDcb:priority-bandwidth:
 	 *
 	 * An array of 8 uint values, where the array index corresponds to the User
 	 * Priority (0 - 7) and the value indicates the percentage of bandwidth of
 	 * the priority's assigned group that the priority may use.  The sum of all
 	 * percentages for priorities which belong to the same group must total 100
 	 * percents.
+	 *
+	 * Element-type: guint
 	 **/
 	/* ---ifcfg-rh---
 	 * property: priority-bandwidth
@@ -1227,11 +1235,13 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 	                                      _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
-	 * NMSettingDcb:priority-strict-bandwidth: (type GArray(gboolean))
+	 * NMSettingDcb:priority-strict-bandwidth:
 	 *
 	 * An array of 8 boolean values, where the array index corresponds to the User
 	 * Priority (0 - 7) and the value indicates whether or not the priority may
 	 * use all of the bandwidth allocated to its assigned group.
+	 *
+	 * Element-type: gboolean
 	 **/
 	/* ---ifcfg-rh---
 	 * property: priority-strict-bandwidth
@@ -1253,11 +1263,13 @@ nm_setting_dcb_class_init (NMSettingDcbClass *setting_class)
 	                                      _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
-	 * NMSettingDcb:priority-traffic-class: (type GArray(guint))
+	 * NMSettingDcb:priority-traffic-class:
 	 *
 	 * An array of 8 uint values, where the array index corresponds to the User
 	 * Priority (0 - 7) and the value indicates the traffic class (0 - 7) to
 	 * which the priority is mapped.
+	 *
+	 * Element-type: guint
 	 **/
 	/* ---ifcfg-rh---
 	 * property: priority-traffic-class
diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c
index a695051f..779d4428 100644
--- a/libnm-core/nm-setting-ip-config.c
+++ b/libnm-core/nm-setting-ip-config.c
@@ -526,14 +526,6 @@ nm_ip_address_set_prefix (NMIPAddress *address,
 	address->prefix = prefix;
 }
 
-const char **
-_nm_ip_address_get_attribute_names (const NMIPAddress *address, gboolean sorted, guint *out_length)
-{
-	nm_assert (address);
-
-	return nm_utils_strdict_get_keys (address->attributes, sorted, out_length);
-}
-
 /**
  * nm_ip_address_get_attribute_names:
  * @address: the #NMIPAddress
@@ -545,12 +537,22 @@ _nm_ip_address_get_attribute_names (const NMIPAddress *address, gboolean sorted,
 char **
 nm_ip_address_get_attribute_names (NMIPAddress *address)
 {
-	const char **names;
+	GHashTableIter iter;
+	const char *key;
+	GPtrArray *names;
+
+	g_return_val_if_fail (address != NULL, NULL);
+
+	names = g_ptr_array_new ();
 
-	g_return_val_if_fail (address, NULL);
+	if (address->attributes) {
+		g_hash_table_iter_init (&iter, address->attributes);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL))
+			g_ptr_array_add (names, g_strdup (key));
+	}
+	g_ptr_array_add (names, NULL);
 
-	names = _nm_ip_address_get_attribute_names (address, TRUE, NULL);
-	return nm_utils_strv_make_deep_copied_nonnull (names);
+	return (char **) g_ptr_array_free (names, FALSE);
 }
 
 /**
@@ -591,7 +593,7 @@ nm_ip_address_set_attribute (NMIPAddress *address, const char *name, GVariant *v
 	g_return_if_fail (strcmp (name, "address") != 0 && strcmp (name, "prefix") != 0);
 
 	if (!address->attributes) {
-		address->attributes = g_hash_table_new_full (nm_str_hash, g_str_equal,
+		address->attributes = g_hash_table_new_full (g_str_hash, g_str_equal,
 		                                             g_free, (GDestroyNotify) g_variant_unref);
 	}
 
@@ -1129,14 +1131,33 @@ _nm_ip_route_get_attributes_direct (NMIPRoute *route)
  *
  * Returns: (array length=out_length) (transfer container): a %NULL-terminated array
  *   of attribute names or %NULL if there are no attributes. The order of the returned
- *   names depends on @sorted.
+ *   names is undefined.
  **/
 const char **
 _nm_ip_route_get_attribute_names (const NMIPRoute *route, gboolean sorted, guint *out_length)
 {
-	nm_assert (route);
+	const char **names;
+	guint length;
+
+	g_return_val_if_fail (route != NULL, NULL);
+
+	if (   !route->attributes
+	    || !g_hash_table_size (route->attributes)) {
+		NM_SET_OUT (out_length, 0);
+		return NULL;
+	}
 
-	return nm_utils_strdict_get_keys (route->attributes, sorted, out_length);
+	names = (const char **) g_hash_table_get_keys_as_array (route->attributes, &length);
+	if (   sorted
+	    && length > 1) {
+		g_qsort_with_data (names,
+		                   length,
+		                   sizeof (char *),
+		                   nm_strcmp_p_with_data,
+		                   NULL);
+	}
+	NM_SET_OUT (out_length, length);
+	return names;
 }
 
 /**
@@ -1150,12 +1171,21 @@ _nm_ip_route_get_attribute_names (const NMIPRoute *route, gboolean sorted, guint
 char **
 nm_ip_route_get_attribute_names (NMIPRoute *route)
 {
-	const char **names;
+	char **names;
+	guint i, len;
 
 	g_return_val_if_fail (route != NULL, NULL);
 
-	names = _nm_ip_route_get_attribute_names (route, TRUE, NULL);
-	return nm_utils_strv_make_deep_copied_nonnull (names);
+	names = (char **) _nm_ip_route_get_attribute_names (route, TRUE, &len);
+	if (!names)
+		return g_new0 (char *, 1);
+
+	nm_assert (len > 0 && names && names[len] == NULL);
+	for (i = 0; i < len; i++) {
+		nm_assert (names[i]);
+		names[i] = g_strdup (names[i]);
+	}
+	return names;
 }
 
 /**
@@ -1197,7 +1227,7 @@ nm_ip_route_set_attribute (NMIPRoute *route, const char *name, GVariant *value)
 	                  && strcmp (name, "next-hop") != 0 && strcmp (name, "metric") != 0);
 
 	if (!route->attributes) {
-		route->attributes = g_hash_table_new_full (nm_str_hash, g_str_equal,
+		route->attributes = g_hash_table_new_full (g_str_hash, g_str_equal,
 		                                           g_free, (GDestroyNotify) g_variant_unref);
 	}
 
@@ -2523,7 +2553,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 			return FALSE;
 		}
 
-		label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+		label = nm_ip_address_get_attribute (addr, "label");
 		if (label) {
 			if (!g_variant_is_of_type (label, G_VARIANT_TYPE_STRING)) {
 				g_set_error (error,
@@ -2930,10 +2960,7 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
 	/**
 	 * NMSettingIPConfig:dns-search:
 	 *
-	 * Array of DNS search domains. Domains starting with a tilde ('~')
-	 * are considered 'routing' domains and are used only to decide the
-	 * interface over which a query must be forwarded; they are not used
-	 * to complete unqualified host names.
+	 * Array of DNS search domains.
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_DNS_SEARCH,
@@ -2993,9 +3020,11 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
 	                       G_PARAM_STATIC_STRINGS));
 
 	/**
-	 * NMSettingIPConfig:addresses: (type GPtrArray(NMIPAddress))
+	 * NMSettingIPConfig:addresses:
 	 *
 	 * Array of IP addresses.
+	 *
+	 * Element-Type: NMIPAddress
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_ADDRESSES,
@@ -3032,9 +3061,11 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
 	                                     NULL);
 
 	/**
-	 * NMSettingIPConfig:routes: (type GPtrArray(NMIPRoute))
+	 * NMSettingIPConfig:routes:
 	 *
 	 * Array of IP routes.
+	 *
+	 * Element-Type: NMIPRoute
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_ROUTES,
@@ -3201,11 +3232,9 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
 	 * addresses on the network.  If an address conflict is detected, the
 	 * activation will fail.  A zero value means that no duplicate address
 	 * detection is performed, -1 means the default value (either configuration
-	 * ipvx.dad-timeout override or zero).  A value greater than zero is a
+	 * ipvx.dad-timeout override or 3 seconds).  A value greater than zero is a
 	 * timeout in milliseconds.
 	 *
-	 * The property is currently implemented only for IPv4.
-	 *
 	 * Since: 1.2
 	 **/
 	g_object_class_install_property
diff --git a/libnm-core/nm-setting-ip-config.h b/libnm-core/nm-setting-ip-config.h
index 69b9a6d2..96e61bb5 100644
--- a/libnm-core/nm-setting-ip-config.h
+++ b/libnm-core/nm-setting-ip-config.h
@@ -32,8 +32,6 @@
 
 G_BEGIN_DECLS
 
-#define NM_IP_ADDRESS_ATTRIBUTE_LABEL        "label"
-
 typedef struct NMIPAddress NMIPAddress;
 
 GType        nm_ip_address_get_type            (void);
diff --git a/libnm-core/nm-setting-ip-tunnel.c b/libnm-core/nm-setting-ip-tunnel.c
index c9cb97df..fa8d4a7a 100644
--- a/libnm-core/nm-setting-ip-tunnel.c
+++ b/libnm-core/nm-setting-ip-tunnel.c
@@ -49,7 +49,6 @@ typedef struct {
 	guint encapsulation_limit;
 	guint flow_label;
 	guint mtu;
-	guint32 flags;
 } NMSettingIPTunnelPrivate;
 
 enum {
@@ -66,7 +65,6 @@ enum {
 	PROP_ENCAPSULATION_LIMIT,
 	PROP_FLOW_LABEL,
 	PROP_MTU,
-	PROP_FLAGS,
 
 	LAST_PROP
 };
@@ -287,24 +285,6 @@ nm_setting_ip_tunnel_get_mtu (NMSettingIPTunnel *setting)
 	return NM_SETTING_IP_TUNNEL_GET_PRIVATE (setting)->mtu;
 }
 
-/*
- * nm_setting_ip_tunnel_get_flags:
- * @setting: the #NMSettingIPTunnel
- *
- * Returns the #NMSettingIPTunnel:flags property of the setting.
- *
- * Returns: the tunnel flags
- *
- * Since: 1.12
- **/
-NMIPTunnelFlags
-nm_setting_ip_tunnel_get_flags (NMSettingIPTunnel *setting)
-{
-	g_return_val_if_fail (NM_IS_SETTING_IP_TUNNEL (setting), NM_IP_TUNNEL_FLAG_NONE);
-
-	return NM_SETTING_IP_TUNNEL_GET_PRIVATE (setting)->flags;
-}
-
 /*****************************************************************************/
 
 static gboolean
@@ -312,7 +292,6 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSettingIPTunnelPrivate *priv = NM_SETTING_IP_TUNNEL_GET_PRIVATE (setting);
 	int family = AF_UNSPEC;
-	guint32 flags;
 
 	switch (priv->mode) {
 	case NM_IP_TUNNEL_MODE_IPIP:
@@ -440,20 +419,6 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	flags = priv->flags;
-	if (NM_IN_SET (priv->mode, NM_IP_TUNNEL_MODE_IPIP6, NM_IP_TUNNEL_MODE_IP6IP6))
-		flags &= (guint32) (~_NM_IP_TUNNEL_FLAG_ALL_IP6TNL);
-	if (flags) {
-		g_set_error (error,
-		             NM_CONNECTION_ERROR,
-		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		             _("some flags are invalid for the select mode: %s"),
-		             nm_utils_enum_to_str (nm_ip_tunnel_flags_get_type (), flags));
-		g_prefix_error (error, "%s.%s: ", NM_SETTING_IP_TUNNEL_SETTING_NAME,
-		                NM_SETTING_IP_TUNNEL_FLAGS);
-		return FALSE;
-	}
-
 	return TRUE;
 }
 
@@ -526,9 +491,6 @@ set_property (GObject *object, guint prop_id,
 	case PROP_MTU:
 		priv->mtu = g_value_get_uint (value);
 		break;
-	case PROP_FLAGS:
-		priv->flags = g_value_get_uint (value);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -579,9 +541,6 @@ get_property (GObject *object, guint prop_id,
 	case PROP_MTU:
 		g_value_set_uint (value, priv->mtu);
 		break;
-	case PROP_FLAGS:
-		g_value_set_uint (value, priv->flags);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -814,23 +773,4 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *setting_class)
 		                    G_PARAM_CONSTRUCT |
 		                    NM_SETTING_PARAM_FUZZY_IGNORE |
 		                    G_PARAM_STATIC_STRINGS));
-
-	/**
-	 * NMSettingIPTunnel:flags:
-	 *
-	 * Tunnel flags. Currently the following values are supported:
-	 * %NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT, %NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS,
-	 * %NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL, %NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV,
-	 * %NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY, %NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK.
-	 * They are valid only for IPv6 tunnels.
-	 *
-	 * Since: 1.12
-	 **/
-	g_object_class_install_property
-		(object_class, PROP_FLAGS,
-		 g_param_spec_uint (NM_SETTING_IP_TUNNEL_FLAGS, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
 }
diff --git a/libnm-core/nm-setting-ip-tunnel.h b/libnm-core/nm-setting-ip-tunnel.h
index efa754a8..dfa62116 100644
--- a/libnm-core/nm-setting-ip-tunnel.h
+++ b/libnm-core/nm-setting-ip-tunnel.h
@@ -50,7 +50,6 @@ G_BEGIN_DECLS
 #define NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT "encapsulation-limit"
 #define NM_SETTING_IP_TUNNEL_FLOW_LABEL          "flow-label"
 #define NM_SETTING_IP_TUNNEL_MTU                 "mtu"
-#define NM_SETTING_IP_TUNNEL_FLAGS               "flags"
 
 /**
  * NMSettingIPTunnel:
@@ -68,33 +67,6 @@ typedef struct {
 	gpointer padding[4];
 } NMSettingIPTunnelClass;
 
-/*
- * NMIPTunnelFlags:
- * @NM_IP_TUNNEL_FLAG_NONE: no flag
- * @NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT: don't add encapsulation limit
- *     if one isn't present in inner packet
- * @NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS: copy the traffic class field
- *     from the inner packet
- * @NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL: copy the flowlabel from the
- *     inner packet
- * @NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV: used for Mobile IPv6
- * @NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY: copy DSCP from the outer packet
- * @NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK: copy fwmark from inner packet
- *
- * IP tunnel flags.
- *
- * Since: 1.12
- */
-typedef enum { /*< flags, prefix=NM_IP_TUNNEL_FLAG >*/
-	NM_IP_TUNNEL_FLAG_NONE                           = 0x0,
-	NM_IP_TUNNEL_FLAG_IP6_IGN_ENCAP_LIMIT            = 0x1,
-	NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_TCLASS            = 0x2,
-	NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FLOWLABEL         = 0x4,
-	NM_IP_TUNNEL_FLAG_IP6_MIP6_DEV                   = 0x8,
-	NM_IP_TUNNEL_FLAG_IP6_RCV_DSCP_COPY              = 0x10,
-	NM_IP_TUNNEL_FLAG_IP6_USE_ORIG_FWMARK            = 0x20,
-} NMIPTunnelFlags;
-
 NM_AVAILABLE_IN_1_2
 GType nm_setting_ip_tunnel_get_type (void);
 
@@ -125,8 +97,6 @@ NM_AVAILABLE_IN_1_2
 guint nm_setting_ip_tunnel_get_flow_label (NMSettingIPTunnel *setting);
 NM_AVAILABLE_IN_1_2
 guint nm_setting_ip_tunnel_get_mtu (NMSettingIPTunnel *setting);
-NM_AVAILABLE_IN_1_12
-NMIPTunnelFlags nm_setting_ip_tunnel_get_flags (NMSettingIPTunnel *setting);
 
 G_END_DECLS
 
diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c
index 8eadd0f7..d9c0cbb1 100644
--- a/libnm-core/nm-setting-ip4-config.c
+++ b/libnm-core/nm-setting-ip4-config.c
@@ -363,7 +363,7 @@ ip4_addresses_set (NMSetting  *setting,
 	if (g_variant_lookup (s_ip4, "address-labels", "^as", &labels)) {
 		for (i = 0; i < addrs->len && labels[i]; i++)
 			if (*labels[i])
-				nm_ip_address_set_attribute (addrs->pdata[i], NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string (labels[i]));
+				nm_ip_address_set_attribute (addrs->pdata[i], "label", g_variant_new_string (labels[i]));
 		g_strfreev (labels);
 	}
 	g_variant_unref (s_ip4);
@@ -391,7 +391,7 @@ ip4_address_labels_get (NMSetting    *setting,
 	num_addrs = nm_setting_ip_config_get_num_addresses (s_ip);
 	for (i = 0; i < num_addrs; i++) {
 		NMIPAddress *addr = nm_setting_ip_config_get_address (s_ip, i);
-		GVariant *label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+		GVariant *label = nm_ip_address_get_attribute (addr, "label");
 
 		if (label) {
 			have_labels = TRUE;
@@ -404,7 +404,7 @@ ip4_address_labels_get (NMSetting    *setting,
 	labels = g_ptr_array_sized_new (num_addrs);
 	for (i = 0; i < num_addrs; i++) {
 		NMIPAddress *addr = nm_setting_ip_config_get_address (s_ip, i);
-		GVariant *label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+		GVariant *label = nm_ip_address_get_attribute (addr, "label");
 
 		g_ptr_array_add (labels, (char *) (label ? g_variant_get_string (label, NULL) : ""));
 	}
@@ -719,24 +719,13 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
 	 * ARP type and the rest is a MAC address).
 	 * If the property is not a hex string it is considered as a
 	 * non-hardware-address client ID and the 'type' field is set to 0.
-	 *
-	 * The special values "mac" and "perm-mac" are supported, which use the
-	 * current or permanent MAC address of the device to generate a client identifier
-	 * with type ethernet type (01). Currently, these options only work for ethernet
-	 * type of links.
-	 *
-	 * The special value "stable" is supported to generate a type 0 client identifier based
-	 * on the stable-id (see connection.stable-id).
-	 *
-	 * If unset, a globally configured default is used. If still unset, the
-	 * client-id from the last lease is reused.
 	 **/
 	/* ---ifcfg-rh---
 	 * property: dhcp-client-id
 	 * variable: DHCP_CLIENT_ID(+)
 	 * description: A string sent to the DHCP server to identify the local machine.
 	 *    A binary value can be specified using hex notation ('aa:bb:cc').
-	 * example: DHCP_CLIENT_ID=ax-srv-1; DHCP_CLIENT_ID=01:44:44:44:44:44:44
+	 * example: DHCP_CLIENT_ID=ax-srv-1; DHCP_CLIENT_ID=01:44:44:44:44:44:44"
 	 * ---end---
 	 */
 	g_object_class_install_property
@@ -748,12 +737,11 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
 
 	/* ---ifcfg-rh---
 	 * property: dad-timeout
-	 * variable: ACD_TIMEOUT, ARPING_WAIT
-	 * default: missing variable means global default (config override or zero)
-	 * description: Timeout (in milliseconds for ACD_TIMEOUT or in seconds
-	 *   for ARPING_WAIT) for address conflict detection before configuring
-	 *   IPv4 addresses. 0 turns off the ACD completely, -1 means default value.
-	 * example: ACD_TIMEOUT=2000 or ARPING_WAIT=2
+	 * variable: ARPING_WAIT
+	 * default: missing variable means global default (config override or 3)
+	 * description: Timeout (in seconds) for performing DAD before configuring
+	 * IPv4 addresses. 0 turns off the DAD completely, -1 means default value.
+	 * example: ARPING_WAIT=2
 	 * ---end---
 	 */
 
diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c
index ca3e3c51..00e9391d 100644
--- a/libnm-core/nm-setting-ip6-config.c
+++ b/libnm-core/nm-setting-ip6-config.c
@@ -784,13 +784,13 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
 	 * example: IPV6_TOKEN=::53
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_TOKEN,
-		 g_param_spec_string (NM_SETTING_IP6_CONFIG_TOKEN, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+        g_object_class_install_property
+                (object_class, PROP_TOKEN,
+                 g_param_spec_string (NM_SETTING_IP6_CONFIG_TOKEN, "", "",
+                                      NULL,
+                                      G_PARAM_READWRITE |
+                                      NM_SETTING_PARAM_INFERRABLE |
+                                      G_PARAM_STATIC_STRINGS));
 
 
 	/* IP6-specific property overrides */
diff --git a/libnm-core/nm-setting-macvlan.c b/libnm-core/nm-setting-macvlan.c
index 6fea8b0d..d684e58d 100644
--- a/libnm-core/nm-setting-macvlan.c
+++ b/libnm-core/nm-setting-macvlan.c
@@ -147,12 +147,16 @@ static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSettingMacvlanPrivate *priv = NM_SETTING_MACVLAN_GET_PRIVATE (setting);
+	NMSettingConnection *s_con;
 	NMSettingWired *s_wired;
 
-	if (connection)
+	if (connection) {
+		s_con = nm_connection_get_setting_connection (connection);
 		s_wired = nm_connection_get_setting_wired (connection);
-	else
+	} else {
+		s_con = NULL;
 		s_wired = NULL;
+	}
 
 	if (priv->parent) {
 		if (   !nm_utils_is_uuid (priv->parent)
diff --git a/libnm-core/nm-setting-ovs-patch.c b/libnm-core/nm-setting-ovs-patch.c
index 3689aecf..9e380914 100644
--- a/libnm-core/nm-setting-ovs-patch.c
+++ b/libnm-core/nm-setting-ovs-patch.c
@@ -83,6 +83,7 @@ static int
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSettingOvsPatch *self = NM_SETTING_OVS_PATCH (setting);
+	int family = AF_UNSPEC;
 
 	if (!_nm_connection_verify_required_interface_name (connection, error))
 		return FALSE;
@@ -98,8 +99,11 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
-	if (   !nm_utils_ipaddr_valid (AF_INET, self->peer)
-	    && !nm_utils_ipaddr_valid (AF_INET6, self->peer)) {
+	if (nm_utils_ipaddr_valid (AF_INET, self->peer))
+		family = AF_INET;
+	else if (nm_utils_ipaddr_valid (AF_INET6, self->peer))
+		family = AF_INET6;
+	else {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
 		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
diff --git a/libnm-core/nm-setting-private.h b/libnm-core/nm-setting-private.h
index 6c50178d..30e2a160 100644
--- a/libnm-core/nm-setting-private.h
+++ b/libnm-core/nm-setting-private.h
@@ -21,10 +21,6 @@
 #ifndef __NM_SETTING_PRIVATE_H__
 #define __NM_SETTING_PRIVATE_H__
 
-#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-#error Cannot use this header.
-#endif
-
 #include "nm-setting.h"
 #include "nm-connection.h"
 #include "nm-core-enum-types.h"
@@ -89,7 +85,7 @@ gboolean _nm_setting_clear_secrets_with_flags (NMSetting *setting,
 /* Ensure the setting's GType is registered at library load time */
 #define NM_SETTING_REGISTER_TYPE(x) \
 static void __attribute__((constructor)) register_setting (void) \
-{ g_type_ensure (x); }
+{ nm_g_type_init (); g_type_ensure (x); }
 
 GVariant *_nm_setting_get_deprecated_virtual_interface_name (NMSetting *setting,
                                                              NMConnection *connection,
diff --git a/libnm-core/nm-setting-tc-config.c b/libnm-core/nm-setting-tc-config.c
index 0fea8868..a4960038 100644
--- a/libnm-core/nm-setting-tc-config.c
+++ b/libnm-core/nm-setting-tc-config.c
@@ -52,7 +52,7 @@ struct NMTCQdisc {
  *
  * Returns: (transfer full): the new #NMTCQdisc object, or %NULL on error
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTCQdisc *
 nm_tc_qdisc_new (const char *kind,
@@ -92,7 +92,7 @@ nm_tc_qdisc_new (const char *kind,
  *
  * Increases the reference count of the object.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_tc_qdisc_ref (NMTCQdisc *qdisc)
@@ -110,7 +110,7 @@ nm_tc_qdisc_ref (NMTCQdisc *qdisc)
  * Decreases the reference count of the object.  If the reference count
  * reaches zero, the object will be destroyed.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_tc_qdisc_unref (NMTCQdisc *qdisc)
@@ -135,7 +135,7 @@ nm_tc_qdisc_unref (NMTCQdisc *qdisc)
  *
  * Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_tc_qdisc_equal (NMTCQdisc *qdisc, NMTCQdisc *other)
@@ -162,7 +162,7 @@ nm_tc_qdisc_equal (NMTCQdisc *qdisc, NMTCQdisc *other)
  *
  * Returns: (transfer full): a copy of @qdisc
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTCQdisc *
 nm_tc_qdisc_dup (NMTCQdisc *qdisc)
@@ -184,7 +184,7 @@ nm_tc_qdisc_dup (NMTCQdisc *qdisc)
  *
  * Returns:
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 const char *
 nm_tc_qdisc_get_kind (NMTCQdisc *qdisc)
@@ -201,7 +201,7 @@ nm_tc_qdisc_get_kind (NMTCQdisc *qdisc)
  *
  * Returns: the queueing discipline handle
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 guint32
 nm_tc_qdisc_get_handle (NMTCQdisc *qdisc)
@@ -219,7 +219,7 @@ nm_tc_qdisc_get_handle (NMTCQdisc *qdisc)
  *
  * Sets the queueing discipline handle.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_tc_qdisc_set_handle (NMTCQdisc *qdisc, guint32 handle)
@@ -236,7 +236,7 @@ nm_tc_qdisc_set_handle (NMTCQdisc *qdisc, guint32 handle)
  *
  * Returns: the parent class
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 guint32
 nm_tc_qdisc_get_parent (NMTCQdisc *qdisc)
@@ -268,7 +268,7 @@ struct NMTCAction {
  *
  * Returns: (transfer full): the new #NMTCAction object, or %NULL on error
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTCAction *
 nm_tc_action_new (const char *kind,
@@ -298,7 +298,7 @@ nm_tc_action_new (const char *kind,
  *
  * Increases the reference count of the object.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_tc_action_ref (NMTCAction *action)
@@ -316,7 +316,7 @@ nm_tc_action_ref (NMTCAction *action)
  * Decreases the reference count of the object.  If the reference count
  * reaches zero, the object will be destroyed.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_tc_action_unref (NMTCAction *action)
@@ -343,7 +343,7 @@ nm_tc_action_unref (NMTCAction *action)
  *
  * Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_tc_action_equal (NMTCAction *action, NMTCAction *other)
@@ -389,7 +389,7 @@ nm_tc_action_equal (NMTCAction *action, NMTCAction *other)
  *
  * Returns: (transfer full): a copy of @action
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTCAction *
 nm_tc_action_dup (NMTCAction *action)
@@ -420,7 +420,7 @@ nm_tc_action_dup (NMTCAction *action)
  *
  * Returns:
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 const char *
 nm_tc_action_get_kind (NMTCAction *action)
@@ -442,12 +442,22 @@ nm_tc_action_get_kind (NMTCAction *action)
 char **
 nm_tc_action_get_attribute_names (NMTCAction *action)
 {
-	const char **names;
+	GHashTableIter iter;
+	const char *key;
+	GPtrArray *names;
+
+	g_return_val_if_fail (action != NULL, NULL);
+
+	names = g_ptr_array_new ();
 
-	g_return_val_if_fail (action, NULL);
+	if (action->attributes) {
+		g_hash_table_iter_init (&iter, action->attributes);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL))
+			g_ptr_array_add (names, g_strdup (key));
+	}
+	g_ptr_array_add (names, NULL);
 
-	names = nm_utils_strdict_get_keys (action->attributes, TRUE, NULL);
-	return nm_utils_strv_make_deep_copied_nonnull (names);
+	return (char **) g_ptr_array_free (names, FALSE);
 }
 
 /**
@@ -488,8 +498,8 @@ nm_tc_action_set_attribute (NMTCAction *action, const char *name, GVariant *valu
 	g_return_if_fail (strcmp (name, "kind") != 0);
 
 	if (!action->attributes) {
-		action->attributes = g_hash_table_new_full (nm_str_hash, g_str_equal,
-		                                             g_free, (GDestroyNotify) g_variant_unref);
+		action->attributes = g_hash_table_new_full (g_str_hash, g_str_equal,
+		                                            g_free, (GDestroyNotify) g_variant_unref);
 	}
 
 	if (value)
@@ -521,7 +531,7 @@ struct NMTCTfilter {
  *
  * Returns: (transfer full): the new #NMTCTfilter object, or %NULL on error
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTCTfilter *
 nm_tc_tfilter_new (const char *kind,
@@ -561,7 +571,7 @@ nm_tc_tfilter_new (const char *kind,
  *
  * Increases the reference count of the object.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_tc_tfilter_ref (NMTCTfilter *tfilter)
@@ -579,7 +589,7 @@ nm_tc_tfilter_ref (NMTCTfilter *tfilter)
  * Decreases the reference count of the object.  If the reference count
  * reaches zero, the object will be destroyed.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_tc_tfilter_unref (NMTCTfilter *tfilter)
@@ -606,7 +616,7 @@ nm_tc_tfilter_unref (NMTCTfilter *tfilter)
  *
  * Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_tc_tfilter_equal (NMTCTfilter *tfilter, NMTCTfilter *other)
@@ -634,7 +644,7 @@ nm_tc_tfilter_equal (NMTCTfilter *tfilter, NMTCTfilter *other)
  *
  * Returns: (transfer full): a copy of @tfilter
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTCTfilter *
 nm_tc_tfilter_dup (NMTCTfilter *tfilter)
@@ -657,7 +667,7 @@ nm_tc_tfilter_dup (NMTCTfilter *tfilter)
  *
  * Returns:
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 const char *
 nm_tc_tfilter_get_kind (NMTCTfilter *tfilter)
@@ -674,7 +684,7 @@ nm_tc_tfilter_get_kind (NMTCTfilter *tfilter)
  *
  * Returns: the queueing discipline handle
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 guint32
 nm_tc_tfilter_get_handle (NMTCTfilter *tfilter)
@@ -692,7 +702,7 @@ nm_tc_tfilter_get_handle (NMTCTfilter *tfilter)
  *
  * Sets the queueing discipline handle.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_tc_tfilter_set_handle (NMTCTfilter *tfilter, guint32 handle)
@@ -709,7 +719,7 @@ nm_tc_tfilter_set_handle (NMTCTfilter *tfilter, guint32 handle)
  *
  * Returns: the parent class
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 guint32
 nm_tc_tfilter_get_parent (NMTCTfilter *tfilter)
@@ -726,7 +736,7 @@ nm_tc_tfilter_get_parent (NMTCTfilter *tfilter)
  *
  * Returns: the action associated with a traffic filter.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTCAction *
 nm_tc_tfilter_get_action (NMTCTfilter *tfilter)
@@ -747,7 +757,7 @@ nm_tc_tfilter_get_action (NMTCTfilter *tfilter)
  *
  * Sets the action associated with a traffic filter.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_tc_tfilter_set_action (NMTCTfilter *tfilter, NMTCAction *action)
@@ -777,7 +787,7 @@ enum {
  *
  * Linux Traffic Contril Settings.
  *
- * Since: 1.12
+ * Since: 1.10.2
  */
 struct _NMSettingTCConfig {
         NMSetting parent;
@@ -800,7 +810,7 @@ NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_TC_CONFIG)
  *
  * Returns: (transfer full): the new empty #NMSettingTCConfig object
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMSetting *
 nm_setting_tc_config_new (void)
@@ -814,7 +824,7 @@ nm_setting_tc_config_new (void)
  *
  * Returns: the number of configured queueing disciplines
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 guint
 nm_setting_tc_config_get_num_qdiscs (NMSettingTCConfig *self)
@@ -831,7 +841,7 @@ nm_setting_tc_config_get_num_qdiscs (NMSettingTCConfig *self)
  *
  * Returns: (transfer none): the qdisc at index @idx
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTCQdisc *
 nm_setting_tc_config_get_qdisc (NMSettingTCConfig *self, guint idx)
@@ -854,7 +864,7 @@ nm_setting_tc_config_get_qdisc (NMSettingTCConfig *self, guint idx)
  *
  * Returns: %TRUE if the qdisc was added; %FALSE if the qdisc was already known.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_tc_config_add_qdisc (NMSettingTCConfig *self,
@@ -882,7 +892,7 @@ nm_setting_tc_config_add_qdisc (NMSettingTCConfig *self,
  *
  * Removes the qdisc at index @idx.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_setting_tc_config_remove_qdisc (NMSettingTCConfig *self, guint idx)
@@ -904,7 +914,7 @@ nm_setting_tc_config_remove_qdisc (NMSettingTCConfig *self, guint idx)
  *
  * Returns: %TRUE if the qdisc was found and removed; %FALSE if it was not.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_tc_config_remove_qdisc_by_value (NMSettingTCConfig *self,
@@ -931,7 +941,7 @@ nm_setting_tc_config_remove_qdisc_by_value (NMSettingTCConfig *self,
  *
  * Removes all configured queueing disciplines.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_setting_tc_config_clear_qdiscs (NMSettingTCConfig *self)
@@ -949,7 +959,7 @@ nm_setting_tc_config_clear_qdiscs (NMSettingTCConfig *self)
  *
  * Returns: the number of configured queueing disciplines
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 guint
 nm_setting_tc_config_get_num_tfilters (NMSettingTCConfig *self)
@@ -966,7 +976,7 @@ nm_setting_tc_config_get_num_tfilters (NMSettingTCConfig *self)
  *
  * Returns: (transfer none): the tfilter at index @idx
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTCTfilter *
 nm_setting_tc_config_get_tfilter (NMSettingTCConfig *self, guint idx)
@@ -989,7 +999,7 @@ nm_setting_tc_config_get_tfilter (NMSettingTCConfig *self, guint idx)
  *
  * Returns: %TRUE if the tfilter was added; %FALSE if the tfilter was already known.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_tc_config_add_tfilter (NMSettingTCConfig *self,
@@ -1017,7 +1027,7 @@ nm_setting_tc_config_add_tfilter (NMSettingTCConfig *self,
  *
  * Removes the tfilter at index @idx.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_setting_tc_config_remove_tfilter (NMSettingTCConfig *self, guint idx)
@@ -1038,7 +1048,7 @@ nm_setting_tc_config_remove_tfilter (NMSettingTCConfig *self, guint idx)
  *
  * Returns: %TRUE if the tfilter was found and removed; %FALSE if it was not.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_tc_config_remove_tfilter_by_value (NMSettingTCConfig *self,
@@ -1065,7 +1075,7 @@ nm_setting_tc_config_remove_tfilter_by_value (NMSettingTCConfig *self,
  *
  * Removes all configured queueing disciplines.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_setting_tc_config_clear_tfilters (NMSettingTCConfig *self)
@@ -1319,7 +1329,7 @@ _action_to_variant (NMTCAction *action)
 
 	for (i = 0; attrs[i]; i++) {
 		g_variant_builder_add (&builder, "{sv}", attrs[i],
-		                       nm_tc_action_get_attribute (action, attrs[i]));
+				       nm_tc_action_get_attribute (action, attrs[i]));
 	}
 
 	return g_variant_builder_end (&builder);
@@ -1507,9 +1517,11 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class)
 	/* Properties */
 
 	/**
-	 * NMSettingTCConfig:qdiscs: (type GPtrArray(NMTCQdisc))
+	 * NMSettingTCConfig:qdiscs:
 	 *
 	 * Array of TC queuening disciplines.
+	 *
+	 * Element-Type: NMTCQdisc
 	 **/
 	/* ---ifcfg-rh---
 	 * property: qdiscs
@@ -1534,9 +1546,11 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *setting_class)
 	                                     NULL);
 
 	/**
-	 * NMSettingTCConfig:tfilters: (type GPtrArray(NMTCTfilter))
+	 * NMSettingTCConfig:tfilters:
 	 *
 	 * Array of TC traffic filters.
+	 *
+	 * Element-Type: NMTCTfilter
 	 **/
 	/* ---ifcfg-rh---
 	 * property: qdiscs
diff --git a/libnm-core/nm-setting-tc-config.h b/libnm-core/nm-setting-tc-config.h
index 4496046e..8117d5ea 100644
--- a/libnm-core/nm-setting-tc-config.h
+++ b/libnm-core/nm-setting-tc-config.h
@@ -30,103 +30,103 @@ G_BEGIN_DECLS
 
 typedef struct NMTCQdisc NMTCQdisc;
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 GType       nm_tc_qdisc_get_type             (void);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCQdisc  *nm_tc_qdisc_new                  (const char *kind,
                                               guint32 parent,
                                               GError **error);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void        nm_tc_qdisc_ref                  (NMTCQdisc *qdisc);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void        nm_tc_qdisc_unref                (NMTCQdisc *qdisc);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean    nm_tc_qdisc_equal                (NMTCQdisc *qdisc,
                                               NMTCQdisc *other);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCQdisc  *nm_tc_qdisc_dup                  (NMTCQdisc  *qdisc);
 
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 const char *nm_tc_qdisc_get_kind             (NMTCQdisc *qdisc);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 guint32     nm_tc_qdisc_get_handle           (NMTCQdisc *qdisc);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void        nm_tc_qdisc_set_handle           (NMTCQdisc *qdisc,
                                               guint32 handle);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 guint32     nm_tc_qdisc_get_parent           (NMTCQdisc *qdisc);
 
 typedef struct NMTCAction NMTCAction;
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 GType       nm_tc_action_get_type            (void);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCAction  *nm_tc_action_new                (const char *kind,
                                               GError **error);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void        nm_tc_action_ref                 (NMTCAction *action);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void        nm_tc_action_unref               (NMTCAction *action);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean    nm_tc_action_equal               (NMTCAction *action,
                                               NMTCAction *other);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCAction  *nm_tc_action_dup                (NMTCAction  *action);
 
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 const char *nm_tc_action_get_kind            (NMTCAction *action);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 char      **nm_tc_action_get_attribute_names (NMTCAction *action);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 GVariant   *nm_tc_action_get_attribute       (NMTCAction *action,
                                               const char *name);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void        nm_tc_action_set_attribute       (NMTCAction *action,
                                               const char *name,
                                               GVariant *value);
 
 typedef struct NMTCTfilter NMTCTfilter;
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 GType       nm_tc_tfilter_get_type           (void);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCTfilter  *nm_tc_tfilter_new              (const char *kind,
                                               guint32 parent,
                                               GError **error);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void        nm_tc_tfilter_ref                (NMTCTfilter *tfilter);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void        nm_tc_tfilter_unref              (NMTCTfilter *tfilter);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean    nm_tc_tfilter_equal              (NMTCTfilter *tfilter,
                                               NMTCTfilter *other);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCTfilter  *nm_tc_tfilter_dup              (NMTCTfilter  *tfilter);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 const char *nm_tc_tfilter_get_kind           (NMTCTfilter *tfilter);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 guint32     nm_tc_tfilter_get_handle         (NMTCTfilter *tfilter);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void        nm_tc_tfilter_set_handle         (NMTCTfilter *tfilter,
                                               guint32 handle);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 guint32     nm_tc_tfilter_get_parent         (NMTCTfilter *tfilter);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCAction *nm_tc_tfilter_get_action         (NMTCTfilter *tfilter);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void        nm_tc_tfilter_set_action         (NMTCTfilter *tfilter, NMTCAction *action);
 
 
@@ -146,41 +146,41 @@ typedef struct _NMSettingTCConfigClass NMSettingTCConfigClass;
 
 GType nm_setting_tc_config_get_type (void);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMSetting *nm_setting_tc_config_new (void);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 guint      nm_setting_tc_config_get_num_qdiscs          (NMSettingTCConfig *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCQdisc *nm_setting_tc_config_get_qdisc               (NMSettingTCConfig *setting,
                                                          guint idx);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean   nm_setting_tc_config_add_qdisc               (NMSettingTCConfig *setting,
                                                          NMTCQdisc *qdisc);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void       nm_setting_tc_config_remove_qdisc            (NMSettingTCConfig *setting,
                                                          guint idx);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean   nm_setting_tc_config_remove_qdisc_by_value   (NMSettingTCConfig *setting,
                                                          NMTCQdisc *qdisc);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void       nm_setting_tc_config_clear_qdiscs            (NMSettingTCConfig *setting);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 guint      nm_setting_tc_config_get_num_tfilters        (NMSettingTCConfig *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCTfilter *nm_setting_tc_config_get_tfilter           (NMSettingTCConfig *setting,
                                                          guint idx);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean   nm_setting_tc_config_add_tfilter             (NMSettingTCConfig *setting,
                                                          NMTCTfilter *tfilter);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void       nm_setting_tc_config_remove_tfilter          (NMSettingTCConfig *setting,
                                                          guint idx);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean   nm_setting_tc_config_remove_tfilter_by_value (NMSettingTCConfig *setting,
                                                          NMTCTfilter *tfilter);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void       nm_setting_tc_config_clear_tfilters          (NMSettingTCConfig *setting);
 
 G_END_DECLS
diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c
index ad954277..5ab8f691 100644
--- a/libnm-core/nm-setting-team-port.c
+++ b/libnm-core/nm-setting-team-port.c
@@ -114,7 +114,7 @@ nm_setting_team_port_get_config (NMSettingTeamPort *setting)
  *
  * Returns: the #NMSettingTeamPort:queue_id property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gint
 nm_setting_team_port_get_queue_id (NMSettingTeamPort *setting)
@@ -130,7 +130,7 @@ nm_setting_team_port_get_queue_id (NMSettingTeamPort *setting)
  *
  * Returns: the #NMSettingTeamPort:prio property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gint
 nm_setting_team_port_get_prio (NMSettingTeamPort *setting)
@@ -146,7 +146,7 @@ nm_setting_team_port_get_prio (NMSettingTeamPort *setting)
  *
  * Returns: the #NMSettingTeamPort:sticky property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_team_port_get_sticky (NMSettingTeamPort *setting)
@@ -162,7 +162,7 @@ nm_setting_team_port_get_sticky (NMSettingTeamPort *setting)
  *
  * Returns: the #NMSettingTeamPort:lacp-prio property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gint
 nm_setting_team_port_get_lacp_prio (NMSettingTeamPort *setting)
@@ -178,7 +178,7 @@ nm_setting_team_port_get_lacp_prio (NMSettingTeamPort *setting)
  *
  * Returns: the #NMSettingTeamPort:lacp-key property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gint
 nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting)
@@ -194,7 +194,7 @@ nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting)
  *
  * Returns: the number of configured link watchers
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 guint
 nm_setting_team_port_get_num_link_watchers (NMSettingTeamPort *setting)
@@ -213,7 +213,7 @@ nm_setting_team_port_get_num_link_watchers (NMSettingTeamPort *setting)
  *
  * Returns: (transfer none): the link watcher at index @idx.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTeamLinkWatcher *
 nm_setting_team_port_get_link_watcher (NMSettingTeamPort *setting, guint idx)
@@ -236,7 +236,7 @@ nm_setting_team_port_get_link_watcher (NMSettingTeamPort *setting, guint idx)
  * Returns: %TRUE if the link watcher is added; %FALSE if an identical link
  * watcher was already there.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_team_port_add_link_watcher (NMSettingTeamPort *setting,
@@ -265,7 +265,7 @@ nm_setting_team_port_add_link_watcher (NMSettingTeamPort *setting,
  *
  * Removes the link watcher at index #idx.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_setting_team_port_remove_link_watcher (NMSettingTeamPort *setting, guint idx)
@@ -288,7 +288,7 @@ nm_setting_team_port_remove_link_watcher (NMSettingTeamPort *setting, guint idx)
  *
  * Returns: %TRUE if the link watcher was found and removed, %FALSE otherwise.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_team_port_remove_link_watcher_by_value (NMSettingTeamPort *setting,
@@ -315,7 +315,7 @@ nm_setting_team_port_remove_link_watcher_by_value (NMSettingTeamPort *setting,
  *
  * Removes all configured link watchers.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_setting_team_port_clear_link_watchers (NMSettingTeamPort *setting)
@@ -630,7 +630,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
 	 * Corresponds to the teamd ports.PORTIFNAME.queue_id.
 	 * When set to -1 means the parameter is skipped from the json config.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_QUEUE_ID,
@@ -644,7 +644,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
 	 *
 	 * Corresponds to the teamd ports.PORTIFNAME.prio.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_PRIO,
@@ -658,7 +658,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
 	 *
 	 * Corresponds to the teamd ports.PORTIFNAME.sticky.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_STICKY,
@@ -672,7 +672,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
 	 *
 	 * Corresponds to the teamd ports.PORTIFNAME.lacp_prio.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_LACP_PRIO,
@@ -686,7 +686,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
 	 *
 	 * Corresponds to the teamd ports.PORTIFNAME.lacp_key.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_LACP_KEY,
@@ -696,7 +696,7 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
 		                   G_PARAM_STATIC_STRINGS));
 
 	/**
-	 * NMSettingTeamPort:link-watchers: (type GPtrArray(NMTeamLinkWatcher))
+	 * NMSettingTeamPort:link-watchers:
 	 *
 	 * Link watchers configuration for the connection: each link watcher is
 	 * defined by a dictionary, whose keys depend upon the selected link
@@ -707,7 +707,8 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
 	 * arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active',
 	 * 'validate-incative', 'send-always'. See teamd.conf man for more details.
 	 *
-	 * Since: 1.12
+	 * Element-Type: NMTeamLinkWatcher
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_LINK_WATCHERS,
diff --git a/libnm-core/nm-setting-team-port.h b/libnm-core/nm-setting-team-port.h
index 4ecb1838..26b2e098 100644
--- a/libnm-core/nm-setting-team-port.h
+++ b/libnm-core/nm-setting-team-port.h
@@ -71,33 +71,33 @@ GType nm_setting_team_port_get_type (void);
 NMSetting *  nm_setting_team_port_new (void);
 
 const char * nm_setting_team_port_get_config (NMSettingTeamPort *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gint nm_setting_team_port_get_queue_id (NMSettingTeamPort *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gint nm_setting_team_port_get_prio (NMSettingTeamPort *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean nm_setting_team_port_get_sticky (NMSettingTeamPort *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gint nm_setting_team_port_get_lacp_prio (NMSettingTeamPort *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gint nm_setting_team_port_get_lacp_key (NMSettingTeamPort *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 guint nm_setting_team_port_get_num_link_watchers (NMSettingTeamPort *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTeamLinkWatcher *
 nm_setting_team_port_get_link_watcher (NMSettingTeamPort *setting, guint idx);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean
 nm_setting_team_port_add_link_watcher (NMSettingTeamPort *setting,
                                        NMTeamLinkWatcher *link_watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void
 nm_setting_team_port_remove_link_watcher (NMSettingTeamPort *setting, guint idx);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean
 nm_setting_team_port_remove_link_watcher_by_value (NMSettingTeamPort *setting,
                                                    NMTeamLinkWatcher *link_watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void nm_setting_team_port_clear_link_watchers (NMSettingTeamPort *setting);
 G_END_DECLS
 
diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c
index 86fbdb01..1db2428d 100644
--- a/libnm-core/nm-setting-team.c
+++ b/libnm-core/nm-setting-team.c
@@ -59,7 +59,7 @@ static const char* _link_watcher_name[] = {
 struct NMTeamLinkWatcher {
 	guint refcount;
 
-	guint8 type; /* LinkWatcherTypes */
+	guint8 type;	/* LinkWatcherTypes */
 
 	/*
 	 * The union is constructed in order to allow mapping the options of all the
@@ -115,7 +115,7 @@ struct NMTeamLinkWatcher {
  *
  * Returns: (transfer full): the new #NMTeamLinkWatcher object
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTeamLinkWatcher *
 nm_team_link_watcher_new_ethtool (gint delay_up,
@@ -158,7 +158,7 @@ nm_team_link_watcher_new_ethtool (gint delay_up,
  *
  * Returns: (transfer full): the new #NMTeamLinkWatcher object, or %NULL on error
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTeamLinkWatcher *
 nm_team_link_watcher_new_nsna_ping (gint init_wait,
@@ -222,7 +222,7 @@ nm_team_link_watcher_new_nsna_ping (gint init_wait,
  *
  * Returns: (transfer full): the new #NMTeamLinkWatcher object, or %NULL on error
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTeamLinkWatcher *
 nm_team_link_watcher_new_arp_ping (gint init_wait,
@@ -287,7 +287,7 @@ nm_team_link_watcher_new_arp_ping (gint init_wait,
  *
  * Increases the reference count of the object.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_team_link_watcher_ref (NMTeamLinkWatcher *watcher){
@@ -303,7 +303,7 @@ nm_team_link_watcher_ref (NMTeamLinkWatcher *watcher){
  * Decreases the reference count of the object.  If the reference count
  * reaches zero, the object will be destroyed.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_team_link_watcher_unref (NMTeamLinkWatcher *watcher)
@@ -328,7 +328,7 @@ nm_team_link_watcher_unref (NMTeamLinkWatcher *watcher)
  *
  * Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_team_link_watcher_equal (NMTeamLinkWatcher *watcher, NMTeamLinkWatcher *other)
@@ -356,7 +356,7 @@ nm_team_link_watcher_equal (NMTeamLinkWatcher *watcher, NMTeamLinkWatcher *other
  *
  * Returns: (transfer full): a copy of @watcher
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTeamLinkWatcher *
 nm_team_link_watcher_dup (NMTeamLinkWatcher *watcher)
@@ -396,7 +396,7 @@ nm_team_link_watcher_dup (NMTeamLinkWatcher *watcher)
  *
  * Gets the name of the link watcher to be used.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 const char *
 nm_team_link_watcher_get_name (NMTeamLinkWatcher *watcher)
@@ -413,7 +413,7 @@ nm_team_link_watcher_get_name (NMTeamLinkWatcher *watcher)
  * Gets the delay_up interval (in milliseconds) that elapses between the link
  * coming up and the runner beeing notified about it.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 int
 nm_team_link_watcher_get_delay_up (NMTeamLinkWatcher *watcher)
@@ -432,7 +432,7 @@ nm_team_link_watcher_get_delay_up (NMTeamLinkWatcher *watcher)
  * Gets the delay_down interval (in milliseconds) that elapses between the link
  * going down and the runner beeing notified about it.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 int
 nm_team_link_watcher_get_delay_down (NMTeamLinkWatcher *watcher)
@@ -451,7 +451,7 @@ nm_team_link_watcher_get_delay_down (NMTeamLinkWatcher *watcher)
  * Gets the init_wait interval (in milliseconds) that the team slave should
  * wait before sending the first packet to the target host.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 int
 nm_team_link_watcher_get_init_wait (NMTeamLinkWatcher *watcher)
@@ -472,7 +472,7 @@ nm_team_link_watcher_get_init_wait (NMTeamLinkWatcher *watcher)
  * Gets the interval (in milliseconds) that the team slave should wait between
  * sending two check packets to the target host.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 int
 nm_team_link_watcher_get_interval (NMTeamLinkWatcher *watcher)
@@ -492,7 +492,7 @@ nm_team_link_watcher_get_interval (NMTeamLinkWatcher *watcher)
  *
  * Gets the number of missed replies after which the link is considered down.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 int
 nm_team_link_watcher_get_missed_max (NMTeamLinkWatcher *watcher)
@@ -513,7 +513,7 @@ nm_team_link_watcher_get_missed_max (NMTeamLinkWatcher *watcher)
  * Gets the host name/ip address to be used as destination for the link probing
  * packets.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 const char *
 nm_team_link_watcher_get_target_host (NMTeamLinkWatcher *watcher)
@@ -529,7 +529,7 @@ nm_team_link_watcher_get_target_host (NMTeamLinkWatcher *watcher)
  *
  * Gets the ip address to be used as source for the link probing packets.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 const char *
 nm_team_link_watcher_get_source_host (NMTeamLinkWatcher *watcher)
@@ -545,7 +545,7 @@ nm_team_link_watcher_get_source_host (NMTeamLinkWatcher *watcher)
  *
  * Gets the arp ping watcher flags.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTeamLinkWatcherArpPingFlags
 nm_team_link_watcher_get_flags (NMTeamLinkWatcher *watcher)
@@ -659,7 +659,7 @@ nm_setting_team_get_config (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:notify-peers-count property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gint
 nm_setting_team_get_notify_peers_count (NMSettingTeam *setting)
@@ -675,7 +675,7 @@ nm_setting_team_get_notify_peers_count (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:notify-peers-interval property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gint
 nm_setting_team_get_notify_peers_interval (NMSettingTeam *setting)
@@ -691,7 +691,7 @@ nm_setting_team_get_notify_peers_interval (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:mcast-rejoin-count property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gint
 nm_setting_team_get_mcast_rejoin_count (NMSettingTeam *setting)
@@ -707,7 +707,7 @@ nm_setting_team_get_mcast_rejoin_count (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:mcast-rejoin-interval property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gint
 nm_setting_team_get_mcast_rejoin_interval (NMSettingTeam *setting)
@@ -723,7 +723,7 @@ nm_setting_team_get_mcast_rejoin_interval (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:runner property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 const char *
 nm_setting_team_get_runner (NMSettingTeam *setting)
@@ -739,7 +739,7 @@ nm_setting_team_get_runner (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:runner-hwaddr-policy property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 const char *
 nm_setting_team_get_runner_hwaddr_policy (NMSettingTeam *setting)
@@ -755,7 +755,7 @@ nm_setting_team_get_runner_hwaddr_policy (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:runner-tx-balancer property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 const char *
 nm_setting_team_get_runner_tx_balancer (NMSettingTeam *setting)
@@ -771,7 +771,7 @@ nm_setting_team_get_runner_tx_balancer (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:runner-tx-balancer_interval property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gint
 nm_setting_team_get_runner_tx_balancer_interval (NMSettingTeam *setting)
@@ -787,7 +787,7 @@ nm_setting_team_get_runner_tx_balancer_interval (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:runner_active property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_team_get_runner_active (NMSettingTeam *setting)
@@ -803,7 +803,7 @@ nm_setting_team_get_runner_active (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:runner-fast-rate property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_team_get_runner_fast_rate (NMSettingTeam *setting)
@@ -819,7 +819,7 @@ nm_setting_team_get_runner_fast_rate (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:runner-sys-prio property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gint
 nm_setting_team_get_runner_sys_prio (NMSettingTeam *setting)
@@ -835,7 +835,7 @@ nm_setting_team_get_runner_sys_prio (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:runner-min-ports property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gint
 nm_setting_team_get_runner_min_ports (NMSettingTeam *setting)
@@ -851,7 +851,7 @@ nm_setting_team_get_runner_min_ports (NMSettingTeam *setting)
  *
  * Returns: the ##NMSettingTeam:runner-agg-select-policy property of the setting
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 const char *
 nm_setting_team_get_runner_agg_select_policy (NMSettingTeam *setting)
@@ -870,7 +870,7 @@ nm_setting_team_get_runner_agg_select_policy (NMSettingTeam *setting)
  *
  * Returns: %TRUE if the txhash element was found and removed; %FALSE if it was not.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_team_remove_runner_tx_hash_by_value (NMSettingTeam *setting,
@@ -899,7 +899,7 @@ nm_setting_team_remove_runner_tx_hash_by_value (NMSettingTeam *setting,
  *
  * Returns: the number of elements in txhash
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 guint
 nm_setting_team_get_num_runner_tx_hash (NMSettingTeam *setting)
@@ -918,7 +918,7 @@ nm_setting_team_get_num_runner_tx_hash (NMSettingTeam *setting)
  *
  * Returns: the txhash element at index @idx
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 const char *
 nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, guint idx)
@@ -938,7 +938,7 @@ nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, guint idx)
  *
  * Removes the txhash element at index @idx.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, guint idx)
@@ -962,7 +962,7 @@ nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, guint idx)
  * Returns: %TRUE if the txhash element was added; %FALSE if the element
  * was already knnown.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_team_add_runner_tx_hash (NMSettingTeam *setting, const char *txhash)
@@ -992,7 +992,7 @@ nm_setting_team_add_runner_tx_hash (NMSettingTeam *setting, const char *txhash)
  *
  * Returns: the number of configured link watchers
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 guint
 nm_setting_team_get_num_link_watchers (NMSettingTeam *setting)
@@ -1011,7 +1011,7 @@ nm_setting_team_get_num_link_watchers (NMSettingTeam *setting)
  *
  * Returns: (transfer none): the link watcher at index @idx.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 NMTeamLinkWatcher *
 nm_setting_team_get_link_watcher (NMSettingTeam *setting, guint idx)
@@ -1034,7 +1034,7 @@ nm_setting_team_get_link_watcher (NMSettingTeam *setting, guint idx)
  * Returns: %TRUE if the link watcher is added; %FALSE if an identical link
  * watcher was already there.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_team_add_link_watcher (NMSettingTeam *setting,
@@ -1063,7 +1063,7 @@ nm_setting_team_add_link_watcher (NMSettingTeam *setting,
  *
  * Removes the link watcher at index #idx.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_setting_team_remove_link_watcher (NMSettingTeam *setting, guint idx)
@@ -1086,7 +1086,7 @@ nm_setting_team_remove_link_watcher (NMSettingTeam *setting, guint idx)
  *
  * Returns: %TRUE if the link watcher was found and removed, %FALSE otherwise.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 gboolean
 nm_setting_team_remove_link_watcher_by_value (NMSettingTeam *setting,
@@ -1113,7 +1113,7 @@ nm_setting_team_remove_link_watcher_by_value (NMSettingTeam *setting,
  *
  * Removes all configured link watchers.
  *
- * Since: 1.12
+ * Since: 1.10.2
  **/
 void
 nm_setting_team_clear_link_watchers (NMSettingTeam *setting) {
@@ -1600,7 +1600,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd notify_peers.count.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_NOTIFY_PEERS_COUNT,
@@ -1614,7 +1614,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd notify_peers.interval.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_NOTIFY_PEERS_INTERVAL,
@@ -1628,7 +1628,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd mcast_rejoin.count.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_MCAST_REJOIN_COUNT,
@@ -1642,7 +1642,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd mcast_rejoin.interval.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_MCAST_REJOIN_INTERVAL,
@@ -1662,7 +1662,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 * other runners will be set to an empty value (or if not possible to
 	 * a default value).
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_RUNNER,
@@ -1676,7 +1676,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd runner.hwaddr_policy.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_RUNNER_HWADDR_POLICY,
@@ -1690,7 +1690,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd runner.tx_hash.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_RUNNER_TX_HASH,
@@ -1705,7 +1705,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd runner.tx_balancer.name.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_RUNNER_TX_BALANCER,
@@ -1719,7 +1719,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd runner.tx_balancer.interval.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_RUNNER_TX_BALANCER_INTERVAL,
@@ -1733,7 +1733,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd runner.active.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_RUNNER_ACTIVE,
@@ -1747,7 +1747,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd runner.fast_rate.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_RUNNER_FAST_RATE,
@@ -1761,7 +1761,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd runner.sys_prio.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_RUNNER_SYS_PRIO,
@@ -1775,7 +1775,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd runner.min_ports.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_RUNNER_MIN_PORTS,
@@ -1789,7 +1789,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 *
 	 * Corresponds to the teamd runner.agg_select_policy.
 	 *
-	 * Since: 1.12
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_RUNNER_AGG_SELECT_POLICY,
@@ -1799,7 +1799,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 		                      G_PARAM_STATIC_STRINGS));
 
 	/**
-	 * NMSettingTeam:link-watchers: (type GPtrArray(NMTeamLinkWatcher))
+	 * NMSettingTeam:link-watchers:
 	 *
 	 * Link watchers configuration for the connection: each link watcher is
 	 * defined by a dictionary, whose keys depend upon the selected link
@@ -1810,7 +1810,8 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
 	 * arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active',
 	 * 'validate-incative', 'send-always'. See teamd.conf man for more details.
 	 *
-	 * Since: 1.12
+	 * Element-Type: NMTeamLinkWatcher
+	 * Since: 1.10.2
 	 **/
 	g_object_class_install_property
 		(object_class, PROP_LINK_WATCHERS,
diff --git a/libnm-core/nm-setting-team.h b/libnm-core/nm-setting-team.h
index 492b2a66..8e5178a4 100644
--- a/libnm-core/nm-setting-team.h
+++ b/libnm-core/nm-setting-team.h
@@ -57,17 +57,17 @@ typedef struct NMTeamLinkWatcher NMTeamLinkWatcher;
 
 GType nm_team_link_watcher_get_type              (void);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTeamLinkWatcher *nm_team_link_watcher_new_ethtool (gint delay_up,
                                                      gint delay_down,
                                                      GError **error);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTeamLinkWatcher *nm_team_link_watcher_new_nsna_ping (gint init_wait,
                                                        gint interval,
                                                        gint missed_max,
                                                        const char *target_host,
                                                        GError **error);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTeamLinkWatcher *nm_team_link_watcher_new_arp_ping (gint init_wait,
                                                       gint interval,
                                                       gint missed_max,
@@ -75,31 +75,31 @@ NMTeamLinkWatcher *nm_team_link_watcher_new_arp_ping (gint init_wait,
                                                       const char *source_host,
                                                       NMTeamLinkWatcherArpPingFlags flags,
                                                       GError **error);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void nm_team_link_watcher_ref                    (NMTeamLinkWatcher *watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void nm_team_link_watcher_unref                  (NMTeamLinkWatcher *watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean nm_team_link_watcher_equal              (NMTeamLinkWatcher *watcher, NMTeamLinkWatcher *other);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTeamLinkWatcher *nm_team_link_watcher_dup      (NMTeamLinkWatcher *watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 const char *nm_team_link_watcher_get_name        (NMTeamLinkWatcher *watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 int nm_team_link_watcher_get_delay_up            (NMTeamLinkWatcher *watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 int nm_team_link_watcher_get_delay_down          (NMTeamLinkWatcher *watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 int nm_team_link_watcher_get_init_wait           (NMTeamLinkWatcher *watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 int nm_team_link_watcher_get_interval            (NMTeamLinkWatcher *watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 int nm_team_link_watcher_get_missed_max          (NMTeamLinkWatcher *watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 const char *nm_team_link_watcher_get_target_host (NMTeamLinkWatcher *watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 const char *nm_team_link_watcher_get_source_host (NMTeamLinkWatcher *watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTeamLinkWatcherArpPingFlags nm_team_link_watcher_get_flags (NMTeamLinkWatcher *watcher);
 
 
@@ -176,55 +176,55 @@ GType nm_setting_team_get_type (void);
 NMSetting *  nm_setting_team_new                (void);
 
 const char * nm_setting_team_get_config (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gint nm_setting_team_get_notify_peers_count (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gint nm_setting_team_get_notify_peers_interval (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gint nm_setting_team_get_mcast_rejoin_count (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gint nm_setting_team_get_mcast_rejoin_interval (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 const char * nm_setting_team_get_runner (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 const char * nm_setting_team_get_runner_hwaddr_policy (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 const char * nm_setting_team_get_runner_tx_balancer (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gint nm_setting_team_get_runner_tx_balancer_interval (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean nm_setting_team_get_runner_active (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean nm_setting_team_get_runner_fast_rate (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gint nm_setting_team_get_runner_sys_prio (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gint nm_setting_team_get_runner_min_ports (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 const char * nm_setting_team_get_runner_agg_select_policy (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean nm_setting_team_remove_runner_tx_hash_by_value (NMSettingTeam *setting, const char *txhash);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 guint nm_setting_team_get_num_runner_tx_hash (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 const char *nm_setting_team_get_runner_tx_hash (NMSettingTeam *setting, guint idx);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, guint idx);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean nm_setting_team_add_runner_tx_hash (NMSettingTeam *setting, const char *txhash);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 guint nm_setting_team_get_num_link_watchers (NMSettingTeam *setting);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTeamLinkWatcher * nm_setting_team_get_link_watcher (NMSettingTeam *setting, guint idx);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean nm_setting_team_add_link_watcher (NMSettingTeam *setting,
                                            NMTeamLinkWatcher *link_watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void nm_setting_team_remove_link_watcher (NMSettingTeam *setting, guint idx);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 gboolean nm_setting_team_remove_link_watcher_by_value (NMSettingTeam *setting,
                                                        NMTeamLinkWatcher *link_watcher);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 void nm_setting_team_clear_link_watchers (NMSettingTeam *setting);
 G_END_DECLS
 
diff --git a/libnm-core/nm-setting-tun.c b/libnm-core/nm-setting-tun.c
index edcb3ffd..dab407bd 100644
--- a/libnm-core/nm-setting-tun.c
+++ b/libnm-core/nm-setting-tun.c
@@ -179,8 +179,8 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSettingTunPrivate *priv = NM_SETTING_TUN_GET_PRIVATE (setting);
 
-	if (!NM_IN_SET (priv->mode, NM_SETTING_TUN_MODE_TUN,
-	                            NM_SETTING_TUN_MODE_TAP)) {
+	if (   priv->mode != NM_SETTING_TUN_MODE_TUN
+	    && priv->mode != NM_SETTING_TUN_MODE_TAP) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
 		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
diff --git a/libnm-core/nm-setting-user.c b/libnm-core/nm-setting-user.c
index ccc030aa..049607e1 100644
--- a/libnm-core/nm-setting-user.c
+++ b/libnm-core/nm-setting-user.c
@@ -210,7 +210,7 @@ nm_setting_user_check_val (const char *val, GError **error)
 static GHashTable *
 _create_data_hash (void)
 {
-	return g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
+	return g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 }
 
 /**
@@ -226,6 +226,7 @@ nm_setting_user_get_keys (NMSettingUser *setting, guint *out_len)
 {
 	NMSettingUser *self = setting;
 	NMSettingUserPrivate *priv;
+	guint len;
 
 	g_return_val_if_fail (NM_IS_SETTING_USER (self), NULL);
 
@@ -236,13 +237,19 @@ nm_setting_user_get_keys (NMSettingUser *setting, guint *out_len)
 		return priv->keys;
 	}
 
-	priv->keys = nm_utils_strdict_get_keys (priv->data,
-	                                        TRUE,
-	                                        out_len);
+	if (!priv->data || !g_hash_table_size (priv->data)) {
+		NM_SET_OUT (out_len, 0);
+		return (const char **) &priv->keys;
+	}
 
-	/* don't return %NULL, but hijack the @keys fields as a pseudo
-	 * empty strv array. */
-	return priv->keys ?: ((const char **) &priv->keys);
+	priv->keys = (const char **) g_hash_table_get_keys_as_array (priv->data, &len);
+	g_qsort_with_data (priv->keys,
+	                   len,
+	                   sizeof (const char *),
+	                   nm_strcmp_p_with_data,
+	                   NULL);
+	NM_SET_OUT (out_len, len);
+	return priv->keys;
 }
 
 /*****************************************************************************/
@@ -580,12 +587,14 @@ nm_setting_user_class_init (NMSettingUserClass *klass)
 	setting_class->verify = verify;
 
 	/**
-	 * NMSettingUser:data: (type GHashTable(utf8,utf8))
+	 * NMSettingUser:data:
 	 *
 	 * A dictionary of key/value pairs with user data. This data is ignored by NetworkManager
 	 * and can be used at the users discretion. The keys only support a strict ascii format,
 	 * but the values can be arbitrary UTF8 strings up to a certain length.
 	 *
+	 * Type: GHashTable(utf8,utf8)
+	 *
 	 * Since: 1.8
 	 **/
 	/* ---ifcfg-rh---
diff --git a/libnm-core/nm-setting-vlan.h b/libnm-core/nm-setting-vlan.h
index 63492ebf..ec41b595 100644
--- a/libnm-core/nm-setting-vlan.h
+++ b/libnm-core/nm-setting-vlan.h
@@ -80,12 +80,12 @@ typedef enum {
  * @NM_VLAN_FLAG_REORDER_HEADERS: indicates that this interface should reorder
  *  outgoing packet headers to look more like a non-VLAN Ethernet interface
  * @NM_VLAN_FLAG_GVRP: indicates that this interface should use GVRP to register
- *  itself with its switch
+ *  itself with it's switch
  * @NM_VLAN_FLAG_LOOSE_BINDING: indicates that this interface's operating
  *  state is tied to the underlying network interface but other details
  *  (like routing) are not.
  * @NM_VLAN_FLAG_MVRP: indicates that this interface should use MVRP to register
- *  itself with its switch
+ *  itself with it's switch
  *
  * #NMVlanFlags values control the behavior of the VLAN interface.
  **/
diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c
index a921f51f..34607252 100644
--- a/libnm-core/nm-setting-vpn.c
+++ b/libnm-core/nm-setting-vpn.c
@@ -216,30 +216,6 @@ nm_setting_vpn_get_data_item (NMSettingVpn *setting, const char *key)
 }
 
 /**
- * nm_setting_vpn_get_data_keys:
- * @setting: the #NMSettingVpn
- * @out_length: (allow-none): (out): the length of the returned array
- *
- * Retrieves every data key inside @setting, as an array.
- *
- * Returns: (array length=out_length) (transfer container): a
- *   %NULL-terminated array containing each data key or %NULL if
- *   there are no data items.
- *
- * Since: 1.12
- */
-const char **
-nm_setting_vpn_get_data_keys (NMSettingVpn *setting,
-                              guint *out_length)
-{
-	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
-
-	return nm_utils_strdict_get_keys (NM_SETTING_VPN_GET_PRIVATE (setting)->data,
-	                                  TRUE,
-	                                  out_length);
-}
-
-/**
  * nm_setting_vpn_remove_data_item:
  * @setting: the #NMSettingVpn
  * @key: the name of the data item to remove
@@ -264,52 +240,32 @@ nm_setting_vpn_remove_data_item (NMSettingVpn *setting, const char *key)
 }
 
 static void
-foreach_item_helper (NMSettingVpn *self,
-                     gboolean is_secrets,
+foreach_item_helper (GHashTable *hash,
                      NMVpnIterFunc func,
                      gpointer user_data)
 {
-	NMSettingVpnPrivate *priv;
-	guint len, i;
-	gs_strfreev char **keys = NULL;
-	GHashTable *hash;
+	GList *keys, *liter;
+	GSList *copied = NULL, *siter;
 
-	nm_assert (NM_IS_SETTING_VPN (self));
-	nm_assert (func);
+	g_return_if_fail (hash != NULL);
 
-	priv = NM_SETTING_VPN_GET_PRIVATE (self);
+	/* Grab keys and copy them so that the callback func can modify
+	 * the hash table items if it wants to.
+	 */
+	keys = g_hash_table_get_keys (hash);
+	for (liter = keys; liter; liter = g_list_next (liter))
+		copied = g_slist_prepend (copied, g_strdup (liter->data));
+	copied = g_slist_reverse (copied);
+	g_list_free (keys);
 
-	if (is_secrets) {
-		keys = (char **) nm_setting_vpn_get_secret_keys (self, &len);
-		hash = priv->secrets;
-	} else {
-		keys = (char **) nm_setting_vpn_get_data_keys (self, &len);
-		hash = priv->data;
-	}
+	for (siter = copied; siter; siter = g_slist_next (siter)) {
+		gpointer value;
 
-	if (!len) {
-		nm_assert (!keys);
-		return;
+		value = g_hash_table_lookup (hash, siter->data);
+		func (siter->data, value, user_data);
 	}
 
-	for (i = 0; i < len; i++) {
-		nm_assert (keys[i]);
-		keys[i] = g_strdup (keys[i]);
-	}
-	nm_assert (!keys[i]);
-
-	for (i = 0; i < len; i++) {
-		const char *value;
-
-		value = g_hash_table_lookup (hash, keys[i]);
-		/* NOTE: note that we call the function with a clone of @key,
-		 * not with the actual key from the dictionary.
-		 *
-		 * The @value on the other hand, is actually inside our dictionary,
-		 * it's not a clone. However, it might be %NULL, in case the key was
-		 * deleted while iterating. */
-		func (keys[i], value, user_data);
-	}
+	g_slist_free_full (copied, g_free);
 }
 
 /**
@@ -328,9 +284,8 @@ nm_setting_vpn_foreach_data_item (NMSettingVpn *setting,
                                   gpointer user_data)
 {
 	g_return_if_fail (NM_IS_SETTING_VPN (setting));
-	g_return_if_fail (func);
 
-	foreach_item_helper (setting, FALSE, func, user_data);
+	foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->data, func, user_data);
 }
 
 /**
@@ -393,30 +348,6 @@ nm_setting_vpn_get_secret (NMSettingVpn *setting, const char *key)
 }
 
 /**
- * nm_setting_vpn_get_secret_keys:
- * @setting: the #NMSettingVpn
- * @out_length: (allow-none): (out): the length of the returned array
- *
- * Retrieves every secret key inside @setting, as an array.
- *
- * Returns: (array length=out_length) (transfer container): a
- *   %NULL-terminated array containing each secret key or %NULL if
- *   there are no secrets.
- *
- * Since: 1.12
- */
-const char **
-nm_setting_vpn_get_secret_keys (NMSettingVpn *setting,
-                                guint *out_length)
-{
-	g_return_val_if_fail (NM_IS_SETTING_VPN (setting), NULL);
-
-	return nm_utils_strdict_get_keys (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets,
-	                                  TRUE,
-	                                  out_length);
-}
-
-/**
  * nm_setting_vpn_remove_secret:
  * @setting: the #NMSettingVpn
  * @key: the name of the secret to remove
@@ -456,9 +387,8 @@ nm_setting_vpn_foreach_secret (NMSettingVpn *setting,
                                gpointer user_data)
 {
 	g_return_if_fail (NM_IS_SETTING_VPN (setting));
-	g_return_if_fail (func);
 
-	foreach_item_helper (setting, TRUE, func, user_data);
+	foreach_item_helper (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, func, user_data);
 }
 
 /**
@@ -789,12 +719,22 @@ clear_secrets_with_flags (NMSetting *setting,
 }
 
 static void
+destroy_one_secret (gpointer data)
+{
+	char *secret = (char *) data;
+
+	/* Don't leave the secret lying around in memory */
+	memset (secret, 0, strlen (secret));
+	g_free (secret);
+}
+
+static void
 nm_setting_vpn_init (NMSettingVpn *setting)
 {
 	NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
 
-	priv->data = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
-	priv->secrets = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) nm_free_secret);
+	priv->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+	priv->secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_one_secret);
 }
 
 static void
@@ -945,10 +885,12 @@ nm_setting_vpn_class_init (NMSettingVpnClass *setting_class)
 		                       G_PARAM_STATIC_STRINGS));
 
 	/**
-	 * NMSettingVpn:data: (type GHashTable(utf8,utf8)):
+	 * NMSettingVpn:data:
 	 *
 	 * Dictionary of key/value pairs of VPN plugin specific data.  Both keys and
 	 * values must be strings.
+	 *
+	 * Type: GHashTable(utf8,utf8)
 	 **/
 	/* ---keyfile---
 	 * property: data
@@ -970,10 +912,12 @@ nm_setting_vpn_class_init (NMSettingVpnClass *setting_class)
 	                                      _nm_utils_strdict_from_dbus);
 
 	/**
-	 * NMSettingVpn:secrets: (type GHashTable(utf8,utf8)):
+	 * NMSettingVpn:secrets:
 	 *
 	 * Dictionary of key/value pairs of VPN plugin specific secrets like
 	 * passwords or private keys.  Both keys and values must be strings.
+	 *
+	 * Type: GHashTable(utf8,utf8)
 	 **/
 	/* ---keyfile---
 	 * property: secrets
diff --git a/libnm-core/nm-setting-vpn.h b/libnm-core/nm-setting-vpn.h
index 1f1576fd..115aa867 100644
--- a/libnm-core/nm-setting-vpn.h
+++ b/libnm-core/nm-setting-vpn.h
@@ -90,9 +90,6 @@ gboolean          nm_setting_vpn_remove_data_item  (NMSettingVpn *setting,
 void              nm_setting_vpn_foreach_data_item (NMSettingVpn *setting,
                                                     NMVpnIterFunc func,
                                                     gpointer user_data);
-NM_AVAILABLE_IN_1_12
-const char **     nm_setting_vpn_get_data_keys     (NMSettingVpn *setting,
-                                                    guint *out_length);
 
 guint32           nm_setting_vpn_get_num_secrets   (NMSettingVpn *setting);
 void              nm_setting_vpn_add_secret        (NMSettingVpn *setting,
@@ -105,11 +102,6 @@ gboolean          nm_setting_vpn_remove_secret     (NMSettingVpn *setting,
 void              nm_setting_vpn_foreach_secret    (NMSettingVpn *setting,
                                                     NMVpnIterFunc func,
                                                     gpointer user_data);
-NM_AVAILABLE_IN_1_12
-const char **     nm_setting_vpn_get_secret_keys   (NMSettingVpn *setting,
-                                                    guint *out_length);
-
-
 NM_AVAILABLE_IN_1_2
 guint32           nm_setting_vpn_get_timeout       (NMSettingVpn *setting);
 
diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c
index 3ad312e5..9d255e1d 100644
--- a/libnm-core/nm-setting-wired.c
+++ b/libnm-core/nm-setting-wired.c
@@ -829,7 +829,7 @@ nm_setting_wired_init (NMSettingWired *setting)
 {
 	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
 
-	priv->s390_options = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
+	priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
 	/* We use GArray rather than GPtrArray so it will automatically be NULL-terminated */
 	priv->mac_address_blacklist = g_array_new (TRUE, FALSE, sizeof (char *));
@@ -1363,12 +1363,14 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class)
 		                      G_PARAM_STATIC_STRINGS));
 
 	/**
-	 * NMSettingWired:s390-options: (type GHashTable(utf8,utf8)):
+	 * NMSettingWired:s390-options:
 	 *
 	 * Dictionary of key/value pairs of s390-specific device options.  Both keys
 	 * and values must be strings.  Allowed keys include "portno", "layer2",
 	 * "portname", "protocol", among others.  Key names must contain only
 	 * alphanumeric characters (ie, [a-zA-Z0-9]).
+	 *
+	 * Type: GHashTable(utf8,utf8)
 	 **/
 	/* ---ifcfg-rh---
 	 * property: s390-options
diff --git a/libnm-core/nm-setting-wireless-security.c b/libnm-core/nm-setting-wireless-security.c
index 31e386f3..de77a493 100644
--- a/libnm-core/nm-setting-wireless-security.c
+++ b/libnm-core/nm-setting-wireless-security.c
@@ -87,9 +87,6 @@ typedef struct {
 
 	/* WPS */
 	NMSettingWirelessSecurityWpsMethod wps_method;
-
-	/* FILS */
-	NMSettingWirelessSecurityFils fils;
 } NMSettingWirelessSecurityPrivate;
 
 enum {
@@ -113,7 +110,6 @@ enum {
 	PROP_LEAP_PASSWORD,
 	PROP_LEAP_PASSWORD_FLAGS,
 	PROP_WPS_METHOD,
-	PROP_FILS,
 
 	LAST_PROP
 };
@@ -818,22 +814,6 @@ nm_setting_wireless_security_get_wps_method (NMSettingWirelessSecurity *setting)
 	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->wps_method;
 }
 
-/*
- * nm_setting_wireless_security_get_fils:
- * @setting: the #NMSettingWirelessSecurity
- *
- * Returns: the #NMSettingWirelessSecurity:fils property of the setting
- *
- * Since: 1.12
- **/
-NMSettingWirelessSecurityFils
-nm_setting_wireless_security_get_fils (NMSettingWirelessSecurity *setting)
-{
-	g_return_val_if_fail (NM_IS_SETTING_WIRELESS_SECURITY (setting), 0);
-
-	return NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting)->fils;
-}
-
 static GPtrArray *
 need_secrets (NMSetting *setting)
 {
@@ -1347,9 +1327,6 @@ set_property (GObject *object, guint prop_id,
 	case PROP_WPS_METHOD:
 		priv->wps_method = g_value_get_uint (value);
 		break;
-	case PROP_FILS:
-		priv->fils = g_value_get_int (value);
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1421,9 +1398,6 @@ get_property (GObject *object, guint prop_id,
 	case PROP_WPS_METHOD:
 		g_value_set_uint (value, priv->wps_method);
 		break;
-	case PROP_FILS:
-		g_value_set_int (value, nm_setting_wireless_security_get_fils (setting));
-		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -1891,35 +1865,4 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *setting
 		                    G_PARAM_CONSTRUCT |
 		                    NM_SETTING_PARAM_FUZZY_IGNORE |
 		                    G_PARAM_STATIC_STRINGS));
-
-	/**
-	 * NMSettingWirelessSecurity:fils:
-	 *
-	 * Indicates whether Fast Initial Link Setup (802.11ai) must be enabled for
-	 * the connection.  One of %NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT (use
-	 * global default value), %NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE
-	 * (disable FILS), %NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL (enable FILS
-	 * if the supplicant and the access point support it) or
-	 * %NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED (enable FILS and fail if not
-	 * supported).  When set to %NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT and
-	 * no global default is set, FILS will be optionally enabled.
-	 *
-	 * Since: 1.12
-	 **/
-	/* ---ifcfg-rh---
-	 * property: fils
-	 * variable: FILS(+)
-	 * values: default, disable, optional, required
-	 * description: Enables or disables FILS (802.11ai)
-	 * example: FILS=required
-	 * ---end---
-	 */
-	g_object_class_install_property
-		(object_class, PROP_FILS,
-		 g_param_spec_int (NM_SETTING_WIRELESS_SECURITY_FILS, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_CONSTRUCT |
-		                   NM_SETTING_PARAM_FUZZY_IGNORE |
-		                   G_PARAM_STATIC_STRINGS));
 }
diff --git a/libnm-core/nm-setting-wireless-security.h b/libnm-core/nm-setting-wireless-security.h
index 5a732ac0..e7641b31 100644
--- a/libnm-core/nm-setting-wireless-security.h
+++ b/libnm-core/nm-setting-wireless-security.h
@@ -109,28 +109,6 @@ typedef enum {
 	NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN         = 0x00000008,
 } NMSettingWirelessSecurityWpsMethod;
 
-/**
- * NMSettingWirelessSecurityFils:
- * @NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT: use the default value
- * @NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE: disable FILS
- * @NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL: enable FILS if the supplicant and the AP support it
- * @NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED: require FILS and fail if not available
- * @_NM_SETTING_WIRELESS_SECURITY_FILS_NUM: placeholder value for bounds-checking
- * @NM_SETTING_WIRELESS_SECURITY_FILS_LAST: placeholder value for bounds-checking
- *
- * These flags indicate whether FILS must be enabled.
- *
- * Since: 1.12
- **/
-typedef enum {
-	NM_SETTING_WIRELESS_SECURITY_FILS_DEFAULT       = 0,
-	NM_SETTING_WIRELESS_SECURITY_FILS_DISABLE       = 1,
-	NM_SETTING_WIRELESS_SECURITY_FILS_OPTIONAL      = 2,
-	NM_SETTING_WIRELESS_SECURITY_FILS_REQUIRED      = 3,
-	_NM_SETTING_WIRELESS_SECURITY_FILS_NUM, /*< skip >*/
-	NM_SETTING_WIRELESS_SECURITY_FILS_LAST          =  _NM_SETTING_WIRELESS_SECURITY_FILS_NUM - 1, /*< skip >*/
-} NMSettingWirelessSecurityFils;
-
 #define NM_SETTING_WIRELESS_SECURITY_KEY_MGMT "key-mgmt"
 #define NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX "wep-tx-keyidx"
 #define NM_SETTING_WIRELESS_SECURITY_AUTH_ALG "auth-alg"
@@ -150,7 +128,6 @@ typedef enum {
 #define NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD "leap-password"
 #define NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS "leap-password-flags"
 #define NM_SETTING_WIRELESS_SECURITY_WPS_METHOD "wps-method"
-#define NM_SETTING_WIRELESS_SECURITY_FILS "fils"
 
 /**
  * NMSettingWirelessSecurity:
@@ -216,9 +193,6 @@ NMWepKeyType nm_setting_wireless_security_get_wep_key_type (NMSettingWirelessSec
 NM_AVAILABLE_IN_1_10
 NMSettingWirelessSecurityWpsMethod nm_setting_wireless_security_get_wps_method (NMSettingWirelessSecurity *setting);
 
-NM_AVAILABLE_IN_1_12
-NMSettingWirelessSecurityFils nm_setting_wireless_security_get_fils (NMSettingWirelessSecurity *setting);
-
 G_END_DECLS
 
 #endif /* __NM_SETTING_WIRELESS_SECURITY_H__ */
diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c
index 4c76ed31..9c8e53ae 100644
--- a/libnm-core/nm-setting.c
+++ b/libnm-core/nm-setting.c
@@ -94,7 +94,8 @@ static void
 _ensure_registered (void)
 {
 	if (G_UNLIKELY (registered_settings == NULL)) {
-		registered_settings = g_hash_table_new (nm_str_hash, g_str_equal);
+		nm_g_type_init ();
+		registered_settings = g_hash_table_new (g_str_hash, g_str_equal);
 		registered_settings_by_type = g_hash_table_new (_nm_gtype_hash, _nm_gtype_equal);
 	}
 }
@@ -806,7 +807,7 @@ _nm_setting_new_from_dbus (GType setting_type,
 		GVariant *entry, *entry_key;
 		char *key;
 
-		keys = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
+		keys = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 
 		g_variant_iter_init (&iter, setting_dict);
 		while ((entry = g_variant_iter_next_value (&iter))) {
@@ -815,7 +816,7 @@ _nm_setting_new_from_dbus (GType setting_type,
 			g_variant_unref (entry_key);
 			g_variant_unref (entry);
 
-			if (!g_hash_table_add (keys, key)) {
+			if (!nm_g_hash_table_add (keys, key)) {
 				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_SETTING,
 				             _("duplicate property"));
 				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), key);
@@ -1358,7 +1359,7 @@ nm_setting_diff (NMSetting *a,
 	}
 
 	if (*results == NULL) {
-		*results = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, NULL);
+		*results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 		results_created = TRUE;
 	}
 
@@ -1799,19 +1800,21 @@ get_secret_flags (NMSetting *setting,
                   NMSettingSecretFlags *out_flags,
                   GError **error)
 {
-	gs_free char *name_to_free = NULL;
+	char *flags_prop;
 	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
 
 	if (verify_secret && !is_secret_prop (setting, secret_name, error)) {
-		NM_SET_OUT (out_flags, NM_SETTING_SECRET_FLAG_NONE);
+		if (out_flags)
+			*out_flags = NM_SETTING_SECRET_FLAG_NONE;
 		return FALSE;
 	}
 
-	g_object_get (G_OBJECT (setting),
-	              nm_construct_name_a ("%s-flags", secret_name, &name_to_free),
-	              &flags,
-	              NULL);
-	NM_SET_OUT (out_flags, flags);
+	flags_prop = g_strdup_printf ("%s-flags", secret_name);
+	g_object_get (G_OBJECT (setting), flags_prop, &flags, NULL);
+	g_free (flags_prop);
+
+	if (out_flags)
+		*out_flags = flags;
 	return TRUE;
 }
 
@@ -1847,15 +1850,14 @@ set_secret_flags (NMSetting *setting,
                   NMSettingSecretFlags flags,
                   GError **error)
 {
-	gs_free char *name_to_free = NULL;
+	char *flags_prop;
 
 	if (verify_secret)
 		g_return_val_if_fail (is_secret_prop (setting, secret_name, error), FALSE);
 
-	g_object_set (G_OBJECT (setting),
-	              nm_construct_name_a ("%s-flags", secret_name, &name_to_free),
-	              flags,
-	              NULL);
+	flags_prop = g_strdup_printf ("%s-flags", secret_name);
+	g_object_set (G_OBJECT (setting), flags_prop, flags, NULL);
+	g_free (flags_prop);
 	return TRUE;
 }
 
diff --git a/libnm-core/nm-simple-connection.c b/libnm-core/nm-simple-connection.c
index f06e1aed..11700666 100644
--- a/libnm-core/nm-simple-connection.c
+++ b/libnm-core/nm-simple-connection.c
@@ -113,16 +113,11 @@ NMConnection *
 nm_simple_connection_new_clone (NMConnection *connection)
 {
 	NMConnection *clone;
-	const char *path;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 
 	clone = nm_simple_connection_new ();
-
-	path = nm_connection_get_path (connection);
-	if (path)
-		nm_connection_set_path (clone, path);
-
+	nm_connection_set_path (clone, nm_connection_get_path (connection));
 	nm_connection_replace_settings_from_connection (clone, connection);
 
 	return clone;
diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h
index 5d0f8386..c76b3200 100644
--- a/libnm-core/nm-utils-private.h
+++ b/libnm-core/nm-utils-private.h
@@ -21,8 +21,8 @@
 #ifndef __NM_UTILS_PRIVATE_H__
 #define __NM_UTILS_PRIVATE_H__
 
-#if !((NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-#error Cannot use this header.
+#ifdef __NETWORKMANAGER_TYPES_H__
+#error "nm-utils-private.h" must not be used outside of libnm-core/. Do you want "nm-core-internal.h"?
 #endif
 
 #include "nm-setting-private.h"
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index a535b679..ebbbfd3a 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -35,11 +35,13 @@
 #include <net/if.h>
 #include <linux/pkt_sched.h>
 
-#if WITH_JSON_VALIDATION
+#if WITH_JANSSON
 #include "nm-json.h"
+#include <jansson.h>
 #endif
 
 #include "nm-utils/nm-enum-utils.h"
+#include "nm-utils/nm-hash-utils.h"
 #include "nm-common-macros.h"
 #include "nm-utils-private.h"
 #include "nm-setting-private.h"
@@ -148,7 +150,7 @@ init_lang_to_encodings_hash (void)
 	if (G_UNLIKELY (langToEncodings5 == NULL)) {
 		/* Five-letter codes */
 		enc = (struct IsoLangToEncodings *) &isoLangEntries5[0];
-		langToEncodings5 = g_hash_table_new (nm_str_hash, g_str_equal);
+		langToEncodings5 = g_hash_table_new (g_str_hash, g_str_equal);
 		while (enc->lang) {
 			g_hash_table_insert (langToEncodings5, (gpointer) enc->lang,
 			                     (gpointer) enc->encodings);
@@ -159,7 +161,7 @@ init_lang_to_encodings_hash (void)
 	if (G_UNLIKELY (langToEncodings2 == NULL)) {
 		/* Two-letter codes */
 		enc = (struct IsoLangToEncodings *) &isoLangEntries2[0];
-		langToEncodings2 = g_hash_table_new (nm_str_hash, g_str_equal);
+		langToEncodings2 = g_hash_table_new (g_str_hash, g_str_equal);
 		while (enc->lang) {
 			g_hash_table_insert (langToEncodings2, (gpointer) enc->lang,
 			                     (gpointer) enc->encodings);
@@ -253,6 +255,8 @@ _nm_utils_init (void)
 	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
 	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 
+	nm_g_type_init ();
+
 	_nm_dbus_errors_init ();
 }
 
@@ -464,7 +468,7 @@ _nm_utils_string_slist_validate (GSList *list, const char **valid_values)
  * @hash: a #GHashTable
  *
  * Utility function to iterate over a hash table and return
- * its values as a #GSList.
+ * it's values as a #GSList.
  *
  * Returns: (element-type gpointer) (transfer container): a newly allocated #GSList
  * containing the values of the hash table. The caller must free the
@@ -542,7 +546,7 @@ _nm_utils_strdict_from_dbus (GVariant *dbus_value,
 	const char *key, *value;
 	GHashTable *hash;
 
-	hash = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
+	hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 	g_variant_iter_init (&iter, dbus_value);
 	while (g_variant_iter_next (&iter, "{&s&s}", &key, &value))
 		g_hash_table_insert (hash, g_strdup (key), g_strdup (value));
@@ -557,7 +561,7 @@ _nm_utils_copy_strdict (GHashTable *strdict)
 	GHashTableIter iter;
 	gpointer key, value;
 
-	copy = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, g_free);
+	copy = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 	if (strdict) {
 		g_hash_table_iter_init (&iter, strdict);
 		while (g_hash_table_iter_next (&iter, &key, &value))
@@ -1879,7 +1883,7 @@ GVariant *
 nm_utils_ip_addresses_to_variant (GPtrArray *addresses)
 {
 	GVariantBuilder builder;
-	guint i;
+	int i;
 
 	g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
 
@@ -1887,8 +1891,8 @@ nm_utils_ip_addresses_to_variant (GPtrArray *addresses)
 		for (i = 0; i < addresses->len; i++) {
 			NMIPAddress *addr = addresses->pdata[i];
 			GVariantBuilder addr_builder;
-			gs_free const char **names = NULL;
-			guint j, len;
+			char **names;
+			int n;
 
 			g_variant_builder_init (&addr_builder, G_VARIANT_TYPE ("a{sv}"));
 			g_variant_builder_add (&addr_builder, "{sv}",
@@ -1898,12 +1902,13 @@ nm_utils_ip_addresses_to_variant (GPtrArray *addresses)
 			                       "prefix",
 			                       g_variant_new_uint32 (nm_ip_address_get_prefix (addr)));
 
-			names = _nm_ip_address_get_attribute_names (addr, TRUE, &len);
-			for (j = 0; j < len; j++) {
+			names = nm_ip_address_get_attribute_names (addr);
+			for (n = 0; names[n]; n++) {
 				g_variant_builder_add (&addr_builder, "{sv}",
-				                       names[j],
-				                       nm_ip_address_get_attribute (addr, names[j]));
+				                       names[n],
+				                       nm_ip_address_get_attribute (addr, names[n]));
 			}
+			g_strfreev (names);
 
 			g_variant_builder_add (&builder, "a{sv}", &addr_builder);
 		}
@@ -2221,7 +2226,7 @@ _nm_utils_string_append_tc_qdisc_rest (GString *string, NMTCQdisc *qdisc)
  *
  * Returns: formatted string or %NULL
  *
- * Since: 1.12
+ * Since: 1.10.2
  */
 char *
 nm_utils_tc_qdisc_to_str (NMTCQdisc *qdisc, GError **error)
@@ -2322,7 +2327,7 @@ _tc_read_common_opts (const char *str,
  *
  * Returns: the %NMTCQdisc or %NULL
  *
- * Since: 1.12
+ * Since: 1.10.2
  */
 NMTCQdisc *
 nm_utils_tc_qdisc_from_str (const char *str, GError **error)
@@ -2411,7 +2416,7 @@ _string_append_tc_action (GString *string, NMTCAction *action, GError **error)
  *
  * Returns: formatted string or %NULL
  *
- * Since: 1.12
+ * Since: 1.10.2
  */
 char *
 nm_utils_tc_action_to_str (NMTCAction *action, GError **error)
@@ -2437,7 +2442,7 @@ nm_utils_tc_action_to_str (NMTCAction *action, GError **error)
  *
  * Returns: the %NMTCAction or %NULL
  *
- * Since: 1.12
+ * Since: 1.10.2
  */
 NMTCAction *
 nm_utils_tc_action_from_str (const char *str, GError **error)
@@ -2453,10 +2458,10 @@ nm_utils_tc_action_from_str (const char *str, GError **error)
 	nm_assert (str);
 	nm_assert (!error || !*error);
 
-	ht = nm_utils_parse_variant_attributes (str,
-	                                        ' ', ' ', FALSE,
-	                                        tc_action_attribute_spec,
-	                                        error);
+        ht = nm_utils_parse_variant_attributes (str,
+                                                ' ', ' ', FALSE,
+                                                tc_action_attribute_spec,
+                                                error);
 	if (!ht)
 		return FALSE;
 
@@ -2492,10 +2497,10 @@ nm_utils_tc_action_from_str (const char *str, GError **error)
 			return NULL;
 		}
 
-		options = nm_utils_parse_variant_attributes (rest,
-		                                             ' ', ' ', FALSE,
-		                                             attrs,
-		                                             error);
+	        options = nm_utils_parse_variant_attributes (rest,
+	                                                     ' ', ' ', FALSE,
+	                                                     attrs,
+	                                                     error);
 		if (!options) {
 			nm_tc_action_unref (action);
 			return NULL;
@@ -2555,7 +2560,7 @@ _nm_utils_string_append_tc_tfilter_rest (GString *string, NMTCTfilter *tfilter,
  *
  * Returns: formatted string or %NULL
  *
- * Since: 1.12
+ * Since: 1.10.2
  */
 char *
 nm_utils_tc_tfilter_to_str (NMTCTfilter *tfilter, GError **error)
@@ -2590,7 +2595,7 @@ static const NMVariantAttributeSpec * const tc_tfilter_attribute_spec[] = {
  *
  * Returns: the %NMTCTfilter or %NULL
  *
- * Since: 1.12
+ * Since: 1.10.2
  */
 NMTCTfilter *
 nm_utils_tc_tfilter_from_str (const char *str, GError **error)
@@ -3439,28 +3444,59 @@ nm_utils_wifi_5ghz_freqs (void)
  * @strength: the access point strength, from 0 to 100
  *
  * Converts @strength into a 4-character-wide graphical representation of
- * strength suitable for printing to stdout.
- *
- * Previous versions used to take a guess at the terminal type and possibly
- * return a wide UTF-8 encoded string. Now it always returns a 7-bit
- * clean strings of one to 0 to 4 asterisks. Users that actually need
- * the functionality are encouraged to make their implementations instead.
+ * strength suitable for printing to stdout. If the current locale and terminal
+ * support it, this will use unicode graphics characters to represent
+ * "bars". Otherwise it will use 0 to 4 asterisks.
  *
  * Returns: the graphical representation of the access point strength
  */
 const char *
 nm_utils_wifi_strength_bars (guint8 strength)
 {
+	static const char *strength_full, *strength_high, *strength_med, *strength_low, *strength_none;
+
+	if (G_UNLIKELY (strength_full == NULL)) {
+		gboolean can_show_graphics = TRUE;
+		char *locale_str;
+
+		if (!g_get_charset (NULL)) {
+			/* Non-UTF-8 locale */
+			locale_str = g_locale_from_utf8 ("\342\226\202\342\226\204\342\226\206\342\226\210", -1, NULL, NULL, NULL);
+			if (locale_str)
+				g_free (locale_str);
+			else
+				can_show_graphics = FALSE;
+		}
+
+		/* The linux console font doesn't have these characters */
+		if (g_strcmp0 (g_getenv ("TERM"), "linux") == 0)
+			can_show_graphics = FALSE;
+
+		if (can_show_graphics) {
+			strength_full = /* ▂▄▆█ */ "\342\226\202\342\226\204\342\226\206\342\226\210";
+			strength_high = /* â–‚â–„â–†_ */ "\342\226\202\342\226\204\342\226\206_";
+			strength_med  = /* â–‚â–„__ */ "\342\226\202\342\226\204__";
+			strength_low  = /* â–‚___ */ "\342\226\202___";
+			strength_none = /* ____ */ "____";
+		} else {
+			strength_full = "****";
+			strength_high = "*** ";
+			strength_med  = "**  ";
+			strength_low  = "*   ";
+			strength_none = "    ";
+		}
+	}
+
 	if (strength > 80)
-		return "****";
+		return strength_full;
 	else if (strength > 55)
-		return "*** ";
+		return strength_high;
 	else if (strength > 30)
-		return "**  ";
+		return strength_med;
 	else if (strength > 5)
-		return "*   ";
+		return strength_low;
 	else
-		return "    ";
+		return strength_none;
 }
 
 /**
@@ -3662,7 +3698,7 @@ _nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize buffer_length, gs
 /**
  * nm_utils_hwaddr_aton:
  * @asc: the ASCII representation of a hardware address
- * @buffer: (type guint8) (array length=length): buffer to store the result into
+ * @buffer: buffer to store the result into
  * @length: the expected length in bytes of the result and
  * the size of the buffer in bytes.
  *
@@ -3894,9 +3930,9 @@ _nm_utils_hwaddr_canonical_or_invalid (const char *mac, gssize length)
 
 /**
  * nm_utils_hwaddr_matches:
- * @hwaddr1: (nullable): pointer to a binary or ASCII hardware address, or %NULL
+ * @hwaddr1: pointer to a binary or ASCII hardware address, or %NULL
  * @hwaddr1_len: size of @hwaddr1, or -1 if @hwaddr1 is ASCII
- * @hwaddr2: (nullable): pointer to a binary or ASCII hardware address, or %NULL
+ * @hwaddr2: pointer to a binary or ASCII hardware address, or %NULL
  * @hwaddr2_len: size of @hwaddr2, or -1 if @hwaddr2 is ASCII
  *
  * Generalized hardware address comparison function. Tests if @hwaddr1 and
@@ -4335,7 +4371,7 @@ nm_utils_inet_ntop (int addr_family, gconstpointer addr, char *dst)
  *  characters. If set to %NULL, it will return a pointer to an internal, static
  *  buffer (shared with nm_utils_inet6_ntop()).  Beware, that the internal
  *  buffer will be overwritten with ever new call of nm_utils_inet4_ntop() or
- *  nm_utils_inet6_ntop() that does not provide its own @dst buffer. Also,
+ *  nm_utils_inet6_ntop() that does not provied it's own @dst buffer. Also,
  *  using the internal buffer is not thread safe. When in doubt, pass your own
  *  @dst buffer to avoid these issues.
  *
@@ -4359,7 +4395,7 @@ nm_utils_inet4_ntop (in_addr_t inaddr, char *dst)
  *  characters. If set to %NULL, it will return a pointer to an internal, static
  *  buffer (shared with nm_utils_inet4_ntop()).  Beware, that the internal
  *  buffer will be overwritten with ever new call of nm_utils_inet4_ntop() or
- *  nm_utils_inet6_ntop() that does not provide its own @dst buffer. Also,
+ *  nm_utils_inet6_ntop() that does not provied it's own @dst buffer. Also,
  *  using the internal buffer is not thread safe. When in doubt, pass your own
  *  @dst buffer to avoid these issues.
  *
@@ -4586,6 +4622,9 @@ _nm_utils_strstrdictkey_hash (gconstpointer a)
 
 		nm_hash_update_val (&h, k->type);
 		if (k->type & STRSTRDICTKEY_ALL_SET) {
+			gsize n;
+
+			n = 0;
 			p = strchr (k->data, '\0');
 			if (k->type == STRSTRDICTKEY_ALL_SET) {
 				/* the key contains two strings. Continue... */
@@ -4805,7 +4844,7 @@ gssize _nm_utils_dns_option_find_idx (GPtrArray *array, const char *option)
 char *
 nm_utils_enum_to_str (GType type, int value)
 {
-	return _nm_utils_enum_to_str_full (type, value, ", ", NULL);
+	return _nm_utils_enum_to_str_full (type, value, ", ");
 }
 
 /**
@@ -4888,7 +4927,33 @@ _nm_utils_is_json_object_no_validation (const char *str, GError **error)
 	return FALSE;
 }
 
-#if WITH_JSON_VALIDATION
+#if WITH_JANSSON
+
+/* Added in Jansson v2.3 (released Jan 27 2012) */
+#ifndef json_object_foreach
+#define json_object_foreach(object, key, value) \
+    for(key = json_object_iter_key(json_object_iter(object)); \
+        key && (value = json_object_iter_value(json_object_iter_at (object, key) )); \
+        key = json_object_iter_key(json_object_iter_next(object, json_object_iter_at (object, key))))
+#endif
+
+/* Added in Jansson v2.4 (released Sep 23 2012), but travis.ci has v2.2. */
+#ifndef json_boolean
+#define json_boolean(val) ((val) ? json_true() : json_false())
+#endif
+
+/* Added in Jansson v2.5 (released Sep 19 2013), but travis.ci has v2.2. */
+#ifndef json_array_foreach
+#define json_array_foreach(array, index, value) \
+        for (index = 0; \
+             index < json_array_size(array) && (value = json_array_get(array, index)); \
+             index++)
+#endif
+
+/* Added in Jansson v2.7 */
+#ifndef json_boolean_value
+#define json_boolean_value json_is_true
+#endif
 
 static void
 _json_add_object (json_t *json,
@@ -5362,6 +5427,15 @@ nm_utils_is_json_object (const char *str, GError **error)
 	return TRUE;
 }
 
+/* json_object_foreach_safe() is only available since Jansson 2.8,
+ * reimplement it */
+#define _json_object_foreach_safe(object, n, key, value) \
+    for (key = json_object_iter_key (json_object_iter (object)), \
+         n = json_object_iter_next (object, json_object_iter_at (object, key)); \
+         key && (value = json_object_iter_value (json_object_iter_at (object, key))); \
+         key = json_object_iter_key (n), \
+         n = json_object_iter_next (object, json_object_iter_at (object, key)))
+
 gboolean
 _nm_utils_team_config_equal (const char *conf1,
                              const char *conf2,
@@ -5401,7 +5475,7 @@ _nm_utils_team_config_equal (const char *conf1,
 	/* Only consider a given subset of nodes, others can change depending on
 	 * current state */
 	for (i = 0, json = json1; i < 2; i++, json = json2) {
-		json_object_foreach_safe (json, tmp, key, value) {
+		_json_object_foreach_safe (json, tmp, key, value) {
 			if (!NM_IN_STRSET (key, "runner", "link_watch"))
 				json_object_del (json, key);
 		}
@@ -5660,7 +5734,7 @@ done:
 	return updated;
 }
 
-#else /* !WITH_JSON_VALIDATION */
+#else /* WITH_JANSSON */
 
 gboolean
 nm_utils_is_json_object (const char *str, GError **error)
@@ -5952,8 +6026,7 @@ attribute_unescape (const char *start, const char *end)
  *
  * Parse attributes from a string.
  *
- * Returns: (transfer full) (element-type utf8 GVariant): a #GHashTable mapping
- * attribute names to #GVariant values.
+ * Returns: (transfer full): a #GHashTable mapping attribute names to #GVariant values.
  *
  * Since: 1.8
  */
@@ -5975,7 +6048,7 @@ nm_utils_parse_variant_attributes (const char *string,
 	g_return_val_if_fail (key_value_separator, NULL);
 	g_return_val_if_fail (!error || !*error, NULL);
 
-	ht = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
+	ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_variant_unref);
 
 	while (TRUE) {
 		gs_free char *name = NULL, *value = NULL;
@@ -6112,7 +6185,7 @@ next:
 
 /*
  * nm_utils_format_variant_attributes:
- * @attributes: (element-type utf8 GVariant): a #GHashTable mapping attribute names to #GVariant values
+ * @attributes:  a #GHashTable mapping attribute names to #GVariant values
  * @attr_separator: the attribute separator character
  * @key_value_separator: character separating key and values
  *
@@ -6134,8 +6207,8 @@ nm_utils_format_variant_attributes (GHashTable *attributes,
 	const char *name, *value;
 	char *escaped;
 	char buf[64];
-	gs_free NMUtilsNamedValue *values = NULL;
-	guint i, len;
+	gs_free_list GList *keys = NULL;
+	GList *iter;
 
 	g_return_val_if_fail (attr_separator, NULL);
 	g_return_val_if_fail (key_value_separator, NULL);
@@ -6143,13 +6216,12 @@ nm_utils_format_variant_attributes (GHashTable *attributes,
 	if (!attributes || !g_hash_table_size (attributes))
 		return NULL;
 
-	values = nm_utils_named_values_from_str_dict (attributes, &len);
-
+	keys = g_list_sort (g_hash_table_get_keys (attributes), (GCompareFunc) g_strcmp0);
 	str = g_string_new ("");
 
-	for (i = 0; i < len; i++) {
-		name = values[i].name;
-		variant = (GVariant *) values[i].value_ptr;
+	for (iter = keys; iter; iter = g_list_next (iter)) {
+		name = iter->data;
+		variant = g_hash_table_lookup (attributes, name);
 		value = NULL;
 
 		if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32))
diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h
index 1f645a80..df9284b3 100644
--- a/libnm-core/nm-utils.h
+++ b/libnm-core/nm-utils.h
@@ -233,19 +233,19 @@ char * nm_utils_format_variant_attributes (GHashTable *attributes,
 
 /*****************************************************************************/
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCQdisc *nm_utils_tc_qdisc_from_str      (const char *str, GError **error);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 char *nm_utils_tc_qdisc_to_str             (NMTCQdisc *qdisc, GError **error);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCAction *nm_utils_tc_action_from_str    (const char *str, GError **error);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 char *nm_utils_tc_action_to_str            (NMTCAction *action, GError **error);
 
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 NMTCTfilter *nm_utils_tc_tfilter_from_str  (const char *str, GError **error);
-NM_AVAILABLE_IN_1_12
+NM_AVAILABLE_IN_1_10_2
 char *nm_utils_tc_tfilter_to_str           (NMTCTfilter *tfilter, GError **error);
 
 G_END_DECLS
diff --git a/libnm-core/nm-version.h b/libnm-core/nm-version.h
index 0412bc8e..61bfa5b1 100644
--- a/libnm-core/nm-version.h
+++ b/libnm-core/nm-version.h
@@ -29,17 +29,17 @@
 
 #if !defined (NM_VERSION_MIN_REQUIRED) || (NM_VERSION_MIN_REQUIRED == 0)
 # undef NM_VERSION_MIN_REQUIRED
-# define NM_VERSION_MIN_REQUIRED (NM_API_VERSION)
+# define NM_VERSION_MIN_REQUIRED (NM_VERSION_CUR_STABLE)
 #endif
 
 #if !defined (NM_VERSION_MAX_ALLOWED) || (NM_VERSION_MAX_ALLOWED == 0)
 # undef NM_VERSION_MAX_ALLOWED
-# define NM_VERSION_MAX_ALLOWED (NM_API_VERSION)
+# define NM_VERSION_MAX_ALLOWED (NM_VERSION_CUR_STABLE)
 #endif
 
 /* sanity checks */
-#if NM_VERSION_MIN_REQUIRED > NM_API_VERSION
-#error "NM_VERSION_MIN_REQUIRED must be <= NM_API_VERSION"
+#if NM_VERSION_MIN_REQUIRED > NM_VERSION_NEXT_STABLE
+#error "NM_VERSION_MIN_REQUIRED must be <= NM_VERSION_NEXT_STABLE"
 #endif
 #if NM_VERSION_MAX_ALLOWED < NM_VERSION_MIN_REQUIRED
 #error "NM_VERSION_MAX_ALLOWED must be >= NM_VERSION_MIN_REQUIRED"
@@ -146,18 +146,10 @@
 # define NM_AVAILABLE_IN_1_10
 #endif
 
-#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_12
-# define NM_DEPRECATED_IN_1_12           G_DEPRECATED
-# define NM_DEPRECATED_IN_1_12_FOR(f)    G_DEPRECATED_FOR(f)
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_10_2
+# define NM_AVAILABLE_IN_1_10_2          G_UNAVAILABLE(1,10.2)
 #else
-# define NM_DEPRECATED_IN_1_12
-# define NM_DEPRECATED_IN_1_12_FOR(f)
-#endif
-
-#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_12
-# define NM_AVAILABLE_IN_1_12            G_UNAVAILABLE(1,12)
-#else
-# define NM_AVAILABLE_IN_1_12
+# define NM_AVAILABLE_IN_1_10_2
 #endif
 
 #endif  /* NM_VERSION_H */
diff --git a/libnm-core/nm-vpn-editor-plugin.h b/libnm-core/nm-vpn-editor-plugin.h
index 0f910e36..e93d8fbd 100644
--- a/libnm-core/nm-vpn-editor-plugin.h
+++ b/libnm-core/nm-vpn-editor-plugin.h
@@ -34,7 +34,7 @@
 
 G_BEGIN_DECLS
 
-typedef struct _NMVpnPluginInfo NMVpnPluginInfo;
+struct _NMVpnPluginInfo;
 
 typedef struct _NMVpnEditorPlugin NMVpnEditorPlugin;
 typedef struct _NMVpnEditor NMVpnEditor;
@@ -128,7 +128,7 @@ typedef struct {
 	char * (*get_suggested_filename) (NMVpnEditorPlugin *plugin, NMConnection *connection);
 
 	void (*notify_plugin_info_set) (NMVpnEditorPlugin *plugin,
-	                                NMVpnPluginInfo *plugin_info);
+	                                struct _NMVpnPluginInfo *plugin_info);
 
 	const NMVpnEditorPluginVT *(*get_vt) (NMVpnEditorPlugin *plugin,
 	                                      gsize *out_vt_size);
@@ -171,12 +171,12 @@ NMVpnEditorPlugin *nm_vpn_editor_plugin_load (const char *plugin_name,
                                               GError **error);
 
 NM_AVAILABLE_IN_1_4
-NMVpnPluginInfo *nm_vpn_editor_plugin_get_plugin_info (NMVpnEditorPlugin *plugin);
+struct _NMVpnPluginInfo *nm_vpn_editor_plugin_get_plugin_info (NMVpnEditorPlugin *plugin);
 NM_AVAILABLE_IN_1_4
-void             nm_vpn_editor_plugin_set_plugin_info (NMVpnEditorPlugin *plugin, NMVpnPluginInfo *plugin_info);
+void             nm_vpn_editor_plugin_set_plugin_info (NMVpnEditorPlugin *plugin, struct _NMVpnPluginInfo *plugin_info);
 
 #include "nm-vpn-plugin-info.h"
 
 G_END_DECLS
 
-#endif /* __NM_VPN_EDITOR_PLUGIN_H__ */
+#endif	/* __NM_VPN_EDITOR_PLUGIN_H__ */
diff --git a/libnm-core/nm-vpn-plugin-info.h b/libnm-core/nm-vpn-plugin-info.h
index 61539cbc..908ee482 100644
--- a/libnm-core/nm-vpn-plugin-info.h
+++ b/libnm-core/nm-vpn-plugin-info.h
@@ -49,10 +49,10 @@ struct _NMVpnPluginInfo;
 /**
  * NMVpnPluginInfo:
  */
-struct _NMVpnPluginInfo {
+typedef struct _NMVpnPluginInfo {
 	NM_AVAILABLE_IN_1_2
 	GObject parent;
-};
+} NMVpnPluginInfo;
 
 typedef struct {
 	NM_AVAILABLE_IN_1_2
diff --git a/libnm-core/tests/meson.build b/libnm-core/tests/meson.build
deleted file mode 100644
index 6e8a2493..00000000
--- a/libnm-core/tests/meson.build
+++ /dev/null
@@ -1,51 +0,0 @@
-# test-cert.p12 created with:
-#
-# openssl pkcs12 -export \
-# -in test_key_and_cert.pem \
-# -inkey test_key_and_cert.pem \
-# -certfile test_ca_cert.pem \
-# -name "test-pkcs12" \
-# -out test-cert.p12
-
-enum_types = 'nm-core-tests-enum-types'
-
-enum = gnome.mkenums(
-  enum_types,
-  sources: 'test-general-enums.h',
-  identifier_prefix: nm_id_prefix,
-  c_template: enum_types + '.c.template',
-  h_template: enum_types + '.h.template'
-)
-
-test_units = [
-  'test-compare',
-  'test-crypto',
-  'test-general',
-  'test-keyfile',
-  'test-secrets',
-  'test-setting',
-  'test-settings-defaults'
-]
-
-test_cert_dir = join_paths(meson.current_source_dir(), 'certs')
-
-cflags = [
-  '-DNETWORKMANAGER_COMPILATION_TEST',
-  '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIBNM_CORE',
-  '-DTEST_CERT_DIR="@0@"'.format(test_cert_dir),
-]
-
-foreach test_unit: test_units
-  exe = executable(
-    'libnm-core-' + test_unit,
-    [test_unit + '.c'] + enum,
-    dependencies: nm_core_dep,
-    c_args: cflags,
-    link_with: libnm_core
-  )
-  test(
-    'libnm-core/' + test_unit,
-    test_script,
-    args: test_args + [exe.full_path()]
-  )
-endforeach
diff --git a/libnm-core/tests/nm-core-tests-enum-types.c.template b/libnm-core/tests/nm-core-tests-enum-types.c.template
deleted file mode 100644
index 1160be86..00000000
--- a/libnm-core/tests/nm-core-tests-enum-types.c.template
+++ /dev/null
@@ -1,35 +0,0 @@
-/*** BEGIN file-header ***/
-#include "config.h"
-
-#include "nm-core-tests-enum-types.h"
-
-#include "test-general-enums.h"
-/*** END file-header ***/
-
-/*** BEGIN value-header ***/
-GType
-@enum_name@_get_type (void)
-{
-  static volatile gsize g_define_type_id__volatile = 0;
-
-  if (g_once_init_enter (&g_define_type_id__volatile))
-    {
-      static const G@Type@Value values[] = {
-/*** END value-header ***/
-
-/*** BEGIN value-production ***/
-        { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
-/*** END value-production ***/
-
-/*** BEGIN value-tail ***/
-        { 0, NULL, NULL }
-      };
-      GType g_define_type_id =
-        g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
-      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
-    }
-
-  return g_define_type_id__volatile;
-}
-
-/*** END value-tail ***/
diff --git a/libnm-core/tests/nm-core-tests-enum-types.h.template b/libnm-core/tests/nm-core-tests-enum-types.h.template
deleted file mode 100644
index 62e82b77..00000000
--- a/libnm-core/tests/nm-core-tests-enum-types.h.template
+++ /dev/null
@@ -1,21 +0,0 @@
-/*** BEGIN file-header ***/
-#ifndef __NM_CORE_TESTS_ENUM_TYPES_H__
-#define __NM_CORE_TESTS_ENUM_TYPES_H__
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-/*** END file-header ***/
-
-/*** BEGIN enumeration-production ***/
-GType @enum_name@_get_type (void) G_GNUC_CONST;
-#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
-
-/*** END enumeration-production ***/
-
-/*** BEGIN file-tail ***/
-G_END_DECLS
-
-#endif /* __NM_CORE_TESTS_ENUM_TYPES_H__ */
-/*** END file-tail ***/
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index 544eb3c0..520ed581 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -26,6 +26,7 @@
 #include <string.h>
 
 #include "nm-utils/c-list-util.h"
+#include "nm-utils/nm-hash-utils.h"
 
 #include "nm-utils.h"
 #include "nm-setting-private.h"
@@ -167,17 +168,6 @@ _test_hash_str (const char *str)
 static void
 test_nm_hash (void)
 {
-	g_assert (nm_hash_static (0));
-	g_assert (nm_hash_static (777));
-
-	g_assert (nm_hash_str (NULL));
-	g_assert (nm_hash_str (""));
-	g_assert (nm_hash_str ("a"));
-
-	g_assert (nm_hash_ptr (NULL));
-	g_assert (nm_hash_ptr (""));
-	g_assert (nm_hash_ptr ("a"));
-
 	_test_hash_str ("");
 	_test_hash_str ("a");
 	_test_hash_str ("aa");
@@ -210,10 +200,6 @@ static void
 test_nm_g_slice_free_fcn (void)
 {
 	gpointer p;
-	struct {
-		char a1;
-		char a2;
-	} xx;
 
 	p = g_slice_new (gint64);
 	(nm_g_slice_free_fcn (gint64)) (p);
@@ -226,9 +212,6 @@ test_nm_g_slice_free_fcn (void)
 
 	p = g_slice_new (gint64);
 	nm_g_slice_free_fcn_gint64 (p);
-
-	p = g_slice_alloc (sizeof (xx));
-	(nm_g_slice_free_fcn (xx)) (p);
 }
 
 /*****************************************************************************/
@@ -296,6 +279,7 @@ test_nm_utils_strsplit_set (void)
 
 typedef struct {
 	int val;
+	int idx;
 	CList lst;
 } CListSort;
 
@@ -319,86 +303,71 @@ _c_list_sort_cmp (const CList *lst_a, const CList *lst_b, const void *user_data)
 }
 
 static void
-_do_test_c_list_sort (CListSort *elements, guint n_list, gboolean headless)
+test_c_list_sort (void)
 {
+	guint i, n_list, repeat, headless;
 	CList head, *iter, *iter_prev, *lst;
-	guint i;
+	CListSort elements[30];
 	const CListSort *el_prev;
-	CListSort *el;
 
 	c_list_init (&head);
-	for (i = 0; i < n_list; i++) {
-		el = &elements[i];
-		el->val = nmtst_get_rand_int () % (2*n_list);
-		c_list_link_tail (&head, &el->lst);
-	}
-
-	if (headless) {
-		lst = head.next;
-		c_list_unlink_stale (&head);
-		lst = c_list_sort_headless (lst, _c_list_sort_cmp, NULL);
-		g_assert (lst);
-		g_assert (lst->next);
-		g_assert (lst->prev);
-		g_assert (c_list_length (lst) == n_list - 1);
-		iter_prev = lst->prev;
-		for (iter = lst; iter != lst; iter = iter->next) {
-			g_assert (iter);
-			g_assert (iter->next);
-			g_assert (iter->prev == iter_prev);
-		}
-		c_list_link_before (lst, &head);
-	} else
-		c_list_sort (&head, _c_list_sort_cmp, NULL);
-
-	g_assert (!c_list_is_empty (&head));
-	g_assert (c_list_length (&head) == n_list);
-
-	el_prev = NULL;
-	c_list_for_each (iter, &head) {
-		el = c_list_entry (iter, CListSort, lst);
-		g_assert (el >= elements && el < &elements[n_list]);
-		if (el_prev) {
-			if (el_prev->val == el->val)
-				g_assert (el_prev < el);
-			else
-				g_assert (el_prev->val < el->val);
-			g_assert (iter->prev == &el_prev->lst);
-			g_assert (el_prev->lst.next == iter);
-		}
-		el_prev = el;
-	}
-	g_assert (head.prev == &el_prev->lst);
-}
-
-static void
-test_c_list_sort (void)
-{
-	const guint N_ELEMENTS = 10000;
-	guint n_list, repeat;
-	gs_free CListSort *elements = NULL;
-
-	{
-		CList head;
-
-		c_list_init (&head);
-		c_list_sort (&head, _c_list_sort_cmp, NULL);
-		g_assert (c_list_length (&head) == 0);
-		g_assert (c_list_is_empty (&head));
-	}
+	c_list_sort (&head, _c_list_sort_cmp, NULL);
+	g_assert (c_list_length (&head) == 0);
+	g_assert (c_list_is_empty (&head));
+
+	for (repeat = 0; repeat < 10; repeat++) {
+		for (n_list = 1; n_list < G_N_ELEMENTS (elements); n_list++) {
+			for (headless = 0; headless < 2; headless++) {
+				c_list_init (&head);
+				for (i = 0; i < n_list; i++) {
+					CListSort *el;
+
+					el = &elements[i];
+					el->val = nmtst_get_rand_int () % (2*n_list);
+					el->idx = i;
+					c_list_link_tail (&head, &el->lst);
+				}
 
-	elements = g_new0 (CListSort, N_ELEMENTS);
-	for (n_list = 1; n_list < N_ELEMENTS; n_list++) {
-		if (n_list > 150) {
-			n_list += nmtst_get_rand_int () % n_list;
-			if (n_list >= N_ELEMENTS)
-				break;
-		}
-		{
-			const guint N_REPEAT = n_list > 50 ? 1 : 5;
+				if (headless) {
+					lst = head.next;
+					c_list_unlink_stale (&head);
+					lst = c_list_sort_headless (lst, _c_list_sort_cmp, NULL);
+					g_assert (lst);
+					g_assert (lst->next);
+					g_assert (lst->prev);
+					g_assert (c_list_length (lst) == n_list - 1);
+					iter_prev = lst->prev;
+					for (iter = lst; iter != lst; iter = iter->next) {
+						g_assert (iter);
+						g_assert (iter->next);
+						g_assert (iter->prev == iter_prev);
+					}
+					c_list_link_before (lst, &head);
+				} else {
+					c_list_sort (&head, _c_list_sort_cmp, NULL);
+				}
 
-			for (repeat = 0; repeat < N_REPEAT; repeat++)
-				_do_test_c_list_sort (elements, n_list, nmtst_get_rand_int () % 2);
+				g_assert (!c_list_is_empty (&head));
+				g_assert (c_list_length (&head) == n_list);
+
+				el_prev = NULL;
+				c_list_for_each (iter, &head) {
+					CListSort *el;
+
+					el = c_list_entry (iter, CListSort, lst);
+					g_assert (el->idx >= 0 && el->idx < n_list);
+					g_assert (el == &elements[el->idx]);
+					if (el_prev) {
+						g_assert (el_prev->val <= el->val);
+						if (el_prev->val == el->val)
+							g_assert (el_prev->idx < el->idx);
+						g_assert (iter->prev == &el_prev->lst);
+						g_assert (el_prev->lst.next == iter);
+					}
+					el_prev = el;
+				}
+				g_assert (head.prev == &el_prev->lst);
+			}
 		}
 	}
 }
@@ -773,54 +742,54 @@ test_setting_vpn_items (void)
 	nm_setting_vpn_remove_secret (s_vpn, "foobar4");
 
 	/* Try to add some blank values and make sure they are rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (key != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (key != NULL));
 	nm_setting_vpn_add_data_item (s_vpn, NULL, NULL);
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (strlen (key) > 0));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (key) > 0));
 	nm_setting_vpn_add_data_item (s_vpn, "", "");
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (item != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (item != NULL));
 	nm_setting_vpn_add_data_item (s_vpn, "foobar1", NULL);
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (strlen (item) > 0));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (item) > 0));
 	nm_setting_vpn_add_data_item (s_vpn, "foobar1", "");
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (key != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (key != NULL));
 	nm_setting_vpn_add_data_item (s_vpn, NULL, "blahblah1");
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (strlen (key) > 0));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (key) > 0));
 	nm_setting_vpn_add_data_item (s_vpn, "", "blahblah1");
 	g_test_assert_expected_messages ();
 
 	nm_setting_vpn_foreach_data_item (s_vpn, vpn_check_empty_func, NULL);
 
 	/* Try to add some blank secrets and make sure they are rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (key != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (key != NULL));
 	nm_setting_vpn_add_secret (s_vpn, NULL, NULL);
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (strlen (key) > 0));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (key) > 0));
 	nm_setting_vpn_add_secret (s_vpn, "", "");
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (secret != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (secret != NULL));
 	nm_setting_vpn_add_secret (s_vpn, "foobar1", NULL);
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (strlen (secret) > 0));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (secret) > 0));
 	nm_setting_vpn_add_secret (s_vpn, "foobar1", "");
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (key != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (key != NULL));
 	nm_setting_vpn_add_secret (s_vpn, NULL, "blahblah1");
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (strlen (key) > 0));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (key) > 0));
 	nm_setting_vpn_add_secret (s_vpn, "", "blahblah1");
 	g_test_assert_expected_messages ();
 
@@ -973,7 +942,7 @@ test_setting_ip4_config_labels (void)
 	nmtst_assert_setting_verifies (NM_SETTING (s_ip4));
 
 	addr = nm_setting_ip_config_get_address (s_ip4, 0);
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label == NULL);
 
 	/* The 'address-labels' property should be omitted from the serialization if
@@ -998,28 +967,28 @@ test_setting_ip4_config_labels (void)
 	/* addr 2 */
 	addr = nm_ip_address_new (AF_INET, "2.3.4.5", 24, &error);
 	g_assert_no_error (error);
-	nm_ip_address_set_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string ("eth0:1"));
+	nm_ip_address_set_attribute (addr, "label", g_variant_new_string ("eth0:1"));
 
 	nm_setting_ip_config_add_address (s_ip4, addr);
 	nm_ip_address_unref (addr);
 	nmtst_assert_setting_verifies (NM_SETTING (s_ip4));
 
 	addr = nm_setting_ip_config_get_address (s_ip4, 1);
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label != NULL);
 	g_assert_cmpstr (g_variant_get_string (label, NULL), ==, "eth0:1");
 
 	/* addr 3 */
 	addr = nm_ip_address_new (AF_INET, "3.4.5.6", 24, &error);
 	g_assert_no_error (error);
-	nm_ip_address_set_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL, NULL);
+	nm_ip_address_set_attribute (addr, "label", NULL);
 
 	nm_setting_ip_config_add_address (s_ip4, addr);
 	nm_ip_address_unref (addr);
 	nmtst_assert_setting_verifies (NM_SETTING (s_ip4));
 
 	addr = nm_setting_ip_config_get_address (s_ip4, 2);
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label == NULL);
 
 	/* Remove addr 1 and re-verify remaining addresses */
@@ -1028,13 +997,13 @@ test_setting_ip4_config_labels (void)
 
 	addr = nm_setting_ip_config_get_address (s_ip4, 0);
 	g_assert_cmpstr (nm_ip_address_get_address (addr), ==, "2.3.4.5");
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label != NULL);
 	g_assert_cmpstr (g_variant_get_string (label, NULL), ==, "eth0:1");
 
 	addr = nm_setting_ip_config_get_address (s_ip4, 1);
 	g_assert_cmpstr (nm_ip_address_get_address (addr), ==, "3.4.5.6");
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label == NULL);
 
 	/* If we serialize as the daemon, the labels should appear in the D-Bus
@@ -1065,11 +1034,11 @@ test_setting_ip4_config_labels (void)
 	g_assert (addrs != NULL);
 	g_assert_cmpint (addrs->len, ==, 2);
 	addr = addrs->pdata[0];
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label != NULL);
 	g_assert_cmpstr (g_variant_get_string (label, NULL), ==, "eth0:1");
 	addr = addrs->pdata[1];
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label == NULL);
 	g_ptr_array_unref (addrs);
 
@@ -1092,13 +1061,13 @@ test_setting_ip4_config_labels (void)
 
 	addr = nm_setting_ip_config_get_address (s_ip4, 0);
 	g_assert_cmpstr (nm_ip_address_get_address (addr), ==, "2.3.4.5");
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label != NULL);
 	g_assert_cmpstr (g_variant_get_string (label, NULL), ==, "eth0:1");
 
 	addr = nm_setting_ip_config_get_address (s_ip4, 1);
 	g_assert_cmpstr (nm_ip_address_get_address (addr), ==, "3.4.5.6");
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label == NULL);
 
 	g_object_unref (conn);
@@ -1115,12 +1084,12 @@ test_setting_ip4_config_labels (void)
 
 	addr = nm_setting_ip_config_get_address (s_ip4, 0);
 	g_assert_cmpstr (nm_ip_address_get_address (addr), ==, "2.3.4.5");
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert_cmpstr (g_variant_get_string (label, NULL), ==, "eth0:1");
 
 	addr = nm_setting_ip_config_get_address (s_ip4, 1);
 	g_assert_cmpstr (nm_ip_address_get_address (addr), ==, "3.4.5.6");
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label == NULL);
 
 	/* Test explicit property assignment */
@@ -1140,13 +1109,13 @@ test_setting_ip4_config_labels (void)
 
 	addr = nm_setting_ip_config_get_address (s_ip4, 0);
 	g_assert_cmpstr (nm_ip_address_get_address (addr), ==, "2.3.4.5");
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label != NULL);
 	g_assert_cmpstr (g_variant_get_string (label, NULL), ==, "eth0:1");
 
 	addr = nm_setting_ip_config_get_address (s_ip4, 1);
 	g_assert_cmpstr (nm_ip_address_get_address (addr), ==, "3.4.5.6");
-	label = nm_ip_address_get_attribute (addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL);
+	label = nm_ip_address_get_attribute (addr, "label");
 	g_assert (label == NULL);
 
 	g_object_unref (conn);
@@ -2290,47 +2259,47 @@ test_setting_connection_permissions_helpers (void)
 	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
 
 	/* Ensure a bad [type] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (strcmp (ptype, "user") == 0));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strcmp (ptype, "user") == 0));
 	success = nm_setting_connection_add_permission (s_con, "foobar", "blah", NULL);
 	g_test_assert_expected_messages ();
 	g_assert (!success);
 
 	/* Ensure a bad [type] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (ptype));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (ptype));
 	success = nm_setting_connection_add_permission (s_con, NULL, "blah", NULL);
 	g_test_assert_expected_messages ();
 	g_assert (!success);
 
 	/* Ensure a bad [item] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (uname));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (p != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (uname));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (p != NULL));
 	success = nm_setting_connection_add_permission (s_con, "user", NULL, NULL);
 	g_test_assert_expected_messages ();
 	g_assert (!success);
 
 	/* Ensure a bad [item] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (uname[0] != '\0'));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (p != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (uname[0] != '\0'));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (p != NULL));
 	success = nm_setting_connection_add_permission (s_con, "user", "", NULL);
 	g_test_assert_expected_messages ();
 	g_assert (!success);
 
 	/* Ensure an [item] with ':' is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (strchr (uname, ':') == NULL));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (p != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strchr (uname, ':') == NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (p != NULL));
 	success = nm_setting_connection_add_permission (s_con, "user", "ad:asdf", NULL);
 	g_test_assert_expected_messages ();
 	g_assert (!success);
 
 	/* Ensure a non-UTF-8 [item] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (g_utf8_validate (uname, -1, NULL) == TRUE));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (p != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (g_utf8_validate (uname, -1, NULL) == TRUE));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (p != NULL));
 	success = nm_setting_connection_add_permission (s_con, "user", buf, NULL);
 	g_test_assert_expected_messages ();
 	g_assert (!success);
 
 	/* Ensure a non-NULL [detail] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (detail == NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (detail == NULL));
 	success = nm_setting_connection_add_permission (s_con, "user", "dafasdf", "asdf");
 	g_test_assert_expected_messages ();
 	g_assert (!success);
@@ -2401,56 +2370,55 @@ test_setting_connection_permissions_property (void)
 	s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
 
 	/* Ensure a bad [type] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0));
 	add_permission_property (s_con, "foobar", "blah", -1, NULL);
 	g_test_assert_expected_messages ();
 	g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0);
 
 	/* Ensure a bad [type] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0));
 	add_permission_property (s_con, NULL, "blah", -1, NULL);
 	g_test_assert_expected_messages ();
 	g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0);
 
 	/* Ensure a bad [item] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (last_colon > str));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (last_colon > str));
 	add_permission_property (s_con, "user", NULL, -1, NULL);
 	g_test_assert_expected_messages ();
 	g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0);
 
 	/* Ensure a bad [item] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (last_colon > str));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (last_colon > str));
 	add_permission_property (s_con, "user", "", -1, NULL);
 	g_test_assert_expected_messages ();
 	g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0);
 
 	/* Ensure an [item] with ':' in the middle is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (str[i] != ':'));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (str[i] != ':'));
 	add_permission_property (s_con, "user", "ad:asdf", -1, NULL);
 	g_test_assert_expected_messages ();
 	g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0);
 
 	/* Ensure an [item] with ':' at the end is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (str[i] != ':'));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (str[i] != ':'));
 	add_permission_property (s_con, "user", "adasdfaf:", -1, NULL);
 	g_test_assert_expected_messages ();
 	g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0);
 
 	/* Ensure a non-UTF-8 [item] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (g_utf8_validate (str, -1, NULL) == TRUE));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (g_utf8_validate (str, -1, NULL) == TRUE));
 	add_permission_property (s_con, "user", buf, (int) sizeof (buf), NULL);
 	g_test_assert_expected_messages ();
 	g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0);
 
 	/* Ensure a non-NULL [detail] is rejected */
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (*(last_colon + 1) == '\0'));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (*(last_colon + 1) == '\0'));
 	add_permission_property (s_con, "user", "dafasdf", -1, "asdf");
 	g_test_assert_expected_messages ();
 	g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0);
 
 	/* Ensure a valid call results in success */
 	success = nm_setting_connection_add_permission (s_con, "user", TEST_UNAME, NULL);
-	g_assert (success);
 	g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 1);
 
 	check_permission (s_con, 0, TEST_UNAME);
@@ -2604,7 +2572,6 @@ test_connection_diff_a_only (void)
 			{ NM_SETTING_CONNECTION_METERED,              NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_LLDP,                 NM_SETTING_DIFF_RESULT_IN_A },
 			{ NM_SETTING_CONNECTION_AUTH_RETRIES,         NM_SETTING_DIFF_RESULT_IN_A },
-			{ NM_SETTING_CONNECTION_MDNS,                 NM_SETTING_DIFF_RESULT_IN_A },
 			{ NULL, NM_SETTING_DIFF_RESULT_UNKNOWN }
 		} },
 		{ NM_SETTING_WIRED_SETTING_NAME, {
@@ -3073,10 +3040,10 @@ test_setting_compare_addresses (void)
 
 	a = nm_ip_address_new (AF_INET, "192.168.7.5", 24, NULL);
 
-	nm_ip_address_set_attribute (a, NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string ("xoxoxo"));
+	nm_ip_address_set_attribute (a, "label", g_variant_new_string ("xoxoxo"));
 	nm_setting_ip_config_add_address ((NMSettingIPConfig *) s1, a);
 
-	nm_ip_address_set_attribute (a, NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string ("hello"));
+	nm_ip_address_set_attribute (a, "label", g_variant_new_string ("hello"));
 	nm_setting_ip_config_add_address ((NMSettingIPConfig *) s2, a);
 
 	nm_ip_address_unref (a);
@@ -3105,10 +3072,10 @@ test_setting_compare_routes (void)
 
 	r = nm_ip_route_new (AF_INET, "192.168.12.0", 24, "192.168.11.1", 473, NULL);
 
-	nm_ip_route_set_attribute (r, NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string ("xoxoxo"));
+	nm_ip_route_set_attribute (r, "label", g_variant_new_string ("xoxoxo"));
 	nm_setting_ip_config_add_route ((NMSettingIPConfig *) s1, r);
 
-	nm_ip_route_set_attribute (r, NM_IP_ADDRESS_ATTRIBUTE_LABEL, g_variant_new_string ("hello"));
+	nm_ip_route_set_attribute (r, "label", g_variant_new_string ("hello"));
 	nm_setting_ip_config_add_route ((NMSettingIPConfig *) s2, r);
 
 	nm_ip_route_unref (r);
@@ -3579,7 +3546,7 @@ test_setting_connection_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_connection_add_permission (s_con, "user", "billsmith", NULL));
 	ASSERT_CHANGED (nm_setting_connection_remove_permission (s_con, 0));
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (iter != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (iter != NULL));
 	ASSERT_UNCHANGED (nm_setting_connection_remove_permission (s_con, 1));
 	g_test_assert_expected_messages ();
 
@@ -3587,7 +3554,7 @@ test_setting_connection_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_connection_add_secondary (s_con, uuid));
 	ASSERT_CHANGED (nm_setting_connection_remove_secondary (s_con, 0));
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (elt != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL));
 	ASSERT_UNCHANGED (nm_setting_connection_remove_secondary (s_con, 1));
 	g_test_assert_expected_messages ();
 
@@ -3639,7 +3606,7 @@ test_setting_ip4_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_dns (s_ip4, "11.22.0.0"));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_dns (s_ip4, 0));
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns (s_ip4, 1));
 	g_test_assert_expected_messages ();
 
@@ -3649,7 +3616,7 @@ test_setting_ip4_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_dns_search (s_ip4, "foobar.com"));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_dns_search (s_ip4, 0));
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns_search->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns_search->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns_search (s_ip4, 1));
 	g_test_assert_expected_messages ();
 
@@ -3661,7 +3628,7 @@ test_setting_ip4_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_address (s_ip4, addr));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_address (s_ip4, 0));
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->addresses->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->addresses->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_address (s_ip4, 1));
 	g_test_assert_expected_messages ();
 
@@ -3674,7 +3641,7 @@ test_setting_ip4_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_route (s_ip4, route));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_route (s_ip4, 0));
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->routes->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->routes->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_route (s_ip4, 1));
 	g_test_assert_expected_messages ();
 
@@ -3684,7 +3651,7 @@ test_setting_ip4_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_dns_option (s_ip4, "debug"));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_dns_option (s_ip4, 0));
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns_options->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns_options->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns_option (s_ip4, 1));
 	g_test_assert_expected_messages ();
 
@@ -3715,7 +3682,7 @@ test_setting_ip6_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_dns (s_ip6, "1:2:3::4:5:6"));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_dns (s_ip6, 0));
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns (s_ip6, 1));
 	g_test_assert_expected_messages ();
 
@@ -3725,7 +3692,7 @@ test_setting_ip6_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_dns_search (s_ip6, "foobar.com"));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_dns_search (s_ip6, 0));
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns_search->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->dns_search->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns_search (s_ip6, 1));
 	g_test_assert_expected_messages ();
 
@@ -3738,7 +3705,7 @@ test_setting_ip6_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_address (s_ip6, addr));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_address (s_ip6, 0));
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->addresses->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->addresses->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_address (s_ip6, 1));
 	g_test_assert_expected_messages ();
 
@@ -3751,7 +3718,7 @@ test_setting_ip6_changed_signal (void)
 	ASSERT_CHANGED (nm_setting_ip_config_add_route (s_ip6, route));
 	ASSERT_CHANGED (nm_setting_ip_config_remove_route (s_ip6, 0));
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->routes->len));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx >= 0 && idx < priv->routes->len));
 	ASSERT_UNCHANGED (nm_setting_ip_config_remove_route (s_ip6, 1));
 	g_test_assert_expected_messages ();
 
@@ -3781,7 +3748,7 @@ test_setting_vlan_changed_signal (void)
 
 	ASSERT_CHANGED (nm_setting_vlan_add_priority (s_vlan, NM_VLAN_INGRESS_MAP, 1, 3));
 	ASSERT_CHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_INGRESS_MAP, 0));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (idx < g_slist_length (list)));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < g_slist_length (list)));
 	ASSERT_UNCHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_INGRESS_MAP, 1));
 	g_test_assert_expected_messages ();
 	ASSERT_CHANGED (nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, "1:3"));
@@ -3789,7 +3756,7 @@ test_setting_vlan_changed_signal (void)
 
 	ASSERT_CHANGED (nm_setting_vlan_add_priority (s_vlan, NM_VLAN_EGRESS_MAP, 1, 3));
 	ASSERT_CHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_EGRESS_MAP, 0));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (idx < g_slist_length (list)));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < g_slist_length (list)));
 	ASSERT_UNCHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_EGRESS_MAP, 1));
 	g_test_assert_expected_messages ();
 	ASSERT_CHANGED (nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, "1:3"));
@@ -3888,7 +3855,7 @@ test_setting_wireless_security_changed_signal (void)
 	/* Protos */
 	ASSERT_CHANGED (nm_setting_wireless_security_add_proto (s_wsec, "wpa"));
 	ASSERT_CHANGED (nm_setting_wireless_security_remove_proto (s_wsec, 0));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (elt != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL));
 	ASSERT_UNCHANGED (nm_setting_wireless_security_remove_proto (s_wsec, 1));
 	g_test_assert_expected_messages ();
 
@@ -3898,7 +3865,7 @@ test_setting_wireless_security_changed_signal (void)
 	/* Pairwise ciphers */
 	ASSERT_CHANGED (nm_setting_wireless_security_add_pairwise (s_wsec, "tkip"));
 	ASSERT_CHANGED (nm_setting_wireless_security_remove_pairwise (s_wsec, 0));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (elt != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL));
 	ASSERT_UNCHANGED (nm_setting_wireless_security_remove_pairwise (s_wsec, 1));
 	g_test_assert_expected_messages ();
 
@@ -3908,7 +3875,7 @@ test_setting_wireless_security_changed_signal (void)
 	/* Group ciphers */
 	ASSERT_CHANGED (nm_setting_wireless_security_add_group (s_wsec, "ccmp"));
 	ASSERT_CHANGED (nm_setting_wireless_security_remove_group (s_wsec, 0));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (elt != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL));
 	ASSERT_UNCHANGED (nm_setting_wireless_security_remove_group (s_wsec, 1));
 	g_test_assert_expected_messages ();
 
@@ -3943,7 +3910,7 @@ test_setting_802_1x_changed_signal (void)
 	/* EAP methods */
 	ASSERT_CHANGED (nm_setting_802_1x_add_eap_method (s_8021x, "tls"));
 	ASSERT_CHANGED (nm_setting_802_1x_remove_eap_method (s_8021x, 0));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (elt != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL));
 	ASSERT_UNCHANGED (nm_setting_802_1x_remove_eap_method (s_8021x, 1));
 	g_test_assert_expected_messages ();
 
@@ -3953,7 +3920,7 @@ test_setting_802_1x_changed_signal (void)
 	/* alternate subject matches */
 	ASSERT_CHANGED (nm_setting_802_1x_add_altsubject_match (s_8021x, "EMAIL:server@example.com"));
 	ASSERT_CHANGED (nm_setting_802_1x_remove_altsubject_match (s_8021x, 0));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (elt != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL));
 	ASSERT_UNCHANGED (nm_setting_802_1x_remove_altsubject_match (s_8021x, 1));
 	g_test_assert_expected_messages ();
 
@@ -3963,7 +3930,7 @@ test_setting_802_1x_changed_signal (void)
 	/* phase2 alternate subject matches */
 	ASSERT_CHANGED (nm_setting_802_1x_add_phase2_altsubject_match (s_8021x, "EMAIL:server@example.com"));
 	ASSERT_CHANGED (nm_setting_802_1x_remove_phase2_altsubject_match (s_8021x, 0));
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (elt != NULL));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL));
 	ASSERT_UNCHANGED (nm_setting_802_1x_remove_phase2_altsubject_match (s_8021x, 1));
 	g_test_assert_expected_messages ();
 
@@ -5881,7 +5848,7 @@ test_nm_utils_check_valid_json (void)
 {
 	_json_config_check_valid (NULL, FALSE);
 	_json_config_check_valid ("", FALSE);
-#if WITH_JSON_VALIDATION
+#if WITH_JANSSON
 	_json_config_check_valid ("{ }", TRUE);
 	_json_config_check_valid ("{ \"a\" : 1 }", TRUE);
 	_json_config_check_valid ("{ \"a\" : }", FALSE);
@@ -5906,7 +5873,7 @@ _team_config_equal_check (const char *conf1,
 static void
 test_nm_utils_team_config_equal (void)
 {
-#if WITH_JSON_VALIDATION
+#if WITH_JANSSON
 	_team_config_equal_check ("", "", TRUE, TRUE);
 	_team_config_equal_check ("{}",
 	                          "{ }",
@@ -6093,6 +6060,66 @@ again:
 
 /*****************************************************************************/
 
+static void
+test_g_ptr_array_insert (void)
+{
+	/* this test only makes sense on a recent glib, where we compare our compat
+	 * with the original implementation. */
+#if GLIB_CHECK_VERSION(2, 40, 0)
+	gs_unref_ptrarray GPtrArray *arr1 = g_ptr_array_new ();
+	gs_unref_ptrarray GPtrArray *arr2 = g_ptr_array_new ();
+	GRand *rand = nmtst_get_rand ();
+	guint i;
+
+	for (i = 0; i < 560; i++) {
+		gint32 idx = g_rand_int_range (rand, -1, arr1->len + 1);
+
+		g_ptr_array_insert (arr1, idx, GINT_TO_POINTER (i));
+		_nm_g_ptr_array_insert (arr2, idx, GINT_TO_POINTER (i));
+
+		g_assert_cmpint (arr1->len, ==, arr2->len);
+		g_assert (memcmp (arr1->pdata, arr2->pdata, arr1->len * sizeof (gpointer)) == 0);
+	}
+#endif
+}
+
+/*****************************************************************************/
+
+static void
+test_g_hash_table_get_keys_as_array (void)
+{
+	GHashTable *table = g_hash_table_new (g_str_hash, g_str_equal);
+	guint length = 0;
+	char **keys;
+
+	g_hash_table_insert (table, "one",   "1");
+	g_hash_table_insert (table, "two",   "2");
+	g_hash_table_insert (table, "three", "3");
+
+	keys = (char **) _nm_g_hash_table_get_keys_as_array (table, &length);
+	g_assert (keys);
+	g_assert_cmpuint (length, ==, 3);
+
+	g_assert (   !strcmp (keys[0], "one")
+	          || !strcmp (keys[1], "one")
+	          || !strcmp (keys[2], "one"));
+
+	g_assert (   !strcmp (keys[0], "two")
+	          || !strcmp (keys[1], "two")
+	          || !strcmp (keys[2], "two"));
+
+	g_assert (   !strcmp (keys[0], "three")
+	          || !strcmp (keys[1], "three")
+	          || !strcmp (keys[2], "three"));
+
+	g_assert (!keys[3]);
+
+	g_free (keys);
+	g_hash_table_unref (table);
+}
+
+/*****************************************************************************/
+
 static int
 _test_find_binary_search_cmp (gconstpointer a, gconstpointer b, gpointer dummy)
 {
@@ -6169,9 +6196,7 @@ _test_find_binary_search_do_uint32 (const int *int_array, gsize len)
 	const int OFFSET = 100;
 	const int NEEDLE = 0 + OFFSET;
 	gssize expected_result = -1;
-	guint32 array[30];
-
-	g_assert (len <= G_N_ELEMENTS (array));
+	guint32 array[len];
 
 	/* the test data has negative values. Shift them... */
 	for (idx = 0; idx < len; idx++) {
@@ -6807,7 +6832,7 @@ test_route_attributes_format (void)
 	gs_unref_hashtable GHashTable *ht = NULL;
 	char *str;
 
-	ht = g_hash_table_new_full (nm_str_hash, g_str_equal,
+	ht = g_hash_table_new_full (g_str_hash, g_str_equal,
 	                            NULL, (GDestroyNotify) g_variant_unref);
 
 	str = nm_utils_format_variant_attributes (NULL, ' ', '=');
@@ -7014,6 +7039,8 @@ int main (int argc, char **argv)
 
 	g_test_add_func ("/core/general/_nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64);
 	g_test_add_func ("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two);
+	g_test_add_func ("/core/general/_glib_compat_g_ptr_array_insert", test_g_ptr_array_insert);
+	g_test_add_func ("/core/general/_glib_compat_g_hash_table_get_keys_as_array", test_g_hash_table_get_keys_as_array);
 	g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search", test_nm_utils_ptrarray_find_binary_search);
 	g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search_with_duplicates", test_nm_utils_ptrarray_find_binary_search_with_duplicates);
 	g_test_add_func ("/core/general/_nm_utils_strstrdictkey", test_nm_utils_strstrdictkey);
diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c
index 543ca629..313932f0 100644
--- a/libnm-core/tests/test-keyfile.c
+++ b/libnm-core/tests/test-keyfile.c
@@ -628,7 +628,7 @@ test_team_conf_read_valid (void)
 static void
 test_team_conf_read_invalid (void)
 {
-#if WITH_JSON_VALIDATION
+#if WITH_JANSSON
 	GKeyFile *keyfile = NULL;
 	gs_unref_object NMConnection *con = NULL;
 	NMSettingTeam *s_team;
@@ -719,33 +719,6 @@ test_user_1 (void)
 
 /*****************************************************************************/
 
-static void
-test_vpn_1 (void)
-{
-	gs_unref_keyfile GKeyFile *keyfile = NULL;
-	gs_unref_object NMConnection *con = NULL;
-	NMSettingVpn *s_vpn;
-
-	con = nmtst_create_connection_from_keyfile (
-	      "[connection]\n"
-	      "id=t\n"
-	      "type=vpn\n"
-	      "\n"
-	      "[vpn]\n"
-	      "service-type=a.b.c\n"
-	      "vpn-key-1=value1\n"
-	      "",
-	      "/test_vpn_1/invalid", NULL);
-	g_assert (con);
-	s_vpn = NM_SETTING_VPN (nm_connection_get_setting (con, NM_TYPE_SETTING_VPN));
-	g_assert (s_vpn);
-	g_assert_cmpstr (nm_setting_vpn_get_data_item (s_vpn, "vpn-key-1"), ==, "value1");
-
-	CLEAR (&con, &keyfile);
-}
-
-/*****************************************************************************/
-
 NMTST_DEFINE ();
 
 int main (int argc, char **argv)
@@ -758,7 +731,6 @@ int main (int argc, char **argv)
 	g_test_add_func ("/core/keyfile/test_team_conf_read/valid", test_team_conf_read_valid);
 	g_test_add_func ("/core/keyfile/test_team_conf_read/invalid", test_team_conf_read_invalid);
 	g_test_add_func ("/core/keyfile/test_user/1", test_user_1);
-	g_test_add_func ("/core/keyfile/test_vpn/1", test_vpn_1);
 
 	return g_test_run ();
 }
diff --git a/libnm-core/tests/test-secrets.c b/libnm-core/tests/test-secrets.c
index 5961b6fc..3328e356 100644
--- a/libnm-core/tests/test-secrets.c
+++ b/libnm-core/tests/test-secrets.c
@@ -651,7 +651,8 @@ test_update_secrets_null_setting_name_with_setting_hash (void)
 
 	secrets = build_wep_secrets (wepkey);
 
-	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (setting_name != NULL || full_connection));
+	g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL,
+	                       NMTST_G_RETURN_MSG (setting_name != NULL || full_connection));
 	success = nm_connection_update_secrets (connection, NULL, secrets, &error);
 	g_test_assert_expected_messages ();
 	g_assert_no_error (error);
diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c
index d0e0b377..a7282f91 100644
--- a/libnm-core/tests/test-setting.c
+++ b/libnm-core/tests/test-setting.c
@@ -663,23 +663,23 @@ test_dcb_flags_invalid (void)
 	s_dcb = (NMSettingDcb *) nm_setting_dcb_new ();
 	g_assert (s_dcb);
 
-	NMTST_EXPECT ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+	g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
 	TEST_FLAG (NM_SETTING_DCB_APP_FCOE_FLAGS, nm_setting_dcb_get_app_fcoe_flags, 0x332523);
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+	g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
 	TEST_FLAG (NM_SETTING_DCB_APP_ISCSI_FLAGS, nm_setting_dcb_get_app_iscsi_flags, 0xFF);
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+	g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
 	TEST_FLAG (NM_SETTING_DCB_APP_FIP_FLAGS, nm_setting_dcb_get_app_fip_flags, 0x1111);
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+	g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
 	TEST_FLAG (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, nm_setting_dcb_get_priority_flow_control_flags, G_MAXUINT32);
 	g_test_assert_expected_messages ();
 
-	NMTST_EXPECT ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
+	g_test_expect_message ("GLib-GObject", G_LOG_LEVEL_WARNING, "*invalid or out of range*");
 	TEST_FLAG (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, nm_setting_dcb_get_priority_group_flags,
 	           (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING) + 1);
 	g_test_assert_expected_messages ();
@@ -869,7 +869,6 @@ test_dcb_bandwidth_sums (void)
 
 /*****************************************************************************/
 
-#if WITH_JSON_VALIDATION
 static void
 _test_team_config_sync (const char *team_config,
                         int notify_peer_count,
@@ -1259,7 +1258,6 @@ test_team_port_full_config (void)
 	                             "\"send_always\": true}]}",
 	                             10, 20, true, 30, 40, NULL);
 }
-#endif
 
 /*****************************************************************************/
 
@@ -1612,7 +1610,7 @@ main (int argc, char **argv)
 	g_test_add_func ("/libnm/settings/tc_config/setting", test_tc_config_setting);
 	g_test_add_func ("/libnm/settings/tc_config/dbus", test_tc_config_dbus);
 
-#if WITH_JSON_VALIDATION
+#if WITH_JANSSON
 	g_test_add_func ("/libnm/settings/team/sync_runner_from_config_roundrobin",
 	                 test_runner_roundrobin_sync_from_config);
 	g_test_add_func ("/libnm/settings/team/sync_runner_from_config_broadcast",
@@ -1641,6 +1639,7 @@ main (int argc, char **argv)
 	g_test_add_func ("/libnm/settings/team-port/sync_from_config_lacp_prio", test_team_port_lacp_prio);
 	g_test_add_func ("/libnm/settings/team-port/sync_from_config_lacp_key", test_team_port_lacp_key);
 	g_test_add_func ("/libnm/settings/team-port/sycn_from_config_full", test_team_port_full_config);
+
 #endif
 
 	return g_test_run ();