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/meson.build30
-rw-r--r--libnm-core/nm-connection.c405
-rw-r--r--libnm-core/nm-connection.h6
-rw-r--r--libnm-core/nm-core-enum-types.c.template4
-rw-r--r--libnm-core/nm-core-internal.h222
-rw-r--r--libnm-core/nm-core-types.h10
-rw-r--r--libnm-core/nm-crypto.c1
-rw-r--r--libnm-core/nm-dbus-interface.h110
-rw-r--r--libnm-core/nm-dbus-types.xml54
-rw-r--r--libnm-core/nm-dbus-utils.c91
-rw-r--r--libnm-core/nm-errors.c3
-rw-r--r--libnm-core/nm-errors.h2
-rw-r--r--libnm-core/nm-keyfile-internal.h15
-rw-r--r--libnm-core/nm-keyfile-utils.c61
-rw-r--r--libnm-core/nm-keyfile-utils.h15
-rw-r--r--libnm-core/nm-keyfile.c1119
-rw-r--r--libnm-core/nm-property-compare.c5
-rw-r--r--libnm-core/nm-setting-6lowpan.c80
-rw-r--r--libnm-core/nm-setting-8021x.c10
-rw-r--r--libnm-core/nm-setting-8021x.h9
-rw-r--r--libnm-core/nm-setting-adsl.c225
-rw-r--r--libnm-core/nm-setting-bluetooth.c128
-rw-r--r--libnm-core/nm-setting-bond.c106
-rw-r--r--libnm-core/nm-setting-bridge-port.c125
-rw-r--r--libnm-core/nm-setting-bridge.c284
-rw-r--r--libnm-core/nm-setting-cdma.c189
-rw-r--r--libnm-core/nm-setting-connection.c652
-rw-r--r--libnm-core/nm-setting-dcb.c432
-rw-r--r--libnm-core/nm-setting-dummy.c34
-rw-r--r--libnm-core/nm-setting-generic.c19
-rw-r--r--libnm-core/nm-setting-gsm.c367
-rw-r--r--libnm-core/nm-setting-gsm.h8
-rw-r--r--libnm-core/nm-setting-infiniband.c204
-rw-r--r--libnm-core/nm-setting-ip-config.c618
-rw-r--r--libnm-core/nm-setting-ip-config.h4
-rw-r--r--libnm-core/nm-setting-ip-tunnel.c336
-rw-r--r--libnm-core/nm-setting-ip4-config.c229
-rw-r--r--libnm-core/nm-setting-ip6-config.c180
-rw-r--r--libnm-core/nm-setting-macsec.c130
-rw-r--r--libnm-core/nm-setting-macvlan.c158
-rw-r--r--libnm-core/nm-setting-match.c17
-rw-r--r--libnm-core/nm-setting-olpc-mesh.c154
-rw-r--r--libnm-core/nm-setting-ovs-bridge.c66
-rw-r--r--libnm-core/nm-setting-ovs-interface.c27
-rw-r--r--libnm-core/nm-setting-ovs-patch.c25
-rw-r--r--libnm-core/nm-setting-ovs-port.c102
-rw-r--r--libnm-core/nm-setting-ppp.c420
-rw-r--r--libnm-core/nm-setting-pppoe.c169
-rw-r--r--libnm-core/nm-setting-private.h29
-rw-r--r--libnm-core/nm-setting-proxy.c136
-rw-r--r--libnm-core/nm-setting-serial.c170
-rw-r--r--libnm-core/nm-setting-sriov.c200
-rw-r--r--libnm-core/nm-setting-tc-config.c267
-rw-r--r--libnm-core/nm-setting-team-port.c331
-rw-r--r--libnm-core/nm-setting-team.c651
-rw-r--r--libnm-core/nm-setting-team.h17
-rw-r--r--libnm-core/nm-setting-tun.c192
-rw-r--r--libnm-core/nm-setting-user.c46
-rw-r--r--libnm-core/nm-setting-vlan.c221
-rw-r--r--libnm-core/nm-setting-vpn.c540
-rw-r--r--libnm-core/nm-setting-vxlan.c417
-rw-r--r--libnm-core/nm-setting-wifi-p2p.c317
-rw-r--r--libnm-core/nm-setting-wifi-p2p.h69
-rw-r--r--libnm-core/nm-setting-wimax.c130
-rw-r--r--libnm-core/nm-setting-wired.c459
-rw-r--r--libnm-core/nm-setting-wired.h27
-rw-r--r--libnm-core/nm-setting-wireguard.c2499
-rw-r--r--libnm-core/nm-setting-wireguard.h213
-rw-r--r--libnm-core/nm-setting-wireless-security.c686
-rw-r--r--libnm-core/nm-setting-wireless-security.h9
-rw-r--r--libnm-core/nm-setting-wireless.c510
-rw-r--r--libnm-core/nm-setting-wireless.h32
-rw-r--r--libnm-core/nm-setting-wpan.c191
-rw-r--r--libnm-core/nm-setting-wpan.h9
-rw-r--r--libnm-core/nm-setting.c1199
-rw-r--r--libnm-core/nm-setting.h98
-rw-r--r--libnm-core/nm-simple-connection.c5
-rw-r--r--libnm-core/nm-utils-private.h18
-rw-r--r--libnm-core/nm-utils.c785
-rw-r--r--libnm-core/nm-utils.h6
-rw-r--r--libnm-core/nm-version.h14
-rw-r--r--libnm-core/nm-vpn-editor-plugin.c36
-rw-r--r--libnm-core/nm-vpn-plugin-info.c166
-rw-r--r--libnm-core/tests/meson.build14
-rw-r--r--libnm-core/tests/test-crypto.c1
-rw-r--r--libnm-core/tests/test-general.c405
-rw-r--r--libnm-core/tests/test-keyfile.c4
-rw-r--r--libnm-core/tests/test-secrets.c2
-rw-r--r--libnm-core/tests/test-setting.c692
-rw-r--r--libnm-core/tests/test-settings-defaults.c2
90 files changed, 12765 insertions, 6710 deletions
diff --git a/libnm-core/meson.build b/libnm-core/meson.build
index ac13a1be..d10dd1c5 100644
--- a/libnm-core/meson.build
+++ b/libnm-core/meson.build
@@ -45,8 +45,10 @@ libnm_core_headers = files(
   'nm-setting-vlan.h',
   'nm-setting-vpn.h',
   'nm-setting-vxlan.h',
+  'nm-setting-wifi-p2p.h',
   'nm-setting-wimax.h',
   'nm-setting-wired.h',
+  'nm-setting-wireguard.h',
   'nm-setting-wireless-security.h',
   'nm-setting-wireless.h',
   'nm-setting-wpan.h',
@@ -56,7 +58,7 @@ libnm_core_headers = files(
   'nm-version.h',
   'nm-vpn-dbus-interface.h',
   'nm-vpn-editor-plugin.h',
-  'nm-vpn-plugin-info.h'
+  'nm-vpn-plugin-info.h',
 )
 
 libnm_core_settings_sources = files(
@@ -100,11 +102,13 @@ libnm_core_settings_sources = files(
   'nm-setting-vlan.c',
   'nm-setting-vpn.c',
   'nm-setting-vxlan.c',
+  'nm-setting-wifi-p2p.c',
   'nm-setting-wimax.c',
   'nm-setting-wired.c',
+  'nm-setting-wireguard.c',
   'nm-setting-wireless-security.c',
   'nm-setting-wireless.c',
-  'nm-setting-wpan.c'
+  'nm-setting-wpan.c',
 )
 
 libnm_core_sources = libnm_core_settings_sources + files(
@@ -119,7 +123,7 @@ libnm_core_sources = libnm_core_settings_sources + files(
   'nm-simple-connection.c',
   'nm-utils.c',
   'nm-vpn-editor-plugin.c',
-  'nm-vpn-plugin-info.c'
+  'nm-vpn-plugin-info.c',
 )
 
 enum_headers = libnm_core_headers + [version_header]
@@ -132,15 +136,17 @@ libnm_core_enum = gnome.mkenums(
   c_template: enum_types + '.c.template',
   h_template: enum_types + '.h.template',
   install_header: true,
-  install_dir: libnm_pkgincludedir
+  install_dir: libnm_pkgincludedir,
 )
 
 deps = [
   dl_dep,
   libudev_dep,
-  shared_dep,
   uuid_dep,
+  glib_dep,
   shared_c_siphash_dep,
+  shared_nm_utils_base_dep,
+  shared_nm_utils_udev_dep,
 ]
 
 cflags = [
@@ -158,7 +164,7 @@ if (crypto_gnutls_dep.found())
     'nm-crypto-gnutls',
     sources: [ 'nm-crypto-gnutls.c' ],
     dependencies: deps + [ crypto_gnutls_dep ],
-    c_args: cflags
+    c_args: cflags,
   )
 endif
 
@@ -167,7 +173,7 @@ if (crypto_nss_dep.found())
     'nm-crypto-nss',
     sources: [ 'nm-crypto-nss.c' ],
     dependencies: deps + [ crypto_nss_dep ],
-    c_args: cflags
+    c_args: cflags,
   )
 endif
 
@@ -183,7 +189,6 @@ libnm_core_sources_all =  libnm_core_sources
 libnm_core_sources_all += libnm_core_enum
 libnm_core_sources_all += shared_nm_meta_setting_c
 libnm_core_sources_all += shared_nm_ethtool_utils_c
-libnm_core_sources_all += shared_files_libnm_core
 libnm_core_sources_all += [version_header]
 
 libnm_core = static_library(
@@ -191,15 +196,16 @@ libnm_core = static_library(
   sources: libnm_core_sources_all,
   dependencies: deps,
   link_with: libnm_crypto,
-  c_args: cflags
+  c_args: cflags,
 )
 
 nm_core_dep = declare_dependency(
   sources: libnm_core_enum[1],
   include_directories: libnm_core_inc,
   dependencies: [
-    shared_dep,
+    shared_nm_utils_base_dep,
     shared_c_siphash_dep,
+    libnm_systemd_shared_dep,
   ],
 )
 
@@ -207,7 +213,7 @@ 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']
+  ['nm-vpn-dbus-types', 'nm-vpn-dbus-interface.h', 'VPN Plugin D-Bus API Types'],
 ]
 
 foreach docbook: docbooks
@@ -221,7 +227,7 @@ foreach docbook: docbooks
     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
+    build_by_default: true,
   )
 
   content_files += xml.full_path()
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
index 5f406cc6..ea7730bf 100644
--- a/libnm-core/nm-connection.c
+++ b/libnm-core/nm-connection.c
@@ -22,10 +22,10 @@
 
 #include "nm-default.h"
 
-#include <string.h>
+#include "nm-connection.h"
+
 #include <arpa/inet.h>
 
-#include "nm-connection.h"
 #include "nm-connection-private.h"
 #include "nm-utils.h"
 #include "nm-setting-private.h"
@@ -50,6 +50,17 @@
  *
  */
 
+/*****************************************************************************/
+
+enum {
+	SECRETS_UPDATED,
+	SECRETS_CLEARED,
+	CHANGED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 typedef struct {
 	NMConnection *self;
 
@@ -59,19 +70,26 @@ typedef struct {
 	char *path;
 } NMConnectionPrivate;
 
+G_DEFINE_INTERFACE (NMConnection, nm_connection, G_TYPE_OBJECT)
+
 static NMConnectionPrivate *nm_connection_get_private (NMConnection *connection);
 #define NM_CONNECTION_GET_PRIVATE(o) (nm_connection_get_private ((NMConnection *)o))
 
-G_DEFINE_INTERFACE (NMConnection, nm_connection, G_TYPE_OBJECT)
+/*****************************************************************************/
 
-enum {
-	SECRETS_UPDATED,
-	SECRETS_CLEARED,
-	CHANGED,
-	LAST_SIGNAL
-};
+static gpointer
+_gtype_to_hash_key (GType gtype)
+{
+#if NM_MORE_ASSERTS
+	_nm_unused const gsize *const test_gtype_typedef = &gtype;
 
-static guint signals[LAST_SIGNAL] = { 0 };
+	nm_assert ((GType) (GPOINTER_TO_SIZE (GSIZE_TO_POINTER (gtype))) == gtype);
+	G_STATIC_ASSERT_EXPR (sizeof (gpointer) >= sizeof (gsize));
+	G_STATIC_ASSERT_EXPR (sizeof (gsize) == sizeof (GType));
+#endif
+
+	return GSIZE_TO_POINTER (gtype);
+}
 
 /*****************************************************************************/
 
@@ -83,10 +101,16 @@ setting_changed_cb (NMSetting *setting,
 	g_signal_emit (self, signals[CHANGED], 0);
 }
 
+static void
+_setting_release (NMConnection *connection, NMSetting *setting)
+{
+	g_signal_handlers_disconnect_by_func (setting, setting_changed_cb, connection);
+}
+
 static gboolean
-_setting_release (gpointer key, gpointer value, gpointer user_data)
+_setting_release_hfr (gpointer key, gpointer value, gpointer user_data)
 {
-	g_signal_handlers_disconnect_by_func (user_data, setting_changed_cb, value);
+	_setting_release (user_data, value);
 	return TRUE;
 }
 
@@ -94,19 +118,20 @@ static void
 _nm_connection_add_setting (NMConnection *connection, NMSetting *setting)
 {
 	NMConnectionPrivate *priv;
-	const char *name;
+	GType setting_type;
 	NMSetting *s_old;
 
 	nm_assert (NM_IS_CONNECTION (connection));
 	nm_assert (NM_IS_SETTING (setting));
 
 	priv = NM_CONNECTION_GET_PRIVATE (connection);
-	name = G_OBJECT_TYPE_NAME (setting);
+	setting_type = G_OBJECT_TYPE (setting);
+
+	if ((s_old = g_hash_table_lookup (priv->settings, _gtype_to_hash_key (setting_type))))
+		_setting_release (connection, s_old);
+
+	g_hash_table_insert (priv->settings, _gtype_to_hash_key (setting_type), setting);
 
-	if ((s_old = g_hash_table_lookup (priv->settings, (gpointer) name)))
-		g_signal_handlers_disconnect_by_func (s_old, setting_changed_cb, connection);
-	g_hash_table_insert (priv->settings, (gpointer) name, setting);
-	/* Listen for property changes so we can emit the 'changed' signal */
 	g_signal_connect (setting, "notify", (GCallback) setting_changed_cb, connection);
 }
 
@@ -135,17 +160,15 @@ _nm_connection_remove_setting (NMConnection *connection, GType setting_type)
 {
 	NMConnectionPrivate *priv;
 	NMSetting *setting;
-	const char *setting_name;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
 	g_return_val_if_fail (g_type_is_a (setting_type, NM_TYPE_SETTING), FALSE);
 
 	priv = NM_CONNECTION_GET_PRIVATE (connection);
-	setting_name = g_type_name (setting_type);
-	setting = g_hash_table_lookup (priv->settings, setting_name);
+	setting = g_hash_table_lookup (priv->settings, _gtype_to_hash_key (setting_type));
 	if (setting) {
 		g_signal_handlers_disconnect_by_func (setting, setting_changed_cb, connection);
-		g_hash_table_remove (priv->settings, setting_name);
+		g_hash_table_remove (priv->settings, _gtype_to_hash_key (setting_type));
 		g_signal_emit (connection, signals[CHANGED], 0);
 		return TRUE;
 	}
@@ -169,11 +192,15 @@ nm_connection_remove_setting (NMConnection *connection, GType setting_type)
 static gpointer
 _connection_get_setting (NMConnection *connection, GType setting_type)
 {
+	NMSetting *setting;
+
 	nm_assert (NM_IS_CONNECTION (connection));
 	nm_assert (g_type_is_a (setting_type, NM_TYPE_SETTING));
 
-	return g_hash_table_lookup (NM_CONNECTION_GET_PRIVATE (connection)->settings,
-	                            g_type_name (setting_type));
+	setting = g_hash_table_lookup (NM_CONNECTION_GET_PRIVATE (connection)->settings,
+	                               _gtype_to_hash_key (setting_type));
+	nm_assert (!setting || G_TYPE_CHECK_INSTANCE_TYPE (setting, setting_type));
+	return setting;
 }
 
 static gpointer
@@ -203,6 +230,18 @@ nm_connection_get_setting (NMConnection *connection, GType setting_type)
 	return _connection_get_setting_check (connection, setting_type);
 }
 
+NMSettingIPConfig *
+nm_connection_get_setting_ip_config (NMConnection *connection,
+                                     int addr_family)
+{
+	nm_assert_addr_family (addr_family);
+
+	return NM_SETTING_IP_CONFIG (_connection_get_setting (connection,
+	                                                        (addr_family == AF_INET)
+	                                                      ? NM_TYPE_SETTING_IP4_CONFIG
+	                                                      : NM_TYPE_SETTING_IP6_CONFIG));
+}
+
 /**
  * nm_connection_get_setting_by_name:
  * @connection: a #NMConnection
@@ -388,7 +427,7 @@ _nm_connection_replace_settings (NMConnection *connection,
 	}
 
 	if (g_hash_table_size (priv->settings) > 0) {
-		g_hash_table_foreach_remove (priv->settings, _setting_release, connection);
+		g_hash_table_foreach_remove (priv->settings, _setting_release_hfr, connection);
 		changed = TRUE;
 	} else
 		changed = (settings != NULL);
@@ -473,7 +512,7 @@ nm_connection_replace_settings_from_connection (NMConnection *connection,
 	new_priv = NM_CONNECTION_GET_PRIVATE (new_connection);
 
 	if ((changed = g_hash_table_size (priv->settings) > 0))
-		g_hash_table_foreach_remove (priv->settings, _setting_release, connection);
+		g_hash_table_foreach_remove (priv->settings, _setting_release_hfr, connection);
 
 	if (g_hash_table_size (new_priv->settings)) {
 		g_hash_table_iter_init (&iter, new_priv->settings);
@@ -502,7 +541,7 @@ nm_connection_clear_settings (NMConnection *connection)
 	priv = NM_CONNECTION_GET_PRIVATE (connection);
 
 	if (g_hash_table_size (priv->settings) > 0) {
-		g_hash_table_foreach_remove (priv->settings, _setting_release, connection);
+		g_hash_table_foreach_remove (priv->settings, _setting_release_hfr, connection);
 		g_signal_emit (connection, signals[CHANGED], 0);
 	}
 }
@@ -646,8 +685,10 @@ _nm_connection_find_base_type_setting (NMConnection *connection)
 {
 	NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection);
 	GHashTableIter iter;
-	NMSetting *setting = NULL, *s_iter;
-	NMSettingPriority setting_prio, s_iter_prio;
+	NMSetting *setting = NULL;
+	NMSetting *s_iter;
+	NMSettingPriority setting_prio = NM_SETTING_PRIORITY_USER;
+	NMSettingPriority s_iter_prio;
 
 	g_hash_table_iter_init (&iter, priv->settings);
 	while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &s_iter)) {
@@ -876,25 +917,24 @@ _supports_addr_family (NMConnection *self, int family)
 static gboolean
 _normalize_ip_config (NMConnection *self, GHashTable *parameters)
 {
-	const char *default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
-	const char *default_ip6_method = NULL;
 	NMSettingIPConfig *s_ip4, *s_ip6;
 	NMSettingProxy *s_proxy;
 	NMSetting *setting;
 	gboolean changed = FALSE;
 	guint num, i;
 
-	if (parameters)
-		default_ip6_method = g_hash_table_lookup (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD);
-	if (!default_ip6_method)
-		default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
-
 	s_ip4 = nm_connection_get_setting_ip4_config (self);
 	s_ip6 = nm_connection_get_setting_ip6_config (self);
 	s_proxy = nm_connection_get_setting_proxy (self);
 
 	if (_supports_addr_family (self, AF_INET)) {
+
 		if (!s_ip4) {
+			const char *default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
+
+			if (nm_connection_is_type (self, NM_SETTING_WIREGUARD_SETTING_NAME))
+				default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+
 			 /* But if no IP4 setting was specified, assume the caller was just
 			  * being lazy and use the default method.
 			  */
@@ -937,6 +977,17 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters)
 
 	if (_supports_addr_family (self, AF_INET6)) {
 		if (!s_ip6) {
+			const char *default_ip6_method = NULL;
+
+			if (parameters)
+				default_ip6_method = g_hash_table_lookup (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD);
+			if (!default_ip6_method) {
+				if (nm_connection_is_type (self, NM_SETTING_WIREGUARD_SETTING_NAME))
+					default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
+				else
+					default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
+			}
+
 			/* If no IP6 setting was specified, then assume that means IP6 config is
 			 * allowed to fail.
 			 */
@@ -1811,26 +1862,14 @@ nm_connection_need_secrets (NMConnection *connection,
 void
 nm_connection_clear_secrets (NMConnection *connection)
 {
-	GHashTableIter iter;
-	NMSetting *setting;
-
-	g_return_if_fail (NM_IS_CONNECTION (connection));
-
-	g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings);
-	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) {
-		g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection);
-		_nm_setting_clear_secrets (setting);
-		g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection);
-	}
-
-	g_signal_emit (connection, signals[SECRETS_CLEARED], 0);
+	return nm_connection_clear_secrets_with_flags (connection, NULL, NULL);
 }
 
 /**
  * nm_connection_clear_secrets_with_flags:
  * @connection: the #NMConnection
- * @func: (scope call): function to be called to determine whether a
- *     specific secret should be cleared or not
+ * @func: (scope call) (allow-none): function to be called to determine whether a
+ *     specific secret should be cleared or not. If %NULL, all secrets are cleared.
  * @user_data: caller-supplied data passed to @func
  *
  * Clears and frees secrets determined by @func.
@@ -1848,13 +1887,115 @@ nm_connection_clear_secrets_with_flags (NMConnection *connection,
 	g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings);
 	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) {
 		g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection);
-		_nm_setting_clear_secrets_with_flags (setting, func, user_data);
+		_nm_setting_clear_secrets (setting, func, user_data);
 		g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection);
 	}
 
 	g_signal_emit (connection, signals[SECRETS_CLEARED], 0);
 }
 
+/*****************************************************************************/
+
+/* Returns always a non-NULL, floating variant that must
+ * be unrefed by the caller. */
+GVariant *
+_nm_connection_for_each_secret (NMConnection *self,
+                                GVariant *secrets,
+                                gboolean remove_non_secrets,
+                                _NMConnectionForEachSecretFunc callback,
+                                gpointer callback_data)
+{
+	GVariantBuilder secrets_builder;
+	GVariantBuilder setting_builder;
+	GVariantIter secrets_iter;
+	GVariantIter *setting_iter;
+	const char *setting_name;
+
+	/* This function, given a dict of dicts representing new secrets of
+	 * an NMConnection, walks through each toplevel dict (which represents a
+	 * NMSetting), and for each setting, walks through that setting dict's
+	 * properties.  For each property that's a secret, it will check that
+	 * secret's flags in the backing NMConnection object, and call a supplied
+	 * callback.
+	 *
+	 * The one complexity is that the VPN setting's 'secrets' property is
+	 * *also* a dict (since the key/value pairs are arbitrary and known
+	 * only to the VPN plugin itself).  That means we have three levels of
+	 * dicts that we potentially have to traverse here.  The differences
+	 * are handled by the virtual for_each_secret() function.
+	 */
+
+	g_return_val_if_fail (callback, NULL);
+
+	g_variant_iter_init (&secrets_iter, secrets);
+	g_variant_builder_init (&secrets_builder, NM_VARIANT_TYPE_CONNECTION);
+	while (g_variant_iter_next (&secrets_iter, "{&sa{sv}}", &setting_name, &setting_iter)) {
+		_nm_unused nm_auto_free_variant_iter GVariantIter *setting_iter_free = setting_iter;
+		NMSetting *setting;
+		const char *secret_name;
+		GVariant *val;
+
+		setting = nm_connection_get_setting_by_name (self, setting_name);
+		if (!setting)
+			continue;
+
+		g_variant_builder_init (&setting_builder, NM_VARIANT_TYPE_SETTING);
+		while (g_variant_iter_next (setting_iter, "{&sv}", &secret_name, &val)) {
+			_nm_unused gs_unref_variant GVariant *val_free = val;
+
+			NM_SETTING_GET_CLASS (setting)->for_each_secret (setting,
+			                                                 secret_name,
+			                                                 val,
+			                                                 remove_non_secrets,
+			                                                 callback,
+			                                                 callback_data,
+			                                                 &setting_builder);
+		}
+
+		g_variant_builder_add (&secrets_builder, "{sa{sv}}", setting_name, &setting_builder);
+	}
+
+	return g_variant_builder_end (&secrets_builder);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+	NMConnectionFindSecretFunc find_func;
+	gpointer find_func_data;
+	gboolean found;
+} FindSecretData;
+
+static gboolean
+find_secret_for_each_func (NMSettingSecretFlags flags,
+                           gpointer user_data)
+{
+	FindSecretData *data = user_data;
+
+	if (!data->found)
+		data->found = data->find_func (flags, data->find_func_data);
+	return FALSE;
+}
+
+gboolean
+_nm_connection_find_secret (NMConnection *self,
+                            GVariant *secrets,
+                            NMConnectionFindSecretFunc callback,
+                            gpointer callback_data)
+{
+	gs_unref_variant GVariant *dummy = NULL;
+	FindSecretData data = {
+		.find_func      = callback,
+		.find_func_data = callback_data,
+		.found          = FALSE,
+	};
+
+	dummy = _nm_connection_for_each_secret (self, secrets, FALSE, find_secret_for_each_func, &data);
+	return data.found;
+}
+
+/*****************************************************************************/
+
 /**
  * nm_connection_to_dbus:
  * @connection: the #NMConnection
@@ -1874,7 +2015,7 @@ nm_connection_to_dbus (NMConnection *connection,
 	NMConnectionPrivate *priv;
 	GVariantBuilder builder;
 	GHashTableIter iter;
-	gpointer key, data;
+	gpointer data;
 	GVariant *setting_dict, *ret;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
@@ -1884,7 +2025,7 @@ nm_connection_to_dbus (NMConnection *connection,
 
 	/* Add each setting's hash to the main hash */
 	g_hash_table_iter_init (&iter, priv->settings);
-	while (g_hash_table_iter_next (&iter, &key, &data)) {
+	while (g_hash_table_iter_next (&iter, NULL, &data)) {
 		NMSetting *setting = NM_SETTING (data);
 
 		setting_dict = _nm_setting_to_dbus (setting, connection, flags);
@@ -1939,7 +2080,7 @@ _for_each_sort (NMSetting **p_a, NMSetting **p_b, void *unused)
 /**
  * nm_connection_get_settings:
  * @connection: the #NMConnection instance
- * @out_length: (allow-none): (out): the length of the returned array
+ * @out_length: (allow-none) (out): the length of the returned array
  *
  * Retrieves the settings in @connection.
  *
@@ -2016,6 +2157,73 @@ nm_connection_for_each_setting_value (NMConnection *connection,
 }
 
 /**
+ * _nm_connection_aggregate:
+ * @connecition: the #NMConnection for which values are to be aggregated.
+ * @type: one of the supported aggrate types.
+ * @arg: the input/output argument that depends on @type.
+ *
+ * For example, with %NM_CONNECTION_AGGREGATE_ANY_SECRETS and
+ * %NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS @arg is a boolean
+ * output argument. It is either %NULL or a pointer to an gboolean
+ * out-argument. The function will always set @arg if given.
+ * Also, the return value of the function is likewise the result
+ * that is set to @arg.
+ *
+ * Returns: a boolean result with the meaning depending on the aggregation
+ *   type @type.
+ */
+gboolean
+_nm_connection_aggregate (NMConnection *connection,
+                          NMConnectionAggregateType type,
+                          gpointer arg)
+{
+	NMConnectionPrivate *priv;
+	GHashTableIter iter;
+	NMSetting *setting;
+	gboolean arg_boolean;
+	gboolean completed_early;
+	gpointer my_arg;
+
+	g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
+
+	switch (type) {
+	case NM_CONNECTION_AGGREGATE_ANY_SECRETS:
+		arg_boolean = FALSE;
+		my_arg = &arg_boolean;
+		goto good;
+	case NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS:
+		arg_boolean = FALSE;
+		my_arg = &arg_boolean;
+		goto good;
+	}
+	g_return_val_if_reached (FALSE);
+
+good:
+	priv = NM_CONNECTION_GET_PRIVATE (connection);
+
+	completed_early = FALSE;
+	g_hash_table_iter_init (&iter, priv->settings);
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) {
+		if (_nm_setting_aggregate (setting, type, my_arg)) {
+			completed_early = TRUE;
+			break;
+		}
+		nm_assert (   my_arg != &arg_boolean
+		           || !arg_boolean);
+	}
+
+	if (my_arg == &arg_boolean) {
+		nm_assert (completed_early == arg_boolean);
+		if (arg)
+			*((gboolean *) arg) = arg_boolean;
+		return arg_boolean;
+	}
+
+	nm_assert_not_reached ();
+	return FALSE;
+}
+
+/**
  * nm_connection_dump:
  * @connection: the #NMConnection
  *
@@ -2029,14 +2237,13 @@ nm_connection_dump (NMConnection *connection)
 {
 	GHashTableIter iter;
 	NMSetting *setting;
-	const char *setting_name;
 	char *str;
 
 	if (!connection)
 		return;
 
 	g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings);
-	while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, (gpointer) &setting)) {
+	while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) {
 		str = nm_setting_to_string (setting);
 		g_print ("%s\n", str);
 		g_free (str);
@@ -2221,23 +2428,24 @@ nm_connection_is_virtual (NMConnection *connection)
 	if (!type)
 		return FALSE;
 
-	if (   !strcmp (type, NM_SETTING_6LOWPAN_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_BOND_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_DUMMY_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_TEAM_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_VLAN_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_TUN_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_IP_TUNNEL_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_MACSEC_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_MACVLAN_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_OVS_BRIDGE_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_OVS_INTERFACE_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_OVS_PORT_SETTING_NAME)
-	    || !strcmp (type, NM_SETTING_VXLAN_SETTING_NAME))
+	if (NM_IN_STRSET (type, NM_SETTING_6LOWPAN_SETTING_NAME,
+	                        NM_SETTING_BOND_SETTING_NAME,
+	                        NM_SETTING_BRIDGE_SETTING_NAME,
+	                        NM_SETTING_DUMMY_SETTING_NAME,
+	                        NM_SETTING_IP_TUNNEL_SETTING_NAME,
+	                        NM_SETTING_MACSEC_SETTING_NAME,
+	                        NM_SETTING_MACVLAN_SETTING_NAME,
+	                        NM_SETTING_OVS_BRIDGE_SETTING_NAME,
+	                        NM_SETTING_OVS_INTERFACE_SETTING_NAME,
+	                        NM_SETTING_OVS_PORT_SETTING_NAME,
+	                        NM_SETTING_TEAM_SETTING_NAME,
+	                        NM_SETTING_TUN_SETTING_NAME,
+	                        NM_SETTING_VLAN_SETTING_NAME,
+	                        NM_SETTING_VXLAN_SETTING_NAME,
+	                        NM_SETTING_WIREGUARD_SETTING_NAME))
 		return TRUE;
 
-	if (!strcmp (type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
+	if (nm_streq (type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
 		NMSettingInfiniband *s_ib;
 
 		s_ib = nm_connection_get_setting_infiniband (connection);
@@ -2886,7 +3094,7 @@ nm_connection_private_free (NMConnectionPrivate *priv)
 {
 	NMConnection *self = priv->self;
 
-	g_hash_table_foreach_remove (priv->settings, _setting_release, self);
+	g_hash_table_foreach_remove (priv->settings, _setting_release_hfr, self);
 	g_hash_table_destroy (priv->settings);
 	g_free (priv->path);
 
@@ -2910,7 +3118,10 @@ 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 (nm_direct_hash,
+		                                        NULL,
+		                                        NULL,
+		                                        g_object_unref);
 	}
 
 	return priv;
@@ -2919,8 +3130,6 @@ nm_connection_get_private (NMConnection *connection)
 static void
 nm_connection_default_init (NMConnectionInterface *iface)
 {
-	/* Signals */
-
 	/**
 	 * NMConnection::secrets-updated:
 	 * @connection: the object on which the signal is emitted
@@ -2931,14 +3140,14 @@ nm_connection_default_init (NMConnectionInterface *iface)
 	 * have been changed.
 	 */
 	signals[SECRETS_UPDATED] =
-		g_signal_new (NM_CONNECTION_SECRETS_UPDATED,
-		              NM_TYPE_CONNECTION,
-		              G_SIGNAL_RUN_FIRST,
-		              G_STRUCT_OFFSET (NMConnectionInterface, secrets_updated),
-		              NULL, NULL,
-		              g_cclosure_marshal_VOID__STRING,
-		              G_TYPE_NONE, 1,
-		              G_TYPE_STRING);
+	    g_signal_new (NM_CONNECTION_SECRETS_UPDATED,
+	                 NM_TYPE_CONNECTION,
+	                 G_SIGNAL_RUN_FIRST,
+	                 G_STRUCT_OFFSET (NMConnectionInterface, secrets_updated),
+	                 NULL, NULL,
+	                 g_cclosure_marshal_VOID__STRING,
+	                 G_TYPE_NONE, 1,
+	                 G_TYPE_STRING);
 
 	/**
 	 * NMConnection::secrets-cleared:
@@ -2948,13 +3157,13 @@ nm_connection_default_init (NMConnectionInterface *iface)
 	 * are cleared.
 	 */
 	signals[SECRETS_CLEARED] =
-		g_signal_new (NM_CONNECTION_SECRETS_CLEARED,
-		              NM_TYPE_CONNECTION,
-		              G_SIGNAL_RUN_FIRST,
-		              G_STRUCT_OFFSET (NMConnectionInterface, secrets_cleared),
-		              NULL, NULL,
-		              g_cclosure_marshal_VOID__VOID,
-		              G_TYPE_NONE, 0);
+	    g_signal_new (NM_CONNECTION_SECRETS_CLEARED,
+	                 NM_TYPE_CONNECTION,
+	                 G_SIGNAL_RUN_FIRST,
+	                 G_STRUCT_OFFSET (NMConnectionInterface, secrets_cleared),
+	                 NULL, NULL,
+	                 g_cclosure_marshal_VOID__VOID,
+	                 G_TYPE_NONE, 0);
 
 	/**
 	 * NMConnection::changed:
@@ -2965,11 +3174,11 @@ nm_connection_default_init (NMConnectionInterface *iface)
 	 * or when settings are added or removed.
 	 */
 	signals[CHANGED] =
-		g_signal_new (NM_CONNECTION_CHANGED,
-		              NM_TYPE_CONNECTION,
-		              G_SIGNAL_RUN_FIRST,
-		              G_STRUCT_OFFSET (NMConnectionInterface, changed),
-		              NULL, NULL,
-		              g_cclosure_marshal_VOID__VOID,
-		              G_TYPE_NONE, 0);
+	    g_signal_new (NM_CONNECTION_CHANGED,
+	                 NM_TYPE_CONNECTION,
+	                 G_SIGNAL_RUN_FIRST,
+	                 G_STRUCT_OFFSET (NMConnectionInterface, changed),
+	                 NULL, NULL,
+	                 g_cclosure_marshal_VOID__VOID,
+	                 G_TYPE_NONE, 0);
 }
diff --git a/libnm-core/nm-connection.h b/libnm-core/nm-connection.h
index b6e00977..8d64a4ce 100644
--- a/libnm-core/nm-connection.h
+++ b/libnm-core/nm-connection.h
@@ -45,7 +45,7 @@ G_BEGIN_DECLS
 
 /*
  * NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD: overwrite the ip6 method
- * when normalizing ip6 configuration. If omited, this defaults to
+ * when normalizing ip6 configuration. If omitted, this defaults to
  * @NM_SETTING_IP6_CONFIG_METHOD_AUTO.
  */
 #define NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD "ip6-config-method"
@@ -59,7 +59,7 @@ G_BEGIN_DECLS
 
 /**
  * NMConnectionInterface:
- * @parent: the parent interace struct
+ * @parent: the parent interface struct
  * @secrets_updated: emitted when the connection's secrets are updated
  * @secrets_cleared: emitted when the connection's secrets are cleared
  * @changed: emitted when any change to the connection's settings occurs
@@ -121,8 +121,6 @@ typedef enum { /*< flags >*/
 	NM_CONNECTION_SERIALIZE_ALL = 0x00000000,
 	NM_CONNECTION_SERIALIZE_NO_SECRETS = 0x00000001,
 	NM_CONNECTION_SERIALIZE_ONLY_SECRETS = 0x00000002,
-
-	/* 0x80000000 is used for a private flag */
 } NMConnectionSerializationFlags;
 
 GVariant     *nm_connection_to_dbus       (NMConnection *connection,
diff --git a/libnm-core/nm-core-enum-types.c.template b/libnm-core/nm-core-enum-types.c.template
index 605fa34a..94744827 100644
--- a/libnm-core/nm-core-enum-types.c.template
+++ b/libnm-core/nm-core-enum-types.c.template
@@ -4,7 +4,6 @@
 #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"
@@ -44,14 +43,17 @@
 #include "nm-setting-vlan.h"
 #include "nm-setting-vpn.h"
 #include "nm-setting-vxlan.h"
+#include "nm-setting-wifi-p2p.h"
 #include "nm-setting-wimax.h"
 #include "nm-setting-wired.h"
+#include "nm-setting-wireguard.h"
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-wireless.h"
 #include "nm-setting-wpan.h"
 #include "nm-setting.h"
 #include "nm-simple-connection.h"
 #include "nm-utils.h"
+#include "nm-version-macros.h"
 #include "nm-version.h"
 #include "nm-vpn-dbus-interface.h"
 #include "nm-vpn-editor-plugin.h"
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index 9d2d0f2c..f2332f76 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -37,6 +37,8 @@
 
 #include "nm-connection.h"
 #include "nm-core-enum-types.h"
+#include "nm-core-types-internal.h"
+#include "nm-meta-setting.h"
 #include "nm-setting-6lowpan.h"
 #include "nm-setting-8021x.h"
 #include "nm-setting-adsl.h"
@@ -52,7 +54,6 @@
 #include "nm-setting-gsm.h"
 #include "nm-setting-infiniband.h"
 #include "nm-setting-ip-tunnel.h"
-#include "nm-setting-proxy.h"
 #include "nm-setting-ip4-config.h"
 #include "nm-setting-ip6-config.h"
 #include "nm-setting-macsec.h"
@@ -65,6 +66,7 @@
 #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-sriov.h"
 #include "nm-setting-tc-config.h"
@@ -74,8 +76,10 @@
 #include "nm-setting-vlan.h"
 #include "nm-setting-vpn.h"
 #include "nm-setting-vxlan.h"
+#include "nm-setting-wifi-p2p.h"
 #include "nm-setting-wimax.h"
 #include "nm-setting-wired.h"
+#include "nm-setting-wireguard.h"
 #include "nm-setting-wireless-security.h"
 #include "nm-setting-wireless.h"
 #include "nm-setting-wpan.h"
@@ -83,9 +87,7 @@
 #include "nm-simple-connection.h"
 #include "nm-utils.h"
 #include "nm-vpn-dbus-interface.h"
-#include "nm-core-types-internal.h"
 #include "nm-vpn-editor-plugin.h"
-#include "nm-meta-setting.h"
 
 /* IEEE 802.1D-1998 timer values */
 #define NM_BR_MIN_HELLO_TIME    1
@@ -126,11 +128,21 @@
  */
 #define NM_SETTING_COMPARE_FLAG_NONE ((NMSettingCompareFlags) 0)
 
-#define NM_SETTING_SECRET_FLAGS_ALL \
-	(NM_SETTING_SECRET_FLAG_NONE | \
-	 NM_SETTING_SECRET_FLAG_AGENT_OWNED | \
-	 NM_SETTING_SECRET_FLAG_NOT_SAVED | \
-	 NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
+/*****************************************************************************/
+
+#define NM_SETTING_SECRET_FLAG_ALL \
+	((NMSettingSecretFlags) (  NM_SETTING_SECRET_FLAG_NONE \
+	                         | NM_SETTING_SECRET_FLAG_AGENT_OWNED \
+	                         | NM_SETTING_SECRET_FLAG_NOT_SAVED \
+	                         | NM_SETTING_SECRET_FLAG_NOT_REQUIRED))
+
+static inline gboolean
+_nm_setting_secret_flags_valid (NMSettingSecretFlags flags)
+{
+	return !NM_FLAGS_ANY (flags, ~NM_SETTING_SECRET_FLAG_ALL);
+}
+
+/*****************************************************************************/
 
 typedef enum { /*< skip >*/
 	NM_SETTING_PARSE_FLAGS_NONE                     = 0,
@@ -151,6 +163,26 @@ gpointer _nm_connection_check_main_setting (NMConnection *connection,
                                             const char *setting_name,
                                             GError **error);
 
+typedef enum {
+	/* whether the connection has any secrets.
+	 *
+	 * @arg may be %NULL or a pointer to a gboolean for the result. The return
+	 *   value of _nm_connection_aggregate() is likewise the boolean result. */
+	NM_CONNECTION_AGGREGATE_ANY_SECRETS,
+
+	/* whether the connection has any secret with flags NM_SETTING_SECRET_FLAG_NONE.
+	 * Note that this only cares about the flags, not whether the secret is actually
+	 * present.
+	 *
+	 * @arg may be %NULL or a pointer to a gboolean for the result. The return
+	 *   value of _nm_connection_aggregate() is likewise the boolean result. */
+	NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS,
+} NMConnectionAggregateType;
+
+gboolean _nm_connection_aggregate (NMConnection *connection,
+                                   NMConnectionAggregateType type,
+                                   gpointer arg);
+
 /**
  * NMSettingVerifyResult:
  * @NM_SETTING_VERIFY_SUCCESS: the setting verifies successfully
@@ -179,13 +211,6 @@ NMSettingPriority _nm_setting_get_setting_priority (NMSetting *setting);
 
 gboolean _nm_setting_get_property (NMSetting *setting, const char *name, GValue *value);
 
-/* NM_CONNECTION_SERIALIZE_NO_SYNTH: This flag is passed to _nm_setting_to_dbus()
- * by nm_setting_to_string() to let it know that it shouldn't serialize the
- * synthetic properties. It wouldn't be able to do so, since the full connection
- * is not available, only the setting alone.
- */
-#define NM_CONNECTION_SERIALIZE_NO_SYNTH ((NMConnectionSerializationFlags) 0x80000000)
-
 /*****************************************************************************/
 
 GHashTable *_nm_setting_gendata_hash (NMSetting *setting,
@@ -224,27 +249,6 @@ guint nm_setting_ethtool_init_features (NMSettingEthtool *setting,
 guint8 *_nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize buffer_length, gsize *out_length);
 const char *nm_utils_hwaddr_ntoa_buf (gconstpointer addr, gsize addr_len, gboolean upper_case, char *buf, gsize buf_len);
 
-char *_nm_utils_bin2hexstr_full (gconstpointer addr, gsize length, const char delimiter, gboolean upper_case, char *out);
-
-guint8 *_nm_utils_hexstr2bin_full (const char *hexstr,
-                                   gboolean allow_0x_prefix,
-                                   gboolean delimiter_required,
-                                   const char *delimiter_candidates,
-                                   gsize required_len,
-                                   guint8 *buffer,
-                                   gsize buffer_len,
-                                   gsize *out_len);
-
-#define _nm_utils_hexstr2bin_buf(hexstr, allow_0x_prefix, delimiter_required, delimiter_candidates, buffer) \
-    _nm_utils_hexstr2bin_full ((hexstr), (allow_0x_prefix), (delimiter_required), (delimiter_candidates), G_N_ELEMENTS (buffer), (buffer), G_N_ELEMENTS (buffer), NULL)
-
-guint8 *_nm_utils_hexstr2bin_alloc (const char *hexstr,
-                                    gboolean allow_0x_prefix,
-                                    gboolean delimiter_required,
-                                    const char *delimiter_candidates,
-                                    gsize required_len,
-                                    gsize *out_len);
-
 GSList *    _nm_utils_hash_values_to_slist (GHashTable *hash);
 
 GHashTable *_nm_utils_copy_strdict (GHashTable *strdict);
@@ -334,6 +338,10 @@ void _nm_dbus_errors_init (void);
 
 extern gboolean _nm_utils_is_manager_process;
 
+gboolean _nm_dbus_typecheck_response (GVariant *response,
+                                      const GVariantType *reply_type,
+                                      GError **error);
+
 gulong _nm_dbus_signal_connect_data (GDBusProxy *proxy,
                                      const char *signal_name,
                                      const GVariantType *signature,
@@ -358,6 +366,11 @@ GVariant *_nm_dbus_proxy_call_sync   (GDBusProxy           *proxy,
                                       GCancellable         *cancellable,
                                       GError              **error);
 
+GVariant * _nm_dbus_connection_call_finish (GDBusConnection *dbus_connection,
+                                            GAsyncResult *result,
+                                            const GVariantType *reply_type,
+                                            GError **error);
+
 gboolean _nm_dbus_error_has_name (GError     *error,
                                   const char *dbus_error_name);
 
@@ -436,6 +449,11 @@ gboolean _nm_utils_generate_mac_address_mask_parse (const char *value,
 
 /*****************************************************************************/
 
+NMSettingIPConfig *nm_connection_get_setting_ip_config (NMConnection *connection,
+                                                        int addr_family);
+
+/*****************************************************************************/
+
 typedef enum {
 	NM_BOND_OPTION_TYPE_INT,
 	NM_BOND_OPTION_TYPE_STRING,
@@ -492,6 +510,30 @@ NMSettingBluetooth *_nm_connection_get_setting_bluetooth_for_nap (NMConnection *
 
 const char *nm_utils_inet_ntop (int addr_family, gconstpointer addr, char *dst);
 
+static inline char *
+nm_utils_inet4_ntop_dup (in_addr_t addr)
+{
+	char buf[NM_UTILS_INET_ADDRSTRLEN];
+
+	return g_strdup (nm_utils_inet4_ntop (addr, buf));
+}
+
+static inline char *
+nm_utils_inet6_ntop_dup (const struct in6_addr *addr)
+{
+	char buf[NM_UTILS_INET_ADDRSTRLEN];
+
+	return g_strdup (nm_utils_inet6_ntop (addr, buf));
+}
+
+static inline char *
+nm_utils_inet_ntop_dup (int addr_family, gconstpointer addr)
+{
+	char buf[NM_UTILS_INET_ADDRSTRLEN];
+
+	return g_strdup (nm_utils_inet_ntop (addr_family, addr, buf));
+}
+
 gboolean _nm_utils_inet6_is_token (const struct in6_addr *in6addr);
 
 /*****************************************************************************/
@@ -557,13 +599,44 @@ gboolean _nm_setting_sriov_sort_vfs (NMSettingSriov *setting);
 
 /*****************************************************************************/
 
-typedef struct _NMSettInfoSetting NMSettInfoSetting;
+typedef struct _NMSockAddrEndpoint NMSockAddrEndpoint;
+
+NMSockAddrEndpoint *nm_sock_addr_endpoint_new (const char *endpoint);
+
+NMSockAddrEndpoint *nm_sock_addr_endpoint_ref (NMSockAddrEndpoint *self);
+void nm_sock_addr_endpoint_unref (NMSockAddrEndpoint *self);
+
+const char *nm_sock_addr_endpoint_get_endpoint (NMSockAddrEndpoint *self);
+const char *nm_sock_addr_endpoint_get_host (NMSockAddrEndpoint *self);
+gint32 nm_sock_addr_endpoint_get_port (NMSockAddrEndpoint *self);
+
+gboolean nm_sock_addr_endpoint_get_fixed_sockaddr (NMSockAddrEndpoint *self,
+                                                   gpointer sockaddr);
+
+#define nm_auto_unref_sockaddrendpoint nm_auto(_nm_auto_unref_sockaddrendpoint)
+NM_AUTO_DEFINE_FCN0 (NMSockAddrEndpoint *, _nm_auto_unref_sockaddrendpoint, nm_sock_addr_endpoint_unref)
+
+/*****************************************************************************/
+
+NMSockAddrEndpoint *_nm_wireguard_peer_get_endpoint (const NMWireGuardPeer *self);
+void _nm_wireguard_peer_set_endpoint (NMWireGuardPeer *self,
+                                      NMSockAddrEndpoint *endpoint);
+
+void _nm_wireguard_peer_set_public_key_bin (NMWireGuardPeer *self,
+                                            const guint8 public_key[static NM_WIREGUARD_PUBLIC_KEY_LEN]);
+
+/*****************************************************************************/
+
+typedef struct _NMSettInfoSetting  NMSettInfoSetting;
+typedef struct _NMSettInfoProperty NMSettInfoProperty;
 
 typedef GVariant *(*NMSettingPropertyGetFunc)           (NMSetting     *setting,
                                                          const char    *property);
-typedef GVariant *(*NMSettingPropertySynthFunc)         (NMSetting     *setting,
+typedef GVariant *(*NMSettingPropertySynthFunc)         (const NMSettInfoSetting *sett_info,
+                                                         guint property_idx,
                                                          NMConnection  *connection,
-                                                         const char    *property);
+                                                         NMSetting     *setting,
+                                                         NMConnectionSerializationFlags flags);
 typedef gboolean  (*NMSettingPropertySetFunc)           (NMSetting     *setting,
                                                          GVariant      *connection_dict,
                                                          const char    *property,
@@ -579,7 +652,7 @@ typedef GVariant *(*NMSettingPropertyTransformToFunc)   (const GValue *from);
 typedef void      (*NMSettingPropertyTransformFromFunc) (GVariant *from,
                                                           GValue *to);
 
-typedef struct {
+struct _NMSettInfoProperty {
 	const char *name;
 	GParamSpec *param_spec;
 	const GVariantType *dbus_type;
@@ -591,7 +664,7 @@ typedef struct {
 
 	NMSettingPropertyTransformToFunc   to_dbus;
 	NMSettingPropertyTransformFromFunc from_dbus;
-} NMSettInfoProperty;
+};
 
 typedef struct {
 	const GVariantType *(*get_variant_type) (const struct _NMSettInfoSetting *sett_info,
@@ -616,15 +689,51 @@ typedef struct {
 
 struct _NMSettInfoSetting {
 	NMSettingClass *setting_class;
+
+	/* the properties, sorted by property name. */
 	const NMSettInfoProperty *property_infos;
+
+	/* the @property_infos list is sorted by property name. For some uses we need
+	 * a different sort order. If @property_infos_sorted is set, this is the order
+	 * instead. It is used for:
+	 *
+	 *   - nm_setting_enumerate_values()
+	 *   - keyfile writer adding keys to the group.
+	 *
+	 * Note that currently only NMSettingConnection implements here a sort order
+	 * that differs from alphabetical sort of the property names.
+	 */
+	const NMSettInfoProperty *const*property_infos_sorted;
+
 	guint property_infos_len;
 	NMSettInfoSettDetail detail;
 };
 
-const NMSettInfoSetting *_nm_sett_info_setting_get (NMSettingClass *setting_class);
+static inline const NMSettInfoProperty *
+_nm_sett_info_property_info_get_sorted (const NMSettInfoSetting *sett_info,
+                                        guint idx)
+{
+	nm_assert (sett_info);
+	nm_assert (idx < sett_info->property_infos_len);
+	nm_assert (!sett_info->property_infos_sorted || sett_info->property_infos_sorted[idx]);
+
+	return   sett_info->property_infos_sorted
+	       ? sett_info->property_infos_sorted[idx]
+	       : &sett_info->property_infos[idx];
+}
+
+const NMSettInfoProperty *_nm_sett_info_setting_get_property_info (const NMSettInfoSetting *sett_info,
+                                                                   const char *property_name);
 
-const NMSettInfoProperty *_nm_sett_info_property_get (NMSettingClass *setting_class,
-                                                      const char *property_name);
+const NMSettInfoSetting *_nm_setting_class_get_sett_info (NMSettingClass *setting_class);
+
+static inline const NMSettInfoProperty *
+_nm_setting_class_get_property_info (NMSettingClass *setting_class,
+                                     const char *property_name)
+{
+	return _nm_sett_info_setting_get_property_info (_nm_setting_class_get_sett_info (setting_class),
+	                                                property_name);
+}
 
 /*****************************************************************************/
 
@@ -637,4 +746,29 @@ GBytes *_nm_setting_802_1x_cert_value_to_bytes (NMSetting8021xCKScheme scheme,
 
 /*****************************************************************************/
 
+GVariant *_nm_connection_for_each_secret (NMConnection *self,
+                                          GVariant *secrets,
+                                          gboolean remove_non_secrets,
+                                          _NMConnectionForEachSecretFunc callback,
+                                          gpointer callback_data);
+
+typedef gboolean (*NMConnectionFindSecretFunc) (NMSettingSecretFlags flags,
+                                                gpointer user_data);
+
+gboolean _nm_connection_find_secret (NMConnection *self,
+                                     GVariant *secrets,
+                                     NMConnectionFindSecretFunc callback,
+                                     gpointer callback_data);
+
+/*****************************************************************************/
+
+#define nm_auto_unref_wgpeer nm_auto(_nm_auto_unref_wgpeer)
+NM_AUTO_DEFINE_FCN0 (NMWireGuardPeer *, _nm_auto_unref_wgpeer, nm_wireguard_peer_unref)
+
+gboolean nm_utils_base64secret_normalize (const char *base64_key,
+                                          gsize required_key_len,
+                                          char **out_base64_key_norm);
+
+/*****************************************************************************/
+
 #endif
diff --git a/libnm-core/nm-core-types.h b/libnm-core/nm-core-types.h
index 89d99579..f20ffc09 100644
--- a/libnm-core/nm-core-types.h
+++ b/libnm-core/nm-core-types.h
@@ -43,12 +43,11 @@ typedef struct _NMSettingDummy            NMSettingDummy;
 typedef struct _NMSettingEthtool          NMSettingEthtool;
 typedef struct _NMSettingGeneric          NMSettingGeneric;
 typedef struct _NMSettingGsm              NMSettingGsm;
-typedef struct _NMSettingInfiniband       NMSettingInfiniband;
-typedef struct _NMSettingIPConfig         NMSettingIPConfig;
-typedef struct _NMSettingIPTunnel         NMSettingIPTunnel;
-typedef struct _NMSettingProxy            NMSettingProxy;
 typedef struct _NMSettingIP4Config        NMSettingIP4Config;
 typedef struct _NMSettingIP6Config        NMSettingIP6Config;
+typedef struct _NMSettingIPConfig         NMSettingIPConfig;
+typedef struct _NMSettingIPTunnel         NMSettingIPTunnel;
+typedef struct _NMSettingInfiniband       NMSettingInfiniband;
 typedef struct _NMSettingMacsec           NMSettingMacsec;
 typedef struct _NMSettingMacvlan          NMSettingMacvlan;
 typedef struct _NMSettingMatch            NMSettingMatch;
@@ -59,6 +58,7 @@ typedef struct _NMSettingOvsPatch         NMSettingOvsPatch;
 typedef struct _NMSettingOvsPort          NMSettingOvsPort;
 typedef struct _NMSettingPpp              NMSettingPpp;
 typedef struct _NMSettingPppoe            NMSettingPppoe;
+typedef struct _NMSettingProxy            NMSettingProxy;
 typedef struct _NMSettingSerial           NMSettingSerial;
 typedef struct _NMSettingSriov            NMSettingSriov;
 typedef struct _NMSettingTCConfig         NMSettingTCConfig;
@@ -69,8 +69,10 @@ typedef struct _NMSettingUser             NMSettingUser;
 typedef struct _NMSettingVlan             NMSettingVlan;
 typedef struct _NMSettingVpn              NMSettingVpn;
 typedef struct _NMSettingVxlan            NMSettingVxlan;
+typedef struct _NMSettingWifiP2P          NMSettingWifiP2P;
 typedef struct _NMSettingWimax            NMSettingWimax;
 typedef struct _NMSettingWired            NMSettingWired;
+typedef struct _NMSettingWireGuard        NMSettingWireGuard;
 typedef struct _NMSettingWireless         NMSettingWireless;
 typedef struct _NMSettingWirelessSecurity NMSettingWirelessSecurity;
 typedef struct _NMSettingWpan             NMSettingWpan;
diff --git a/libnm-core/nm-crypto.c b/libnm-core/nm-crypto.c
index 1af2ea28..67f206c4 100644
--- a/libnm-core/nm-crypto.c
+++ b/libnm-core/nm-crypto.c
@@ -25,7 +25,6 @@
 
 #include "nm-crypto.h"
 
-#include <string.h>
 #include <strings.h>
 #include <unistd.h>
 #include <stdlib.h>
diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h
index 4e12dc63..038d2c9e 100644
--- a/libnm-core/nm-dbus-interface.h
+++ b/libnm-core/nm-dbus-interface.h
@@ -38,48 +38,52 @@
  */
 #define NM_DBUS_SERVICE                        "org.freedesktop.NetworkManager"
 
-#define NM_DBUS_PATH                           "/org/freedesktop/NetworkManager"
 #define NM_DBUS_INTERFACE                      "org.freedesktop.NetworkManager"
+#define NM_DBUS_INTERFACE_ACCESS_POINT         NM_DBUS_INTERFACE ".AccessPoint"
+#define NM_DBUS_INTERFACE_ACTIVE_CONNECTION    NM_DBUS_INTERFACE ".Connection.Active"
+#define NM_DBUS_INTERFACE_CHECKPOINT           NM_DBUS_INTERFACE ".Checkpoint"
 #define NM_DBUS_INTERFACE_DEVICE               NM_DBUS_INTERFACE ".Device"
-#define NM_DBUS_INTERFACE_DEVICE_WIRED         NM_DBUS_INTERFACE_DEVICE ".Wired"
+#define NM_DBUS_INTERFACE_DEVICE_6LOWPAN       NM_DBUS_INTERFACE_DEVICE ".Lowpan"
 #define NM_DBUS_INTERFACE_DEVICE_ADSL          NM_DBUS_INTERFACE_DEVICE ".Adsl"
-#define NM_DBUS_INTERFACE_DEVICE_WIRELESS      NM_DBUS_INTERFACE_DEVICE ".Wireless"
 #define NM_DBUS_INTERFACE_DEVICE_BLUETOOTH     NM_DBUS_INTERFACE_DEVICE ".Bluetooth"
-#define NM_DBUS_INTERFACE_DEVICE_OLPC_MESH     NM_DBUS_INTERFACE_DEVICE ".OlpcMesh"
-#define NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE NM_DBUS_INTERFACE_DEVICE ".OvsInterface"
-#define NM_DBUS_INTERFACE_DEVICE_OVS_PORT      NM_DBUS_INTERFACE_DEVICE ".OvsPort"
-#define NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE    NM_DBUS_INTERFACE_DEVICE ".OvsBridge"
-#define NM_DBUS_PATH_ACCESS_POINT              NM_DBUS_PATH "/AccessPoint"
-#define NM_DBUS_INTERFACE_ACCESS_POINT         NM_DBUS_INTERFACE ".AccessPoint"
-#define NM_DBUS_INTERFACE_DEVICE_MODEM         NM_DBUS_INTERFACE_DEVICE ".Modem"
-#define NM_DBUS_INTERFACE_DEVICE_WIMAX         NM_DBUS_INTERFACE_DEVICE ".WiMax"
-#define NM_DBUS_INTERFACE_WIMAX_NSP            NM_DBUS_INTERFACE ".WiMax.Nsp"
-#define NM_DBUS_PATH_WIMAX_NSP                 NM_DBUS_PATH "/Nsp"
-#define NM_DBUS_INTERFACE_ACTIVE_CONNECTION    NM_DBUS_INTERFACE ".Connection.Active"
-#define NM_DBUS_INTERFACE_IP4_CONFIG           NM_DBUS_INTERFACE ".IP4Config"
-#define NM_DBUS_INTERFACE_DHCP4_CONFIG         NM_DBUS_INTERFACE ".DHCP4Config"
-#define NM_DBUS_INTERFACE_IP6_CONFIG           NM_DBUS_INTERFACE ".IP6Config"
-#define NM_DBUS_INTERFACE_DHCP6_CONFIG         NM_DBUS_INTERFACE ".DHCP6Config"
-#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND    NM_DBUS_INTERFACE_DEVICE ".Infiniband"
 #define NM_DBUS_INTERFACE_DEVICE_BOND          NM_DBUS_INTERFACE_DEVICE ".Bond"
-#define NM_DBUS_INTERFACE_DEVICE_DUMMY         NM_DBUS_INTERFACE_DEVICE ".Dummy"
-#define NM_DBUS_INTERFACE_DEVICE_TEAM          NM_DBUS_INTERFACE_DEVICE ".Team"
-#define NM_DBUS_INTERFACE_DEVICE_VLAN          NM_DBUS_INTERFACE_DEVICE ".Vlan"
 #define NM_DBUS_INTERFACE_DEVICE_BRIDGE        NM_DBUS_INTERFACE_DEVICE ".Bridge"
+#define NM_DBUS_INTERFACE_DEVICE_DUMMY         NM_DBUS_INTERFACE_DEVICE ".Dummy"
 #define NM_DBUS_INTERFACE_DEVICE_GENERIC       NM_DBUS_INTERFACE_DEVICE ".Generic"
-#define NM_DBUS_INTERFACE_DEVICE_VETH          NM_DBUS_INTERFACE_DEVICE ".Veth"
-#define NM_DBUS_INTERFACE_DEVICE_TUN           NM_DBUS_INTERFACE_DEVICE ".Tun"
+#define NM_DBUS_INTERFACE_DEVICE_GRE           NM_DBUS_INTERFACE_DEVICE ".Gre"
+#define NM_DBUS_INTERFACE_DEVICE_INFINIBAND    NM_DBUS_INTERFACE_DEVICE ".Infiniband"
+#define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL     NM_DBUS_INTERFACE_DEVICE ".IPTunnel"
 #define NM_DBUS_INTERFACE_DEVICE_MACSEC        NM_DBUS_INTERFACE_DEVICE ".Macsec"
 #define NM_DBUS_INTERFACE_DEVICE_MACVLAN       NM_DBUS_INTERFACE_DEVICE ".Macvlan"
+#define NM_DBUS_INTERFACE_DEVICE_MODEM         NM_DBUS_INTERFACE_DEVICE ".Modem"
+#define NM_DBUS_INTERFACE_DEVICE_OLPC_MESH     NM_DBUS_INTERFACE_DEVICE ".OlpcMesh"
+#define NM_DBUS_INTERFACE_DEVICE_OVS_BRIDGE    NM_DBUS_INTERFACE_DEVICE ".OvsBridge"
+#define NM_DBUS_INTERFACE_DEVICE_OVS_INTERFACE NM_DBUS_INTERFACE_DEVICE ".OvsInterface"
+#define NM_DBUS_INTERFACE_DEVICE_OVS_PORT      NM_DBUS_INTERFACE_DEVICE ".OvsPort"
 #define NM_DBUS_INTERFACE_DEVICE_PPP           NM_DBUS_INTERFACE_DEVICE ".Ppp"
+#define NM_DBUS_INTERFACE_DEVICE_STATISTICS    NM_DBUS_INTERFACE_DEVICE ".Statistics"
+#define NM_DBUS_INTERFACE_DEVICE_TEAM          NM_DBUS_INTERFACE_DEVICE ".Team"
+#define NM_DBUS_INTERFACE_DEVICE_TUN           NM_DBUS_INTERFACE_DEVICE ".Tun"
+#define NM_DBUS_INTERFACE_DEVICE_VETH          NM_DBUS_INTERFACE_DEVICE ".Veth"
+#define NM_DBUS_INTERFACE_DEVICE_VLAN          NM_DBUS_INTERFACE_DEVICE ".Vlan"
 #define NM_DBUS_INTERFACE_DEVICE_VXLAN         NM_DBUS_INTERFACE_DEVICE ".Vxlan"
+#define NM_DBUS_INTERFACE_DEVICE_WIFI_P2P      NM_DBUS_INTERFACE_DEVICE ".WifiP2P"
+#define NM_DBUS_INTERFACE_DEVICE_WIMAX         NM_DBUS_INTERFACE_DEVICE ".WiMax"
+#define NM_DBUS_INTERFACE_DEVICE_WIRED         NM_DBUS_INTERFACE_DEVICE ".Wired"
 #define NM_DBUS_INTERFACE_DEVICE_WIREGUARD     NM_DBUS_INTERFACE_DEVICE ".WireGuard"
-#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_WIRELESS      NM_DBUS_INTERFACE_DEVICE ".Wireless"
 #define NM_DBUS_INTERFACE_DEVICE_WPAN          NM_DBUS_INTERFACE_DEVICE ".Wpan"
-#define NM_DBUS_INTERFACE_DEVICE_6LOWPAN       NM_DBUS_INTERFACE_DEVICE ".Lowpan"
-#define NM_DBUS_INTERFACE_DEVICE_STATISTICS    NM_DBUS_INTERFACE_DEVICE ".Statistics"
-#define NM_DBUS_INTERFACE_CHECKPOINT           NM_DBUS_INTERFACE ".Checkpoint"
+#define NM_DBUS_INTERFACE_DHCP4_CONFIG         NM_DBUS_INTERFACE ".DHCP4Config"
+#define NM_DBUS_INTERFACE_DHCP6_CONFIG         NM_DBUS_INTERFACE ".DHCP6Config"
+#define NM_DBUS_INTERFACE_IP4_CONFIG           NM_DBUS_INTERFACE ".IP4Config"
+#define NM_DBUS_INTERFACE_IP6_CONFIG           NM_DBUS_INTERFACE ".IP6Config"
+#define NM_DBUS_INTERFACE_WIFI_P2P_PEER        NM_DBUS_INTERFACE ".WifiP2PPeer"
+#define NM_DBUS_INTERFACE_WIMAX_NSP            NM_DBUS_INTERFACE ".WiMax.Nsp"
+
+#define NM_DBUS_PATH                           "/org/freedesktop/NetworkManager"
+#define NM_DBUS_PATH_ACCESS_POINT              NM_DBUS_PATH "/AccessPoint"
+#define NM_DBUS_PATH_WIFI_P2P_PEER             NM_DBUS_PATH "/WifiP2PPeer"
+#define NM_DBUS_PATH_WIMAX_NSP                 NM_DBUS_PATH "/Nsp"
 
 #define NM_DBUS_INTERFACE_SETTINGS        "org.freedesktop.NetworkManager.Settings"
 #define NM_DBUS_PATH_SETTINGS             "/org/freedesktop/NetworkManager/Settings"
@@ -168,7 +172,7 @@ typedef enum {
  * @NM_CONNECTIVITY_PORTAL: The Internet connection is hijacked by a captive
  *   portal gateway. The graphical shell may open a sandboxed web browser window
  *   (because the captive portals typically attempt a man-in-the-middle attacks
- *   agains the https connections) for the purpose of authenticating to a gateway
+ *   against the https connections) for the purpose of authenticating to a gateway
  *   and retrigger the connectivity check with CheckConnectivity() when the
  *   browser window is dismissed.
  * @NM_CONNECTIVITY_LIMITED: The host is connected to a network, does not appear
@@ -190,7 +194,7 @@ typedef enum {
  * @NM_DEVICE_TYPE_UNKNOWN: unknown device
  * @NM_DEVICE_TYPE_GENERIC: generic support for unrecognized device types
  * @NM_DEVICE_TYPE_ETHERNET: a wired ethernet device
- * @NM_DEVICE_TYPE_WIFI: an 802.11 WiFi device
+ * @NM_DEVICE_TYPE_WIFI: an 802.11 Wi-Fi device
  * @NM_DEVICE_TYPE_UNUSED1: not used
  * @NM_DEVICE_TYPE_UNUSED2: not used
  * @NM_DEVICE_TYPE_BT: a Bluetooth device supporting PAN or DUN access protocols
@@ -218,6 +222,7 @@ typedef enum {
  * @NM_DEVICE_TYPE_WPAN: a IEEE 802.15.4 (WPAN) MAC Layer Device
  * @NM_DEVICE_TYPE_6LOWPAN: 6LoWPAN interface
  * @NM_DEVICE_TYPE_WIREGUARD: a WireGuard interface
+ * @NM_DEVICE_TYPE_WIFI_P2P: an 802.11 Wi-Fi P2P device (Since: 1.16)
  *
  * #NMDeviceType values indicate the type of hardware represented by a
  * device object.
@@ -253,6 +258,7 @@ typedef enum {
 	NM_DEVICE_TYPE_WPAN          = 27,
 	NM_DEVICE_TYPE_6LOWPAN       = 28,
 	NM_DEVICE_TYPE_WIREGUARD     = 29,
+	NM_DEVICE_TYPE_WIFI_P2P      = 30,
 } NMDeviceType;
 
 /**
@@ -344,6 +350,8 @@ typedef enum { /*< underscore_name=nm_802_11_ap_flags, flags >*/
  * supported
  * @NM_802_11_AP_SEC_KEY_MGMT_802_1X: 802.1x authentication and key management
  * is supported
+ * @NM_802_11_AP_SEC_KEY_MGMT_SAE: WPA/RSN Simultaneous Authentication of Equals is
+ * supported
  *
  * 802.11 access point security and authentication flags.  These flags describe
  * the current security requirements of an access point as determined from the
@@ -361,6 +369,7 @@ typedef enum { /*< underscore_name=nm_802_11_ap_security_flags, flags >*/
 	NM_802_11_AP_SEC_GROUP_CCMP      = 0x00000080,
 	NM_802_11_AP_SEC_KEY_MGMT_PSK    = 0x00000100,
 	NM_802_11_AP_SEC_KEY_MGMT_802_1X = 0x00000200,
+	NM_802_11_AP_SEC_KEY_MGMT_SAE    = 0x00000400,
 } NM80211ApSecurityFlags;
 
 /**
@@ -456,7 +465,7 @@ typedef enum {
  *   setting physical link properties, and anything else required to connect
  *   to the requested network.
  * @NM_DEVICE_STATE_CONFIG: the device is connecting to the requested network.
- *   This may include operations like associating with the WiFi AP, dialing
+ *   This may include operations like associating with the Wi-Fi AP, dialing
  *   the modem, connecting to the remote Bluetooth device, etc.
  * @NM_DEVICE_STATE_NEED_AUTH: the device requires more information to continue
  *   connecting to the requested network.  This includes secrets like WiFi
@@ -549,7 +558,7 @@ typedef enum {
  * @NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED: A dependency of the connection failed
  * @NM_DEVICE_STATE_REASON_BR2684_FAILED: Problem with the RFC 2684 Ethernet over ADSL bridge
  * @NM_DEVICE_STATE_REASON_MODEM_MANAGER_UNAVAILABLE: ModemManager not running
- * @NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: The WiFi network could not be found
+ * @NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: The Wi-Fi network could not be found
  * @NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED: A secondary connection of the base connection failed
  * @NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED: DCB or FCoE setup failed
  * @NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED: teamd control failed
@@ -563,6 +572,7 @@ typedef enum {
  * @NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE: a duplicate IP address was detected
  * @NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED: The selected IP method is not supported
  * @NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED: configuration of SR-IOV parameters failed
+ * @NM_DEVICE_STATE_REASON_PEER_NOT_FOUND: The Wi-Fi P2P peer could not be found
  *
  * Device state change reason codes
  */
@@ -634,6 +644,7 @@ typedef enum {
 	NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE           = 64,
 	NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED          = 65,
 	NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED     = 66,
+	NM_DEVICE_STATE_REASON_PEER_NOT_FOUND                 = 67,
 } NMDeviceStateReason;
 
 /**
@@ -960,20 +971,25 @@ typedef enum { /*< flags >*/
  * @NM_ACTIVATION_STATE_FLAG_IP6_READY: IPv6 setting is completed.
  * @NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES: The master has any slave devices attached.
  *   This only makes sense if the device is a master.
+ * @NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY: the lifetime
+ *   of the activation is bound to the visilibity of the connection profile,
+ *   which in turn depends on "connection.permissions" and whether a session
+ *   for the user exists. Since: 1.16
  *
  * Flags describing the current activation state.
  *
  * Since: 1.10
  **/
 typedef enum { /*< flags >*/
-	NM_ACTIVATION_STATE_FLAG_NONE                       = 0,
+	NM_ACTIVATION_STATE_FLAG_NONE                                 = 0,
 
-	NM_ACTIVATION_STATE_FLAG_IS_MASTER                  = (1LL <<  0),
-	NM_ACTIVATION_STATE_FLAG_IS_SLAVE                   = (1LL <<  1),
-	NM_ACTIVATION_STATE_FLAG_LAYER2_READY               = (1LL <<  2),
-	NM_ACTIVATION_STATE_FLAG_IP4_READY                  = (1LL <<  3),
-	NM_ACTIVATION_STATE_FLAG_IP6_READY                  = (1LL <<  4),
-	NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES          = (1LL <<  5),
+	NM_ACTIVATION_STATE_FLAG_IS_MASTER                            = 0x1,
+	NM_ACTIVATION_STATE_FLAG_IS_SLAVE                             = 0x2,
+	NM_ACTIVATION_STATE_FLAG_LAYER2_READY                         = 0x4,
+	NM_ACTIVATION_STATE_FLAG_IP4_READY                            = 0x8,
+	NM_ACTIVATION_STATE_FLAG_IP6_READY                            = 0x10,
+	NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES                    = 0x20,
+	NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY = 0x40,
 } NMActivationStateFlags;
 
 /**
@@ -1004,7 +1020,7 @@ typedef enum { /*< flags >*/
  *   again (because it's about to be deleted), but a manual activation will
  *   clear the volatile flag.
  * @NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT: usually, when the connection
- *   has autoconnect enabled and is modified, it becomes elegible to autoconnect
+ *   has autoconnect enabled and is modified, it becomes eligible to autoconnect
  *   right away. Setting this flag, disables autoconnect until the connection
  *   is manually activated.
  *
@@ -1012,12 +1028,12 @@ typedef enum { /*< flags >*/
  */
 typedef enum { /*< flags >*/
 	NM_SETTINGS_UPDATE2_FLAG_NONE                       = 0,
-	NM_SETTINGS_UPDATE2_FLAG_TO_DISK                    = (1LL <<  0),
-	NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY                  = (1LL <<  1),
-	NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED         = (1LL <<  2),
-	NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY             = (1LL <<  3),
-	NM_SETTINGS_UPDATE2_FLAG_VOLATILE                   = (1LL <<  4),
-	NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT          = (1LL <<  5),
+	NM_SETTINGS_UPDATE2_FLAG_TO_DISK                    = 0x1,
+	NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY                  = 0x2,
+	NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED         = 0x4,
+	NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY             = 0x8,
+	NM_SETTINGS_UPDATE2_FLAG_VOLATILE                   = 0x10,
+	NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT          = 0x20,
 } NMSettingsUpdate2Flags;
 
 /**
diff --git a/libnm-core/nm-dbus-types.xml b/libnm-core/nm-dbus-types.xml
index e3e9fca9..a65e1d08 100644
--- a/libnm-core/nm-dbus-types.xml
+++ b/libnm-core/nm-dbus-types.xml
@@ -125,7 +125,7 @@
             <row role="constant">
               <entry role="enum_member_name"><para>NM_CONNECTIVITY_PORTAL</para><para></para></entry>
               <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The Internet connection is hijacked by a captive portal gateway. The graphical shell may open a sandboxed web browser window (because the captive portals typically attempt a man-in-the-middle attacks agains the https connections) for the purpose of authenticating to a gateway and retrigger the connectivity check with CheckConnectivity() when the browser window is dismissed.</para><para></para></entry>
+              <entry role="enum_member_description"><para>The Internet connection is hijacked by a captive portal gateway. The graphical shell may open a sandboxed web browser window (because the captive portals typically attempt a man-in-the-middle attacks against the https connections) for the purpose of authenticating to a gateway and retrigger the connectivity check with CheckConnectivity() when the browser window is dismissed.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_CONNECTIVITY_LIMITED</para><para></para></entry>
@@ -175,7 +175,7 @@
             <row role="constant">
               <entry role="enum_member_name"><para>NM_DEVICE_TYPE_WIFI</para><para></para></entry>
               <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>an 802.11 WiFi device</para><para></para></entry>
+              <entry role="enum_member_description"><para>an 802.11 Wi-Fi device</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_DEVICE_TYPE_UNUSED1</para><para></para></entry>
@@ -307,6 +307,11 @@
               <entry role="enum_member_value"><para>= <literal>29</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>a WireGuard interface</para><para></para></entry>
             </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_TYPE_WIFI_P2P</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>30</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>an 802.11 Wi-Fi P2P device (Since: 1.16)</para><para></para></entry>
+            </row>
           </tbody>
         </tgroup>
       </informaltable>
@@ -552,6 +557,11 @@
               <entry role="enum_member_value"><para>= <literal>0x00000200</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>802.1x authentication and key management is supported</para><para></para></entry>
             </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_802_11_AP_SEC_KEY_MGMT_SAE</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x00000400</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>WPA/RSN Simultaneous Authentication of Equals is supported</para><para></para></entry>
+            </row>
           </tbody>
         </tgroup>
       </informaltable>
@@ -760,7 +770,7 @@
             <row role="constant">
               <entry role="enum_member_name"><para>NM_DEVICE_STATE_CONFIG</para><para></para></entry>
               <entry role="enum_member_value"><para>= <literal>50</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>the device is connecting to the requested network. This may include operations like associating with the WiFi AP, dialing the modem, connecting to the remote Bluetooth device, etc.</para><para></para></entry>
+              <entry role="enum_member_description"><para>the device is connecting to the requested network. This may include operations like associating with the Wi-Fi AP, dialing the modem, connecting to the remote Bluetooth device, etc.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_DEVICE_STATE_NEED_AUTH</para><para></para></entry>
@@ -1085,7 +1095,7 @@
             <row role="constant">
               <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SSID_NOT_FOUND</para><para></para></entry>
               <entry role="enum_member_value"><para>= <literal>53</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>The WiFi network could not be found</para><para></para></entry>
+              <entry role="enum_member_description"><para>The Wi-Fi network could not be found</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_SECONDARY_CONNECTION_FAILED</para><para></para></entry>
@@ -1152,6 +1162,11 @@
               <entry role="enum_member_value"><para>= <literal>66</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>configuration of SR-IOV parameters failed</para><para></para></entry>
             </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_DEVICE_STATE_REASON_PEER_NOT_FOUND</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>67</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>The Wi-Fi P2P peer could not be found</para><para></para></entry>
+            </row>
           </tbody>
         </tgroup>
       </informaltable>
@@ -1694,34 +1709,39 @@
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IS_MASTER</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>the device is a master.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IS_SLAVE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x2</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>the device is a slave.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_LAYER2_READY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x4</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>layer2 is activated and ready.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IP4_READY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x8</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>IPv4 setting is completed.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_IP6_READY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x10</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>IPv6 setting is completed.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_MASTER_HAS_SLAVES</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x20</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>The master has any slave devices attached. This only makes sense if the device is a master.</para><para></para></entry>
             </row>
+            <row role="constant">
+              <entry role="enum_member_name"><para>NM_ACTIVATION_STATE_FLAG_LIFETIME_BOUND_TO_PROFILE_VISIBILITY</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x40</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>the lifetime of the activation is bound to the visilibity of the connection profile, which in turn depends on "connection.permissions" and whether a session for the user exists. Since: 1.16</para><para></para></entry>
+            </row>
           </tbody>
         </tgroup>
       </informaltable>
@@ -1749,33 +1769,33 @@
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_TO_DISK</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x1</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>to persist the connection to disk.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x2</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>to make the connection in-memory only. If the connection was previously persistent, the corresponding file on disk is not deleted but merely the connection is decoupled from the file on disk. If you later delete an in-memory connection, the connection on disk will be deleted as well.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x4</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>this is like @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, but if the connection has a corresponding file on disk, the association between the connection and the file is forgotten but the file is not modified. The difference to %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY is if you later save the connection again to disk, a new file name will be chosen without overwriting the remaining file on disk. Also, if you delete the connection later, the file on disk will not be deleted.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x8</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>this is like @NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY, but if the connection has a corresponding file on disk, the file on disk will be deleted.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_VOLATILE</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x10</literal></para><para></para></entry>
               <entry role="enum_member_description"><para>This can be specified with either %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_DETACHED or %NM_SETTINGS_UPDATE2_FLAG_IN_MEMORY_ONLY. After making the connection in-memory only, the connection is marked as volatile. That means, if the connection is currently not active it will be deleted right away. Otherwise, it is marked to for deletion once the connection deactivates. A volatile connection cannot autoactivate again (because it's about to be deleted), but a manual activation will clear the volatile flag.</para><para></para></entry>
             </row>
             <row role="constant">
               <entry role="enum_member_name"><para>NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT</para><para></para></entry>
-              <entry role="enum_member_value"><para>= <literal>(1LL</literal></para><para></para></entry>
-              <entry role="enum_member_description"><para>usually, when the connection has autoconnect enabled and is modified, it becomes elegible to autoconnect right away. Setting this flag, disables autoconnect until the connection is manually activated.</para><para></para></entry>
+              <entry role="enum_member_value"><para>= <literal>0x20</literal></para><para></para></entry>
+              <entry role="enum_member_description"><para>usually, when the connection has autoconnect enabled and is modified, it becomes eligible to autoconnect right away. Setting this flag, disables autoconnect until the connection is manually activated.</para><para></para></entry>
             </row>
           </tbody>
         </tgroup>
diff --git a/libnm-core/nm-dbus-utils.c b/libnm-core/nm-dbus-utils.c
index 25af7c99..8c455971 100644
--- a/libnm-core/nm-dbus-utils.c
+++ b/libnm-core/nm-dbus-utils.c
@@ -20,8 +20,6 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-core-internal.h"
 
 typedef struct {
@@ -174,23 +172,35 @@ _nm_dbus_signal_connect_data (GDBusProxy *proxy,
  * Returns: the signal handler ID, as with _nm_signal_connect_data().
  */
 
-static void
-typecheck_response (GVariant **response,
-                    const GVariantType *reply_type,
-                    GError **error)
+/**
+ * _nm_dbus_typecheck_response:
+ * @response: the #GVariant response to check.
+ * @reply_type: the expected reply type. It may be %NULL to perform no
+ *   checking.
+ * @error: (allow-none): the error in case the @reply_type does not match.
+ *
+ * Returns: %TRUE, if @response is of the expected @reply_type.
+ */
+gboolean
+_nm_dbus_typecheck_response (GVariant *response,
+                             const GVariantType *reply_type,
+                             GError **error)
 {
-	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.
-		 */
-		g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
-		             _("Method returned type '%s', but expected '%s'"),
-		             g_variant_get_type_string (*response),
-		             g_variant_type_peek_string (reply_type));
-		g_clear_pointer (response, g_variant_unref);
-	}
+	g_return_val_if_fail (response, FALSE);
+
+	if (!reply_type)
+		return TRUE;
+	if (g_variant_is_of_type (response, reply_type))
+		return TRUE;
+
+	/* This is the same error code that g_dbus_connection_call() returns if
+	 * @reply_type doesn't match.
+	 */
+	g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+	             _("Method returned type '%s', but expected '%s'"),
+	             g_variant_get_type_string (response),
+	             g_variant_type_peek_string (reply_type));
+	return FALSE;
 }
 
 /**
@@ -215,11 +225,15 @@ _nm_dbus_proxy_call_finish (GDBusProxy          *proxy,
                             const GVariantType  *reply_type,
                             GError             **error)
 {
-	GVariant *ret;
+	GVariant *variant;
 
-	ret = g_dbus_proxy_call_finish (proxy, res, error);
-	typecheck_response (&ret, reply_type, error);
-	return ret;
+	variant = g_dbus_proxy_call_finish (proxy,
+	                                    res,
+	                                    error);
+	if (   variant
+	    && !_nm_dbus_typecheck_response (variant, reply_type, error))
+		nm_clear_pointer (&variant, g_variant_unref);
+	return variant;
 }
 
 /**
@@ -253,13 +267,34 @@ _nm_dbus_proxy_call_sync (GDBusProxy          *proxy,
                           GCancellable        *cancellable,
                           GError             **error)
 {
-	GVariant *ret;
+	GVariant *variant;
+
+	variant = g_dbus_proxy_call_sync (proxy,
+	                                  method_name,
+	                                  parameters,
+	                                  flags,
+	                                  timeout_msec,
+	                                  cancellable,
+	                                  error);
+	if (   variant
+	    && !_nm_dbus_typecheck_response (variant, reply_type, error))
+		nm_clear_pointer (&variant, g_variant_unref);
+	return variant;
+}
+
+GVariant *
+_nm_dbus_connection_call_finish (GDBusConnection *dbus_connection,
+                                 GAsyncResult *result,
+                                 const GVariantType *reply_type,
+                                 GError **error)
+{
+	GVariant *variant;
 
-	ret = g_dbus_proxy_call_sync (proxy, method_name, parameters,
-	                              flags, timeout_msec,
-	                              cancellable, error);
-	typecheck_response (&ret, reply_type, error);
-	return ret;
+	variant = g_dbus_connection_call_finish (dbus_connection, result, error);
+	if (   variant
+	    && !_nm_dbus_typecheck_response (variant, reply_type, error))
+		nm_clear_pointer (&variant, g_variant_unref);
+	return variant;
 }
 
 /**
diff --git a/libnm-core/nm-errors.c b/libnm-core/nm-errors.c
index 351bc1d0..beef95c7 100644
--- a/libnm-core/nm-errors.c
+++ b/libnm-core/nm-errors.c
@@ -20,9 +20,8 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-errors.h"
+
 #include "nm-vpn-dbus-interface.h"
 #include "nm-core-internal.h"
 
diff --git a/libnm-core/nm-errors.h b/libnm-core/nm-errors.h
index bdd6c223..ef73790e 100644
--- a/libnm-core/nm-errors.h
+++ b/libnm-core/nm-errors.h
@@ -145,6 +145,7 @@ GQuark nm_crypto_error_quark (void);
  * @NM_DEVICE_ERROR_VERSION_ID_MISMATCH: the version id did not match.
  * @NM_DEVICE_ERROR_MISSING_DEPENDENCIES: the requested operation could not
  *   be completed due to missing dependencies.
+ * @NM_DEVICE_ERROR_INVALID_ARGUMENT: invalid argument. Since: 1.16
  *
  * Device-related errors.
  *
@@ -163,6 +164,7 @@ typedef enum {
 	NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND, /*< nick=SpecificObjectNotFound >*/
 	NM_DEVICE_ERROR_VERSION_ID_MISMATCH,       /*< nick=VersionIdMismatch >*/
 	NM_DEVICE_ERROR_MISSING_DEPENDENCIES,      /*< nick=MissingDependencies >*/
+	NM_DEVICE_ERROR_INVALID_ARGUMENT,          /*< nick=InvalidArgument >*/
 } NMDeviceError;
 
 #define NM_DEVICE_ERROR nm_device_error_quark ()
diff --git a/libnm-core/nm-keyfile-internal.h b/libnm-core/nm-keyfile-internal.h
index 94228d95..5487f59d 100644
--- a/libnm-core/nm-keyfile-internal.h
+++ b/libnm-core/nm-keyfile-internal.h
@@ -168,4 +168,19 @@ gboolean _nm_keyfile_a_contains_all_in_b (GKeyFile *kf_a, GKeyFile *kf_b);
 gboolean _nm_keyfile_equals (GKeyFile *kf_a, GKeyFile *kf_b, gboolean consider_order);
 gboolean _nm_keyfile_has_values (GKeyFile *keyfile);
 
+/*****************************************************************************/
+
+#define NM_KEYFILE_PATH_NAME_ETC_DEFAULT         NMCONFDIR "/system-connections"
+#define NM_KEYFILE_PATH_NAME_RUN                 NMRUNDIR "/system-connections"
+
+#define NM_KEYFILE_PATH_SUFFIX_NMCONNECTION      ".nmconnection"
+
+#define NM_KEYFILE_PATH_PREFIX_NMLOADED          ".loaded-"
+
+#define NM_KEYFILE_PATH_NMLOADED_NULL            "/dev/null"
+
+gboolean nm_keyfile_utils_ignore_filename (const char *filename, gboolean require_extension);
+
+char *nm_keyfile_utils_create_filename (const char *filename, gboolean with_extension);
+
 #endif /* __NM_KEYFILE_INTERNAL_H__ */
diff --git a/libnm-core/nm-keyfile-utils.c b/libnm-core/nm-keyfile-utils.c
index 21f8b07d..e243150d 100644
--- a/libnm-core/nm-keyfile-utils.c
+++ b/libnm-core/nm-keyfile-utils.c
@@ -20,10 +20,10 @@
 
 #include "nm-default.h"
 
+#include "nm-keyfile-utils.h"
+
 #include <stdlib.h>
-#include <string.h>
 
-#include "nm-keyfile-utils.h"
 #include "nm-keyfile-internal.h"
 #include "nm-setting-wired.h"
 #include "nm-setting-wireless.h"
@@ -48,7 +48,7 @@ nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name)
 	g_return_val_if_fail (setting_name != NULL, NULL);
 
 	for (i = 0; i < G_N_ELEMENTS (alias_list); i++) {
-		if (strcmp (setting_name, alias_list[i].setting) == 0)
+		if (nm_streq (setting_name, alias_list[i].setting))
 			return alias_list[i].alias;
 	}
 	return NULL;
@@ -62,7 +62,7 @@ nm_keyfile_plugin_get_setting_name_for_alias (const char *alias)
 	g_return_val_if_fail (alias != NULL, NULL);
 
 	for (i = 0; i < G_N_ELEMENTS (alias_list); i++) {
-		if (strcmp (alias, alias_list[i].alias) == 0)
+		if (nm_streq (alias, alias_list[i].alias))
 			return alias_list[i].setting;
 	}
 	return NULL;
@@ -82,17 +82,22 @@ nm_keyfile_plugin_kf_get_##stype##_list (GKeyFile *kf, \
 	get_ctype list; \
 	const char *alias; \
 	GError *local = NULL; \
+	gsize l; \
  \
-	list = g_key_file_get_##stype##_list (kf, group, key, out_length, &local); \
+	list = g_key_file_get_##stype##_list (kf, group, key, &l, &local); \
 	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) { \
 		alias = nm_keyfile_plugin_get_alias_for_setting_name (group); \
 		if (alias) { \
 			g_clear_error (&local); \
-			list = g_key_file_get_##stype##_list (kf, alias, key, out_length, &local); \
+			list = g_key_file_get_##stype##_list (kf, alias, key, &l, &local); \
 		} \
 	} \
+	nm_assert ((!local) != (!list)); \
 	if (local) \
 		g_propagate_error (error, local); \
+	if (!list) \
+		l = 0; \
+	NM_SET_OUT (out_length, l); \
 	return list; \
 } \
  \
@@ -174,11 +179,41 @@ nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
 }
 
 DEFINE_KF_WRAPPER(string, char*, const char*);
-DEFINE_KF_WRAPPER(integer, int, int);
-DEFINE_KF_WRAPPER(uint64, guint64, guint64);
 DEFINE_KF_WRAPPER(boolean, gboolean, gboolean);
 DEFINE_KF_WRAPPER(value, char*, const char*);
 
+gint64
+nm_keyfile_plugin_kf_get_int64 (GKeyFile *kf,
+                                const char *group,
+                                const char *key,
+                                guint base,
+                                gint64 min,
+                                gint64 max,
+                                gint64 fallback,
+                                GError **error)
+{
+	gs_free char *s = NULL;
+	int errsv;
+	gint64 v;
+
+	s = nm_keyfile_plugin_kf_get_value (kf, group, key, error);
+	if (!s) {
+		errno = ENODATA;
+		return fallback;
+	}
+
+	v = _nm_utils_ascii_str_to_int64 (s, base, min, max, fallback);
+	errsv = errno;
+	if (   errsv != 0
+	    && error) {
+		g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
+		             _("value is not an integer in range [%lld, %lld]"),
+		             (long long) min, (long long) max);
+		errno = errsv;
+	}
+	return v;
+}
+
 char **
 nm_keyfile_plugin_kf_get_keys (GKeyFile *kf,
                                const char *group,
@@ -188,15 +223,21 @@ nm_keyfile_plugin_kf_get_keys (GKeyFile *kf,
 	char **keys;
 	const char *alias;
 	GError *local = NULL;
+	gsize l;
 
-	keys = g_key_file_get_keys (kf, group, out_length, &local);
+	keys = g_key_file_get_keys (kf, group, &l, &local);
 	if (g_error_matches (local, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_GROUP_NOT_FOUND)) {
 		alias = nm_keyfile_plugin_get_alias_for_setting_name (group);
 		if (alias) {
 			g_clear_error (&local);
-			keys = g_key_file_get_keys (kf, alias, out_length, error ? &local : NULL);
+			keys = g_key_file_get_keys (kf, alias, &l, error ? &local : NULL);
 		}
 	}
+	nm_assert ((!local) != (!keys));
+	if (!keys)
+		l = 0;
+	nm_assert (l == NM_PTRARRAY_LEN (keys));
+	NM_SET_OUT (out_length, l);
 	if (local)
 		g_propagate_error (error, local);
 	return keys;
diff --git a/libnm-core/nm-keyfile-utils.h b/libnm-core/nm-keyfile-utils.h
index 46a6d564..9403dfa3 100644
--- a/libnm-core/nm-keyfile-utils.h
+++ b/libnm-core/nm-keyfile-utils.h
@@ -25,7 +25,8 @@
 #error Cannot use this header.
 #endif
 
-#define NM_KEYFILE_GROUP_VPN_SECRETS "vpn-secrets"
+#define NM_KEYFILE_GROUP_VPN_SECRETS          "vpn-secrets"
+#define NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER "wireguard-peer."
 
 const char *nm_keyfile_plugin_get_alias_for_setting_name (const char *setting_name);
 
@@ -67,12 +68,19 @@ void nm_keyfile_plugin_kf_set_##stype (GKeyFile *kf, \
                                        const char *key, \
                                        set_ctype value);
 DEFINE_KF_WRAPPER_PROTO(string, char*, const char*)
-DEFINE_KF_WRAPPER_PROTO(integer, int, int)
-DEFINE_KF_WRAPPER_PROTO(uint64, guint64, guint64)
 DEFINE_KF_WRAPPER_PROTO(boolean, gboolean, gboolean)
 DEFINE_KF_WRAPPER_PROTO(value, char*, const char*)
 
 /* Misc */
+gint64 nm_keyfile_plugin_kf_get_int64 (GKeyFile *kf,
+                                       const char *group,
+                                       const char *key,
+                                       guint base,
+                                       gint64 min,
+                                       gint64 max,
+                                       gint64 fallback,
+                                       GError **error);
+
 char ** nm_keyfile_plugin_kf_get_keys    (GKeyFile *kf,
                                            const char *group,
                                            gsize *out_length,
@@ -90,4 +98,3 @@ const char *nm_keyfile_key_decode (const char *key,
                                    char **out_to_free);
 
 #endif  /* __NM_KEYFILE_UTILS_H__ */
-
diff --git a/libnm-core/nm-keyfile.c b/libnm-core/nm-keyfile.c
index 5aa8a4ac..05c6bf97 100644
--- a/libnm-core/nm-keyfile.c
+++ b/libnm-core/nm-keyfile.c
@@ -23,17 +23,16 @@
 
 #include "nm-keyfile-internal.h"
 
-#include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <arpa/inet.h>
-#include <string.h>
 #include <linux/pkt_sched.h>
 
 #include "nm-utils/nm-secret-utils.h"
+#include "systemd/nm-sd-utils-shared.h"
 #include "nm-common-macros.h"
 #include "nm-core-internal.h"
 #include "nm-keyfile-utils.h"
@@ -96,6 +95,15 @@ _handle_warn (KeyfileReaderInfo *info,
 		_info->error == NULL; \
 	})
 
+/*****************************************************************************/
+
+static gboolean
+_secret_flags_persist_secret (NMSettingSecretFlags flags)
+{
+	return flags == NM_SETTING_SECRET_FLAG_NONE;
+}
+
+/*****************************************************************************/
 /* Some setting properties also contain setting names, such as
  * NMSettingConnection's 'type' property (which specifies the base type of the
  * connection, e.g. ethernet or wifi) or 'slave-type' (specifies type of slave
@@ -106,17 +114,18 @@ static void
 setting_alias_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
 	const char *setting_name = nm_setting_get_name (setting);
-	char *s;
 	const char *key_setting_name;
+	gs_free char *s = NULL;
 
 	s = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
-	if (s) {
-		key_setting_name = nm_keyfile_plugin_get_setting_name_for_alias (s);
-		g_object_set (G_OBJECT (setting),
-		              key, key_setting_name ?: s,
-		              NULL);
-		g_free (s);
-	}
+	if (!s)
+		return;
+
+	key_setting_name = nm_keyfile_plugin_get_setting_name_for_alias (s);
+	g_object_set (G_OBJECT (setting),
+	              key,
+	              key_setting_name ?: s,
+	              NULL);
 }
 
 static void
@@ -129,7 +138,7 @@ sriov_vfs_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 	int i;
 
 	keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL);
-	if (!keys || n_keys == 0)
+	if (n_keys == 0)
 		return;
 
 	vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
@@ -372,7 +381,7 @@ read_field (char **current, const char **out_err_str, const char *characters, co
  * address/plen,gateway,metric (NETWORK via GATEWAY dev DEVICE metric METRIC)
  *
  * For backward, forward and sideward compatibility, slash (/),
- * semicolon (;) and comma (,) are interchangable. The choice of
+ * semicolon (;) and comma (,) are interchangeable. The choice of
  * separator in the above examples is therefore not significant.
  *
  * Leaving out the prefix length is discouraged and DEPRECATED. The
@@ -622,17 +631,16 @@ ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const c
 	gs_free char *gateway = NULL;
 	gs_unref_ptrarray GPtrArray *list = NULL;
 	gs_strfreev char **keys = NULL;
-	gsize i_keys, keys_len;
+	gsize i_keys, n_keys;
 	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);
-
-	if (keys_len == 0)
+	keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL);
+	if (n_keys == 0)
 		return;
 
 	/* first create a list of all relevant keys, and sort them. */
-	for (i_keys = 0; i_keys < keys_len; i_keys++) {
+	for (i_keys = 0; i_keys < n_keys; i_keys++) {
 		const char *s_key = keys[i_keys];
 		gint32 key_idx;
 		gint8 key_type;
@@ -641,7 +649,7 @@ ip_address_or_route_parser (KeyfileReaderInfo *info, NMSetting *setting, const c
 			continue;
 
 		if (G_UNLIKELY (!build_list))
-			build_list = g_new (IPAddrRouteBuildListData, keys_len - i_keys);
+			build_list = g_new (IPAddrRouteBuildListData, n_keys - i_keys);
 
 		build_list[build_list_len].s_key = s_key;
 		build_list[build_list_len].key_idx = key_idx;
@@ -786,7 +794,6 @@ mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key
 	const char *p, *mac_str;
 	gs_free guint8 *buf_arr = NULL;
 	guint buf_len = 0;
-	gsize length;
 
 	tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
 
@@ -819,6 +826,7 @@ mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key
 
 	if (!buf_arr) {
 		gs_free int *tmp_list = NULL;
+		gsize length;
 
 		/* Old format; list of ints */
 		tmp_list = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL);
@@ -879,9 +887,10 @@ read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key)
 	const char *const*iter;
 	const char *setting_name = nm_setting_get_name (setting);
 	gboolean is_vpn;
+	gsize n_keys;
 
-	keys = nm_keyfile_plugin_kf_get_keys (file, setting_name, NULL, NULL);
-	if (!keys || !*keys)
+	keys = nm_keyfile_plugin_kf_get_keys (file, setting_name, &n_keys, NULL);
+	if (n_keys == 0)
 		return;
 
 	if (   (is_vpn = NM_IS_SETTING_VPN (setting))
@@ -902,7 +911,7 @@ read_hash_of_string (GKeyFile *file, NMSetting *setting, const char *key)
 				if (!g_object_class_find_property (G_OBJECT_GET_CLASS (setting), name))
 					nm_setting_vpn_add_data_item (NM_SETTING_VPN (setting), name, value);
 			} else {
-				if (strcmp (name, "interface-name"))
+				if (!nm_streq (name, "interface-name"))
 					nm_setting_bond_add_option (NM_SETTING_BOND (setting), name, value);
 			}
 		}
@@ -1374,55 +1383,119 @@ cert_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 	g_object_set (setting, key, bytes, NULL);
 }
 
+static int
+_parity_from_char (int ch)
+{
+#if NM_MORE_ASSERTS > 5
+	{
+		static char check = 0;
+
+		if (check == 0) {
+			nm_auto_unref_gtypeclass GEnumClass *klass = g_type_class_ref (NM_TYPE_SETTING_SERIAL_PARITY);
+			guint i;
+
+			check = 1;
+
+			/* In older versions, parity was G_TYPE_CHAR/gint8, and the character
+			 * value was stored as integer.
+			 * For example parity=69 equals parity=E, meaning NM_SETTING_SERIAL_PARITY_EVEN.
+			 *
+			 * That means, certain values are reserved. Assert that these numbers
+			 * are not reused when we extend NMSettingSerialParity enum.
+			 * Actually, since NM_SETTING_SERIAL_PARITY is g_param_spec_enum(),
+			 * we anyway cannot extend the enum without breaking API...
+			 *
+			 * [1] commit "a91e60902e libnm-core: make NMSettingSerial:parity an enum"
+			 * [2] https://cgit.freedesktop.org/NetworkManager/NetworkManager/commit/?id=a91e60902eabae1de93d61323dae6ac894b5d40f
+			 */
+			g_assert (G_IS_ENUM_CLASS (klass));
+			for (i = 0; i < klass->n_values; i++) {
+				const GEnumValue *v = &klass->values[i];
+				int num = v->value;
+
+				g_assert (_parity_from_char (num) == -1);
+				g_assert (!NM_IN_SET (num, 'e', 'E', 'o', 'O', 'n', 'N'));
+			}
+		}
+	}
+#endif
+
+	switch (ch) {
+	case 'E':
+	case 'e':
+		return NM_SETTING_SERIAL_PARITY_EVEN;
+	case 'O':
+	case 'o':
+		return NM_SETTING_SERIAL_PARITY_ODD;
+	case 'N':
+	case 'n':
+		return NM_SETTING_SERIAL_PARITY_NONE;
+	}
+
+	return -1;
+}
+
 static void
 parity_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 {
 	const char *setting_name = nm_setting_get_name (setting);
-	NMSettingSerialParity parity;
-	int int_val;
-	gs_free char *str_val = NULL;
+	gs_free_error GError *err = NULL;
+	int parity;
+	gs_free char *tmp_str = NULL;
+	gint64 i64;
 
 	/* Keyfile traditionally stored this as the ASCII value for 'E', 'o', or 'n'.
 	 * We now accept either that or the (case-insensitive) character itself (but
 	 * still always write it the old way, for backward compatibility).
 	 */
-	int_val = nm_keyfile_plugin_kf_get_integer (info->keyfile, setting_name, key, NULL);
-	if (!int_val) {
-		str_val = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
-		if (str_val) {
-			if (str_val[0] && !str_val[1])
-				int_val = str_val[0];
-			else {
-				/* This will hit the warning below */
-				int_val = 'X';
-			}
+	tmp_str = nm_keyfile_plugin_kf_get_value (info->keyfile, setting_name, key, &err);
+	if (err)
+		goto out_err;
+
+	if (   tmp_str
+	    && tmp_str[0] != '\0'
+	    && tmp_str[1] == '\0') {
+		/* the ASCII characters like 'E' are taken directly... */
+		parity = _parity_from_char (tmp_str[0]);
+		if (parity >= 0)
+			goto parity_good;
+	}
+
+	i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT, G_MAXINT, G_MININT64);
+	if (   i64 != G_MININT64
+	    && errno == 0) {
+
+		if ((parity = _parity_from_char (i64)) >= 0) {
+			/* another oddity: the string is a valid number. However, if the numeric values
+			 * is one of the supported ASCII codes, accept it (like 69 for 'E').
+			 */
+			goto parity_good;
 		}
+
+		/* Finally, take the numeric value as is. */
+		parity = i64;
+		goto parity_good;
 	}
 
-	if (!int_val)
-		return;
+	handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+	             _("invalid parity value '%s'"),
+	             tmp_str ?: "");
+	return;
 
-	switch (int_val) {
-	case 'E':
-	case 'e':
-		parity = NM_SETTING_SERIAL_PARITY_EVEN;
-		break;
-	case 'O':
-	case 'o':
-		parity = NM_SETTING_SERIAL_PARITY_ODD;
-		break;
-	case 'N':
-	case 'n':
-		parity = NM_SETTING_SERIAL_PARITY_NONE;
-		break;
-	default:
-		handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-		             _("invalid parity value '%s'"),
-		             str_val ?: "");
+parity_good:
+	nm_g_object_set_property_enum (G_OBJECT (setting), key, NM_TYPE_SETTING_SERIAL_PARITY, parity, &err);
+
+out_err:
+	if (!err)
+		return;
+	if (   err->domain == G_KEY_FILE_ERROR
+	    && NM_IN_SET (err->code, G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
+	                             G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+		/* ignore such errors. The key is not present. */
 		return;
 	}
-
-	g_object_set (setting, key, parity, NULL);
+	handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+	             _("invalid setting: %s"), err->message);
 }
 
 static void
@@ -1433,7 +1506,9 @@ team_config_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key
 	gs_free_error GError *error = NULL;
 
 	conf = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL);
-	if (conf && conf[0] && !nm_utils_is_json_object (conf, &error)) {
+	if (   conf
+	    && conf[0]
+	    && !nm_utils_is_json_object (conf, &error)) {
 		handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
 		             _("ignoring invalid team configuration: %s"),
 		             error->message);
@@ -1455,7 +1530,7 @@ qdisc_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 	qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref);
 
 	keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL);
-	if (!keys || n_keys == 0)
+	if (n_keys == 0)
 		return;
 
 	for (i = 0; i < n_keys; i++) {
@@ -1503,7 +1578,7 @@ tfilter_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key)
 	tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref);
 
 	keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, setting_name, &n_keys, NULL);
-	if (!keys || n_keys == 0)
+	if (n_keys == 0)
 		return;
 
 	for (i = 0; i < n_keys; i++) {
@@ -1663,16 +1738,21 @@ write_ip_values (GKeyFile *file,
                  const char *gateway,
                  gboolean is_route)
 {
-	GString *output;
-	int family, i;
-	const char *addr, *gw;
+	nm_auto_free_gstring GString *output = NULL;
+	int addr_family;
+	guint i;
+	const char *addr;
+	const char *gw;
 	guint32 plen;
-	char key_name[64], *key_name_idx;
+	char key_name[64];
+	char *key_name_idx;
 
 	if (!array->len)
 		return;
 
-	family = !strcmp (setting_name, NM_SETTING_IP4_CONFIG_SETTING_NAME) ? AF_INET : AF_INET6;
+	addr_family =   nm_streq (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);
@@ -1693,7 +1773,9 @@ write_ip_values (GKeyFile *file,
 
 			addr = nm_ip_address_get_address (address);
 			plen = nm_ip_address_get_prefix (address);
-			gw = i == 0 ? gateway : NULL;
+			gw =   (i == 0)
+			     ? gateway
+			     : NULL;
 		}
 
 		g_string_set_size (output, 0);
@@ -1706,18 +1788,19 @@ write_ip_values (GKeyFile *file,
 			 * The current version supports reading of the above form.
 			 */
 			if (!gw) {
-				if (family == AF_INET)
+				if (addr_family == AF_INET)
 					gw = "0.0.0.0";
 				else
 					gw = "::";
 			}
 
 			g_string_append_printf (output, ",%s", gw);
-			if (is_route && metric != -1)
+			if (   is_route
+			    && metric != -1)
 				g_string_append_printf (output, ",%lu", (unsigned long) metric);
 		}
 
-		sprintf (key_name_idx, "%d", i + 1);
+		sprintf (key_name_idx, "%u", i + 1);
 		nm_keyfile_plugin_kf_set_string (file, setting_name, key_name, output->str);
 
 		if (is_route) {
@@ -1732,7 +1815,6 @@ write_ip_values (GKeyFile *file,
 			}
 		}
 	}
-	g_string_free (output, TRUE);
 }
 
 static void
@@ -1843,7 +1925,8 @@ write_hash_of_string (GKeyFile *file,
 	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)) {
+	if (   NM_IS_SETTING_VPN (setting)
+	    && nm_streq (key, NM_SETTING_VPN_SECRETS)) {
 		group_name = NM_KEYFILE_GROUP_VPN_SECRETS;
 		vpn_secrets = TRUE;
 	}
@@ -1852,8 +1935,8 @@ write_hash_of_string (GKeyFile *file,
 
 	keys = nm_utils_strdict_get_keys (hash, TRUE, &l);
 	for (i = 0; i < l; i++) {
+		gs_free char *to_free = NULL;
 		const char *property, *data;
-		gboolean write_item = TRUE;
 
 		property = keys[i];
 
@@ -1864,19 +1947,16 @@ write_hash_of_string (GKeyFile *file,
 		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 (!nm_setting_get_secret_flags (setting, property, &secret_flags, NULL))
+				nm_assert_not_reached ();
+			if (!_secret_flags_persist_secret (secret_flags))
+				continue;
 		}
 
-		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);
-		}
+		data = g_hash_table_lookup (hash, property);
+		nm_keyfile_plugin_kf_set_string (file, group_name,
+		                                 nm_keyfile_key_encode (property, &to_free),
+		                                 data);
 	}
 }
 
@@ -1976,9 +2056,10 @@ cert_writer_default (NMConnection *connection,
 
 	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 *path_free = NULL;
 		gs_free char *base_dir = NULL;
+		gs_free char *tmp = NULL;
+		const char *path;
 
 		path = cert_data->vtable->path_func (cert_data->setting);
 		g_assert (path);
@@ -1988,7 +2069,8 @@ cert_writer_default (NMConnection *connection,
 		 * context. */
 		if (path[0] && path[0] != '/') {
 			base_dir = g_get_current_dir ();
-			path = path_free = g_strconcat (base_dir, "/", path, NULL);
+			path_free = g_strconcat (base_dir, "/", path, NULL);
+			path = path_free;
 		} else
 			base_dir = g_path_get_dirname (path);
 
@@ -1998,20 +2080,21 @@ cert_writer_default (NMConnection *connection,
 		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);
+		else {
+			tmp = g_strconcat (NM_KEYFILE_CERT_SCHEME_PREFIX_PATH, path, NULL);
+			path = tmp;
+		}
 
 		/* 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;
+		gs_free char *blob_base64 = NULL;
+		gs_free char *val = NULL;
 
 		blob = cert_data->vtable->blob_func (cert_data->setting);
 		g_assert (blob);
@@ -2021,8 +2104,6 @@ cert_writer_default (NMConnection *connection,
 		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));
@@ -2048,7 +2129,7 @@ cert_writer (KeyfileWriterInfo *info,
 	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) {
+		if (nm_streq0 (nm_setting_8021x_scheme_vtable[i].setting_key, key)) {
 			objtype = &nm_setting_8021x_scheme_vtable[i];
 			break;
 		}
@@ -2506,26 +2587,28 @@ _parse_info_find (NMSetting *setting,
 /*****************************************************************************/
 
 static void
-read_one_setting_value (NMSetting *setting,
-                        const char *key,
-                        const GValue *value,
-                        GParamFlags flags,
-                        gpointer user_data)
+read_one_setting_value (KeyfileReaderInfo *info,
+                        NMSetting *setting,
+                        const NMSettInfoProperty *property_info)
 {
-	KeyfileReaderInfo *info = user_data;
 	GKeyFile *keyfile = info->keyfile;
-	const char *setting_name;
-	int errsv;
-	GType type;
 	gs_free_error GError *err = NULL;
 	const ParseInfoProperty *pip;
+	gs_free char *tmp_str = NULL;
+	const char *setting_name;
+	const char *key;
+	GType type;
+	guint64 u64;
+	gint64 i64;
 
-	if (info->error)
-		return;
+	nm_assert (!info->error);
+	nm_assert (property_info->param_spec);
 
-	if (!(flags & G_PARAM_WRITABLE))
+	if ((property_info->param_spec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) != G_PARAM_WRITABLE)
 		return;
 
+	key = property_info->param_spec->name;
+
 	pip = _parse_info_find (setting, key, &setting_name);
 
 	nm_assert (setting_name);
@@ -2544,7 +2627,7 @@ read_one_setting_value (NMSetting *setting,
 	 */
 	if (   (!pip || !pip->parser_no_check_key)
 	    && !nm_keyfile_plugin_kf_has_key (keyfile, setting_name, key, &err)) {
-		/* Key doesn't exist or an error ocurred, thus nothing to do. */
+		/* Key doesn't exist or an error occurred, thus nothing to do. */
 		if (err) {
 			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
 			                  _("error loading setting value: %s"),
@@ -2559,69 +2642,78 @@ read_one_setting_value (NMSetting *setting,
 		return;
 	}
 
-	type = G_VALUE_TYPE (value);
+	type = G_PARAM_SPEC_VALUE_TYPE (property_info->param_spec);
 
 	if (type == G_TYPE_STRING) {
 		gs_free char *str_val = NULL;
 
-		str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, NULL);
-		g_object_set (setting, key, str_val, NULL);
+		str_val = nm_keyfile_plugin_kf_get_string (keyfile, setting_name, key, &err);
+		if (!err)
+			nm_g_object_set_property_string_take (G_OBJECT (setting), key, g_steal_pointer (&str_val), &err);
 	} else if (type == G_TYPE_UINT) {
-		int int_val;
-
-		int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL);
-		if (int_val < 0) {
-			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-			                  _("invalid negative value (%i)"),
-			                  int_val))
-				return;
+		tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
+		if (!err) {
+			u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT, G_MAXUINT64);
+			if (   u64 == G_MAXUINT64
+			    && errno != 0) {
+				g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
+				                     _("value cannot be interpreted as integer"));
+			} else
+				nm_g_object_set_property_uint (G_OBJECT (setting), key, u64, &err);
 		}
-		g_object_set (setting, key, int_val, NULL);
 	} else if (type == G_TYPE_INT) {
-		int int_val;
-
-		int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL);
-		g_object_set (setting, key, int_val, NULL);
+		tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
+		if (!err) {
+			i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT, G_MAXINT, G_MININT64);
+			if (   i64 == G_MININT64
+			    && errno != 0) {
+				g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
+				                     _("value cannot be interpreted as integer"));
+			} else
+				nm_g_object_set_property_int (G_OBJECT (setting), key, i64, &err);
+		}
 	} else if (type == G_TYPE_BOOLEAN) {
 		gboolean bool_val;
 
-		bool_val = nm_keyfile_plugin_kf_get_boolean (keyfile, setting_name, key, NULL);
-		g_object_set (setting, key, bool_val, NULL);
+		bool_val = nm_keyfile_plugin_kf_get_boolean (keyfile, setting_name, key, &err);
+		if (!err)
+			nm_g_object_set_property_boolean (G_OBJECT (setting), key, bool_val, &err);
 	} else if (type == G_TYPE_CHAR) {
-		int int_val;
-
-		int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, NULL);
-		if (int_val < G_MININT8 || int_val > G_MAXINT8) {
-			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-			                  _("invalid char value (%i)"),
-			                  int_val))
-				return;
+		tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
+		if (!err) {
+			/* As documented by glib, G_TYPE_CHAR is really a (signed!) gint8. */
+			i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT8, G_MAXINT8, G_MININT64);
+			if (   i64 == G_MININT64
+			    && errno != 0) {
+				g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
+				                     _("value cannot be interpreted as integer"));
+			} else
+				nm_g_object_set_property_char (G_OBJECT (setting), key, i64, &err);
 		}
-
-		g_object_set (setting, key, int_val, NULL);
 	} else if (type == G_TYPE_UINT64) {
-		gs_free char *tmp_str = NULL;
-		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_object_set (setting, key, uint_val, NULL);
+		tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
+		if (!err) {
+			u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT64, G_MAXUINT64);
+			if (   u64 == G_MAXUINT64
+			    && errno != 0) {
+				g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
+				                     _("value cannot be interpreted as integer"));
+			} else
+				nm_g_object_set_property_uint64 (G_OBJECT (setting), key, u64, &err);
+		}
 	} else if (type == G_TYPE_INT64) {
-		gs_free char *tmp_str = NULL;
-		gint64 int_val;
-
-		tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, NULL);
-		int_val = _nm_utils_ascii_str_to_int64 (tmp_str, 10, G_MININT64, G_MAXINT64, 0);
-		errsv = errno;
-		if (errsv) {
-			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-			                  _("invalid int64 value (%s)"),
-			                  tmp_str))
-				return;
-		} else
-			g_object_set (setting, key, int_val, NULL);
+		tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
+		if (!err) {
+			i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT64, G_MAXINT64, G_MAXINT64);
+			if (   i64 == G_MAXINT64
+			    && errno != 0) {
+				g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
+				                     _("value cannot be interpreted as integer"));
+			} else
+				nm_g_object_set_property_int64 (G_OBJECT (setting), key, i64, &err);
+		}
 	} else if (type == G_TYPE_BYTES) {
-		int *tmp;
+		gs_free int *tmp = NULL;
 		GByteArray *array;
 		GBytes *bytes;
 		gsize length;
@@ -2632,7 +2724,7 @@ read_one_setting_value (NMSetting *setting,
 
 		array = g_byte_array_sized_new (length);
 		for (i = 0; i < length; i++) {
-			int val = tmp[i];
+			const int val = tmp[i];
 			unsigned char v = (unsigned char) (val & 0xFF);
 
 			if (val < 0 || val > 255) {
@@ -2641,7 +2733,6 @@ read_one_setting_value (NMSetting *setting,
 				                     _("ignoring invalid byte element '%d' (not between 0 and 255 inclusive)"),
 				                     val)) {
 					g_byte_array_unref (array);
-					g_free (tmp);
 					return;
 				}
 				already_warned = TRUE;
@@ -2652,54 +2743,61 @@ read_one_setting_value (NMSetting *setting,
 		bytes = g_byte_array_free_to_bytes (array);
 		g_object_set (setting, key, bytes, NULL);
 		g_bytes_unref (bytes);
-		g_free (tmp);
 	} else if (type == G_TYPE_STRV) {
-		char **sa;
+		gs_strfreev char **sa = NULL;
 		gsize length;
 
 		sa = nm_keyfile_plugin_kf_get_string_list (keyfile, setting_name, key, &length, NULL);
 		g_object_set (setting, key, sa, NULL);
-		g_strfreev (sa);
 	} else if (type == G_TYPE_HASH_TABLE) {
 		read_hash_of_string (keyfile, setting, key);
 	} else if (type == G_TYPE_ARRAY) {
 		read_array_of_uint (keyfile, setting, key);
-	} else if (G_VALUE_HOLDS_FLAGS (value)) {
-		guint64 uint_val;
-
-		/* Flags are guint but GKeyFile has no uint reader, just uint64 */
-		uint_val = nm_keyfile_plugin_kf_get_uint64 (keyfile, setting_name, key, &err);
+	} else if (G_TYPE_IS_FLAGS (type)) {
+		tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
 		if (!err) {
-			if (uint_val <= G_MAXUINT)
-				g_object_set (setting, key, (guint) uint_val, NULL);
-			else {
-				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;
-			}
+			u64 = _nm_utils_ascii_str_to_uint64 (tmp_str, 0, 0, G_MAXUINT, G_MAXUINT64);
+			if (   u64 == G_MAXUINT64
+			    && errno != 0) {
+				g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
+				                     _("value cannot be interpreted as integer"));
+			} else
+				nm_g_object_set_property_flags (G_OBJECT (setting), key, type, u64, &err);
+		}
+	} else if (G_TYPE_IS_ENUM (type)) {
+		tmp_str = nm_keyfile_plugin_kf_get_value (keyfile, setting_name, key, &err);
+		if (!err) {
+			i64 = _nm_utils_ascii_str_to_int64 (tmp_str, 0, G_MININT, G_MAXINT, G_MAXINT64);
+			if (   i64 == G_MAXINT64
+			    && errno != 0) {
+				g_set_error_literal (&err, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE,
+				                     _("value cannot be interpreted as integer"));
+			} else
+				nm_g_object_set_property_enum (G_OBJECT (setting), key, type, i64, &err);
 		}
-	} else if (G_VALUE_HOLDS_ENUM (value)) {
-		int int_val;
+	} else
+		g_return_if_reached ();
 
-		int_val = nm_keyfile_plugin_kf_get_integer (keyfile, setting_name, key, &err);
-		if (!err)
-			g_object_set (setting, key, (int) int_val, NULL);
-	} else {
-		if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
-		                 _("unhandled setting property type '%s'"),
-		                 G_VALUE_TYPE_NAME (value)))
-			return;
+	if (err) {
+		if (   err->domain == G_KEY_FILE_ERROR
+		    && NM_IN_SET (err->code, G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
+		                             G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+			/* ignore such errors. The key is not present. */
+		} else {
+			handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			             _("invalid setting: %s"), err->message);
+		}
 	}
 }
 
-static NMSetting *
-read_setting (KeyfileReaderInfo *info)
+static void
+_read_setting (KeyfileReaderInfo *info)
 {
 	const NMSettInfoSetting *sett_info;
 	gs_unref_object NMSetting *setting = NULL;
 	const char *alias;
 	GType type;
+	guint i;
 
 	alias = nm_keyfile_plugin_get_setting_name_for_alias (info->group);
 	if (!alias)
@@ -2709,34 +2807,36 @@ read_setting (KeyfileReaderInfo *info)
 	if (!type) {
 		handle_warn (info, NULL, NM_KEYFILE_WARN_SEVERITY_WARN,
 		             _("invalid setting name '%s'"), info->group);
-		return NULL;
+		return;
 	}
 
 	setting = g_object_new (type, NULL);
 
 	info->setting = setting;
 
-	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+	sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (setting));
 
 	if (sett_info->detail.gendata_info) {
 		gs_free char **keys = NULL;
-		gsize i, n_keys;
+		gsize k, n_keys;
 
 		keys = g_key_file_get_keys (info->keyfile, info->group, &n_keys, NULL);
+		if (!keys)
+			n_keys = 0;
 		if (n_keys > 0) {
 			GHashTable *h = _nm_setting_gendata_hash (setting, TRUE);
 
 			nm_utils_strv_sort (keys, n_keys);
-			for (i = 0; i < n_keys; i++) {
-				gs_free char *key = keys[i];
+			for (k = 0; k < n_keys; k++) {
+				gs_free char *key = keys[k];
 				gs_free_error GError *local = NULL;
 				const GVariantType *variant_type;
 				GVariant *variant;
 
-				/* a GKeyfile can return duplicate keys, there is just no API to make sense
+				/* a GKeyFile can return duplicate keys, there is just no API to make sense
 				 * of them. Skip them. */
-				if (   i + 1 < n_keys
-				    && nm_streq (key, keys[i + 1]))
+				if (   k + 1 < n_keys
+				    && nm_streq (key, keys[k + 1]))
 					continue;
 
 				/* currently, the API is very simple. The setting class just returns
@@ -2781,34 +2881,175 @@ read_setting (KeyfileReaderInfo *info)
 				                     g_steal_pointer (&key),
 				                     g_variant_take_ref (variant));
 			}
-			for (; i < n_keys; i++)
-				g_free (keys[i]);
+			for (; k < n_keys; k++)
+				g_free (keys[k]);
 		}
-	} else
-		nm_setting_enumerate_values (setting, read_one_setting_value, info);
+	}
 
+	for (i = 0; i < sett_info->property_infos_len; i++) {
+		const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
+
+		if (property_info->param_spec) {
+			read_one_setting_value (info, setting, property_info);
+			if (info->error)
+				goto out;
+		}
+	}
+
+out:
 	info->setting = NULL;
+	if (!info->error)
+		nm_connection_add_setting (info->connection, g_steal_pointer (&setting));
+}
+
+static void
+_read_setting_wireguard_peer (KeyfileReaderInfo *info)
+{
+	gs_unref_object NMSettingWireGuard *s_wg_new = NULL;
+	nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL;
+	gs_free_error GError *error = NULL;
+	NMSettingWireGuard *s_wg;
+	gs_free char *str = NULL;
+	const char *cstr = NULL;
+	const char *key;
+	gint64 i64;
+	gs_strfreev char **sa = NULL;
+	gsize n_sa;
+
+	peer = nm_wireguard_peer_new ();
+
+	nm_assert (g_str_has_prefix (info->group, NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER));
+	cstr = &info->group[NM_STRLEN (NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER)];
+	if (   !nm_utils_base64secret_normalize (cstr, NM_WIREGUARD_PUBLIC_KEY_LEN, &str)
+	    || !nm_streq0 (str, cstr)) {
+		/* the group name must be identical to the normalized(!) key, so that it
+		 * is uniquely identified. */
+		handle_warn (info, NULL, NM_KEYFILE_WARN_SEVERITY_WARN,
+		             _("invalid peer public key in section '%s'"),
+		             info->group);
+		return;
+	}
+	nm_wireguard_peer_set_public_key (peer, cstr, TRUE);
+	nm_clear_g_free (&str);
+
+	key = NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY;
+	str = nm_keyfile_plugin_kf_get_string (info->keyfile, info->group, key, NULL);
+	if (str) {
+		if (!nm_wireguard_peer_set_preshared_key (peer, str, FALSE)) {
+			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			                  _("key '%s.%s' is not not a valid 256 bit key in base64 encoding"),
+			                  info->group, key))
+				return;
+		}
+		nm_clear_g_free (&str);
+	}
+
+	key = NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS;
+	i64 = nm_keyfile_plugin_kf_get_int64 (info->keyfile, info->group, key, 0, 0, NM_SETTING_SECRET_FLAG_ALL, -1, NULL);
+	if (errno != ENODATA) {
+		if (   i64 == -1
+		    || !_nm_setting_secret_flags_valid (i64)) {
+			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			                  _("key '%s.%s' is not not a valid secret flag"),
+			                  info->group, key))
+				return;
+		} else
+			nm_wireguard_peer_set_preshared_key_flags (peer, i64);
+	}
+
+	key = NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE;
+	i64 = nm_keyfile_plugin_kf_get_int64 (info->keyfile, info->group, key, 0, 0, G_MAXUINT32, -1, NULL);
+	if (errno != ENODATA) {
+		if (i64 == -1) {
+			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			                  _("key '%s.%s' is not not a integer in range 0 to 2^32"),
+			                  info->group, key))
+				return;
+		} else
+			nm_wireguard_peer_set_persistent_keepalive (peer, i64);
+	}
+
+	key = NM_WIREGUARD_PEER_ATTR_ENDPOINT;
+	str = nm_keyfile_plugin_kf_get_string (info->keyfile, info->group, key, NULL);
+	if (str && str[0]) {
+		if (!nm_wireguard_peer_set_endpoint (peer, str, FALSE)) {
+			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			                  _("key '%s.%s' is not not a valid endpoint"),
+			                  info->group, key))
+				return;
+		}
+	}
+	nm_clear_g_free (&str);
+
+	key = NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS;
+	sa = nm_keyfile_plugin_kf_get_string_list (info->keyfile, info->group, key, &n_sa, NULL);
+	if (n_sa > 0) {
+		gboolean has_error = FALSE;
+		gsize i;
+
+		for (i = 0; i < n_sa; i++) {
+			if (!nm_utils_parse_inaddr_prefix_bin (AF_UNSPEC, sa[i], NULL, NULL, NULL)) {
+				has_error = TRUE;
+				continue;
+			}
+			nm_wireguard_peer_append_allowed_ip (peer, sa[i], TRUE);
+		}
+		if (has_error) {
+			if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+			                  _("key '%s.%s' has invalid allowed-ips"),
+			                  info->group, key))
+				return;
+		}
+	}
+	nm_clear_pointer (&sa, g_strfreev);
 
 	if (info->error)
-		return NULL;
-	return g_steal_pointer (&setting);
+		return;
+
+	if (!nm_wireguard_peer_is_valid (peer, TRUE, TRUE, &error)) {
+		if (!handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN,
+		                  _("peer '%s' is invalid: %s"),
+		                  info->group, error->message))
+			return;
+		return;
+	}
+
+	s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (info->connection, NM_TYPE_SETTING_WIREGUARD));
+	if (!s_wg) {
+		s_wg_new = NM_SETTING_WIREGUARD (nm_setting_wireguard_new ());
+		s_wg = s_wg_new;
+	}
+
+	nm_setting_wireguard_append_peer (s_wg, peer);
+
+	if (s_wg_new) {
+		nm_connection_add_setting (info->connection,
+		                           NM_SETTING (g_steal_pointer (&s_wg_new)));
+	}
 }
 
 static void
-read_vpn_secrets (KeyfileReaderInfo *info, NMSettingVpn *s_vpn)
+_read_setting_vpn_secrets (KeyfileReaderInfo *info)
 {
 	gs_strfreev char **keys = NULL;
-	char **iter;
+	gsize i, n_keys;
+	NMSettingVpn *s_vpn;
+
+	s_vpn = nm_connection_get_setting_vpn (info->connection);
+	if (!s_vpn) {
+		/* if we don't also have a [vpn] section (which must be parsed earlier),
+		 * we don't do anything. */
+		nm_assert (!g_key_file_has_group (info->keyfile, "vpn"));
+		return;
+	}
 
-	keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, NM_KEYFILE_GROUP_VPN_SECRETS, NULL, NULL);
-	for (iter = keys; *iter; iter++) {
-		char *secret;
+	keys = nm_keyfile_plugin_kf_get_keys (info->keyfile, NM_KEYFILE_GROUP_VPN_SECRETS, &n_keys, NULL);
+	for (i = 0; i < n_keys; i++) {
+		gs_free char *secret = NULL;
 
-		secret = nm_keyfile_plugin_kf_get_string (info->keyfile, NM_KEYFILE_GROUP_VPN_SECRETS, *iter, NULL);
-		if (secret) {
-			nm_setting_vpn_add_secret (s_vpn, *iter, secret);
-			g_free (secret);
-		}
+		secret = nm_keyfile_plugin_kf_get_string (info->keyfile, NM_KEYFILE_GROUP_VPN_SECRETS, keys[i], NULL);
+		if (secret)
+			nm_setting_vpn_add_secret (s_vpn, keys[i], secret);
 	}
 }
 
@@ -2876,12 +3117,11 @@ nm_keyfile_read (GKeyFile *keyfile,
 {
 	gs_unref_object NMConnection *connection = NULL;
 	NMSettingConnection *s_con;
-	NMSetting *setting;
-	char **groups;
-	gsize length;
+	gs_strfreev char **groups = NULL;
+	gsize n_groups;
 	gsize i;
 	gboolean vpn_secrets = FALSE;
-	KeyfileReaderInfo info = { 0 };
+	KeyfileReaderInfo info;
 
 	g_return_val_if_fail (keyfile, NULL);
 	g_return_val_if_fail (!error || !*error, NULL);
@@ -2889,31 +3129,35 @@ nm_keyfile_read (GKeyFile *keyfile,
 
 	connection = nm_simple_connection_new ();
 
-	info.connection = connection;
-	info.keyfile = (GKeyFile *) keyfile;
-	info.base_dir = base_dir;
-	info.handler = handler;
-	info.user_data = user_data;
+	info = (KeyfileReaderInfo) {
+		.connection = connection,
+		.keyfile    = keyfile,
+		.base_dir   = base_dir,
+		.handler    = handler,
+		.user_data  = user_data,
+	};
+
+	groups = g_key_file_get_groups (keyfile, &n_groups);
+	if (!groups)
+		n_groups = 0;
 
-	groups = g_key_file_get_groups (keyfile, &length);
-	for (i = 0; i < length; i++) {
-		/* Only read out secrets when needed */
-		if (!strcmp (groups[i], NM_KEYFILE_GROUP_VPN_SECRETS)) {
-			vpn_secrets = TRUE;
-			continue;
-		}
+	for (i = 0; i < n_groups; i++) {
 
 		info.group = groups[i];
-		setting = read_setting (&info);
+
+		if (nm_streq (groups[i], NM_KEYFILE_GROUP_VPN_SECRETS)) {
+			/* Only read out secrets when needed */
+			vpn_secrets = TRUE;
+		} else if (NM_STR_HAS_PREFIX (groups[i], NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER))
+			_read_setting_wireguard_peer (&info);
+		else
+			_read_setting (&info);
+
 		info.group = NULL;
-		if (info.error) {
-			g_propagate_error (error, info.error);
-			return NULL;
-		}
-		if (setting)
-			nm_connection_add_setting (connection, setting);
+
+		if (info.error)
+			goto out_with_info_error;
 	}
-	g_strfreev (groups);
 
 	s_con = nm_connection_get_setting_connection (connection);
 	if (!s_con) {
@@ -2926,55 +3170,51 @@ nm_keyfile_read (GKeyFile *keyfile,
 	 */
 	if (   !nm_setting_connection_get_interface_name (s_con)
 	    && nm_setting_connection_get_connection_type (s_con)) {
-		char *interface_name;
+		gs_free char *interface_name = NULL;
 
 		interface_name = g_key_file_get_string (keyfile,
 		                                        nm_setting_connection_get_connection_type (s_con),
 		                                        "interface-name",
 		                                        NULL);
-		if (interface_name) {
+		if (interface_name)
 			g_object_set (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, interface_name, NULL);
-			g_free (interface_name);
-		}
 	}
 
-	/* Handle vpn secrets after the 'vpn' setting was read */
 	if (vpn_secrets) {
-		NMSettingVpn *s_vpn;
-
-		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;
-			}
-		}
+		info.group = NM_KEYFILE_GROUP_VPN_SECRETS;
+		_read_setting_vpn_secrets (&info);
+		info.group = NULL;;
+		if (info.error)
+			goto out_with_info_error;
 	}
 
 	return g_steal_pointer (&connection);
+
+out_with_info_error:
+	g_propagate_error (error, info.error);
+	return NULL;
 }
 
 /*****************************************************************************/
 
 static void
-write_setting_value (NMSetting *setting,
-                     const char *key,
-                     const GValue *value,
-                     GParamFlags flag,
-                     gpointer user_data)
+write_setting_value (KeyfileWriterInfo *info,
+                     NMSetting *setting,
+                     const NMSettInfoProperty *property_info)
 {
-	KeyfileWriterInfo *info = user_data;
+	const ParseInfoProperty *pip;
 	const char *setting_name;
+	const char *key;
+	char numstr[64];
+	GValue value;
 	GType type;
-	const ParseInfoProperty *pip;
-	GParamSpec *pspec;
 
-	if (info->error)
+	nm_assert (!info->error);
+
+	if (!property_info->param_spec)
 		return;
 
-	pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), key);
-	nm_assert (pspec);
+	key = property_info->param_spec->name;
 
 	pip = _parse_info_find (setting, key, &setting_name);
 
@@ -3002,58 +3242,65 @@ write_setting_value (NMSetting *setting,
 	 * the secret flags there are in a third-level hash in the 'secrets'
 	 * property.
 	 */
-	if (   (pspec->flags & NM_SETTING_PARAM_SECRET)
+	if (   (property_info->param_spec->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)
+			g_return_if_reached ();
+		if (!_secret_flags_persist_secret (secret_flags))
 			return;
 	}
 
+	value = (GValue) { 0 };
+
+	g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (property_info->param_spec));
+	g_object_get_property (G_OBJECT (setting), property_info->param_spec->name, &value);
+
 	if (   (!pip || !pip->writer_persist_default)
-	    && g_param_value_defaults (pspec, (GValue *) value)) {
+	    && g_param_value_defaults (property_info->param_spec, &value)) {
 		nm_assert (!g_key_file_has_key (info->keyfile, setting_name, key, NULL));
-		return;
+		goto out_unset_value;
 	}
 
 	if (pip && pip->writer) {
-		pip->writer (info, setting, key, value);
-		return;
+		pip->writer (info, setting, key, &value);
+		goto out_unset_value;
 	}
 
-	type = G_VALUE_TYPE (value);
+	type = G_VALUE_TYPE (&value);
 	if (type == G_TYPE_STRING) {
 		const char *str;
 
-		str = g_value_get_string (value);
+		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));
+	} else if (type == G_TYPE_UINT) {
+		nm_sprintf_buf (numstr, "%u", g_value_get_uint (&value));
+		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
+	} else if (type == G_TYPE_INT) {
+		nm_sprintf_buf (numstr, "%d", g_value_get_int (&value));
+		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
+	} else if (type == G_TYPE_UINT64) {
+		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_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));
+		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key,
+		                                  g_value_get_boolean (&value)
+		                                ? "true"
+		                                : "false");
 	} else if (type == G_TYPE_CHAR) {
-		nm_keyfile_plugin_kf_set_integer (info->keyfile, setting_name, key, (int) g_value_get_schar (value));
+		nm_sprintf_buf (numstr, "%d", (int) g_value_get_schar (&value));
+		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
 	} else if (type == G_TYPE_BYTES) {
 		GBytes *bytes;
 		const guint8 *data;
 		gsize len = 0;
 
-		bytes = g_value_get_boxed (value);
+		bytes = g_value_get_boxed (&value);
 		data = bytes ? g_bytes_get_data (bytes, &len) : NULL;
 
 		if (data != NULL && len > 0)
@@ -3061,19 +3308,109 @@ write_setting_value (NMSetting *setting,
 	} else if (type == G_TYPE_STRV) {
 		char **array;
 
-		array = (char **) g_value_get_boxed (value);
+		array = (char **) g_value_get_boxed (&value);
 		nm_keyfile_plugin_kf_set_string_list (info->keyfile, setting_name, key, (const char **const) array, g_strv_length (array));
 	} else if (type == G_TYPE_HASH_TABLE) {
-		write_hash_of_string (info->keyfile, setting, key, value);
+		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, (int) g_value_get_enum (value));
-	else
-		g_warn_if_reached ();
+		write_array_of_uint (info->keyfile, setting, key, &value);
+	} else if (G_VALUE_HOLDS_FLAGS (&value)) {
+		nm_sprintf_buf (numstr, "%u", g_value_get_flags (&value));
+		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
+	} else if (G_VALUE_HOLDS_ENUM (&value)) {
+		nm_sprintf_buf (numstr, "%d", g_value_get_enum (&value));
+		nm_keyfile_plugin_kf_set_value (info->keyfile, setting_name, key, numstr);
+	} else
+		g_return_if_reached ();
+
+out_unset_value:
+	g_value_unset (&value);
+}
+
+static void
+_write_setting_wireguard (NMSetting *setting, KeyfileWriterInfo *info)
+{
+	NMSettingWireGuard *s_wg;
+	guint i_peer, n_peers;
+
+	s_wg = NM_SETTING_WIREGUARD (setting);
+
+	n_peers = nm_setting_wireguard_get_peers_len (s_wg);
+	for (i_peer = 0; i_peer < n_peers; i_peer++) {
+		NMWireGuardPeer *peer = nm_setting_wireguard_get_peer (s_wg, i_peer);
+		const char *public_key;
+		char group[NM_STRLEN (NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER) + 200];
+		NMSettingSecretFlags secret_flags;
+		gboolean any_key = FALSE;
+		guint i_aip, n_aip;
+		const char *cstr;
+		guint32 u32;
+
+		public_key = nm_wireguard_peer_get_public_key (peer);
+		if (   !public_key
+		    || !public_key[0]
+		    || !NM_STRCHAR_ALL (public_key, ch, nm_sd_utils_unbase64char (ch, TRUE) >= 0)) {
+			/* invalid peer. Skip it */
+			continue;
+		}
+
+		if (g_snprintf (group,
+		                sizeof (group),
+		                "%s%s",
+		                NM_KEYFILE_GROUPPREFIX_WIREGUARD_PEER,
+		                nm_wireguard_peer_get_public_key (peer)) >= sizeof (group)) {
+			/* Too long. Not a valid public key. Skip the peer. */
+			continue;
+		}
+
+		cstr = nm_wireguard_peer_get_endpoint (peer);
+		if (cstr) {
+			g_key_file_set_string (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_ENDPOINT, cstr);
+			any_key = TRUE;
+		}
+
+		secret_flags = nm_wireguard_peer_get_preshared_key_flags (peer);
+		if (_secret_flags_persist_secret (secret_flags)) {
+			cstr = nm_wireguard_peer_get_preshared_key (peer);
+			if (cstr) {
+				g_key_file_set_string (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, cstr);
+				any_key = TRUE;
+			}
+		}
+
+		/* usually, we don't persist the secret-flags 0 (because they are the default).
+		 * For WireGuard peers, the default secret-flags for preshared-key are 4 (not-required).
+		 * So, in this case behave differently: a missing preshared-key-flag setting means
+		 * "not-required". */
+		if (secret_flags != NM_SETTING_SECRET_FLAG_NOT_REQUIRED) {
+			g_key_file_set_int64 (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS, secret_flags);
+			any_key = TRUE;
+		}
+
+		u32 = nm_wireguard_peer_get_persistent_keepalive (peer);
+		if (u32) {
+			g_key_file_set_uint64 (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE, u32);
+			any_key = TRUE;
+		}
+
+		n_aip = nm_wireguard_peer_get_allowed_ips_len (peer);
+		if (n_aip > 0) {
+			gs_free const char **strv = NULL;
+
+			strv = g_new (const char *, ((gsize) n_aip) + 1);
+			for (i_aip = 0; i_aip < n_aip; i_aip++)
+				strv[i_aip] = nm_wireguard_peer_get_allowed_ip (peer, i_aip, NULL);
+			strv[n_aip] = NULL;
+			g_key_file_set_string_list (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS,
+			                            strv, n_aip);
+			any_key = TRUE;
+		}
+
+		if (!any_key) {
+			/* we cannot omit all keys. At an empty endpoint. */
+			g_key_file_set_string (info->keyfile, group, NM_WIREGUARD_PEER_ATTR_ENDPOINT, "");
+		}
+	}
 }
 
 GKeyFile *
@@ -3082,9 +3419,10 @@ nm_keyfile_write (NMConnection *connection,
                   void *user_data,
                   GError **error)
 {
-	KeyfileWriterInfo info = { 0 };
+	gs_unref_keyfile GKeyFile *keyfile = NULL;
+	KeyfileWriterInfo info;
 	gs_free NMSetting **settings = NULL;
-	guint i, length = 0;
+	guint i, j, n_settings = 0;
 
 	g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
 	g_return_val_if_fail (!error || !*error, NULL);
@@ -3092,18 +3430,22 @@ nm_keyfile_write (NMConnection *connection,
 	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;
+	keyfile = g_key_file_new ();
 
-	settings = nm_connection_get_settings (connection, &length);
-	for (i = 0; i < length; i++) {
+	info = (KeyfileWriterInfo) {
+		.connection = connection,
+		.keyfile    = keyfile,
+		.error      = NULL,
+		.handler    = handler,
+		.user_data  = user_data,
+	};
+
+	settings = nm_connection_get_settings (connection, &n_settings);
+	for (i = 0; i < n_settings; i++) {
 		const NMSettInfoSetting *sett_info;
 		NMSetting *setting = settings[i];
 
-		sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+		sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (setting));
 
 		if (sett_info->detail.gendata_info) {
 			guint k, n_keys;
@@ -3138,18 +3480,175 @@ nm_keyfile_write (NMConnection *connection,
 					}
 				}
 			}
-		} else
-			nm_setting_enumerate_values (setting, write_setting_value, &info);
+		}
 
-		if (info.error)
-			break;
+		for (j = 0; j < sett_info->property_infos_len; j++) {
+			const NMSettInfoProperty *property_info = _nm_sett_info_property_info_get_sorted (sett_info, j);
+
+			write_setting_value (&info, setting, property_info);
+			if (info.error)
+				goto out_with_info_error;
+		}
+
+		if (NM_IS_SETTING_WIREGUARD (setting)) {
+			_write_setting_wireguard (setting, &info);
+			if (info.error)
+				goto out_with_info_error;
+		}
+
+		nm_assert (!info.error);
 	}
 
-	if (info.error) {
-		g_propagate_error (error, info.error);
-		g_key_file_unref (info.keyfile);
-		return NULL;
+	nm_assert (!info.error);
+
+	return g_steal_pointer (&keyfile);
+
+out_with_info_error:
+	g_propagate_error (error, info.error);
+	return NULL;
+}
+
+/*****************************************************************************/
+
+static const char temp_letters[] =
+"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+/*
+ * Check '.[a-zA-Z0-9]{6}' file suffix used for temporary files by g_file_set_contents() (mkstemp()).
+ */
+static gboolean
+check_mkstemp_suffix (const char *path)
+{
+	const char *ptr;
+
+	nm_assert (path);
+
+	/* Matches *.[a-zA-Z0-9]{6} suffix of mkstemp()'s temporary files */
+	ptr = strrchr (path, '.');
+	if (   ptr
+	    && strspn (&ptr[1], temp_letters) == 6
+	    && ptr[7] == '\0')
+		return TRUE;
+	return FALSE;
+}
+
+static gboolean
+_check_suffix_impl (const char *base, const char *tag, gsize tag_len)
+{
+	gsize len;
+
+	nm_assert (base);
+	nm_assert (tag);
+	nm_assert (strlen (tag) == tag_len);
+
+	len = strlen (base);
+	if (   len > tag_len
+	    && !g_ascii_strcasecmp (base + len - tag_len, tag))
+		return TRUE;
+	return FALSE;
+}
+#define check_suffix(base, tag) _check_suffix_impl ((base), ""tag"", NM_STRLEN (tag))
+
+#define SWP_TAG ".swp"
+#define SWPX_TAG ".swpx"
+#define PEM_TAG ".pem"
+#define DER_TAG ".der"
+
+gboolean
+nm_keyfile_utils_ignore_filename (const char *filename, gboolean require_extension)
+{
+	const char *base;
+	gsize l;
+
+	/* ignore_filename() must mirror nm_keyfile_utils_create_filename() */
+
+	g_return_val_if_fail (filename, TRUE);
+
+	base = strrchr (filename, '/');
+	if (base)
+		base++;
+	else
+		base = filename;
+
+	if (!base[0]) {
+		/* this check above with strrchr() also rejects "/some/path/with/trailing/slash/",
+		 * but that is fine, because such a path would name a directory, and we are not
+		 * interested in directories. */
+		return TRUE;
+	}
+
+	if (base[0] == '.') {
+		/* don't allow hidden files */
+		return TRUE;
+	}
+
+	l = strlen (base);
+
+	if (require_extension) {
+		if (   l <= NM_STRLEN (NM_KEYFILE_PATH_SUFFIX_NMCONNECTION)
+		    || !g_str_has_suffix (base, NM_KEYFILE_PATH_SUFFIX_NMCONNECTION))
+			return TRUE;
+		return FALSE;
 	}
 
-	return info.keyfile;
+	/* Ignore backup files */
+	if (base[l - 1] == '~')
+		return TRUE;
+
+	/* Ignore temporary files */
+	if (check_mkstemp_suffix (base))
+		return TRUE;
+
+	/* Ignore 802.1x certificates and keys */
+	if (   check_suffix (base, PEM_TAG)
+	    || check_suffix (base, DER_TAG))
+		return TRUE;
+
+	return FALSE;
+}
+
+char *
+nm_keyfile_utils_create_filename (const char *name,
+                                  gboolean with_extension)
+{
+	GString *str;
+	const char *f = name;
+	/* keyfile used to escape with '*', do not change that behavior.
+	 *
+	 * But for newly added escapings, use '_' instead.
+	 * Also, @with_extension is new-style. */
+	const char ESCAPE_CHAR = with_extension ? '_' : '*';
+	const char ESCAPE_CHAR2 = '_';
+
+	g_return_val_if_fail (name && name[0], NULL);
+
+	str = g_string_sized_new (60);
+
+	/* Convert '/' to ESCAPE_CHAR */
+	for (f = name; f[0]; f++) {
+		if (f[0] == '/')
+			g_string_append_c (str, ESCAPE_CHAR);
+		else
+			g_string_append_c (str, f[0]);
+	}
+
+	/* nm_keyfile_utils_create_filename() must avoid anything that ignore_filename() would reject.
+	 * We can escape here more aggressivly then what we would read back. */
+	if (str->str[0] == '.')
+		str->str[0] = ESCAPE_CHAR2;
+	if (str->str[str->len - 1] == '~')
+		str->str[str->len - 1] = ESCAPE_CHAR2;
+	if (   check_mkstemp_suffix (str->str)
+	    || check_suffix (str->str, PEM_TAG)
+	    || check_suffix (str->str, DER_TAG))
+		g_string_append_c (str, ESCAPE_CHAR2);
+
+	if (with_extension)
+		g_string_append (str, NM_KEYFILE_PATH_SUFFIX_NMCONNECTION);
+
+	/* nm_keyfile_utils_create_filename() must mirror ignore_filename() */
+	nm_assert (!strchr (str->str, '/'));
+	nm_assert (!nm_keyfile_utils_ignore_filename (str->str, with_extension));
+
+	return g_string_free (str, FALSE);;
 }
diff --git a/libnm-core/nm-property-compare.c b/libnm-core/nm-property-compare.c
index 2a3ee397..d3a19ec7 100644
--- a/libnm-core/nm-property-compare.c
+++ b/libnm-core/nm-property-compare.c
@@ -22,11 +22,10 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-#include <netinet/in.h>
-
 #include "nm-property-compare.h"
 
+#include <netinet/in.h>
+
 static int
 _nm_property_compare_collection (GVariant *value1, GVariant *value2)
 {
diff --git a/libnm-core/nm-setting-6lowpan.c b/libnm-core/nm-setting-6lowpan.c
index c0ce232d..3a2b084e 100644
--- a/libnm-core/nm-setting-6lowpan.c
+++ b/libnm-core/nm-setting-6lowpan.c
@@ -19,9 +19,20 @@
 
 #include "nm-default.h"
 
-#include "nm-setting-private.h"
 #include "nm-setting-6lowpan.h"
 
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-6lowpan
+ * @short_description: Describes connection properties for 6LoWPAN interfaces
+ *
+ * The #NMSetting6Lowpan object is a #NMSetting subclass that describes properties
+ * necessary for connection to 6LoWPAN interfaces.
+ **/
+
+/*****************************************************************************/
+
 NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 	PROP_PARENT,
 );
@@ -43,33 +54,11 @@ struct _NMSetting6LowpanClass {
 	NMSettingClass parent;
 };
 
-/**
- * SECTION:nm-setting-6lowpan
- * @short_description: Describes connection properties for 6LoWPAN interfaces
- *
- * The #NMSetting6Lowpan object is a #NMSetting subclass that describes properties
- * necessary for connection to 6LoWPAN interfaces.
- **/
-
 G_DEFINE_TYPE (NMSetting6Lowpan, nm_setting_6lowpan, NM_TYPE_SETTING)
 
 #define NM_SETTING_6LOWPAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_6LOWPAN, NMSetting6LowpanPrivate))
 
-
-/**
- * nm_setting_6lowpan_new:
- *
- * Creates a new #NMSetting6Lowpan object with default values.
- *
- * Returns: (transfer full): the new empty #NMSetting6Lowpan object
- *
- * Since: 1.14
- **/
-NMSetting *
-nm_setting_6lowpan_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_6LOWPAN, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_6lowpan_get_parent:
@@ -143,22 +132,18 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static void
-nm_setting_6lowpan_init (NMSetting6Lowpan *setting)
-{
-}
+/*****************************************************************************/
 
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSetting6Lowpan *setting = NM_SETTING_6LOWPAN (object);
 	NMSetting6LowpanPrivate *priv = NM_SETTING_6LOWPAN_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_free (priv->parent);
-		priv->parent = g_value_dup_string (value);
+		g_value_set_string (value, priv->parent);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -167,15 +152,16 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSetting6Lowpan *setting = NM_SETTING_6LOWPAN (object);
 	NMSetting6LowpanPrivate *priv = NM_SETTING_6LOWPAN_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_value_set_string (value, priv->parent);
+		g_free (priv->parent);
+		priv->parent = g_value_dup_string (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -183,6 +169,28 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_6lowpan_init (NMSetting6Lowpan *setting)
+{
+}
+
+/**
+ * nm_setting_6lowpan_new:
+ *
+ * Creates a new #NMSetting6Lowpan object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSetting6Lowpan object
+ *
+ * Since: 1.14
+ **/
+NMSetting *
+nm_setting_6lowpan_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_6LOWPAN, NULL);
+}
+
 static void
 finalize (GObject *object)
 {
@@ -202,8 +210,8 @@ nm_setting_6lowpan_class_init (NMSetting6LowpanClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSetting6LowpanPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c
index 7e004c90..8a23fb34 100644
--- a/libnm-core/nm-setting-8021x.c
+++ b/libnm-core/nm-setting-8021x.c
@@ -24,8 +24,6 @@
 
 #include "nm-setting-8021x.h"
 
-#include <string.h>
-
 #include "nm-utils/nm-secret-utils.h"
 #include "nm-utils.h"
 #include "nm-crypto.h"
@@ -202,7 +200,7 @@ G_DEFINE_TYPE (NMSetting8021x, nm_setting_802_1x, NM_TYPE_SETTING)
  * nm_setting_802_1x_check_cert_scheme:
  * @pdata: (allow-none): the data pointer
  * @length: the length of the data
- * @error: (allow-none): (out): validation reason
+ * @error: (allow-none) (out): validation reason
  *
  * Determines and verifies the blob type.
  * When setting certificate properties of NMSetting8021x
@@ -246,7 +244,7 @@ nm_setting_802_1x_check_cert_scheme (gconstpointer pdata, gsize length, GError *
 	if (scheme != NM_SETTING_802_1X_CK_SCHEME_BLOB) {
 		/* An actual URI must be NUL terminated, contain at least
 		 * one non-NUL character, and contain only one trailing NUL
-		 * chracter.
+		 * character.
 		 * And ensure it's UTF-8 valid too so we can pass it through
 		 * D-Bus and stuff like that. */
 
@@ -3369,8 +3367,6 @@ finalize (GObject *object)
 	NMSetting8021x *self = NM_SETTING_802_1X (object);
 	NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE (self);
 
-	/* Strings first. g_free() already checks for NULLs so we don't have to */
-
 	g_free (priv->identity);
 	g_free (priv->anonymous_identity);
 	g_free (priv->ca_path);
@@ -3416,8 +3412,8 @@ nm_setting_802_1x_class_init (NMSetting8021xClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSetting8021xPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify       = verify;
diff --git a/libnm-core/nm-setting-8021x.h b/libnm-core/nm-setting-8021x.h
index 82ef5eba..eb7b1948 100644
--- a/libnm-core/nm-setting-8021x.h
+++ b/libnm-core/nm-setting-8021x.h
@@ -90,12 +90,11 @@ typedef enum { /*< underscore_name=nm_setting_802_1x_ck_scheme >*/
  */
 typedef enum { /*< underscore_name=nm_setting_802_1x_auth_flags >*/
 	NM_SETTING_802_1X_AUTH_FLAGS_NONE                = 0,
-	NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE     = (1 << 0),
-	NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE     = (1 << 1),
-	NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE     = (1 << 2),
+	NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_0_DISABLE     = 0x1,
+	NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_1_DISABLE     = 0x2,
+	NM_SETTING_802_1X_AUTH_FLAGS_TLS_1_2_DISABLE     = 0x4,
 
-	_NM_SETTING_802_1X_AUTH_FLAGS_LAST, /*< skip >*/
-	NM_SETTING_802_1X_AUTH_FLAGS_ALL                 = (((_NM_SETTING_802_1X_AUTH_FLAGS_LAST - 1) << 1) - 1),
+	NM_SETTING_802_1X_AUTH_FLAGS_ALL                 = 0x7,
 } NMSetting8021xAuthFlags;
 
 #define NM_TYPE_SETTING_802_1X            (nm_setting_802_1x_get_type ())
diff --git a/libnm-core/nm-setting-adsl.c b/libnm-core/nm-setting-adsl.c
index 9ffff927..b2de7027 100644
--- a/libnm-core/nm-setting-adsl.c
+++ b/libnm-core/nm-setting-adsl.c
@@ -21,9 +21,8 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-setting-adsl.h"
+
 #include "nm-setting-ppp.h"
 #include "nm-setting-private.h"
 #include "nm-utils.h"
@@ -37,9 +36,17 @@
  * properties of ADSL connections.
  */
 
-G_DEFINE_TYPE (NMSettingAdsl, nm_setting_adsl, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_ADSL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_ADSL, NMSettingAdslPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_USERNAME,
+	PROP_PASSWORD,
+	PROP_PASSWORD_FLAGS,
+	PROP_PROTOCOL,
+	PROP_ENCAPSULATION,
+	PROP_VPI,
+	PROP_VCI,
+);
 
 typedef struct {
 	char *  username;
@@ -51,31 +58,11 @@ typedef struct {
 	guint32 vci;
 } NMSettingAdslPrivate;
 
-enum {
-	PROP_0,
-	PROP_USERNAME,
-	PROP_PASSWORD,
-	PROP_PASSWORD_FLAGS,
-	PROP_PROTOCOL,
-	PROP_ENCAPSULATION,
-	PROP_VPI,
-	PROP_VCI,
+G_DEFINE_TYPE (NMSettingAdsl, nm_setting_adsl, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_ADSL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_ADSL, NMSettingAdslPrivate))
 
-/**
- * nm_setting_adsl_new:
- *
- * Creates a new #NMSettingAdsl object with default values.
- *
- * Returns: the new empty #NMSettingAdsl object
- **/
-NMSetting *
-nm_setting_adsl_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_ADSL, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_adsl_get_username:
@@ -250,22 +237,40 @@ need_secrets (NMSetting *setting)
 	return secrets;
 }
 
-static void
-nm_setting_adsl_init (NMSettingAdsl *setting)
-{
-}
+/*****************************************************************************/
 
 static void
-finalize (GObject *object)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE (object);
-
-	g_free (priv->username);
-	g_free (priv->password);
-	g_free (priv->protocol);
-	g_free (priv->encapsulation);
+	NMSettingAdsl *setting = NM_SETTING_ADSL (object);
 
-	G_OBJECT_CLASS (nm_setting_adsl_parent_class)->finalize (object);
+	switch (prop_id) {
+	case PROP_USERNAME:
+		g_value_set_string (value, nm_setting_adsl_get_username (setting));
+		break;
+	case PROP_PASSWORD:
+		g_value_set_string (value, nm_setting_adsl_get_password (setting));
+		break;
+	case PROP_PASSWORD_FLAGS:
+		g_value_set_flags (value, nm_setting_adsl_get_password_flags (setting));
+		break;
+	case PROP_PROTOCOL:
+		g_value_set_string (value, nm_setting_adsl_get_protocol (setting));
+		break;
+	case PROP_ENCAPSULATION:
+		g_value_set_string (value, nm_setting_adsl_get_encapsulation (setting));
+		break;
+	case PROP_VPI:
+		g_value_set_uint (value, nm_setting_adsl_get_vpi (setting));
+		break;
+	case PROP_VCI:
+		g_value_set_uint (value, nm_setting_adsl_get_vci (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -309,38 +314,37 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_adsl_init (NMSettingAdsl *setting)
 {
-	NMSettingAdsl *setting = NM_SETTING_ADSL (object);
+}
 
-	switch (prop_id) {
-	case PROP_USERNAME:
-		g_value_set_string (value, nm_setting_adsl_get_username (setting));
-		break;
-	case PROP_PASSWORD:
-		g_value_set_string (value, nm_setting_adsl_get_password (setting));
-		break;
-	case PROP_PASSWORD_FLAGS:
-		g_value_set_flags (value, nm_setting_adsl_get_password_flags (setting));
-		break;
-	case PROP_PROTOCOL:
-		g_value_set_string (value, nm_setting_adsl_get_protocol (setting));
-		break;
-	case PROP_ENCAPSULATION:
-		g_value_set_string (value, nm_setting_adsl_get_encapsulation (setting));
-		break;
-	case PROP_VPI:
-		g_value_set_uint (value, nm_setting_adsl_get_vpi (setting));
-		break;
-	case PROP_VCI:
-		g_value_set_uint (value, nm_setting_adsl_get_vci (setting));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+/**
+ * nm_setting_adsl_new:
+ *
+ * Creates a new #NMSettingAdsl object with default values.
+ *
+ * Returns: the new empty #NMSettingAdsl object
+ **/
+NMSetting *
+nm_setting_adsl_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_ADSL, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE (object);
+
+	g_free (priv->username);
+	g_free (priv->password);
+	g_free (priv->protocol);
+	g_free (priv->encapsulation);
+
+	G_OBJECT_CLASS (nm_setting_adsl_parent_class)->finalize (object);
 }
 
 static void
@@ -351,8 +355,8 @@ nm_setting_adsl_class_init (NMSettingAdslClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingAdslPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify         = verify;
@@ -364,86 +368,81 @@ nm_setting_adsl_class_init (NMSettingAdslClass *klass)
 	 *
 	 * Username used to authenticate with the ADSL service.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_USERNAME,
-		 g_param_spec_string (NM_SETTING_ADSL_USERNAME, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_USERNAME] =
+	    g_param_spec_string (NM_SETTING_ADSL_USERNAME, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingAdsl:password:
 	 *
 	 * Password used to authenticate with the ADSL service.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD,
-		 g_param_spec_string (NM_SETTING_ADSL_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_ADSL_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingAdsl:password-flags:
 	 *
 	 * Flags indicating how to handle the #NMSettingAdsl:password property.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_ADSL_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_ADSL_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingAdsl:protocol:
 	 *
 	 * ADSL connection protocol.  Can be "pppoa", "pppoe" or "ipoatm".
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PROTOCOL,
-		 g_param_spec_string (NM_SETTING_ADSL_PROTOCOL, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PROTOCOL] =
+	    g_param_spec_string (NM_SETTING_ADSL_PROTOCOL, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingAdsl:encapsulation:
 	 *
 	 * Encapsulation of ADSL connection.  Can be "vcmux" or "llc".
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_ENCAPSULATION,
-		 g_param_spec_string (NM_SETTING_ADSL_ENCAPSULATION, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ENCAPSULATION] =
+	    g_param_spec_string (NM_SETTING_ADSL_ENCAPSULATION, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingAdsl:vpi:
 	 *
 	 * VPI of ADSL connection
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_VPI,
-		 g_param_spec_uint (NM_SETTING_ADSL_VPI, "", "",
-		                    0, 65536, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_VPI] =
+	    g_param_spec_uint (NM_SETTING_ADSL_VPI, "", "",
+	                       0, 65536, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingAdsl:vci:
 	 *
 	 * VCI of ADSL connection
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_VCI,
-		 g_param_spec_uint (NM_SETTING_ADSL_VCI, "", "",
-		                    0, 65536, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_VCI] =
+	    g_param_spec_uint (NM_SETTING_ADSL_VCI, "", "",
+	                       0, 65536, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_ADSL);
 }
diff --git a/libnm-core/nm-setting-bluetooth.c b/libnm-core/nm-setting-bluetooth.c
index bbc6a83d..bc4f8bd0 100644
--- a/libnm-core/nm-setting-bluetooth.c
+++ b/libnm-core/nm-setting-bluetooth.c
@@ -22,11 +22,11 @@
 
 #include "nm-default.h"
 
-#include <string.h>
+#include "nm-setting-bluetooth.h"
+
 #include <net/ethernet.h>
 
 #include "nm-connection-private.h"
-#include "nm-setting-bluetooth.h"
 #include "nm-setting-cdma.h"
 #include "nm-setting-gsm.h"
 #include "nm-setting-private.h"
@@ -43,34 +43,23 @@
  * Point (NAP) profiles.
  **/
 
-G_DEFINE_TYPE (NMSettingBluetooth, nm_setting_bluetooth, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_BLUETOOTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_BDADDR,
+	PROP_TYPE,
+);
 
 typedef struct {
 	char *bdaddr;
 	char *type;
 } NMSettingBluetoothPrivate;
 
-enum {
-	PROP_0,
-	PROP_BDADDR,
-	PROP_TYPE,
+G_DEFINE_TYPE (NMSettingBluetooth, nm_setting_bluetooth, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_BLUETOOTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BLUETOOTH, NMSettingBluetoothPrivate))
 
-/**
- * nm_setting_bluetooth_new:
- *
- * Creates a new #NMSettingBluetooth object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingBluetooth object
- **/
-NMSetting *nm_setting_bluetooth_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_BLUETOOTH, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_bluetooth_get_connection_type:
@@ -224,20 +213,25 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static void
-nm_setting_bluetooth_init (NMSettingBluetooth *setting)
-{
-}
+/*****************************************************************************/
 
 static void
-finalize (GObject *object)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (object);
-
-	g_free (priv->bdaddr);
-	g_free (priv->type);
+	NMSettingBluetooth *setting = NM_SETTING_BLUETOOTH (object);
 
-	G_OBJECT_CLASS (nm_setting_bluetooth_parent_class)->finalize (object);
+	switch (prop_id) {
+	case PROP_BDADDR:
+		g_value_set_string (value, nm_setting_bluetooth_get_bdaddr (setting));
+		break;
+	case PROP_TYPE:
+		g_value_set_string (value, nm_setting_bluetooth_get_connection_type (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -261,23 +255,34 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_bluetooth_init (NMSettingBluetooth *setting)
 {
-	NMSettingBluetooth *setting = NM_SETTING_BLUETOOTH (object);
+}
 
-	switch (prop_id) {
-	case PROP_BDADDR:
-		g_value_set_string (value, nm_setting_bluetooth_get_bdaddr (setting));
-		break;
-	case PROP_TYPE:
-		g_value_set_string (value, nm_setting_bluetooth_get_connection_type (setting));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+/**
+ * nm_setting_bluetooth_new:
+ *
+ * Creates a new #NMSettingBluetooth object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingBluetooth object
+ **/
+NMSetting *nm_setting_bluetooth_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_BLUETOOTH, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE (object);
+
+	g_free (priv->bdaddr);
+	g_free (priv->type);
+
+	G_OBJECT_CLASS (nm_setting_bluetooth_parent_class)->finalize (object);
 }
 
 static void
@@ -289,8 +294,8 @@ nm_setting_bluetooth_class_init (NMSettingBluetoothClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingBluetoothPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify       = verify;
@@ -300,17 +305,15 @@ nm_setting_bluetooth_class_init (NMSettingBluetoothClass *klass)
 	 *
 	 * The Bluetooth address of the device.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_BDADDR,
-		 g_param_spec_string (NM_SETTING_BLUETOOTH_BDADDR, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_BDADDR] =
+	    g_param_spec_string (NM_SETTING_BLUETOOTH_BDADDR, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_BLUETOOTH_BDADDR),
+	                                    obj_properties[PROP_BDADDR],
 	                                    G_VARIANT_TYPE_BYTESTRING,
 	                                    _nm_utils_hwaddr_to_dbus,
 	                                    _nm_utils_hwaddr_from_dbus);
@@ -321,13 +324,14 @@ nm_setting_bluetooth_class_init (NMSettingBluetoothClass *klass)
 	 * Either "dun" for Dial-Up Networking connections or "panu" for Personal
 	 * Area Networking connections to devices supporting the NAP profile.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_TYPE,
-		 g_param_spec_string (NM_SETTING_BLUETOOTH_TYPE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TYPE] =
+	    g_param_spec_string (NM_SETTING_BLUETOOTH_TYPE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_BLUETOOTH,
 	                               NULL, properties_override);
diff --git a/libnm-core/nm-setting-bond.c b/libnm-core/nm-setting-bond.c
index 0c84b3a0..51ce2deb 100644
--- a/libnm-core/nm-setting-bond.c
+++ b/libnm-core/nm-setting-bond.c
@@ -21,13 +21,12 @@
 
 #include "nm-default.h"
 
-#include <string.h>
+#include "nm-setting-bond.h"
+
 #include <stdlib.h>
-#include <errno.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#include "nm-setting-bond.h"
 #include "nm-utils.h"
 #include "nm-utils-private.h"
 #include "nm-connection-private.h"
@@ -46,11 +45,9 @@
 
 /*****************************************************************************/
 
-enum {
-	PROP_0,
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingBond,
 	PROP_OPTIONS,
-	LAST_PROP
-};
+);
 
 typedef struct {
 	GHashTable *options;
@@ -178,19 +175,14 @@ nm_setting_bond_get_option (NMSettingBond *setting,
 static gboolean
 validate_int (const char *name, const char *value, const BondDefault *def)
 {
-	long num;
-	guint i;
+	guint64 num;
 
-	for (i = 0; i < strlen (value); i++) {
-		if (!g_ascii_isdigit (value[i]) && value[i] != '-')
-			return FALSE;
-	}
-
-	errno = 0;
-	num = strtol (value, NULL, 10);
-	if (errno)
+	if (!NM_STRCHAR_ALL (value, ch, g_ascii_isdigit (ch)))
 		return FALSE;
-	if (num < def->min || num > def->max)
+
+	num = _nm_utils_ascii_str_to_uint64 (value, 10, def->min, def->max, G_MAXUINT64);
+	if (   num == G_MAXUINT64
+	    && errno != 0)
 		return FALSE;
 
 	return TRUE;
@@ -365,7 +357,7 @@ nm_setting_bond_add_option (NMSettingBond *setting,
 		g_hash_table_remove (priv->options, NM_SETTING_BOND_OPTION_UPDELAY);
 	}
 
-	g_object_notify (G_OBJECT (setting), NM_SETTING_BOND_OPTIONS);
+	_notify (setting, PROP_OPTIONS);
 
 	return TRUE;
 }
@@ -398,7 +390,7 @@ nm_setting_bond_remove_option (NMSettingBond *setting,
 	nm_clear_g_free (&priv->options_idx_cache);
 	found = g_hash_table_remove (priv->options, name);
 	if (found)
-		g_object_notify (G_OBJECT (setting), NM_SETTING_BOND_OPTIONS);
+		_notify (setting, PROP_OPTIONS);
 	return found;
 }
 
@@ -811,15 +803,15 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 static gboolean
-options_hash_match (NMSettingBond *s_bond,
-                    GHashTable *options1,
-                    GHashTable *options2,
+options_equal_asym (NMSettingBond *s_bond,
+                    NMSettingBond *s_bond2,
                     NMSettingCompareFlags flags)
 {
+	GHashTable *options2 = NM_SETTING_BOND_GET_PRIVATE (s_bond2)->options;
 	GHashTableIter iter;
 	const char *key, *value, *value2;
 
-	g_hash_table_iter_init (&iter, options1);
+	g_hash_table_iter_init (&iter, NM_SETTING_BOND_GET_PRIVATE (s_bond)->options);
 	while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) {
 
 		if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)) {
@@ -841,15 +833,10 @@ options_hash_match (NMSettingBond *s_bond,
 				value2 = g_hash_table_lookup (options2, "num_grat_arp");
 		}
 
-		if (value2) {
-			if (nm_streq (value, value2))
-				continue;
-		} else {
-			if (nm_streq (value, nm_setting_bond_get_option_default (s_bond, key)))
-				continue;
-		}
-
-		return FALSE;
+		if (!value2)
+			value2 = nm_setting_bond_get_option_default (s_bond2, key);
+		if (!nm_streq (value, value2))
+			return FALSE;
 	}
 
 	return TRUE;
@@ -857,31 +844,32 @@ options_hash_match (NMSettingBond *s_bond,
 
 static gboolean
 options_equal (NMSettingBond *s_bond,
-               GHashTable *options1,
-               GHashTable *options2,
+               NMSettingBond *s_bond2,
                NMSettingCompareFlags flags)
 {
-	return    options_hash_match (s_bond, options1, options2, flags)
-	       && options_hash_match (s_bond, options2, options1, flags);
+	return    options_equal_asym (s_bond, s_bond2, flags)
+	       && options_equal_asym (s_bond2, s_bond, flags);
 }
 
-static gboolean
-compare_property (NMSetting *setting,
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
                   NMSetting *other,
-                  const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	NMSettingClass *setting_class;
-
-	if (nm_streq0 (prop_spec->name, NM_SETTING_BOND_OPTIONS)) {
-		return options_equal (NM_SETTING_BOND (setting),
-		                      NM_SETTING_BOND_GET_PRIVATE (setting)->options,
-		                      NM_SETTING_BOND_GET_PRIVATE (other)->options,
-		                      flags);
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_BOND_OPTIONS)) {
+		return (   !other
+		        || options_equal (NM_SETTING_BOND (setting),
+		                          NM_SETTING_BOND (other),
+		                          flags));
 	}
 
-	setting_class = NM_SETTING_CLASS (nm_setting_bond_parent_class);
-	return setting_class->compare_property (setting, other, prop_spec, flags);
+	return NM_SETTING_CLASS (nm_setting_bond_parent_class)->compare_property (sett_info,
+	                                                                          property_idx,
+	                                                                          setting,
+	                                                                          other,
+	                                                                          flags);
 }
 
 /*****************************************************************************/
@@ -966,8 +954,8 @@ nm_setting_bond_class_init (NMSettingBondClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingBondPrivate));
 
-	object_class->set_property     = set_property;
 	object_class->get_property     = get_property;
+	object_class->set_property     = set_property;
 	object_class->finalize         = finalize;
 
 	setting_class->verify           = verify;
@@ -987,17 +975,15 @@ nm_setting_bond_class_init (NMSettingBondClass *klass)
 	 * example: BONDING_OPTS="miimon=100 mode=broadcast"
 	 * ---end---
 	 */
-	 g_object_class_install_property
-		 (object_class, PROP_OPTIONS,
-		 g_param_spec_boxed (NM_SETTING_BOND_OPTIONS, "", "",
-		                     G_TYPE_HASH_TABLE,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_OPTIONS] =
+	    g_param_spec_boxed (NM_SETTING_BOND_OPTIONS, "", "",
+	                        G_TYPE_HASH_TABLE,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_BOND_OPTIONS),
+	                                    obj_properties[PROP_OPTIONS],
 	                                    G_VARIANT_TYPE ("a{ss}"),
 	                                    _nm_utils_strdict_to_dbus,
 	                                    _nm_utils_strdict_from_dbus);
@@ -1016,6 +1002,8 @@ nm_setting_bond_class_init (NMSettingBondClass *klass)
 	                                    _nm_setting_get_deprecated_virtual_interface_name,
 	                                    NULL);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_BOND,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-bridge-port.c b/libnm-core/nm-setting-bridge-port.c
index bc9c3e6f..614c883c 100644
--- a/libnm-core/nm-setting-bridge-port.c
+++ b/libnm-core/nm-setting-bridge-port.c
@@ -21,11 +21,11 @@
 
 #include "nm-default.h"
 
-#include <string.h>
+#include "nm-setting-bridge-port.h"
+
 #include <ctype.h>
 #include <stdlib.h>
 
-#include "nm-setting-bridge-port.h"
 #include "nm-utils.h"
 #include "nm-utils-private.h"
 #include "nm-connection-private.h"
@@ -40,9 +40,13 @@
  * optional properties that apply to bridge ports.
  **/
 
-G_DEFINE_TYPE (NMSettingBridgePort, nm_setting_bridge_port, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_BRIDGE_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PRIORITY,
+	PROP_PATH_COST,
+	PROP_HAIRPIN_MODE,
+);
 
 typedef struct {
 	guint16 priority;
@@ -50,13 +54,9 @@ typedef struct {
 	gboolean hairpin_mode;
 } NMSettingBridgePortPrivate;
 
-enum {
-	PROP_0,
-	PROP_PRIORITY,
-	PROP_PATH_COST,
-	PROP_HAIRPIN_MODE,
-	LAST_PROP
-};
+G_DEFINE_TYPE (NMSettingBridgePort, nm_setting_bridge_port, NM_TYPE_SETTING)
+
+#define NM_SETTING_BRIDGE_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE_PORT, NMSettingBridgePortPrivate))
 
 /*****************************************************************************/
 
@@ -141,39 +141,21 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 
 /*****************************************************************************/
 
-/**
- * nm_setting_bridge_port_new:
- *
- * Creates a new #NMSettingBridgePort object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingBridgePort object
- **/
-NMSetting *
-nm_setting_bridge_port_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_BRIDGE_PORT, NULL);
-}
-
 static void
-nm_setting_bridge_port_init (NMSettingBridgePort *setting)
-{
-}
-
-static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_PRIORITY:
-		priv->priority = g_value_get_uint (value);
+		g_value_set_uint (value, priv->priority);
 		break;
 	case PROP_PATH_COST:
-		priv->path_cost = g_value_get_uint (value);
+		g_value_set_uint (value, priv->path_cost);
 		break;
 	case PROP_HAIRPIN_MODE:
-		priv->hairpin_mode = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->hairpin_mode);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -182,20 +164,20 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_PRIORITY:
-		g_value_set_uint (value, priv->priority);
+		priv->priority = g_value_get_uint (value);
 		break;
 	case PROP_PATH_COST:
-		g_value_set_uint (value, priv->path_cost);
+		priv->path_cost = g_value_get_uint (value);
 		break;
 	case PROP_HAIRPIN_MODE:
-		g_value_set_boolean (value, priv->hairpin_mode);
+		priv->hairpin_mode = g_value_get_boolean (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -203,6 +185,26 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_bridge_port_init (NMSettingBridgePort *setting)
+{
+}
+
+/**
+ * nm_setting_bridge_port_new:
+ *
+ * Creates a new #NMSettingBridgePort object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingBridgePort object
+ **/
+NMSetting *
+nm_setting_bridge_port_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_BRIDGE_PORT, NULL);
+}
+
 static void
 nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass)
 {
@@ -211,8 +213,8 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingBridgePortPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 
 	setting_class->verify = verify;
 
@@ -229,14 +231,13 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass)
 	 * description: STP priority.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PRIORITY,
-		 g_param_spec_uint (NM_SETTING_BRIDGE_PORT_PRIORITY, "", "",
-		                    0, NM_BR_PORT_MAX_PRIORITY, NM_BR_PORT_DEF_PRIORITY,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIORITY] =
+	    g_param_spec_uint (NM_SETTING_BRIDGE_PORT_PRIORITY, "", "",
+	                       0, NM_BR_PORT_MAX_PRIORITY, NM_BR_PORT_DEF_PRIORITY,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingBridgePort:path-cost:
@@ -252,13 +253,12 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass)
 	 * description: STP cost.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PATH_COST,
-		 g_param_spec_uint (NM_SETTING_BRIDGE_PORT_PATH_COST, "", "",
-		                    0, NM_BR_PORT_MAX_PATH_COST, 100,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PATH_COST] =
+	    g_param_spec_uint (NM_SETTING_BRIDGE_PORT_PATH_COST, "", "",
+	                       0, NM_BR_PORT_MAX_PATH_COST, 100,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingBridgePort:hairpin-mode:
@@ -273,13 +273,14 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *klass)
 	 * description: Hairpin mode of the bridge port.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_HAIRPIN_MODE,
-		 g_param_spec_boolean (NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_HAIRPIN_MODE] =
+	    g_param_spec_boolean (NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_BRIDGE_PORT);
 }
diff --git a/libnm-core/nm-setting-bridge.c b/libnm-core/nm-setting-bridge.c
index 71fe2ed2..e418013c 100644
--- a/libnm-core/nm-setting-bridge.c
+++ b/libnm-core/nm-setting-bridge.c
@@ -21,11 +21,11 @@
 
 #include "nm-default.h"
 
-#include <string.h>
+#include "nm-setting-bridge.h"
+
 #include <ctype.h>
 #include <stdlib.h>
 
-#include "nm-setting-bridge.h"
 #include "nm-connection-private.h"
 #include "nm-utils.h"
 #include "nm-utils-private.h"
@@ -38,9 +38,19 @@
  * necessary for bridging connections.
  **/
 
-G_DEFINE_TYPE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_BRIDGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE, NMSettingBridgePrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_MAC_ADDRESS,
+	PROP_STP,
+	PROP_PRIORITY,
+	PROP_FORWARD_DELAY,
+	PROP_HELLO_TIME,
+	PROP_MAX_AGE,
+	PROP_AGEING_TIME,
+	PROP_GROUP_FORWARD_MASK,
+	PROP_MULTICAST_SNOOPING,
+);
 
 typedef struct {
 	char *   mac_address;
@@ -54,32 +64,11 @@ typedef struct {
 	gboolean multicast_snooping;
 } NMSettingBridgePrivate;
 
-enum {
-	PROP_0,
-	PROP_MAC_ADDRESS,
-	PROP_STP,
-	PROP_PRIORITY,
-	PROP_FORWARD_DELAY,
-	PROP_HELLO_TIME,
-	PROP_MAX_AGE,
-	PROP_AGEING_TIME,
-	PROP_GROUP_FORWARD_MASK,
-	PROP_MULTICAST_SNOOPING,
-	LAST_PROP
-};
+G_DEFINE_TYPE (NMSettingBridge, nm_setting_bridge, NM_TYPE_SETTING)
 
-/**
- * nm_setting_bridge_new:
- *
- * Creates a new #NMSettingBridge object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingBridge object
- **/
-NMSetting *
-nm_setting_bridge_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_BRIDGE, NULL);
-}
+#define NM_SETTING_BRIDGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_BRIDGE, NMSettingBridgePrivate))
+
+/*****************************************************************************/
 
 /**
  * nm_setting_bridge_get_mac_address:
@@ -211,7 +200,7 @@ nm_setting_bridge_get_multicast_snooping (NMSettingBridge *setting)
 	return NM_SETTING_BRIDGE_GET_PRIVATE (setting)->multicast_snooping;
 }
 
-static inline gboolean
+static gboolean
 check_range (guint32 val,
              guint32 min,
              guint32 max,
@@ -292,56 +281,42 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return _nm_connection_verify_required_interface_name (connection, error);
 }
 
-static void
-nm_setting_bridge_init (NMSettingBridge *setting)
-{
-}
-
-static void
-finalize (GObject *object)
-{
-	NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object);
-
-	g_free (priv->mac_address);
-
-	G_OBJECT_CLASS (nm_setting_bridge_parent_class)->finalize (object);
-}
+/*****************************************************************************/
 
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object);
+	NMSettingBridge *setting = NM_SETTING_BRIDGE (object);
 
 	switch (prop_id) {
 	case PROP_MAC_ADDRESS:
-		g_free (priv->mac_address);
-		priv->mac_address = _nm_utils_hwaddr_canonical_or_invalid (g_value_get_string (value),
-		                                                           ETH_ALEN);
+		g_value_set_string (value, nm_setting_bridge_get_mac_address (setting));
 		break;
 	case PROP_STP:
-		priv->stp = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->stp);
 		break;
 	case PROP_PRIORITY:
-		priv->priority = (guint16) g_value_get_uint (value);
+		g_value_set_uint (value, priv->priority);
 		break;
 	case PROP_FORWARD_DELAY:
-		priv->forward_delay = (guint16) g_value_get_uint (value);
+		g_value_set_uint (value, priv->forward_delay);
 		break;
 	case PROP_HELLO_TIME:
-		priv->hello_time = (guint16) g_value_get_uint (value);
+		g_value_set_uint (value, priv->hello_time);
 		break;
 	case PROP_MAX_AGE:
-		priv->max_age = (guint16) g_value_get_uint (value);
+		g_value_set_uint (value, priv->max_age);
 		break;
 	case PROP_AGEING_TIME:
-		priv->ageing_time = g_value_get_uint (value);
+		g_value_set_uint (value, priv->ageing_time);
 		break;
 	case PROP_GROUP_FORWARD_MASK:
-		priv->group_forward_mask = (guint16) g_value_get_uint (value);
+		g_value_set_uint (value, priv->group_forward_mask);
 		break;
 	case PROP_MULTICAST_SNOOPING:
-		priv->multicast_snooping = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->multicast_snooping);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -350,39 +325,40 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object);
-	NMSettingBridge *setting = NM_SETTING_BRIDGE (object);
 
 	switch (prop_id) {
 	case PROP_MAC_ADDRESS:
-		g_value_set_string (value, nm_setting_bridge_get_mac_address (setting));
+		g_free (priv->mac_address);
+		priv->mac_address = _nm_utils_hwaddr_canonical_or_invalid (g_value_get_string (value),
+		                                                           ETH_ALEN);
 		break;
 	case PROP_STP:
-		g_value_set_boolean (value, priv->stp);
+		priv->stp = g_value_get_boolean (value);
 		break;
 	case PROP_PRIORITY:
-		g_value_set_uint (value, priv->priority);
+		priv->priority = (guint16) g_value_get_uint (value);
 		break;
 	case PROP_FORWARD_DELAY:
-		g_value_set_uint (value, priv->forward_delay);
+		priv->forward_delay = (guint16) g_value_get_uint (value);
 		break;
 	case PROP_HELLO_TIME:
-		g_value_set_uint (value, priv->hello_time);
+		priv->hello_time = (guint16) g_value_get_uint (value);
 		break;
 	case PROP_MAX_AGE:
-		g_value_set_uint (value, priv->max_age);
+		priv->max_age = (guint16) g_value_get_uint (value);
 		break;
 	case PROP_AGEING_TIME:
-		g_value_set_uint (value, priv->ageing_time);
+		priv->ageing_time = g_value_get_uint (value);
 		break;
 	case PROP_GROUP_FORWARD_MASK:
-		g_value_set_uint (value, priv->group_forward_mask);
+		priv->group_forward_mask = (guint16) g_value_get_uint (value);
 		break;
 	case PROP_MULTICAST_SNOOPING:
-		g_value_set_boolean (value, priv->multicast_snooping);
+		priv->multicast_snooping = g_value_get_boolean (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -390,6 +366,36 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_bridge_init (NMSettingBridge *setting)
+{
+}
+
+/**
+ * nm_setting_bridge_new:
+ *
+ * Creates a new #NMSettingBridge object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingBridge object
+ **/
+NMSetting *
+nm_setting_bridge_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_BRIDGE, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE (object);
+
+	g_free (priv->mac_address);
+
+	G_OBJECT_CLASS (nm_setting_bridge_parent_class)->finalize (object);
+}
+
 static void
 nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 {
@@ -399,8 +405,8 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingBridgePrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -435,17 +441,15 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 	 *   BRIDGE_MACADDR for bridges is an NM extension.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MAC_ADDRESS,
-		 g_param_spec_string (NM_SETTING_BRIDGE_MAC_ADDRESS, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MAC_ADDRESS] =
+	    g_param_spec_string (NM_SETTING_BRIDGE_MAC_ADDRESS, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_BRIDGE_MAC_ADDRESS),
+	                                    obj_properties[PROP_MAC_ADDRESS],
 	                                    G_VARIANT_TYPE_BYTESTRING,
 	                                    _nm_utils_hwaddr_to_dbus,
 	                                    _nm_utils_hwaddr_from_dbus);
@@ -462,14 +466,13 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 	 * description: Span tree protocol participation.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_STP,
-		 g_param_spec_boolean (NM_SETTING_BRIDGE_STP, "", "",
-		                       TRUE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_STP] =
+	    g_param_spec_boolean (NM_SETTING_BRIDGE_STP, "", "",
+	                          TRUE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingBridge:priority:
@@ -486,14 +489,13 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 	 * description: STP priority.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PRIORITY,
-		 g_param_spec_uint (NM_SETTING_BRIDGE_PRIORITY, "", "",
-		                    0, G_MAXUINT16, 0x8000,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIORITY] =
+	    g_param_spec_uint (NM_SETTING_BRIDGE_PRIORITY, "", "",
+	                       0, G_MAXUINT16, 0x8000,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingBridge:forward-delay:
@@ -508,14 +510,13 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 	 * description: STP forwarding delay.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_FORWARD_DELAY,
-		 g_param_spec_uint (NM_SETTING_BRIDGE_FORWARD_DELAY, "", "",
-		                    0, NM_BR_MAX_FORWARD_DELAY, 15,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_FORWARD_DELAY] =
+	    g_param_spec_uint (NM_SETTING_BRIDGE_FORWARD_DELAY, "", "",
+	                       0, NM_BR_MAX_FORWARD_DELAY, 15,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingBridge:hello-time:
@@ -530,14 +531,13 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 	 * description: STP hello time.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_HELLO_TIME,
-		 g_param_spec_uint (NM_SETTING_BRIDGE_HELLO_TIME, "", "",
-		                    0, NM_BR_MAX_HELLO_TIME, 2,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_HELLO_TIME] =
+	    g_param_spec_uint (NM_SETTING_BRIDGE_HELLO_TIME, "", "",
+	                       0, NM_BR_MAX_HELLO_TIME, 2,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingBridge:max-age:
@@ -552,14 +552,13 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 	 * description: STP maximum message age.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MAX_AGE,
-		 g_param_spec_uint (NM_SETTING_BRIDGE_MAX_AGE, "", "",
-		                    0, NM_BR_MAX_MAX_AGE, 20,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MAX_AGE] =
+	    g_param_spec_uint (NM_SETTING_BRIDGE_MAX_AGE, "", "",
+	                       0, NM_BR_MAX_MAX_AGE, 20,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingBridge:ageing-time:
@@ -574,14 +573,13 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 	 * description: Ethernet MAC ageing time.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_AGEING_TIME,
-		 g_param_spec_uint (NM_SETTING_BRIDGE_AGEING_TIME, "", "",
-		                    NM_BR_MIN_AGEING_TIME, NM_BR_MAX_AGEING_TIME, 300,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_AGEING_TIME] =
+	    g_param_spec_uint (NM_SETTING_BRIDGE_AGEING_TIME, "", "",
+	                       NM_BR_MIN_AGEING_TIME, NM_BR_MAX_AGEING_TIME, 300,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingBridge:group-forward-mask:
@@ -595,14 +593,13 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_GROUP_FORWARD_MASK,
-		 g_param_spec_uint (NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, "", "",
-		                    0, 0xFFFF, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_GROUP_FORWARD_MASK] =
+	    g_param_spec_uint (NM_SETTING_BRIDGE_GROUP_FORWARD_MASK, "", "",
+	                       0, 0xFFFF, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingBridge:multicast-snooping:
@@ -622,14 +619,13 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 	 * description: IGMP snooping support.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MULTICAST_SNOOPING,
-		 g_param_spec_boolean (NM_SETTING_BRIDGE_MULTICAST_SNOOPING, "", "",
-		                       TRUE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MULTICAST_SNOOPING] =
+	    g_param_spec_boolean (NM_SETTING_BRIDGE_MULTICAST_SNOOPING, "", "",
+	                          TRUE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/* ---dbus---
 	 * property: interface-name
@@ -645,6 +641,8 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *klass)
 	                                    _nm_setting_get_deprecated_virtual_interface_name,
 	                                    NULL);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_BRIDGE,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-cdma.c b/libnm-core/nm-setting-cdma.c
index a5d5428d..b05daaff 100644
--- a/libnm-core/nm-setting-cdma.c
+++ b/libnm-core/nm-setting-cdma.c
@@ -21,9 +21,8 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-setting-cdma.h"
+
 #include "nm-utils.h"
 #include "nm-setting-private.h"
 #include "nm-core-enum-types.h"
@@ -37,9 +36,15 @@
  * networks, including those using CDMA2000/EVDO technology.
  */
 
-G_DEFINE_TYPE (NMSettingCdma, nm_setting_cdma, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CDMA, NMSettingCdmaPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_NUMBER,
+	PROP_USERNAME,
+	PROP_PASSWORD,
+	PROP_PASSWORD_FLAGS,
+	PROP_MTU,
+);
 
 typedef struct {
 	char *number; /* For dialing, duh */
@@ -49,29 +54,11 @@ typedef struct {
 	guint32 mtu;
 } NMSettingCdmaPrivate;
 
-enum {
-	PROP_0,
-	PROP_NUMBER,
-	PROP_USERNAME,
-	PROP_PASSWORD,
-	PROP_PASSWORD_FLAGS,
-	PROP_MTU,
+G_DEFINE_TYPE (NMSettingCdma, nm_setting_cdma, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CDMA, NMSettingCdmaPrivate))
 
-/**
- * nm_setting_cdma_new:
- *
- * Creates a new #NMSettingCdma object with default values.
- *
- * Returns: the new empty #NMSettingCdma object
- **/
-NMSetting *
-nm_setting_cdma_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_CDMA, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_cdma_get_number:
@@ -206,21 +193,34 @@ need_secrets (NMSetting *setting)
 	return secrets;
 }
 
-static void
-nm_setting_cdma_init (NMSettingCdma *setting)
-{
-}
+/*****************************************************************************/
 
 static void
-finalize (GObject *object)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (object);
-
-	g_free (priv->number);
-	g_free (priv->username);
-	g_free (priv->password);
+	NMSettingCdma *setting = NM_SETTING_CDMA (object);
 
-	G_OBJECT_CLASS (nm_setting_cdma_parent_class)->finalize (object);
+	switch (prop_id) {
+	case PROP_NUMBER:
+		g_value_set_string (value, nm_setting_cdma_get_number (setting));
+		break;
+	case PROP_USERNAME:
+		g_value_set_string (value, nm_setting_cdma_get_username (setting));
+		break;
+	case PROP_PASSWORD:
+		g_value_set_string (value, nm_setting_cdma_get_password (setting));
+		break;
+	case PROP_PASSWORD_FLAGS:
+		g_value_set_flags (value, nm_setting_cdma_get_password_flags (setting));
+		break;
+	case PROP_MTU:
+		g_value_set_uint (value, nm_setting_cdma_get_mtu (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -254,32 +254,36 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_cdma_init (NMSettingCdma *setting)
 {
-	NMSettingCdma *setting = NM_SETTING_CDMA (object);
+}
 
-	switch (prop_id) {
-	case PROP_NUMBER:
-		g_value_set_string (value, nm_setting_cdma_get_number (setting));
-		break;
-	case PROP_USERNAME:
-		g_value_set_string (value, nm_setting_cdma_get_username (setting));
-		break;
-	case PROP_PASSWORD:
-		g_value_set_string (value, nm_setting_cdma_get_password (setting));
-		break;
-	case PROP_PASSWORD_FLAGS:
-		g_value_set_flags (value, nm_setting_cdma_get_password_flags (setting));
-		break;
-	case PROP_MTU:
-		g_value_set_uint (value, nm_setting_cdma_get_mtu (setting));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+/**
+ * nm_setting_cdma_new:
+ *
+ * Creates a new #NMSettingCdma object with default values.
+ *
+ * Returns: the new empty #NMSettingCdma object
+ **/
+NMSetting *
+nm_setting_cdma_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_CDMA, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE (object);
+
+	g_free (priv->number);
+	g_free (priv->username);
+	g_free (priv->password);
+
+	G_OBJECT_CLASS (nm_setting_cdma_parent_class)->finalize (object);
 }
 
 static void
@@ -290,8 +294,8 @@ nm_setting_cdma_class_init (NMSettingCdmaClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingCdmaPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify         = verify;
@@ -305,12 +309,11 @@ nm_setting_cdma_class_init (NMSettingCdmaClass *klass)
 	 * broadband network, if any.  If not specified, the default number (#777)
 	 * is used when required.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NUMBER,
-		 g_param_spec_string (NM_SETTING_CDMA_NUMBER, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NUMBER] =
+	    g_param_spec_string (NM_SETTING_CDMA_NUMBER, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingCdma:username:
@@ -319,12 +322,11 @@ nm_setting_cdma_class_init (NMSettingCdmaClass *klass)
 	 * providers do not require a username, or accept any username.  But if a
 	 * username is required, it is specified here.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_USERNAME,
-		 g_param_spec_string (NM_SETTING_CDMA_USERNAME, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_USERNAME] =
+	    g_param_spec_string (NM_SETTING_CDMA_USERNAME, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingCdma:password:
@@ -333,26 +335,24 @@ nm_setting_cdma_class_init (NMSettingCdmaClass *klass)
 	 * providers do not require a password, or accept any password.  But if a
 	 * password is required, it is specified here.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD,
-		 g_param_spec_string (NM_SETTING_CDMA_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_CDMA_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingCdma:password-flags:
 	 *
 	 * Flags indicating how to handle the #NMSettingCdma:password property.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_CDMA_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_CDMA_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingCdma:mtu:
@@ -362,14 +362,15 @@ nm_setting_cdma_class_init (NMSettingCdmaClass *klass)
 	 *
 	 * Since: 1.8
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_MTU,
-		 g_param_spec_uint (NM_SETTING_CDMA_MTU, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MTU] =
+	    g_param_spec_uint (NM_SETTING_CDMA_MTU, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_CDMA);
 }
diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c
index 44021b9c..e8d008e3 100644
--- a/libnm-core/nm-setting-connection.c
+++ b/libnm-core/nm-setting-connection.c
@@ -24,8 +24,6 @@
 
 #include "nm-setting-connection.h"
 
-#include <string.h>
-
 #include "nm-utils.h"
 #include "nm-utils-private.h"
 #include "nm-core-enum-types.h"
@@ -45,9 +43,7 @@
  * a #NMSettingConnection setting.
  **/
 
-G_DEFINE_TYPE (NMSettingConnection, nm_setting_connection, NM_TYPE_SETTING)
-
-#define NM_SETTING_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionPrivate))
+/*****************************************************************************/
 
 typedef enum {
 	PERM_TYPE_USER = 0,
@@ -58,6 +54,32 @@ typedef struct {
 	char *item;
 } Permission;
 
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingConnection,
+	PROP_ID,
+	PROP_UUID,
+	PROP_INTERFACE_NAME,
+	PROP_TYPE,
+	PROP_PERMISSIONS,
+	PROP_AUTOCONNECT,
+	PROP_AUTOCONNECT_PRIORITY,
+	PROP_AUTOCONNECT_RETRIES,
+	PROP_MULTI_CONNECT,
+	PROP_TIMESTAMP,
+	PROP_READ_ONLY,
+	PROP_ZONE,
+	PROP_MASTER,
+	PROP_SLAVE_TYPE,
+	PROP_AUTOCONNECT_SLAVES,
+	PROP_SECONDARIES,
+	PROP_GATEWAY_PING_TIMEOUT,
+	PROP_METERED,
+	PROP_LLDP,
+	PROP_MDNS,
+	PROP_LLMNR,
+	PROP_STABLE_ID,
+	PROP_AUTH_RETRIES,
+);
+
 typedef struct {
 	char *id;
 	char *uuid;
@@ -84,34 +106,9 @@ typedef struct {
 	int llmnr;
 } NMSettingConnectionPrivate;
 
-enum {
-	PROP_0,
-	PROP_ID,
-	PROP_UUID,
-	PROP_INTERFACE_NAME,
-	PROP_TYPE,
-	PROP_PERMISSIONS,
-	PROP_AUTOCONNECT,
-	PROP_AUTOCONNECT_PRIORITY,
-	PROP_AUTOCONNECT_RETRIES,
-	PROP_MULTI_CONNECT,
-	PROP_TIMESTAMP,
-	PROP_READ_ONLY,
-	PROP_ZONE,
-	PROP_MASTER,
-	PROP_SLAVE_TYPE,
-	PROP_AUTOCONNECT_SLAVES,
-	PROP_SECONDARIES,
-	PROP_GATEWAY_PING_TIMEOUT,
-	PROP_METERED,
-	PROP_LLDP,
-	PROP_MDNS,
-	PROP_LLMNR,
-	PROP_STABLE_ID,
-	PROP_AUTH_RETRIES,
+G_DEFINE_TYPE (NMSettingConnection, nm_setting_connection, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_CONNECTION, NMSettingConnectionPrivate))
 
 /*****************************************************************************/
 
@@ -197,18 +194,6 @@ permission_free (Permission *p)
 /*****************************************************************************/
 
 /**
- * nm_setting_connection_new:
- *
- * Creates a new #NMSettingConnection object with default values.
- *
- * Returns: the new empty #NMSettingConnection object
- **/
-NMSetting *nm_setting_connection_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_CONNECTION, NULL);
-}
-
-/**
  * nm_setting_connection_get_id:
  * @setting: the #NMSettingConnection
  *
@@ -312,7 +297,7 @@ nm_setting_connection_get_num_permissions (NMSettingConnection *setting)
  * @setting: the #NMSettingConnection
  * @idx: the zero-based index of the permissions entry
  * @out_ptype: on return, the permission type (at this time, always "user")
- * @out_pitem: on return, the permission item (formatted accoring to @ptype, see
+ * @out_pitem: on return, the permission item (formatted according to @ptype, see
  * #NMSettingConnection:permissions for more detail
  * @out_detail: on return, the permission detail (at this time, always %NULL)
  *
@@ -431,7 +416,7 @@ nm_setting_connection_add_permission (NMSettingConnection *setting,
 	p = permission_new (pitem);
 	g_return_val_if_fail (p != NULL, FALSE);
 	priv->permissions = g_slist_append (priv->permissions, p);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_PERMISSIONS);
+	_notify (setting, PROP_PERMISSIONS);
 
 	return TRUE;
 }
@@ -458,7 +443,7 @@ nm_setting_connection_remove_permission (NMSettingConnection *setting,
 
 	permission_free ((Permission *) iter->data);
 	priv->permissions = g_slist_delete_link (priv->permissions, iter);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_PERMISSIONS);
+	_notify (setting, PROP_PERMISSIONS);
 }
 
 /**
@@ -498,7 +483,7 @@ nm_setting_connection_remove_permission_by_value (NMSettingConnection *setting,
 		if (strcmp (pitem, p->item) == 0) {
 			permission_free ((Permission *) iter->data);
 			priv->permissions = g_slist_delete_link (priv->permissions, iter);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_PERMISSIONS);
+			_notify (setting, PROP_PERMISSIONS);
 			return TRUE;
 		}
 	}
@@ -748,7 +733,7 @@ nm_setting_connection_get_secondary (NMSettingConnection *setting, guint32 idx)
  * @setting: the #NMSettingConnection
  * @sec_uuid: the secondary connection UUID to add
  *
- * Adds a new secondary connetion UUID to the setting.
+ * Adds a new secondary connection UUID to the setting.
  *
  * Returns: %TRUE if the secondary connection UUID was added; %FALSE if the UUID
  * was already present
@@ -771,7 +756,7 @@ nm_setting_connection_add_secondary (NMSettingConnection *setting,
 	}
 
 	priv->secondaries = g_slist_append (priv->secondaries, g_strdup (sec_uuid));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_SECONDARIES);
+	_notify (setting, PROP_SECONDARIES);
 	return TRUE;
 }
 
@@ -796,7 +781,7 @@ nm_setting_connection_remove_secondary (NMSettingConnection *setting, guint32 id
 
 	g_free (elt->data);
 	priv->secondaries = g_slist_delete_link (priv->secondaries, elt);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_SECONDARIES);
+	_notify (setting, PROP_SECONDARIES);
 }
 
 /**
@@ -823,7 +808,7 @@ nm_setting_connection_remove_secondary_by_value (NMSettingConnection *setting,
 	for (iter = priv->secondaries; iter; iter = g_slist_next (iter)) {
 		if (!strcmp (sec_uuid, (char *) iter->data)) {
 			priv->secondaries = g_slist_delete_link (priv->secondaries, iter);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_CONNECTION_SECONDARIES);
+			_notify (setting, PROP_SECONDARIES);
 			return TRUE;
 		}
 	}
@@ -1281,51 +1266,26 @@ nm_setting_connection_no_interface_name (NMSetting *setting,
 	return TRUE;
 }
 
-static gboolean
-compare_property (NMSetting *setting,
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
                   NMSetting *other,
-                  const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	/* Handle ignore ID */
-	if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID)
-	    && g_strcmp0 (prop_spec->name, NM_SETTING_CONNECTION_ID) == 0)
-		return TRUE;
-
-	/* Handle ignore timestamp */
-	if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)
-	    && g_strcmp0 (prop_spec->name, NM_SETTING_CONNECTION_TIMESTAMP) == 0)
-		return TRUE;
-
-	return NM_SETTING_CLASS (nm_setting_connection_parent_class)->compare_property (setting, other, prop_spec, flags);
-}
-
-static void
-nm_setting_connection_init (NMSettingConnection *setting)
-{
-	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
-
-	priv->mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT;
-	priv->llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
-}
-
-static void
-finalize (GObject *object)
-{
-	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (object);
-
-	g_free (priv->id);
-	g_free (priv->uuid);
-	g_free (priv->stable_id);
-	g_free (priv->interface_name);
-	g_free (priv->type);
-	g_free (priv->zone);
-	g_free (priv->master);
-	g_free (priv->slave_type);
-	g_slist_free_full (priv->permissions, (GDestroyNotify) permission_free);
-	g_slist_free_full (priv->secondaries, g_free);
-
-	G_OBJECT_CLASS (nm_setting_connection_parent_class)->finalize (object);
+	if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_ID)
+	    && nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_CONNECTION_ID))
+		return NM_TERNARY_DEFAULT;
+
+	if (   NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)
+	    && nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_CONNECTION_TIMESTAMP))
+		return NM_TERNARY_DEFAULT;
+
+	return NM_SETTING_CLASS (nm_setting_connection_parent_class)->compare_property (sett_info,
+	                                                                                property_idx,
+	                                                                                setting,
+	                                                                                other,
+	                                                                                flags);
 }
 
 static GSList *
@@ -1348,91 +1308,98 @@ perm_strv_to_permlist (char **strv)
 	return list;
 }
 
+static char **
+perm_permlist_to_strv (GSList *permlist)
+{
+	GPtrArray *strings;
+	GSList *iter;
+
+	strings = g_ptr_array_new ();
+	for (iter = permlist; iter; iter = g_slist_next (iter))
+		g_ptr_array_add (strings, permission_to_string ((Permission *) iter->data));
+	g_ptr_array_add (strings, NULL);
+
+	return (char **) g_ptr_array_free (strings, FALSE);
+}
+
+/*****************************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (object);
+	NMSettingConnection *setting = NM_SETTING_CONNECTION (object);
+	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_ID:
-		g_free (priv->id);
-		priv->id = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_connection_get_id (setting));
 		break;
 	case PROP_UUID:
-		g_free (priv->uuid);
-		priv->uuid = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_connection_get_uuid (setting));
 		break;
 	case PROP_STABLE_ID:
-		g_free (priv->stable_id);
-		priv->stable_id = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_connection_get_stable_id (setting));
 		break;
 	case PROP_INTERFACE_NAME:
-		g_free (priv->interface_name);
-		priv->interface_name = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_connection_get_interface_name (setting));
 		break;
 	case PROP_TYPE:
-		g_free (priv->type);
-		priv->type = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_connection_get_connection_type (setting));
 		break;
 	case PROP_PERMISSIONS:
-		g_slist_free_full (priv->permissions, (GDestroyNotify) permission_free);
-		priv->permissions = perm_strv_to_permlist (g_value_get_boxed (value));
+		g_value_take_boxed (value, perm_permlist_to_strv (priv->permissions));
 		break;
 	case PROP_AUTOCONNECT:
-		priv->autoconnect = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_connection_get_autoconnect (setting));
 		break;
 	case PROP_AUTOCONNECT_PRIORITY:
-		priv->autoconnect_priority = g_value_get_int (value);
+		g_value_set_int (value, nm_setting_connection_get_autoconnect_priority (setting));
 		break;
 	case PROP_AUTOCONNECT_RETRIES:
-		priv->autoconnect_retries = g_value_get_int (value);
+		g_value_set_int (value, nm_setting_connection_get_autoconnect_retries (setting));
 		break;
 	case PROP_MULTI_CONNECT:
-		priv->multi_connect = g_value_get_int (value);
+		g_value_set_int (value, priv->multi_connect);
 		break;
 	case PROP_TIMESTAMP:
-		priv->timestamp = g_value_get_uint64 (value);
+		g_value_set_uint64 (value, nm_setting_connection_get_timestamp (setting));
 		break;
 	case PROP_READ_ONLY:
-		priv->read_only = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_connection_get_read_only (setting));
 		break;
 	case PROP_ZONE:
-		g_free (priv->zone);
-		priv->zone = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_connection_get_zone (setting));
 		break;
 	case PROP_MASTER:
-		g_free (priv->master);
-		priv->master = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_connection_get_master (setting));
 		break;
 	case PROP_SLAVE_TYPE:
-		g_free (priv->slave_type);
-		priv->slave_type = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_connection_get_slave_type (setting));
 		break;
 	case PROP_AUTOCONNECT_SLAVES:
-		priv->autoconnect_slaves = g_value_get_enum (value);
+		g_value_set_enum (value, nm_setting_connection_get_autoconnect_slaves (setting));
 		break;
 	case PROP_SECONDARIES:
-		g_slist_free_full (priv->secondaries, g_free);
-		priv->secondaries = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
+		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->secondaries, TRUE));
 		break;
 	case PROP_GATEWAY_PING_TIMEOUT:
-		priv->gateway_ping_timeout = g_value_get_uint (value);
+		g_value_set_uint (value, priv->gateway_ping_timeout);
 		break;
 	case PROP_METERED:
-		priv->metered = g_value_get_enum (value);
+		g_value_set_enum (value, priv->metered);
 		break;
 	case PROP_LLDP:
-		priv->lldp = g_value_get_int (value);
+		g_value_set_int (value, priv->lldp);
 		break;
 	case PROP_AUTH_RETRIES:
-		priv->auth_retries = g_value_get_int (value);
+		g_value_set_int (value, priv->auth_retries);
 		break;
 	case PROP_MDNS:
-		priv->mdns = g_value_get_int (value);
+		g_value_set_int (value, priv->mdns);
 		break;
 	case PROP_LLMNR:
-		priv->llmnr = g_value_get_int (value);
+		g_value_set_int (value, priv->llmnr);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1440,96 +1407,91 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
-static char **
-perm_permlist_to_strv (GSList *permlist)
-{
-	GPtrArray *strings;
-	GSList *iter;
-
-	strings = g_ptr_array_new ();
-	for (iter = permlist; iter; iter = g_slist_next (iter))
-		g_ptr_array_add (strings, permission_to_string ((Permission *) iter->data));
-	g_ptr_array_add (strings, NULL);
-
-	return (char **) g_ptr_array_free (strings, FALSE);
-}
-
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
-	NMSettingConnection *setting = NM_SETTING_CONNECTION (object);
-	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_ID:
-		g_value_set_string (value, nm_setting_connection_get_id (setting));
+		g_free (priv->id);
+		priv->id = g_value_dup_string (value);
 		break;
 	case PROP_UUID:
-		g_value_set_string (value, nm_setting_connection_get_uuid (setting));
+		g_free (priv->uuid);
+		priv->uuid = g_value_dup_string (value);
 		break;
 	case PROP_STABLE_ID:
-		g_value_set_string (value, nm_setting_connection_get_stable_id (setting));
+		g_free (priv->stable_id);
+		priv->stable_id = g_value_dup_string (value);
 		break;
 	case PROP_INTERFACE_NAME:
-		g_value_set_string (value, nm_setting_connection_get_interface_name (setting));
+		g_free (priv->interface_name);
+		priv->interface_name = g_value_dup_string (value);
 		break;
 	case PROP_TYPE:
-		g_value_set_string (value, nm_setting_connection_get_connection_type (setting));
+		g_free (priv->type);
+		priv->type = g_value_dup_string (value);
 		break;
 	case PROP_PERMISSIONS:
-		g_value_take_boxed (value, perm_permlist_to_strv (priv->permissions));
+		g_slist_free_full (priv->permissions, (GDestroyNotify) permission_free);
+		priv->permissions = perm_strv_to_permlist (g_value_get_boxed (value));
 		break;
 	case PROP_AUTOCONNECT:
-		g_value_set_boolean (value, nm_setting_connection_get_autoconnect (setting));
+		priv->autoconnect = g_value_get_boolean (value);
 		break;
 	case PROP_AUTOCONNECT_PRIORITY:
-		g_value_set_int (value, nm_setting_connection_get_autoconnect_priority (setting));
+		priv->autoconnect_priority = g_value_get_int (value);
 		break;
 	case PROP_AUTOCONNECT_RETRIES:
-		g_value_set_int (value, nm_setting_connection_get_autoconnect_retries (setting));
+		priv->autoconnect_retries = g_value_get_int (value);
 		break;
 	case PROP_MULTI_CONNECT:
-		g_value_set_int (value, priv->multi_connect);
+		priv->multi_connect = g_value_get_int (value);
 		break;
 	case PROP_TIMESTAMP:
-		g_value_set_uint64 (value, nm_setting_connection_get_timestamp (setting));
+		priv->timestamp = g_value_get_uint64 (value);
 		break;
 	case PROP_READ_ONLY:
-		g_value_set_boolean (value, nm_setting_connection_get_read_only (setting));
+		priv->read_only = g_value_get_boolean (value);
 		break;
 	case PROP_ZONE:
-		g_value_set_string (value, nm_setting_connection_get_zone (setting));
+		g_free (priv->zone);
+		priv->zone = g_value_dup_string (value);
 		break;
 	case PROP_MASTER:
-		g_value_set_string (value, nm_setting_connection_get_master (setting));
+		g_free (priv->master);
+		priv->master = g_value_dup_string (value);
 		break;
 	case PROP_SLAVE_TYPE:
-		g_value_set_string (value, nm_setting_connection_get_slave_type (setting));
+		g_free (priv->slave_type);
+		priv->slave_type = g_value_dup_string (value);
 		break;
 	case PROP_AUTOCONNECT_SLAVES:
-		g_value_set_enum (value, nm_setting_connection_get_autoconnect_slaves (setting));
+		priv->autoconnect_slaves = g_value_get_enum (value);
 		break;
 	case PROP_SECONDARIES:
-		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->secondaries, TRUE));
+		g_slist_free_full (priv->secondaries, g_free);
+		priv->secondaries = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
 		break;
 	case PROP_GATEWAY_PING_TIMEOUT:
-		g_value_set_uint (value, priv->gateway_ping_timeout);
+		priv->gateway_ping_timeout = g_value_get_uint (value);
 		break;
 	case PROP_METERED:
-		g_value_set_enum (value, priv->metered);
+		priv->metered = g_value_get_enum (value);
 		break;
 	case PROP_LLDP:
-		g_value_set_int (value, priv->lldp);
+		priv->lldp = g_value_get_int (value);
 		break;
 	case PROP_AUTH_RETRIES:
-		g_value_set_int (value, priv->auth_retries);
+		priv->auth_retries = g_value_get_int (value);
 		break;
 	case PROP_MDNS:
-		g_value_set_int (value, priv->mdns);
+		priv->mdns = g_value_get_int (value);
 		break;
 	case PROP_LLMNR:
-		g_value_set_int (value, priv->llmnr);
+		priv->llmnr = g_value_get_int (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1537,6 +1499,48 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_connection_init (NMSettingConnection *setting)
+{
+	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (setting);
+
+	priv->mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT;
+	priv->llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
+}
+
+/**
+ * nm_setting_connection_new:
+ *
+ * Creates a new #NMSettingConnection object with default values.
+ *
+ * Returns: the new empty #NMSettingConnection object
+ **/
+NMSetting *nm_setting_connection_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_CONNECTION, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE (object);
+
+	g_free (priv->id);
+	g_free (priv->uuid);
+	g_free (priv->stable_id);
+	g_free (priv->interface_name);
+	g_free (priv->type);
+	g_free (priv->zone);
+	g_free (priv->master);
+	g_free (priv->slave_type);
+	g_slist_free_full (priv->permissions, (GDestroyNotify) permission_free);
+	g_slist_free_full (priv->secondaries, g_free);
+
+	G_OBJECT_CLASS (nm_setting_connection_parent_class)->finalize (object);
+}
+
 static void
 nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 {
@@ -1546,8 +1550,8 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingConnectionPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify           = verify;
@@ -1565,13 +1569,12 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * description: User friendly name for the connection profile.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_ID,
-		 g_param_spec_string (NM_SETTING_CONNECTION_ID, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_FUZZY_IGNORE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ID] =
+	    g_param_spec_string (NM_SETTING_CONNECTION_ID, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_FUZZY_IGNORE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:uuid:
@@ -1596,13 +1599,12 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 *   creates the UUID itself (by hashing the filename).
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_UUID,
-		 g_param_spec_string (NM_SETTING_CONNECTION_UUID, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_FUZZY_IGNORE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_UUID] =
+	    g_param_spec_string (NM_SETTING_CONNECTION_UUID, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_FUZZY_IGNORE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:stable-id:
@@ -1629,7 +1631,7 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * at runtime. Currently supported are "${CONNECTION}", "${DEVICE}", "${MAC}",
 	 * "${BOOT}", "${RANDOM}".
 	 * These effectively create unique IDs per-connection, per-device, per-boot,
-	 * or every time. Note that "${DEVICE}" corresponds the the interface name of the
+	 * or every time. Note that "${DEVICE}" corresponds to the interface name of the
 	 * device and "${MAC}" is the permanent MAC address of the device.
 	 * Any unrecognized patterns following '$' are treated verbatim, however
 	 * are reserved for future use. You are thus advised to avoid '$' or
@@ -1650,13 +1652,12 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * description: Token to generate stable IDs.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_STABLE_ID,
-		 g_param_spec_string (NM_SETTING_CONNECTION_STABLE_ID, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_FUZZY_IGNORE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_STABLE_ID] =
+	    g_param_spec_string (NM_SETTING_CONNECTION_STABLE_ID, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_FUZZY_IGNORE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:interface-name:
@@ -1681,17 +1682,15 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 *   can be required for some connection types.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_INTERFACE_NAME,
-		 g_param_spec_string (NM_SETTING_CONNECTION_INTERFACE_NAME, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_INTERFACE_NAME] =
+	    g_param_spec_string (NM_SETTING_CONNECTION_INTERFACE_NAME, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_override (properties_override,
-	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                 NM_SETTING_CONNECTION_INTERFACE_NAME),
+	                                   obj_properties[PROP_INTERFACE_NAME],
 	                                   G_VARIANT_TYPE_STRING,
 	                                   NULL,
 	                                   nm_setting_connection_set_interface_name,
@@ -1715,13 +1714,12 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * example: TYPE=Ethernet; TYPE=Bond; TYPE=Bridge; DEVICETYPE=TeamPort
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_TYPE,
-		 g_param_spec_string (NM_SETTING_CONNECTION_TYPE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TYPE] =
+	    g_param_spec_string (NM_SETTING_CONNECTION_TYPE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:permissions:
@@ -1748,12 +1746,11 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * example: USERS="joe bob"
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PERMISSIONS,
-		 g_param_spec_boxed (NM_SETTING_CONNECTION_PERMISSIONS, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PERMISSIONS] =
+	    g_param_spec_boxed (NM_SETTING_CONNECTION_PERMISSIONS, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:autoconnect:
@@ -1774,14 +1771,13 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * description: Whether the connection should be autoconnected (not only while booting).
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_AUTOCONNECT,
-		 g_param_spec_boolean (NM_SETTING_CONNECTION_AUTOCONNECT, "", "",
-		                       TRUE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_FUZZY_IGNORE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_AUTOCONNECT] =
+	    g_param_spec_boolean (NM_SETTING_CONNECTION_AUTOCONNECT, "", "",
+	                          TRUE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_FUZZY_IGNORE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:autoconnect-priority:
@@ -1800,8 +1796,7 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * example: AUTOCONNECT_PRIORITY=20
 	 * ---end---
 	 */
-	g_object_class_install_property
-	    (object_class, PROP_AUTOCONNECT_PRIORITY,
+	obj_properties[PROP_AUTOCONNECT_PRIORITY] =
 	     g_param_spec_int (NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, "", "",
 	                       NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN,
 	                       NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX,
@@ -1809,7 +1804,7 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	                       G_PARAM_READWRITE |
 	                       G_PARAM_CONSTRUCT |
 	                       NM_SETTING_PARAM_FUZZY_IGNORE |
-	                       G_PARAM_STATIC_STRINGS));
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:autoconnect-retries:
@@ -1829,14 +1824,13 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * example: AUTOCONNECT_RETRIES=1
 	 * ---end---
 	 */
-	g_object_class_install_property
-	    (object_class, PROP_AUTOCONNECT_RETRIES,
+	obj_properties[PROP_AUTOCONNECT_RETRIES] =
 	     g_param_spec_int (NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES, "", "",
 	                       -1, G_MAXINT32, -1,
 	                       G_PARAM_READWRITE |
 	                       G_PARAM_CONSTRUCT |
 	                       NM_SETTING_PARAM_FUZZY_IGNORE |
-	                       G_PARAM_STATIC_STRINGS));
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:multi-connect:
@@ -1854,13 +1848,12 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * example: ZONE=3
 	 * ---end---
 	 */
-	g_object_class_install_property
-	    (object_class, PROP_MULTI_CONNECT,
+	obj_properties[PROP_MULTI_CONNECT] =
 	     g_param_spec_int (NM_SETTING_CONNECTION_MULTI_CONNECT, "", "",
 	                       G_MININT32, G_MAXINT32, NM_CONNECTION_MULTI_CONNECT_DEFAULT,
 	                       G_PARAM_READWRITE |
 	                       NM_SETTING_PARAM_FUZZY_IGNORE |
-	                       G_PARAM_STATIC_STRINGS));
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:timestamp:
@@ -1873,14 +1866,13 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * timestamp. The property is only meant for reading (changes to this
 	 * property will not be preserved).
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_TIMESTAMP,
-		 g_param_spec_uint64 (NM_SETTING_CONNECTION_TIMESTAMP, "", "",
-		                      0, G_MAXUINT64, 0,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      NM_SETTING_PARAM_FUZZY_IGNORE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TIMESTAMP] =
+	    g_param_spec_uint64 (NM_SETTING_CONNECTION_TIMESTAMP, "", "",
+	                         0, G_MAXUINT64, 0,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_FUZZY_IGNORE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:read-only:
@@ -1889,14 +1881,13 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * service's D-Bus interface with the right privileges, or %TRUE if the
 	 * connection is read-only and cannot be modified.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_READ_ONLY,
-		 g_param_spec_boolean (NM_SETTING_CONNECTION_READ_ONLY, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_FUZZY_IGNORE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_READ_ONLY] =
+	    g_param_spec_boolean (NM_SETTING_CONNECTION_READ_ONLY, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_FUZZY_IGNORE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:zone:
@@ -1917,15 +1908,14 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * example: ZONE=Work
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_ZONE,
-		 g_param_spec_string (NM_SETTING_CONNECTION_ZONE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      NM_SETTING_PARAM_FUZZY_IGNORE |
-		                      NM_SETTING_PARAM_REAPPLY_IMMEDIATELY |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ZONE] =
+	    g_param_spec_string (NM_SETTING_CONNECTION_ZONE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_FUZZY_IGNORE |
+	                         NM_SETTING_PARAM_REAPPLY_IMMEDIATELY |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:master:
@@ -1941,14 +1931,13 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 *   for compatibility with legacy tooling.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MASTER,
-		 g_param_spec_string (NM_SETTING_CONNECTION_MASTER, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_FUZZY_IGNORE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MASTER] =
+	    g_param_spec_string (NM_SETTING_CONNECTION_MASTER, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_FUZZY_IGNORE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:slave-type:
@@ -1967,14 +1956,13 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 *   and BRIDGE_UUID for bridging.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_SLAVE_TYPE,
-		 g_param_spec_string (NM_SETTING_CONNECTION_SLAVE_TYPE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_FUZZY_IGNORE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SLAVE_TYPE] =
+	    g_param_spec_string (NM_SETTING_CONNECTION_SLAVE_TYPE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_FUZZY_IGNORE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:autoconnect-slaves:
@@ -1999,15 +1987,14 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 *   when this connection is activated.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_AUTOCONNECT_SLAVES,
-		 g_param_spec_enum (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, "", "",
-		                    NM_TYPE_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
-		                    NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_AUTOCONNECT_SLAVES] =
+	    g_param_spec_enum (NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES, "", "",
+	                       NM_TYPE_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
+	                       NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:secondaries:
@@ -2023,13 +2010,12 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 *   together with this connection.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_SECONDARIES,
-		 g_param_spec_boxed (NM_SETTING_CONNECTION_SECONDARIES, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_FUZZY_IGNORE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SECONDARIES] =
+	    g_param_spec_boxed (NM_SETTING_CONNECTION_SECONDARIES, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_FUZZY_IGNORE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:gateway-ping-timeout:
@@ -2046,13 +2032,12 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * example: GATEWAY_PING_TIMEOUT=5
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_GATEWAY_PING_TIMEOUT,
-		 g_param_spec_uint (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, "", "",
-		                    0, 600, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_GATEWAY_PING_TIMEOUT] =
+	    g_param_spec_uint (NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT, "", "",
+	                       0, 600, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:metered:
@@ -2072,14 +2057,13 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * example: CONNECTION_METERED=yes
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_METERED,
-		 g_param_spec_enum (NM_SETTING_CONNECTION_METERED, "", "",
-		                    NM_TYPE_METERED,
-		                    NM_METERED_UNKNOWN,
-		                    G_PARAM_READWRITE |
-		                    NM_SETTING_PARAM_REAPPLY_IMMEDIATELY |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_METERED] =
+	    g_param_spec_enum (NM_SETTING_CONNECTION_METERED, "", "",
+	                       NM_TYPE_METERED,
+	                       NM_METERED_UNKNOWN,
+	                       G_PARAM_READWRITE |
+	                       NM_SETTING_PARAM_REAPPLY_IMMEDIATELY |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:lldp:
@@ -2097,14 +2081,13 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * example: LLDP=no
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_LLDP,
-		 g_param_spec_int (NM_SETTING_CONNECTION_LLDP, "", "",
-		                   G_MININT32, G_MAXINT32, NM_SETTING_CONNECTION_LLDP_DEFAULT,
-		                   NM_SETTING_PARAM_FUZZY_IGNORE |
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_CONSTRUCT |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LLDP] =
+	    g_param_spec_int (NM_SETTING_CONNECTION_LLDP, "", "",
+	                      G_MININT32, G_MAXINT32, NM_SETTING_CONNECTION_LLDP_DEFAULT,
+	                      NM_SETTING_PARAM_FUZZY_IGNORE |
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_CONSTRUCT |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:auth-retries:
@@ -2124,14 +2107,13 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * description: Number of retries for authentication.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_AUTH_RETRIES,
-		 g_param_spec_int (NM_SETTING_CONNECTION_AUTH_RETRIES, "", "",
-		                   -1, G_MAXINT32, -1,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_CONSTRUCT |
-		                   NM_SETTING_PARAM_FUZZY_IGNORE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_AUTH_RETRIES] =
+	    g_param_spec_int (NM_SETTING_CONNECTION_AUTH_RETRIES, "", "",
+	                      -1, G_MAXINT32, -1,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_CONSTRUCT |
+	                      NM_SETTING_PARAM_FUZZY_IGNORE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingConnection:mdns:
@@ -2156,13 +2138,12 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * example: 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));
+	obj_properties[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);
 
 	/**
 	 * NMSettingConnection:llmnr:
@@ -2190,13 +2171,14 @@ nm_setting_connection_class_init (NMSettingConnectionClass *klass)
 	 * example: LLMNR=yes
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_LLMNR,
-		 g_param_spec_int (NM_SETTING_CONNECTION_LLMNR, "", "",
-		                   G_MININT32, G_MAXINT32,
-		                   NM_SETTING_CONNECTION_LLMNR_DEFAULT,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LLMNR] =
+	    g_param_spec_int (NM_SETTING_CONNECTION_LLMNR, "", "",
+	                      G_MININT32, G_MAXINT32,
+	                      NM_SETTING_CONNECTION_LLMNR_DEFAULT,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_CONNECTION,
 	                               NULL, properties_override);
diff --git a/libnm-core/nm-setting-dcb.c b/libnm-core/nm-setting-dcb.c
index dabcbbaf..419d9825 100644
--- a/libnm-core/nm-setting-dcb.c
+++ b/libnm-core/nm-setting-dcb.c
@@ -21,9 +21,8 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-setting-dcb.h"
+
 #include "nm-utils.h"
 #include "nm-utils-private.h"
 #include "nm-setting-private.h"
@@ -40,9 +39,30 @@
  * of storage technologies like Fibre Channel over Ethernet (FCoE) and iSCSI.
  **/
 
-G_DEFINE_TYPE (NMSettingDcb, nm_setting_dcb, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_DCB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_DCB, NMSettingDcbPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingDcb,
+
+	PROP_APP_FCOE_FLAGS,
+	PROP_APP_FCOE_PRIORITY,
+	PROP_APP_FCOE_MODE,
+
+	PROP_APP_ISCSI_FLAGS,
+	PROP_APP_ISCSI_PRIORITY,
+
+	PROP_APP_FIP_FLAGS,
+	PROP_APP_FIP_PRIORITY,
+
+	PROP_PFC_FLAGS,
+	PROP_PRIORITY_FLOW_CONTROL,
+
+	PROP_PRIORITY_GROUP_FLAGS,
+	PROP_PRIORITY_GROUP_ID,
+	PROP_PRIORITY_GROUP_BANDWIDTH,
+	PROP_PRIORITY_BANDWIDTH,
+	PROP_PRIORITY_STRICT_BANDWIDTH,
+	PROP_PRIORITY_TRAFFIC_CLASS,
+);
 
 typedef struct {
 	NMSettingDcbFlags app_fcoe_flags;
@@ -68,43 +88,11 @@ typedef struct {
 	guint             priority_traffic_class[8];
 } NMSettingDcbPrivate;
 
-enum {
-	PROP_0,
-	PROP_APP_FCOE_FLAGS,
-	PROP_APP_FCOE_PRIORITY,
-	PROP_APP_FCOE_MODE,
-
-	PROP_APP_ISCSI_FLAGS,
-	PROP_APP_ISCSI_PRIORITY,
-
-	PROP_APP_FIP_FLAGS,
-	PROP_APP_FIP_PRIORITY,
-
-	PROP_PFC_FLAGS,
-	PROP_PFC,
-
-	PROP_PRIORITY_GROUP_FLAGS,
-	PROP_PRIORITY_GROUP_ID,
-	PROP_PRIORITY_GROUP_BANDWIDTH,
-	PROP_PRIORITY_BANDWIDTH,
-	PROP_PRIORITY_STRICT,
-	PROP_PRIORITY_TRAFFIC_CLASS,
+G_DEFINE_TYPE (NMSettingDcb, nm_setting_dcb, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_DCB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_DCB, NMSettingDcbPrivate))
 
-/**
- * nm_setting_dcb_new:
- *
- * Creates a new #NMSettingDcb object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingDcb object
- **/
-NMSetting *
-nm_setting_dcb_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_DCB, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_dcb_get_app_fcoe_flags:
@@ -258,7 +246,7 @@ nm_setting_dcb_set_priority_flow_control (NMSettingDcb *setting,
 	priv = NM_SETTING_DCB_GET_PRIVATE (setting);
 	if (priv->pfc[user_priority] != uint_enabled) {
 		priv->pfc[user_priority] = uint_enabled;
-		g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_FLOW_CONTROL);
+		_notify (setting, PROP_PRIORITY_FLOW_CONTROL);
 	}
 }
 
@@ -318,7 +306,7 @@ nm_setting_dcb_set_priority_group_id (NMSettingDcb *setting,
 	priv = NM_SETTING_DCB_GET_PRIVATE (setting);
 	if (priv->priority_group_id[user_priority] != group_id) {
 		priv->priority_group_id[user_priority] = group_id;
-		g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_GROUP_ID);
+		_notify (setting, PROP_PRIORITY_GROUP_ID);
 	}
 }
 
@@ -363,7 +351,7 @@ nm_setting_dcb_set_priority_group_bandwidth (NMSettingDcb *setting,
 	priv = NM_SETTING_DCB_GET_PRIVATE (setting);
 	if (priv->priority_group_bandwidth[group_id] != bandwidth_percent) {
 		priv->priority_group_bandwidth[group_id] = bandwidth_percent;
-		g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH);
+		_notify (setting, PROP_PRIORITY_GROUP_BANDWIDTH);
 	}
 }
 
@@ -409,7 +397,7 @@ nm_setting_dcb_set_priority_bandwidth (NMSettingDcb *setting,
 	priv = NM_SETTING_DCB_GET_PRIVATE (setting);
 	if (priv->priority_bandwidth[user_priority] != bandwidth_percent) {
 		priv->priority_bandwidth[user_priority] = bandwidth_percent;
-		g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_BANDWIDTH);
+		_notify (setting, PROP_PRIORITY_BANDWIDTH);
 	}
 }
 
@@ -455,7 +443,7 @@ nm_setting_dcb_set_priority_strict_bandwidth (NMSettingDcb *setting,
 	priv = NM_SETTING_DCB_GET_PRIVATE (setting);
 	if (priv->priority_strict[user_priority] != uint_strict) {
 		priv->priority_strict[user_priority] = uint_strict;
-		g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH);
+		_notify (setting, PROP_PRIORITY_STRICT_BANDWIDTH);
 	}
 }
 
@@ -500,7 +488,7 @@ nm_setting_dcb_set_priority_traffic_class (NMSettingDcb *setting,
 	priv = NM_SETTING_DCB_GET_PRIVATE (setting);
 	if (priv->priority_traffic_class[user_priority] != traffic_class) {
 		priv->priority_traffic_class[user_priority] = traffic_class;
-		g_object_notify (G_OBJECT (setting), NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS);
+		_notify (setting, PROP_PRIORITY_TRAFFIC_CLASS);
 	}
 }
 
@@ -724,14 +712,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 
 /*****************************************************************************/
 
-static void
-nm_setting_dcb_init (NMSettingDcb *setting)
-{
-}
-
 G_STATIC_ASSERT (sizeof (guint) == sizeof (gboolean));
 
-static inline void
+static void
 set_array_from_gvalue (const GValue *v, uint *a, size_t len)
 {
 	GArray *src = g_value_get_boxed (v);
@@ -746,7 +729,7 @@ set_array_from_gvalue (const GValue *v, uint *a, size_t len)
 }
 #define SET_ARRAY_FROM_GVALUE(v, a)  set_array_from_gvalue (v, a, G_N_ELEMENTS (a))
 
-static inline void
+static void
 set_gvalue_from_array (GValue *v, uint *a, size_t len)
 {
 	GArray *src = g_array_sized_new (FALSE, TRUE, sizeof (guint), len);
@@ -776,58 +759,60 @@ _nm_setting_dcb_uint_array_from_dbus (GVariant *dbus_value,
 	set_gvalue_from_array (prop_value, (guint *) array, length);
 }
 
+/*****************************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (object);
+	NMSettingDcb *setting = NM_SETTING_DCB (object);
+	NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_APP_FCOE_FLAGS:
-		priv->app_fcoe_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->app_fcoe_flags);
 		break;
 	case PROP_APP_FCOE_PRIORITY:
-		priv->app_fcoe_priority = g_value_get_int (value);
+		g_value_set_int (value, priv->app_fcoe_priority);
 		break;
 	case PROP_APP_FCOE_MODE:
-		g_free (priv->app_fcoe_mode);
-		priv->app_fcoe_mode = g_value_dup_string (value);
+		g_value_set_string (value, priv->app_fcoe_mode);
 		break;
 	case PROP_APP_ISCSI_FLAGS:
-		priv->app_iscsi_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->app_iscsi_flags);
 		break;
 	case PROP_APP_ISCSI_PRIORITY:
-		priv->app_iscsi_priority = g_value_get_int (value);
+		g_value_set_int (value, priv->app_iscsi_priority);
 		break;
 	case PROP_APP_FIP_FLAGS:
-		priv->app_fip_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->app_fip_flags);
 		break;
 	case PROP_APP_FIP_PRIORITY:
-		priv->app_fip_priority = g_value_get_int (value);
+		g_value_set_int (value, priv->app_fip_priority);
 		break;
 	case PROP_PFC_FLAGS:
-		priv->pfc_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->pfc_flags);
 		break;
-	case PROP_PFC:
-		SET_ARRAY_FROM_GVALUE (value, priv->pfc);
+	case PROP_PRIORITY_FLOW_CONTROL:
+		SET_GVALUE_FROM_ARRAY (value, priv->pfc);
 		break;
 	case PROP_PRIORITY_GROUP_FLAGS:
-		priv->priority_group_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->priority_group_flags);
 		break;
 	case PROP_PRIORITY_GROUP_ID:
-		SET_ARRAY_FROM_GVALUE (value, priv->priority_group_id);
+		SET_GVALUE_FROM_ARRAY (value, priv->priority_group_id);
 		break;
 	case PROP_PRIORITY_GROUP_BANDWIDTH:
-		SET_ARRAY_FROM_GVALUE (value, priv->priority_group_bandwidth);
+		SET_GVALUE_FROM_ARRAY (value, priv->priority_group_bandwidth);
 		break;
 	case PROP_PRIORITY_BANDWIDTH:
-		SET_ARRAY_FROM_GVALUE (value, priv->priority_bandwidth);
+		SET_GVALUE_FROM_ARRAY (value, priv->priority_bandwidth);
 		break;
-	case PROP_PRIORITY_STRICT:
-		SET_ARRAY_FROM_GVALUE (value, priv->priority_strict);
+	case PROP_PRIORITY_STRICT_BANDWIDTH:
+		SET_GVALUE_FROM_ARRAY (value, priv->priority_strict);
 		break;
 	case PROP_PRIORITY_TRAFFIC_CLASS:
-		SET_ARRAY_FROM_GVALUE (value, priv->priority_traffic_class);
+		SET_GVALUE_FROM_ARRAY (value, priv->priority_traffic_class);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -836,57 +821,57 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
-	NMSettingDcb *setting = NM_SETTING_DCB (object);
-	NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (setting);
+	NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_APP_FCOE_FLAGS:
-		g_value_set_flags (value, priv->app_fcoe_flags);
+		priv->app_fcoe_flags = g_value_get_flags (value);
 		break;
 	case PROP_APP_FCOE_PRIORITY:
-		g_value_set_int (value, priv->app_fcoe_priority);
+		priv->app_fcoe_priority = g_value_get_int (value);
 		break;
 	case PROP_APP_FCOE_MODE:
-		g_value_set_string (value, priv->app_fcoe_mode);
+		g_free (priv->app_fcoe_mode);
+		priv->app_fcoe_mode = g_value_dup_string (value);
 		break;
 	case PROP_APP_ISCSI_FLAGS:
-		g_value_set_flags (value, priv->app_iscsi_flags);
+		priv->app_iscsi_flags = g_value_get_flags (value);
 		break;
 	case PROP_APP_ISCSI_PRIORITY:
-		g_value_set_int (value, priv->app_iscsi_priority);
+		priv->app_iscsi_priority = g_value_get_int (value);
 		break;
 	case PROP_APP_FIP_FLAGS:
-		g_value_set_flags (value, priv->app_fip_flags);
+		priv->app_fip_flags = g_value_get_flags (value);
 		break;
 	case PROP_APP_FIP_PRIORITY:
-		g_value_set_int (value, priv->app_fip_priority);
+		priv->app_fip_priority = g_value_get_int (value);
 		break;
 	case PROP_PFC_FLAGS:
-		g_value_set_flags (value, priv->pfc_flags);
+		priv->pfc_flags = g_value_get_flags (value);
 		break;
-	case PROP_PFC:
-		SET_GVALUE_FROM_ARRAY (value, priv->pfc);
+	case PROP_PRIORITY_FLOW_CONTROL:
+		SET_ARRAY_FROM_GVALUE (value, priv->pfc);
 		break;
 	case PROP_PRIORITY_GROUP_FLAGS:
-		g_value_set_flags (value, priv->priority_group_flags);
+		priv->priority_group_flags = g_value_get_flags (value);
 		break;
 	case PROP_PRIORITY_GROUP_ID:
-		SET_GVALUE_FROM_ARRAY (value, priv->priority_group_id);
+		SET_ARRAY_FROM_GVALUE (value, priv->priority_group_id);
 		break;
 	case PROP_PRIORITY_GROUP_BANDWIDTH:
-		SET_GVALUE_FROM_ARRAY (value, priv->priority_group_bandwidth);
+		SET_ARRAY_FROM_GVALUE (value, priv->priority_group_bandwidth);
 		break;
 	case PROP_PRIORITY_BANDWIDTH:
-		SET_GVALUE_FROM_ARRAY (value, priv->priority_bandwidth);
+		SET_ARRAY_FROM_GVALUE (value, priv->priority_bandwidth);
 		break;
-	case PROP_PRIORITY_STRICT:
-		SET_GVALUE_FROM_ARRAY (value, priv->priority_strict);
+	case PROP_PRIORITY_STRICT_BANDWIDTH:
+		SET_ARRAY_FROM_GVALUE (value, priv->priority_strict);
 		break;
 	case PROP_PRIORITY_TRAFFIC_CLASS:
-		SET_GVALUE_FROM_ARRAY (value, priv->priority_traffic_class);
+		SET_ARRAY_FROM_GVALUE (value, priv->priority_traffic_class);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -894,6 +879,26 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_dcb_init (NMSettingDcb *setting)
+{
+}
+
+/**
+ * nm_setting_dcb_new:
+ *
+ * Creates a new #NMSettingDcb object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingDcb object
+ **/
+NMSetting *
+nm_setting_dcb_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_DCB, NULL);
+}
+
 static void
 finalize (GObject *object)
 {
@@ -913,9 +918,9 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingDcbPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
-	object_class->finalize = finalize;
+	object_class->set_property = set_property;
+	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
 
@@ -934,13 +939,12 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * example: DCB_APP_FCOE_ENABLE=yes DCB_APP_FCOE_ADVERTISE=yes
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_APP_FCOE_FLAGS,
-		 g_param_spec_flags (NM_SETTING_DCB_APP_FCOE_FLAGS, "", "",
-		                     NM_TYPE_SETTING_DCB_FLAGS,
-		                     NM_SETTING_DCB_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_APP_FCOE_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_DCB_APP_FCOE_FLAGS, "", "",
+	                        NM_TYPE_SETTING_DCB_FLAGS,
+	                        NM_SETTING_DCB_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingDcb:app-fcoe-priority:
@@ -956,13 +960,12 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * description: Priority of FCoE frames.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_APP_FCOE_PRIORITY,
-		 g_param_spec_int (NM_SETTING_DCB_APP_FCOE_PRIORITY, "", "",
-		                   -1, 7, -1,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_CONSTRUCT |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_APP_FCOE_PRIORITY] =
+	    g_param_spec_int (NM_SETTING_DCB_APP_FCOE_PRIORITY, "", "",
+	                      -1, 7, -1,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_CONSTRUCT |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingDcb:app-fcoe-mode:
@@ -978,13 +981,12 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * description: FCoE controller mode.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_APP_FCOE_MODE,
-		 g_param_spec_string (NM_SETTING_DCB_APP_FCOE_MODE, "", "",
-		                      NM_SETTING_DCB_FCOE_MODE_FABRIC,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_APP_FCOE_MODE] =
+	    g_param_spec_string (NM_SETTING_DCB_APP_FCOE_MODE, "", "",
+	                         NM_SETTING_DCB_FCOE_MODE_FABRIC,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingDcb:app-iscsi-flags:
@@ -1000,13 +1002,12 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * description: iSCSI flags.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_APP_ISCSI_FLAGS,
-		 g_param_spec_flags (NM_SETTING_DCB_APP_ISCSI_FLAGS, "", "",
-		                     NM_TYPE_SETTING_DCB_FLAGS,
-		                     NM_SETTING_DCB_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_APP_ISCSI_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_DCB_APP_ISCSI_FLAGS, "", "",
+	                        NM_TYPE_SETTING_DCB_FLAGS,
+	                        NM_SETTING_DCB_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingDcb:app-iscsi-priority:
@@ -1022,13 +1023,12 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * description: Priority of iSCSI frames.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_APP_ISCSI_PRIORITY,
-		 g_param_spec_int (NM_SETTING_DCB_APP_ISCSI_PRIORITY, "", "",
-		                   -1, 7, -1,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_CONSTRUCT |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_APP_ISCSI_PRIORITY] =
+	    g_param_spec_int (NM_SETTING_DCB_APP_ISCSI_PRIORITY, "", "",
+	                      -1, 7, -1,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_CONSTRUCT |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingDcb:app-fip-flags:
@@ -1044,13 +1044,12 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * description: FIP flags.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_APP_FIP_FLAGS,
-		 g_param_spec_flags (NM_SETTING_DCB_APP_FIP_FLAGS, "", "",
-		                     NM_TYPE_SETTING_DCB_FLAGS,
-		                     NM_SETTING_DCB_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_APP_FIP_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_DCB_APP_FIP_FLAGS, "", "",
+	                        NM_TYPE_SETTING_DCB_FLAGS,
+	                        NM_SETTING_DCB_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingDcb:app-fip-priority:
@@ -1066,13 +1065,12 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * description: Priority of FIP frames.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_APP_FIP_PRIORITY,
-		 g_param_spec_int (NM_SETTING_DCB_APP_FIP_PRIORITY, "", "",
-		                   -1, 7, -1,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_CONSTRUCT |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_APP_FIP_PRIORITY] =
+	    g_param_spec_int (NM_SETTING_DCB_APP_FIP_PRIORITY, "", "",
+	                      -1, 7, -1,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_CONSTRUCT |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingDcb:priority-flow-control-flags:
@@ -1088,13 +1086,12 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * description: Priority flow control flags.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PFC_FLAGS,
-		 g_param_spec_flags (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, "", "",
-		                     NM_TYPE_SETTING_DCB_FLAGS,
-		                     NM_SETTING_DCB_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PFC_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS, "", "",
+	                        NM_TYPE_SETTING_DCB_FLAGS,
+	                        NM_SETTING_DCB_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingDcb:priority-flow-control: (type GArray(gboolean))
@@ -1111,19 +1108,17 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * example: DCB_PFC_UP=01101110
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PFC,
-		 g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL, "", "",
-		                     G_TYPE_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIORITY_FLOW_CONTROL] =
+	    g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_FLOW_CONTROL, "", "",
+	                        G_TYPE_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_DCB_PRIORITY_FLOW_CONTROL),
-	                                    G_VARIANT_TYPE ("au"),
-	                                    _nm_setting_dcb_uint_array_to_dbus,
-	                                    _nm_setting_dcb_uint_array_from_dbus);
+	                                   obj_properties[PROP_PRIORITY_FLOW_CONTROL],
+	                                   G_VARIANT_TYPE ("au"),
+	                                   _nm_setting_dcb_uint_array_to_dbus,
+	                                   _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
 	 * NMSettingDcb:priority-group-flags:
@@ -1139,13 +1134,12 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * description: Priority groups flags.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PRIORITY_GROUP_FLAGS,
-		 g_param_spec_flags (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, "", "",
-		                     NM_TYPE_SETTING_DCB_FLAGS,
-		                     NM_SETTING_DCB_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIORITY_GROUP_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_DCB_PRIORITY_GROUP_FLAGS, "", "",
+	                        NM_TYPE_SETTING_DCB_FLAGS,
+	                        NM_SETTING_DCB_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingDcb:priority-group-id: (type GArray(guint))
@@ -1162,19 +1156,17 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * example: DCB_PG_ID=1205f173
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PRIORITY_GROUP_ID,
-		 g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_GROUP_ID, "", "",
-		                     G_TYPE_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIORITY_GROUP_ID] =
+	    g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_GROUP_ID, "", "",
+	                        G_TYPE_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_DCB_PRIORITY_GROUP_ID),
-	                                    G_VARIANT_TYPE ("au"),
-	                                    _nm_setting_dcb_uint_array_to_dbus,
-	                                    _nm_setting_dcb_uint_array_from_dbus);
+	                                   obj_properties[PROP_PRIORITY_GROUP_ID],
+	                                   G_VARIANT_TYPE ("au"),
+	                                   _nm_setting_dcb_uint_array_to_dbus,
+	                                   _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
 	 * NMSettingDcb:priority-group-bandwidth: (type GArray(guint))
@@ -1191,19 +1183,17 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * example: DCB_PG_PCT=10,5,10,15,10,10,10,30
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PRIORITY_GROUP_BANDWIDTH,
-		 g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH, "", "",
-		                     G_TYPE_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIORITY_GROUP_BANDWIDTH] =
+	    g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH, "", "",
+	                        G_TYPE_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_DCB_PRIORITY_GROUP_BANDWIDTH),
-	                                    G_VARIANT_TYPE ("au"),
-	                                    _nm_setting_dcb_uint_array_to_dbus,
-	                                    _nm_setting_dcb_uint_array_from_dbus);
+	                                   obj_properties[PROP_PRIORITY_GROUP_BANDWIDTH],
+	                                   G_VARIANT_TYPE ("au"),
+	                                   _nm_setting_dcb_uint_array_to_dbus,
+	                                   _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
 	 * NMSettingDcb:priority-bandwidth: (type GArray(guint))
@@ -1222,19 +1212,17 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * example: DCB_PG_UPPCT=7,13,10,10,15,15,10,20
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PRIORITY_BANDWIDTH,
-		 g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_BANDWIDTH, "", "",
-		                     G_TYPE_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIORITY_BANDWIDTH] =
+	    g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_BANDWIDTH, "", "",
+	                        G_TYPE_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_DCB_PRIORITY_BANDWIDTH),
-	                                    G_VARIANT_TYPE ("au"),
-	                                    _nm_setting_dcb_uint_array_to_dbus,
-	                                    _nm_setting_dcb_uint_array_from_dbus);
+	                                   obj_properties[PROP_PRIORITY_BANDWIDTH],
+	                                   G_VARIANT_TYPE ("au"),
+	                                   _nm_setting_dcb_uint_array_to_dbus,
+	                                   _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
 	 * NMSettingDcb:priority-strict-bandwidth: (type GArray(gboolean))
@@ -1251,19 +1239,17 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	 * example: DCB_PG_STRICT=01101110
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PRIORITY_STRICT,
-		 g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH, "", "",
-		                     G_TYPE_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIORITY_STRICT_BANDWIDTH] =
+	    g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH, "", "",
+	                        G_TYPE_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_DCB_PRIORITY_STRICT_BANDWIDTH),
-	                                    G_VARIANT_TYPE ("au"),
-	                                    _nm_setting_dcb_uint_array_to_dbus,
-	                                    _nm_setting_dcb_uint_array_from_dbus);
+	                                   obj_properties[PROP_PRIORITY_STRICT_BANDWIDTH],
+	                                   G_VARIANT_TYPE ("au"),
+	                                   _nm_setting_dcb_uint_array_to_dbus,
+	                                   _nm_setting_dcb_uint_array_from_dbus);
 
 	/**
 	 * NMSettingDcb:priority-traffic-class: (type GArray(guint))
@@ -1275,24 +1261,24 @@ nm_setting_dcb_class_init (NMSettingDcbClass *klass)
 	/* ---ifcfg-rh---
 	 * property: priority-traffic-class
 	 * variable: DCB_PG_UP2TC
-	 * description: Priority values. String of eight trafic class values (0 - 7).
+	 * description: Priority values. String of eight traffic class values (0 - 7).
 	 * example: DCB_PG_UP2TC=01623701
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PRIORITY_TRAFFIC_CLASS,
-		 g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS, "", "",
-		                     G_TYPE_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIORITY_TRAFFIC_CLASS] =
+	    g_param_spec_boxed (NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS, "", "",
+	                        G_TYPE_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_DCB_PRIORITY_TRAFFIC_CLASS),
+	                                    obj_properties[PROP_PRIORITY_TRAFFIC_CLASS],
 	                                    G_VARIANT_TYPE ("au"),
 	                                    _nm_setting_dcb_uint_array_to_dbus,
 	                                    _nm_setting_dcb_uint_array_from_dbus);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_DCB,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-dummy.c b/libnm-core/nm-setting-dummy.c
index 9ae05ec4..bd85a74e 100644
--- a/libnm-core/nm-setting-dummy.c
+++ b/libnm-core/nm-setting-dummy.c
@@ -34,22 +34,11 @@
  * necessary for connection to dummy devices
  **/
 
+/*****************************************************************************/
+
 G_DEFINE_TYPE (NMSettingDummy, nm_setting_dummy, NM_TYPE_SETTING)
 
-/**
- * nm_setting_dummy_new:
- *
- * Creates a new #NMSettingDummy object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingDummy object
- *
- * Since: 1.8
- **/
-NMSetting *
-nm_setting_dummy_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_DUMMY, NULL);
-}
+/*****************************************************************************/
 
 static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
@@ -60,11 +49,28 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
+/*****************************************************************************/
+
 static void
 nm_setting_dummy_init (NMSettingDummy *setting)
 {
 }
 
+/**
+ * nm_setting_dummy_new:
+ *
+ * Creates a new #NMSettingDummy object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingDummy object
+ *
+ * Since: 1.8
+ **/
+NMSetting *
+nm_setting_dummy_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_DUMMY, NULL);
+}
+
 static void
 nm_setting_dummy_class_init (NMSettingDummyClass *klass)
 {
diff --git a/libnm-core/nm-setting-generic.c b/libnm-core/nm-setting-generic.c
index 44402e39..fe5ce3ca 100644
--- a/libnm-core/nm-setting-generic.c
+++ b/libnm-core/nm-setting-generic.c
@@ -22,6 +22,7 @@
 #include "nm-default.h"
 
 #include "nm-setting-generic.h"
+
 #include "nm-setting-private.h"
 
 /**
@@ -36,16 +37,23 @@
  * the "connection type" setting on #NMConnections for generic devices.
  **/
 
-G_DEFINE_TYPE (NMSettingGeneric, nm_setting_generic, NM_TYPE_SETTING)
-
-#define NM_SETTING_GENERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GENERIC, NMSettingGenericPrivate))
+/*****************************************************************************/
 
 typedef struct {
 	int dummy;
 } NMSettingGenericPrivate;
 
+G_DEFINE_TYPE (NMSettingGeneric, nm_setting_generic, NM_TYPE_SETTING)
+
+#define NM_SETTING_GENERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GENERIC, NMSettingGenericPrivate))
+
 /*****************************************************************************/
 
+static void
+nm_setting_generic_init (NMSettingGeneric *setting)
+{
+}
+
 /**
  * nm_setting_generic_new:
  *
@@ -60,11 +68,6 @@ nm_setting_generic_new (void)
 }
 
 static void
-nm_setting_generic_init (NMSettingGeneric *setting)
-{
-}
-
-static void
 nm_setting_generic_class_init (NMSettingGenericClass *klass)
 {
 	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
diff --git a/libnm-core/nm-setting-gsm.c b/libnm-core/nm-setting-gsm.c
index b31da689..e6784e42 100644
--- a/libnm-core/nm-setting-gsm.c
+++ b/libnm-core/nm-setting-gsm.c
@@ -22,9 +22,8 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-setting-gsm.h"
+
 #include "nm-utils.h"
 #include "nm-setting-private.h"
 #include "nm-core-enum-types.h"
@@ -38,9 +37,23 @@
  * networks, including those using GPRS/EDGE and UMTS/HSPA technology.
  */
 
-G_DEFINE_TYPE (NMSettingGsm, nm_setting_gsm, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GSM, NMSettingGsmPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_NUMBER,
+	PROP_USERNAME,
+	PROP_PASSWORD,
+	PROP_PASSWORD_FLAGS,
+	PROP_APN,
+	PROP_NETWORK_ID,
+	PROP_PIN,
+	PROP_PIN_FLAGS,
+	PROP_HOME_ONLY,
+	PROP_DEVICE_ID,
+	PROP_SIM_ID,
+	PROP_SIM_OPERATOR_ID,
+	PROP_MTU,
+);
 
 typedef struct {
 	char *number; /* For dialing, duh */
@@ -63,43 +76,19 @@ typedef struct {
 	guint32 mtu;
 } NMSettingGsmPrivate;
 
-enum {
-	PROP_0,
-	PROP_NUMBER,
-	PROP_USERNAME,
-	PROP_PASSWORD,
-	PROP_PASSWORD_FLAGS,
-	PROP_APN,
-	PROP_NETWORK_ID,
-	PROP_PIN,
-	PROP_PIN_FLAGS,
-	PROP_HOME_ONLY,
-	PROP_DEVICE_ID,
-	PROP_SIM_ID,
-	PROP_SIM_OPERATOR_ID,
-	PROP_MTU,
+G_DEFINE_TYPE (NMSettingGsm, nm_setting_gsm, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_GSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_GSM, NMSettingGsmPrivate))
 
-/**
- * nm_setting_gsm_new:
- *
- * Creates a new #NMSettingGsm object with default values.
- *
- * Returns: the new empty #NMSettingGsm object
- **/
-NMSetting *
-nm_setting_gsm_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_GSM, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_gsm_get_number:
  * @setting: the #NMSettingGsm
  *
  * Returns: the #NMSettingGsm:number property of the setting
+ *
+ * Deprecated: 1.16:  user-provided values for this setting are no longer used.
  **/
 const char *
 nm_setting_gsm_get_number (NMSettingGsm *setting)
@@ -458,27 +447,58 @@ need_secrets (NMSetting *setting)
 	return secrets;
 }
 
-static void
-nm_setting_gsm_init (NMSettingGsm *setting)
-{
-}
+/*****************************************************************************/
 
 static void
-finalize (GObject *object)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (object);
-
-	g_free (priv->number);
-	g_free (priv->username);
-	g_free (priv->password);
-	g_free (priv->apn);
-	g_free (priv->network_id);
-	g_free (priv->pin);
-	g_free (priv->device_id);
-	g_free (priv->sim_id);
-	g_free (priv->sim_operator_id);
+	NMSettingGsm *setting = NM_SETTING_GSM (object);
 
-	G_OBJECT_CLASS (nm_setting_gsm_parent_class)->finalize (object);
+	switch (prop_id) {
+	case PROP_NUMBER:
+		g_value_set_string (value, nm_setting_gsm_get_number (setting));
+		break;
+	case PROP_USERNAME:
+		g_value_set_string (value, nm_setting_gsm_get_username (setting));
+		break;
+	case PROP_PASSWORD:
+		g_value_set_string (value, nm_setting_gsm_get_password (setting));
+		break;
+	case PROP_PASSWORD_FLAGS:
+		g_value_set_flags (value, nm_setting_gsm_get_password_flags (setting));
+		break;
+	case PROP_APN:
+		g_value_set_string (value, nm_setting_gsm_get_apn (setting));
+		break;
+	case PROP_NETWORK_ID:
+		g_value_set_string (value, nm_setting_gsm_get_network_id (setting));
+		break;
+	case PROP_PIN:
+		g_value_set_string (value, nm_setting_gsm_get_pin (setting));
+		break;
+	case PROP_PIN_FLAGS:
+		g_value_set_flags (value, nm_setting_gsm_get_pin_flags (setting));
+		break;
+	case PROP_HOME_ONLY:
+		g_value_set_boolean (value, nm_setting_gsm_get_home_only (setting));
+		break;
+	case PROP_DEVICE_ID:
+		g_value_set_string (value, nm_setting_gsm_get_device_id (setting));
+		break;
+	case PROP_SIM_ID:
+		g_value_set_string (value, nm_setting_gsm_get_sim_id (setting));
+		break;
+	case PROP_SIM_OPERATOR_ID:
+		g_value_set_string (value, nm_setting_gsm_get_sim_operator_id (setting));
+		break;
+	case PROP_MTU:
+		g_value_set_uint (value, nm_setting_gsm_get_mtu (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -549,56 +569,42 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_gsm_init (NMSettingGsm *setting)
 {
-	NMSettingGsm *setting = NM_SETTING_GSM (object);
+}
 
-	switch (prop_id) {
-	case PROP_NUMBER:
-		g_value_set_string (value, nm_setting_gsm_get_number (setting));
-		break;
-	case PROP_USERNAME:
-		g_value_set_string (value, nm_setting_gsm_get_username (setting));
-		break;
-	case PROP_PASSWORD:
-		g_value_set_string (value, nm_setting_gsm_get_password (setting));
-		break;
-	case PROP_PASSWORD_FLAGS:
-		g_value_set_flags (value, nm_setting_gsm_get_password_flags (setting));
-		break;
-	case PROP_APN:
-		g_value_set_string (value, nm_setting_gsm_get_apn (setting));
-		break;
-	case PROP_NETWORK_ID:
-		g_value_set_string (value, nm_setting_gsm_get_network_id (setting));
-		break;
-	case PROP_PIN:
-		g_value_set_string (value, nm_setting_gsm_get_pin (setting));
-		break;
-	case PROP_PIN_FLAGS:
-		g_value_set_flags (value, nm_setting_gsm_get_pin_flags (setting));
-		break;
-	case PROP_HOME_ONLY:
-		g_value_set_boolean (value, nm_setting_gsm_get_home_only (setting));
-		break;
-	case PROP_DEVICE_ID:
-		g_value_set_string (value, nm_setting_gsm_get_device_id (setting));
-		break;
-	case PROP_SIM_ID:
-		g_value_set_string (value, nm_setting_gsm_get_sim_id (setting));
-		break;
-	case PROP_SIM_OPERATOR_ID:
-		g_value_set_string (value, nm_setting_gsm_get_sim_operator_id (setting));
-		break;
-	case PROP_MTU:
-		g_value_set_uint (value, nm_setting_gsm_get_mtu (setting));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+/**
+ * nm_setting_gsm_new:
+ *
+ * Creates a new #NMSettingGsm object with default values.
+ *
+ * Returns: the new empty #NMSettingGsm object
+ **/
+NMSetting *
+nm_setting_gsm_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_GSM, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingGsmPrivate *priv = NM_SETTING_GSM_GET_PRIVATE (object);
+
+	g_free (priv->number);
+	g_free (priv->username);
+	g_free (priv->password);
+	g_free (priv->apn);
+	g_free (priv->network_id);
+	g_free (priv->pin);
+	g_free (priv->device_id);
+	g_free (priv->sim_id);
+	g_free (priv->sim_operator_id);
+
+	G_OBJECT_CLASS (nm_setting_gsm_parent_class)->finalize (object);
 }
 
 static void
@@ -610,8 +616,8 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingGsmPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify         = verify;
@@ -621,17 +627,16 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	/**
 	 * NMSettingGsm:number:
 	 *
-	 * Number to dial when establishing a PPP data session with the GSM-based
-	 * mobile broadband network.  Many modems do not require PPP for connections
-	 * to the mobile network and thus this property should be left blank, which
-	 * allows NetworkManager to select the appropriate settings automatically.
+	 * Legacy setting that used to help establishing PPP data sessions for
+	 * GSM-based modems.
+	 *
+	 * Deprecated: 1.16: user-provided values for this setting are no longer used.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NUMBER,
-		 g_param_spec_string (NM_SETTING_GSM_NUMBER, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NUMBER] =
+	    g_param_spec_string (NM_SETTING_GSM_NUMBER, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:username:
@@ -640,12 +645,11 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	 * providers do not require a username, or accept any username.  But if a
 	 * username is required, it is specified here.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_USERNAME,
-		 g_param_spec_string (NM_SETTING_GSM_USERNAME, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_USERNAME] =
+	    g_param_spec_string (NM_SETTING_GSM_USERNAME, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:password:
@@ -654,26 +658,24 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	 * providers do not require a password, or accept any password.  But if a
 	 * password is required, it is specified here.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD,
-		 g_param_spec_string (NM_SETTING_GSM_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_GSM_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:password-flags:
 	 *
 	 * Flags indicating how to handle the #NMSettingGsm:password property.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_GSM_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_GSM_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:apn:
@@ -686,12 +688,11 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	 * The APN may only be composed of the characters a-z, 0-9, ., and - per GSM
 	 * 03.60 Section 14.9.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_APN,
-		 g_param_spec_string (NM_SETTING_GSM_APN, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_APN] =
+	    g_param_spec_string (NM_SETTING_GSM_APN, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:network-id:
@@ -702,12 +703,11 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	 * This can be used to ensure that the device does not roam when direct
 	 * roaming control of the device is not otherwise possible.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NETWORK_ID,
-		 g_param_spec_string (NM_SETTING_GSM_NETWORK_ID, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NETWORK_ID] =
+	    g_param_spec_string (NM_SETTING_GSM_NETWORK_ID, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:pin:
@@ -716,26 +716,24 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	 * operations are requested.  Specify the PIN here to allow operation of the
 	 * device.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PIN,
-		 g_param_spec_string (NM_SETTING_GSM_PIN, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PIN] =
+	    g_param_spec_string (NM_SETTING_GSM_PIN, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:pin-flags:
 	 *
 	 * Flags indicating how to handle the #NMSettingGsm:pin property.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PIN_FLAGS,
-		 g_param_spec_flags (NM_SETTING_GSM_PIN_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PIN_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_GSM_PIN_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:home-only:
@@ -743,12 +741,11 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	 * When %TRUE, only connections to the home network will be allowed.
 	 * Connections to roaming networks will not be made.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_HOME_ONLY,
-		 g_param_spec_boolean (NM_SETTING_GSM_HOME_ONLY, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_HOME_ONLY] =
+	    g_param_spec_boolean (NM_SETTING_GSM_HOME_ONLY, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:device-id:
@@ -759,12 +756,11 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_DEVICE_ID,
-		 g_param_spec_string (NM_SETTING_GSM_DEVICE_ID, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DEVICE_ID] =
+	    g_param_spec_string (NM_SETTING_GSM_DEVICE_ID, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:sim-id:
@@ -776,12 +772,11 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_SIM_ID,
-		 g_param_spec_string (NM_SETTING_GSM_SIM_ID, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SIM_ID] =
+	    g_param_spec_string (NM_SETTING_GSM_SIM_ID, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:sim-operator-id:
@@ -794,12 +789,11 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_SIM_OPERATOR_ID,
-		 g_param_spec_string (NM_SETTING_GSM_SIM_OPERATOR_ID, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SIM_OPERATOR_ID] =
+	    g_param_spec_string (NM_SETTING_GSM_SIM_OPERATOR_ID, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingGsm:mtu:
@@ -809,14 +803,13 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	 *
 	 * Since: 1.8
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_MTU,
-		 g_param_spec_uint (NM_SETTING_GSM_MTU, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MTU] =
+	    g_param_spec_uint (NM_SETTING_GSM_MTU, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/* Ignore incoming deprecated properties */
 	_properties_override_add_dbus_only (properties_override,
@@ -831,6 +824,8 @@ nm_setting_gsm_class_init (NMSettingGsmClass *klass)
 	                                    NULL,
 	                                    NULL);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_GSM,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-gsm.h b/libnm-core/nm-setting-gsm.h
index 8516698d..0763b9df 100644
--- a/libnm-core/nm-setting-gsm.h
+++ b/libnm-core/nm-setting-gsm.h
@@ -40,7 +40,6 @@ G_BEGIN_DECLS
 
 #define NM_SETTING_GSM_SETTING_NAME "gsm"
 
-#define NM_SETTING_GSM_NUMBER          "number"
 #define NM_SETTING_GSM_USERNAME        "username"
 #define NM_SETTING_GSM_PASSWORD        "password"
 #define NM_SETTING_GSM_PASSWORD_FLAGS  "password-flags"
@@ -54,6 +53,9 @@ G_BEGIN_DECLS
 #define NM_SETTING_GSM_SIM_OPERATOR_ID "sim-operator-id"
 #define NM_SETTING_GSM_MTU             "mtu"
 
+/* Deprecated */
+#define NM_SETTING_GSM_NUMBER          "number"
+
 /**
  * NMSettingGsm:
  *
@@ -73,7 +75,6 @@ typedef struct {
 GType nm_setting_gsm_get_type (void);
 
 NMSetting *nm_setting_gsm_new                  (void);
-const char *nm_setting_gsm_get_number          (NMSettingGsm *setting);
 const char *nm_setting_gsm_get_username        (NMSettingGsm *setting);
 const char *nm_setting_gsm_get_password        (NMSettingGsm *setting);
 const char *nm_setting_gsm_get_apn             (NMSettingGsm *setting);
@@ -90,6 +91,9 @@ const char *nm_setting_gsm_get_sim_operator_id (NMSettingGsm *setting);
 NM_AVAILABLE_IN_1_8
 guint32 nm_setting_gsm_get_mtu                 (NMSettingGsm *setting);
 
+NM_DEPRECATED_IN_1_16
+const char *nm_setting_gsm_get_number          (NMSettingGsm *setting);
+
 NMSettingSecretFlags nm_setting_gsm_get_pin_flags      (NMSettingGsm *setting);
 NMSettingSecretFlags nm_setting_gsm_get_password_flags (NMSettingGsm *setting);
 
diff --git a/libnm-core/nm-setting-infiniband.c b/libnm-core/nm-setting-infiniband.c
index d5ae7085..9348fa2b 100644
--- a/libnm-core/nm-setting-infiniband.c
+++ b/libnm-core/nm-setting-infiniband.c
@@ -21,9 +21,10 @@
 
 #include "nm-default.h"
 
+#include "nm-setting-infiniband.h"
+
 #include <stdlib.h>
 
-#include "nm-setting-infiniband.h"
 #include "nm-utils.h"
 #include "nm-utils-private.h"
 #include "nm-setting-private.h"
@@ -37,9 +38,15 @@
  * necessary for connection to IP-over-InfiniBand networks.
  **/
 
-G_DEFINE_TYPE (NMSettingInfiniband, nm_setting_infiniband, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_INFINIBAND, NMSettingInfinibandPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_MAC_ADDRESS,
+	PROP_MTU,
+	PROP_TRANSPORT_MODE,
+	PROP_P_KEY,
+	PROP_PARENT,
+);
 
 typedef struct {
 	char *mac_address;
@@ -49,29 +56,11 @@ typedef struct {
 	char *parent, *virtual_iface_name;
 } NMSettingInfinibandPrivate;
 
-enum {
-	PROP_0,
-	PROP_MAC_ADDRESS,
-	PROP_MTU,
-	PROP_TRANSPORT_MODE,
-	PROP_P_KEY,
-	PROP_PARENT,
+G_DEFINE_TYPE (NMSettingInfiniband, nm_setting_infiniband, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_INFINIBAND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_INFINIBAND, NMSettingInfinibandPrivate))
 
-/**
- * nm_setting_infiniband_new:
- *
- * Creates a new #NMSettingInfiniband object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingInfiniband object
- **/
-NMSetting *
-nm_setting_infiniband_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_INFINIBAND, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_infiniband_get_mac_address:
@@ -295,22 +284,34 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static void
-nm_setting_infiniband_init (NMSettingInfiniband *setting)
-{
-}
+/*****************************************************************************/
 
 static void
-finalize (GObject *object)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE (object);
-
-	g_free (priv->transport_mode);
-	g_free (priv->mac_address);
-	g_free (priv->parent);
-	g_free (priv->virtual_iface_name);
+	NMSettingInfiniband *setting = NM_SETTING_INFINIBAND (object);
 
-	G_OBJECT_CLASS (nm_setting_infiniband_parent_class)->finalize (object);
+	switch (prop_id) {
+	case PROP_MAC_ADDRESS:
+		g_value_set_string (value, nm_setting_infiniband_get_mac_address (setting));
+		break;
+	case PROP_MTU:
+		g_value_set_uint (value, nm_setting_infiniband_get_mtu (setting));
+		break;
+	case PROP_TRANSPORT_MODE:
+		g_value_set_string (value, nm_setting_infiniband_get_transport_mode (setting));
+		break;
+	case PROP_P_KEY:
+		g_value_set_int (value, nm_setting_infiniband_get_p_key (setting));
+		break;
+	case PROP_PARENT:
+		g_value_set_string (value, nm_setting_infiniband_get_parent (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -347,32 +348,37 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_infiniband_init (NMSettingInfiniband *setting)
 {
-	NMSettingInfiniband *setting = NM_SETTING_INFINIBAND (object);
+}
 
-	switch (prop_id) {
-	case PROP_MAC_ADDRESS:
-		g_value_set_string (value, nm_setting_infiniband_get_mac_address (setting));
-		break;
-	case PROP_MTU:
-		g_value_set_uint (value, nm_setting_infiniband_get_mtu (setting));
-		break;
-	case PROP_TRANSPORT_MODE:
-		g_value_set_string (value, nm_setting_infiniband_get_transport_mode (setting));
-		break;
-	case PROP_P_KEY:
-		g_value_set_int (value, nm_setting_infiniband_get_p_key (setting));
-		break;
-	case PROP_PARENT:
-		g_value_set_string (value, nm_setting_infiniband_get_parent (setting));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+/**
+ * nm_setting_infiniband_new:
+ *
+ * Creates a new #NMSettingInfiniband object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingInfiniband object
+ **/
+NMSetting *
+nm_setting_infiniband_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_INFINIBAND, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingInfinibandPrivate *priv = NM_SETTING_INFINIBAND_GET_PRIVATE (object);
+
+	g_free (priv->transport_mode);
+	g_free (priv->mac_address);
+	g_free (priv->parent);
+	g_free (priv->virtual_iface_name);
+
+	G_OBJECT_CLASS (nm_setting_infiniband_parent_class)->finalize (object);
 }
 
 static void
@@ -384,8 +390,8 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingInfinibandPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -415,17 +421,15 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *klass)
 	 * example: HWADDR=01:02:03:04:05:06:07:08:09:0A:01:02:03:04:05:06:07:08:09:11
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MAC_ADDRESS,
-		 g_param_spec_string (NM_SETTING_INFINIBAND_MAC_ADDRESS, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MAC_ADDRESS] =
+	    g_param_spec_string (NM_SETTING_INFINIBAND_MAC_ADDRESS, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_INFINIBAND_MAC_ADDRESS),
+	                                    obj_properties[PROP_MAC_ADDRESS],
 	                                    G_VARIANT_TYPE_BYTESTRING,
 	                                    _nm_utils_hwaddr_to_dbus,
 	                                    _nm_utils_hwaddr_from_dbus);
@@ -442,14 +446,13 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *klass)
 	 * description: MTU of the interface.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MTU,
-		 g_param_spec_uint (NM_SETTING_INFINIBAND_MTU, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MTU] =
+	    g_param_spec_uint (NM_SETTING_INFINIBAND_MTU, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingInfiniband:transport-mode:
@@ -465,14 +468,13 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *klass)
 	 *   "datagram" mode
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_TRANSPORT_MODE,
-		 g_param_spec_string (NM_SETTING_INFINIBAND_TRANSPORT_MODE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TRANSPORT_MODE] =
+	    g_param_spec_string (NM_SETTING_INFINIBAND_TRANSPORT_MODE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingInfiniband:p-key:
@@ -492,14 +494,13 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *klass)
 	 * example: PKEY=yes PKEY_ID=2 PHYSDEV=mlx4_ib0 DEVICE=mlx4_ib0.8002
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_P_KEY,
-		 g_param_spec_int (NM_SETTING_INFINIBAND_P_KEY, "", "",
-		                   -1, 0xFFFF, -1,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_CONSTRUCT |
-		                   NM_SETTING_PARAM_INFERRABLE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_P_KEY] =
+	    g_param_spec_int (NM_SETTING_INFINIBAND_P_KEY, "", "",
+	                      -1, 0xFFFF, -1,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_CONSTRUCT |
+	                      NM_SETTING_PARAM_INFERRABLE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingInfiniband:parent:
@@ -517,14 +518,15 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *klass)
 	 * example: PHYSDEV=ib0
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PARENT,
-		 g_param_spec_string (NM_SETTING_INFINIBAND_PARENT, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PARENT] =
+	    g_param_spec_string (NM_SETTING_INFINIBAND_PARENT, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_INFINIBAND,
 	                               NULL, properties_override);
diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c
index 2b3134b6..5250ae43 100644
--- a/libnm-core/nm-setting-ip-config.c
+++ b/libnm-core/nm-setting-ip-config.c
@@ -22,10 +22,10 @@
 
 #include "nm-default.h"
 
-#include <string.h>
+#include "nm-setting-ip-config.h"
+
 #include <arpa/inet.h>
 
-#include "nm-setting-ip-config.h"
 #include "nm-setting-ip4-config.h"
 #include "nm-setting-ip6-config.h"
 #include "nm-utils.h"
@@ -44,6 +44,8 @@
  * related to IP addressing, routing, and Domain Name Service.
  **/
 
+/*****************************************************************************/
+
 const NMUtilsDNSOptionDesc _nm_utils_dns_option_descs[] = {
 	{ NM_SETTING_DNS_OPTION_DEBUG,                 FALSE,   FALSE },
 	{ NM_SETTING_DNS_OPTION_NDOTS,                 TRUE,    FALSE },
@@ -1123,7 +1125,7 @@ _nm_ip_route_get_attributes_direct (NMIPRoute *route)
  * @route: the #NMIPRoute
  * @sorted: whether to sort the names. Otherwise, their order is
  *   undefined and unstable.
- * @out_length: (allow-none): (out): the number of elements
+ * @out_length: (allow-none) (out): the number of elements
  *
  * Gets an array of attribute names defined on @route.
  *
@@ -1382,9 +1384,26 @@ _nm_ip_route_attribute_validate_all (const NMIPRoute *route)
 
 /*****************************************************************************/
 
-G_DEFINE_ABSTRACT_TYPE (NMSettingIPConfig, nm_setting_ip_config, NM_TYPE_SETTING)
-
-#define NM_SETTING_IP_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP_CONFIG, NMSettingIPConfigPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingIPConfig,
+	PROP_METHOD,
+	PROP_DNS,
+	PROP_DNS_SEARCH,
+	PROP_DNS_OPTIONS,
+	PROP_DNS_PRIORITY,
+	PROP_ADDRESSES,
+	PROP_GATEWAY,
+	PROP_ROUTES,
+	PROP_ROUTE_METRIC,
+	PROP_ROUTE_TABLE,
+	PROP_IGNORE_AUTO_ROUTES,
+	PROP_IGNORE_AUTO_DNS,
+	PROP_DHCP_HOSTNAME,
+	PROP_DHCP_SEND_HOSTNAME,
+	PROP_NEVER_DEFAULT,
+	PROP_MAY_FAIL,
+	PROP_DAD_TIMEOUT,
+	PROP_DHCP_TIMEOUT,
+);
 
 typedef struct {
 	char *method;
@@ -1407,29 +1426,11 @@ typedef struct {
 	int dhcp_timeout;
 } NMSettingIPConfigPrivate;
 
-enum {
-	PROP_0,
-	PROP_METHOD,
-	PROP_DNS,
-	PROP_DNS_SEARCH,
-	PROP_DNS_OPTIONS,
-	PROP_DNS_PRIORITY,
-	PROP_ADDRESSES,
-	PROP_GATEWAY,
-	PROP_ROUTES,
-	PROP_ROUTE_METRIC,
-	PROP_ROUTE_TABLE,
-	PROP_IGNORE_AUTO_ROUTES,
-	PROP_IGNORE_AUTO_DNS,
-	PROP_DHCP_HOSTNAME,
-	PROP_DHCP_SEND_HOSTNAME,
-	PROP_NEVER_DEFAULT,
-	PROP_MAY_FAIL,
-	PROP_DAD_TIMEOUT,
-	PROP_DHCP_TIMEOUT,
+G_DEFINE_ABSTRACT_TYPE (NMSettingIPConfig, nm_setting_ip_config, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_IP_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP_CONFIG, NMSettingIPConfigPrivate))
+
+/*****************************************************************************/
 
 #define NM_SETTING_IP_CONFIG_GET_FAMILY(setting) (NM_IS_SETTING_IP4_CONFIG (setting) ? AF_INET : AF_INET6)
 
@@ -1515,7 +1516,7 @@ nm_setting_ip_config_add_dns (NMSettingIPConfig *setting, const char *dns)
 	}
 
 	g_ptr_array_add (priv->dns, dns_canonical);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS);
+	_notify (setting, PROP_DNS);
 	return TRUE;
 }
 
@@ -1537,7 +1538,7 @@ nm_setting_ip_config_remove_dns (NMSettingIPConfig *setting, int idx)
 	g_return_if_fail (idx >= 0 && idx < priv->dns->len);
 
 	g_ptr_array_remove_index (priv->dns, idx);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS);
+	_notify (setting, PROP_DNS);
 }
 
 /**
@@ -1566,7 +1567,7 @@ nm_setting_ip_config_remove_dns_by_value (NMSettingIPConfig *setting, const char
 	for (i = 0; i < priv->dns->len; i++) {
 		if (!strcmp (dns_canonical, priv->dns->pdata[i])) {
 			g_ptr_array_remove_index (priv->dns, i);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS);
+			_notify (setting, PROP_DNS);
 			g_free (dns_canonical);
 			return TRUE;
 		}
@@ -1592,7 +1593,7 @@ nm_setting_ip_config_clear_dns (NMSettingIPConfig *setting)
 
 	if (priv->dns->len != 0) {
 		g_ptr_array_set_size (priv->dns, 0);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS);
+		_notify (setting, PROP_DNS);
 	}
 }
 
@@ -1658,7 +1659,7 @@ nm_setting_ip_config_add_dns_search (NMSettingIPConfig *setting,
 	}
 
 	g_ptr_array_add (priv->dns_search, g_strdup (dns_search));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_SEARCH);
+	_notify (setting, PROP_DNS_SEARCH);
 	return TRUE;
 }
 
@@ -1680,7 +1681,7 @@ nm_setting_ip_config_remove_dns_search (NMSettingIPConfig *setting, int idx)
 	g_return_if_fail (idx >= 0 && idx < priv->dns_search->len);
 
 	g_ptr_array_remove_index (priv->dns_search, idx);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_SEARCH);
+	_notify (setting, PROP_DNS_SEARCH);
 }
 
 /**
@@ -1707,7 +1708,7 @@ nm_setting_ip_config_remove_dns_search_by_value (NMSettingIPConfig *setting,
 	for (i = 0; i < priv->dns_search->len; i++) {
 		if (!strcmp (dns_search, priv->dns_search->pdata[i])) {
 			g_ptr_array_remove_index (priv->dns_search, i);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_SEARCH);
+			_notify (setting, PROP_DNS_SEARCH);
 			return TRUE;
 		}
 	}
@@ -1731,7 +1732,7 @@ nm_setting_ip_config_clear_dns_searches (NMSettingIPConfig *setting)
 
 	if (priv->dns_search->len != 0) {
 		g_ptr_array_set_size (priv->dns_search, 0);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_SEARCH);
+		_notify (setting, PROP_DNS_SEARCH);
 	}
 }
 
@@ -1863,7 +1864,7 @@ nm_setting_ip_config_add_dns_option (NMSettingIPConfig *setting,
 	}
 
 	g_ptr_array_add (priv->dns_options, g_strdup (dns_option));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_OPTIONS);
+	_notify (setting, PROP_DNS_OPTIONS);
 	return TRUE;
 }
 
@@ -1888,7 +1889,7 @@ nm_setting_ip_config_remove_dns_option (NMSettingIPConfig *setting, int idx)
 	g_return_if_fail (idx >= 0 && idx < priv->dns_options->len);
 
 	g_ptr_array_remove_index (priv->dns_options, idx);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_OPTIONS);
+	_notify (setting, PROP_DNS_OPTIONS);
 }
 
 /**
@@ -1920,7 +1921,7 @@ nm_setting_ip_config_remove_dns_option_by_value (NMSettingIPConfig *setting,
 	i = _nm_utils_dns_option_find_idx (priv->dns_options, dns_option);
 	if (i >= 0) {
 		g_ptr_array_remove_index (priv->dns_options, i);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_OPTIONS);
+		_notify (setting, PROP_DNS_OPTIONS);
 		return TRUE;
 	}
 
@@ -1959,7 +1960,7 @@ nm_setting_ip_config_clear_dns_options (NMSettingIPConfig *setting, gboolean is_
 			g_ptr_array_set_size (priv->dns_options, 0);
 		}
 	}
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_DNS_OPTIONS);
+	_notify (setting, PROP_DNS_OPTIONS);
 }
 
 /**
@@ -2042,7 +2043,7 @@ nm_setting_ip_config_add_address (NMSettingIPConfig *setting,
 
 	g_ptr_array_add (priv->addresses, nm_ip_address_dup (address));
 
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ADDRESSES);
+	_notify (setting, PROP_ADDRESSES);
 	return TRUE;
 }
 
@@ -2065,7 +2066,7 @@ nm_setting_ip_config_remove_address (NMSettingIPConfig *setting, int idx)
 
 	g_ptr_array_remove_index (priv->addresses, idx);
 
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ADDRESSES);
+	_notify (setting, PROP_ADDRESSES);
 }
 
 /**
@@ -2091,7 +2092,7 @@ nm_setting_ip_config_remove_address_by_value (NMSettingIPConfig *setting,
 	for (i = 0; i < priv->addresses->len; i++) {
 		if (nm_ip_address_equal (priv->addresses->pdata[i], address)) {
 			g_ptr_array_remove_index (priv->addresses, i);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ADDRESSES);
+			_notify (setting, PROP_ADDRESSES);
 			return TRUE;
 		}
 	}
@@ -2113,7 +2114,7 @@ nm_setting_ip_config_clear_addresses (NMSettingIPConfig *setting)
 
 	if (priv->addresses->len != 0) {
 		g_ptr_array_set_size (priv->addresses, 0);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ADDRESSES);
+		_notify (setting, PROP_ADDRESSES);
 	}
 }
 
@@ -2200,7 +2201,7 @@ nm_setting_ip_config_add_route (NMSettingIPConfig *setting,
 	}
 
 	g_ptr_array_add (priv->routes, nm_ip_route_dup (route));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES);
+	_notify (setting, PROP_ROUTES);
 	return TRUE;
 }
 
@@ -2222,7 +2223,7 @@ nm_setting_ip_config_remove_route (NMSettingIPConfig *setting, int idx)
 	g_return_if_fail (idx >= 0 && idx < priv->routes->len);
 
 	g_ptr_array_remove_index (priv->routes, idx);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES);
+	_notify (setting, PROP_ROUTES);
 }
 
 /**
@@ -2250,7 +2251,7 @@ nm_setting_ip_config_remove_route_by_value (NMSettingIPConfig *setting,
 	for (i = 0; i < priv->routes->len; i++) {
 		if (nm_ip_route_equal_full (priv->routes->pdata[i], route, NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS)) {
 			g_ptr_array_remove_index (priv->routes, i);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES);
+			_notify (setting, PROP_ROUTES);
 			return TRUE;
 		}
 	}
@@ -2272,7 +2273,7 @@ nm_setting_ip_config_clear_routes (NMSettingIPConfig *setting)
 
 	if (priv->routes->len != 0) {
 		g_ptr_array_set_size (priv->routes, 0);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_IP_CONFIG_ROUTES);
+		_notify (setting, PROP_ROUTES);
 	}
 }
 
@@ -2612,78 +2613,161 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static gboolean
-compare_property (NMSetting *setting,
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
                   NMSetting *other,
-                  const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	NMSettingIPConfigPrivate *a_priv, *b_priv;
-	NMSettingClass *setting_class;
+	NMSettingIPConfigPrivate *a_priv;
+	NMSettingIPConfigPrivate *b_priv;
 	guint i;
 
-	if (nm_streq (prop_spec->name, NM_SETTING_IP_CONFIG_ADDRESSES)) {
-		a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-		b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (other);
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_IP_CONFIG_ADDRESSES)) {
+		if (other) {
+			a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
+			b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (other);
 
-		if (a_priv->addresses->len != b_priv->addresses->len)
-			return FALSE;
-		for (i = 0; i < a_priv->addresses->len; i++) {
-			if (!_nm_ip_address_equal (a_priv->addresses->pdata[i], b_priv->addresses->pdata[i], TRUE))
+			if (a_priv->addresses->len != b_priv->addresses->len)
 				return FALSE;
+			for (i = 0; i < a_priv->addresses->len; i++) {
+				if (!_nm_ip_address_equal (a_priv->addresses->pdata[i], b_priv->addresses->pdata[i], TRUE))
+					return FALSE;
+			}
 		}
 		return TRUE;
 	}
 
-	if (nm_streq (prop_spec->name, NM_SETTING_IP_CONFIG_ROUTES)) {
-		a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
-		b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (other);
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_IP_CONFIG_ROUTES)) {
+		if (other) {
+			a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
+			b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (other);
 
-		if (a_priv->routes->len != b_priv->routes->len)
-			return FALSE;
-		for (i = 0; i < a_priv->routes->len; i++) {
-			if (!nm_ip_route_equal_full (a_priv->routes->pdata[i], b_priv->routes->pdata[i], NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS))
+			if (a_priv->routes->len != b_priv->routes->len)
 				return FALSE;
+			for (i = 0; i < a_priv->routes->len; i++) {
+				if (!nm_ip_route_equal_full (a_priv->routes->pdata[i], b_priv->routes->pdata[i], NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS))
+					return FALSE;
+			}
 		}
 		return TRUE;
 	}
 
-	setting_class = NM_SETTING_CLASS (nm_setting_ip_config_parent_class);
-	return setting_class->compare_property (setting, other, prop_spec, flags);
+	return NM_SETTING_CLASS (nm_setting_ip_config_parent_class)->compare_property (sett_info,
+	                                                                               property_idx,
+	                                                                               setting,
+	                                                                               other,
+	                                                                               flags);
 }
 
 /*****************************************************************************/
 
-static void
-nm_setting_ip_config_init (NMSettingIPConfig *setting)
+static gboolean
+ip_gateway_set (NMSetting  *setting,
+                GVariant   *connection_dict,
+                const char *property,
+                GVariant   *value,
+                NMSettingParseFlags parse_flags,
+                GError    **error)
 {
-	NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
+	/* FIXME: properly handle errors */
 
-	priv->dns = g_ptr_array_new_with_free_func (g_free);
-	priv->dns_search = g_ptr_array_new_with_free_func (g_free);
-	priv->dns_options = NULL;
-	priv->addresses = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_address_unref);
-	priv->routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_route_unref);
+	/* Don't set from 'gateway' if we're going to use the gateway in 'addresses' */
+	if (_nm_setting_use_legacy_property (setting, connection_dict, "addresses", "gateway"))
+		return TRUE;
+
+	g_object_set (setting, property, g_variant_get_string (value, NULL), NULL);
+	return TRUE;
 }
 
-static void
-finalize (GObject *object)
+GArray *
+_nm_sett_info_property_override_create_array_ip_config (void)
 {
-	NMSettingIPConfig *self = NM_SETTING_IP_CONFIG (object);
-	NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (self);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	g_free (priv->method);
-	g_free (priv->gateway);
-	g_free (priv->dhcp_hostname);
+	_properties_override_add_override (properties_override,
+	                                   obj_properties[PROP_GATEWAY],
+	                                   G_VARIANT_TYPE_STRING,
+	                                   NULL,
+	                                   ip_gateway_set,
+	                                   NULL);
 
-	g_ptr_array_unref (priv->dns);
-	g_ptr_array_unref (priv->dns_search);
-	if (priv->dns_options)
-		g_ptr_array_unref (priv->dns_options);
-	g_ptr_array_unref (priv->addresses);
-	g_ptr_array_unref (priv->routes);
+	return properties_override;
+}
 
-	G_OBJECT_CLASS (nm_setting_ip_config_parent_class)->finalize (object);
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingIPConfig *setting = NM_SETTING_IP_CONFIG (object);
+	NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
+
+	switch (prop_id) {
+	case PROP_METHOD:
+		g_value_set_string (value, nm_setting_ip_config_get_method (setting));
+		break;
+	case PROP_DNS:
+		g_value_take_boxed (value, _nm_utils_ptrarray_to_strv (priv->dns));
+		break;
+	case PROP_DNS_SEARCH:
+		g_value_take_boxed (value, _nm_utils_ptrarray_to_strv (priv->dns_search));
+		break;
+	case PROP_DNS_OPTIONS:
+		g_value_take_boxed (value, priv->dns_options ? _nm_utils_ptrarray_to_strv (priv->dns_options) : NULL);
+		break;
+	case PROP_DNS_PRIORITY:
+		g_value_set_int (value, priv->dns_priority);
+		break;
+	case PROP_ADDRESSES:
+		g_value_take_boxed (value, _nm_utils_copy_array (priv->addresses,
+		                                                 (NMUtilsCopyFunc) nm_ip_address_dup,
+		                                                 (GDestroyNotify) nm_ip_address_unref));
+		break;
+	case PROP_GATEWAY:
+		g_value_set_string (value, nm_setting_ip_config_get_gateway (setting));
+		break;
+	case PROP_ROUTES:
+		g_value_take_boxed (value, _nm_utils_copy_array (priv->routes,
+		                                                 (NMUtilsCopyFunc) nm_ip_route_dup,
+		                                                 (GDestroyNotify) nm_ip_route_unref));
+		break;
+	case PROP_ROUTE_METRIC:
+		g_value_set_int64 (value, priv->route_metric);
+		break;
+	case PROP_ROUTE_TABLE:
+		g_value_set_uint (value, priv->route_table);
+		break;
+	case PROP_IGNORE_AUTO_ROUTES:
+		g_value_set_boolean (value, nm_setting_ip_config_get_ignore_auto_routes (setting));
+		break;
+	case PROP_IGNORE_AUTO_DNS:
+		g_value_set_boolean (value, nm_setting_ip_config_get_ignore_auto_dns (setting));
+		break;
+	case PROP_DHCP_HOSTNAME:
+		g_value_set_string (value, nm_setting_ip_config_get_dhcp_hostname (setting));
+		break;
+	case PROP_DHCP_SEND_HOSTNAME:
+		g_value_set_boolean (value, nm_setting_ip_config_get_dhcp_send_hostname (setting));
+		break;
+	case PROP_NEVER_DEFAULT:
+		g_value_set_boolean (value, priv->never_default);
+		break;
+	case PROP_MAY_FAIL:
+		g_value_set_boolean (value, priv->may_fail);
+		break;
+	case PROP_DAD_TIMEOUT:
+		g_value_set_int (value, nm_setting_ip_config_get_dad_timeout (setting));
+		break;
+	case PROP_DHCP_TIMEOUT:
+		g_value_set_int (value, nm_setting_ip_config_get_dhcp_timeout (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -2786,111 +2870,38 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_ip_config_init (NMSettingIPConfig *setting)
 {
-	NMSettingIPConfig *setting = NM_SETTING_IP_CONFIG (object);
 	NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting);
 
-	switch (prop_id) {
-	case PROP_METHOD:
-		g_value_set_string (value, nm_setting_ip_config_get_method (setting));
-		break;
-	case PROP_DNS:
-		g_value_take_boxed (value, _nm_utils_ptrarray_to_strv (priv->dns));
-		break;
-	case PROP_DNS_SEARCH:
-		g_value_take_boxed (value, _nm_utils_ptrarray_to_strv (priv->dns_search));
-		break;
-	case PROP_DNS_OPTIONS:
-		g_value_take_boxed (value, priv->dns_options ? _nm_utils_ptrarray_to_strv (priv->dns_options) : NULL);
-		break;
-	case PROP_DNS_PRIORITY:
-		g_value_set_int (value, priv->dns_priority);
-		break;
-	case PROP_ADDRESSES:
-		g_value_take_boxed (value, _nm_utils_copy_array (priv->addresses,
-		                                                 (NMUtilsCopyFunc) nm_ip_address_dup,
-		                                                 (GDestroyNotify) nm_ip_address_unref));
-		break;
-	case PROP_GATEWAY:
-		g_value_set_string (value, nm_setting_ip_config_get_gateway (setting));
-		break;
-	case PROP_ROUTES:
-		g_value_take_boxed (value, _nm_utils_copy_array (priv->routes,
-		                                                 (NMUtilsCopyFunc) nm_ip_route_dup,
-		                                                 (GDestroyNotify) nm_ip_route_unref));
-		break;
-	case PROP_ROUTE_METRIC:
-		g_value_set_int64 (value, priv->route_metric);
-		break;
-	case PROP_ROUTE_TABLE:
-		g_value_set_uint (value, priv->route_table);
-		break;
-	case PROP_IGNORE_AUTO_ROUTES:
-		g_value_set_boolean (value, nm_setting_ip_config_get_ignore_auto_routes (setting));
-		break;
-	case PROP_IGNORE_AUTO_DNS:
-		g_value_set_boolean (value, nm_setting_ip_config_get_ignore_auto_dns (setting));
-		break;
-	case PROP_DHCP_HOSTNAME:
-		g_value_set_string (value, nm_setting_ip_config_get_dhcp_hostname (setting));
-		break;
-	case PROP_DHCP_SEND_HOSTNAME:
-		g_value_set_boolean (value, nm_setting_ip_config_get_dhcp_send_hostname (setting));
-		break;
-	case PROP_NEVER_DEFAULT:
-		g_value_set_boolean (value, priv->never_default);
-		break;
-	case PROP_MAY_FAIL:
-		g_value_set_boolean (value, priv->may_fail);
-		break;
-	case PROP_DAD_TIMEOUT:
-		g_value_set_int (value, nm_setting_ip_config_get_dad_timeout (setting));
-		break;
-	case PROP_DHCP_TIMEOUT:
-		g_value_set_int (value, nm_setting_ip_config_get_dhcp_timeout (setting));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	priv->dns = g_ptr_array_new_with_free_func (g_free);
+	priv->dns_search = g_ptr_array_new_with_free_func (g_free);
+	priv->dns_options = NULL;
+	priv->addresses = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_address_unref);
+	priv->routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_route_unref);
 }
 
-static gboolean
-ip_gateway_set (NMSetting  *setting,
-                GVariant   *connection_dict,
-                const char *property,
-                GVariant   *value,
-                NMSettingParseFlags parse_flags,
-                GError    **error)
+static void
+finalize (GObject *object)
 {
-	/* FIXME: properly handle errors */
-
-	/* Don't set from 'gateway' if we're going to use the gateway in 'addresses' */
-	if (_nm_setting_use_legacy_property (setting, connection_dict, "addresses", "gateway"))
-		return TRUE;
-
-	g_object_set (setting, property, g_variant_get_string (value, NULL), NULL);
-	return TRUE;
-}
+	NMSettingIPConfig *self = NM_SETTING_IP_CONFIG (object);
+	NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (self);
 
-GArray *
-_nm_sett_info_property_override_create_array_ip_config (void)
-{
-	nm_auto_unref_gtypeclass NMSettingClass *setting_class = g_type_class_ref (NM_TYPE_SETTING_IP_CONFIG);
-	GArray *properties_override = _nm_sett_info_property_override_create_array ();
+	g_free (priv->method);
+	g_free (priv->gateway);
+	g_free (priv->dhcp_hostname);
 
-	_properties_override_add_override (properties_override,
-	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                 NM_SETTING_IP_CONFIG_GATEWAY),
-	                                   G_VARIANT_TYPE_STRING,
-	                                   NULL,
-	                                   ip_gateway_set,
-	                                   NULL);
+	g_ptr_array_unref (priv->dns);
+	g_ptr_array_unref (priv->dns_search);
+	if (priv->dns_options)
+		g_ptr_array_unref (priv->dns_options);
+	g_ptr_array_unref (priv->addresses);
+	g_ptr_array_unref (priv->routes);
 
-	return properties_override;
+	G_OBJECT_CLASS (nm_setting_ip_config_parent_class)->finalize (object);
 }
 
 static void
@@ -2901,8 +2912,8 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingIPConfigPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify           = verify;
@@ -2931,25 +2942,23 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * shared method must be configured on the interface which shares the internet
 	 * to a subnet, not on the uplink which is shared.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_METHOD,
-		 g_param_spec_string (NM_SETTING_IP_CONFIG_METHOD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_METHOD] =
+	    g_param_spec_string (NM_SETTING_IP_CONFIG_METHOD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:dns:
 	 *
 	 * Array of IP addresses of DNS servers.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_DNS,
-		 g_param_spec_boxed (NM_SETTING_IP_CONFIG_DNS, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DNS] =
+	    g_param_spec_boxed (NM_SETTING_IP_CONFIG_DNS, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:dns-search:
@@ -2959,12 +2968,11 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * interface over which a query must be forwarded; they are not used
 	 * to complete unqualified host names.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_DNS_SEARCH,
-		 g_param_spec_boxed (NM_SETTING_IP_CONFIG_DNS_SEARCH, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DNS_SEARCH] =
+	    g_param_spec_boxed (NM_SETTING_IP_CONFIG_DNS_SEARCH, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:dns-options:
@@ -2977,12 +2985,11 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_DNS_OPTIONS,
-		 g_param_spec_boxed (NM_SETTING_IP_CONFIG_DNS_OPTIONS, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DNS_OPTIONS] =
+	    g_param_spec_boxed (NM_SETTING_IP_CONFIG_DNS_OPTIONS, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:dns-priority:
@@ -2990,8 +2997,9 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * DNS servers priority.
 	 *
 	 * The relative priority for DNS servers specified by this setting.  A lower
-	 * value is better (higher priority).  Zero selects the default value, which
-	 * is 50 for VPNs and 100 for other connections.
+	 * value is better (higher priority). Zero selects a globally configured
+	 * default value. If the latter is missing or zero too, it defaults to
+	 * 50 for VPNs and 100 for other connections.
 	 *
 	 * Note that the priority is to order DNS settings for multiple active
 	 * connections.  It does not disambiguate multiple DNS servers within the
@@ -3018,31 +3026,29 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 *
 	 * Since: 1.4
 	 **/
-	g_object_class_install_property
-	    (object_class, PROP_DNS_PRIORITY,
+	obj_properties[PROP_DNS_PRIORITY] =
 	     g_param_spec_int (NM_SETTING_IP_CONFIG_DNS_PRIORITY, "", "",
 	                       G_MININT32, G_MAXINT32, 0,
 	                       G_PARAM_READWRITE |
 	                       G_PARAM_CONSTRUCT |
-	                       G_PARAM_STATIC_STRINGS));
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:addresses: (type GPtrArray(NMIPAddress))
 	 *
 	 * Array of IP addresses.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_ADDRESSES,
-		 g_param_spec_boxed (NM_SETTING_IP_CONFIG_ADDRESSES, "", "",
-		                     G_TYPE_PTR_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     /* "addresses" is a legacy D-Bus property, because the
-		                      * "addresses" GObject property normally gets set from
-		                      * the "address-data" D-Bus property...
-		                      */
-		                     NM_SETTING_PARAM_LEGACY |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ADDRESSES] =
+	    g_param_spec_boxed (NM_SETTING_IP_CONFIG_ADDRESSES, "", "",
+	                        G_TYPE_PTR_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        /* "addresses" is a legacy D-Bus property, because the
+	                         * "addresses" GObject property normally gets set from
+	                         * the "address-data" D-Bus property...
+	                         */
+	                        NM_SETTING_PARAM_LEGACY |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:gateway:
@@ -3050,28 +3056,26 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * The gateway associated with this configuration. This is only meaningful
 	 * if #NMSettingIPConfig:addresses is also set.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_GATEWAY,
-		 g_param_spec_string (NM_SETTING_IP_CONFIG_GATEWAY, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_GATEWAY] =
+	    g_param_spec_string (NM_SETTING_IP_CONFIG_GATEWAY, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:routes: (type GPtrArray(NMIPRoute))
 	 *
 	 * Array of IP routes.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_ROUTES,
-		 g_param_spec_boxed (NM_SETTING_IP_CONFIG_ROUTES, "", "",
-		                     G_TYPE_PTR_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     /* See :addresses above Re: LEGACY */
-		                     NM_SETTING_PARAM_LEGACY |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ROUTES] =
+	    g_param_spec_boxed (NM_SETTING_IP_CONFIG_ROUTES, "", "",
+	                        G_TYPE_PTR_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        /* See :addresses above Re: LEGACY */
+	                        NM_SETTING_PARAM_LEGACY |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:route-metric:
@@ -3087,13 +3091,12 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * mean setting it to 1024.
 	 * For IPv4, zero is a regular value for the metric.
 	 **/
-	g_object_class_install_property
-	    (object_class, PROP_ROUTE_METRIC,
+	obj_properties[PROP_ROUTE_METRIC] =
 	     g_param_spec_int64 (NM_SETTING_IP_CONFIG_ROUTE_METRIC, "", "",
 	                         -1, G_MAXUINT32, -1,
 	                         G_PARAM_READWRITE |
 	                         G_PARAM_CONSTRUCT |
-	                         G_PARAM_STATIC_STRINGS));
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:route-table:
@@ -3116,13 +3119,12 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_ROUTE_TABLE,
-		 g_param_spec_uint (NM_SETTING_IP_CONFIG_ROUTE_TABLE, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ROUTE_TABLE] =
+	    g_param_spec_uint (NM_SETTING_IP_CONFIG_ROUTE_TABLE, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 	/**
 	 * NMSettingIPConfig:ignore-auto-routes:
 	 *
@@ -3130,13 +3132,12 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * %TRUE, automatically configured routes are ignored and only routes
 	 * specified in the #NMSettingIPConfig:routes property, if any, are used.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_IGNORE_AUTO_ROUTES,
-		 g_param_spec_boolean (NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_IGNORE_AUTO_ROUTES] =
+	    g_param_spec_boolean (NM_SETTING_IP_CONFIG_IGNORE_AUTO_ROUTES, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:ignore-auto-dns:
@@ -3147,13 +3148,12 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * #NMSettingIPConfig:dns and #NMSettingIPConfig:dns-search properties, if
 	 * any, are used.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_IGNORE_AUTO_DNS,
-		 g_param_spec_boolean (NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_IGNORE_AUTO_DNS] =
+	    g_param_spec_boolean (NM_SETTING_IP_CONFIG_IGNORE_AUTO_DNS, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:dhcp-hostname:
@@ -3163,12 +3163,11 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * This property and #NMSettingIP4Config:dhcp-fqdn are mutually exclusive and
 	 * cannot be set at the same time.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_DHCP_HOSTNAME,
-		 g_param_spec_string (NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DHCP_HOSTNAME] =
+	    g_param_spec_string (NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:dhcp-send-hostname:
@@ -3179,13 +3178,12 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * #NMSettingIPConfig:dhcp-hostname property is %NULL and this property is
 	 * %TRUE, the current persistent hostname of the computer is sent.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_DHCP_SEND_HOSTNAME,
-		 g_param_spec_boolean (NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, "", "",
-		                       TRUE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DHCP_SEND_HOSTNAME] =
+	    g_param_spec_boolean (NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, "", "",
+	                          TRUE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:never-default:
@@ -3194,13 +3192,12 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * IP type, meaning it will never be assigned the default route by
 	 * NetworkManager.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NEVER_DEFAULT,
-		 g_param_spec_boolean (NM_SETTING_IP_CONFIG_NEVER_DEFAULT, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NEVER_DEFAULT] =
+	    g_param_spec_boolean (NM_SETTING_IP_CONFIG_NEVER_DEFAULT, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:may-fail:
@@ -3213,13 +3210,12 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 * to succeed if IPv4 configuration fails but IPv6 configuration completes
 	 * successfully.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_MAY_FAIL,
-		 g_param_spec_boolean (NM_SETTING_IP_CONFIG_MAY_FAIL, "", "",
-		                       TRUE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MAY_FAIL] =
+	    g_param_spec_boolean (NM_SETTING_IP_CONFIG_MAY_FAIL, "", "",
+	                          TRUE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:dad-timeout:
@@ -3235,25 +3231,25 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_DAD_TIMEOUT,
-		 g_param_spec_int (NM_SETTING_IP_CONFIG_DAD_TIMEOUT, "", "",
-		                    -1, NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX, -1,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DAD_TIMEOUT] =
+	    g_param_spec_int (NM_SETTING_IP_CONFIG_DAD_TIMEOUT, "", "",
+	                       -1, NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX, -1,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPConfig:dhcp-timeout:
 	 *
 	 * A timeout for a DHCP transaction in seconds.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_DHCP_TIMEOUT,
-		 g_param_spec_int (NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, "", "",
-		                   0, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   NM_SETTING_PARAM_FUZZY_IGNORE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DHCP_TIMEOUT] =
+	    g_param_spec_int (NM_SETTING_IP_CONFIG_DHCP_TIMEOUT, "", "",
+	                      0, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      NM_SETTING_PARAM_FUZZY_IGNORE |
+	                      G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 }
diff --git a/libnm-core/nm-setting-ip-config.h b/libnm-core/nm-setting-ip-config.h
index 882a88b6..39cb36a4 100644
--- a/libnm-core/nm-setting-ip-config.h
+++ b/libnm-core/nm-setting-ip-config.h
@@ -94,9 +94,9 @@ void         nm_ip_route_unref               (NMIPRoute  *route);
 gboolean     nm_ip_route_equal               (NMIPRoute  *route,
                                               NMIPRoute  *other);
 
-enum {
+enum { /*< flags >*/
 	NM_IP_ROUTE_EQUAL_CMP_FLAGS_NONE         = 0,
-	NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS   = (1LL <<  0),
+	NM_IP_ROUTE_EQUAL_CMP_FLAGS_WITH_ATTRS   = 0x1,
 };
 
 NM_AVAILABLE_IN_1_10
diff --git a/libnm-core/nm-setting-ip-tunnel.c b/libnm-core/nm-setting-ip-tunnel.c
index 41314f58..9fdb5481 100644
--- a/libnm-core/nm-setting-ip-tunnel.c
+++ b/libnm-core/nm-setting-ip-tunnel.c
@@ -30,9 +30,23 @@
  * @short_description: Describes connection properties for IP tunnel devices
  **/
 
-G_DEFINE_TYPE (NMSettingIPTunnel, nm_setting_ip_tunnel, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_IP_TUNNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP_TUNNEL, NMSettingIPTunnelPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PARENT,
+	PROP_MODE,
+	PROP_LOCAL,
+	PROP_REMOTE,
+	PROP_TTL,
+	PROP_TOS,
+	PROP_PATH_MTU_DISCOVERY,
+	PROP_INPUT_KEY,
+	PROP_OUTPUT_KEY,
+	PROP_ENCAPSULATION_LIMIT,
+	PROP_FLOW_LABEL,
+	PROP_MTU,
+	PROP_FLAGS,
+);
 
 typedef struct {
 	char *parent;
@@ -50,24 +64,11 @@ typedef struct {
 	guint32 flags;
 } NMSettingIPTunnelPrivate;
 
-enum {
-	PROP_0,
-	PROP_PARENT,
-	PROP_MODE,
-	PROP_LOCAL,
-	PROP_REMOTE,
-	PROP_TTL,
-	PROP_TOS,
-	PROP_PATH_MTU_DISCOVERY,
-	PROP_INPUT_KEY,
-	PROP_OUTPUT_KEY,
-	PROP_ENCAPSULATION_LIMIT,
-	PROP_FLOW_LABEL,
-	PROP_MTU,
-	PROP_FLAGS,
+G_DEFINE_TYPE (NMSettingIPTunnel, nm_setting_ip_tunnel, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_IP_TUNNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP_TUNNEL, NMSettingIPTunnelPrivate))
+
+/*****************************************************************************/
 
 /**
  * nm_setting_ip_tunnel_get_parent:
@@ -474,77 +475,54 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-/**
- * nm_setting_ip_tunnel_new:
- *
- * Creates a new #NMSettingIPTunnel object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingIPTunnel object
- *
- * Since: 1.2
- **/
-NMSetting *
-nm_setting_ip_tunnel_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP_TUNNEL, NULL);
-}
-
-static void
-nm_setting_ip_tunnel_init (NMSettingIPTunnel *setting)
-{
-}
+/*****************************************************************************/
 
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSettingIPTunnel *setting = NM_SETTING_IP_TUNNEL (object);
 	NMSettingIPTunnelPrivate *priv = NM_SETTING_IP_TUNNEL_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_free (priv->parent);
-		priv->parent = g_value_dup_string (value);
+		g_value_set_string (value, priv->parent);
 		break;
 	case PROP_MODE:
-		priv->mode = g_value_get_uint (value);
+		g_value_set_uint (value, priv->mode);
 		break;
 	case PROP_LOCAL:
-		g_free (priv->local);
-		priv->local = g_value_dup_string (value);
+		g_value_set_string (value, priv->local);
 		break;
 	case PROP_REMOTE:
-		g_free (priv->remote);
-		priv->remote = g_value_dup_string (value);
+		g_value_set_string (value, priv->remote);
 		break;
 	case PROP_TTL:
-		priv->ttl = g_value_get_uint (value);
+		g_value_set_uint (value, priv->ttl);
 		break;
 	case PROP_TOS:
-		priv->tos = g_value_get_uint (value);
+		g_value_set_uint (value, priv->tos);
 		break;
 	case PROP_PATH_MTU_DISCOVERY:
-		priv->path_mtu_discovery = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->path_mtu_discovery);
 		break;
 	case PROP_INPUT_KEY:
-		g_free (priv->input_key);
-		priv->input_key = g_value_dup_string (value);
+		g_value_set_string (value, priv->input_key);
 		break;
 	case PROP_OUTPUT_KEY:
-		g_free (priv->output_key);
-		priv->output_key = g_value_dup_string (value);
+		g_value_set_string (value, priv->output_key);
 		break;
 	case PROP_ENCAPSULATION_LIMIT:
-		priv->encapsulation_limit = g_value_get_uint (value);
+		g_value_set_uint (value, priv->encapsulation_limit);
 		break;
 	case PROP_FLOW_LABEL:
-		priv->flow_label = g_value_get_uint (value);
+		g_value_set_uint (value, priv->flow_label);
 		break;
 	case PROP_MTU:
-		priv->mtu = g_value_get_uint (value);
+		g_value_set_uint (value, priv->mtu);
 		break;
 	case PROP_FLAGS:
-		priv->flags = g_value_get_uint (value);
+		g_value_set_uint (value, priv->flags);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -553,51 +531,56 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSettingIPTunnel *setting = NM_SETTING_IP_TUNNEL (object);
 	NMSettingIPTunnelPrivate *priv = NM_SETTING_IP_TUNNEL_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_value_set_string (value, priv->parent);
+		g_free (priv->parent);
+		priv->parent = g_value_dup_string (value);
 		break;
 	case PROP_MODE:
-		g_value_set_uint (value, priv->mode);
+		priv->mode = g_value_get_uint (value);
 		break;
 	case PROP_LOCAL:
-		g_value_set_string (value, priv->local);
+		g_free (priv->local);
+		priv->local = g_value_dup_string (value);
 		break;
 	case PROP_REMOTE:
-		g_value_set_string (value, priv->remote);
+		g_free (priv->remote);
+		priv->remote = g_value_dup_string (value);
 		break;
 	case PROP_TTL:
-		g_value_set_uint (value, priv->ttl);
+		priv->ttl = g_value_get_uint (value);
 		break;
 	case PROP_TOS:
-		g_value_set_uint (value, priv->tos);
+		priv->tos = g_value_get_uint (value);
 		break;
 	case PROP_PATH_MTU_DISCOVERY:
-		g_value_set_boolean (value, priv->path_mtu_discovery);
+		priv->path_mtu_discovery = g_value_get_boolean (value);
 		break;
 	case PROP_INPUT_KEY:
-		g_value_set_string (value, priv->input_key);
+		g_free (priv->input_key);
+		priv->input_key = g_value_dup_string (value);
 		break;
 	case PROP_OUTPUT_KEY:
-		g_value_set_string (value, priv->output_key);
+		g_free (priv->output_key);
+		priv->output_key = g_value_dup_string (value);
 		break;
 	case PROP_ENCAPSULATION_LIMIT:
-		g_value_set_uint (value, priv->encapsulation_limit);
+		priv->encapsulation_limit = g_value_get_uint (value);
 		break;
 	case PROP_FLOW_LABEL:
-		g_value_set_uint (value, priv->flow_label);
+		priv->flow_label = g_value_get_uint (value);
 		break;
 	case PROP_MTU:
-		g_value_set_uint (value, priv->mtu);
+		priv->mtu = g_value_get_uint (value);
 		break;
 	case PROP_FLAGS:
-		g_value_set_uint (value, priv->flags);
+		priv->flags = g_value_get_uint (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -605,6 +588,28 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_ip_tunnel_init (NMSettingIPTunnel *setting)
+{
+}
+
+/**
+ * nm_setting_ip_tunnel_new:
+ *
+ * Creates a new #NMSettingIPTunnel object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingIPTunnel object
+ *
+ * Since: 1.2
+ **/
+NMSetting *
+nm_setting_ip_tunnel_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP_TUNNEL, NULL);
+}
+
 static void
 finalize (GObject *object)
 {
@@ -628,8 +633,8 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingIPTunnelPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -643,13 +648,12 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PARENT,
-		 g_param_spec_string (NM_SETTING_IP_TUNNEL_PARENT, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PARENT] =
+	    g_param_spec_string (NM_SETTING_IP_TUNNEL_PARENT, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:mode:
@@ -659,13 +663,12 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_MODE,
-		 g_param_spec_uint (NM_SETTING_IP_TUNNEL_MODE, "", "",
-		                    0, G_MAXUINT, 0,
-		                    G_PARAM_READWRITE |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MODE] =
+	    g_param_spec_uint (NM_SETTING_IP_TUNNEL_MODE, "", "",
+	                       0, G_MAXUINT, 0,
+	                       G_PARAM_READWRITE |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:local:
@@ -675,13 +678,12 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_LOCAL,
-		 g_param_spec_string (NM_SETTING_IP_TUNNEL_LOCAL, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LOCAL] =
+	    g_param_spec_string (NM_SETTING_IP_TUNNEL_LOCAL, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:remote:
@@ -691,13 +693,12 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_REMOTE,
-		 g_param_spec_string (NM_SETTING_IP_TUNNEL_REMOTE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_REMOTE] =
+	    g_param_spec_string (NM_SETTING_IP_TUNNEL_REMOTE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:ttl
@@ -707,14 +708,13 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_TTL,
-		 g_param_spec_uint (NM_SETTING_IP_TUNNEL_TTL, "", "",
-		                    0, 255, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TTL] =
+	    g_param_spec_uint (NM_SETTING_IP_TUNNEL_TTL, "", "",
+	                       0, 255, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:tos
@@ -724,14 +724,13 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_TOS,
-		 g_param_spec_uint (NM_SETTING_IP_TUNNEL_TOS, "", "",
-		                    0, 255, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TOS] =
+	    g_param_spec_uint (NM_SETTING_IP_TUNNEL_TOS, "", "",
+	                       0, 255, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:path-mtu-discovery
@@ -740,14 +739,13 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PATH_MTU_DISCOVERY,
-		 g_param_spec_boolean (NM_SETTING_IP_TUNNEL_PATH_MTU_DISCOVERY, "", "",
-		                       TRUE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PATH_MTU_DISCOVERY] =
+	    g_param_spec_boolean (NM_SETTING_IP_TUNNEL_PATH_MTU_DISCOVERY, "", "",
+	                          TRUE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:input-key:
@@ -757,13 +755,12 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_INPUT_KEY,
-		 g_param_spec_string (NM_SETTING_IP_TUNNEL_INPUT_KEY, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_INPUT_KEY] =
+	    g_param_spec_string (NM_SETTING_IP_TUNNEL_INPUT_KEY, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:output-key:
@@ -773,13 +770,12 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_OUTPUT_KEY,
-		 g_param_spec_string (NM_SETTING_IP_TUNNEL_OUTPUT_KEY, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_OUTPUT_KEY] =
+	    g_param_spec_string (NM_SETTING_IP_TUNNEL_OUTPUT_KEY, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:encapsulation-limit:
@@ -789,14 +785,13 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_ENCAPSULATION_LIMIT,
-		 g_param_spec_uint (NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT, "", "",
-		                    0, 255, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ENCAPSULATION_LIMIT] =
+	    g_param_spec_uint (NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT, "", "",
+	                       0, 255, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:flow-label:
@@ -806,14 +801,13 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_FLOW_LABEL,
-		 g_param_spec_uint (NM_SETTING_IP_TUNNEL_FLOW_LABEL, "", "",
-		                    0, (1 << 20) - 1, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_FLOW_LABEL] =
+	    g_param_spec_uint (NM_SETTING_IP_TUNNEL_FLOW_LABEL, "", "",
+	                       0, (1 << 20) - 1, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:mtu:
@@ -823,14 +817,13 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_MTU,
-		 g_param_spec_uint (NM_SETTING_IP_TUNNEL_MTU, "", "",
-		                    0, G_MAXUINT, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MTU] =
+	    g_param_spec_uint (NM_SETTING_IP_TUNNEL_MTU, "", "",
+	                       0, G_MAXUINT, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIPTunnel:flags:
@@ -843,13 +836,14 @@ nm_setting_ip_tunnel_class_init (NMSettingIPTunnelClass *klass)
 	 *
 	 * 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));
+	obj_properties[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);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_IP_TUNNEL);
 }
diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c
index 36765484..dd3b79b7 100644
--- a/libnm-core/nm-setting-ip4-config.c
+++ b/libnm-core/nm-setting-ip4-config.c
@@ -21,9 +21,8 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-setting-ip4-config.h"
+
 #include "nm-setting-private.h"
 
 /**
@@ -50,35 +49,23 @@
  * connection.
  **/
 
-G_DEFINE_TYPE (NMSettingIP4Config, nm_setting_ip4_config, NM_TYPE_SETTING_IP_CONFIG)
+/*****************************************************************************/
 
-#define NM_SETTING_IP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4ConfigPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_DHCP_CLIENT_ID,
+	PROP_DHCP_FQDN,
+);
 
 typedef struct {
 	char *dhcp_client_id;
 	char *dhcp_fqdn;
 } NMSettingIP4ConfigPrivate;
 
-enum {
-	PROP_0,
-	PROP_DHCP_CLIENT_ID,
-	PROP_DHCP_FQDN,
+G_DEFINE_TYPE (NMSettingIP4Config, nm_setting_ip4_config, NM_TYPE_SETTING_IP_CONFIG)
 
-	LAST_PROP
-};
+#define NM_SETTING_IP4_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP4_CONFIG, NMSettingIP4ConfigPrivate))
 
-/**
- * nm_setting_ip4_config_new:
- *
- * Creates a new #NMSettingIP4Config object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingIP4Config object
- **/
-NMSetting *
-nm_setting_ip4_config_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP4_CONFIG, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_ip4_config_get_dhcp_client_id:
@@ -252,62 +239,6 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static void
-nm_setting_ip4_config_init (NMSettingIP4Config *setting)
-{
-}
-
-static void
-finalize (GObject *object)
-{
-	NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (object);
-
-	g_free (priv->dhcp_client_id);
-	g_free (priv->dhcp_fqdn);
-
-	G_OBJECT_CLASS (nm_setting_ip4_config_parent_class)->finalize (object);
-}
-
-static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
-{
-	NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (object);
-
-	switch (prop_id) {
-	case PROP_DHCP_CLIENT_ID:
-		g_free (priv->dhcp_client_id);
-		priv->dhcp_client_id = g_value_dup_string (value);
-		break;
-	case PROP_DHCP_FQDN:
-		g_free (priv->dhcp_fqdn);
-		priv->dhcp_fqdn = g_value_dup_string (value);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	NMSettingIP4Config *s_ip4 = NM_SETTING_IP4_CONFIG (object);
-
-	switch (prop_id) {
-	case PROP_DHCP_CLIENT_ID:
-		g_value_set_string (value, nm_setting_ip4_config_get_dhcp_client_id (s_ip4));
-		break;
-	case PROP_DHCP_FQDN:
-		g_value_set_string (value, nm_setting_ip4_config_get_dhcp_fqdn (s_ip4));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
 static GVariant *
 ip4_dns_to_dbus (const GValue *prop_value)
 {
@@ -376,9 +307,11 @@ ip4_addresses_set (NMSetting  *setting,
 }
 
 static GVariant *
-ip4_address_labels_get (NMSetting    *setting,
+ip4_address_labels_get (const NMSettInfoSetting *sett_info,
+                        guint property_idx,
                         NMConnection *connection,
-                        const char   *property)
+                        NMSetting *setting,
+                        NMConnectionSerializationFlags flags)
 {
 	NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting);
 	gboolean have_labels = FALSE;
@@ -386,6 +319,9 @@ ip4_address_labels_get (NMSetting    *setting,
 	GVariant *ret;
 	int num_addrs, i;
 
+	if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		return NULL;
+
 	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);
@@ -414,18 +350,19 @@ ip4_address_labels_get (NMSetting    *setting,
 }
 
 static GVariant *
-ip4_address_data_get (NMSetting    *setting,
+ip4_address_data_get (const NMSettInfoSetting *sett_info,
+                      guint property_idx,
                       NMConnection *connection,
-                      const char   *property)
+                      NMSetting *setting,
+                      NMConnectionSerializationFlags flags)
 {
-	GPtrArray *addrs;
-	GVariant *ret;
+	gs_unref_ptrarray GPtrArray *addrs = NULL;
 
-	g_object_get (setting, NM_SETTING_IP_CONFIG_ADDRESSES, &addrs, NULL);
-	ret = nm_utils_ip_addresses_to_variant (addrs);
-	g_ptr_array_unref (addrs);
+	if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		return NULL;
 
-	return ret;
+	g_object_get (setting, NM_SETTING_IP_CONFIG_ADDRESSES, &addrs, NULL);
+	return nm_utils_ip_addresses_to_variant (addrs);
 }
 
 static gboolean
@@ -486,18 +423,19 @@ ip4_routes_set (NMSetting  *setting,
 }
 
 static GVariant *
-ip4_route_data_get (NMSetting    *setting,
+ip4_route_data_get (const NMSettInfoSetting *sett_info,
+                    guint property_idx,
                     NMConnection *connection,
-                    const char   *property)
+                    NMSetting *setting,
+                    NMConnectionSerializationFlags flags)
 {
-	GPtrArray *routes;
-	GVariant *ret;
+	gs_unref_ptrarray GPtrArray *routes = NULL;
 
-	g_object_get (setting, NM_SETTING_IP_CONFIG_ROUTES, &routes, NULL);
-	ret = nm_utils_ip_routes_to_variant (routes);
-	g_ptr_array_unref (routes);
+	if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		return NULL;
 
-	return ret;
+	g_object_get (setting, NM_SETTING_IP_CONFIG_ROUTES, &routes, NULL);
+	return nm_utils_ip_routes_to_variant (routes);
 }
 
 static gboolean
@@ -522,6 +460,79 @@ ip4_route_data_set (NMSetting  *setting,
 	return TRUE;
 }
 
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingIP4Config *s_ip4 = NM_SETTING_IP4_CONFIG (object);
+
+	switch (prop_id) {
+	case PROP_DHCP_CLIENT_ID:
+		g_value_set_string (value, nm_setting_ip4_config_get_dhcp_client_id (s_ip4));
+		break;
+	case PROP_DHCP_FQDN:
+		g_value_set_string (value, nm_setting_ip4_config_get_dhcp_fqdn (s_ip4));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_DHCP_CLIENT_ID:
+		g_free (priv->dhcp_client_id);
+		priv->dhcp_client_id = g_value_dup_string (value);
+		break;
+	case PROP_DHCP_FQDN:
+		g_free (priv->dhcp_fqdn);
+		priv->dhcp_fqdn = g_value_dup_string (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_ip4_config_init (NMSettingIP4Config *setting)
+{
+}
+
+/**
+ * nm_setting_ip4_config_new:
+ *
+ * Creates a new #NMSettingIP4Config object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingIP4Config object
+ **/
+NMSetting *
+nm_setting_ip4_config_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP4_CONFIG, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (object);
+
+	g_free (priv->dhcp_client_id);
+	g_free (priv->dhcp_fqdn);
+
+	G_OBJECT_CLASS (nm_setting_ip4_config_parent_class)->finalize (object);
+}
+
 static void
 nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *klass)
 {
@@ -531,8 +542,8 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *klass)
 
 	g_type_class_add_private (setting_class, sizeof (NMSettingIP4ConfigPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -740,12 +751,11 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *klass)
 	 * example: DHCP_CLIENT_ID=ax-srv-1; DHCP_CLIENT_ID=01:44:44:44:44:44:44
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_DHCP_CLIENT_ID,
-		 g_param_spec_string (NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DHCP_CLIENT_ID] =
+	    g_param_spec_string (NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/* ---ifcfg-rh---
 	 * property: dad-timeout
@@ -784,12 +794,11 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *klass)
 	 * example: DHCP_FQDN=foo.bar.com
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_DHCP_FQDN,
-		 g_param_spec_string (NM_SETTING_IP4_CONFIG_DHCP_FQDN, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DHCP_FQDN] =
+	    g_param_spec_string (NM_SETTING_IP4_CONFIG_DHCP_FQDN, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/* IP4-specific property overrides */
 
@@ -895,6 +904,8 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *klass)
 	                                    ip4_route_data_get,
 	                                    ip4_route_data_set);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_IP4_CONFIG,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c
index fa65be91..d6c085ac 100644
--- a/libnm-core/nm-setting-ip6-config.c
+++ b/libnm-core/nm-setting-ip6-config.c
@@ -23,7 +23,6 @@
 
 #include "nm-setting-ip6-config.h"
 
-#include <string.h>
 #include <arpa/inet.h>
 
 #include "nm-setting-private.h"
@@ -52,9 +51,14 @@
  * supported.
  **/
 
-G_DEFINE_TYPE (NMSettingIP6Config, nm_setting_ip6_config, NM_TYPE_SETTING_IP_CONFIG)
+/*****************************************************************************/
 
-#define NM_SETTING_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6ConfigPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_IP6_PRIVACY,
+	PROP_ADDR_GEN_MODE,
+	PROP_TOKEN,
+	PROP_DHCP_DUID,
+);
 
 typedef struct {
 	NMSettingIP6ConfigPrivacy ip6_privacy;
@@ -63,28 +67,11 @@ typedef struct {
 	char *dhcp_duid;
 } NMSettingIP6ConfigPrivate;
 
-enum {
-	PROP_0,
-	PROP_IP6_PRIVACY,
-	PROP_ADDR_GEN_MODE,
-	PROP_TOKEN,
-	PROP_DHCP_DUID,
+G_DEFINE_TYPE (NMSettingIP6Config, nm_setting_ip6_config, NM_TYPE_SETTING_IP_CONFIG)
 
-	LAST_PROP
-};
+#define NM_SETTING_IP6_CONFIG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_IP6_CONFIG, NMSettingIP6ConfigPrivate))
 
-/**
- * nm_setting_ip6_config_new:
- *
- * Creates a new #NMSettingIP6Config object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingIP6Config object
- **/
-NMSetting *
-nm_setting_ip6_config_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP6_CONFIG, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_ip6_config_get_ip6_privacy:
@@ -312,11 +299,6 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static void
-nm_setting_ip6_config_init (NMSettingIP6Config *setting)
-{
-}
-
 static GVariant *
 ip6_dns_to_dbus (const GValue *prop_value)
 {
@@ -374,18 +356,19 @@ ip6_addresses_set (NMSetting  *setting,
 }
 
 static GVariant *
-ip6_address_data_get (NMSetting    *setting,
+ip6_address_data_get (const NMSettInfoSetting *sett_info,
+                      guint property_idx,
                       NMConnection *connection,
-                      const char   *property)
+                      NMSetting *setting,
+                      NMConnectionSerializationFlags flags)
 {
-	GPtrArray *addrs;
-	GVariant *ret;
+	gs_unref_ptrarray GPtrArray *addrs = NULL;
 
-	g_object_get (setting, NM_SETTING_IP_CONFIG_ADDRESSES, &addrs, NULL);
-	ret = nm_utils_ip_addresses_to_variant (addrs);
-	g_ptr_array_unref (addrs);
+	if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		return NULL;
 
-	return ret;
+	g_object_get (setting, NM_SETTING_IP_CONFIG_ADDRESSES, &addrs, NULL);
+	return nm_utils_ip_addresses_to_variant (addrs);
 }
 
 static gboolean
@@ -446,18 +429,19 @@ ip6_routes_set (NMSetting  *setting,
 }
 
 static GVariant *
-ip6_route_data_get (NMSetting    *setting,
+ip6_route_data_get (const NMSettInfoSetting *sett_info,
+                    guint property_idx,
                     NMConnection *connection,
-                    const char   *property)
+                    NMSetting *setting,
+                    NMConnectionSerializationFlags flags)
 {
-	GPtrArray *routes;
-	GVariant *ret;
+	gs_unref_ptrarray GPtrArray *routes = NULL;
 
-	g_object_get (setting, NM_SETTING_IP_CONFIG_ROUTES, &routes, NULL);
-	ret = nm_utils_ip_routes_to_variant (routes);
-	g_ptr_array_unref (routes);
+	if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		return NULL;
 
-	return ret;
+	g_object_get (setting, NM_SETTING_IP_CONFIG_ROUTES, &routes, NULL);
+	return nm_utils_ip_routes_to_variant (routes);
 }
 
 static gboolean
@@ -482,26 +466,26 @@ ip6_route_data_set (NMSetting  *setting,
 	return TRUE;
 }
 
+/*****************************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_IP6_PRIVACY:
-		priv->ip6_privacy = g_value_get_enum (value);
+		g_value_set_enum (value, priv->ip6_privacy);
 		break;
 	case PROP_ADDR_GEN_MODE:
-		priv->addr_gen_mode = g_value_get_int (value);
+		g_value_set_int (value, priv->addr_gen_mode);
 		break;
 	case PROP_TOKEN:
-		g_free (priv->token);
-		priv->token = g_value_dup_string (value);
+		g_value_set_string (value, priv->token);
 		break;
 	case PROP_DHCP_DUID:
-		g_free (priv->dhcp_duid);
-		priv->dhcp_duid = g_value_dup_string (value);
+		g_value_set_string (value, priv->dhcp_duid);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -510,23 +494,25 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_IP6_PRIVACY:
-		g_value_set_enum (value, priv->ip6_privacy);
+		priv->ip6_privacy = g_value_get_enum (value);
 		break;
 	case PROP_ADDR_GEN_MODE:
-		g_value_set_int (value, priv->addr_gen_mode);
+		priv->addr_gen_mode = g_value_get_int (value);
 		break;
 	case PROP_TOKEN:
-		g_value_set_string (value, priv->token);
+		g_free (priv->token);
+		priv->token = g_value_dup_string (value);
 		break;
 	case PROP_DHCP_DUID:
-		g_value_set_string (value, priv->dhcp_duid);
+		g_free (priv->dhcp_duid);
+		priv->dhcp_duid = g_value_dup_string (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -534,6 +520,26 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_ip6_config_init (NMSettingIP6Config *setting)
+{
+}
+
+/**
+ * nm_setting_ip6_config_new:
+ *
+ * Creates a new #NMSettingIP6Config object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingIP6Config object
+ **/
+NMSetting *
+nm_setting_ip6_config_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_IP6_CONFIG, NULL);
+}
+
 static void
 finalize (GObject *object)
 {
@@ -555,8 +561,8 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingIP6ConfigPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -747,14 +753,13 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *klass)
 	 * example: IPV6_PRIVACY=rfc3041 IPV6_PRIVACY_PREFER_PUBLIC_IP=yes
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_IP6_PRIVACY,
-		 g_param_spec_enum (NM_SETTING_IP6_CONFIG_IP6_PRIVACY, "", "",
-		                    NM_TYPE_SETTING_IP6_CONFIG_PRIVACY,
-		                    NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_IP6_PRIVACY] =
+	    g_param_spec_enum (NM_SETTING_IP6_CONFIG_IP6_PRIVACY, "", "",
+	                       NM_TYPE_SETTING_IP6_CONFIG_PRIVACY,
+	                       NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIP6Config:addr-gen-mode:
@@ -797,14 +802,13 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *klass)
 	 * example: IPV6_ADDR_GEN_MODE=stable-privacy
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_ADDR_GEN_MODE,
-		 g_param_spec_int (NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, "", "",
-		                   G_MININT, G_MAXINT,
-		                   NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_CONSTRUCT |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ADDR_GEN_MODE] =
+	    g_param_spec_int (NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, "", "",
+	                      G_MININT, G_MAXINT,
+	                      NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_CONSTRUCT |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIP6Config:token:
@@ -821,13 +825,12 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *klass)
 	 * 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));
+	obj_properties[PROP_TOKEN] =
+	    g_param_spec_string (NM_SETTING_IP6_CONFIG_TOKEN, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingIP6Config:dhcp-duid:
@@ -874,12 +877,11 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *klass)
 	 * example: DHCPV6_DUID=LL; DHCPV6_DUID=0301deadbeef0001; DHCPV6_DUID=03:01:de:ad:be:ef:00:01
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_DHCP_DUID,
-		 g_param_spec_string (NM_SETTING_IP6_CONFIG_DHCP_DUID, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DHCP_DUID] =
+	    g_param_spec_string (NM_SETTING_IP6_CONFIG_DHCP_DUID, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/* IP6-specific property overrides */
 
@@ -974,6 +976,8 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *klass)
 	                                    ip6_route_data_get,
 	                                    ip6_route_data_set);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_IP6_CONFIG,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-macsec.c b/libnm-core/nm-setting-macsec.c
index 5c468221..040aa17d 100644
--- a/libnm-core/nm-setting-macsec.c
+++ b/libnm-core/nm-setting-macsec.c
@@ -23,7 +23,8 @@
 #include "nm-setting-macsec.h"
 
 #include <stdlib.h>
-#include <string.h>
+
+#include "nm-utils/nm-secret-utils.h"
 
 #include "nm-utils.h"
 #include "nm-core-types-internal.h"
@@ -40,9 +41,19 @@
  * necessary for connection to MACsec (IEEE 802.1AE) interfaces.
  **/
 
-G_DEFINE_TYPE (NMSettingMacsec, nm_setting_macsec, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_MACSEC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_MACSEC, NMSettingMacsecPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PARENT,
+	PROP_MODE,
+	PROP_ENCRYPT,
+	PROP_MKA_CAK,
+	PROP_MKA_CAK_FLAGS,
+	PROP_MKA_CKN,
+	PROP_PORT,
+	PROP_VALIDATION,
+	PROP_SEND_SCI,
+);
 
 typedef struct {
 	char *parent;
@@ -56,32 +67,11 @@ typedef struct {
 	NMSettingMacsecValidation validation;
 } NMSettingMacsecPrivate;
 
-NM_GOBJECT_PROPERTIES_DEFINE_BASE (
-	PROP_PARENT,
-	PROP_MODE,
-	PROP_ENCRYPT,
-	PROP_MKA_CAK,
-	PROP_MKA_CAK_FLAGS,
-	PROP_MKA_CKN,
-	PROP_PORT,
-	PROP_VALIDATION,
-	PROP_SEND_SCI,
-);
+G_DEFINE_TYPE (NMSettingMacsec, nm_setting_macsec, NM_TYPE_SETTING)
 
-/**
- * nm_setting_macsec_new:
- *
- * Creates a new #NMSettingMacsec object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingMacsec object
- *
- * Since: 1.6
- **/
-NMSetting *
-nm_setting_macsec_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_MACSEC, NULL);
-}
+#define NM_SETTING_MACSEC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_MACSEC, NMSettingMacsecPrivate))
+
+/*****************************************************************************/
 
 /**
  * nm_setting_macsec_get_parent:
@@ -393,48 +383,42 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static void
-nm_setting_macsec_init (NMSettingMacsec *setting)
-{
-}
+/*****************************************************************************/
 
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSettingMacsec *setting = NM_SETTING_MACSEC (object);
 	NMSettingMacsecPrivate *priv = NM_SETTING_MACSEC_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_free (priv->parent);
-		priv->parent = g_value_dup_string (value);
+		g_value_set_string (value, priv->parent);
 		break;
 	case PROP_MODE:
-		priv->mode = g_value_get_int (value);
+		g_value_set_int (value, priv->mode);
 		break;
 	case PROP_ENCRYPT:
-		priv->encrypt = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->encrypt);
 		break;
 	case PROP_MKA_CAK:
-		g_free (priv->mka_cak);
-		priv->mka_cak = g_value_dup_string (value);
+		g_value_set_string (value, priv->mka_cak);
 		break;
 	case PROP_MKA_CAK_FLAGS:
-		priv->mka_cak_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->mka_cak_flags);
 		break;
 	case PROP_MKA_CKN:
-		g_free (priv->mka_ckn);
-		priv->mka_ckn = g_value_dup_string (value);
+		g_value_set_string (value, priv->mka_ckn);
 		break;
 	case PROP_PORT:
-		priv->port = g_value_get_int (value);
+		g_value_set_int (value, priv->port);
 		break;
 	case PROP_VALIDATION:
-		priv->validation = g_value_get_int (value);
+		g_value_set_int (value, priv->validation);
 		break;
 	case PROP_SEND_SCI:
-		priv->send_sci = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->send_sci);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -443,39 +427,42 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSettingMacsec *setting = NM_SETTING_MACSEC (object);
 	NMSettingMacsecPrivate *priv = NM_SETTING_MACSEC_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_value_set_string (value, priv->parent);
+		g_free (priv->parent);
+		priv->parent = g_value_dup_string (value);
 		break;
 	case PROP_MODE:
-		g_value_set_int (value, priv->mode);
+		priv->mode = g_value_get_int (value);
 		break;
 	case PROP_ENCRYPT:
-		g_value_set_boolean (value, priv->encrypt);
+		priv->encrypt = g_value_get_boolean (value);
 		break;
 	case PROP_MKA_CAK:
-		g_value_set_string (value, priv->mka_cak);
+		nm_free_secret (priv->mka_cak);
+		priv->mka_cak = g_value_dup_string (value);
 		break;
 	case PROP_MKA_CAK_FLAGS:
-		g_value_set_flags (value, priv->mka_cak_flags);
+		priv->mka_cak_flags = g_value_get_flags (value);
 		break;
 	case PROP_MKA_CKN:
-		g_value_set_string (value, priv->mka_ckn);
+		g_free (priv->mka_ckn);
+		priv->mka_ckn = g_value_dup_string (value);
 		break;
 	case PROP_PORT:
-		g_value_set_int (value, priv->port);
+		priv->port = g_value_get_int (value);
 		break;
 	case PROP_VALIDATION:
-		g_value_set_int (value, priv->validation);
+		priv->validation = g_value_get_int (value);
 		break;
 	case PROP_SEND_SCI:
-		g_value_set_boolean (value, priv->send_sci);
+		priv->send_sci = g_value_get_boolean (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -483,6 +470,28 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_macsec_init (NMSettingMacsec *setting)
+{
+}
+
+/**
+ * nm_setting_macsec_new:
+ *
+ * Creates a new #NMSettingMacsec object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingMacsec object
+ *
+ * Since: 1.6
+ **/
+NMSetting *
+nm_setting_macsec_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_MACSEC, NULL);
+}
+
 static void
 finalize (GObject *object)
 {
@@ -490,10 +499,7 @@ finalize (GObject *object)
 	NMSettingMacsecPrivate *priv = NM_SETTING_MACSEC_GET_PRIVATE (setting);
 
 	g_free (priv->parent);
-	if (priv->mka_cak) {
-		memset (priv->mka_cak, 0, strlen (priv->mka_cak));
-		g_free (priv->mka_cak);
-	}
+	nm_free_secret (priv->mka_cak);
 	g_free (priv->mka_ckn);
 
 	G_OBJECT_CLASS (nm_setting_macsec_parent_class)->finalize (object);
@@ -507,8 +513,8 @@ nm_setting_macsec_class_init (NMSettingMacsecClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingMacsecPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify       = verify;
diff --git a/libnm-core/nm-setting-macvlan.c b/libnm-core/nm-setting-macvlan.c
index 1fc16116..332dc007 100644
--- a/libnm-core/nm-setting-macvlan.c
+++ b/libnm-core/nm-setting-macvlan.c
@@ -21,10 +21,10 @@
 
 #include "nm-default.h"
 
+#include "nm-setting-macvlan.h"
+
 #include <stdlib.h>
-#include <string.h>
 
-#include "nm-setting-macvlan.h"
 #include "nm-utils.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-private.h"
@@ -39,9 +39,14 @@
  * necessary for connection to macvlan interfaces.
  **/
 
-G_DEFINE_TYPE (NMSettingMacvlan, nm_setting_macvlan, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_MACVLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_MACVLAN, NMSettingMacvlanPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PARENT,
+	PROP_MODE,
+	PROP_PROMISCUOUS,
+	PROP_TAP,
+);
 
 typedef struct {
 	char *parent;
@@ -50,29 +55,11 @@ typedef struct {
 	gboolean tap;
 } NMSettingMacvlanPrivate;
 
-enum {
-	PROP_0,
-	PROP_PARENT,
-	PROP_MODE,
-	PROP_PROMISCUOUS,
-	PROP_TAP,
-	LAST_PROP
-};
+G_DEFINE_TYPE (NMSettingMacvlan, nm_setting_macvlan, NM_TYPE_SETTING)
 
-/**
- * nm_setting_macvlan_new:
- *
- * Creates a new #NMSettingMacvlan object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingMacvlan object
- *
- * Since: 1.2
- **/
-NMSetting *
-nm_setting_macvlan_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_MACVLAN, NULL);
-}
+#define NM_SETTING_MACVLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_MACVLAN, NMSettingMacvlanPrivate))
+
+/*****************************************************************************/
 
 /**
  * nm_setting_macvlan_get_parent:
@@ -136,11 +123,6 @@ nm_setting_macvlan_get_tap (NMSettingMacvlan *setting)
 
 /*****************************************************************************/
 
-static void
-nm_setting_macvlan_init (NMSettingMacvlan *setting)
-{
-}
-
 static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
@@ -193,26 +175,27 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
+/*****************************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSettingMacvlan *setting = NM_SETTING_MACVLAN (object);
 	NMSettingMacvlanPrivate *priv = NM_SETTING_MACVLAN_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_free (priv->parent);
-		priv->parent = g_value_dup_string (value);
+		g_value_set_string (value, priv->parent);
 		break;
 	case PROP_MODE:
-		priv->mode = g_value_get_uint (value);
+		g_value_set_uint (value, priv->mode);
 		break;
 	case PROP_PROMISCUOUS:
-		priv->promiscuous = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->promiscuous);
 		break;
 	case PROP_TAP:
-		priv->tap = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->tap);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -221,24 +204,25 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSettingMacvlan *setting = NM_SETTING_MACVLAN (object);
 	NMSettingMacvlanPrivate *priv = NM_SETTING_MACVLAN_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_value_set_string (value, priv->parent);
+		g_free (priv->parent);
+		priv->parent = g_value_dup_string (value);
 		break;
 	case PROP_MODE:
-		g_value_set_uint (value, priv->mode);
+		priv->mode = g_value_get_uint (value);
 		break;
 	case PROP_PROMISCUOUS:
-		g_value_set_boolean (value, priv->promiscuous);
+		priv->promiscuous = g_value_get_boolean (value);
 		break;
 	case PROP_TAP:
-		g_value_set_boolean (value, priv->tap);
+		priv->tap = g_value_get_boolean (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -246,6 +230,28 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_macvlan_init (NMSettingMacvlan *setting)
+{
+}
+
+/**
+ * nm_setting_macvlan_new:
+ *
+ * Creates a new #NMSettingMacvlan object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingMacvlan object
+ *
+ * Since: 1.2
+ **/
+NMSetting *
+nm_setting_macvlan_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_MACVLAN, NULL);
+}
+
 static void
 finalize (GObject *object)
 {
@@ -265,8 +271,8 @@ nm_setting_macvlan_class_init (NMSettingMacvlanClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingMacvlanPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -281,14 +287,13 @@ nm_setting_macvlan_class_init (NMSettingMacvlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PARENT,
-		 g_param_spec_string (NM_SETTING_MACVLAN_PARENT, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PARENT] =
+	    g_param_spec_string (NM_SETTING_MACVLAN_PARENT, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingMacvlan:mode:
@@ -298,14 +303,13 @@ nm_setting_macvlan_class_init (NMSettingMacvlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_MODE,
-		 g_param_spec_uint (NM_SETTING_MACVLAN_MODE, "", "",
-		                    0, G_MAXUINT, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MODE] =
+	    g_param_spec_uint (NM_SETTING_MACVLAN_MODE, "", "",
+	                       0, G_MAXUINT, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingMacvlan:promiscuous:
@@ -314,14 +318,13 @@ nm_setting_macvlan_class_init (NMSettingMacvlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PROMISCUOUS,
-		 g_param_spec_boolean (NM_SETTING_MACVLAN_PROMISCUOUS, "", "",
-		                       TRUE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PROMISCUOUS] =
+	    g_param_spec_boolean (NM_SETTING_MACVLAN_PROMISCUOUS, "", "",
+	                          TRUE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingMacvlan:tap:
@@ -330,14 +333,15 @@ nm_setting_macvlan_class_init (NMSettingMacvlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_TAP,
-		 g_param_spec_boolean (NM_SETTING_MACVLAN_TAP, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TAP] =
+	    g_param_spec_boolean (NM_SETTING_MACVLAN_TAP, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_MACVLAN);
 }
diff --git a/libnm-core/nm-setting-match.c b/libnm-core/nm-setting-match.c
index 0964c644..2f331060 100644
--- a/libnm-core/nm-setting-match.c
+++ b/libnm-core/nm-setting-match.c
@@ -15,6 +15,7 @@
 #include "nm-default.h"
 
 #include "nm-setting-match.h"
+
 #include "nm-setting-private.h"
 #include "nm-utils-private.h"
 
@@ -24,6 +25,12 @@
  * @include: nm-setting-match.h
  **/
 
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingMatch,
+	PROP_INTERFACE_NAME,
+);
+
 /**
  * NMSettingMatch:
  *
@@ -42,10 +49,6 @@ struct _NMSettingMatchClass {
 
 G_DEFINE_TYPE (NMSettingMatch, nm_setting_match, NM_TYPE_SETTING)
 
-NM_GOBJECT_PROPERTIES_DEFINE (NMSettingMatch,
-	PROP_INTERFACE_NAME,
-);
-
 /*****************************************************************************/
 
 /**
@@ -194,6 +197,8 @@ nm_setting_match_get_interface_names (NMSettingMatch *setting, guint *length)
 	return (const char *const *) setting->interface_name->pdata;
 }
 
+/*****************************************************************************/
+
 static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
@@ -227,6 +232,8 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
 nm_setting_match_init (NMSettingMatch *setting)
 {
@@ -264,9 +271,9 @@ nm_setting_match_class_init (NMSettingMatchClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	object_class->finalize     = finalize;
 	object_class->get_property = get_property;
 	object_class->set_property = set_property;
+	object_class->finalize     = finalize;
 
 	/**
 	 * NMSettingMatch:interface-name
diff --git a/libnm-core/nm-setting-olpc-mesh.c b/libnm-core/nm-setting-olpc-mesh.c
index 7f4a1bbf..f6a32dbb 100644
--- a/libnm-core/nm-setting-olpc-mesh.c
+++ b/libnm-core/nm-setting-olpc-mesh.c
@@ -22,9 +22,8 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-setting-olpc-mesh.h"
+
 #include "nm-utils.h"
 #include "nm-utils-private.h"
 #include "nm-setting-private.h"
@@ -37,11 +36,13 @@
  * necessary for connection to OLPC-Mesh devices.
  **/
 
-static void nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting);
-
-G_DEFINE_TYPE (NMSettingOlpcMesh, nm_setting_olpc_mesh, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_SSID,
+	PROP_CHANNEL,
+	PROP_DHCP_ANYCAST_ADDRESS,
+);
 
 typedef struct {
 	GBytes *ssid;
@@ -49,31 +50,11 @@ typedef struct {
 	char *dhcp_anycast_addr;
 } NMSettingOlpcMeshPrivate;
 
-enum {
-	PROP_0,
-	PROP_SSID,
-	PROP_CHANNEL,
-	PROP_DHCP_ANYCAST_ADDRESS,
-
-	LAST_PROP
-};
+G_DEFINE_TYPE (NMSettingOlpcMesh, nm_setting_olpc_mesh, NM_TYPE_SETTING)
 
-/**
- * nm_setting_olpc_mesh_new:
- *
- * Creates a new #NMSettingOlpcMesh object with default values.
- *
- * Returns: the new empty #NMSettingOlpcMesh object
- **/
-NMSetting *nm_setting_olpc_mesh_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_OLPC_MESH, NULL);
-}
+#define NM_SETTING_OLPC_MESH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_OLPC_MESH, NMSettingOlpcMeshPrivate))
 
-static void
-nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting)
-{
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_olpc_mesh_get_ssid:
@@ -152,16 +133,28 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
+/*****************************************************************************/
+
 static void
-finalize (GObject *object)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (object);
-
-	if (priv->ssid)
-		g_bytes_unref (priv->ssid);
-	g_free (priv->dhcp_anycast_addr);
+	NMSettingOlpcMesh *setting = NM_SETTING_OLPC_MESH (object);
 
-	G_OBJECT_CLASS (nm_setting_olpc_mesh_parent_class)->finalize (object);
+	switch (prop_id) {
+	case PROP_SSID:
+		g_value_set_boxed (value, nm_setting_olpc_mesh_get_ssid (setting));
+		break;
+	case PROP_CHANNEL:
+		g_value_set_uint (value, nm_setting_olpc_mesh_get_channel (setting));
+		break;
+	case PROP_DHCP_ANYCAST_ADDRESS:
+		g_value_set_string (value, nm_setting_olpc_mesh_get_dhcp_anycast_address (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -189,26 +182,35 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_olpc_mesh_init (NMSettingOlpcMesh *setting)
 {
-	NMSettingOlpcMesh *setting = NM_SETTING_OLPC_MESH (object);
+}
 
-	switch (prop_id) {
-	case PROP_SSID:
-		g_value_set_boxed (value, nm_setting_olpc_mesh_get_ssid (setting));
-		break;
-	case PROP_CHANNEL:
-		g_value_set_uint (value, nm_setting_olpc_mesh_get_channel (setting));
-		break;
-	case PROP_DHCP_ANYCAST_ADDRESS:
-		g_value_set_string (value, nm_setting_olpc_mesh_get_dhcp_anycast_address (setting));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+/**
+ * nm_setting_olpc_mesh_new:
+ *
+ * Creates a new #NMSettingOlpcMesh object with default values.
+ *
+ * Returns: the new empty #NMSettingOlpcMesh object
+ **/
+NMSetting *nm_setting_olpc_mesh_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_OLPC_MESH, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE (object);
+
+	if (priv->ssid)
+		g_bytes_unref (priv->ssid);
+	g_free (priv->dhcp_anycast_addr);
+
+	G_OBJECT_CLASS (nm_setting_olpc_mesh_parent_class)->finalize (object);
 }
 
 static void
@@ -220,8 +222,8 @@ nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingOlpcMeshPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -231,27 +233,25 @@ nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *klass)
 	 *
 	 * SSID of the mesh network to join.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_SSID,
-		 g_param_spec_boxed (NM_SETTING_OLPC_MESH_SSID, "", "",
-		                     G_TYPE_BYTES,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SSID] =
+	    g_param_spec_boxed (NM_SETTING_OLPC_MESH_SSID, "", "",
+	                        G_TYPE_BYTES,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingOlpcMesh:channel:
 	 *
 	 * Channel on which the mesh network to join is located.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_CHANNEL,
-		 g_param_spec_uint (NM_SETTING_OLPC_MESH_CHANNEL, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CHANNEL] =
+	    g_param_spec_uint (NM_SETTING_OLPC_MESH_CHANNEL, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingOlpcMesh:dhcp-anycast-address:
@@ -260,20 +260,20 @@ nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *klass)
 	 * The specific anycast address used determines which DHCP server class
 	 * answers the request.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_DHCP_ANYCAST_ADDRESS,
-		 g_param_spec_string (NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DHCP_ANYCAST_ADDRESS] =
+	    g_param_spec_string (NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS),
+	                                    obj_properties[PROP_DHCP_ANYCAST_ADDRESS],
 	                                    G_VARIANT_TYPE_BYTESTRING,
 	                                    _nm_utils_hwaddr_to_dbus,
 	                                    _nm_utils_hwaddr_from_dbus);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_OLPC_MESH,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-ovs-bridge.c b/libnm-core/nm-setting-ovs-bridge.c
index 9ca5566f..e69dcdea 100644
--- a/libnm-core/nm-setting-ovs-bridge.c
+++ b/libnm-core/nm-setting-ovs-bridge.c
@@ -34,14 +34,14 @@
  * necessary for Open vSwitch bridges.
  **/
 
-enum {
-	PROP_0,
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 	PROP_FAIL_MODE,
 	PROP_MCAST_SNOOPING_ENABLE,
 	PROP_RSTP_ENABLE,
 	PROP_STP_ENABLE,
-	LAST_PROP
-};
+);
 
 /**
  * NMSettingOvsBridge:
@@ -267,8 +267,8 @@ nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -280,14 +280,13 @@ nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_FAIL_MODE,
-	         g_param_spec_string (NM_SETTING_OVS_BRIDGE_FAIL_MODE, "", "",
-	                              NULL,
-	                              G_PARAM_READWRITE |
-	                              G_PARAM_CONSTRUCT |
-	                              NM_SETTING_PARAM_INFERRABLE |
-	                              G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_FAIL_MODE] =
+	    g_param_spec_string (NM_SETTING_OVS_BRIDGE_FAIL_MODE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingOvsBridge:mcast-snooping-enable:
@@ -296,13 +295,12 @@ nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_MCAST_SNOOPING_ENABLE,
-	         g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE, "", "",
-	                               FALSE,
-	                               G_PARAM_READWRITE |
-	                               G_PARAM_CONSTRUCT |
-	                               G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MCAST_SNOOPING_ENABLE] =
+	    g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingOvsBridge:rstp-enable:
@@ -311,13 +309,12 @@ nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_RSTP_ENABLE,
-	         g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_RSTP_ENABLE, "", "",
-	                               FALSE,
-	                               G_PARAM_READWRITE |
-	                               G_PARAM_CONSTRUCT |
-	                               G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RSTP_ENABLE] =
+	    g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_RSTP_ENABLE, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingOvsBridge:stp-enable:
@@ -326,13 +323,14 @@ nm_setting_ovs_bridge_class_init (NMSettingOvsBridgeClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_STP_ENABLE,
-	         g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_STP_ENABLE, "", "",
-	                               FALSE,
-	                               G_PARAM_READWRITE |
-	                               G_PARAM_CONSTRUCT |
-	                               G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_STP_ENABLE] =
+	    g_param_spec_boolean (NM_SETTING_OVS_BRIDGE_STP_ENABLE, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_OVS_BRIDGE);
 }
diff --git a/libnm-core/nm-setting-ovs-interface.c b/libnm-core/nm-setting-ovs-interface.c
index a177098d..ad7f2ead 100644
--- a/libnm-core/nm-setting-ovs-interface.c
+++ b/libnm-core/nm-setting-ovs-interface.c
@@ -33,11 +33,11 @@
  * necessary for Open vSwitch interfaces.
  **/
 
-enum {
-	PROP_0,
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 	PROP_TYPE,
-	LAST_PROP
-};
+);
 
 /**
  * NMSettingOvsInterface:
@@ -366,8 +366,8 @@ nm_setting_ovs_interface_class_init (NMSettingOvsInterfaceClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -379,14 +379,15 @@ nm_setting_ovs_interface_class_init (NMSettingOvsInterfaceClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_TYPE,
-	         g_param_spec_string (NM_SETTING_OVS_INTERFACE_TYPE, "", "",
-	                              NULL,
-	                              G_PARAM_READWRITE |
-	                              G_PARAM_CONSTRUCT |
-	                              NM_SETTING_PARAM_INFERRABLE |
-	                              G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TYPE] =
+	    g_param_spec_string (NM_SETTING_OVS_INTERFACE_TYPE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_OVS_INTERFACE);
 }
diff --git a/libnm-core/nm-setting-ovs-patch.c b/libnm-core/nm-setting-ovs-patch.c
index 15005eea..2a46810c 100644
--- a/libnm-core/nm-setting-ovs-patch.c
+++ b/libnm-core/nm-setting-ovs-patch.c
@@ -34,11 +34,11 @@
  * necessary for Open vSwitch interfaces of type "patch".
  **/
 
-enum {
-	PROP_0,
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 	PROP_PEER,
-	LAST_PROP
-};
+);
 
 /**
  * NMSettingOvsPatch:
@@ -199,14 +199,15 @@ nm_setting_ovs_patch_class_init (NMSettingOvsPatchClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_PEER,
-	         g_param_spec_string (NM_SETTING_OVS_PATCH_PEER, "", "",
-	                              NULL,
-	                              G_PARAM_READWRITE |
-	                              G_PARAM_CONSTRUCT |
-	                              NM_SETTING_PARAM_INFERRABLE |
-	                              G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PEER] =
+	    g_param_spec_string (NM_SETTING_OVS_PATCH_PEER, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_OVS_PATCH);
 }
diff --git a/libnm-core/nm-setting-ovs-port.c b/libnm-core/nm-setting-ovs-port.c
index 10e542e7..e690d641 100644
--- a/libnm-core/nm-setting-ovs-port.c
+++ b/libnm-core/nm-setting-ovs-port.c
@@ -34,16 +34,16 @@
  * necessary for Open vSwitch ports.
  **/
 
-enum {
-	PROP_0,
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 	PROP_VLAN_MODE,
 	PROP_TAG,
 	PROP_LACP,
 	PROP_BOND_MODE,
 	PROP_BOND_UPDELAY,
 	PROP_BOND_DOWNDELAY,
-	LAST_PROP
-};
+);
 
 /**
  * NMSettingOvsPort:
@@ -364,8 +364,8 @@ nm_setting_ovs_port_class_init (NMSettingOvsPortClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -378,14 +378,13 @@ nm_setting_ovs_port_class_init (NMSettingOvsPortClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_VLAN_MODE,
-	         g_param_spec_string (NM_SETTING_OVS_PORT_VLAN_MODE, "", "",
-	                              NULL,
-	                              G_PARAM_READWRITE |
-	                              G_PARAM_CONSTRUCT |
-	                              NM_SETTING_PARAM_INFERRABLE |
-	                              G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_VLAN_MODE] =
+	    g_param_spec_string (NM_SETTING_OVS_PORT_VLAN_MODE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingOvsPort:tag:
@@ -394,14 +393,13 @@ nm_setting_ovs_port_class_init (NMSettingOvsPortClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_TAG,
-	         g_param_spec_uint (NM_SETTING_OVS_PORT_TAG, "", "",
-	                            0, 4095, 0,
-	                            G_PARAM_READWRITE |
-	                            G_PARAM_CONSTRUCT |
-	                            NM_SETTING_PARAM_INFERRABLE |
-	                            G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TAG] =
+	    g_param_spec_uint (NM_SETTING_OVS_PORT_TAG, "", "",
+	                       0, 4095, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingOvsPort:lacp:
@@ -410,14 +408,13 @@ nm_setting_ovs_port_class_init (NMSettingOvsPortClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_LACP,
-	         g_param_spec_string (NM_SETTING_OVS_PORT_LACP, "", "",
-	                              NULL,
-	                              G_PARAM_READWRITE |
-	                              G_PARAM_CONSTRUCT |
-	                              NM_SETTING_PARAM_INFERRABLE |
-	                              G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LACP] =
+	    g_param_spec_string (NM_SETTING_OVS_PORT_LACP, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingOvsPort:bond-mode:
@@ -426,14 +423,13 @@ nm_setting_ovs_port_class_init (NMSettingOvsPortClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_BOND_MODE,
-	         g_param_spec_string (NM_SETTING_OVS_PORT_BOND_MODE, "", "",
-	                              NULL,
-	                              G_PARAM_READWRITE |
-	                              G_PARAM_CONSTRUCT |
-	                              NM_SETTING_PARAM_INFERRABLE |
-	                              G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_BOND_MODE] =
+	    g_param_spec_string (NM_SETTING_OVS_PORT_BOND_MODE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingOvsPort:bond-updelay:
@@ -442,14 +438,13 @@ nm_setting_ovs_port_class_init (NMSettingOvsPortClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_BOND_UPDELAY,
-	         g_param_spec_uint (NM_SETTING_OVS_PORT_BOND_UPDELAY, "", "",
-	                            0, G_MAXUINT, 0,
-	                            G_PARAM_READWRITE |
-	                            G_PARAM_CONSTRUCT |
-	                            NM_SETTING_PARAM_INFERRABLE |
-	                            G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_BOND_UPDELAY] =
+	    g_param_spec_uint (NM_SETTING_OVS_PORT_BOND_UPDELAY, "", "",
+	                       0, G_MAXUINT, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingOvsPort:bond-downdelay:
@@ -458,14 +453,15 @@ nm_setting_ovs_port_class_init (NMSettingOvsPortClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_BOND_DOWNDELAY,
-	         g_param_spec_uint (NM_SETTING_OVS_PORT_BOND_DOWNDELAY, "", "",
-	                            0, G_MAXUINT, 0,
-	                            G_PARAM_READWRITE |
-	                            G_PARAM_CONSTRUCT |
-	                            NM_SETTING_PARAM_INFERRABLE |
-	                            G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_BOND_DOWNDELAY] =
+	    g_param_spec_uint (NM_SETTING_OVS_PORT_BOND_DOWNDELAY, "", "",
+	                       0, G_MAXUINT, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_OVS_PORT);
 }
diff --git a/libnm-core/nm-setting-ppp.c b/libnm-core/nm-setting-ppp.c
index 7425da19..12a8c366 100644
--- a/libnm-core/nm-setting-ppp.c
+++ b/libnm-core/nm-setting-ppp.c
@@ -23,6 +23,7 @@
 #include "nm-default.h"
 
 #include "nm-setting-ppp.h"
+
 #include "nm-setting-private.h"
 
 /**
@@ -35,9 +36,28 @@
  * cable and DSL modems and some mobile broadband devices.
  **/
 
-G_DEFINE_TYPE (NMSettingPpp, nm_setting_ppp, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_PPP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPP, NMSettingPppPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_NOAUTH,
+	PROP_REFUSE_EAP,
+	PROP_REFUSE_PAP,
+	PROP_REFUSE_CHAP,
+	PROP_REFUSE_MSCHAP,
+	PROP_REFUSE_MSCHAPV2,
+	PROP_NOBSDCOMP,
+	PROP_NODEFLATE,
+	PROP_NO_VJ_COMP,
+	PROP_REQUIRE_MPPE,
+	PROP_REQUIRE_MPPE_128,
+	PROP_MPPE_STATEFUL,
+	PROP_CRTSCTS,
+	PROP_BAUD,
+	PROP_MRU,
+	PROP_MTU,
+	PROP_LCP_ECHO_FAILURE,
+	PROP_LCP_ECHO_INTERVAL,
+);
 
 typedef struct {
 	gboolean noauth;
@@ -60,42 +80,11 @@ typedef struct {
 	guint32 lcp_echo_interval;
 } NMSettingPppPrivate;
 
-enum {
-	PROP_0,
-	PROP_NOAUTH,
-	PROP_REFUSE_EAP,
-	PROP_REFUSE_PAP,
-	PROP_REFUSE_CHAP,
-	PROP_REFUSE_MSCHAP,
-	PROP_REFUSE_MSCHAPV2,
-	PROP_NOBSDCOMP,
-	PROP_NODEFLATE,
-	PROP_NO_VJ_COMP,
-	PROP_REQUIRE_MPPE,
-	PROP_REQUIRE_MPPE_128,
-	PROP_MPPE_STATEFUL,
-	PROP_CRTSCTS,
-	PROP_BAUD,
-	PROP_MRU,
-	PROP_MTU,
-	PROP_LCP_ECHO_FAILURE,
-	PROP_LCP_ECHO_INTERVAL,
+G_DEFINE_TYPE (NMSettingPpp, nm_setting_ppp, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_PPP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPP, NMSettingPppPrivate))
 
-/**
- * nm_setting_ppp_new:
- *
- * Creates a new #NMSettingPpp object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingPpp object
- **/
-NMSetting *
-nm_setting_ppp_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_PPP, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_ppp_get_noauth:
@@ -383,71 +372,68 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static void
-nm_setting_ppp_init (NMSettingPpp *setting)
-{
-}
+/*****************************************************************************/
 
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingPppPrivate *priv = NM_SETTING_PPP_GET_PRIVATE (object);
+	NMSettingPpp *setting = NM_SETTING_PPP (object);
 
 	switch (prop_id) {
 	case PROP_NOAUTH:
-		priv->noauth = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_noauth (setting));
 		break;
 	case PROP_REFUSE_EAP:
-		priv->refuse_eap = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_refuse_eap (setting));
 		break;
 	case PROP_REFUSE_PAP:
-		priv->refuse_pap = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_refuse_pap (setting));
 		break;
 	case PROP_REFUSE_CHAP:
-		priv->refuse_chap = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_refuse_chap (setting));
 		break;
 	case PROP_REFUSE_MSCHAP:
-		priv->refuse_mschap = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_refuse_mschap (setting));
 		break;
 	case PROP_REFUSE_MSCHAPV2:
-		priv->refuse_mschapv2 = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_refuse_mschapv2 (setting));
 		break;
 	case PROP_NOBSDCOMP:
-		priv->nobsdcomp = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_nobsdcomp (setting));
 		break;
 	case PROP_NODEFLATE:
-		priv->nodeflate = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_nodeflate (setting));
 		break;
 	case PROP_NO_VJ_COMP:
-		priv->no_vj_comp = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_no_vj_comp (setting));
 		break;
 	case PROP_REQUIRE_MPPE:
-		priv->require_mppe = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_require_mppe (setting));
 		break;
 	case PROP_REQUIRE_MPPE_128:
-		priv->require_mppe_128 = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_require_mppe_128 (setting));
 		break;
 	case PROP_MPPE_STATEFUL:
-		priv->mppe_stateful = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_mppe_stateful (setting));
 		break;
 	case PROP_CRTSCTS:
-		priv->crtscts = g_value_get_boolean (value);
+		g_value_set_boolean (value, nm_setting_ppp_get_crtscts (setting));
 		break;
 	case PROP_BAUD:
-		priv->baud = g_value_get_uint (value);
+		g_value_set_uint (value, nm_setting_ppp_get_baud (setting));
 		break;
 	case PROP_MRU:
-		priv->mru = g_value_get_uint (value);
+		g_value_set_uint (value, nm_setting_ppp_get_mru (setting));
 		break;
 	case PROP_MTU:
-		priv->mtu = g_value_get_uint (value);
+		g_value_set_uint (value, nm_setting_ppp_get_mtu (setting));
 		break;
 	case PROP_LCP_ECHO_FAILURE:
-		priv->lcp_echo_failure = g_value_get_uint (value);
+		g_value_set_uint (value, nm_setting_ppp_get_lcp_echo_failure (setting));
 		break;
 	case PROP_LCP_ECHO_INTERVAL:
-		priv->lcp_echo_interval = g_value_get_uint (value);
+		g_value_set_uint (value, nm_setting_ppp_get_lcp_echo_interval (setting));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -456,65 +442,65 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
-	NMSettingPpp *setting = NM_SETTING_PPP (object);
+	NMSettingPppPrivate *priv = NM_SETTING_PPP_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_NOAUTH:
-		g_value_set_boolean (value, nm_setting_ppp_get_noauth (setting));
+		priv->noauth = g_value_get_boolean (value);
 		break;
 	case PROP_REFUSE_EAP:
-		g_value_set_boolean (value, nm_setting_ppp_get_refuse_eap (setting));
+		priv->refuse_eap = g_value_get_boolean (value);
 		break;
 	case PROP_REFUSE_PAP:
-		g_value_set_boolean (value, nm_setting_ppp_get_refuse_pap (setting));
+		priv->refuse_pap = g_value_get_boolean (value);
 		break;
 	case PROP_REFUSE_CHAP:
-		g_value_set_boolean (value, nm_setting_ppp_get_refuse_chap (setting));
+		priv->refuse_chap = g_value_get_boolean (value);
 		break;
 	case PROP_REFUSE_MSCHAP:
-		g_value_set_boolean (value, nm_setting_ppp_get_refuse_mschap (setting));
+		priv->refuse_mschap = g_value_get_boolean (value);
 		break;
 	case PROP_REFUSE_MSCHAPV2:
-		g_value_set_boolean (value, nm_setting_ppp_get_refuse_mschapv2 (setting));
+		priv->refuse_mschapv2 = g_value_get_boolean (value);
 		break;
 	case PROP_NOBSDCOMP:
-		g_value_set_boolean (value, nm_setting_ppp_get_nobsdcomp (setting));
+		priv->nobsdcomp = g_value_get_boolean (value);
 		break;
 	case PROP_NODEFLATE:
-		g_value_set_boolean (value, nm_setting_ppp_get_nodeflate (setting));
+		priv->nodeflate = g_value_get_boolean (value);
 		break;
 	case PROP_NO_VJ_COMP:
-		g_value_set_boolean (value, nm_setting_ppp_get_no_vj_comp (setting));
+		priv->no_vj_comp = g_value_get_boolean (value);
 		break;
 	case PROP_REQUIRE_MPPE:
-		g_value_set_boolean (value, nm_setting_ppp_get_require_mppe (setting));
+		priv->require_mppe = g_value_get_boolean (value);
 		break;
 	case PROP_REQUIRE_MPPE_128:
-		g_value_set_boolean (value, nm_setting_ppp_get_require_mppe_128 (setting));
+		priv->require_mppe_128 = g_value_get_boolean (value);
 		break;
 	case PROP_MPPE_STATEFUL:
-		g_value_set_boolean (value, nm_setting_ppp_get_mppe_stateful (setting));
+		priv->mppe_stateful = g_value_get_boolean (value);
 		break;
 	case PROP_CRTSCTS:
-		g_value_set_boolean (value, nm_setting_ppp_get_crtscts (setting));
+		priv->crtscts = g_value_get_boolean (value);
 		break;
 	case PROP_BAUD:
-		g_value_set_uint (value, nm_setting_ppp_get_baud (setting));
+		priv->baud = g_value_get_uint (value);
 		break;
 	case PROP_MRU:
-		g_value_set_uint (value, nm_setting_ppp_get_mru (setting));
+		priv->mru = g_value_get_uint (value);
 		break;
 	case PROP_MTU:
-		g_value_set_uint (value, nm_setting_ppp_get_mtu (setting));
+		priv->mtu = g_value_get_uint (value);
 		break;
 	case PROP_LCP_ECHO_FAILURE:
-		g_value_set_uint (value, nm_setting_ppp_get_lcp_echo_failure (setting));
+		priv->lcp_echo_failure = g_value_get_uint (value);
 		break;
 	case PROP_LCP_ECHO_INTERVAL:
-		g_value_set_uint (value, nm_setting_ppp_get_lcp_echo_interval (setting));
+		priv->lcp_echo_interval = g_value_get_uint (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -522,6 +508,26 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_ppp_init (NMSettingPpp *setting)
+{
+}
+
+/**
+ * nm_setting_ppp_new:
+ *
+ * Creates a new #NMSettingPpp object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingPpp object
+ **/
+NMSetting *
+nm_setting_ppp_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_PPP, NULL);
+}
+
 static void
 nm_setting_ppp_class_init (NMSettingPppClass *klass)
 {
@@ -530,8 +536,8 @@ nm_setting_ppp_class_init (NMSettingPppClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingPppPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 
 	setting_class->verify = verify;
 
@@ -542,120 +548,111 @@ nm_setting_ppp_class_init (NMSettingPppClass *klass)
 	 * authenticate itself to the client.  If %FALSE, require authentication
 	 * from the remote side.  In almost all cases, this should be %TRUE.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NOAUTH,
-		 g_param_spec_boolean (NM_SETTING_PPP_NOAUTH, "", "",
-		                       TRUE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NOAUTH] =
+	    g_param_spec_boolean (NM_SETTING_PPP_NOAUTH, "", "",
+	                          TRUE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:refuse-eap:
 	 *
 	 * If %TRUE, the EAP authentication method will not be used.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_REFUSE_EAP,
-		 g_param_spec_boolean (NM_SETTING_PPP_REFUSE_EAP, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_REFUSE_EAP] =
+	    g_param_spec_boolean (NM_SETTING_PPP_REFUSE_EAP, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:refuse-pap:
 	 *
 	 * If %TRUE, the PAP authentication method will not be used.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_REFUSE_PAP,
-		 g_param_spec_boolean (NM_SETTING_PPP_REFUSE_PAP, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_REFUSE_PAP] =
+	    g_param_spec_boolean (NM_SETTING_PPP_REFUSE_PAP, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:refuse-chap:
 	 *
 	 * If %TRUE, the CHAP authentication method will not be used.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_REFUSE_CHAP,
-		 g_param_spec_boolean (NM_SETTING_PPP_REFUSE_CHAP, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_REFUSE_CHAP] =
+	    g_param_spec_boolean (NM_SETTING_PPP_REFUSE_CHAP, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:refuse-mschap:
 	 *
 	 * If %TRUE, the MSCHAP authentication method will not be used.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_REFUSE_MSCHAP,
-		 g_param_spec_boolean (NM_SETTING_PPP_REFUSE_MSCHAP, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_REFUSE_MSCHAP] =
+	    g_param_spec_boolean (NM_SETTING_PPP_REFUSE_MSCHAP, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:refuse-mschapv2:
 	 *
 	 * If %TRUE, the MSCHAPv2 authentication method will not be used.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_REFUSE_MSCHAPV2,
-		 g_param_spec_boolean (NM_SETTING_PPP_REFUSE_MSCHAPV2, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_REFUSE_MSCHAPV2] =
+	    g_param_spec_boolean (NM_SETTING_PPP_REFUSE_MSCHAPV2, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:nobsdcomp:
 	 *
 	 * If %TRUE, BSD compression will not be requested.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NOBSDCOMP,
-		 g_param_spec_boolean (NM_SETTING_PPP_NOBSDCOMP, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_FUZZY_IGNORE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NOBSDCOMP] =
+	    g_param_spec_boolean (NM_SETTING_PPP_NOBSDCOMP, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_FUZZY_IGNORE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:nodeflate:
 	 *
 	 * If %TRUE, "deflate" compression will not be requested.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NODEFLATE,
-		 g_param_spec_boolean (NM_SETTING_PPP_NODEFLATE, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_FUZZY_IGNORE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NODEFLATE] =
+	    g_param_spec_boolean (NM_SETTING_PPP_NODEFLATE, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_FUZZY_IGNORE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:no-vj-comp:
 	 *
 	 * If %TRUE, Van Jacobsen TCP header compression will not be requested.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NO_VJ_COMP,
-		 g_param_spec_boolean (NM_SETTING_PPP_NO_VJ_COMP, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_FUZZY_IGNORE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NO_VJ_COMP] =
+	    g_param_spec_boolean (NM_SETTING_PPP_NO_VJ_COMP, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_FUZZY_IGNORE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:require-mppe:
@@ -665,13 +662,12 @@ nm_setting_ppp_class_init (NMSettingPppClass *klass)
 	 * session will fail.  Note that MPPE is not used on mobile broadband
 	 * connections.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_REQUIRE_MPPE,
-		 g_param_spec_boolean (NM_SETTING_PPP_REQUIRE_MPPE, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_REQUIRE_MPPE] =
+	    g_param_spec_boolean (NM_SETTING_PPP_REQUIRE_MPPE, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:require-mppe-128:
@@ -680,12 +676,11 @@ nm_setting_ppp_class_init (NMSettingPppClass *klass)
 	 * required for the PPP session, and the "require-mppe" property must also
 	 * be set to %TRUE.  If 128-bit MPPE is not available the session will fail.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_REQUIRE_MPPE_128,
-		 g_param_spec_boolean (NM_SETTING_PPP_REQUIRE_MPPE_128, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE  | G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_REQUIRE_MPPE_128] =
+	    g_param_spec_boolean (NM_SETTING_PPP_REQUIRE_MPPE_128, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE  | G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:mppe-stateful:
@@ -693,13 +688,12 @@ nm_setting_ppp_class_init (NMSettingPppClass *klass)
 	 * If %TRUE, stateful MPPE is used.  See pppd documentation for more
 	 * information on stateful MPPE.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_MPPE_STATEFUL,
-		 g_param_spec_boolean (NM_SETTING_PPP_MPPE_STATEFUL, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MPPE_STATEFUL] =
+	    g_param_spec_boolean (NM_SETTING_PPP_MPPE_STATEFUL, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:crtscts:
@@ -708,13 +702,12 @@ nm_setting_ppp_class_init (NMSettingPppClass *klass)
 	 * flow control with RTS and CTS signals.  This value should normally be set
 	 * to %FALSE.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_CRTSCTS,
-		 g_param_spec_boolean (NM_SETTING_PPP_CRTSCTS, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CRTSCTS] =
+	    g_param_spec_boolean (NM_SETTING_PPP_CRTSCTS, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:baud:
@@ -723,14 +716,13 @@ nm_setting_ppp_class_init (NMSettingPppClass *klass)
 	 * baudrate.  This value should normally be left as 0 to automatically
 	 * choose the speed.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_BAUD,
-		 g_param_spec_uint (NM_SETTING_PPP_BAUD, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_BAUD] =
+	    g_param_spec_uint (NM_SETTING_PPP_BAUD, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:mru:
@@ -739,13 +731,12 @@ nm_setting_ppp_class_init (NMSettingPppClass *klass)
 	 * larger than the specified size.  If non-zero, the MRU should be between
 	 * 128 and 16384.
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MRU,
-		 g_param_spec_uint (NM_SETTING_PPP_MRU, "", "",
-		                    0, 16384, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MRU] =
+	    g_param_spec_uint (NM_SETTING_PPP_MRU, "", "",
+	                       0, 16384, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:mtu:
@@ -753,14 +744,13 @@ nm_setting_ppp_class_init (NMSettingPppClass *klass)
 	 * If non-zero, instruct pppd to send packets no larger than the specified
 	 * size.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_MTU,
-		 g_param_spec_uint (NM_SETTING_PPP_MTU, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MTU] =
+	    g_param_spec_uint (NM_SETTING_PPP_MTU, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:lcp-echo-failure:
@@ -770,14 +760,13 @@ nm_setting_ppp_class_init (NMSettingPppClass *klass)
 	 * peer.  The "lcp-echo-interval" property must also be set to a non-zero
 	 * value if this property is used.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_LCP_ECHO_FAILURE,
-		 g_param_spec_uint (NM_SETTING_PPP_LCP_ECHO_FAILURE, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LCP_ECHO_FAILURE] =
+	    g_param_spec_uint (NM_SETTING_PPP_LCP_ECHO_FAILURE, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPpp:lcp-echo-interval:
@@ -787,14 +776,15 @@ nm_setting_ppp_class_init (NMSettingPppClass *klass)
 	 * peers will respond to echo requests and some will not, and it is not
 	 * possible to autodetect this.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_LCP_ECHO_INTERVAL,
-		 g_param_spec_uint (NM_SETTING_PPP_LCP_ECHO_INTERVAL, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LCP_ECHO_INTERVAL] =
+	    g_param_spec_uint (NM_SETTING_PPP_LCP_ECHO_INTERVAL, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_PPP);
 }
diff --git a/libnm-core/nm-setting-pppoe.c b/libnm-core/nm-setting-pppoe.c
index 793d93ce..e2d516c5 100644
--- a/libnm-core/nm-setting-pppoe.c
+++ b/libnm-core/nm-setting-pppoe.c
@@ -22,9 +22,8 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-setting-pppoe.h"
+
 #include "nm-setting-ppp.h"
 #include "nm-setting-private.h"
 #include "nm-core-enum-types.h"
@@ -38,9 +37,15 @@
  * to provide IP transport, for example cable or DSL modems.
  **/
 
-G_DEFINE_TYPE (NMSettingPppoe, nm_setting_pppoe, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_PPPOE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPPOE, NMSettingPppoePrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PARENT,
+	PROP_SERVICE,
+	PROP_USERNAME,
+	PROP_PASSWORD,
+	PROP_PASSWORD_FLAGS,
+);
 
 typedef struct {
 	char *parent;
@@ -50,29 +55,11 @@ typedef struct {
 	NMSettingSecretFlags password_flags;
 } NMSettingPppoePrivate;
 
-enum {
-	PROP_0,
-	PROP_PARENT,
-	PROP_SERVICE,
-	PROP_USERNAME,
-	PROP_PASSWORD,
-	PROP_PASSWORD_FLAGS,
+G_DEFINE_TYPE (NMSettingPppoe, nm_setting_pppoe, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_PPPOE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PPPOE, NMSettingPppoePrivate))
 
-/**
- * nm_setting_pppoe_new:
- *
- * Creates a new #NMSettingPppoe object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingPppoe object
- **/
-NMSetting *
-nm_setting_pppoe_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_PPPOE, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_pppoe_get_parent:
@@ -207,36 +194,29 @@ need_secrets (NMSetting *setting)
 	return secrets;
 }
 
-static void
-nm_setting_pppoe_init (NMSettingPppoe *setting)
-{
-}
+/*****************************************************************************/
 
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingPppoePrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (object);
+	NMSettingPppoe *setting = NM_SETTING_PPPOE (object);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_free (priv->parent);
-		priv->parent = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_pppoe_get_parent (setting));
 		break;
 	case PROP_SERVICE:
-		g_free (priv->service);
-		priv->service = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_pppoe_get_service (setting));
 		break;
 	case PROP_USERNAME:
-		g_free (priv->username);
-		priv->username = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_pppoe_get_username (setting));
 		break;
 	case PROP_PASSWORD:
-		g_free (priv->password);
-		priv->password = g_value_dup_string (value);
+		g_value_set_string (value, nm_setting_pppoe_get_password (setting));
 		break;
 	case PROP_PASSWORD_FLAGS:
-		priv->password_flags = g_value_get_flags (value);
+		g_value_set_flags (value, nm_setting_pppoe_get_password_flags (setting));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -245,26 +225,30 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
-	NMSettingPppoe *setting = NM_SETTING_PPPOE (object);
+	NMSettingPppoePrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_value_set_string (value, nm_setting_pppoe_get_parent (setting));
+		g_free (priv->parent);
+		priv->parent = g_value_dup_string (value);
 		break;
 	case PROP_SERVICE:
-		g_value_set_string (value, nm_setting_pppoe_get_service (setting));
+		g_free (priv->service);
+		priv->service = g_value_dup_string (value);
 		break;
 	case PROP_USERNAME:
-		g_value_set_string (value, nm_setting_pppoe_get_username (setting));
+		g_free (priv->username);
+		priv->username = g_value_dup_string (value);
 		break;
 	case PROP_PASSWORD:
-		g_value_set_string (value, nm_setting_pppoe_get_password (setting));
+		g_free (priv->password);
+		priv->password = g_value_dup_string (value);
 		break;
 	case PROP_PASSWORD_FLAGS:
-		g_value_set_flags (value, nm_setting_pppoe_get_password_flags (setting));
+		priv->password_flags = g_value_get_flags (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -272,6 +256,26 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_pppoe_init (NMSettingPppoe *setting)
+{
+}
+
+/**
+ * nm_setting_pppoe_new:
+ *
+ * Creates a new #NMSettingPppoe object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingPppoe object
+ **/
+NMSetting *
+nm_setting_pppoe_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_PPPOE, NULL);
+}
+
 static void
 finalize (GObject *object)
 {
@@ -293,8 +297,8 @@ nm_setting_pppoe_class_init (NMSettingPppoeClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingPppoePrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify       = verify;
@@ -310,14 +314,13 @@ nm_setting_pppoe_class_init (NMSettingPppoeClass *klass)
 	 *
 	 * Since: 1.10
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PARENT,
-		 g_param_spec_string (NM_SETTING_PPPOE_PARENT, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PARENT] =
+	    g_param_spec_string (NM_SETTING_PPPOE_PARENT, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPppoe:service:
@@ -327,50 +330,48 @@ nm_setting_pppoe_class_init (NMSettingPppoeClass *klass)
 	 * this should be left blank.  It is only required if there are multiple
 	 * access concentrators or a specific service is known to be required.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_SERVICE,
-		 g_param_spec_string (NM_SETTING_PPPOE_SERVICE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SERVICE] =
+	    g_param_spec_string (NM_SETTING_PPPOE_SERVICE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPppoe:username:
 	 *
 	 * Username used to authenticate with the PPPoE service.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_USERNAME,
-		 g_param_spec_string (NM_SETTING_PPPOE_USERNAME, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_USERNAME] =
+	    g_param_spec_string (NM_SETTING_PPPOE_USERNAME, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPppoe:password:
 	 *
 	 * Password used to authenticate with the PPPoE service.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD,
-		 g_param_spec_string (NM_SETTING_PPPOE_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_PPPOE_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingPppoe:password-flags:
 	 *
 	 * Flags indicating how to handle the #NMSettingPppoe:password property.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_PPPOE_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_PPPOE_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_PPPOE);
 }
diff --git a/libnm-core/nm-setting-private.h b/libnm-core/nm-setting-private.h
index 1e25226e..2af74ec4 100644
--- a/libnm-core/nm-setting-private.h
+++ b/libnm-core/nm-setting-private.h
@@ -39,6 +39,8 @@ int _nm_setting_compare_priority (gconstpointer a, gconstpointer b);
 
 /*****************************************************************************/
 
+void _nm_setting_emit_property_changed (NMSetting *setting);
+
 typedef enum NMSettingUpdateSecretResult {
 	NM_SETTING_UPDATE_SECRET_ERROR              = FALSE,
 	NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED   = TRUE,
@@ -48,10 +50,9 @@ typedef enum NMSettingUpdateSecretResult {
 NMSettingUpdateSecretResult _nm_setting_update_secrets (NMSetting *setting,
                                                         GVariant *secrets,
                                                         GError **error);
-gboolean _nm_setting_clear_secrets (NMSetting *setting);
-gboolean _nm_setting_clear_secrets_with_flags (NMSetting *setting,
-                                               NMSettingClearSecretsWithFlagsFn func,
-                                               gpointer user_data);
+gboolean _nm_setting_clear_secrets (NMSetting *setting,
+                                    NMSettingClearSecretsWithFlagsFn func,
+                                    gpointer user_data);
 
 /* The property of the #NMSetting should be considered during comparisons that
  * use the %NM_SETTING_COMPARE_FLAG_INFERRABLE flag. Properties that don't have
@@ -82,9 +83,11 @@ gboolean _nm_setting_clear_secrets_with_flags (NMSetting *setting,
 
 #define NM_SETTING_PARAM_GENDATA_BACKED (1 << (7 + G_PARAM_USER_SHIFT))
 
-GVariant *_nm_setting_get_deprecated_virtual_interface_name (NMSetting *setting,
+GVariant *_nm_setting_get_deprecated_virtual_interface_name (const NMSettInfoSetting *sett_info,
+                                                             guint property_idx,
                                                              NMConnection *connection,
-                                                             const char *property);
+                                                             NMSetting *setting,
+                                                             NMConnectionSerializationFlags flags);
 
 NMSettingVerifyResult _nm_setting_verify (NMSetting *setting,
                                           NMConnection *connection,
@@ -95,6 +98,10 @@ gboolean _nm_setting_verify_secret_string (const char *str,
                                            const char *property,
                                            GError **error);
 
+gboolean _nm_setting_aggregate (NMSetting *setting,
+                                NMConnectionAggregateType type,
+                                gpointer arg);
+
 gboolean _nm_setting_slave_type_is_valid (const char *slave_type, const char **out_port_type);
 
 GVariant   *_nm_setting_to_dbus       (NMSetting *setting,
@@ -107,6 +114,11 @@ NMSetting  *_nm_setting_new_from_dbus (GType setting_type,
                                        NMSettingParseFlags parse_flags,
                                        GError **error);
 
+gboolean _nm_setting_property_is_regular_secret (NMSetting *setting,
+                                                 const char *secret_name);
+gboolean _nm_setting_property_is_regular_secret_flags (NMSetting *setting,
+                                                       const char *secret_flags_name);
+
 /*****************************************************************************/
 
 static inline GArray *
@@ -187,6 +199,11 @@ gboolean _nm_setting_use_legacy_property (NMSetting *setting,
 
 GPtrArray  *_nm_setting_need_secrets (NMSetting *setting);
 
+gboolean _nm_setting_should_compare_secret_property (NMSetting *setting,
+                                                     NMSetting *other,
+                                                     const char *secret_name,
+                                                     NMSettingCompareFlags flags);
+
 /*****************************************************************************/
 
 #endif  /* NM_SETTING_PRIVATE_H */
diff --git a/libnm-core/nm-setting-proxy.c b/libnm-core/nm-setting-proxy.c
index d544c7fa..dbb69aeb 100644
--- a/libnm-core/nm-setting-proxy.c
+++ b/libnm-core/nm-setting-proxy.c
@@ -22,6 +22,7 @@
 #include "nm-default.h"
 
 #include "nm-setting-proxy.h"
+
 #include "nm-utils.h"
 #include "nm-setting-private.h"
 
@@ -39,9 +40,14 @@
  * to fulfill client queries.
  **/
 
-G_DEFINE_TYPE (NMSettingProxy, nm_setting_proxy, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_PROXY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PROXY, NMSettingProxyPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_METHOD,
+	PROP_BROWSER_ONLY,
+	PROP_PAC_URL,
+	PROP_PAC_SCRIPT,
+);
 
 typedef struct {
 	NMSettingProxyMethod method;
@@ -50,30 +56,11 @@ typedef struct {
 	char *pac_script;
 } NMSettingProxyPrivate;
 
-enum {
-	PROP_0,
-	PROP_METHOD,
-	PROP_BROWSER_ONLY,
-	PROP_PAC_URL,
-	PROP_PAC_SCRIPT,
+G_DEFINE_TYPE (NMSettingProxy, nm_setting_proxy, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_PROXY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_PROXY, NMSettingProxyPrivate))
 
-/**
- * nm_setting_proxy_new:
- *
- * Creates a new #NMSettingProxy object.
- *
- * Returns: the new empty #NMSettingProxy object
- *
- * Since: 1.6
- **/
-NMSetting *
-nm_setting_proxy_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_PROXY, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_proxy_get_method:
@@ -213,22 +200,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static void
-nm_setting_proxy_init (NMSettingProxy *setting)
-{
-}
-
-static void
-finalize (GObject *object)
-{
-	NMSettingProxy *self = NM_SETTING_PROXY (object);
-	NMSettingProxyPrivate *priv = NM_SETTING_PROXY_GET_PRIVATE (self);
-
-	g_free (priv->pac_url);
-	g_free (priv->pac_script);
-
-	G_OBJECT_CLASS (nm_setting_proxy_parent_class)->finalize (object);
-}
+/*****************************************************************************/
 
 static void
 get_property (GObject *object, guint prop_id,
@@ -282,6 +254,40 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_proxy_init (NMSettingProxy *setting)
+{
+}
+
+/**
+ * nm_setting_proxy_new:
+ *
+ * Creates a new #NMSettingProxy object.
+ *
+ * Returns: the new empty #NMSettingProxy object
+ *
+ * Since: 1.6
+ **/
+NMSetting *
+nm_setting_proxy_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_PROXY, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingProxy *self = NM_SETTING_PROXY (object);
+	NMSettingProxyPrivate *priv = NM_SETTING_PROXY_GET_PRIVATE (self);
+
+	g_free (priv->pac_url);
+	g_free (priv->pac_script);
+
+	G_OBJECT_CLASS (nm_setting_proxy_parent_class)->finalize (object);
+}
+
 static void
 nm_setting_proxy_class_init (NMSettingProxyClass *klass)
 {
@@ -290,8 +296,8 @@ nm_setting_proxy_class_init (NMSettingProxyClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingProxyPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -312,13 +318,12 @@ nm_setting_proxy_class_init (NMSettingProxyClass *klass)
 	 * values: none, auto
 	 * ---end---
 	 */
-	g_object_class_install_property
-	    (object_class, PROP_METHOD,
-	     g_param_spec_int (NM_SETTING_PROXY_METHOD, "", "",
-	                       G_MININT32, G_MAXINT32, NM_SETTING_PROXY_METHOD_NONE,
-	                       G_PARAM_READWRITE |
-	                       G_PARAM_CONSTRUCT |
-	                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_METHOD] =
+	    g_param_spec_int (NM_SETTING_PROXY_METHOD, "", "",
+	                      G_MININT32, G_MAXINT32, NM_SETTING_PROXY_METHOD_NONE,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_CONSTRUCT |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingProxy:browser-only:
@@ -334,12 +339,11 @@ nm_setting_proxy_class_init (NMSettingProxyClass *klass)
 	 * description: Whether the proxy configuration is for browser only.
 	 * ---end---
 	 */
-	g_object_class_install_property
-	    (object_class, PROP_BROWSER_ONLY,
-	     g_param_spec_boolean (NM_SETTING_PROXY_BROWSER_ONLY, "", "",
-	                           FALSE,
-	                           G_PARAM_READWRITE |
-	                           G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_BROWSER_ONLY] =
+	    g_param_spec_boolean (NM_SETTING_PROXY_BROWSER_ONLY, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingProxy:pac-url:
@@ -355,12 +359,11 @@ nm_setting_proxy_class_init (NMSettingProxyClass *klass)
 	 * example: PAC_URL=http://wpad.mycompany.com/wpad.dat
 	 * ---end---
 	 */
-	g_object_class_install_property
-	    (object_class, PROP_PAC_URL,
-	     g_param_spec_string (NM_SETTING_PROXY_PAC_URL, "", "",
-	                          NULL,
-	                          G_PARAM_READWRITE |
-	                          G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PAC_URL] =
+	    g_param_spec_string (NM_SETTING_PROXY_PAC_URL, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingProxy:pac-script:
@@ -376,12 +379,13 @@ nm_setting_proxy_class_init (NMSettingProxyClass *klass)
 	 * example: PAC_SCRIPT=/home/joe/proxy.pac
 	 * ---end---
 	 */
-	g_object_class_install_property
-	    (object_class, PROP_PAC_SCRIPT,
-	     g_param_spec_string (NM_SETTING_PROXY_PAC_SCRIPT, "", "",
-	                          NULL,
-	                          G_PARAM_READWRITE |
-	                          G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PAC_SCRIPT] =
+	    g_param_spec_string (NM_SETTING_PROXY_PAC_SCRIPT, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_PROXY);
 }
diff --git a/libnm-core/nm-setting-serial.c b/libnm-core/nm-setting-serial.c
index b173197d..36bc4f69 100644
--- a/libnm-core/nm-setting-serial.c
+++ b/libnm-core/nm-setting-serial.c
@@ -22,9 +22,8 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-setting-serial.h"
+
 #include "nm-setting-private.h"
 
 /**
@@ -37,9 +36,15 @@
  * such as mobile broadband or analog telephone connections.
  **/
 
-G_DEFINE_TYPE (NMSettingSerial, nm_setting_serial, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_SERIAL, NMSettingSerialPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_BAUD,
+	PROP_BITS,
+	PROP_PARITY,
+	PROP_STOPBITS,
+	PROP_SEND_DELAY,
+);
 
 typedef struct {
 	guint baud;
@@ -49,29 +54,11 @@ typedef struct {
 	guint64 send_delay;
 } NMSettingSerialPrivate;
 
-enum {
-	PROP_0,
-	PROP_BAUD,
-	PROP_BITS,
-	PROP_PARITY,
-	PROP_STOPBITS,
-	PROP_SEND_DELAY,
+G_DEFINE_TYPE (NMSettingSerial, nm_setting_serial, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_SERIAL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_SERIAL, NMSettingSerialPrivate))
 
-/**
- * nm_setting_serial_new:
- *
- * Creates a new #NMSettingSerial object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingSerial object
- **/
-NMSetting *
-nm_setting_serial_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_SERIAL, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_serial_get_baud:
@@ -143,11 +130,6 @@ nm_setting_serial_get_send_delay (NMSettingSerial *setting)
 	return NM_SETTING_SERIAL_GET_PRIVATE (setting)->send_delay;
 }
 
-static void
-nm_setting_serial_init (NMSettingSerial *setting)
-{
-}
-
 static GVariant *
 parity_to_dbus (const GValue *from)
 {
@@ -179,27 +161,29 @@ parity_from_dbus (GVariant *from, GValue *to)
 	}
 }
 
+/*****************************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingSerialPrivate *priv = NM_SETTING_SERIAL_GET_PRIVATE (object);
+	NMSettingSerial *setting = NM_SETTING_SERIAL (object);
 
 	switch (prop_id) {
 	case PROP_BAUD:
-		priv->baud = g_value_get_uint (value);
+		g_value_set_uint (value, nm_setting_serial_get_baud (setting));
 		break;
 	case PROP_BITS:
-		priv->bits = g_value_get_uint (value);
+		g_value_set_uint (value, nm_setting_serial_get_bits (setting));
 		break;
 	case PROP_PARITY:
-		priv->parity = g_value_get_enum (value);
+		g_value_set_enum (value, nm_setting_serial_get_parity (setting));
 		break;
 	case PROP_STOPBITS:
-		priv->stopbits = g_value_get_uint (value);
+		g_value_set_uint (value, nm_setting_serial_get_stopbits (setting));
 		break;
 	case PROP_SEND_DELAY:
-		priv->send_delay = g_value_get_uint64 (value);
+		g_value_set_uint64 (value, nm_setting_serial_get_send_delay (setting));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -208,26 +192,26 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
-	NMSettingSerial *setting = NM_SETTING_SERIAL (object);
+	NMSettingSerialPrivate *priv = NM_SETTING_SERIAL_GET_PRIVATE (object);
 
 	switch (prop_id) {
 	case PROP_BAUD:
-		g_value_set_uint (value, nm_setting_serial_get_baud (setting));
+		priv->baud = g_value_get_uint (value);
 		break;
 	case PROP_BITS:
-		g_value_set_uint (value, nm_setting_serial_get_bits (setting));
+		priv->bits = g_value_get_uint (value);
 		break;
 	case PROP_PARITY:
-		g_value_set_enum (value, nm_setting_serial_get_parity (setting));
+		priv->parity = g_value_get_enum (value);
 		break;
 	case PROP_STOPBITS:
-		g_value_set_uint (value, nm_setting_serial_get_stopbits (setting));
+		priv->stopbits = g_value_get_uint (value);
 		break;
 	case PROP_SEND_DELAY:
-		g_value_set_uint64 (value, nm_setting_serial_get_send_delay (setting));
+		priv->send_delay = g_value_get_uint64 (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -235,6 +219,26 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_serial_init (NMSettingSerial *setting)
+{
+}
+
+/**
+ * nm_setting_serial_new:
+ *
+ * Creates a new #NMSettingSerial object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingSerial object
+ **/
+NMSetting *
+nm_setting_serial_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_SERIAL, NULL);
+}
+
 static void
 nm_setting_serial_class_init (NMSettingSerialClass *klass)
 {
@@ -244,8 +248,8 @@ nm_setting_serial_class_init (NMSettingSerialClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingSerialPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 
 	/**
 	 * NMSettingSerial:baud:
@@ -254,26 +258,24 @@ nm_setting_serial_class_init (NMSettingSerialClass *klass)
 	 * value usually has no effect for mobile broadband modems as they generally
 	 * ignore speed settings and use the highest available speed.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_BAUD,
-		 g_param_spec_uint (NM_SETTING_SERIAL_BAUD, "", "",
-		                    0, G_MAXUINT, 57600,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_BAUD] =
+	    g_param_spec_uint (NM_SETTING_SERIAL_BAUD, "", "",
+	                       0, G_MAXUINT, 57600,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingSerial:bits:
 	 *
 	 * Byte-width of the serial communication. The 8 in "8n1" for example.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_BITS,
-		 g_param_spec_uint (NM_SETTING_SERIAL_BITS, "", "",
-		                    5, 8, 8,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_BITS] =
+	    g_param_spec_uint (NM_SETTING_SERIAL_BITS, "", "",
+	                       5, 8, 8,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingSerial:parity:
@@ -295,18 +297,16 @@ nm_setting_serial_class_init (NMSettingSerialClass *klass)
 	 *   111 (ASCII 'o') for odd, 110 (ASCII 'n') for none.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PARITY,
-		 g_param_spec_enum (NM_SETTING_SERIAL_PARITY, "", "",
-		                    NM_TYPE_SETTING_SERIAL_PARITY,
-		                    NM_SETTING_SERIAL_PARITY_NONE,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PARITY] =
+	    g_param_spec_enum (NM_SETTING_SERIAL_PARITY, "", "",
+	                       NM_TYPE_SETTING_SERIAL_PARITY,
+	                       NM_SETTING_SERIAL_PARITY_NONE,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_SERIAL_PARITY),
+	                                    obj_properties[PROP_PARITY],
 	                                    G_VARIANT_TYPE_BYTE,
 	                                    parity_to_dbus,
 	                                    parity_from_dbus);
@@ -317,26 +317,26 @@ nm_setting_serial_class_init (NMSettingSerialClass *klass)
 	 * Number of stop bits for communication on the serial port.  Either 1 or 2.
 	 * The 1 in "8n1" for example.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_STOPBITS,
-		 g_param_spec_uint (NM_SETTING_SERIAL_STOPBITS, "", "",
-		                    1, 2, 1,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_STOPBITS] =
+	    g_param_spec_uint (NM_SETTING_SERIAL_STOPBITS, "", "",
+	                       1, 2, 1,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingSerial:send-delay:
 	 *
 	 * Time to delay between each byte sent to the modem, in microseconds.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_SEND_DELAY,
-		 g_param_spec_uint64 (NM_SETTING_SERIAL_SEND_DELAY, "", "",
-		                      0, G_MAXUINT64, 0,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SEND_DELAY] =
+	    g_param_spec_uint64 (NM_SETTING_SERIAL_SEND_DELAY, "", "",
+	                         0, G_MAXUINT64, 0,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_SERIAL,
 	                               NULL, properties_override);
diff --git a/libnm-core/nm-setting-sriov.c b/libnm-core/nm-setting-sriov.c
index 0addd972..9c7306e0 100644
--- a/libnm-core/nm-setting-sriov.c
+++ b/libnm-core/nm-setting-sriov.c
@@ -15,6 +15,7 @@
 #include "nm-default.h"
 
 #include "nm-setting-sriov.h"
+
 #include "nm-setting-private.h"
 #include "nm-utils-private.h"
 
@@ -24,6 +25,14 @@
  * @include: nm-setting-sriov.h
  **/
 
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingSriov,
+	PROP_TOTAL_VFS,
+	PROP_VFS,
+	PROP_AUTOPROBE_DRIVERS,
+);
+
 /**
  * NMSettingSriov:
  *
@@ -44,15 +53,6 @@ struct _NMSettingSriovClass {
 
 G_DEFINE_TYPE (NMSettingSriov, nm_setting_sriov, NM_TYPE_SETTING)
 
-enum {
-	PROP_0,
-	PROP_TOTAL_VFS,
-	PROP_VFS,
-	PROP_AUTOPROBE_DRIVERS,
-
-	LAST_PROP
-};
-
 /*****************************************************************************/
 
 G_DEFINE_BOXED_TYPE (NMSriovVF, nm_sriov_vf, nm_sriov_vf_dup, nm_sriov_vf_unref)
@@ -705,21 +705,6 @@ nm_sriov_vf_get_vlan_protocol (const NMSriovVF *vf, guint vlan_id)
 /*****************************************************************************/
 
 /**
- * nm_setting_sriov_new:
- *
- * Creates a new #NMSettingSriov object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingSriov object
- *
- * Since: 1.14
- **/
-NMSetting *
-nm_setting_sriov_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_SRIOV, NULL);
-}
-
-/**
  * nm_setting_sriov_get_total_vfs:
  * @setting: the #NMSettingSriov
  *
@@ -790,7 +775,7 @@ nm_setting_sriov_add_vf (NMSettingSriov *setting, NMSriovVF *vf)
 	g_return_if_fail (vf->refcount > 0);
 
 	g_ptr_array_add (setting->vfs, nm_sriov_vf_dup (vf));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+	_notify (setting, PROP_VFS);
 }
 
 /**
@@ -809,7 +794,7 @@ nm_setting_sriov_remove_vf (NMSettingSriov *setting, guint idx)
 	g_return_if_fail (idx < setting->vfs->len);
 
 	g_ptr_array_remove_index (setting->vfs, idx);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+	_notify (setting, PROP_VFS);
 }
 
 /**
@@ -834,7 +819,7 @@ nm_setting_sriov_remove_vf_by_index (NMSettingSriov *setting,
 	for (i = 0; i < setting->vfs->len; i++) {
 		if (nm_sriov_vf_get_index  (setting->vfs->pdata[i]) == index) {
 			g_ptr_array_remove_index (setting->vfs, i);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+			_notify (setting, PROP_VFS);
 			return TRUE;
 		}
 	}
@@ -856,7 +841,7 @@ nm_setting_sriov_clear_vfs (NMSettingSriov *setting)
 
 	if (setting->vfs->len != 0) {
 		g_ptr_array_set_size (setting->vfs, 0);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_SRIOV_VFS);
+		_notify (setting, PROP_VFS);
 	}
 }
 
@@ -934,7 +919,7 @@ vfs_to_dbus (NMSetting *setting, const char *property)
 			GVariantBuilder vf_builder;
 			const guint *vlan_ids;
 			const char **name;
-			guint num_vlans;
+			guint num_vlans = 0;
 
 			g_variant_builder_init (&vf_builder, G_VARIANT_TYPE_VARDICT);
 			g_variant_builder_add (&vf_builder, "{sv}", "index",
@@ -952,7 +937,7 @@ vfs_to_dbus (NMSetting *setting, const char *property)
 
 			/* VLANs are translated into an array of maps, where each map has
 			 * keys 'id', 'qos' and 'proto'. This guarantees enough flexibility
-			 * to accomodate any future new option. */
+			 * to accommodate any future new option. */
 			vlan_ids = nm_sriov_vf_get_vlan_ids (vf, &num_vlans);
 			if (num_vlans) {
 				GVariantBuilder vlans_builder;
@@ -1139,24 +1124,58 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
+                  NMSetting *other,
+                  NMSettingCompareFlags flags)
+{
+	NMSettingSriov *a;
+	NMSettingSriov *b;
+	guint i;
+
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_SRIOV_VFS)) {
+		if (other) {
+			a = NM_SETTING_SRIOV (setting);
+			b = NM_SETTING_SRIOV (other);
+
+			if (a->vfs->len != b->vfs->len)
+				return FALSE;
+			for (i = 0; i < a->vfs->len; i++) {
+				if (!nm_sriov_vf_equal (a->vfs->pdata[i], b->vfs->pdata[i]))
+					return FALSE;
+			}
+		}
+		return TRUE;
+	}
+
+	return NM_SETTING_CLASS (nm_setting_sriov_parent_class)->compare_property (sett_info,
+	                                                                           property_idx,
+	                                                                           setting,
+	                                                                           other,
+	                                                                           flags);
+}
+
+/*****************************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSettingSriov *self = NM_SETTING_SRIOV (object);
 
 	switch (prop_id) {
 	case PROP_TOTAL_VFS:
-		self->total_vfs = g_value_get_uint (value);
+		g_value_set_uint (value, self->total_vfs);
 		break;
 	case PROP_VFS:
-		g_ptr_array_unref (self->vfs);
-		self->vfs = _nm_utils_copy_array (g_value_get_boxed (value),
-		                                  (NMUtilsCopyFunc) nm_sriov_vf_dup,
-		                                  (GDestroyNotify) nm_sriov_vf_unref);
+		g_value_take_boxed (value, _nm_utils_copy_array (self->vfs,
+		                                                 (NMUtilsCopyFunc) nm_sriov_vf_dup,
+		                                                 (GDestroyNotify) nm_sriov_vf_unref));
 		break;
 	case PROP_AUTOPROBE_DRIVERS:
-		self->autoprobe_drivers = g_value_get_enum (value);
+		g_value_set_enum (value, self->autoprobe_drivers);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1165,22 +1184,23 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSettingSriov *self = NM_SETTING_SRIOV (object);
 
 	switch (prop_id) {
 	case PROP_TOTAL_VFS:
-		g_value_set_uint (value, self->total_vfs);
+		self->total_vfs = g_value_get_uint (value);
 		break;
 	case PROP_VFS:
-		g_value_take_boxed (value, _nm_utils_copy_array (self->vfs,
-		                                                 (NMUtilsCopyFunc) nm_sriov_vf_dup,
-		                                                 (GDestroyNotify) nm_sriov_vf_unref));
+		g_ptr_array_unref (self->vfs);
+		self->vfs = _nm_utils_copy_array (g_value_get_boxed (value),
+		                                  (NMUtilsCopyFunc) nm_sriov_vf_dup,
+		                                  (GDestroyNotify) nm_sriov_vf_unref);
 		break;
 	case PROP_AUTOPROBE_DRIVERS:
-		g_value_set_enum (value, self->autoprobe_drivers);
+		self->autoprobe_drivers = g_value_get_enum (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1188,30 +1208,7 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
-static gboolean
-compare_property (NMSetting *setting,
-                  NMSetting *other,
-                  const GParamSpec *prop_spec,
-                  NMSettingCompareFlags flags)
-{
-	NMSettingSriov *a = NM_SETTING_SRIOV (setting);
-	NMSettingSriov *b = NM_SETTING_SRIOV (other);
-	NMSettingClass *setting_class;
-	guint i;
-
-	if (nm_streq (prop_spec->name, NM_SETTING_SRIOV_VFS)) {
-		if (a->vfs->len != b->vfs->len)
-			return FALSE;
-		for (i = 0; i < a->vfs->len; i++) {
-			if (!nm_sriov_vf_equal (a->vfs->pdata[i], b->vfs->pdata[i]))
-				return FALSE;
-		}
-		return TRUE;
-	}
-
-	setting_class = NM_SETTING_CLASS (nm_setting_sriov_parent_class);
-	return setting_class->compare_property (setting, other, prop_spec, flags);
-}
+/*****************************************************************************/
 
 static void
 nm_setting_sriov_init (NMSettingSriov *setting)
@@ -1219,6 +1216,21 @@ nm_setting_sriov_init (NMSettingSriov *setting)
 	setting->vfs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_sriov_vf_unref);
 }
 
+/**
+ * nm_setting_sriov_new:
+ *
+ * Creates a new #NMSettingSriov object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingSriov object
+ *
+ * Since: 1.14
+ **/
+NMSetting *
+nm_setting_sriov_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_SRIOV, NULL);
+}
+
 static void
 finalize (GObject *object)
 {
@@ -1262,14 +1274,13 @@ nm_setting_sriov_class_init (NMSettingSriovClass *klass)
 	 * example: SRIOV_TOTAL_VFS=16
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_TOTAL_VFS,
-		 g_param_spec_uint (NM_SETTING_SRIOV_TOTAL_VFS, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TOTAL_VFS] =
+	    g_param_spec_uint (NM_SETTING_SRIOV_TOTAL_VFS, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingSriov:vfs: (type GPtrArray(NMSriovVF))
@@ -1310,17 +1321,15 @@ nm_setting_sriov_class_init (NMSettingSriovClass *klass)
 	 * example: SRIOV_VF10="mac=00:11:22:33:44:55", ...
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_VFS,
-		 g_param_spec_boxed (NM_SETTING_SRIOV_VFS, "", "",
-		                     G_TYPE_PTR_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_VFS] =
+	    g_param_spec_boxed (NM_SETTING_SRIOV_VFS, "", "",
+	                        G_TYPE_PTR_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_override (properties_override,
-	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                 NM_SETTING_SRIOV_VFS),
+	                                   obj_properties[PROP_VFS],
 	                                   G_VARIANT_TYPE ("aa{sv}"),
 	                                   vfs_to_dbus,
 	                                   vfs_from_dbus,
@@ -1352,15 +1361,16 @@ nm_setting_sriov_class_init (NMSettingSriovClass *klass)
 	 * example: SRIOV_AUTOPROBE_DRIVERS=0,1
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_AUTOPROBE_DRIVERS,
-		 g_param_spec_enum (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, "", "",
-		                    nm_ternary_get_type (),
-		                    NM_TERNARY_DEFAULT,
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_AUTOPROBE_DRIVERS] =
+	    g_param_spec_enum (NM_SETTING_SRIOV_AUTOPROBE_DRIVERS, "", "",
+	                       nm_ternary_get_type (),
+	                       NM_TERNARY_DEFAULT,
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_SRIOV,
 	                               NULL, properties_override);
diff --git a/libnm-core/nm-setting-tc-config.c b/libnm-core/nm-setting-tc-config.c
index 43015d37..7425ae73 100644
--- a/libnm-core/nm-setting-tc-config.c
+++ b/libnm-core/nm-setting-tc-config.c
@@ -19,9 +19,10 @@
 
 #include "nm-default.h"
 
+#include "nm-setting-tc-config.h"
+
 #include <linux/pkt_sched.h>
 
-#include "nm-setting-tc-config.h"
 #include "nm-setting-private.h"
 
 /**
@@ -809,13 +810,10 @@ nm_tc_tfilter_set_action (NMTCTfilter *tfilter, NMTCAction *action)
 
 /*****************************************************************************/
 
-enum {
-	PROP_0,
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingTCConfig,
 	PROP_QDISCS,
 	PROP_TFILTERS,
-
-	LAST_PROP
-};
+);
 
 /**
  * NMSettingTCConfig:
@@ -836,20 +834,7 @@ struct _NMSettingTCConfigClass {
 
 G_DEFINE_TYPE (NMSettingTCConfig, nm_setting_tc_config, NM_TYPE_SETTING)
 
-/**
- * nm_setting_tc_config_new:
- *
- * Creates a new #NMSettingTCConfig object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingTCConfig object
- *
- * Since: 1.12
- **/
-NMSetting *
-nm_setting_tc_config_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_TC_CONFIG, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_tc_config_get_num_qdiscs:
@@ -914,7 +899,7 @@ nm_setting_tc_config_add_qdisc (NMSettingTCConfig *self,
 	}
 
 	g_ptr_array_add (self->qdiscs, nm_tc_qdisc_dup (qdisc));
-	g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS);
+	_notify (self, PROP_QDISCS);
 	return TRUE;
 }
 
@@ -935,7 +920,7 @@ nm_setting_tc_config_remove_qdisc (NMSettingTCConfig *self, guint idx)
 	g_return_if_fail (idx < self->qdiscs->len);
 
 	g_ptr_array_remove_index (self->qdiscs, idx);
-	g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS);
+	_notify (self, PROP_QDISCS);
 }
 
 /**
@@ -961,7 +946,7 @@ nm_setting_tc_config_remove_qdisc_by_value (NMSettingTCConfig *self,
 	for (i = 0; i < self->qdiscs->len; i++) {
 		if (nm_tc_qdisc_equal (self->qdiscs->pdata[i], qdisc)) {
 			g_ptr_array_remove_index (self->qdiscs, i);
-			g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS);
+			_notify (self, PROP_QDISCS);
 			return TRUE;
 		}
 	}
@@ -983,7 +968,7 @@ nm_setting_tc_config_clear_qdiscs (NMSettingTCConfig *self)
 
 	if (self->qdiscs->len != 0) {
 		g_ptr_array_set_size (self->qdiscs, 0);
-		g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_QDISCS);
+		_notify (self, PROP_QDISCS);
 	}
 }
 
@@ -1051,7 +1036,7 @@ nm_setting_tc_config_add_tfilter (NMSettingTCConfig *self,
 	}
 
 	g_ptr_array_add (self->tfilters, nm_tc_tfilter_dup (tfilter));
-	g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS);
+	_notify (self, PROP_TFILTERS);
 	return TRUE;
 }
 
@@ -1071,7 +1056,7 @@ nm_setting_tc_config_remove_tfilter (NMSettingTCConfig *self, guint idx)
 	g_return_if_fail (idx < self->tfilters->len);
 
 	g_ptr_array_remove_index (self->tfilters, idx);
-	g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS);
+	_notify (self, PROP_TFILTERS);
 }
 
 /**
@@ -1097,7 +1082,7 @@ nm_setting_tc_config_remove_tfilter_by_value (NMSettingTCConfig *self,
 	for (i = 0; i < self->tfilters->len; i++) {
 		if (nm_tc_tfilter_equal (self->tfilters->pdata[i], tfilter)) {
 			g_ptr_array_remove_index (self->tfilters, i);
-			g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS);
+			_notify (self, PROP_TFILTERS);
 			return TRUE;
 		}
 	}
@@ -1119,71 +1104,12 @@ nm_setting_tc_config_clear_tfilters (NMSettingTCConfig *self)
 
 	if (self->tfilters->len != 0) {
 		g_ptr_array_set_size (self->tfilters, 0);
-		g_object_notify (G_OBJECT (self), NM_SETTING_TC_CONFIG_TFILTERS);
+		_notify (self, PROP_TFILTERS);
 	}
 }
 
 /*****************************************************************************/
 
-static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
-{
-	NMSettingTCConfig *self = NM_SETTING_TC_CONFIG (object);
-
-	switch (prop_id) {
-	case PROP_QDISCS:
-		g_ptr_array_unref (self->qdiscs);
-		self->qdiscs = _nm_utils_copy_array (g_value_get_boxed (value),
-		                                     (NMUtilsCopyFunc) nm_tc_qdisc_dup,
-		                                     (GDestroyNotify) nm_tc_qdisc_unref);
-		break;
-	case PROP_TFILTERS:
-		g_ptr_array_unref (self->tfilters);
-		self->tfilters = _nm_utils_copy_array (g_value_get_boxed (value),
-		                                       (NMUtilsCopyFunc) nm_tc_tfilter_dup,
-		                                       (GDestroyNotify) nm_tc_tfilter_unref);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
-{
-	NMSettingTCConfig *self = NM_SETTING_TC_CONFIG (object);
-
-	switch (prop_id) {
-	case PROP_QDISCS:
-		g_value_take_boxed (value, _nm_utils_copy_array (self->qdiscs,
-		                                                 (NMUtilsCopyFunc) nm_tc_qdisc_dup,
-		                                                 (GDestroyNotify) nm_tc_qdisc_unref));
-		break;
-	case PROP_TFILTERS:
-		g_value_take_boxed (value, _nm_utils_copy_array (self->tfilters,
-		                                                 (NMUtilsCopyFunc) nm_tc_tfilter_dup,
-		                                                 (GDestroyNotify) nm_tc_tfilter_unref));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
-static void
-finalize (GObject *object)
-{
-	NMSettingTCConfig *self = NM_SETTING_TC_CONFIG (object);
-
-	g_ptr_array_unref (self->qdiscs);
-	g_ptr_array_unref (self->tfilters);
-
-	G_OBJECT_CLASS (nm_setting_tc_config_parent_class)->finalize (object);
-}
-
 static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
@@ -1233,46 +1159,46 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static gboolean
-compare_property (NMSetting *setting,
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
                   NMSetting *other,
-                  const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
 	NMSettingTCConfig *a_tc_config = NM_SETTING_TC_CONFIG (setting);
 	NMSettingTCConfig *b_tc_config = NM_SETTING_TC_CONFIG (other);
-	NMSettingClass *setting_class;
 	guint i;
 
-	if (nm_streq (prop_spec->name, NM_SETTING_TC_CONFIG_QDISCS)) {
-		if (a_tc_config->qdiscs->len != b_tc_config->qdiscs->len)
-			return FALSE;
-		for (i = 0; i < a_tc_config->qdiscs->len; i++) {
-			if (!nm_tc_qdisc_equal (a_tc_config->qdiscs->pdata[i], b_tc_config->qdiscs->pdata[i]))
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TC_CONFIG_QDISCS)) {
+		if (other) {
+			if (a_tc_config->qdiscs->len != b_tc_config->qdiscs->len)
 				return FALSE;
+			for (i = 0; i < a_tc_config->qdiscs->len; i++) {
+				if (!nm_tc_qdisc_equal (a_tc_config->qdiscs->pdata[i], b_tc_config->qdiscs->pdata[i]))
+					return FALSE;
+			}
 		}
 		return TRUE;
 	}
 
-	if (nm_streq (prop_spec->name, NM_SETTING_TC_CONFIG_TFILTERS)) {
-		if (a_tc_config->tfilters->len != b_tc_config->tfilters->len)
-			return FALSE;
-		for (i = 0; i < a_tc_config->tfilters->len; i++) {
-			if (!nm_tc_tfilter_equal (a_tc_config->tfilters->pdata[i], b_tc_config->tfilters->pdata[i]))
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TC_CONFIG_TFILTERS)) {
+		if (other) {
+			if (a_tc_config->tfilters->len != b_tc_config->tfilters->len)
 				return FALSE;
+			for (i = 0; i < a_tc_config->tfilters->len; i++) {
+				if (!nm_tc_tfilter_equal (a_tc_config->tfilters->pdata[i], b_tc_config->tfilters->pdata[i]))
+					return FALSE;
+			}
 		}
 		return TRUE;
 	}
 
-	setting_class = NM_SETTING_CLASS (nm_setting_tc_config_parent_class);
-	return setting_class->compare_property (setting, other, prop_spec, flags);
-}
-
-static void
-nm_setting_tc_config_init (NMSettingTCConfig *self)
-{
-	self->qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref);
-	self->tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref);
+	return NM_SETTING_CLASS (nm_setting_tc_config_parent_class)->compare_property (sett_info,
+	                                                                               property_idx,
+	                                                                               setting,
+	                                                                               other,
+	                                                                               flags);
 }
 
 /**
@@ -1586,6 +1512,91 @@ tc_tfilters_set (NMSetting *setting,
 	return TRUE;
 }
 
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingTCConfig *self = NM_SETTING_TC_CONFIG (object);
+
+	switch (prop_id) {
+	case PROP_QDISCS:
+		g_value_take_boxed (value, _nm_utils_copy_array (self->qdiscs,
+		                                                 (NMUtilsCopyFunc) nm_tc_qdisc_dup,
+		                                                 (GDestroyNotify) nm_tc_qdisc_unref));
+		break;
+	case PROP_TFILTERS:
+		g_value_take_boxed (value, _nm_utils_copy_array (self->tfilters,
+		                                                 (NMUtilsCopyFunc) nm_tc_tfilter_dup,
+		                                                 (GDestroyNotify) nm_tc_tfilter_unref));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSettingTCConfig *self = NM_SETTING_TC_CONFIG (object);
+
+	switch (prop_id) {
+	case PROP_QDISCS:
+		g_ptr_array_unref (self->qdiscs);
+		self->qdiscs = _nm_utils_copy_array (g_value_get_boxed (value),
+		                                     (NMUtilsCopyFunc) nm_tc_qdisc_dup,
+		                                     (GDestroyNotify) nm_tc_qdisc_unref);
+		break;
+	case PROP_TFILTERS:
+		g_ptr_array_unref (self->tfilters);
+		self->tfilters = _nm_utils_copy_array (g_value_get_boxed (value),
+		                                       (NMUtilsCopyFunc) nm_tc_tfilter_dup,
+		                                       (GDestroyNotify) nm_tc_tfilter_unref);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_tc_config_init (NMSettingTCConfig *self)
+{
+	self->qdiscs = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_qdisc_unref);
+	self->tfilters = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_tc_tfilter_unref);
+}
+
+/**
+ * nm_setting_tc_config_new:
+ *
+ * Creates a new #NMSettingTCConfig object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingTCConfig object
+ *
+ * Since: 1.12
+ **/
+NMSetting *
+nm_setting_tc_config_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_TC_CONFIG, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingTCConfig *self = NM_SETTING_TC_CONFIG (object);
+
+	g_ptr_array_unref (self->qdiscs);
+	g_ptr_array_unref (self->tfilters);
+
+	G_OBJECT_CLASS (nm_setting_tc_config_parent_class)->finalize (object);
+}
+
 static void
 nm_setting_tc_config_class_init (NMSettingTCConfigClass *klass)
 {
@@ -1593,8 +1604,8 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *klass)
 	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	object_class->set_property     = set_property;
 	object_class->get_property     = get_property;
+	object_class->set_property     = set_property;
 	object_class->finalize         = finalize;
 
 	setting_class->compare_property = compare_property;
@@ -1612,17 +1623,15 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *klass)
 	 * example: QDISC1=ingress, QDISC2="root handle 1234: fq_codel"
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_QDISCS,
-		 g_param_spec_boxed (NM_SETTING_TC_CONFIG_QDISCS, "", "",
-		                     G_TYPE_PTR_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_QDISCS] =
+	    g_param_spec_boxed (NM_SETTING_TC_CONFIG_QDISCS, "", "",
+	                        G_TYPE_PTR_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_override (properties_override,
-	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                 NM_SETTING_TC_CONFIG_QDISCS),
+	                                   obj_properties[PROP_QDISCS],
 	                                   G_VARIANT_TYPE ("aa{sv}"),
 	                                   tc_qdiscs_get,
 	                                   tc_qdiscs_set,
@@ -1640,22 +1649,22 @@ nm_setting_tc_config_class_init (NMSettingTCConfigClass *klass)
 	 * example: FILTER1="parent ffff: matchall action simple sdata Input", ...
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_TFILTERS,
-		 g_param_spec_boxed (NM_SETTING_TC_CONFIG_TFILTERS, "", "",
-		                     G_TYPE_PTR_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TFILTERS] =
+	    g_param_spec_boxed (NM_SETTING_TC_CONFIG_TFILTERS, "", "",
+	                        G_TYPE_PTR_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_override (properties_override,
-	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                 NM_SETTING_TC_CONFIG_TFILTERS),
+	                                   obj_properties[PROP_TFILTERS],
 	                                   G_VARIANT_TYPE ("aa{sv}"),
 	                                   tc_tfilters_get,
 	                                   tc_tfilters_set,
 	                                   NULL);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_TC_CONFIG,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c
index ee84aaf7..1db8f22a 100644
--- a/libnm-core/nm-setting-team-port.c
+++ b/libnm-core/nm-setting-team-port.c
@@ -21,11 +21,11 @@
 
 #include "nm-default.h"
 
-#include <string.h>
+#include "nm-setting-team-port.h"
+
 #include <ctype.h>
 #include <stdlib.h>
 
-#include "nm-setting-team-port.h"
 #include "nm-utils.h"
 #include "nm-utils-private.h"
 #include "nm-connection-private.h"
@@ -39,9 +39,27 @@
  * optional properties that apply to team ports.
  **/
 
-G_DEFINE_TYPE (NMSettingTeamPort, nm_setting_team_port, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_TEAM_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPortPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingTeamPort,
+	PROP_CONFIG,
+	PROP_QUEUE_ID,
+	PROP_PRIO,
+	PROP_STICKY,
+	PROP_LACP_PRIO,
+	PROP_LACP_KEY,
+	PROP_LINK_WATCHERS,
+);
+
+static const _NMUtilsTeamPropertyKeys _prop_to_keys[_PROPERTY_ENUMS_LAST] = {
+	[PROP_CONFIG] =        { },
+	[PROP_QUEUE_ID] =      { .key1 = "queue_id",   .default_int = NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT, },
+	[PROP_PRIO] =          { .key1 = "prio",       },
+	[PROP_STICKY] =        { .key1 = "sticky",     },
+	[PROP_LACP_PRIO] =     { .key1 = "lacp_prio",  .default_int = NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT, },
+	[PROP_LACP_KEY] =      { .key1 = "lacp_key",   },
+	[PROP_LINK_WATCHERS] = { .key1 = "link_watch", },
+};
 
 typedef struct {
 	char *config;
@@ -53,43 +71,11 @@ typedef struct {
 	GPtrArray *link_watchers; /* Array of NMTeamLinkWatcher */
 } NMSettingTeamPortPrivate;
 
-/* Keep aligned with _prop_to_keys[] */
-enum {
-	PROP_0,
-	PROP_CONFIG,
-	PROP_QUEUE_ID,
-	PROP_PRIO,
-	PROP_STICKY,
-	PROP_LACP_PRIO,
-	PROP_LACP_KEY,
-	PROP_LINK_WATCHERS,
-	LAST_PROP
-};
+G_DEFINE_TYPE (NMSettingTeamPort, nm_setting_team_port, NM_TYPE_SETTING)
 
-/* Keep aligned with team-port properties enum */
-static const _NMUtilsTeamPropertyKeys _prop_to_keys[LAST_PROP] = {
-	[PROP_0] =             { NULL, NULL, NULL, 0 },
-	[PROP_CONFIG] =        { NULL, NULL, NULL, 0 },
-	[PROP_QUEUE_ID] =      { "queue_id", NULL, NULL, NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT },
-	[PROP_PRIO] =          { "prio", NULL, NULL, 0 },
-	[PROP_STICKY] =        { "sticky", NULL, NULL, 0 },
-	[PROP_LACP_PRIO] =     { "lacp_prio", NULL, NULL, NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT },
-	[PROP_LACP_KEY] =      { "lacp_key", NULL, NULL, 0 },
-	[PROP_LINK_WATCHERS] = { "link_watch", NULL, NULL, 0 }
-};
+#define NM_SETTING_TEAM_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM_PORT, NMSettingTeamPortPrivate))
 
-/**
- * nm_setting_team_port_new:
- *
- * Creates a new #NMSettingTeamPort object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingTeamPort object
- **/
-NMSetting *
-nm_setting_team_port_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_TEAM_PORT, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_team_port_get_config:
@@ -251,7 +237,7 @@ nm_setting_team_port_add_link_watcher (NMSettingTeamPort *setting,
 	}
 
 	g_ptr_array_add (priv->link_watchers, nm_team_link_watcher_dup (link_watcher));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS);
+	_notify (setting, PROP_LINK_WATCHERS);
 	return TRUE;
 }
 
@@ -273,7 +259,7 @@ nm_setting_team_port_remove_link_watcher (NMSettingTeamPort *setting, guint idx)
 	g_return_if_fail (idx < priv->link_watchers->len);
 
 	g_ptr_array_remove_index (priv->link_watchers, idx);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS);
+	_notify (setting, PROP_LINK_WATCHERS);
 }
 
 /**
@@ -299,7 +285,7 @@ nm_setting_team_port_remove_link_watcher_by_value (NMSettingTeamPort *setting,
 	for (i = 0; i < priv->link_watchers->len; i++) {
 		if (nm_team_link_watcher_equal (priv->link_watchers->pdata[i], link_watcher)) {
 			g_ptr_array_remove_index (priv->link_watchers, i);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS);
+			_notify (setting, PROP_LINK_WATCHERS);
 			return TRUE;
 		}
 	}
@@ -323,7 +309,7 @@ nm_setting_team_port_clear_link_watchers (NMSettingTeamPort *setting)
 
 	if (priv->link_watchers->len != 0) {
 		g_ptr_array_set_size (priv->link_watchers, 0);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_PORT_LINK_WATCHERS);
+		_notify (setting, PROP_LINK_WATCHERS);
 	}
 }
 
@@ -401,62 +387,109 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static gboolean
-compare_property (NMSetting *setting,
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
                   NMSetting *other,
-                  const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	NMSettingClass *setting_class;
-	NMSettingTeamPortPrivate *a_priv, *b_priv;
+	NMSettingTeamPortPrivate *a_priv;
+	NMSettingTeamPortPrivate *b_priv;
 	guint i, j;
 
-	/* If we are trying to match a connection in order to assume it (and thus
-	 * @flags contains INFERRABLE), use the "relaxed" matching for team
-	 * configuration. Otherwise, for all other purposes (including connection
-	 * comparison before an update), resort to the default string comparison.
-	 */
-	if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)
-	    && nm_streq0 (prop_spec->name, NM_SETTING_TEAM_PORT_CONFIG)) {
-		return _nm_utils_team_config_equal (NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->config,
-		                                    NM_SETTING_TEAM_PORT_GET_PRIVATE (other)->config,
-		                                    TRUE);
-	}
-	if (nm_streq0 (prop_spec->name, NM_SETTING_TEAM_PORT_LINK_WATCHERS)) {
-		a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting);
-		b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (other);
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_PORT_LINK_WATCHERS)) {
 
-		if (a_priv->link_watchers->len != b_priv->link_watchers->len)
-			return FALSE;
-		for (i = 0; i < a_priv->link_watchers->len; i++) {
-			for (j = 0; j < b_priv->link_watchers->len; j++) {
-				if (nm_team_link_watcher_equal (a_priv->link_watchers->pdata[i],
-				                                b_priv->link_watchers->pdata[j])) {
-					break;
+		if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+			return NM_TERNARY_DEFAULT;
+
+		if (other) {
+			a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting);
+			b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (other);
+
+			if (a_priv->link_watchers->len != b_priv->link_watchers->len)
+				return FALSE;
+			for (i = 0; i < a_priv->link_watchers->len; i++) {
+				for (j = 0; j < b_priv->link_watchers->len; j++) {
+					if (nm_team_link_watcher_equal (a_priv->link_watchers->pdata[i],
+					                                b_priv->link_watchers->pdata[j])) {
+						break;
+					}
 				}
+				if (j == b_priv->link_watchers->len)
+					return FALSE;
 			}
-			if (j == b_priv->link_watchers->len)
-				return FALSE;
 		}
 		return TRUE;
 	}
 
-	setting_class = NM_SETTING_CLASS (nm_setting_team_port_parent_class);
-	return setting_class->compare_property (setting, other, prop_spec, flags);
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_PORT_CONFIG)) {
+		if (other) {
+			a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting);
+			b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (other);
+
+			if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)) {
+				/* If we are trying to match a connection in order to assume it (and thus
+				 * @flags contains INFERRABLE), use the "relaxed" matching for team
+				 * configuration. Otherwise, for all other purposes (including connection
+				 * comparison before an update), resort to the default string comparison. */
+				return _nm_utils_team_config_equal (a_priv->config,
+				                                    b_priv->config,
+				                                    TRUE);
+			}
+
+			return nm_streq0 (a_priv->config, b_priv->config);
+		}
+
+		return TRUE;
+	}
+
+	return NM_SETTING_CLASS (nm_setting_team_port_parent_class)->compare_property (sett_info,
+	                                                                               property_idx,
+	                                                                               setting,
+	                                                                               other,
+	                                                                               flags);
 }
 
+/*****************************************************************************/
+
 static void
-nm_setting_team_port_init (NMSettingTeamPort *setting)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
+	NMSettingTeamPort *setting = NM_SETTING_TEAM_PORT (object);
 	NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting);
 
-	priv->queue_id = NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT;
-	priv->lacp_prio = NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT;
-	priv->link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
+	switch (prop_id) {
+	case PROP_CONFIG:
+		g_value_set_string (value, nm_setting_team_port_get_config (setting));
+		break;
+	case PROP_QUEUE_ID:
+		g_value_set_int (value, priv->queue_id);
+		break;
+	case PROP_PRIO:
+		g_value_set_int (value, priv->prio);
+		break;
+	case PROP_STICKY:
+		g_value_set_boolean (value, priv->sticky);
+		break;
+	case PROP_LACP_PRIO:
+		g_value_set_int (value, priv->lacp_prio);
+		break;
+	case PROP_LACP_KEY:
+		g_value_set_int (value, priv->lacp_key);
+		break;
+	case PROP_LINK_WATCHERS:
+		g_value_take_boxed (value, _nm_utils_copy_array (priv->link_watchers,
+		                                                 (NMUtilsCopyFunc) nm_team_link_watcher_dup,
+		                                                 (GDestroyNotify) nm_team_link_watcher_unref));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
-#define JSON_TO_VAL(typ, id)   _nm_utils_json_extract_##typ (priv->config, _prop_to_keys[id], TRUE)
-
 static void
 set_property (GObject *object, guint prop_id,
               const GValue *value, GParamSpec *pspec)
@@ -465,6 +498,8 @@ set_property (GObject *object, guint prop_id,
 	const GValue *align_value = NULL;
 	gboolean align_config = FALSE;
 
+#define JSON_TO_VAL(typ, id)   _nm_utils_json_extract_##typ (priv->config, _prop_to_keys[id], TRUE)
+
 	switch (prop_id) {
 	case PROP_CONFIG:
 		g_free (priv->config);
@@ -536,41 +571,29 @@ set_property (GObject *object, guint prop_id,
 		_nm_utils_json_append_gvalue (&priv->config, _prop_to_keys[prop_id], align_value);
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_team_port_init (NMSettingTeamPort *setting)
 {
-	NMSettingTeamPort *setting = NM_SETTING_TEAM_PORT (object);
 	NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting);
 
-	switch (prop_id) {
-	case PROP_CONFIG:
-		g_value_set_string (value, nm_setting_team_port_get_config (setting));
-		break;
-	case PROP_QUEUE_ID:
-		g_value_set_int (value, priv->queue_id);
-		break;
-	case PROP_PRIO:
-		g_value_set_int (value, priv->prio);
-		break;
-	case PROP_STICKY:
-		g_value_set_boolean (value, priv->sticky);
-		break;
-	case PROP_LACP_PRIO:
-		g_value_set_int (value, priv->lacp_prio);
-		break;
-	case PROP_LACP_KEY:
-		g_value_set_int (value, priv->lacp_key);
-		break;
-	case PROP_LINK_WATCHERS:
-		g_value_take_boxed (value, _nm_utils_copy_array (priv->link_watchers,
-		                                                 (NMUtilsCopyFunc) nm_team_link_watcher_dup,
-		                                                 (GDestroyNotify) nm_team_link_watcher_unref));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	priv->queue_id = NM_SETTING_TEAM_PORT_QUEUE_ID_DEFAULT;
+	priv->lacp_prio = NM_SETTING_TEAM_PORT_LACP_PRIO_DEFAULT;
+	priv->link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
+}
+
+/**
+ * nm_setting_team_port_new:
+ *
+ * Creates a new #NMSettingTeamPort object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingTeamPort object
+ **/
+NMSetting *
+nm_setting_team_port_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_TEAM_PORT, NULL);
 }
 
 static void
@@ -593,8 +616,8 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingTeamPortPrivate));
 
-	object_class->set_property     = set_property;
 	object_class->get_property     = get_property;
+	object_class->set_property     = set_property;
 	object_class->finalize         = finalize;
 
 	setting_class->compare_property = compare_property;
@@ -614,13 +637,12 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass)
 	 * description: Team port configuration in JSON. See man teamd.conf for details.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CONFIG,
-		 g_param_spec_string (NM_SETTING_TEAM_PORT_CONFIG, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CONFIG] =
+	    g_param_spec_string (NM_SETTING_TEAM_PORT_CONFIG, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeamPort:queue-id:
@@ -630,12 +652,11 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_QUEUE_ID,
-		 g_param_spec_int (NM_SETTING_TEAM_PORT_QUEUE_ID, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_QUEUE_ID] =
+	    g_param_spec_int (NM_SETTING_TEAM_PORT_QUEUE_ID, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeamPort:prio:
@@ -644,12 +665,11 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PRIO,
-		 g_param_spec_int (NM_SETTING_TEAM_PORT_PRIO, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PRIO] =
+	    g_param_spec_int (NM_SETTING_TEAM_PORT_PRIO, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeamPort:sticky:
@@ -658,12 +678,11 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_STICKY,
-		 g_param_spec_boolean (NM_SETTING_TEAM_PORT_STICKY, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_STICKY] =
+	    g_param_spec_boolean (NM_SETTING_TEAM_PORT_STICKY, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeamPort:lacp-prio:
@@ -672,12 +691,11 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_LACP_PRIO,
-		 g_param_spec_int (NM_SETTING_TEAM_PORT_LACP_PRIO, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LACP_PRIO] =
+	    g_param_spec_int (NM_SETTING_TEAM_PORT_LACP_PRIO, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeamPort:lacp-key:
@@ -686,12 +704,11 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_LACP_KEY,
-		 g_param_spec_int (NM_SETTING_TEAM_PORT_LACP_KEY, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LACP_KEY] =
+	    g_param_spec_int (NM_SETTING_TEAM_PORT_LACP_KEY, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeamPort:link-watchers: (type GPtrArray(NMTeamLinkWatcher))
@@ -703,24 +720,24 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *klass)
 	 * Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait';
 	 * nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host';
 	 * arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active',
-	 * 'validate-incative', 'send-always'. See teamd.conf man for more details.
+	 * 'validate-inactive', 'send-always'. See teamd.conf man for more details.
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_LINK_WATCHERS,
-		 g_param_spec_boxed (NM_SETTING_TEAM_PORT_LINK_WATCHERS, "", "",
-		                     G_TYPE_PTR_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LINK_WATCHERS] =
+	    g_param_spec_boxed (NM_SETTING_TEAM_PORT_LINK_WATCHERS, "", "",
+	                        G_TYPE_PTR_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_TEAM_PORT_LINK_WATCHERS),
+	                                    obj_properties[PROP_LINK_WATCHERS],
 	                                    G_VARIANT_TYPE ("aa{sv}"),
 	                                    team_link_watchers_to_dbus,
 	                                    team_link_watchers_from_dbus);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_TEAM_PORT,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c
index 3d151462..42ee3eec 100644
--- a/libnm-core/nm-setting-team.c
+++ b/libnm-core/nm-setting-team.c
@@ -21,10 +21,10 @@
 
 #include "nm-default.h"
 
-#include <string.h>
+#include "nm-setting-team.h"
+
 #include <stdlib.h>
 
-#include "nm-setting-team.h"
 #include "nm-utils.h"
 #include "nm-utils-private.h"
 #include "nm-connection-private.h"
@@ -86,6 +86,7 @@ struct NMTeamLinkWatcher {
 			char *target_host;
 			char *source_host;
 			NMTeamLinkWatcherArpPingFlags flags;
+			int vlanid;
 		} arp_ping;
 	};
 };
@@ -233,6 +234,45 @@ nm_team_link_watcher_new_arp_ping (int init_wait,
                                    NMTeamLinkWatcherArpPingFlags flags,
                                    GError **error)
 {
+	return nm_team_link_watcher_new_arp_ping2 (init_wait,
+	                                           interval,
+	                                           missed_max,
+	                                           -1,
+	                                           target_host,
+	                                           source_host,
+	                                           flags,
+	                                           error);
+}
+
+/**
+ * nm_team_link_watcher_new_arp_ping2:
+ * @init_wait: init_wait value
+ * @interval: interval value
+ * @missed_max: missed_max value
+ * @vlanid: vlanid value
+ * @target_host: the host name or the ip address that will be used as destination
+ *   address in the arp request
+ * @source_host: the host name or the ip address that will be used as source
+ *   address in the arp request
+ * @flags: the watcher #NMTeamLinkWatcherArpPingFlags
+ * @error: (out) (allow-none): location to store the error on failure
+ *
+ * Creates a new arp_ping #NMTeamLinkWatcher object
+ *
+ * Returns: (transfer full): the new #NMTeamLinkWatcher object, or %NULL on error
+ *
+ * Since: 1.16
+ **/
+NMTeamLinkWatcher *
+nm_team_link_watcher_new_arp_ping2 (int init_wait,
+                                    int interval,
+                                    int missed_max,
+                                    int vlanid,
+                                    const char *target_host,
+                                    const char *source_host,
+                                    NMTeamLinkWatcherArpPingFlags flags,
+                                    GError **error)
+{
 	NMTeamLinkWatcher *watcher;
 	const char *val_fail = NULL;
 
@@ -255,11 +295,11 @@ nm_team_link_watcher_new_arp_ping (int init_wait,
 		return NULL;
 	}
 
-	if (init_wait < 0 || !_NM_INT_LE_MAXINT32 (init_wait))
+	else if (init_wait < 0 || !_NM_INT_LE_MAXINT32 (init_wait))
 		val_fail = "init-wait";
-	if (interval < 0 || !_NM_INT_LE_MAXINT32 (interval))
+	else if (interval < 0 || !_NM_INT_LE_MAXINT32 (interval))
 		val_fail = "interval";
-	if (missed_max < 0 || !_NM_INT_LE_MAXINT32 (missed_max))
+	else if (missed_max < 0 || !_NM_INT_LE_MAXINT32 (missed_max))
 		val_fail = "missed-max";
 	if (val_fail) {
 		g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED,
@@ -267,6 +307,12 @@ nm_team_link_watcher_new_arp_ping (int init_wait,
 		return NULL;
 	}
 
+	if (vlanid < -1 || vlanid > 4094) {
+		g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED,
+		                     _("vlanid is out of range [-1, 4094]"));
+		return NULL;
+	}
+
 	watcher = g_slice_new0 (NMTeamLinkWatcher);
 	watcher->refcount = 1;
 
@@ -277,6 +323,7 @@ nm_team_link_watcher_new_arp_ping (int init_wait,
 	watcher->arp_ping.target_host = g_strdup (target_host);
 	watcher->arp_ping.source_host = g_strdup (source_host);
 	watcher->arp_ping.flags = flags;
+	watcher->arp_ping.vlanid = vlanid;
 
 	return watcher;
 }
@@ -342,6 +389,7 @@ nm_team_link_watcher_equal (NMTeamLinkWatcher *watcher, NMTeamLinkWatcher *other
 	    || watcher->arp_ping.init_wait != other->arp_ping.init_wait
 	    || watcher->arp_ping.interval != other->arp_ping.interval
 	    || watcher->arp_ping.missed_max != other->arp_ping.missed_max
+	    || watcher->arp_ping.vlanid != other->arp_ping.vlanid
 	    || watcher->arp_ping.flags != other->arp_ping.flags)
 		return FALSE;
 
@@ -377,12 +425,13 @@ nm_team_link_watcher_dup (NMTeamLinkWatcher *watcher)
 		                                           NULL);
 		break;
 	case LINK_WATCHER_ARP_PING:
-		return nm_team_link_watcher_new_arp_ping (watcher->arp_ping.init_wait,
-		                                          watcher->arp_ping.interval,
-		                                          watcher->arp_ping.missed_max,
-		                                          watcher->arp_ping.target_host,
-		                                          watcher->arp_ping.source_host,
-		                                          watcher->arp_ping.flags,
+		return nm_team_link_watcher_new_arp_ping2 (watcher->arp_ping.init_wait,
+		                                           watcher->arp_ping.interval,
+		                                           watcher->arp_ping.missed_max,
+		                                           watcher->arp_ping.vlanid,
+		                                           watcher->arp_ping.target_host,
+		                                           watcher->arp_ping.source_host,
+		                                           watcher->arp_ping.flags,
 		                                          NULL);
 	default:
 		g_assert_not_reached ();
@@ -411,7 +460,7 @@ nm_team_link_watcher_get_name (NMTeamLinkWatcher *watcher)
  * @watcher: the #NMTeamLinkWatcher
  *
  * Gets the delay_up interval (in milliseconds) that elapses between the link
- * coming up and the runner beeing notified about it.
+ * coming up and the runner being notified about it.
  *
  * Since: 1.12
  **/
@@ -430,7 +479,7 @@ nm_team_link_watcher_get_delay_up (NMTeamLinkWatcher *watcher)
  * @watcher: the #NMTeamLinkWatcher
  *
  * Gets the delay_down interval (in milliseconds) that elapses between the link
- * going down and the runner beeing notified about it.
+ * going down and the runner being notified about it.
  *
  * Since: 1.12
  **/
@@ -507,6 +556,24 @@ nm_team_link_watcher_get_missed_max (NMTeamLinkWatcher *watcher)
 }
 
 /**
+ * nm_team_link_watcher_get_vlanid:
+ * @watcher: the #NMTeamLinkWatcher
+ *
+ * Gets the VLAN tag ID to be used to outgoing link probes
+ *
+ * Since: 1.16
+ **/
+int
+nm_team_link_watcher_get_vlanid (NMTeamLinkWatcher *watcher)
+{
+	_CHECK_WATCHER (watcher, -1);
+
+	if (watcher->type != LINK_WATCHER_ARP_PING)
+		return -1;
+	return watcher->arp_ping.vlanid;
+}
+
+/**
  * nm_team_link_watcher_get_target_host:
  * @watcher: the #NMTeamLinkWatcher
  *
@@ -557,9 +624,43 @@ nm_team_link_watcher_get_flags (NMTeamLinkWatcher *watcher)
 
 /*****************************************************************************/
 
-G_DEFINE_TYPE (NMSettingTeam, nm_setting_team, NM_TYPE_SETTING)
-
-#define NM_SETTING_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM, NMSettingTeamPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingTeam,
+	PROP_CONFIG,
+	PROP_NOTIFY_PEERS_COUNT,
+	PROP_NOTIFY_PEERS_INTERVAL,
+	PROP_MCAST_REJOIN_COUNT,
+	PROP_MCAST_REJOIN_INTERVAL,
+	PROP_RUNNER,
+	PROP_RUNNER_HWADDR_POLICY,
+	PROP_RUNNER_TX_HASH,
+	PROP_RUNNER_TX_BALANCER,
+	PROP_RUNNER_TX_BALANCER_INTERVAL,
+	PROP_RUNNER_ACTIVE,
+	PROP_RUNNER_FAST_RATE,
+	PROP_RUNNER_SYS_PRIO,
+	PROP_RUNNER_MIN_PORTS,
+	PROP_RUNNER_AGG_SELECT_POLICY,
+	PROP_LINK_WATCHERS,
+);
+
+static const _NMUtilsTeamPropertyKeys _prop_to_keys[_PROPERTY_ENUMS_LAST] = {
+	[PROP_CONFIG] =                      { },
+	[PROP_NOTIFY_PEERS_COUNT] =          { .key1 = "notify_peers", .key2 = "count",                                           },
+	[PROP_NOTIFY_PEERS_INTERVAL] =       { .key1 = "notify_peers", .key2 = "interval",                                        },
+	[PROP_MCAST_REJOIN_COUNT] =          { .key1 = "mcast_rejoin", .key2 = "count",                                           },
+	[PROP_MCAST_REJOIN_INTERVAL] =       { .key1 = "mcast_rejoin", .key2 = "interval",                                        },
+	[PROP_RUNNER] =                      { .key1 = "runner",       .key2 = "name",                                            .default_str = NM_SETTING_TEAM_RUNNER_DEFAULT, },
+	[PROP_RUNNER_HWADDR_POLICY] =        { .key1 = "runner",       .key2 = "hwaddr_policy",                                   },
+	[PROP_RUNNER_TX_HASH] =              { .key1 = "runner",       .key2 = "tx_hash",                                         },
+	[PROP_RUNNER_TX_BALANCER] =          { .key1 = "runner",       .key2 = "tx_balancer",       .key3 = "name", },
+	[PROP_RUNNER_TX_BALANCER_INTERVAL] = { .key1 = "runner",       .key2 = "tx_balancer",       .key3 = "balancing_interval", .default_int = -1 },
+	[PROP_RUNNER_ACTIVE] =               { .key1 = "runner",       .key2 = "active",                                          },
+	[PROP_RUNNER_FAST_RATE] =            { .key1 = "runner",       .key2 = "fast_rate",                                       },
+	[PROP_RUNNER_SYS_PRIO] =             { .key1 = "runner",       .key2 = "sys_prio",                                        .default_int = -1, },
+	[PROP_RUNNER_MIN_PORTS] =            { .key1 = "runner",       .key2 = "min_ports",                                       .default_int = -1, },
+	[PROP_RUNNER_AGG_SELECT_POLICY] =    { .key1 = "runner",       .key2 = "agg_select_policy",                               },
+	[PROP_LINK_WATCHERS] =               { .key1 = "link_watch",                                                              },
+};
 
 typedef struct {
 	char *config;
@@ -580,62 +681,11 @@ typedef struct {
 	GPtrArray *link_watchers; /* Array of NMTeamLinkWatcher */
 } NMSettingTeamPrivate;
 
-/* Keep aligned with _prop_to_keys[] */
-enum {
-	PROP_0,
-	PROP_CONFIG,
-	PROP_NOTIFY_PEERS_COUNT,
-	PROP_NOTIFY_PEERS_INTERVAL,
-	PROP_MCAST_REJOIN_COUNT,
-	PROP_MCAST_REJOIN_INTERVAL,
-	PROP_RUNNER,
-	PROP_RUNNER_HWADDR_POLICY,
-	PROP_RUNNER_TX_HASH,
-	PROP_RUNNER_TX_BALANCER,
-	PROP_RUNNER_TX_BALANCER_INTERVAL,
-	PROP_RUNNER_ACTIVE,
-	PROP_RUNNER_FAST_RATE,
-	PROP_RUNNER_SYS_PRIO,
-	PROP_RUNNER_MIN_PORTS,
-	PROP_RUNNER_AGG_SELECT_POLICY,
-	PROP_LINK_WATCHERS,
-	LAST_PROP
-};
+G_DEFINE_TYPE (NMSettingTeam, nm_setting_team, NM_TYPE_SETTING)
 
-/* Keep aligned with team properties enum */
-static const _NMUtilsTeamPropertyKeys _prop_to_keys[LAST_PROP] = {
-	[PROP_0] =                           { NULL, NULL, NULL, 0 },
-	[PROP_CONFIG] =                      { NULL, NULL, NULL, 0 },
-	[PROP_NOTIFY_PEERS_COUNT] =          { "notify_peers", "count", NULL, 0 },
-	[PROP_NOTIFY_PEERS_INTERVAL] =       { "notify_peers", "interval", NULL, 0 },
-	[PROP_MCAST_REJOIN_COUNT] =          { "mcast_rejoin", "count", NULL, 0 },
-	[PROP_MCAST_REJOIN_INTERVAL] =       { "mcast_rejoin", "interval", NULL, 0 },
-	[PROP_RUNNER] =                      { "runner", "name", NULL,
-	                                       {.default_str = NM_SETTING_TEAM_RUNNER_DEFAULT} },
-	[PROP_RUNNER_HWADDR_POLICY] =        { "runner", "hwaddr_policy", NULL, 0 },
-	[PROP_RUNNER_TX_HASH] =              { "runner", "tx_hash", NULL, 0 },
-	[PROP_RUNNER_TX_BALANCER] =          { "runner", "tx_balancer", "name", 0 },
-	[PROP_RUNNER_TX_BALANCER_INTERVAL] = { "runner", "tx_balancer", "balancing_interval", -1 },
-	[PROP_RUNNER_ACTIVE] =               { "runner", "active", NULL, 0 },
-	[PROP_RUNNER_FAST_RATE] =            { "runner", "fast_rate", NULL, 0 },
-	[PROP_RUNNER_SYS_PRIO] =             { "runner", "sys_prio", NULL, -1 },
-	[PROP_RUNNER_MIN_PORTS] =            { "runner", "min_ports", NULL, -1 },
-	[PROP_RUNNER_AGG_SELECT_POLICY] =    { "runner", "agg_select_policy", NULL, 0 },
-	[PROP_LINK_WATCHERS] =               { "link_watch", NULL, NULL, 0 }
-};
+#define NM_SETTING_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TEAM, NMSettingTeamPrivate))
 
-/**
- * nm_setting_team_new:
- *
- * Creates a new #NMSettingTeam object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingTeam object
- **/
-NMSetting *
-nm_setting_team_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_TEAM, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_team_get_config:
@@ -884,7 +934,7 @@ nm_setting_team_remove_runner_tx_hash_by_value (NMSettingTeam *setting,
 	for (i = 0; i < priv->runner_tx_hash->len; i++) {
 		if (nm_streq (txhash, priv->runner_tx_hash->pdata[i])) {
 			g_ptr_array_remove_index (priv->runner_tx_hash, i);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_RUNNER_TX_HASH);
+			_notify (setting, PROP_RUNNER_TX_HASH);
 			return TRUE;
 		}
 	}
@@ -947,7 +997,7 @@ nm_setting_team_remove_runner_tx_hash (NMSettingTeam *setting, guint idx)
 	g_return_if_fail (idx < priv->runner_tx_hash->len);
 
 	g_ptr_array_remove_index (priv->runner_tx_hash, idx);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_RUNNER_TX_HASH);
+	_notify (setting, PROP_RUNNER_TX_HASH);
 }
 
 /**
@@ -980,7 +1030,7 @@ nm_setting_team_add_runner_tx_hash (NMSettingTeam *setting, const char *txhash)
 	}
 
 	g_ptr_array_add (priv->runner_tx_hash, g_strdup (txhash));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_RUNNER_TX_HASH);
+	_notify (setting, PROP_RUNNER_TX_HASH);
 	return TRUE;
 }
 
@@ -1050,7 +1100,7 @@ nm_setting_team_add_link_watcher (NMSettingTeam *setting,
 	}
 
 	g_ptr_array_add (priv->link_watchers, nm_team_link_watcher_dup (link_watcher));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS);
+	_notify (setting, PROP_LINK_WATCHERS);
 	return TRUE;
 }
 
@@ -1072,7 +1122,7 @@ nm_setting_team_remove_link_watcher (NMSettingTeam *setting, guint idx)
 	g_return_if_fail (idx < priv->link_watchers->len);
 
 	g_ptr_array_remove_index (priv->link_watchers, idx);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS);
+	_notify (setting, PROP_LINK_WATCHERS);
 }
 
 /**
@@ -1098,7 +1148,7 @@ nm_setting_team_remove_link_watcher_by_value (NMSettingTeam *setting,
 	for (i = 0; i < priv->link_watchers->len; i++) {
 		if (nm_team_link_watcher_equal (priv->link_watchers->pdata[i], link_watcher)) {
 			g_ptr_array_remove_index (priv->link_watchers, i);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS);
+			_notify (setting, PROP_LINK_WATCHERS);
 			return TRUE;
 		}
 	}
@@ -1121,7 +1171,7 @@ nm_setting_team_clear_link_watchers (NMSettingTeam *setting) {
 
 	if (priv->link_watchers->len != 0) {
 		g_ptr_array_set_size (priv->link_watchers, 0);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_TEAM_LINK_WATCHERS);
+		_notify (setting, PROP_LINK_WATCHERS);
 	}
 }
 
@@ -1230,38 +1280,33 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static gboolean
-compare_property (NMSetting *setting,
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
                   NMSetting *other,
-                  const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
 	NMSettingTeamPrivate *a_priv, *b_priv;
-	NMSettingClass *setting_class;
 	guint i, j;
 
-	/* If we are trying to match a connection in order to assume it (and thus
-	 * @flags contains INFERRABLE), use the "relaxed" matching for team
-	 * configuration. Otherwise, for all other purposes (including connection
-	 * comparison before an update), resort to the default string comparison.
-	 */
-	if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)
-	    && nm_streq0 (prop_spec->name, NM_SETTING_TEAM_CONFIG)) {
-		return _nm_utils_team_config_equal (NM_SETTING_TEAM_GET_PRIVATE (setting)->config,
-		                                    NM_SETTING_TEAM_GET_PRIVATE (other)->config,
-		                                    FALSE);
-	}
-	if (nm_streq0 (prop_spec->name, NM_SETTING_TEAM_LINK_WATCHERS)) {
-		a_priv = NM_SETTING_TEAM_GET_PRIVATE (setting);
-		b_priv = NM_SETTING_TEAM_GET_PRIVATE (other);
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_LINK_WATCHERS)) {
 
-		if (a_priv->link_watchers->len != b_priv->link_watchers->len)
-			return FALSE;
-		for (i = 0; i < a_priv->link_watchers->len; i++) {
-			for (j = 0; j < b_priv->link_watchers->len; j++) {
-				if (nm_team_link_watcher_equal (a_priv->link_watchers->pdata[i],
-				                                b_priv->link_watchers->pdata[j])) {
-					break;
+		if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+			return NM_TERNARY_DEFAULT;
+
+		if (other) {
+			a_priv = NM_SETTING_TEAM_GET_PRIVATE (setting);
+			b_priv = NM_SETTING_TEAM_GET_PRIVATE (other);
+
+			if (a_priv->link_watchers->len != b_priv->link_watchers->len)
+				return FALSE;
+			for (i = 0; i < a_priv->link_watchers->len; i++) {
+				for (j = 0; j < b_priv->link_watchers->len; j++) {
+					if (nm_team_link_watcher_equal (a_priv->link_watchers->pdata[i],
+					                                b_priv->link_watchers->pdata[j])) {
+						break;
+					}
 				}
 				if (j == b_priv->link_watchers->len)
 					return FALSE;
@@ -1270,37 +1315,32 @@ compare_property (NMSetting *setting,
 		return TRUE;
 	}
 
-	setting_class = NM_SETTING_CLASS (nm_setting_team_parent_class);
-	return setting_class->compare_property (setting, other, prop_spec, flags);
-}
-
-static void
-nm_setting_team_init (NMSettingTeam *setting)
-{
-	NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting);
-
-	priv->runner = g_strdup (NM_SETTING_TEAM_RUNNER_ROUNDROBIN);
-	priv->runner_tx_balancer_interval = -1;
-	priv->runner_sys_prio = -1;
-	priv->runner_min_ports = -1;
-	priv->link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
-}
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_CONFIG)) {
+		if (other) {
+			a_priv = NM_SETTING_TEAM_GET_PRIVATE (setting);
+			b_priv = NM_SETTING_TEAM_GET_PRIVATE (other);
+
+			if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)) {
+				/* If we are trying to match a connection in order to assume it (and thus
+				 * @flags contains INFERRABLE), use the "relaxed" matching for team
+				 * configuration. Otherwise, for all other purposes (including connection
+				 * comparison before an update), resort to the default string comparison. */
+				return _nm_utils_team_config_equal (a_priv->config,
+				                                    b_priv->config,
+				                                    TRUE);
+			}
 
-static void
-finalize (GObject *object)
-{
-	NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (object);
+			return nm_streq0 (a_priv->config, b_priv->config);
+		}
 
-	g_free (priv->config);
-	g_free (priv->runner);
-	g_free (priv->runner_hwaddr_policy);
-	g_free (priv->runner_tx_balancer);
-	g_free (priv->runner_agg_select_policy);
-	if (priv->runner_tx_hash)
-		g_ptr_array_unref (priv->runner_tx_hash);
-	g_ptr_array_unref (priv->link_watchers);
+		return TRUE;
+	}
 
-	G_OBJECT_CLASS (nm_setting_team_parent_class)->finalize (object);
+	return NM_SETTING_CLASS (nm_setting_team_parent_class)->compare_property (sett_info,
+	                                                                          property_idx,
+	                                                                          setting,
+	                                                                          other,
+	                                                                          flags);
 }
 
 #define JSON_TO_VAL(typ, id)   _nm_utils_json_extract_##typ (priv->config, _prop_to_keys[id], FALSE)
@@ -1347,6 +1387,73 @@ _align_team_properties (NMSettingTeam *setting)
 	priv->link_watchers = JSON_TO_VAL (ptr_array, PROP_LINK_WATCHERS);
 }
 
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingTeam *setting = NM_SETTING_TEAM (object);
+	NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting);
+
+	switch (prop_id) {
+	case PROP_CONFIG:
+		g_value_set_string (value, nm_setting_team_get_config (setting));
+		break;
+	case PROP_NOTIFY_PEERS_COUNT:
+		g_value_set_int (value, priv->notify_peers_count);
+		break;
+	case PROP_NOTIFY_PEERS_INTERVAL:
+		g_value_set_int (value, priv->notify_peers_interval);
+		break;
+	case PROP_MCAST_REJOIN_COUNT:
+		g_value_set_int (value, priv->mcast_rejoin_count);
+		break;
+	case PROP_MCAST_REJOIN_INTERVAL:
+		g_value_set_int (value, priv->mcast_rejoin_interval);
+		break;
+	case PROP_RUNNER:
+		g_value_set_string (value, nm_setting_team_get_runner (setting));
+		break;
+	case PROP_RUNNER_HWADDR_POLICY:
+		g_value_set_string (value, nm_setting_team_get_runner_hwaddr_policy (setting));
+		break;
+	case PROP_RUNNER_TX_HASH:
+		g_value_take_boxed (value, priv->runner_tx_hash ?
+		                    _nm_utils_ptrarray_to_strv (priv->runner_tx_hash): NULL);
+		break;
+	case PROP_RUNNER_TX_BALANCER:
+		g_value_set_string (value, nm_setting_team_get_runner_tx_balancer (setting));
+		break;
+	case PROP_RUNNER_TX_BALANCER_INTERVAL:
+		g_value_set_int (value, priv->runner_tx_balancer_interval);
+		break;
+	case PROP_RUNNER_ACTIVE:
+		g_value_set_boolean (value, nm_setting_team_get_runner_active (setting));
+		break;
+	case PROP_RUNNER_FAST_RATE:
+		g_value_set_boolean (value, nm_setting_team_get_runner_fast_rate (setting));
+		break;
+	case PROP_RUNNER_SYS_PRIO:
+		g_value_set_int (value, priv->runner_sys_prio);
+		break;
+	case PROP_RUNNER_MIN_PORTS:
+		g_value_set_int (value, priv->runner_min_ports);
+		break;
+	case PROP_RUNNER_AGG_SELECT_POLICY:
+		g_value_set_string (value, nm_setting_team_get_runner_agg_select_policy (setting));
+		break;
+	case PROP_LINK_WATCHERS:
+		g_value_take_boxed (value, _nm_utils_copy_array (priv->link_watchers,
+		                                                 (NMUtilsCopyFunc) nm_team_link_watcher_dup,
+		                                                 (GDestroyNotify) nm_team_link_watcher_unref));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
 static void
 set_property (GObject *object, guint prop_id,
               const GValue *value, GParamSpec *pspec)
@@ -1490,69 +1597,48 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_team_init (NMSettingTeam *setting)
 {
-	NMSettingTeam *setting = NM_SETTING_TEAM (object);
 	NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting);
 
-	switch (prop_id) {
-	case PROP_CONFIG:
-		g_value_set_string (value, nm_setting_team_get_config (setting));
-		break;
-	case PROP_NOTIFY_PEERS_COUNT:
-		g_value_set_int (value, priv->notify_peers_count);
-		break;
-	case PROP_NOTIFY_PEERS_INTERVAL:
-		g_value_set_int (value, priv->notify_peers_interval);
-		break;
-	case PROP_MCAST_REJOIN_COUNT:
-		g_value_set_int (value, priv->mcast_rejoin_count);
-		break;
-	case PROP_MCAST_REJOIN_INTERVAL:
-		g_value_set_int (value, priv->mcast_rejoin_interval);
-		break;
-	case PROP_RUNNER:
-		g_value_set_string (value, nm_setting_team_get_runner (setting));
-		break;
-	case PROP_RUNNER_HWADDR_POLICY:
-		g_value_set_string (value, nm_setting_team_get_runner_hwaddr_policy (setting));
-		break;
-	case PROP_RUNNER_TX_HASH:
-		g_value_take_boxed (value, priv->runner_tx_hash ?
-		                    _nm_utils_ptrarray_to_strv (priv->runner_tx_hash): NULL);
-		break;
-	case PROP_RUNNER_TX_BALANCER:
-		g_value_set_string (value, nm_setting_team_get_runner_tx_balancer (setting));
-		break;
-	case PROP_RUNNER_TX_BALANCER_INTERVAL:
-		g_value_set_int (value, priv->runner_tx_balancer_interval);
-		break;
-	case PROP_RUNNER_ACTIVE:
-		g_value_set_boolean (value, nm_setting_team_get_runner_active (setting));
-		break;
-	case PROP_RUNNER_FAST_RATE:
-		g_value_set_boolean (value, nm_setting_team_get_runner_fast_rate (setting));
-		break;
-	case PROP_RUNNER_SYS_PRIO:
-		g_value_set_int (value, priv->runner_sys_prio);
-		break;
-	case PROP_RUNNER_MIN_PORTS:
-		g_value_set_int (value, priv->runner_min_ports);
-		break;
-	case PROP_RUNNER_AGG_SELECT_POLICY:
-		g_value_set_string (value, nm_setting_team_get_runner_agg_select_policy (setting));
-		break;
-	case PROP_LINK_WATCHERS:
-		g_value_take_boxed (value, _nm_utils_copy_array (priv->link_watchers,
-		                                                 (NMUtilsCopyFunc) nm_team_link_watcher_dup,
-		                                                 (GDestroyNotify) nm_team_link_watcher_unref));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	priv->runner = g_strdup (NM_SETTING_TEAM_RUNNER_ROUNDROBIN);
+	priv->runner_tx_balancer_interval = -1;
+	priv->runner_sys_prio = -1;
+	priv->runner_min_ports = -1;
+	priv->link_watchers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_team_link_watcher_unref);
+}
+
+/**
+ * nm_setting_team_new:
+ *
+ * Creates a new #NMSettingTeam object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingTeam object
+ **/
+NMSetting *
+nm_setting_team_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_TEAM, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (object);
+
+	g_free (priv->config);
+	g_free (priv->runner);
+	g_free (priv->runner_hwaddr_policy);
+	g_free (priv->runner_tx_balancer);
+	g_free (priv->runner_agg_select_policy);
+	if (priv->runner_tx_hash)
+		g_ptr_array_unref (priv->runner_tx_hash);
+	g_ptr_array_unref (priv->link_watchers);
+
+	G_OBJECT_CLASS (nm_setting_team_parent_class)->finalize (object);
 }
 
 static void
@@ -1564,8 +1650,8 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingTeamPrivate));
 
-	object_class->set_property     = set_property;
 	object_class->get_property     = get_property;
+	object_class->set_property     = set_property;
 	object_class->finalize         = finalize;
 
 	setting_class->compare_property = compare_property;
@@ -1585,13 +1671,12 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 * description: Team configuration in JSON. See man teamd.conf for details.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CONFIG,
-		 g_param_spec_string (NM_SETTING_TEAM_CONFIG, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CONFIG] =
+	    g_param_spec_string (NM_SETTING_TEAM_CONFIG, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:notify-peers-count:
@@ -1600,12 +1685,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NOTIFY_PEERS_COUNT,
-		 g_param_spec_int (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NOTIFY_PEERS_COUNT] =
+	    g_param_spec_int (NM_SETTING_TEAM_NOTIFY_PEERS_COUNT, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:notify-peers-interval:
@@ -1614,12 +1698,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NOTIFY_PEERS_INTERVAL,
-		 g_param_spec_int (NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NOTIFY_PEERS_INTERVAL] =
+	    g_param_spec_int (NM_SETTING_TEAM_NOTIFY_PEERS_INTERVAL, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:mcast-rejoin-count:
@@ -1628,12 +1711,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_MCAST_REJOIN_COUNT,
-		 g_param_spec_int (NM_SETTING_TEAM_MCAST_REJOIN_COUNT, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MCAST_REJOIN_COUNT] =
+	    g_param_spec_int (NM_SETTING_TEAM_MCAST_REJOIN_COUNT, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:mcast-rejoin-interval:
@@ -1642,12 +1724,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_MCAST_REJOIN_INTERVAL,
-		 g_param_spec_int (NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MCAST_REJOIN_INTERVAL] =
+	    g_param_spec_int (NM_SETTING_TEAM_MCAST_REJOIN_INTERVAL, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:runner:
@@ -1662,12 +1743,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_RUNNER,
-		 g_param_spec_string (NM_SETTING_TEAM_RUNNER, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RUNNER] =
+	    g_param_spec_string (NM_SETTING_TEAM_RUNNER, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:runner-hwaddr-policy:
@@ -1676,12 +1756,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_RUNNER_HWADDR_POLICY,
-		 g_param_spec_string (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RUNNER_HWADDR_POLICY] =
+	    g_param_spec_string (NM_SETTING_TEAM_RUNNER_HWADDR_POLICY, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:runner-tx-hash:
@@ -1690,13 +1769,12 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_RUNNER_TX_HASH,
-		 g_param_spec_boxed (NM_SETTING_TEAM_RUNNER_TX_HASH, "", "",
-		                     G_TYPE_STRV,
+	obj_properties[PROP_RUNNER_TX_HASH] =
+	    g_param_spec_boxed (NM_SETTING_TEAM_RUNNER_TX_HASH, "", "",
+	                        G_TYPE_STRV,
 	                             G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-	                             G_PARAM_STATIC_STRINGS));
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                             G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:runner-tx-balancer:
@@ -1705,12 +1783,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_RUNNER_TX_BALANCER,
-		 g_param_spec_string (NM_SETTING_TEAM_RUNNER_TX_BALANCER, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RUNNER_TX_BALANCER] =
+	    g_param_spec_string (NM_SETTING_TEAM_RUNNER_TX_BALANCER, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:runner-tx-balancer-interval:
@@ -1719,12 +1796,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_RUNNER_TX_BALANCER_INTERVAL,
-		 g_param_spec_int (NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RUNNER_TX_BALANCER_INTERVAL] =
+	    g_param_spec_int (NM_SETTING_TEAM_RUNNER_TX_BALANCER_INTERVAL, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:runner-active:
@@ -1733,12 +1809,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_RUNNER_ACTIVE,
-		 g_param_spec_boolean (NM_SETTING_TEAM_RUNNER_ACTIVE, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RUNNER_ACTIVE] =
+	    g_param_spec_boolean (NM_SETTING_TEAM_RUNNER_ACTIVE, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:runner-fast-rate:
@@ -1747,12 +1822,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_RUNNER_FAST_RATE,
-		 g_param_spec_boolean (NM_SETTING_TEAM_RUNNER_FAST_RATE, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RUNNER_FAST_RATE] =
+	    g_param_spec_boolean (NM_SETTING_TEAM_RUNNER_FAST_RATE, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:runner-sys-prio:
@@ -1761,12 +1835,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_RUNNER_SYS_PRIO,
-		 g_param_spec_int (NM_SETTING_TEAM_RUNNER_SYS_PRIO, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RUNNER_SYS_PRIO] =
+	    g_param_spec_int (NM_SETTING_TEAM_RUNNER_SYS_PRIO, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:runner-min-ports:
@@ -1775,12 +1848,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_RUNNER_MIN_PORTS,
-		 g_param_spec_int (NM_SETTING_TEAM_RUNNER_MIN_PORTS, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RUNNER_MIN_PORTS] =
+	    g_param_spec_int (NM_SETTING_TEAM_RUNNER_MIN_PORTS, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:runner-agg-select-policy:
@@ -1789,12 +1861,11 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_RUNNER_AGG_SELECT_POLICY,
-		 g_param_spec_string (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RUNNER_AGG_SELECT_POLICY] =
+	    g_param_spec_string (NM_SETTING_TEAM_RUNNER_AGG_SELECT_POLICY, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTeam:link-watchers: (type GPtrArray(NMTeamLinkWatcher))
@@ -1806,20 +1877,18 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	 * Available keys are:   ethtool: 'delay-up', 'delay-down', 'init-wait';
 	 * nsna_ping: 'init-wait', 'interval', 'missed-max', 'target-host';
 	 * arp_ping: all the ones in nsna_ping and 'source-host', 'validate-active',
-	 * 'validate-incative', 'send-always'. See teamd.conf man for more details.
+	 * 'validate-inactive', 'send-always'. See teamd.conf man for more details.
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_LINK_WATCHERS,
-		 g_param_spec_boxed (NM_SETTING_TEAM_LINK_WATCHERS, "", "",
-		                     G_TYPE_PTR_ARRAY,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LINK_WATCHERS] =
+	    g_param_spec_boxed (NM_SETTING_TEAM_LINK_WATCHERS, "", "",
+	                        G_TYPE_PTR_ARRAY,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_TEAM_LINK_WATCHERS),
+	                                    obj_properties[PROP_LINK_WATCHERS],
 	                                    G_VARIANT_TYPE ("aa{sv}"),
 	                                    team_link_watchers_to_dbus,
 	                                    team_link_watchers_from_dbus);
@@ -1838,6 +1907,8 @@ nm_setting_team_class_init (NMSettingTeamClass *klass)
 	                                    _nm_setting_get_deprecated_virtual_interface_name,
 	                                    NULL);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_TEAM,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-team.h b/libnm-core/nm-setting-team.h
index a600053a..a6ef387f 100644
--- a/libnm-core/nm-setting-team.h
+++ b/libnm-core/nm-setting-team.h
@@ -43,9 +43,9 @@ G_BEGIN_DECLS
  */
 typedef enum { /*< flags >*/
 	NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE              = 0, /*< skip >*/
-	NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE   = (1 << 1),
-	NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE = (1 << 2),
-	NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS       = (1 << 3)
+	NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE   = 0x2,
+	NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_INACTIVE = 0x4,
+	NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS       = 0x8,
 } NMTeamLinkWatcherArpPingFlags;
 
 #define NM_TEAM_LINK_WATCHER_ETHTOOL   "ethtool"
@@ -74,6 +74,15 @@ NMTeamLinkWatcher *nm_team_link_watcher_new_arp_ping (int init_wait,
                                                       const char *source_host,
                                                       NMTeamLinkWatcherArpPingFlags flags,
                                                       GError **error);
+NM_AVAILABLE_IN_1_16
+NMTeamLinkWatcher *nm_team_link_watcher_new_arp_ping2 (int init_wait,
+                                                       int interval,
+                                                       int missed_max,
+                                                       int vlanid,
+                                                       const char *target_host,
+                                                       const char *source_host,
+                                                       NMTeamLinkWatcherArpPingFlags flags,
+                                                       GError **error);
 NM_AVAILABLE_IN_1_12
 void nm_team_link_watcher_ref                    (NMTeamLinkWatcher *watcher);
 NM_AVAILABLE_IN_1_12
@@ -100,6 +109,8 @@ NM_AVAILABLE_IN_1_12
 const char *nm_team_link_watcher_get_source_host (NMTeamLinkWatcher *watcher);
 NM_AVAILABLE_IN_1_12
 NMTeamLinkWatcherArpPingFlags nm_team_link_watcher_get_flags (NMTeamLinkWatcher *watcher);
+NM_AVAILABLE_IN_1_16
+int nm_team_link_watcher_get_vlanid              (NMTeamLinkWatcher *watcher);
 
 #define NM_TYPE_SETTING_TEAM            (nm_setting_team_get_type ())
 #define NM_SETTING_TEAM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_TEAM, NMSettingTeam))
diff --git a/libnm-core/nm-setting-tun.c b/libnm-core/nm-setting-tun.c
index 3e033e25..9801f5cf 100644
--- a/libnm-core/nm-setting-tun.c
+++ b/libnm-core/nm-setting-tun.c
@@ -21,10 +21,10 @@
 
 #include "nm-default.h"
 
+#include "nm-setting-tun.h"
+
 #include <stdlib.h>
-#include <string.h>
 
-#include "nm-setting-tun.h"
 #include "nm-utils.h"
 #include "nm-setting-connection.h"
 #include "nm-setting-private.h"
@@ -38,9 +38,16 @@
  * necessary for connection to TUN/TAP interfaces.
  **/
 
-G_DEFINE_TYPE (NMSettingTun, nm_setting_tun, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_TUN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TUN, NMSettingTunPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_MODE,
+	PROP_OWNER,
+	PROP_GROUP,
+	PROP_PI,
+	PROP_VNET_HDR,
+	PROP_MULTI_QUEUE,
+);
 
 typedef struct {
 	NMSettingTunMode mode;
@@ -51,31 +58,11 @@ typedef struct {
 	gboolean multi_queue;
 } NMSettingTunPrivate;
 
-enum {
-	PROP_0,
-	PROP_MODE,
-	PROP_OWNER,
-	PROP_GROUP,
-	PROP_PI,
-	PROP_VNET_HDR,
-	PROP_MULTI_QUEUE,
-	LAST_PROP
-};
+G_DEFINE_TYPE (NMSettingTun, nm_setting_tun, NM_TYPE_SETTING)
 
-/**
- * nm_setting_tun_new:
- *
- * Creates a new #NMSettingTun object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingTun object
- *
- * Since: 1.2
- **/
-NMSetting *
-nm_setting_tun_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_TUN, NULL);
-}
+#define NM_SETTING_TUN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_TUN, NMSettingTunPrivate))
+
+/*****************************************************************************/
 
 /**
  * nm_setting_tun_get_mode:
@@ -167,11 +154,6 @@ nm_setting_tun_get_multi_queue (NMSettingTun *setting)
 	return NM_SETTING_TUN_GET_PRIVATE (setting)->multi_queue;
 }
 
-static void
-nm_setting_tun_init (NMSettingTun *setting)
-{
-}
-
 static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
@@ -212,70 +194,94 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
+/*****************************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSettingTun *setting = NM_SETTING_TUN (object);
 	NMSettingTunPrivate *priv = NM_SETTING_TUN_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_MODE:
-		priv->mode = g_value_get_uint (value);
+		g_value_set_uint (value, priv->mode);
 		break;
 	case PROP_OWNER:
-		g_free (priv->owner);
-		priv->owner = g_value_dup_string (value);
+		g_value_set_string (value, priv->owner);
 		break;
 	case PROP_GROUP:
-		g_free (priv->group);
-		priv->group = g_value_dup_string (value);
+		g_value_set_string (value, priv->group);
 		break;
 	case PROP_PI:
-		priv->pi = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->pi);
 		break;
 	case PROP_VNET_HDR:
-		priv->vnet_hdr = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->vnet_hdr);
 		break;
 	case PROP_MULTI_QUEUE:
-		priv->multi_queue = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->multi_queue);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
 	}
 }
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSettingTun *setting = NM_SETTING_TUN (object);
 	NMSettingTunPrivate *priv = NM_SETTING_TUN_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_MODE:
-		g_value_set_uint (value, priv->mode);
+		priv->mode = g_value_get_uint (value);
 		break;
 	case PROP_OWNER:
-		g_value_set_string (value, priv->owner);
+		g_free (priv->owner);
+		priv->owner = g_value_dup_string (value);
 		break;
 	case PROP_GROUP:
-		g_value_set_string (value, priv->group);
+		g_free (priv->group);
+		priv->group = g_value_dup_string (value);
 		break;
 	case PROP_PI:
-		g_value_set_boolean (value, priv->pi);
+		priv->pi = g_value_get_boolean (value);
 		break;
 	case PROP_VNET_HDR:
-		g_value_set_boolean (value, priv->vnet_hdr);
+		priv->vnet_hdr = g_value_get_boolean (value);
 		break;
 	case PROP_MULTI_QUEUE:
-		g_value_set_boolean (value, priv->multi_queue);
+		priv->multi_queue = g_value_get_boolean (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
 	}
 }
+/*****************************************************************************/
+
+static void
+nm_setting_tun_init (NMSettingTun *setting)
+{
+}
+
+/**
+ * nm_setting_tun_new:
+ *
+ * Creates a new #NMSettingTun object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingTun object
+ *
+ * Since: 1.2
+ **/
+NMSetting *
+nm_setting_tun_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_TUN, NULL);
+}
 
 static void
 finalize (GObject *object)
@@ -297,8 +303,8 @@ nm_setting_tun_class_init (NMSettingTunClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingTunPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -313,14 +319,13 @@ nm_setting_tun_class_init (NMSettingTunClass *klass)
 	 *
 	 * Since: 1.2
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MODE,
-		 g_param_spec_uint (NM_SETTING_TUN_MODE, "", "",
-		                    0, G_MAXUINT, NM_SETTING_TUN_MODE_TUN,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MODE] =
+	    g_param_spec_uint (NM_SETTING_TUN_MODE, "", "",
+	                       0, G_MAXUINT, NM_SETTING_TUN_MODE_TUN,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTun:owner:
@@ -330,13 +335,12 @@ nm_setting_tun_class_init (NMSettingTunClass *klass)
 	 *
 	 * Since: 1.2
 	 */
-	g_object_class_install_property
-		(object_class, PROP_OWNER,
-		 g_param_spec_string (NM_SETTING_TUN_OWNER, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_OWNER] =
+	    g_param_spec_string (NM_SETTING_TUN_OWNER, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTun:group:
@@ -346,13 +350,12 @@ nm_setting_tun_class_init (NMSettingTunClass *klass)
 	 *
 	 * Since: 1.2
 	 */
-	g_object_class_install_property
-		(object_class, PROP_GROUP,
-		 g_param_spec_string (NM_SETTING_TUN_GROUP, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_GROUP] =
+	    g_param_spec_string (NM_SETTING_TUN_GROUP, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTun:pi:
@@ -362,13 +365,12 @@ nm_setting_tun_class_init (NMSettingTunClass *klass)
 	 *
 	 * Since: 1.2
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PI,
-		 g_param_spec_boolean (NM_SETTING_TUN_PI, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PI] =
+	    g_param_spec_boolean (NM_SETTING_TUN_PI, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTun:vnet-hdr:
@@ -378,13 +380,12 @@ nm_setting_tun_class_init (NMSettingTunClass *klass)
 	 *
 	 * Since: 1.2
 	 */
-	g_object_class_install_property
-		(object_class, PROP_VNET_HDR,
-		 g_param_spec_boolean (NM_SETTING_TUN_VNET_HDR, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_VNET_HDR] =
+	    g_param_spec_boolean (NM_SETTING_TUN_VNET_HDR, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingTun:multi-queue:
@@ -396,13 +397,14 @@ nm_setting_tun_class_init (NMSettingTunClass *klass)
 	 *
 	 * Since: 1.2
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MULTI_QUEUE,
-		 g_param_spec_boolean (NM_SETTING_TUN_MULTI_QUEUE, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MULTI_QUEUE] =
+	    g_param_spec_boolean (NM_SETTING_TUN_MULTI_QUEUE, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_TUN);
 }
diff --git a/libnm-core/nm-setting-user.c b/libnm-core/nm-setting-user.c
index d72c9094..01ac671b 100644
--- a/libnm-core/nm-setting-user.c
+++ b/libnm-core/nm-setting-user.c
@@ -395,33 +395,34 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static gboolean
-compare_property (NMSetting *setting,
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
                   NMSetting *other,
-                  const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
 	NMSettingUserPrivate *priv, *pri2;
 
-	g_return_val_if_fail (NM_IS_SETTING_USER (setting), FALSE);
-	g_return_val_if_fail (NM_IS_SETTING_USER (other), FALSE);
-
-	if (!nm_streq0 (prop_spec->name, NM_SETTING_USER_DATA))
-		goto call_parent;
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_USER_DATA)) {
 
-	priv = NM_SETTING_USER_GET_PRIVATE (NM_SETTING_USER (setting));
-	pri2 = NM_SETTING_USER_GET_PRIVATE (NM_SETTING_USER (other));
-
-	if (!nm_utils_hash_table_equal (priv->data, pri2->data, TRUE, g_str_equal))
-		return FALSE;
+		if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+			return NM_TERNARY_DEFAULT;
 
-	if (!nm_utils_hash_table_equal (priv->data_invalid, pri2->data_invalid, TRUE, g_str_equal))
-		return FALSE;
+		if (!other)
+			return TRUE;
 
-	return TRUE;
+		priv = NM_SETTING_USER_GET_PRIVATE (NM_SETTING_USER (setting));
+		pri2 = NM_SETTING_USER_GET_PRIVATE (NM_SETTING_USER (other));
+		return    nm_utils_hash_table_equal (priv->data, pri2->data, TRUE, g_str_equal)
+		       && nm_utils_hash_table_equal (priv->data_invalid, pri2->data_invalid, TRUE, g_str_equal);
+	}
 
-call_parent:
-	return NM_SETTING_CLASS (nm_setting_user_parent_class)->compare_property (setting, other, prop_spec, flags);
+	return NM_SETTING_CLASS (nm_setting_user_parent_class)->compare_property (sett_info,
+	                                                                          property_idx,
+	                                                                          setting,
+	                                                                          other,
+	                                                                          flags);
 }
 
 /*****************************************************************************/
@@ -549,8 +550,8 @@ nm_setting_user_class_init (NMSettingUserClass *klass)
 	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
 	GArray *properties_override = _nm_sett_info_property_override_create_array ();
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->compare_property = compare_property;
@@ -583,15 +584,14 @@ nm_setting_user_class_init (NMSettingUserClass *klass)
 	                        G_PARAM_READWRITE |
 	                        G_PARAM_STATIC_STRINGS);
 
-	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
-
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_USER_DATA),
+	                                    obj_properties[PROP_DATA],
 	                                    G_VARIANT_TYPE ("a{ss}"),
 	                                    _nm_utils_strdict_to_dbus,
 	                                    _nm_utils_strdict_from_dbus);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_USER,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-vlan.c b/libnm-core/nm-setting-vlan.c
index 1b61da55..5b8a49d4 100644
--- a/libnm-core/nm-setting-vlan.c
+++ b/libnm-core/nm-setting-vlan.c
@@ -24,7 +24,6 @@
 #include "nm-setting-vlan.h"
 
 #include <stdlib.h>
-#include <string.h>
 
 #include "nm-utils.h"
 #include "nm-core-types-internal.h"
@@ -41,9 +40,15 @@
  * necessary for connection to VLAN interfaces.
  **/
 
-G_DEFINE_TYPE (NMSettingVlan, nm_setting_vlan, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VLAN, NMSettingVlanPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingVlan,
+	PROP_PARENT,
+	PROP_ID,
+	PROP_FLAGS,
+	PROP_INGRESS_PRIORITY_MAP,
+	PROP_EGRESS_PRIORITY_MAP,
+);
 
 typedef struct {
 	char *parent;
@@ -53,33 +58,16 @@ typedef struct {
 	GSList *egress_priority_map;
 } NMSettingVlanPrivate;
 
-enum {
-	PROP_0,
-	PROP_PARENT,
-	PROP_ID,
-	PROP_FLAGS,
-	PROP_INGRESS_PRIORITY_MAP,
-	PROP_EGRESS_PRIORITY_MAP,
-	LAST_PROP
-};
+G_DEFINE_TYPE (NMSettingVlan, nm_setting_vlan, NM_TYPE_SETTING)
+
+#define NM_SETTING_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VLAN, NMSettingVlanPrivate))
+
+/*****************************************************************************/
 
 #define MAX_SKB_PRIO   G_MAXUINT32
 #define MAX_8021P_PRIO 7  /* Max 802.1p priority */
 
 /**
- * nm_setting_vlan_new:
- *
- * Creates a new #NMSettingVlan object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingVlan object
- **/
-NMSetting *
-nm_setting_vlan_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_VLAN, NULL);
-}
-
-/**
  * nm_setting_vlan_get_parent:
  * @setting: the #NMSettingVlan
  *
@@ -214,10 +202,10 @@ set_map (NMSettingVlan *self, NMVlanPriorityMap map, GSList *list)
 
 	if (map == NM_VLAN_INGRESS_MAP) {
 		NM_SETTING_VLAN_GET_PRIVATE (self)->ingress_priority_map = list;
-		g_object_notify (G_OBJECT (self), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP);
+		_notify (self, PROP_INGRESS_PRIORITY_MAP);
 	} else if (map == NM_VLAN_EGRESS_MAP) {
 		NM_SETTING_VLAN_GET_PRIVATE (self)->egress_priority_map = list;
-		g_object_notify (G_OBJECT (self), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP);
+		_notify (self, PROP_EGRESS_PRIORITY_MAP);
 	} else
 		g_assert_not_reached ();
 }
@@ -273,9 +261,9 @@ nm_setting_vlan_add_priority_str (NMSettingVlan *setting,
 	if (check_replace_duplicate_priority (list, item->from, item->to)) {
 		g_free (item);
 		if (map == NM_VLAN_INGRESS_MAP)
-			g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP);
+			_notify (setting, PROP_INGRESS_PRIORITY_MAP);
 		else
-			g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP);
+			_notify (setting, PROP_EGRESS_PRIORITY_MAP);
 		return TRUE;
 	}
 
@@ -377,9 +365,9 @@ nm_setting_vlan_add_priority (NMSettingVlan *setting,
 	list = get_map (setting, map);
 	if (check_replace_duplicate_priority (list, from, to)) {
 		if (map == NM_VLAN_INGRESS_MAP)
-			g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_INGRESS_PRIORITY_MAP);
+			_notify (setting, PROP_INGRESS_PRIORITY_MAP);
 		else
-			g_object_notify (G_OBJECT (setting), NM_SETTING_VLAN_EGRESS_PRIORITY_MAP);
+			_notify (setting, PROP_EGRESS_PRIORITY_MAP);
 		return TRUE;
 	}
 
@@ -593,11 +581,6 @@ nm_setting_vlan_clear_priorities (NMSettingVlan *setting, NMVlanPriorityMap map)
 
 /*****************************************************************************/
 
-static void
-nm_setting_vlan_init (NMSettingVlan *setting)
-{
-}
-
 static int
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
@@ -736,38 +719,6 @@ priority_strv_to_maplist (NMVlanPriorityMap map, char **strv)
 	return g_slist_sort (list, prio_map_compare);
 }
 
-static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
-{
-	NMSettingVlan *setting = NM_SETTING_VLAN (object);
-	NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting);
-
-	switch (prop_id) {
-	case PROP_PARENT:
-		g_free (priv->parent);
-		priv->parent = g_value_dup_string (value);
-		break;
-	case PROP_ID:
-		priv->id = g_value_get_uint (value);
-		break;
-	case PROP_FLAGS:
-		priv->flags = g_value_get_flags (value);
-		break;
-	case PROP_INGRESS_PRIORITY_MAP:
-		g_slist_free_full (priv->ingress_priority_map, g_free);
-		priv->ingress_priority_map = priority_strv_to_maplist (NM_VLAN_INGRESS_MAP, g_value_get_boxed (value));
-		break;
-	case PROP_EGRESS_PRIORITY_MAP:
-		g_slist_free_full (priv->egress_priority_map, g_free);
-		priv->egress_priority_map = priority_strv_to_maplist (NM_VLAN_EGRESS_MAP, g_value_get_boxed (value));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
-}
-
 static char **
 priority_maplist_to_strv (GSList *list)
 {
@@ -786,6 +737,8 @@ priority_maplist_to_strv (GSList *list)
 	return (char **) g_ptr_array_free (strv, FALSE);
 }
 
+/*****************************************************************************/
+
 static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
@@ -816,6 +769,58 @@ get_property (GObject *object, guint prop_id,
 }
 
 static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSettingVlan *setting = NM_SETTING_VLAN (object);
+	NMSettingVlanPrivate *priv = NM_SETTING_VLAN_GET_PRIVATE (setting);
+
+	switch (prop_id) {
+	case PROP_PARENT:
+		g_free (priv->parent);
+		priv->parent = g_value_dup_string (value);
+		break;
+	case PROP_ID:
+		priv->id = g_value_get_uint (value);
+		break;
+	case PROP_FLAGS:
+		priv->flags = g_value_get_flags (value);
+		break;
+	case PROP_INGRESS_PRIORITY_MAP:
+		g_slist_free_full (priv->ingress_priority_map, g_free);
+		priv->ingress_priority_map = priority_strv_to_maplist (NM_VLAN_INGRESS_MAP, g_value_get_boxed (value));
+		break;
+	case PROP_EGRESS_PRIORITY_MAP:
+		g_slist_free_full (priv->egress_priority_map, g_free);
+		priv->egress_priority_map = priority_strv_to_maplist (NM_VLAN_EGRESS_MAP, g_value_get_boxed (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_vlan_init (NMSettingVlan *setting)
+{
+}
+
+/**
+ * nm_setting_vlan_new:
+ *
+ * Creates a new #NMSettingVlan object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingVlan object
+ **/
+NMSetting *
+nm_setting_vlan_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_VLAN, NULL);
+}
+
+static void
 finalize (GObject *object)
 {
 	NMSettingVlan *setting = NM_SETTING_VLAN (object);
@@ -837,8 +842,8 @@ nm_setting_vlan_class_init (NMSettingVlanClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingVlanPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -857,14 +862,13 @@ nm_setting_vlan_class_init (NMSettingVlanClass *klass)
 	 * description: Parent interface of the VLAN.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PARENT,
-		 g_param_spec_string (NM_SETTING_VLAN_PARENT, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PARENT] =
+	    g_param_spec_string (NM_SETTING_VLAN_PARENT, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVlan:id:
@@ -878,14 +882,13 @@ nm_setting_vlan_class_init (NMSettingVlanClass *klass)
 	 * description: VLAN identifier.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_ID,
-		 g_param_spec_uint (NM_SETTING_VLAN_ID, "", "",
-		                    0, 4095, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ID] =
+	    g_param_spec_uint (NM_SETTING_VLAN_ID, "", "",
+	                       0, 4095, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVlan:flags:
@@ -909,19 +912,17 @@ nm_setting_vlan_class_init (NMSettingVlanClass *klass)
 	 * description: VLAN flags.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_FLAGS,
-		 g_param_spec_flags (NM_SETTING_VLAN_FLAGS, "", "",
-		                     NM_TYPE_VLAN_FLAGS,
-		                     NM_VLAN_FLAG_REORDER_HEADERS,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_CONSTRUCT |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_VLAN_FLAGS, "", "",
+	                        NM_TYPE_VLAN_FLAGS,
+	                        NM_VLAN_FLAG_REORDER_HEADERS,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_CONSTRUCT |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_override (properties_override,
-	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                 NM_SETTING_VLAN_FLAGS),
+	                                   obj_properties[PROP_FLAGS],
 	                                   NULL,
 	                                   _override_flags_get,
 	                                   NULL,
@@ -941,13 +942,12 @@ nm_setting_vlan_class_init (NMSettingVlanClass *klass)
 	 * example: VLAN_INGRESS_PRIORITY_MAP=4:2,3:5
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_INGRESS_PRIORITY_MAP,
-		 g_param_spec_boxed (NM_SETTING_VLAN_INGRESS_PRIORITY_MAP, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_INGRESS_PRIORITY_MAP] =
+	    g_param_spec_boxed (NM_SETTING_VLAN_INGRESS_PRIORITY_MAP, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVlan:egress-priority-map:
@@ -963,13 +963,12 @@ nm_setting_vlan_class_init (NMSettingVlanClass *klass)
 	 * example: VLAN_EGRESS_PRIORITY_MAP=5:4,4:1,3:7
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_EGRESS_PRIORITY_MAP,
-		 g_param_spec_boxed (NM_SETTING_VLAN_EGRESS_PRIORITY_MAP, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_EGRESS_PRIORITY_MAP] =
+	    g_param_spec_boxed (NM_SETTING_VLAN_EGRESS_PRIORITY_MAP, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/* ---ifcfg-rh---
 	 * property: interface-name
@@ -993,6 +992,8 @@ nm_setting_vlan_class_init (NMSettingVlanClass *klass)
 	                                    _nm_setting_get_deprecated_virtual_interface_name,
 	                                    NULL);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_VLAN,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-vpn.c b/libnm-core/nm-setting-vpn.c
index fcbeec68..20a0ae14 100644
--- a/libnm-core/nm-setting-vpn.c
+++ b/libnm-core/nm-setting-vpn.c
@@ -21,13 +21,11 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-#include <errno.h>
+#include "nm-setting-vpn.h"
+
 #include <stdlib.h>
 
 #include "nm-utils/nm-secret-utils.h"
-
-#include "nm-setting-vpn.h"
 #include "nm-utils.h"
 #include "nm-utils-private.h"
 #include "nm-setting-private.h"
@@ -45,9 +43,16 @@
  * properties.
  **/
 
-G_DEFINE_TYPE (NMSettingVpn, nm_setting_vpn, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_VPN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VPN, NMSettingVpnPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingVpn,
+	PROP_SERVICE_TYPE,
+	PROP_USER_NAME,
+	PROP_PERSISTENT,
+	PROP_DATA,
+	PROP_SECRETS,
+	PROP_TIMEOUT,
+);
 
 typedef struct {
 	char *service_type;
@@ -85,30 +90,11 @@ typedef struct {
 	guint32 timeout;
 } NMSettingVpnPrivate;
 
-enum {
-	PROP_0,
-	PROP_SERVICE_TYPE,
-	PROP_USER_NAME,
-	PROP_PERSISTENT,
-	PROP_DATA,
-	PROP_SECRETS,
-	PROP_TIMEOUT,
+G_DEFINE_TYPE (NMSettingVpn, nm_setting_vpn, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_VPN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VPN, NMSettingVpnPrivate))
 
-/**
- * nm_setting_vpn_new:
- *
- * Creates a new #NMSettingVpn object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingVpn object
- **/
-NMSetting *
-nm_setting_vpn_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_VPN, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_vpn_get_service_type:
@@ -194,7 +180,7 @@ nm_setting_vpn_add_data_item (NMSettingVpn *setting,
 
 	g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data,
 	                     g_strdup (key), g_strdup (item));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_DATA);
+	_notify (setting, PROP_DATA);
 }
 
 /**
@@ -218,7 +204,7 @@ 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
+ * @out_length: (allow-none) (out): the length of the returned array
  *
  * Retrieves every data key inside @setting, as an array.
  *
@@ -259,7 +245,7 @@ nm_setting_vpn_remove_data_item (NMSettingVpn *setting, const char *key)
 
 	found = g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->data, key);
 	if (found)
-		g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_DATA);
+		_notify (setting, PROP_DATA);
 	return found;
 }
 
@@ -371,7 +357,7 @@ nm_setting_vpn_add_secret (NMSettingVpn *setting,
 
 	g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets,
 	                     g_strdup (key), g_strdup (secret));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
+	_notify (setting, PROP_SECRETS);
 }
 
 /**
@@ -395,7 +381,7 @@ 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
+ * @out_length: (allow-none) (out): the length of the returned array
  *
  * Retrieves every secret key inside @setting, as an array.
  *
@@ -436,7 +422,7 @@ nm_setting_vpn_remove_secret (NMSettingVpn *setting, const char *key)
 
 	found = g_hash_table_remove (NM_SETTING_VPN_GET_PRIVATE (setting)->secrets, key);
 	if (found)
-		g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
+		_notify (setting, PROP_SECRETS);
 	return found;
 }
 
@@ -461,6 +447,63 @@ nm_setting_vpn_foreach_secret (NMSettingVpn *setting,
 	foreach_item_helper (setting, TRUE, func, user_data);
 }
 
+static gboolean
+aggregate (NMSetting *setting,
+           int type_i,
+           gpointer arg)
+{
+	NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+	NMConnectionAggregateType type = type_i;
+	NMSettingSecretFlags secret_flags;
+	const char *key_name;
+	GHashTableIter iter;
+
+	switch (type) {
+
+	case NM_CONNECTION_AGGREGATE_ANY_SECRETS:
+		if (g_hash_table_size (priv->secrets) > 0) {
+			*((gboolean *) arg) = TRUE;
+			return TRUE;
+		}
+		return FALSE;
+
+	case NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS:
+
+		g_hash_table_iter_init (&iter, priv->secrets);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &key_name, NULL)) {
+			if (!nm_setting_get_secret_flags (NM_SETTING (setting), key_name, &secret_flags, NULL))
+				nm_assert_not_reached ();
+			if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) {
+				*((gboolean *) arg) = TRUE;
+				return TRUE;
+			}
+		}
+
+		/* Ok, we have no secrets with system-secret flags.
+		 * But do we have any secret-flags (without secrets) that indicate system secrets? */
+		g_hash_table_iter_init (&iter, priv->data);
+		while (g_hash_table_iter_next (&iter, (gpointer *) &key_name, NULL)) {
+			gs_free char *secret_name = NULL;
+
+			if (!g_str_has_suffix (key_name, "-flags"))
+				continue;
+			secret_name = g_strndup (key_name, strlen (key_name) - NM_STRLEN ("-flags"));
+			if (secret_name[0] == '\0')
+				continue;
+			if (!nm_setting_get_secret_flags (NM_SETTING (setting), secret_name, &secret_flags, NULL))
+				nm_assert_not_reached ();
+			if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) {
+				*((gboolean *) arg) = TRUE;
+				return TRUE;
+			}
+		}
+
+		return FALSE;
+	}
+
+	g_return_val_if_reached (FALSE);
+}
+
 /**
  * nm_setting_vpn_get_timeout:
  * @setting: the #NMSettingVpn
@@ -634,56 +677,118 @@ update_one_secret (NMSetting *setting, const char *key, GVariant *value, GError
 	}
 
 	if (success == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
-		g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
+		_notify (NM_SETTING_VPN (setting), PROP_SECRETS);
 
 	return success;
 }
 
+static void
+for_each_secret (NMSetting *setting,
+                 const char *secret_name,
+                 GVariant *val,
+                 gboolean remove_non_secrets,
+                 _NMConnectionForEachSecretFunc callback,
+                 gpointer callback_data,
+                 GVariantBuilder *setting_builder)
+{
+	GVariantBuilder vpn_secrets_builder;
+	GVariantIter vpn_secrets_iter;
+	const char *vpn_secret_name;
+	const char *secret;
+
+	if (!nm_streq (secret_name, NM_SETTING_VPN_SECRETS)) {
+		NM_SETTING_CLASS (nm_setting_vpn_parent_class)->for_each_secret (setting,
+		                                                                 secret_name,
+		                                                                 val,
+		                                                                 remove_non_secrets,
+		                                                                 callback,
+		                                                                 callback_data,
+		                                                                 setting_builder);
+		return;
+	}
+
+	if (!g_variant_is_of_type (val, G_VARIANT_TYPE ("a{ss}"))) {
+		/* invalid type. Silently ignore the secrets as we cannot find out the
+		 * secret-flags. */
+		return;
+	}
+
+	/* Iterate through each secret from the VPN dict in the overall secrets dict */
+	g_variant_builder_init (&vpn_secrets_builder, G_VARIANT_TYPE ("a{ss}"));
+	g_variant_iter_init (&vpn_secrets_iter, val);
+	while (g_variant_iter_next (&vpn_secrets_iter, "{&s&s}", &vpn_secret_name, &secret)) {
+		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+		/* we ignore the return value of get_secret_flags. The function may determine
+		 * that this is not a secret, based on having not secret-flags and no secrets.
+		 * But we have the secret at hand. We know it would be a valid secret, if we
+		 * only add it to the VPN settings. */
+		nm_setting_get_secret_flags (setting, vpn_secret_name, &secret_flags, NULL);
+
+		if (callback (secret_flags, callback_data))
+			g_variant_builder_add (&vpn_secrets_builder, "{ss}", vpn_secret_name, secret);
+	}
+
+	g_variant_builder_add (setting_builder, "{sv}",
+	                       secret_name, g_variant_builder_end (&vpn_secrets_builder));
+}
+
 static gboolean
 get_secret_flags (NMSetting *setting,
                   const char *secret_name,
-                  gboolean verify_secret,
                   NMSettingSecretFlags *out_flags,
                   GError **error)
 {
 	NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
-	gs_free char *flags_key = NULL;
-	gpointer val;
-	unsigned long tmp;
-	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
-
-	flags_key = g_strdup_printf ("%s-flags", secret_name);
-	if (g_hash_table_lookup_extended (priv->data, flags_key, NULL, &val)) {
-		errno = 0;
-		tmp = strtoul ((const char *) val, NULL, 10);
-		if ((errno != 0) || (tmp > NM_SETTING_SECRET_FLAGS_ALL)) {
-			g_set_error (error,
-			             NM_CONNECTION_ERROR,
-			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
-			             _("failed to convert value '%s' to uint"),
-			             (const char *) val);
+	gs_free char *flags_key_free = NULL;
+	const char *flags_key;
+	const char *flags_val;
+	gint64 i64;
+
+	flags_key = nm_construct_name_a ("%s-flags", secret_name, &flags_key_free);
+
+	if (!g_hash_table_lookup_extended (priv->data, flags_key, NULL, (gpointer *) &flags_val)) {
+		NM_SET_OUT (out_flags, NM_SETTING_SECRET_FLAG_NONE);
+
+		/* having no secret flag for the secret is fine, as long as there
+		 * is the secret itself... */
+		if (!g_hash_table_contains (priv->secrets, secret_name)) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_PROPERTY_NOT_SECRET,
+			                     _("secret flags property not found"));
 			g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, flags_key);
 			return FALSE;
 		}
-		flags = (NMSettingSecretFlags) tmp;
+		return TRUE;
+	}
+
+	i64 = _nm_utils_ascii_str_to_int64 (flags_val, 10, 0, NM_SETTING_SECRET_FLAG_ALL, -1);
+	if (   i64 == -1
+	    || !_nm_setting_secret_flags_valid (i64)) {
+		/* The flags keys is set to an unexpected value. That is a configuration
+		 * error. Note that keys named "*-flags" are reserved for secrets. The user
+		 * must not use this for anything but secret flags. Hence, we cannot fail
+		 * to read the secret, we pretend that the secret flag is set to the default
+		 * NM_SETTING_SECRET_FLAG_NONE. */
+		NM_SET_OUT (out_flags, NM_SETTING_SECRET_FLAG_NONE);
+		return TRUE;
 	}
 
-	if (out_flags)
-		*out_flags = flags;
+	NM_SET_OUT (out_flags, (NMSettingSecretFlags) i64);
 	return TRUE;
 }
 
 static gboolean
 set_secret_flags (NMSetting *setting,
                   const char *secret_name,
-                  gboolean verify_secret,
                   NMSettingSecretFlags flags,
                   GError **error)
 {
 	g_hash_table_insert (NM_SETTING_VPN_GET_PRIVATE (setting)->data,
 	                     g_strdup_printf ("%s-flags", secret_name),
 	                     g_strdup_printf ("%u", flags));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
+	_notify (NM_SETTING_VPN (setting), PROP_SECRETS);
 	return TRUE;
 }
 
@@ -694,131 +799,143 @@ need_secrets (NMSetting *setting)
 	return g_ptr_array_sized_new (1);
 }
 
-static gboolean
-_compare_secrets (NMSettingVpn *a,
-                  NMSettingVpn *b,
-                  NMSettingCompareFlags flags)
+static NMTernary
+compare_property_secrets (NMSettingVpn *a,
+                          NMSettingVpn *b,
+                          NMSettingCompareFlags flags)
 {
-	GHashTable *a_secrets;
 	GHashTableIter iter;
 	const char *key, *val;
+	int run;
 
-	a_secrets = NM_SETTING_VPN_GET_PRIVATE (a)->secrets;
-	g_hash_table_iter_init (&iter, a_secrets);
-	while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &val)) {
-		NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-		NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+	if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_FUZZY))
+		return NM_TERNARY_DEFAULT;
+	if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS))
+		return NM_TERNARY_DEFAULT;
 
-		nm_setting_get_secret_flags (NM_SETTING (a), key, &a_secret_flags, NULL);
-		nm_setting_get_secret_flags (NM_SETTING (b), key, &b_secret_flags, NULL);
+	if (!b)
+		return TRUE;
 
-		/* If the secret flags aren't the same, the settings aren't the same */
-		if (a_secret_flags != b_secret_flags)
-			return FALSE;
+	for (run = 0; run < 2; run++) {
+		NMSettingVpn *current_a = (run == 0) ? a : b;
+		NMSettingVpn *current_b = (run == 0) ? b : a;
 
-		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
-		    && (a_secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
-			continue;
+		g_hash_table_iter_init (&iter, NM_SETTING_VPN_GET_PRIVATE (current_a)->secrets);
+		while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &val)) {
 
-		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
-		    && (a_secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
-			continue;
+			if (nm_streq0 (val, nm_setting_vpn_get_secret (current_b, key)))
+				continue;
+			if (!_nm_setting_should_compare_secret_property (NM_SETTING (current_a),
+			                                                 NM_SETTING (current_b),
+			                                                 key,
+			                                                 flags))
+				continue;
 
-		/* Now compare the values themselves */
-		if (g_strcmp0 (val, nm_setting_vpn_get_secret (b, key)) != 0)
 			return FALSE;
+		}
 	}
 
 	return TRUE;
 }
 
-static gboolean
-compare_one_secret (NMSettingVpn *a,
-                    NMSettingVpn *b,
-                    NMSettingCompareFlags flags)
-{
-	if (!_compare_secrets (a, b, flags))
-		return FALSE;
-	if (!_compare_secrets (b, a, flags))
-		return FALSE;
-
-	return TRUE;
-}
-
-static gboolean
-compare_property (NMSetting *setting,
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
                   NMSetting *other,
-                  const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	gboolean same;
-
-	/* We only need to treat the 'secrets' property specially */
-	if (g_strcmp0 (prop_spec->name, NM_SETTING_VPN_SECRETS) != 0)
-		return NM_SETTING_CLASS (nm_setting_vpn_parent_class)->compare_property (setting, other, prop_spec, flags);
-
-	/* Compare A to B to ensure everything in A is found in B */
-	same = compare_one_secret (NM_SETTING_VPN (setting), NM_SETTING_VPN (other), flags);
-	if (same) {
-		/* And then B to A to ensure everything in B is also found in A */
-		same = compare_one_secret (NM_SETTING_VPN (other), NM_SETTING_VPN (setting), flags);
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_VPN_SECRETS)) {
+		if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+			return NM_TERNARY_DEFAULT;
+		return compare_property_secrets (NM_SETTING_VPN (setting), NM_SETTING_VPN (other), flags);
 	}
 
-	return same;
+	return NM_SETTING_CLASS (nm_setting_vpn_parent_class)->compare_property (sett_info,
+	                                                                         property_idx,
+	                                                                         setting,
+	                                                                         other,
+	                                                                         flags);
 }
 
 static gboolean
-clear_secrets_with_flags (NMSetting *setting,
-                          GParamSpec *pspec,
-                          NMSettingClearSecretsWithFlagsFn func,
-                          gpointer user_data)
+clear_secrets (const NMSettInfoSetting *sett_info,
+               guint property_idx,
+               NMSetting *setting,
+               NMSettingClearSecretsWithFlagsFn func,
+               gpointer user_data)
 {
 	NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
+	GParamSpec *prop_spec = sett_info->property_infos[property_idx].param_spec;
 	GHashTableIter iter;
 	const char *secret;
 	gboolean changed = TRUE;
 
-	if (priv->secrets == NULL)
+	if (   !prop_spec
+	    || !NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_SECRET))
+		return FALSE;
+
+	nm_assert (nm_streq (prop_spec->name, NM_SETTING_VPN_SECRETS));
+
+	if (!priv->secrets)
 		return FALSE;
 
-	/* Iterate through secrets hash and check each entry */
 	g_hash_table_iter_init (&iter, priv->secrets);
 	while (g_hash_table_iter_next (&iter, (gpointer) &secret, NULL)) {
-		NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
 
-		nm_setting_get_secret_flags (setting, secret, &flags, NULL);
-		if (func (setting, pspec->name, flags, user_data) == TRUE) {
-			g_hash_table_iter_remove (&iter);
-			changed = TRUE;
-		}
+		if (func) {
+			NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+
+			if (!nm_setting_get_secret_flags (setting, secret, &flags, NULL))
+				nm_assert_not_reached ();
+
+			if (!func (setting, secret, flags, user_data))
+				continue;
+		} else
+			nm_assert (nm_setting_get_secret_flags (setting, secret, NULL, NULL));
+
+		g_hash_table_iter_remove (&iter);
+		changed = TRUE;
 	}
 
 	if (changed)
-		g_object_notify (G_OBJECT (setting), NM_SETTING_VPN_SECRETS);
+		_notify (NM_SETTING_VPN (setting), PROP_SECRETS);
 
 	return changed;
 }
 
-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);
-}
+/*****************************************************************************/
 
 static void
-finalize (GObject *object)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object);
-
-	g_free (priv->service_type);
-	g_free (priv->user_name);
-	g_hash_table_destroy (priv->data);
-	g_hash_table_destroy (priv->secrets);
+	NMSettingVpn *setting = NM_SETTING_VPN (object);
+	NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
 
-	G_OBJECT_CLASS (nm_setting_vpn_parent_class)->finalize (object);
+	switch (prop_id) {
+	case PROP_SERVICE_TYPE:
+		g_value_set_string (value, nm_setting_vpn_get_service_type (setting));
+		break;
+	case PROP_USER_NAME:
+		g_value_set_string (value, nm_setting_vpn_get_user_name (setting));
+		break;
+	case PROP_PERSISTENT:
+		g_value_set_boolean (value, priv->persistent);
+		break;
+	case PROP_DATA:
+		g_value_take_boxed (value, _nm_utils_copy_strdict (priv->data));
+		break;
+	case PROP_SECRETS:
+		g_value_take_boxed (value, _nm_utils_copy_strdict (priv->secrets));
+		break;
+	case PROP_TIMEOUT:
+		g_value_set_uint (value, nm_setting_vpn_get_timeout (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -856,36 +973,41 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_vpn_init (NMSettingVpn *setting)
 {
-	NMSettingVpn *setting = NM_SETTING_VPN (object);
 	NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting);
 
-	switch (prop_id) {
-	case PROP_SERVICE_TYPE:
-		g_value_set_string (value, nm_setting_vpn_get_service_type (setting));
-		break;
-	case PROP_USER_NAME:
-		g_value_set_string (value, nm_setting_vpn_get_user_name (setting));
-		break;
-	case PROP_PERSISTENT:
-		g_value_set_boolean (value, priv->persistent);
-		break;
-	case PROP_DATA:
-		g_value_take_boxed (value, _nm_utils_copy_strdict (priv->data));
-		break;
-	case PROP_SECRETS:
-		g_value_take_boxed (value, _nm_utils_copy_strdict (priv->secrets));
-		break;
-	case PROP_TIMEOUT:
-		g_value_set_uint (value, nm_setting_vpn_get_timeout (setting));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	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);
+}
+
+/**
+ * nm_setting_vpn_new:
+ *
+ * Creates a new #NMSettingVpn object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingVpn object
+ **/
+NMSetting *
+nm_setting_vpn_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_VPN, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (object);
+
+	g_free (priv->service_type);
+	g_free (priv->user_name);
+	g_hash_table_destroy (priv->data);
+	g_hash_table_destroy (priv->secrets);
+
+	G_OBJECT_CLASS (nm_setting_vpn_parent_class)->finalize (object);
 }
 
 static void
@@ -897,17 +1019,19 @@ nm_setting_vpn_class_init (NMSettingVpnClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingVpnPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
-	setting_class->verify                   = verify;
-	setting_class->update_one_secret        = update_one_secret;
-	setting_class->get_secret_flags         = get_secret_flags;
-	setting_class->set_secret_flags         = set_secret_flags;
-	setting_class->need_secrets             = need_secrets;
-	setting_class->compare_property         = compare_property;
-	setting_class->clear_secrets_with_flags = clear_secrets_with_flags;
+	setting_class->verify            = verify;
+	setting_class->update_one_secret = update_one_secret;
+	setting_class->for_each_secret   = for_each_secret;
+	setting_class->get_secret_flags  = get_secret_flags;
+	setting_class->set_secret_flags  = set_secret_flags;
+	setting_class->need_secrets      = need_secrets;
+	setting_class->compare_property  = compare_property;
+	setting_class->clear_secrets     = clear_secrets;
+	setting_class->aggregate         = aggregate;
 
 	/**
 	 * NMSettingVpn:service-type:
@@ -916,12 +1040,11 @@ nm_setting_vpn_class_init (NMSettingVpnClass *klass)
 	 * its network.  i.e. org.freedesktop.NetworkManager.vpnc for the vpnc
 	 * plugin.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_SERVICE_TYPE,
-		 g_param_spec_string (NM_SETTING_VPN_SERVICE_TYPE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SERVICE_TYPE] =
+	    g_param_spec_string (NM_SETTING_VPN_SERVICE_TYPE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVpn:user-name:
@@ -933,12 +1056,11 @@ nm_setting_vpn_class_init (NMSettingVpnClass *klass)
 	 * will automatically supply the username of the user which requested the
 	 * VPN connection.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_USER_NAME,
-		 g_param_spec_string (NM_SETTING_VPN_USER_NAME, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_USER_NAME] =
+	    g_param_spec_string (NM_SETTING_VPN_USER_NAME, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVpn:persistent:
@@ -947,12 +1069,11 @@ nm_setting_vpn_class_init (NMSettingVpnClass *klass)
 	 * the VPN will attempt to stay connected across link changes and outages,
 	 * until explicitly disconnected.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PERSISTENT,
-		 g_param_spec_boolean (NM_SETTING_VPN_PERSISTENT, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PERSISTENT] =
+	    g_param_spec_boolean (NM_SETTING_VPN_PERSISTENT, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVpn:data: (type GHashTable(utf8,utf8)):
@@ -968,16 +1089,14 @@ nm_setting_vpn_class_init (NMSettingVpnClass *klass)
 	 * example: remote=ovpn.corp.com cipher=AES-256-CBC username=joe
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_DATA,
-		 g_param_spec_boxed (NM_SETTING_VPN_DATA, "", "",
-		                     G_TYPE_HASH_TABLE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DATA] =
+	    g_param_spec_boxed (NM_SETTING_VPN_DATA, "", "",
+	                        G_TYPE_HASH_TABLE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_VPN_DATA),
+	                                    obj_properties[PROP_DATA],
 	                                    G_VARIANT_TYPE ("a{ss}"),
 	                                    _nm_utils_strdict_to_dbus,
 	                                    _nm_utils_strdict_from_dbus);
@@ -996,17 +1115,15 @@ nm_setting_vpn_class_init (NMSettingVpnClass *klass)
 	 * example: password=Popocatepetl
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_SECRETS,
-		 g_param_spec_boxed (NM_SETTING_VPN_SECRETS, "", "",
-		                     G_TYPE_HASH_TABLE,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_SECRET |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SECRETS] =
+	    g_param_spec_boxed (NM_SETTING_VPN_SECRETS, "", "",
+	                        G_TYPE_HASH_TABLE,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_SECRET |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_VPN_SECRETS),
+	                                    obj_properties[PROP_SECRETS],
 	                                    G_VARIANT_TYPE ("a{ss}"),
 	                                    _nm_utils_strdict_to_dbus,
 	                                    _nm_utils_strdict_from_dbus);
@@ -1022,12 +1139,13 @@ nm_setting_vpn_class_init (NMSettingVpnClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_TIMEOUT,
-		 g_param_spec_uint (NM_SETTING_VPN_TIMEOUT, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TIMEOUT] =
+	    g_param_spec_uint (NM_SETTING_VPN_TIMEOUT, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_VPN,
 	                               NULL, properties_override);
diff --git a/libnm-core/nm-setting-vxlan.c b/libnm-core/nm-setting-vxlan.c
index d00c5a7a..0c116f06 100644
--- a/libnm-core/nm-setting-vxlan.c
+++ b/libnm-core/nm-setting-vxlan.c
@@ -21,10 +21,10 @@
 
 #include "nm-default.h"
 
+#include "nm-setting-vxlan.h"
+
 #include <stdlib.h>
-#include <string.h>
 
-#include "nm-setting-vxlan.h"
 #include "nm-utils.h"
 #include "nm-setting-private.h"
 
@@ -36,9 +36,28 @@
  * necessary for connection to VXLAN interfaces.
  **/
 
-G_DEFINE_TYPE (NMSettingVxlan, nm_setting_vxlan, NM_TYPE_SETTING)
+#define DST_PORT_DEFAULT   8472
 
-#define NM_SETTING_VXLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VXLAN, NMSettingVxlanPrivate))
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PARENT,
+	PROP_ID,
+	PROP_LOCAL,
+	PROP_REMOTE,
+	PROP_SOURCE_PORT_MIN,
+	PROP_SOURCE_PORT_MAX,
+	PROP_DESTINATION_PORT,
+	PROP_TOS,
+	PROP_TTL,
+	PROP_AGEING,
+	PROP_LIMIT,
+	PROP_LEARNING,
+	PROP_PROXY,
+	PROP_RSC,
+	PROP_L2_MISS,
+	PROP_L3_MISS,
+);
 
 typedef struct {
 	char *parent;
@@ -59,44 +78,11 @@ typedef struct {
 	gboolean l3_miss;
 } NMSettingVxlanPrivate;
 
-enum {
-	PROP_0,
-	PROP_PARENT,
-	PROP_ID,
-	PROP_LOCAL,
-	PROP_REMOTE,
-	PROP_SOURCE_PORT_MIN,
-	PROP_SOURCE_PORT_MAX,
-	PROP_DESTINATION_PORT,
-	PROP_TOS,
-	PROP_TTL,
-	PROP_AGEING,
-	PROP_LIMIT,
-	PROP_LEARNING,
-	PROP_PROXY,
-	PROP_RSC,
-	PROP_L2_MISS,
-	PROP_L3_MISS,
-
-	LAST_PROP
-};
+G_DEFINE_TYPE (NMSettingVxlan, nm_setting_vxlan, NM_TYPE_SETTING)
 
-#define DST_PORT_DEFAULT   8472
+#define NM_SETTING_VXLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_VXLAN, NMSettingVxlanPrivate))
 
-/**
- * nm_setting_vxlan_new:
- *
- * Creates a new #NMSettingVxlan object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingVxlan object
- *
- * Since: 1.2
- **/
-NMSetting *
-nm_setting_vxlan_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_VXLAN, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_vxlan_get_parent:
@@ -340,11 +326,6 @@ nm_setting_vxlan_get_l3_miss (NMSettingVxlan *setting)
 
 /*****************************************************************************/
 
-static void
-nm_setting_vxlan_init (NMSettingVxlan *setting)
-{
-}
-
 static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
@@ -422,64 +403,63 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
+/*****************************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSettingVxlan *setting = NM_SETTING_VXLAN (object);
 	NMSettingVxlanPrivate *priv = NM_SETTING_VXLAN_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_free (priv->parent);
-		priv->parent = g_value_dup_string (value);
+		g_value_set_string (value, priv->parent);
 		break;
 	case PROP_ID:
-		priv->id = g_value_get_uint (value);
+		g_value_set_uint (value, priv->id);
 		break;
 	case PROP_LOCAL:
-		g_free (priv->local);
-		priv->local = g_value_dup_string (value);
+		g_value_set_string (value, priv->local);
 		break;
 	case PROP_REMOTE:
-		g_free (priv->remote);
-		priv->remote = g_value_dup_string (value);
+		g_value_set_string (value, priv->remote);
 		break;
 	case PROP_SOURCE_PORT_MIN:
-		priv->source_port_min = g_value_get_uint (value);
+		g_value_set_uint (value, priv->source_port_min);
 		break;
 	case PROP_SOURCE_PORT_MAX:
-		priv->source_port_max = g_value_get_uint (value);
+		g_value_set_uint (value, priv->source_port_max);
 		break;
 	case PROP_DESTINATION_PORT:
-		priv->destination_port = g_value_get_uint (value);
+		g_value_set_uint (value, priv->destination_port);
 		break;
 	case PROP_TOS:
-		priv->tos = g_value_get_uint (value);
+		g_value_set_uint (value, priv->tos);
 		break;
 	case PROP_AGEING:
-		priv->ageing = g_value_get_uint (value);
+		g_value_set_uint (value, priv->ageing);
 		break;
 	case PROP_LIMIT:
-		priv->limit = g_value_get_uint (value);
+		g_value_set_uint (value, priv->limit);
 		break;
 	case PROP_PROXY:
-		priv->proxy = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->proxy);
 		break;
 	case PROP_TTL:
-		priv->ttl = g_value_get_uint (value);
+		g_value_set_uint (value, priv->ttl);
 		break;
 	case PROP_LEARNING:
-		priv->learning = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->learning);
 		break;
 	case PROP_RSC:
-		priv->rsc = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->rsc);
 		break;
 	case PROP_L2_MISS:
-		priv->l2_miss = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->l2_miss);
 		break;
 	case PROP_L3_MISS:
-		priv->l3_miss = g_value_get_boolean (value);
+		g_value_set_boolean (value, priv->l3_miss);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -488,60 +468,63 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSettingVxlan *setting = NM_SETTING_VXLAN (object);
 	NMSettingVxlanPrivate *priv = NM_SETTING_VXLAN_GET_PRIVATE (setting);
 
 	switch (prop_id) {
 	case PROP_PARENT:
-		g_value_set_string (value, priv->parent);
+		g_free (priv->parent);
+		priv->parent = g_value_dup_string (value);
 		break;
 	case PROP_ID:
-		g_value_set_uint (value, priv->id);
+		priv->id = g_value_get_uint (value);
 		break;
 	case PROP_LOCAL:
-		g_value_set_string (value, priv->local);
+		g_free (priv->local);
+		priv->local = g_value_dup_string (value);
 		break;
 	case PROP_REMOTE:
-		g_value_set_string (value, priv->remote);
+		g_free (priv->remote);
+		priv->remote = g_value_dup_string (value);
 		break;
 	case PROP_SOURCE_PORT_MIN:
-		g_value_set_uint (value, priv->source_port_min);
+		priv->source_port_min = g_value_get_uint (value);
 		break;
 	case PROP_SOURCE_PORT_MAX:
-		g_value_set_uint (value, priv->source_port_max);
+		priv->source_port_max = g_value_get_uint (value);
 		break;
 	case PROP_DESTINATION_PORT:
-		g_value_set_uint (value, priv->destination_port);
+		priv->destination_port = g_value_get_uint (value);
 		break;
 	case PROP_TOS:
-		g_value_set_uint (value, priv->tos);
+		priv->tos = g_value_get_uint (value);
 		break;
 	case PROP_AGEING:
-		g_value_set_uint (value, priv->ageing);
+		priv->ageing = g_value_get_uint (value);
 		break;
 	case PROP_LIMIT:
-		g_value_set_uint (value, priv->limit);
+		priv->limit = g_value_get_uint (value);
 		break;
 	case PROP_PROXY:
-		g_value_set_boolean (value, priv->proxy);
+		priv->proxy = g_value_get_boolean (value);
 		break;
 	case PROP_TTL:
-		g_value_set_uint (value, priv->ttl);
+		priv->ttl = g_value_get_uint (value);
 		break;
 	case PROP_LEARNING:
-		g_value_set_boolean (value, priv->learning);
+		priv->learning = g_value_get_boolean (value);
 		break;
 	case PROP_RSC:
-		g_value_set_boolean (value, priv->rsc);
+		priv->rsc = g_value_get_boolean (value);
 		break;
 	case PROP_L2_MISS:
-		g_value_set_boolean (value, priv->l2_miss);
+		priv->l2_miss = g_value_get_boolean (value);
 		break;
 	case PROP_L3_MISS:
-		g_value_set_boolean (value, priv->l3_miss);
+		priv->l3_miss = g_value_get_boolean (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -549,6 +532,28 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_vxlan_init (NMSettingVxlan *setting)
+{
+}
+
+/**
+ * nm_setting_vxlan_new:
+ *
+ * Creates a new #NMSettingVxlan object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingVxlan object
+ *
+ * Since: 1.2
+ **/
+NMSetting *
+nm_setting_vxlan_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_VXLAN, NULL);
+}
+
 static void
 finalize (GObject *object)
 {
@@ -570,8 +575,8 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingVxlanPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -583,14 +588,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PARENT,
-		 g_param_spec_string (NM_SETTING_VXLAN_PARENT, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PARENT] =
+	    g_param_spec_string (NM_SETTING_VXLAN_PARENT, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 	/**
 	 * NMSettingVxlan:id:
 	 *
@@ -599,14 +603,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_ID,
-		 g_param_spec_uint (NM_SETTING_VXLAN_ID, "", "",
-		                    0, (1 << 24) - 1, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_ID] =
+	    g_param_spec_uint (NM_SETTING_VXLAN_ID, "", "",
+	                       0, (1 << 24) - 1, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:local:
@@ -615,14 +618,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_LOCAL,
-		 g_param_spec_string (NM_SETTING_VXLAN_LOCAL, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LOCAL] =
+	    g_param_spec_string (NM_SETTING_VXLAN_LOCAL, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:remote:
@@ -633,14 +635,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_REMOTE,
-		 g_param_spec_string (NM_SETTING_VXLAN_REMOTE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_CONSTRUCT |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_REMOTE] =
+	    g_param_spec_string (NM_SETTING_VXLAN_REMOTE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_CONSTRUCT |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:source-port-min:
@@ -650,14 +651,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_SOURCE_PORT_MIN,
-		 g_param_spec_uint (NM_SETTING_VXLAN_SOURCE_PORT_MIN, "", "",
-		                    0, G_MAXUINT16, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SOURCE_PORT_MIN] =
+	    g_param_spec_uint (NM_SETTING_VXLAN_SOURCE_PORT_MIN, "", "",
+	                       0, G_MAXUINT16, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:source-port-max:
@@ -667,14 +667,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_SOURCE_PORT_MAX,
-		 g_param_spec_uint (NM_SETTING_VXLAN_SOURCE_PORT_MAX, "", "",
-		                    0, G_MAXUINT16, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SOURCE_PORT_MAX] =
+	    g_param_spec_uint (NM_SETTING_VXLAN_SOURCE_PORT_MAX, "", "",
+	                       0, G_MAXUINT16, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:destination-port:
@@ -684,14 +683,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_DESTINATION_PORT,
-		 g_param_spec_uint (NM_SETTING_VXLAN_DESTINATION_PORT, "", "",
-		                    0, G_MAXUINT16, DST_PORT_DEFAULT,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DESTINATION_PORT] =
+	    g_param_spec_uint (NM_SETTING_VXLAN_DESTINATION_PORT, "", "",
+	                       0, G_MAXUINT16, DST_PORT_DEFAULT,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:ageing:
@@ -700,14 +698,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_AGEING,
-		 g_param_spec_uint (NM_SETTING_VXLAN_AGEING, "", "",
-		                    0, G_MAXUINT32, 300,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_AGEING] =
+	    g_param_spec_uint (NM_SETTING_VXLAN_AGEING, "", "",
+	                       0, G_MAXUINT32, 300,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:limit:
@@ -717,14 +714,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_LIMIT,
-		 g_param_spec_uint (NM_SETTING_VXLAN_LIMIT, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LIMIT] =
+	    g_param_spec_uint (NM_SETTING_VXLAN_LIMIT, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:tos:
@@ -733,14 +729,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_TOS,
-		 g_param_spec_uint (NM_SETTING_VXLAN_TOS, "", "",
-		                    0, 255, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TOS] =
+	    g_param_spec_uint (NM_SETTING_VXLAN_TOS, "", "",
+	                       0, 255, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:ttl:
@@ -749,14 +744,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_TTL,
-		 g_param_spec_uint (NM_SETTING_VXLAN_TTL, "", "",
-		                    0, 255, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_INFERRABLE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TTL] =
+	    g_param_spec_uint (NM_SETTING_VXLAN_TTL, "", "",
+	                       0, 255, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_INFERRABLE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:proxy:
@@ -765,14 +759,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_PROXY,
-		 g_param_spec_boolean (NM_SETTING_VXLAN_PROXY, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PROXY] =
+	    g_param_spec_boolean (NM_SETTING_VXLAN_PROXY, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:learning:
@@ -782,14 +775,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_LEARNING,
-		 g_param_spec_boolean (NM_SETTING_VXLAN_LEARNING, "", "",
-		                       TRUE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LEARNING] =
+	    g_param_spec_boolean (NM_SETTING_VXLAN_LEARNING, "", "",
+	                          TRUE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	/**
 	 * NMSettingVxlan:rsc:
 	 *
@@ -797,14 +789,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_RSC,
-		 g_param_spec_boolean (NM_SETTING_VXLAN_RSC, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RSC] =
+	    g_param_spec_boolean (NM_SETTING_VXLAN_RSC, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
 	/**
 	 * NMSettingVxlan:l2-miss:
 	 *
@@ -812,14 +803,13 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_L2_MISS,
-		 g_param_spec_boolean (NM_SETTING_VXLAN_L2_MISS, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_L2_MISS] =
+	    g_param_spec_boolean (NM_SETTING_VXLAN_L2_MISS, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingVxlan:l3-miss:
@@ -828,14 +818,15 @@ nm_setting_vxlan_class_init (NMSettingVxlanClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_L3_MISS,
-		 g_param_spec_boolean (NM_SETTING_VXLAN_L3_MISS, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       NM_SETTING_PARAM_INFERRABLE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_L3_MISS] =
+	    g_param_spec_boolean (NM_SETTING_VXLAN_L3_MISS, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          NM_SETTING_PARAM_INFERRABLE |
+	                          G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_VXLAN);
 }
diff --git a/libnm-core/nm-setting-wifi-p2p.c b/libnm-core/nm-setting-wifi-p2p.c
new file mode 100644
index 00000000..da978720
--- /dev/null
+++ b/libnm-core/nm-setting-wifi-p2p.c
@@ -0,0 +1,317 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2019 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-wifi-p2p.h"
+
+#include <net/ethernet.h>
+
+#include "nm-utils.h"
+#include "nm-common-macros.h"
+#include "nm-utils-private.h"
+#include "nm-setting-private.h"
+
+/**
+ * SECTION:nm-setting-wifi-p2p
+ * @short_description: Describes connection properties for 802.11 Wi-Fi P2P networks
+ *
+ * The #NMSettingWifiP2P object is a #NMSetting subclass that describes properties
+ * necessary for connection to 802.11 Wi-Fi P2P networks (aka Wi-Fi Direct).
+ **/
+
+/**
+ * NMSettingWifiP2P:
+ *
+ * Wi-Fi P2P Settings
+ *
+ * Since: 1.16
+ */
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_PEER,
+	PROP_WPS_METHOD,
+	PROP_WFD_IES,
+);
+
+typedef struct {
+	char *peer_mac_address;
+	GBytes *wfd_ies;
+
+	NMSettingWirelessSecurityWpsMethod wps_method;
+} NMSettingWifiP2PPrivate;
+
+struct _NMSettingWifiP2P {
+	NMSetting parent;
+	NMSettingWifiP2PPrivate _priv;
+};
+
+struct _NMSettingWifiP2PClass {
+	NMSettingClass parent;
+};
+
+G_DEFINE_TYPE (NMSettingWifiP2P, nm_setting_wifi_p2p, NM_TYPE_SETTING)
+
+#define NM_SETTING_WIFI_P2P_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSettingWifiP2P, NM_IS_SETTING_WIFI_P2P, NMSetting)
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_wifi_p2p_get_peer:
+ * @setting: the #NMSettingWifiP2P
+ *
+ * Returns: the #NMSettingWifiP2P:peer property of the setting
+ *
+ * Since: 1.16
+ **/
+const char *
+nm_setting_wifi_p2p_get_peer (NMSettingWifiP2P *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIFI_P2P (setting), NULL);
+
+	return NM_SETTING_WIFI_P2P_GET_PRIVATE (setting)->peer_mac_address;
+}
+
+/**
+ * nm_setting_wifi_p2p_get_wps_method:
+ * @setting: the #NMSettingWifiP2P
+ *
+ * Returns: the #NMSettingWifiP2P:wps-method property of the setting
+ *
+ * Since: 1.16
+ **/
+NMSettingWirelessSecurityWpsMethod
+nm_setting_wifi_p2p_get_wps_method (NMSettingWifiP2P *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIFI_P2P (setting), NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT);
+
+	return NM_SETTING_WIFI_P2P_GET_PRIVATE (setting)->wps_method;
+}
+
+/**
+ * nm_setting_wifi_p2p_get_wfd_ies:
+ * @setting: the #NMSettingWiFiP2P
+ *
+ * Returns: (transfer none): the #NMSettingWiFiP2P:wfd-ies property of the setting
+ *
+ * Since: 1.16
+ **/
+GBytes *
+nm_setting_wifi_p2p_get_wfd_ies (NMSettingWifiP2P *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIFI_P2P (setting), NULL);
+
+	return NM_SETTING_WIFI_P2P_GET_PRIVATE (setting)->wfd_ies;
+}
+
+/*****************************************************************************/
+
+static gboolean
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+	NMSettingWifiP2PPrivate *priv = NM_SETTING_WIFI_P2P_GET_PRIVATE (setting);
+
+	if (!priv->peer_mac_address) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_MISSING_PROPERTY,
+		                     _("property is missing"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIFI_P2P_SETTING_NAME, NM_SETTING_WIFI_P2P_PEER);
+		return FALSE;
+	}
+
+	if (!nm_utils_hwaddr_valid (priv->peer_mac_address, ETH_ALEN)) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("property is invalid"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIFI_P2P_SETTING_NAME, NM_SETTING_WIFI_P2P_PEER);
+		return FALSE;
+	}
+
+	if (!_nm_utils_wps_method_validate (priv->wps_method,
+	                                    NM_SETTING_WIFI_P2P_SETTING_NAME,
+	                                    NM_SETTING_WIFI_P2P_WPS_METHOD,
+	                                    TRUE,
+	                                    error))
+		return FALSE;
+
+	return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingWifiP2P *setting = NM_SETTING_WIFI_P2P (object);
+
+	switch (prop_id) {
+	case PROP_PEER:
+		g_value_set_string (value, nm_setting_wifi_p2p_get_peer (setting));
+		break;
+	case PROP_WPS_METHOD:
+		g_value_set_uint (value, nm_setting_wifi_p2p_get_wps_method (setting));
+		break;
+	case PROP_WFD_IES:
+		g_value_set_boxed (value, nm_setting_wifi_p2p_get_wfd_ies (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSettingWifiP2PPrivate *priv = NM_SETTING_WIFI_P2P_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PEER:
+		g_free (priv->peer_mac_address);
+		priv->peer_mac_address = _nm_utils_hwaddr_canonical_or_invalid (g_value_get_string (value),
+		                                                                ETH_ALEN);
+		break;
+	case PROP_WPS_METHOD:
+		priv->wps_method = g_value_get_uint (value);
+		break;
+	case PROP_WFD_IES:
+		g_clear_pointer (&priv->wfd_ies, g_bytes_unref);
+		priv->wfd_ies = g_value_dup_boxed (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_wifi_p2p_init (NMSettingWifiP2P *setting)
+{
+}
+
+/**
+ * nm_setting_wifi_p2p_new:
+ *
+ * Creates a new #NMSettingWifiP2P object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingWifiP2P object
+ *
+ * Since: 1.16
+ **/
+NMSetting *
+nm_setting_wifi_p2p_new (void)
+{
+	return g_object_new (NM_TYPE_SETTING_WIFI_P2P, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingWifiP2PPrivate *priv = NM_SETTING_WIFI_P2P_GET_PRIVATE (object);
+
+	g_free (priv->peer_mac_address);
+	g_bytes_unref (priv->wfd_ies);
+
+	G_OBJECT_CLASS (nm_setting_wifi_p2p_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_wifi_p2p_class_init (NMSettingWifiP2PClass *setting_wifi_p2p_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (setting_wifi_p2p_class);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (setting_wifi_p2p_class);
+
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->finalize     = finalize;
+
+	setting_class->verify      = verify;
+
+	/**
+	 * NMSettingWifiP2P:peer:
+	 *
+	 * The P2P device that should be connected to. Currently this is the only
+	 * way to create or join a group.
+	 *
+	 * Since: 1.16
+	 */
+	/* ---keyfile---
+	 * property: peer
+	 * format: usual hex-digits-and-colons notation
+	 * description: MAC address in traditional hex-digits-and-colons notation
+	 *   (e.g. 00:22:68:12:79:A2), or semicolon separated list of 6 bytes (obsolete)
+	 *   (e.g. 0;34;104;18;121;162).
+	 * ---end---
+	 */
+	obj_properties[PROP_PEER] =
+	    g_param_spec_string (NM_SETTING_WIFI_P2P_PEER, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * NMSettingWifiP2P:wps-method:
+	 *
+	 * Flags indicating which mode of WPS is to be used.
+	 *
+	 * There's little point in changing the default setting as NetworkManager will
+	 * automatically determine the best method to use.
+	 *
+	 * Since: 1.16
+	 */
+	obj_properties[PROP_WPS_METHOD] =
+	    g_param_spec_uint (NM_SETTING_WIFI_P2P_WPS_METHOD, "", "",
+	                       0,
+	                       G_MAXUINT32,
+	                       NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT,
+	                       G_PARAM_READWRITE |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * NMSettingWifiP2P:wfd-ies:
+	 *
+	 * The Wi-Fi Display (WFD) Information Elements (IEs) to set.
+	 *
+	 * Wi-Fi Display requires a protocol specific information element to be
+	 * set in certain Wi-Fi frames. These can be specified here for the
+	 * purpose of establishing a connection.
+	 * This setting is only useful when implementing a Wi-Fi Display client.
+	 *
+	 * Since: 1.16
+	 */
+	obj_properties[PROP_WFD_IES] =
+	    g_param_spec_boxed (NM_SETTING_WIFI_P2P_WFD_IES, "", "",
+	                        G_TYPE_BYTES,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_FUZZY_IGNORE |
+	                        G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_WIFI_P2P);
+}
diff --git a/libnm-core/nm-setting-wifi-p2p.h b/libnm-core/nm-setting-wifi-p2p.h
new file mode 100644
index 00000000..ddb611c1
--- /dev/null
+++ b/libnm-core/nm-setting-wifi-p2p.h
@@ -0,0 +1,69 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2019 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_WIFI_P2P_H__
+#define __NM_SETTING_WIFI_P2P_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-setting.h"
+#include "nm-setting-wireless-security.h"
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_SETTING_WIFI_P2P            (nm_setting_wifi_p2p_get_type ())
+#define NM_SETTING_WIFI_P2P(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIFI_P2P, NMSettingWifiP2P))
+#define NM_SETTING_WIFI_P2P_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIFI_P2P, NMSettingWifiP2PClass))
+#define NM_IS_SETTING_WIFI_P2P(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIFI_P2P))
+#define NM_IS_SETTING_WIFI_P2P_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WIFI_P2P))
+#define NM_SETTING_WIFI_P2P_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIFI_P2P, NMSettingWifiP2PClass))
+
+#define NM_SETTING_WIFI_P2P_SETTING_NAME "wifi-p2p"
+
+/**
+ * NM_SETTING_WIFI_P2P_PEER:
+ *
+ * The mac address of the peer to connect to.
+ */
+#define NM_SETTING_WIFI_P2P_PEER        "peer"
+#define NM_SETTING_WIFI_P2P_WPS_METHOD  "wps-method"
+#define NM_SETTING_WIFI_P2P_WFD_IES     "wfd-ies"
+
+typedef struct _NMSettingWifiP2PClass NMSettingWifiP2PClass;
+
+NM_AVAILABLE_IN_1_16
+GType nm_setting_wifi_p2p_get_type (void);
+
+NM_AVAILABLE_IN_1_16
+NMSetting *nm_setting_wifi_p2p_new (void);
+
+NM_AVAILABLE_IN_1_16
+const char *nm_setting_wifi_p2p_get_peer (NMSettingWifiP2P *setting);
+
+NM_AVAILABLE_IN_1_16
+NMSettingWirelessSecurityWpsMethod nm_setting_wifi_p2p_get_wps_method (NMSettingWifiP2P *setting);
+
+NM_AVAILABLE_IN_1_16
+GBytes *nm_setting_wifi_p2p_get_wfd_ies (NMSettingWifiP2P *setting);
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_WIFI_P2P_H__ */
diff --git a/libnm-core/nm-setting-wimax.c b/libnm-core/nm-setting-wimax.c
index 4c1a572e..a700f7fe 100644
--- a/libnm-core/nm-setting-wimax.c
+++ b/libnm-core/nm-setting-wimax.c
@@ -22,10 +22,10 @@
 
 #include "nm-default.h"
 
-#include <string.h>
+#include "nm-setting-wimax.h"
+
 #include <net/ethernet.h>
 
-#include "nm-setting-wimax.h"
 #include "nm-setting-private.h"
 #include "nm-utils.h"
 #include "nm-utils-private.h"
@@ -42,37 +42,23 @@
  * connections cannot be activated.
  **/
 
-G_DEFINE_TYPE (NMSettingWimax, nm_setting_wimax, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_WIMAX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIMAX, NMSettingWimaxPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_NETWORK_NAME,
+	PROP_MAC_ADDRESS,
+);
 
 typedef struct {
 	char *network_name;
 	char *mac_address;
 } NMSettingWimaxPrivate;
 
-enum {
-	PROP_0,
-	PROP_NETWORK_NAME,
-	PROP_MAC_ADDRESS,
+G_DEFINE_TYPE (NMSettingWimax, nm_setting_wimax, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_WIMAX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIMAX, NMSettingWimaxPrivate))
 
-/**
- * nm_setting_wimax_new:
- *
- * Creates a new #NMSettingWimax object with default values.
- *
- * Returns: the new empty #NMSettingWimax object
- *
- * Deprecated: 1.2: WiMAX is no longer supported.
- **/
-NMSetting *
-nm_setting_wimax_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIMAX, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_wimax_get_network_name:
@@ -147,20 +133,25 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static void
-nm_setting_wimax_init (NMSettingWimax *setting)
-{
-}
+/*****************************************************************************/
 
 static void
-finalize (GObject *object)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingWimaxPrivate *priv = NM_SETTING_WIMAX_GET_PRIVATE (object);
-
-	g_free (priv->network_name);
-	g_free (priv->mac_address);
+	NMSettingWimax *setting = NM_SETTING_WIMAX (object);
 
-	G_OBJECT_CLASS (nm_setting_wimax_parent_class)->finalize (object);
+	switch (prop_id) {
+	case PROP_NETWORK_NAME:
+		g_value_set_string (value, nm_setting_wimax_get_network_name (setting));
+		break;
+	case PROP_MAC_ADDRESS:
+		g_value_set_string (value, nm_setting_wimax_get_mac_address (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -185,23 +176,37 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_wimax_init (NMSettingWimax *setting)
 {
-	NMSettingWimax *setting = NM_SETTING_WIMAX (object);
+}
 
-	switch (prop_id) {
-	case PROP_NETWORK_NAME:
-		g_value_set_string (value, nm_setting_wimax_get_network_name (setting));
-		break;
-	case PROP_MAC_ADDRESS:
-		g_value_set_string (value, nm_setting_wimax_get_mac_address (setting));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+/**
+ * nm_setting_wimax_new:
+ *
+ * Creates a new #NMSettingWimax object with default values.
+ *
+ * Returns: the new empty #NMSettingWimax object
+ *
+ * Deprecated: 1.2: WiMAX is no longer supported.
+ **/
+NMSetting *
+nm_setting_wimax_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIMAX, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingWimaxPrivate *priv = NM_SETTING_WIMAX_GET_PRIVATE (object);
+
+	g_free (priv->network_name);
+	g_free (priv->mac_address);
+
+	G_OBJECT_CLASS (nm_setting_wimax_parent_class)->finalize (object);
 }
 
 static void
@@ -213,8 +218,8 @@ nm_setting_wimax_class_init (NMSettingWimaxClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingWimaxPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -227,12 +232,11 @@ nm_setting_wimax_class_init (NMSettingWimaxClass *klass)
 	 *
 	 * Deprecated: 1.2: WiMAX is no longer supported.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NETWORK_NAME,
-		 g_param_spec_string (NM_SETTING_WIMAX_NETWORK_NAME, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NETWORK_NAME] =
+	    g_param_spec_string (NM_SETTING_WIMAX_NETWORK_NAME, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWimax:mac-address:
@@ -243,20 +247,20 @@ nm_setting_wimax_class_init (NMSettingWimaxClass *klass)
 	 *
 	 * Deprecated: 1.2: WiMAX is no longer supported.
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_MAC_ADDRESS,
-		 g_param_spec_string (NM_SETTING_WIMAX_MAC_ADDRESS, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MAC_ADDRESS] =
+	    g_param_spec_string (NM_SETTING_WIMAX_MAC_ADDRESS, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_WIMAX_MAC_ADDRESS),
+	                                    obj_properties[PROP_MAC_ADDRESS],
 	                                    G_VARIANT_TYPE_BYTESTRING,
 	                                    _nm_utils_hwaddr_to_dbus,
 	                                    _nm_utils_hwaddr_from_dbus);
 
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_WIMAX,
 	                               NULL, properties_override);
 }
diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c
index baa40cc4..ef23294f 100644
--- a/libnm-core/nm-setting-wired.c
+++ b/libnm-core/nm-setting-wired.c
@@ -24,7 +24,6 @@
 
 #include "nm-setting-wired.h"
 
-#include <string.h>
 #include <net/ethernet.h>
 
 #include "nm-utils.h"
@@ -40,9 +39,29 @@
  * necessary for connection to Ethernet networks.
  **/
 
-G_DEFINE_TYPE (NMSettingWired, nm_setting_wired, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_WIRED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRED, NMSettingWiredPrivate))
+G_STATIC_ASSERT (NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS == (  NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT
+                                                                  | NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE));
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingWired,
+	PROP_PORT,
+	PROP_SPEED,
+	PROP_DUPLEX,
+	PROP_AUTO_NEGOTIATE,
+	PROP_MAC_ADDRESS,
+	PROP_CLONED_MAC_ADDRESS,
+	PROP_GENERATE_MAC_ADDRESS_MASK,
+	PROP_MAC_ADDRESS_BLACKLIST,
+	PROP_MTU,
+	PROP_S390_SUBCHANNELS,
+	PROP_S390_NETTYPE,
+	PROP_S390_OPTIONS,
+	PROP_WAKE_ON_LAN,
+	PROP_WAKE_ON_LAN_PASSWORD,
+);
 
 typedef struct {
 	char *port;
@@ -61,25 +80,11 @@ typedef struct {
 	char *wol_password;
 } NMSettingWiredPrivate;
 
-enum {
-	PROP_0,
-	PROP_PORT,
-	PROP_SPEED,
-	PROP_DUPLEX,
-	PROP_AUTO_NEGOTIATE,
-	PROP_MAC_ADDRESS,
-	PROP_CLONED_MAC_ADDRESS,
-	PROP_GENERATE_MAC_ADDRESS_MASK,
-	PROP_MAC_ADDRESS_BLACKLIST,
-	PROP_MTU,
-	PROP_S390_SUBCHANNELS,
-	PROP_S390_NETTYPE,
-	PROP_S390_OPTIONS,
-	PROP_WAKE_ON_LAN,
-	PROP_WAKE_ON_LAN_PASSWORD,
+G_DEFINE_TYPE (NMSettingWired, nm_setting_wired, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_WIRED_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRED, NMSettingWiredPrivate))
+
+/*****************************************************************************/
 
 static const char *valid_s390_opts[] = {
 	"portno", "layer2", "portname", "protocol", "priority_queueing",
@@ -92,19 +97,6 @@ static const char *valid_s390_opts[] = {
 };
 
 /**
- * nm_setting_wired_new:
- *
- * Creates a new #NMSettingWired object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingWired object
- **/
-NMSetting *
-nm_setting_wired_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRED, NULL);
-}
-
-/**
  * nm_setting_wired_get_port:
  * @setting: the #NMSettingWired
  *
@@ -288,7 +280,7 @@ nm_setting_wired_add_mac_blacklist_item (NMSettingWired *setting, const char *ma
 
 	mac = nm_utils_hwaddr_canonical (mac, ETH_ALEN);
 	g_array_append_val (priv->mac_address_blacklist, mac);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST);
+	_notify (setting, PROP_MAC_ADDRESS_BLACKLIST);
 	return TRUE;
 }
 
@@ -310,7 +302,7 @@ nm_setting_wired_remove_mac_blacklist_item (NMSettingWired *setting, guint32 idx
 	g_return_if_fail (idx < priv->mac_address_blacklist->len);
 
 	g_array_remove_index (priv->mac_address_blacklist, idx);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST);
+	_notify (setting, PROP_MAC_ADDRESS_BLACKLIST);
 }
 
 /**
@@ -338,7 +330,7 @@ nm_setting_wired_remove_mac_blacklist_item_by_value (NMSettingWired *setting, co
 		candidate = g_array_index (priv->mac_address_blacklist, char *, i);
 		if (!nm_utils_hwaddr_matches (mac, -1, candidate, -1)) {
 			g_array_remove_index (priv->mac_address_blacklist, i);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST);
+			_notify (setting, PROP_MAC_ADDRESS_BLACKLIST);
 			return TRUE;
 		}
 	}
@@ -357,7 +349,7 @@ nm_setting_wired_clear_mac_blacklist_items (NMSettingWired *setting)
 	g_return_if_fail (NM_IS_SETTING_WIRED (setting));
 
 	g_array_set_size (NM_SETTING_WIRED_GET_PRIVATE (setting)->mac_address_blacklist, 0);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST);
+	_notify (setting, PROP_MAC_ADDRESS_BLACKLIST);
 }
 
 /**
@@ -530,7 +522,7 @@ nm_setting_wired_add_s390_option (NMSettingWired *setting,
 	g_hash_table_insert (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options,
 	                     g_strdup (key),
 	                     g_strdup (value));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_S390_OPTIONS);
+	_notify (setting, PROP_S390_OPTIONS);
 	return TRUE;
 }
 
@@ -557,7 +549,7 @@ nm_setting_wired_remove_s390_option (NMSettingWired *setting,
 
 	found = g_hash_table_remove (NM_SETTING_WIRED_GET_PRIVATE (setting)->s390_options, key);
 	if (found)
-		g_object_notify (G_OBJECT (setting), NM_SETTING_WIRED_S390_OPTIONS);
+		_notify (setting, PROP_S390_OPTIONS);
 	return found;
 }
 
@@ -775,21 +767,25 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	return TRUE;
 }
 
-static gboolean
-compare_property (NMSetting *setting,
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
                   NMSetting *other,
-                  const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	NMSettingClass *setting_class;
 
-	if (nm_streq (prop_spec->name, NM_SETTING_WIRED_CLONED_MAC_ADDRESS)) {
-		return nm_streq0 (NM_SETTING_WIRED_GET_PRIVATE (setting)->cloned_mac_address,
-		                  NM_SETTING_WIRED_GET_PRIVATE (other)->cloned_mac_address);
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_WIRED_CLONED_MAC_ADDRESS)) {
+		return    !other
+		       || nm_streq0 (NM_SETTING_WIRED_GET_PRIVATE (setting)->cloned_mac_address,
+		                     NM_SETTING_WIRED_GET_PRIVATE (other)->cloned_mac_address);
 	}
 
-	setting_class = NM_SETTING_CLASS (nm_setting_wired_parent_class);
-	return setting_class->compare_property (setting, other, prop_spec, flags);
+	return NM_SETTING_CLASS (nm_setting_wired_parent_class)->compare_property (sett_info,
+	                                                                           property_idx,
+	                                                                           setting,
+	                                                                           other,
+	                                                                           flags);
 }
 
 static GVariant *
@@ -806,40 +802,62 @@ clear_blacklist_item (char **item_p)
 	g_free (*item_p);
 }
 
-static void
-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);
-
-	/* We use GArray rather than GPtrArray so it will automatically be NULL-terminated */
-	priv->mac_address_blacklist = g_array_new (TRUE, FALSE, sizeof (char *));
-	g_array_set_clear_func (priv->mac_address_blacklist, (GDestroyNotify) clear_blacklist_item);
-}
+/*****************************************************************************/
 
 static void
-finalize (GObject *object)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (object);
-
-	g_free (priv->port);
-	g_free (priv->duplex);
-	g_free (priv->s390_nettype);
-
-	g_hash_table_destroy (priv->s390_options);
-
-	g_free (priv->device_mac_address);
-	g_free (priv->cloned_mac_address);
-	g_free (priv->generate_mac_address_mask);
-	g_array_unref (priv->mac_address_blacklist);
-
-	if (priv->s390_subchannels)
-		g_strfreev (priv->s390_subchannels);
-
-	g_free (priv->wol_password);
+	NMSettingWired *setting = NM_SETTING_WIRED (object);
+	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
 
-	G_OBJECT_CLASS (nm_setting_wired_parent_class)->finalize (object);
+	switch (prop_id) {
+	case PROP_PORT:
+		g_value_set_string (value, nm_setting_wired_get_port (setting));
+		break;
+	case PROP_SPEED:
+		g_value_set_uint (value, nm_setting_wired_get_speed (setting));
+		break;
+	case PROP_DUPLEX:
+		g_value_set_string (value, nm_setting_wired_get_duplex (setting));
+		break;
+	case PROP_AUTO_NEGOTIATE:
+		g_value_set_boolean (value, nm_setting_wired_get_auto_negotiate (setting));
+		break;
+	case PROP_MAC_ADDRESS:
+		g_value_set_string (value, nm_setting_wired_get_mac_address (setting));
+		break;
+	case PROP_CLONED_MAC_ADDRESS:
+		g_value_set_string (value, nm_setting_wired_get_cloned_mac_address (setting));
+		break;
+	case PROP_GENERATE_MAC_ADDRESS_MASK:
+		g_value_set_string (value, nm_setting_wired_get_generate_mac_address_mask (setting));
+		break;
+	case PROP_MAC_ADDRESS_BLACKLIST:
+		g_value_set_boxed (value, (char **) priv->mac_address_blacklist->data);
+		break;
+	case PROP_MTU:
+		g_value_set_uint (value, nm_setting_wired_get_mtu (setting));
+		break;
+	case PROP_S390_SUBCHANNELS:
+		g_value_set_boxed (value, priv->s390_subchannels);
+		break;
+	case PROP_S390_NETTYPE:
+		g_value_set_string (value, nm_setting_wired_get_s390_nettype (setting));
+		break;
+	case PROP_S390_OPTIONS:
+		g_value_take_boxed (value, _nm_utils_copy_strdict (priv->s390_options));
+		break;
+	case PROP_WAKE_ON_LAN:
+		g_value_set_uint (value, priv->wol);
+		break;
+	case PROP_WAKE_ON_LAN_PASSWORD:
+		g_value_set_string (value, priv->wol_password);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -919,60 +937,55 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_wired_init (NMSettingWired *setting)
 {
-	NMSettingWired *setting = NM_SETTING_WIRED (object);
 	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (setting);
 
-	switch (prop_id) {
-	case PROP_PORT:
-		g_value_set_string (value, nm_setting_wired_get_port (setting));
-		break;
-	case PROP_SPEED:
-		g_value_set_uint (value, nm_setting_wired_get_speed (setting));
-		break;
-	case PROP_DUPLEX:
-		g_value_set_string (value, nm_setting_wired_get_duplex (setting));
-		break;
-	case PROP_AUTO_NEGOTIATE:
-		g_value_set_boolean (value, nm_setting_wired_get_auto_negotiate (setting));
-		break;
-	case PROP_MAC_ADDRESS:
-		g_value_set_string (value, nm_setting_wired_get_mac_address (setting));
-		break;
-	case PROP_CLONED_MAC_ADDRESS:
-		g_value_set_string (value, nm_setting_wired_get_cloned_mac_address (setting));
-		break;
-	case PROP_GENERATE_MAC_ADDRESS_MASK:
-		g_value_set_string (value, nm_setting_wired_get_generate_mac_address_mask (setting));
-		break;
-	case PROP_MAC_ADDRESS_BLACKLIST:
-		g_value_set_boxed (value, (char **) priv->mac_address_blacklist->data);
-		break;
-	case PROP_MTU:
-		g_value_set_uint (value, nm_setting_wired_get_mtu (setting));
-		break;
-	case PROP_S390_SUBCHANNELS:
-		g_value_set_boxed (value, priv->s390_subchannels);
-		break;
-	case PROP_S390_NETTYPE:
-		g_value_set_string (value, nm_setting_wired_get_s390_nettype (setting));
-		break;
-	case PROP_S390_OPTIONS:
-		g_value_take_boxed (value, _nm_utils_copy_strdict (priv->s390_options));
-		break;
-	case PROP_WAKE_ON_LAN:
-		g_value_set_uint (value, priv->wol);
-		break;
-	case PROP_WAKE_ON_LAN_PASSWORD:
-		g_value_set_string (value, priv->wol_password);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	priv->s390_options = g_hash_table_new_full (nm_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 *));
+	g_array_set_clear_func (priv->mac_address_blacklist, (GDestroyNotify) clear_blacklist_item);
+}
+
+/**
+ * nm_setting_wired_new:
+ *
+ * Creates a new #NMSettingWired object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingWired object
+ **/
+NMSetting *
+nm_setting_wired_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRED, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE (object);
+
+	g_free (priv->port);
+	g_free (priv->duplex);
+	g_free (priv->s390_nettype);
+
+	g_hash_table_destroy (priv->s390_options);
+
+	g_free (priv->device_mac_address);
+	g_free (priv->cloned_mac_address);
+	g_free (priv->generate_mac_address_mask);
+	g_array_unref (priv->mac_address_blacklist);
+
+	if (priv->s390_subchannels)
+		g_strfreev (priv->s390_subchannels);
+
+	g_free (priv->wol_password);
+
+	G_OBJECT_CLASS (nm_setting_wired_parent_class)->finalize (object);
 }
 
 static void
@@ -984,8 +997,8 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingWiredPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify           = verify;
@@ -1005,12 +1018,11 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 * description: The property is not saved by the plugin.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PORT,
-		 g_param_spec_string (NM_SETTING_WIRED_PORT, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PORT] =
+	    g_param_spec_string (NM_SETTING_WIRED_PORT, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWired:speed:
@@ -1036,13 +1048,12 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 *    parameter in the ETHTOOL_OPTS variable.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_SPEED,
-		 g_param_spec_uint (NM_SETTING_WIRED_SPEED, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SPEED] =
+	    g_param_spec_uint (NM_SETTING_WIRED_SPEED, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWired:duplex:
@@ -1067,12 +1078,11 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 *    "duplex" parameter in the ETHOOL_OPTS variable.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_DUPLEX,
-		 g_param_spec_string (NM_SETTING_WIRED_DUPLEX, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_DUPLEX] =
+	    g_param_spec_string (NM_SETTING_WIRED_DUPLEX, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWired:auto-negotiate:
@@ -1094,17 +1104,15 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 *    "speed" and "duplex" parameters (skips link configuration).
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_AUTO_NEGOTIATE,
-		 g_param_spec_boolean (NM_SETTING_WIRED_AUTO_NEGOTIATE, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_CONSTRUCT |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_AUTO_NEGOTIATE] =
+	    g_param_spec_boolean (NM_SETTING_WIRED_AUTO_NEGOTIATE, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_CONSTRUCT |
+	                          G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_override (properties_override,
-	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                 NM_SETTING_WIRED_AUTO_NEGOTIATE),
+	                                   obj_properties[PROP_AUTO_NEGOTIATE],
 	                                   G_VARIANT_TYPE_BOOLEAN,
 	                                   _override_autoneg_get,
 	                                   NULL,
@@ -1134,17 +1142,15 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 *    permanent MAC address exists, the MAC address initially configured on the device.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MAC_ADDRESS,
-		 g_param_spec_string (NM_SETTING_WIRED_MAC_ADDRESS, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MAC_ADDRESS] =
+	    g_param_spec_string (NM_SETTING_WIRED_MAC_ADDRESS, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_WIRED_MAC_ADDRESS),
+	                                    obj_properties[PROP_MAC_ADDRESS],
 	                                    G_VARIANT_TYPE_BYTESTRING,
 	                                    _nm_utils_hwaddr_to_dbus,
 	                                    _nm_utils_hwaddr_from_dbus);
@@ -1192,17 +1198,15 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 *    For libnm and nmcli, this field is called "cloned-mac-address".
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CLONED_MAC_ADDRESS,
-		 g_param_spec_string (NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CLONED_MAC_ADDRESS] =
+	    g_param_spec_string (NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_override (properties_override,
-	                                     g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                   NM_SETTING_WIRED_CLONED_MAC_ADDRESS),
+	                                     obj_properties[PROP_CLONED_MAC_ADDRESS],
 	                                     G_VARIANT_TYPE_BYTESTRING,
 	                                     _nm_utils_hwaddr_cloned_get,
 	                                     _nm_utils_hwaddr_cloned_set,
@@ -1215,7 +1219,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 *   a hardware address in ASCII representation, or one of the special values
 	 *   "preserve", "permanent", "random" or "stable".
 	 *   This field replaces the deprecated "cloned-mac-address" on D-Bus, which
-	 *   can only contain explict hardware addresses. Note that this property
+	 *   can only contain explicit hardware addresses. Note that this property
 	 *   only exists in D-Bus API. libnm and nmcli continue to call this property
 	 *   "cloned-mac-address".
 	 * ---end---
@@ -1265,13 +1269,12 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 *   cloned-mac-address.
 	 * ---end---
 	 */
-	g_object_class_install_property
-	    (object_class, PROP_GENERATE_MAC_ADDRESS_MASK,
+	obj_properties[PROP_GENERATE_MAC_ADDRESS_MASK] =
 	     g_param_spec_string (NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, "", "",
 	                          NULL,
 	                          G_PARAM_READWRITE |
 	                          NM_SETTING_PARAM_FUZZY_IGNORE |
-	                          G_PARAM_STATIC_STRINGS));
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWired:mac-address-blacklist:
@@ -1295,13 +1298,12 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 * example: HWADDR_BLACKLIST="00:22:68:11:69:08 00:11:22:11:44:55"
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MAC_ADDRESS_BLACKLIST,
-		 g_param_spec_boxed (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_FUZZY_IGNORE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MAC_ADDRESS_BLACKLIST] =
+	    g_param_spec_boxed (NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_FUZZY_IGNORE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWired:mtu:
@@ -1315,14 +1317,13 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 * description: MTU of the interface.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MTU,
-		 g_param_spec_uint (NM_SETTING_WIRED_MTU, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MTU] =
+	    g_param_spec_uint (NM_SETTING_WIRED_MTU, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWired:s390-subchannels:
@@ -1342,13 +1343,12 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 * example: SUBCHANNELS=0.0.b00a,0.0.b00b,0.0.b00c
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_S390_SUBCHANNELS,
-		 g_param_spec_boxed (NM_SETTING_WIRED_S390_SUBCHANNELS, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_S390_SUBCHANNELS] =
+	    g_param_spec_boxed (NM_SETTING_WIRED_S390_SUBCHANNELS, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWired:s390-nettype:
@@ -1364,13 +1364,12 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 * example: NETTYPE=qeth
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_S390_NETTYPE,
-		 g_param_spec_string (NM_SETTING_WIRED_S390_NETTYPE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_INFERRABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_S390_NETTYPE] =
+	    g_param_spec_string (NM_SETTING_WIRED_S390_NETTYPE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWired:s390-options: (type GHashTable(utf8,utf8)):
@@ -1387,17 +1386,15 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 *   "portname" and "ctcprot" that have their own variables.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_S390_OPTIONS,
-		 g_param_spec_boxed (NM_SETTING_WIRED_S390_OPTIONS, "", "",
-		                     G_TYPE_HASH_TABLE,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_INFERRABLE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_S390_OPTIONS] =
+	    g_param_spec_boxed (NM_SETTING_WIRED_S390_OPTIONS, "", "",
+	                        G_TYPE_HASH_TABLE,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_INFERRABLE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_WIRED_S390_OPTIONS),
+	                                    obj_properties[PROP_S390_OPTIONS],
 	                                    G_VARIANT_TYPE ("a{ss}"),
 	                                    _nm_utils_strdict_to_dbus,
 	                                    _nm_utils_strdict_from_dbus);
@@ -1416,13 +1413,12 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_WAKE_ON_LAN,
-		 g_param_spec_uint (NM_SETTING_WIRED_WAKE_ON_LAN, "", "",
-		                    0, G_MAXUINT32, NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT,
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_WAKE_ON_LAN] =
+	    g_param_spec_uint (NM_SETTING_WIRED_WAKE_ON_LAN, "", "",
+	                       0, G_MAXUINT32, NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT,
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWired:wake-on-lan-password:
@@ -1433,12 +1429,13 @@ nm_setting_wired_class_init (NMSettingWiredClass *klass)
 	 *
 	 * Since: 1.2
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_WAKE_ON_LAN_PASSWORD,
-		 g_param_spec_string (NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_WAKE_ON_LAN_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_WIRED,
 	                               NULL, properties_override);
diff --git a/libnm-core/nm-setting-wired.h b/libnm-core/nm-setting-wired.h
index 015f09da..8e707b1b 100644
--- a/libnm-core/nm-setting-wired.h
+++ b/libnm-core/nm-setting-wired.h
@@ -63,20 +63,19 @@ G_BEGIN_DECLS
  * Since: 1.2
  */
 typedef enum { /*< flags >*/
-	NM_SETTING_WIRED_WAKE_ON_LAN_NONE      = 0, /*< skip >*/
-	NM_SETTING_WIRED_WAKE_ON_LAN_PHY       = (1 << 1),
-	NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST   = (1 << 2),
-	NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST = (1 << 3),
-	NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST = (1 << 4),
-	NM_SETTING_WIRED_WAKE_ON_LAN_ARP       = (1 << 5),
-	NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC     = (1 << 6),
-
-	_NM_SETTING_WIRED_WAKE_ON_LAN_LAST_OPT, /*< skip >*/
-	NM_SETTING_WIRED_WAKE_ON_LAN_ALL       = (((_NM_SETTING_WIRED_WAKE_ON_LAN_LAST_OPT - 1) << 1) - 1) - (1 << 0 /*DEFAULT*/), /*< skip >*/
-
-	NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT   = (1 << 0),
-	NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE    = (1 << 15),
-	NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS = NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT | NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE, /*< skip >*/
+	NM_SETTING_WIRED_WAKE_ON_LAN_NONE            = 0, /*< skip >*/
+	NM_SETTING_WIRED_WAKE_ON_LAN_PHY             = 0x2,
+	NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST         = 0x4,
+	NM_SETTING_WIRED_WAKE_ON_LAN_MULTICAST       = 0x8,
+	NM_SETTING_WIRED_WAKE_ON_LAN_BROADCAST       = 0x10,
+	NM_SETTING_WIRED_WAKE_ON_LAN_ARP             = 0x20,
+	NM_SETTING_WIRED_WAKE_ON_LAN_MAGIC           = 0x40,
+
+	NM_SETTING_WIRED_WAKE_ON_LAN_ALL             = 0x7E, /*< skip >*/
+
+	NM_SETTING_WIRED_WAKE_ON_LAN_DEFAULT         = 0x1,
+	NM_SETTING_WIRED_WAKE_ON_LAN_IGNORE          = 0x8000,
+	NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS = 0x8001, /*< skip >*/
 } NMSettingWiredWakeOnLan;
 
 #define NM_SETTING_WIRED_PORT "port"
diff --git a/libnm-core/nm-setting-wireguard.c b/libnm-core/nm-setting-wireguard.c
new file mode 100644
index 00000000..8c5b25a5
--- /dev/null
+++ b/libnm-core/nm-setting-wireguard.c
@@ -0,0 +1,2499 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2018 - 2019 Red Hat, Inc.
+ */
+
+#include "nm-default.h"
+
+#include "nm-setting-wireguard.h"
+
+#include "nm-setting-private.h"
+#include "nm-utils-private.h"
+#include "nm-connection-private.h"
+#include "nm-utils/nm-secret-utils.h"
+
+/*****************************************************************************/
+
+/**
+ * SECTION:nm-setting-wireguard
+ * @short_description: Describes connection properties for wireguard related options
+ *
+ * The #NMSettingWireGuard object is a #NMSetting subclass that contains settings
+ * for configuring WireGuard.
+ **/
+
+/*****************************************************************************/
+
+static NMWireGuardPeer *_wireguard_peer_dup (const NMWireGuardPeer *self);
+
+G_DEFINE_BOXED_TYPE (NMWireGuardPeer, nm_wireguard_peer, _wireguard_peer_dup, nm_wireguard_peer_unref)
+
+/* NMWireGuardPeer can also track invalid allowed-ip settings, and only reject
+ * them later during is_valid(). Such values are marked by a leading 'X' character
+ * in the @allowed_ips. It is expected, that such values are the expception, and
+ * commonly not present. */
+#define ALLOWED_IP_INVALID_X     'X'
+#define ALLOWED_IP_INVALID_X_STR "X"
+
+/**
+ * NMWireGuardPeer:
+ *
+ * The settings of one WireGuard peer.
+ *
+ * Since: 1.16
+ */
+struct _NMWireGuardPeer {
+	NMSockAddrEndpoint *endpoint;
+	char *public_key;
+	char *preshared_key;
+	GPtrArray *allowed_ips;
+	guint refcount;
+	NMSettingSecretFlags preshared_key_flags;
+	guint16 persistent_keepalive;
+	bool public_key_valid:1;
+	bool preshared_key_valid:1;
+	bool sealed:1;
+};
+
+static gboolean
+NM_IS_WIREGUARD_PEER (const NMWireGuardPeer *self, gboolean also_sealed)
+{
+	return    self
+	       && self->refcount > 0
+	       && (   also_sealed
+	           || !self->sealed);
+}
+
+/**
+ * nm_wireguard_peer_new:
+ *
+ * Returns: (transfer full): a new, default, unsealed #NMWireGuardPeer instance.
+ *
+ * Since: 1.16
+ */
+NMWireGuardPeer *
+nm_wireguard_peer_new (void)
+{
+	NMWireGuardPeer *self;
+
+	self = g_slice_new (NMWireGuardPeer);
+	*self = (NMWireGuardPeer) {
+		.refcount            = 1,
+		.preshared_key_flags = NM_SETTING_SECRET_FLAG_NOT_REQUIRED,
+	};
+	return self;
+}
+
+/**
+ * nm_wireguard_peer_new_clone:
+ * @self: the #NMWireGuardPeer instance to copy.
+ * @with_secrets: if %TRUE, the preshared-key secrets are copied
+ *  as well. Otherwise, they will be removed.
+ *
+ * Returns: (transfer full): a clone of @self. This instance
+ *   is always unsealed.
+ *
+ * Since: 1.16
+ */
+NMWireGuardPeer *
+nm_wireguard_peer_new_clone (const NMWireGuardPeer *self,
+                             gboolean with_secrets)
+{
+	NMWireGuardPeer *new;
+	guint i;
+
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), NULL);
+
+	new = g_slice_new (NMWireGuardPeer);
+	*new = (NMWireGuardPeer) {
+		.refcount             = 1,
+		.public_key           = g_strdup (self->public_key),
+		.public_key_valid     = self->public_key_valid,
+		.preshared_key        = with_secrets ? g_strdup (self->preshared_key) : NULL,
+		.preshared_key_valid  = self->preshared_key_valid,
+		.preshared_key_flags  = self->preshared_key_flags,
+		.endpoint             = nm_sock_addr_endpoint_ref (self->endpoint),
+		.persistent_keepalive = self->persistent_keepalive,
+	};
+	if (   self->allowed_ips
+	    && self->allowed_ips->len > 0) {
+		new->allowed_ips = g_ptr_array_new_full (self->allowed_ips->len,
+		                                         g_free);
+		for (i = 0; i < self->allowed_ips->len; i++) {
+			g_ptr_array_add (new->allowed_ips,
+			                 g_strdup (self->allowed_ips->pdata[i]));
+		}
+	}
+	return new;
+}
+
+/**
+ * nm_wireguard_peer_ref:
+ * @self: (allow-none): the #NMWireGuardPeer instance
+ *
+ * This is not thread-safe.
+ *
+ * Returns: returns the input argument @self after incrementing
+ *   the reference count.
+ *
+ * Since: 1.16
+ */
+NMWireGuardPeer *
+nm_wireguard_peer_ref (NMWireGuardPeer *self)
+{
+	if (!self)
+		return NULL;
+
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), NULL);
+
+	nm_assert (self->refcount < G_MAXUINT);
+
+	self->refcount++;
+	return self;
+}
+
+/**
+ * nm_wireguard_peer_unref:
+ * @self: (allow-none): the #NMWireGuardPeer instance
+ *
+ * Drop a reference to @self. If the last reference is dropped,
+ * the instance is freed and all accociate data released.
+ *
+ * This is not thread-safe.
+ *
+ * Since: 1.16
+ */
+void
+nm_wireguard_peer_unref (NMWireGuardPeer *self)
+{
+	if (!self)
+		return;
+
+	g_return_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE));
+
+	if (--self->refcount > 0)
+		return;
+
+	nm_sock_addr_endpoint_unref (self->endpoint);
+	if (self->allowed_ips)
+		g_ptr_array_unref (self->allowed_ips);
+	g_free (self->public_key);
+	nm_free_secret (self->preshared_key);
+	g_slice_free (NMWireGuardPeer, self);
+}
+
+/**
+ * _wireguard_peer_dup:
+ * @self: the #NMWireGuardPeer instance
+ *
+ * Duplicates the #NMWireGuardPeer instance. Note that if @self
+ * is already sealed, this increments the reference count and
+ * returns it. If the instance is still unsealed, it is copied.
+ *
+ * Returns: (transfer full): a duplicate of @self, or (if the
+ *   instance is sealed and thus immutable) a reference to @self.
+ *   As such, the instance will be sealed if and only if @self is
+ *   sealed.
+ */
+static NMWireGuardPeer *
+_wireguard_peer_dup (const NMWireGuardPeer *self)
+{
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), NULL);
+
+	if (self->sealed)
+		return nm_wireguard_peer_ref ((NMWireGuardPeer *) self);
+	return nm_wireguard_peer_new_clone (self, TRUE);
+}
+
+/**
+ * nm_wireguard_peer_seal:
+ * @self: the #NMWireGuardPeer instance
+ *
+ * Seal the #NMWireGuardPeer instance. Afterwards, it is a bug
+ * to call all functions that modify the instance (except ref/unref).
+ * A sealed instance cannot be unsealed again, but you can create
+ * an unsealed copy with nm_wireguard_peer_new_clone().
+ *
+ * Since: 1.16
+ */
+void
+nm_wireguard_peer_seal (NMWireGuardPeer *self)
+{
+	g_return_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE));
+
+	self->sealed = TRUE;
+
+	if (self->allowed_ips) {
+		if (self->allowed_ips->len == 0)
+			nm_clear_pointer (&self->allowed_ips, g_ptr_array_unref);
+	}
+}
+
+/**
+ * nm_wireguard_peer_is_sealed:
+ * @self: the #NMWireGuardPeer instance
+ *
+ * Returns: whether @self is sealed or not.
+ *
+ * Since: 1.16
+ */
+gboolean
+nm_wireguard_peer_is_sealed (const NMWireGuardPeer *self)
+{
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), FALSE);
+
+	return self->sealed;
+}
+
+/**
+ * nm_wireguard_peer_get_public_key:
+ * @self: the #NMWireGuardPeer instance
+ *
+ * Returns: (transfer none): the public key or %NULL if unset.
+ *
+ * Since: 1.16
+ */
+const char *
+nm_wireguard_peer_get_public_key (const NMWireGuardPeer *self)
+{
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), NULL);
+
+	return self->public_key;
+}
+
+/**
+ * nm_wireguard_peer_set_public_key:
+ * @self: the unsealed #NMWireGuardPeer instance
+ * @public_key: (allow-none) (transfer none): the new public
+ *   key or %NULL to clear the public key.
+ * @accept_invalid: if %TRUE and @public_key is not %NULL and
+ *   invalid, then do not modify the instance.
+ *
+ * Reset the public key. Note that if the public key is valid, it
+ * will be normalized (which may or may not modify the set value).
+ *
+ * It is a bug trying to modify a sealed #NMWireGuardPeer instance.
+ *
+ * Returns: %TRUE if the key was valid or %NULL. Returns
+ *   %FALSE for invalid keys. Depending on @accept_invalid
+ *   will an invalid key be set or not.
+ *
+ * Since: 1.16
+ */
+gboolean
+nm_wireguard_peer_set_public_key (NMWireGuardPeer *self,
+                                  const char *public_key,
+                                  gboolean accept_invalid)
+{
+	char *public_key_normalized = NULL;
+	gboolean is_valid;
+
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, FALSE), FALSE);
+
+	if (!public_key) {
+		nm_clear_g_free (&self->public_key);
+		return TRUE;
+	}
+
+	is_valid = nm_utils_base64secret_normalize (public_key,
+	                                            NM_WIREGUARD_PUBLIC_KEY_LEN,
+	                                            &public_key_normalized);
+	nm_assert (is_valid == (public_key_normalized != NULL));
+
+	if (   !is_valid
+	    && !accept_invalid)
+		return FALSE;
+
+	self->public_key_valid = is_valid;
+	g_free (self->public_key);
+	self->public_key = public_key_normalized ?: g_strdup (public_key);
+	return is_valid;
+}
+
+void
+_nm_wireguard_peer_set_public_key_bin (NMWireGuardPeer *self,
+                                       const guint8 public_key[static NM_WIREGUARD_PUBLIC_KEY_LEN])
+{
+	g_return_if_fail (NM_IS_WIREGUARD_PEER (self, FALSE));
+
+	nm_clear_g_free (&self->public_key);
+
+	if (!public_key)
+		return;
+
+	self->public_key = g_base64_encode (public_key, NM_WIREGUARD_PUBLIC_KEY_LEN);
+	self->public_key_valid = TRUE;
+}
+
+/**
+ * nm_wireguard_peer_get_preshared_key:
+ * @self: the #NMWireGuardPeer instance
+ *
+ * Returns: (transfer none): the preshared key or %NULL if unset.
+ *
+ * Since: 1.16
+ */
+const char *
+nm_wireguard_peer_get_preshared_key (const NMWireGuardPeer *self)
+{
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), NULL);
+
+	return self->preshared_key;
+}
+
+/**
+ * nm_wireguard_peer_set_preshared_key:
+ * @self: the unsealed #NMWireGuardPeer instance
+ * @preshared_key: (allow-none) (transfer none): the new preshared
+ *   key or %NULL to clear the preshared key.
+ * @accept_invalid: whether to allow setting the key to an invalid
+ *   value. If %FALSE, @self is unchanged if the key is invalid
+ *   and if %FALSE is returned.
+ *
+ * Reset the preshared key. Note that if the preshared key is valid, it
+ * will be normalized (which may or may not modify the set value).
+ *
+ * Note that the preshared-key is a secret and consequently has corresponding
+ * preshared-key-flags property. This is so that secrets can be optional
+ * and requested on demand from a secret-agent. Also, an invalid  preshared-key
+ * may optionally cause nm_wireguard_peer_is_valid() to fail or it may
+ * be accepted.
+ *
+ * It is a bug trying to modify a sealed #NMWireGuardPeer instance.
+ *
+ * Returns: %TRUE if the preshared-key is valid, otherwise %FALSE.
+ *   %NULL is considered a valid value.
+ *   If the key is invalid, it depends on @accept_invalid whether the
+ *   previous value was reset.
+ *
+ * Since: 1.16
+ */
+gboolean
+nm_wireguard_peer_set_preshared_key (NMWireGuardPeer *self,
+                                     const char *preshared_key,
+                                     gboolean accept_invalid)
+{
+	char *preshared_key_normalized = NULL;
+	gboolean is_valid;
+
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, FALSE), FALSE);
+
+	if (!preshared_key) {
+		nm_clear_pointer (&self->preshared_key, nm_free_secret);
+		return TRUE;
+	}
+
+	is_valid = nm_utils_base64secret_normalize (preshared_key,
+	                                            NM_WIREGUARD_SYMMETRIC_KEY_LEN,
+	                                            &preshared_key_normalized);
+	nm_assert (is_valid == (preshared_key_normalized != NULL));
+
+	if (   !is_valid
+	    && !accept_invalid)
+		return FALSE;
+
+	self->preshared_key_valid = is_valid;
+	nm_free_secret (self->preshared_key);
+	self->preshared_key = preshared_key_normalized ?: g_strdup (preshared_key);
+	return is_valid;
+}
+
+/**
+ * nm_wireguard_peer_get_preshared_key_flags:
+ * @self: the #NMWireGuardPeer instance
+ *
+ * Returns: get the secret flags for the preshared-key.
+ *
+ * Since: 1.16
+ */
+NMSettingSecretFlags
+nm_wireguard_peer_get_preshared_key_flags (const NMWireGuardPeer *self)
+{
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), 0);
+
+	return self->preshared_key_flags;
+}
+
+/**
+ * nm_wireguard_peer_set_preshared_key_flags:
+ * @self: the unsealed #NMWireGuardPeer instance
+ * @preshared_key_flags: the secret flags to set.
+ *
+ * It is a bug trying to modify a sealed #NMWireGuardPeer instance.
+ *
+ * Since: 1.16
+ */
+void
+nm_wireguard_peer_set_preshared_key_flags (NMWireGuardPeer *self,
+                                           NMSettingSecretFlags preshared_key_flags)
+{
+	g_return_if_fail (NM_IS_WIREGUARD_PEER (self, FALSE));
+
+	self->preshared_key_flags = preshared_key_flags;
+}
+
+/**
+ * nm_wireguard_peer_get_persistent_keepalive:
+ * @self: the #NMWireGuardPeer instance
+ *
+ * Returns: get the persistent-keepalive setting in seconds. Set to zero to disable
+ *   keep-alive.
+ *
+ * Since: 1.16
+ */
+guint16
+nm_wireguard_peer_get_persistent_keepalive (const NMWireGuardPeer *self)
+{
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), 0);
+
+	return self->persistent_keepalive;
+}
+
+/**
+ * nm_wireguard_peer_set_persistent_keepalive:
+ * @self: the unsealed #NMWireGuardPeer instance
+ * @persistent_keepalive: the keep-alive value to set.
+ *
+ * It is a bug trying to modify a sealed #NMWireGuardPeer instance.
+ *
+ * Since: 1.16
+ */
+void
+nm_wireguard_peer_set_persistent_keepalive (NMWireGuardPeer *self,
+                                            guint16 persistent_keepalive)
+{
+	g_return_if_fail (NM_IS_WIREGUARD_PEER (self, FALSE));
+
+	self->persistent_keepalive = persistent_keepalive;
+}
+
+NMSockAddrEndpoint *
+_nm_wireguard_peer_get_endpoint (const NMWireGuardPeer *self)
+{
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), NULL);
+
+	return self->endpoint;
+}
+
+/**
+ * nm_wireguard_peer_get_endpoint:
+ * @self: the #NMWireGuardPeer instance
+ *
+ * Returns: (transfer none): the endpoint or %NULL if none was set.
+ *
+ * Since: 1.16
+ */
+const char *
+nm_wireguard_peer_get_endpoint (const NMWireGuardPeer *self)
+{
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), NULL);
+
+	return   self->endpoint
+	       ? nm_sock_addr_endpoint_get_endpoint (self->endpoint)
+	       : NULL;
+}
+
+void
+_nm_wireguard_peer_set_endpoint (NMWireGuardPeer *self,
+                                 NMSockAddrEndpoint *endpoint)
+{
+	NMSockAddrEndpoint *old;
+
+	nm_assert (NM_IS_WIREGUARD_PEER (self, FALSE));
+
+	old = self->endpoint;
+	self->endpoint = nm_sock_addr_endpoint_ref (endpoint);
+	nm_sock_addr_endpoint_unref (old);
+}
+
+/**
+ * nm_wireguard_peer_set_endpoint:
+ * @self: the unsealed #NMWireGuardPeer instance
+ * @endpoint: the socket address endpoint to set or %NULL.
+ * @allow_invalid: if %TRUE, also invalid values are set.
+ *   If %FALSE, the function does nothing for invalid @endpoint
+ *   arguments.
+ *
+ * Sets or clears the endpoint of @self.
+ *
+ * It is a bug trying to modify a sealed #NMWireGuardPeer instance.
+ *
+ * Returns: %TRUE if the endpoint is %NULL or valid. For an
+ *   invalid @endpoint argument, %FALSE is returned. Depending
+ *   on @allow_invalid, the instance will be modified.
+ *
+ * Since: 1.16
+ */
+gboolean
+nm_wireguard_peer_set_endpoint (NMWireGuardPeer *self,
+                                const char *endpoint,
+                                gboolean allow_invalid)
+{
+	NMSockAddrEndpoint *old;
+	NMSockAddrEndpoint *new;
+	gboolean is_valid;
+
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, FALSE), FALSE);
+
+	if (!endpoint) {
+		nm_clear_pointer (&self->endpoint, nm_sock_addr_endpoint_unref);
+		return TRUE;
+	}
+
+	new = nm_sock_addr_endpoint_new (endpoint);
+
+	is_valid = (nm_sock_addr_endpoint_get_host (new) != NULL);
+
+	if (   !allow_invalid
+	    && !is_valid) {
+		nm_sock_addr_endpoint_unref (new);
+		return FALSE;
+	}
+
+	old = self->endpoint;
+	self->endpoint = new;
+	nm_sock_addr_endpoint_unref (old);
+	return is_valid;
+}
+
+/**
+ * nm_wireguard_peer_get_allowed_ips_len:
+ * @self: the #NMWireGuardPeer instance
+ *
+ * Returns: the number of allowed-ips entries.
+ *
+ * Since: 1.16
+ */
+guint
+nm_wireguard_peer_get_allowed_ips_len (const NMWireGuardPeer *self)
+{
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), 0);
+
+	return self->allowed_ips ? self->allowed_ips->len : 0u;
+}
+
+/**
+ * nm_wireguard_peer_get_allowed_ip:
+ * @self: the #NMWireGuardPeer instance
+ * @idx: the index from zero to (allowed-ips-len - 1) to
+ *   retrieve.
+ * @out_is_valid: (allow-none): %TRUE if the returned value is a valid allowed-ip
+ *   setting.
+ *
+ * Returns: (transfer none): the allowed-ip setting at index @idx.
+ *   If @idx is out of range, %NULL will be returned.
+ *
+ * Since: 1.16
+ */
+const char *
+nm_wireguard_peer_get_allowed_ip (const NMWireGuardPeer *self,
+                                  guint idx,
+                                  gboolean *out_is_valid)
+{
+	const char *s;
+
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), NULL);
+
+	if (   !self->allowed_ips
+	    || idx >= self->allowed_ips->len) {
+		NM_SET_OUT (out_is_valid, FALSE);
+		return NULL;
+	}
+
+	s = self->allowed_ips->pdata[idx];
+	NM_SET_OUT (out_is_valid, s[0] != ALLOWED_IP_INVALID_X);
+	return s[0] == ALLOWED_IP_INVALID_X ? &s[1] : s;
+}
+
+/**
+ * nm_wireguard_peer_clear_allowed_ips:
+ * @self: the unsealed #NMWireGuardPeer instance
+ *
+ * Removes all allowed-ip entries.
+ *
+ * It is a bug trying to modify a sealed #NMWireGuardPeer instance.
+ *
+ * Since: 1.16
+ */
+void
+nm_wireguard_peer_clear_allowed_ips (NMWireGuardPeer *self)
+{
+	g_return_if_fail (NM_IS_WIREGUARD_PEER (self, FALSE));
+
+	if (self->allowed_ips)
+		g_ptr_array_set_size (self->allowed_ips, 0);
+}
+
+static gboolean
+_peer_append_allowed_ip (NMWireGuardPeer *self,
+                         const char *allowed_ip,
+                         gboolean accept_invalid)
+{
+	int addr_family;
+	int prefix;
+	NMIPAddr addrbin;
+	char *str;
+	gboolean is_valid = TRUE;
+
+	nm_assert (NM_IS_WIREGUARD_PEER (self, FALSE));
+	nm_assert (allowed_ip);
+
+	/* normalize the address (if it is valid. Otherwise, take it
+	 * as-is (it will render the instance invalid). */
+	if (!nm_utils_parse_inaddr_prefix_bin (AF_UNSPEC,
+	                                       allowed_ip,
+	                                       &addr_family,
+	                                       &addrbin,
+	                                       &prefix)) {
+		if (!accept_invalid)
+			return FALSE;
+		/* mark the entry as invalid by having a "X" prefix. */
+		str = g_strconcat (ALLOWED_IP_INVALID_X_STR, allowed_ip, NULL);
+		is_valid = FALSE;
+	} else {
+		char addrstr[NM_UTILS_INET_ADDRSTRLEN];
+
+		nm_assert_addr_family (addr_family);
+
+		nm_utils_inet_ntop (addr_family, &addrbin, addrstr);
+		if (prefix >= 0)
+			str = g_strdup_printf ("%s/%d", addrstr, prefix);
+		else
+			str = g_strdup (addrstr);
+		nm_assert (str[0] != ALLOWED_IP_INVALID_X);
+	}
+
+	if (!self->allowed_ips)
+		self->allowed_ips = g_ptr_array_new_with_free_func (g_free);
+
+	g_ptr_array_add (self->allowed_ips, str);
+	return is_valid;
+}
+
+/**
+ * nm_wireguard_peer_append_allowed_ip:
+ * @self: the unsealed #NMWireGuardPeer instance
+ * @allowed_ip: the allowed-ip entry to set.
+ * @accept_invalid: if %TRUE, also invalid @allowed_ip value
+ *   will be appended. Otherwise, the function does nothing
+ *   in face of invalid values and returns %FALSE.
+ *
+ * Appends @allowed_ip setting to the list. This does not check
+ * for duplicates and always appends @allowed_ip to the end of the
+ * list. If @allowed_ip is valid, it will be normalized and a modified
+ * for might be appended. If @allowed_ip is invalid, it will still be
+ * appended, but later verification will fail.
+ *
+ * It is a bug trying to modify a sealed #NMWireGuardPeer instance.
+ *
+ * Returns: %TRUE if the value is a valid allowed-ips value, %FALSE otherwise.
+ *   Depending on @accept_invalid, also invalid values are added.
+ *
+ * Since: 1.16
+ */
+gboolean
+nm_wireguard_peer_append_allowed_ip (NMWireGuardPeer *self,
+                                     const char *allowed_ip,
+                                     gboolean accept_invalid)
+{
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, FALSE), FALSE);
+	g_return_val_if_fail (allowed_ip, FALSE);
+
+	return _peer_append_allowed_ip (self, allowed_ip, accept_invalid);
+}
+
+/**
+ * nm_wireguard_peer_remove_allowed_ip:
+ * @self: the unsealed #NMWireGuardPeer instance
+ * @idx: the index from zero to (allowed-ips-len - 1) to
+ *   retrieve. If the index is out of range, %FALSE is returned
+ *   and nothing is done.
+ *
+ * Removes the allowed-ip at the given @idx. This shifts all
+ * following entries one index down.
+ *
+ * It is a bug trying to modify a sealed #NMWireGuardPeer instance.
+ *
+ * Returns: %TRUE if @idx was valid and the allowed-ip was removed.
+ *   %FALSE otherwise, and the peer will not be changed.
+ *
+ * Since: 1.16
+ */
+gboolean
+nm_wireguard_peer_remove_allowed_ip (NMWireGuardPeer *self,
+                                     guint idx)
+{
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, FALSE), FALSE);
+
+	if (   !self->allowed_ips
+	    || idx >= self->allowed_ips->len)
+		return FALSE;
+
+	g_ptr_array_remove_index (self->allowed_ips, idx);
+	return TRUE;
+}
+
+/**
+ * nm_wireguard_peer_is_valid:
+ * @self: the #NMWireGuardPeer instance
+ * @check_secrets: if %TRUE, non-secret properties are validated.
+ *   Otherwise they are ignored for this purpose.
+ * @check_non_secrets: if %TRUE, secret properties are validated.
+ *   Otherwise they are ignored for this purpose.
+ * @error: the #GError location for returning the failure reason.
+ *
+ * Returns:  %TRUE if the peer is valid or fails with an error
+ *   reason.
+ *
+ * Since: 1.16
+ */
+gboolean
+nm_wireguard_peer_is_valid (const NMWireGuardPeer *self,
+                            gboolean check_non_secrets,
+                            gboolean check_secrets,
+                            GError **error)
+{
+	guint i;
+
+	g_return_val_if_fail (NM_IS_WIREGUARD_PEER (self, TRUE), FALSE);
+	g_return_val_if_fail (!error || !*error, FALSE);
+
+	if (check_non_secrets) {
+		if (!self->public_key) {
+			g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY,
+			                     _("missing public-key for peer"));
+			return FALSE;
+		} else if (!self->public_key_valid) {
+			g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("invalid public-key for peer"));
+			return FALSE;
+		}
+	}
+
+	if (check_secrets) {
+		if (   self->preshared_key
+		    && !self->preshared_key_valid) {
+			g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("invalid preshared-key for peer"));
+			return FALSE;
+		}
+	}
+
+	if (check_non_secrets) {
+		if (!_nm_utils_secret_flags_validate (self->preshared_key_flags,
+		                                      NULL,
+		                                      NULL,
+		                                      NM_SETTING_SECRET_FLAG_NONE,
+		                                      error))
+			return FALSE;
+	}
+
+	if (check_non_secrets) {
+		if (   self->endpoint
+		    && !nm_sock_addr_endpoint_get_host (self->endpoint)) {
+			g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("invalid endpoint for peer"));
+			return FALSE;
+		}
+
+		if (self->allowed_ips) {
+			for (i = 0; i < self->allowed_ips->len; i++) {
+				const char *s = self->allowed_ips->pdata[i];
+
+				if (s[0] == ALLOWED_IP_INVALID_X) {
+					g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
+					             _("invalid IP address \"%s\" for allowed-ip of peer"),
+					             &s[1]);
+					return FALSE;
+				}
+			}
+		}
+
+		if (!_nm_setting_secret_flags_valid (self->preshared_key_flags)) {
+			g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("invalid preshared-key-flags for peer"));
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+/**
+ * nm_wireguard_peer_cmp:
+ * @a: (allow-none): the #NMWireGuardPeer to compare.
+ * @b: (allow-none): the other #NMWireGuardPeer to compare.
+ * @compare_flags: #NMSettingCompareFlags to affect the comparison.
+ *
+ * Returns: zero of the two instances are equivalent or
+ *   a non-zero integer otherwise. This defines a total ordering
+ *   over the peers. Whether a peer is sealed or not, does not
+ *   affect the comparison.
+ *
+ * Since: 1.16
+ */
+int
+nm_wireguard_peer_cmp (const NMWireGuardPeer *a,
+                       const NMWireGuardPeer *b,
+                       NMSettingCompareFlags compare_flags)
+{
+	guint i, n;
+
+	NM_CMP_SELF (a, b);
+
+	/* regardless of the @compare_flags, the public-key is the ID of the peer. It must
+	 * always be compared. */
+	NM_CMP_FIELD_BOOL (a, b, public_key_valid);
+	NM_CMP_FIELD_STR0 (a, b, public_key);
+
+	if (NM_FLAGS_ANY (compare_flags,   NM_SETTING_COMPARE_FLAG_INFERRABLE
+	                                 | NM_SETTING_COMPARE_FLAG_FUZZY))
+		return 0;
+
+	NM_CMP_FIELD_BOOL (a, b, endpoint);
+	if (a->endpoint) {
+		NM_CMP_DIRECT_STRCMP0 (nm_sock_addr_endpoint_get_endpoint (a->endpoint),
+		                       nm_sock_addr_endpoint_get_endpoint (b->endpoint));
+	}
+
+	NM_CMP_FIELD (a, b, persistent_keepalive);
+
+	NM_CMP_DIRECT ((n = (a->allowed_ips ? a->allowed_ips->len : 0u)),
+	               (     b->allowed_ips ? b->allowed_ips->len : 0u ));
+	for (i = 0; i < n; i++)
+		NM_CMP_DIRECT_STRCMP0 (a->allowed_ips->pdata[i], b->allowed_ips->pdata[i]);
+
+	NM_CMP_FIELD (a, b, preshared_key_flags);
+
+	if (!NM_FLAGS_HAS (compare_flags, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)) {
+		if (   NM_FLAGS_HAS (compare_flags, NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
+		    && NM_FLAGS_HAS (a->preshared_key_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED)) {
+			/* pass */
+		} else if (   NM_FLAGS_HAS (compare_flags, NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
+		           && NM_FLAGS_HAS (a->preshared_key_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
+			/* pass */
+		} else {
+			NM_CMP_FIELD_BOOL (a, b, preshared_key_valid);
+			NM_CMP_FIELD_STR0 (a, b, preshared_key);
+		}
+	}
+
+	return 0;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+	const char *public_key;
+	NMWireGuardPeer *peer;
+	guint idx;
+} PeerData;
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
+	PROP_FWMARK,
+	PROP_LISTEN_PORT,
+	PROP_MTU,
+	PROP_PEER_ROUTES,
+	PROP_PRIVATE_KEY,
+	PROP_PRIVATE_KEY_FLAGS,
+);
+
+typedef struct {
+	char *private_key;
+	GPtrArray *peers_arr;
+	GHashTable *peers_hash;
+	NMSettingSecretFlags private_key_flags;
+	guint32 fwmark;
+	guint32 mtu;
+	guint16 listen_port;
+	bool private_key_valid:1;
+	bool peer_routes:1;
+} NMSettingWireGuardPrivate;
+
+/**
+ * NMSettingWireGuard:
+ *
+ * WireGuard Ethernet Settings
+ *
+ * Since: 1.16
+ */
+struct _NMSettingWireGuard {
+	NMSetting parent;
+	NMSettingWireGuardPrivate _priv;
+};
+
+struct _NMSettingWireGuardClass {
+	NMSettingClass parent;
+};
+
+G_DEFINE_TYPE (NMSettingWireGuard, nm_setting_wireguard, NM_TYPE_SETTING)
+
+#define NM_SETTING_WIREGUARD_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMSettingWireGuard, NM_IS_SETTING_WIREGUARD, NMSetting)
+
+/*****************************************************************************/
+
+#define peers_psk_get_secret_name_a(public_key, to_free) \
+	nm_construct_name_a (NM_SETTING_WIREGUARD_PEERS".%s."NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, (public_key), (to_free))
+
+#define peers_psk_get_secret_name_dup(public_key) \
+	g_strdup_printf (NM_SETTING_WIREGUARD_PEERS".%s."NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, (public_key))
+
+#define peers_psk_get_secret_parse_a(secret_public_key, public_key_free) \
+	({ \
+		const char *_secret_public_key = (secret_public_key); \
+		char **_public_key_free = (public_key_free); \
+		const char *_public_key = NULL; \
+		\
+		nm_assert (_public_key_free && !*_public_key_free); \
+		\
+		if (NM_STR_HAS_PREFIX (_secret_public_key, NM_SETTING_WIREGUARD_PEERS".")) { \
+			_secret_public_key += NM_STRLEN (NM_SETTING_WIREGUARD_PEERS"."); \
+			if (NM_STR_HAS_SUFFIX (_secret_public_key, "."NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)) { \
+				_public_key = nm_strndup_a (300, _secret_public_key, strlen (_secret_public_key) - NM_STRLEN ("."NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY), _public_key_free); \
+			} \
+		} \
+		\
+		_public_key; \
+	})
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_wireguard_get_private_key:
+ * @self: the #NMSettingWireGuard instance
+ *
+ * Returns: (transfer none): the set private-key or %NULL.
+ *
+ * Since: 1.16
+ */
+const char *
+nm_setting_wireguard_get_private_key (NMSettingWireGuard *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), NULL);
+
+	return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->private_key;
+}
+
+/**
+ * nm_setting_wireguard_get_private_key_flags:
+ * @self: the #NMSettingWireGuard instance
+ *
+ * Returns: the secret-flags for #NMSettingWireGuard:private-key.
+ *
+ * Since: 1.16
+ */
+NMSettingSecretFlags
+nm_setting_wireguard_get_private_key_flags (NMSettingWireGuard *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), 0);
+
+	return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->private_key_flags;
+}
+
+/**
+ * nm_setting_wireguard_get_fwmark:
+ * @self: the #NMSettingWireGuard instance
+ *
+ * Returns: the set firewall mark.
+ *
+ * Since: 1.16
+ */
+guint32
+nm_setting_wireguard_get_fwmark (NMSettingWireGuard *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), 0);
+
+	return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->fwmark;
+}
+
+/**
+ * nm_setting_wireguard_get_listen_port:
+ * @self: the #NMSettingWireGuard instance
+ *
+ * Returns: the set UDP listen port.
+ *
+ * Since: 1.16
+ */
+guint16
+nm_setting_wireguard_get_listen_port (NMSettingWireGuard *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), 0);
+
+	return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->listen_port;
+}
+
+/**
+ * nm_setting_wireguard_get_peer_routes:
+ * @self: the #NMSettingWireGuard instance
+ *
+ * Returns: whether automatically add peer routes.
+ *
+ * Since: 1.16
+ */
+gboolean
+nm_setting_wireguard_get_peer_routes (NMSettingWireGuard *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), TRUE);
+
+	return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->peer_routes;
+}
+
+/**
+ * nm_setting_wireguard_get_mtu:
+ * @self: the #NMSettingWireGuard instance
+ *
+ * Returns: the MTU of the setting.
+ *
+ * Since: 1.16
+ */
+guint32
+nm_setting_wireguard_get_mtu (NMSettingWireGuard *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), 0);
+
+	return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->mtu;
+}
+
+/*****************************************************************************/
+
+static void
+_peer_free (PeerData *pd)
+{
+	nm_assert (pd);
+
+	nm_wireguard_peer_unref (pd->peer);
+	g_slice_free (PeerData, pd);
+}
+
+/*****************************************************************************/
+
+static void
+_peers_notify (gpointer self)
+{
+	_nm_setting_emit_property_changed (self);
+}
+
+static PeerData *
+_peers_get (NMSettingWireGuardPrivate *priv,
+            guint idx)
+{
+	PeerData *pd;
+
+	nm_assert (priv);
+	nm_assert (idx < priv->peers_arr->len);
+
+	pd = priv->peers_arr->pdata[idx];
+
+	nm_assert (pd);
+	nm_assert (pd->idx == idx);
+	nm_assert (NM_IS_WIREGUARD_PEER (pd->peer, TRUE));
+	nm_assert (nm_wireguard_peer_is_sealed (pd->peer));
+	nm_assert (pd->public_key == nm_wireguard_peer_get_public_key (pd->peer));
+	nm_assert (g_hash_table_lookup (priv->peers_hash, pd) == pd);
+
+	return pd;
+}
+
+static PeerData *
+_peers_get_by_public_key (NMSettingWireGuardPrivate *priv,
+                          const char *public_key,
+                          gboolean try_with_normalized_key)
+{
+	gs_free char *public_key_normalized = NULL;
+	PeerData *pd;
+
+again:
+	nm_assert (priv);
+	nm_assert (public_key);
+
+	pd = g_hash_table_lookup (priv->peers_hash, &public_key);
+	if (pd) {
+		nm_assert (_peers_get (priv, pd->idx) == pd);
+		return pd;
+	}
+	if (   try_with_normalized_key
+	    && nm_utils_base64secret_normalize (public_key,
+	                                        NM_WIREGUARD_PUBLIC_KEY_LEN,
+	                                        &public_key_normalized)) {
+		public_key = public_key_normalized;
+		try_with_normalized_key = FALSE;
+		goto again;
+	}
+	return NULL;
+}
+
+static void
+_peers_remove (NMSettingWireGuardPrivate *priv,
+               PeerData *pd,
+               gboolean do_free)
+{
+	guint i;
+
+	nm_assert (pd);
+	nm_assert (_peers_get (priv, pd->idx) == pd);
+
+	for (i = pd->idx + 1; i < priv->peers_arr->len; i++)
+		_peers_get (priv, i)->idx--;
+
+	g_ptr_array_remove_index (priv->peers_arr, pd->idx);
+	if (!g_hash_table_remove (priv->peers_hash, pd))
+		nm_assert_not_reached ();
+	if (do_free)
+		_peer_free (pd);
+}
+
+/**
+ * nm_setting_wireguard_get_peers_len:
+ * @self: the #NMSettingWireGuard instance
+ *
+ * Returns: the number of registered peers.
+ *
+ * Since: 1.16
+ */
+guint
+nm_setting_wireguard_get_peers_len (NMSettingWireGuard *self)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), 0);
+
+	return NM_SETTING_WIREGUARD_GET_PRIVATE (self)->peers_arr->len;
+}
+
+/**
+ * nm_setting_wireguard_get_peer:
+ * @self: the #NMSettingWireGuard instance
+ * @idx: the index to lookup.
+ *
+ * Returns: (transfer none): the #NMWireGuardPeer entry at
+ *   index @idx. If the index is out of range, %NULL is returned.
+ *
+ * Since: 1.16
+ */
+NMWireGuardPeer *
+nm_setting_wireguard_get_peer (NMSettingWireGuard *self,
+                               guint idx)
+{
+	NMSettingWireGuardPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), NULL);
+
+	priv = NM_SETTING_WIREGUARD_GET_PRIVATE (self);
+
+	if (idx >= priv->peers_arr->len)
+		return NULL;
+
+	return _peers_get (priv, idx)->peer;
+}
+
+/**
+ * nm_setting_wireguard_get_peer_by_public_key:
+ * @self: the #NMSettingWireGuard instance
+ * @public_key: the public key for looking up the
+ *   peer.
+ * @out_idx: (out) (allow-none): optional output argument
+ *   for the index of the found peer. If no index is found,
+ *   this is set to the nm_setting_wireguard_get_peers_len().
+ *
+ * Returns: (transfer none): the #NMWireGuardPeer instance with a
+ *   matching public key. If no such peer exists, %NULL is returned.
+ *
+ * Since: 1.16
+ */
+NMWireGuardPeer *
+nm_setting_wireguard_get_peer_by_public_key (NMSettingWireGuard *self,
+                                             const char *public_key,
+                                             guint *out_idx)
+{
+	NMSettingWireGuardPrivate *priv;
+	PeerData *pd;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), NULL);
+	g_return_val_if_fail (public_key, NULL);
+
+	priv = NM_SETTING_WIREGUARD_GET_PRIVATE (self);
+
+	pd = _peers_get_by_public_key (priv, public_key, TRUE);
+	if (!pd) {
+		NM_SET_OUT (out_idx, priv->peers_arr->len);
+		return NULL;
+	}
+	NM_SET_OUT (out_idx, pd->idx);
+	return pd->peer;
+}
+
+static gboolean
+_peers_set (NMSettingWireGuardPrivate *priv,
+            NMWireGuardPeer *peer,
+            guint idx,
+            gboolean check_same_key)
+{
+	PeerData *pd_same_key = NULL;
+	PeerData *pd_idx = NULL;
+	const char *public_key;
+
+	nm_assert (idx <= priv->peers_arr->len);
+
+	public_key = nm_wireguard_peer_get_public_key (peer);
+
+	if (idx < priv->peers_arr->len) {
+		pd_idx = _peers_get (priv, idx);
+
+		if (pd_idx->peer == peer)
+			return FALSE;
+
+		if (   check_same_key
+		    && nm_streq (public_key, nm_wireguard_peer_get_public_key (pd_idx->peer)))
+			check_same_key = FALSE;
+	}
+
+	nm_wireguard_peer_seal (peer);
+	nm_wireguard_peer_ref (peer);
+
+	if (check_same_key) {
+		pd_same_key = _peers_get_by_public_key (priv, public_key, FALSE);
+		if (pd_same_key) {
+			if (pd_idx) {
+				nm_assert (pd_same_key != pd_idx);
+				_peers_remove (priv, pd_same_key, TRUE);
+				pd_same_key = NULL;
+			} else {
+				if (   pd_same_key->peer == peer
+				    && pd_same_key->idx == priv->peers_arr->len - 1) {
+					nm_wireguard_peer_unref (peer);
+					return FALSE;
+				}
+				_peers_remove (priv, pd_same_key, FALSE);
+				nm_wireguard_peer_unref (pd_same_key->peer);
+			}
+		}
+	} else
+		nm_assert (_peers_get_by_public_key (priv, public_key, FALSE) == pd_idx);
+
+	if (pd_idx) {
+		g_hash_table_remove (priv->peers_hash, pd_idx);
+		nm_wireguard_peer_unref (pd_idx->peer);
+		pd_idx->public_key = public_key;
+		pd_idx->peer = peer;
+		g_hash_table_add (priv->peers_hash, pd_idx);
+		return TRUE;
+	}
+
+
+	if (!pd_same_key)
+		pd_same_key = g_slice_new (PeerData);
+
+	*pd_same_key = (PeerData) {
+		.peer = peer,
+		.public_key = public_key,
+		.idx = priv->peers_arr->len,
+	};
+
+	g_ptr_array_add (priv->peers_arr, pd_same_key);
+	if (!nm_g_hash_table_add (priv->peers_hash, pd_same_key))
+		nm_assert_not_reached ();
+
+	nm_assert (_peers_get (priv, pd_same_key->idx) == pd_same_key);
+
+	return TRUE;
+}
+
+static gboolean
+_peers_append (NMSettingWireGuardPrivate *priv,
+               NMWireGuardPeer *peer,
+               gboolean check_same_key)
+{
+	return _peers_set (priv, peer, priv->peers_arr->len, check_same_key);
+}
+
+/**
+ * nm_setting_wireguard_set_peer:
+ * @self: the #NMSettingWireGuard instance
+ * @peer: the #NMWireGuardPeer instance to set.
+ *   This seals @peer and keeps a reference on the
+ *   instance.
+ * @idx: the index, in the range of 0 to the number of
+ *   peers (including). That means, if @idx is one past
+ *   the end of the number of peers, this is the same as
+ *   nm_setting_wireguard_append_peer(). Otherwise, the
+ *   peer at this index is replaced.
+ *
+ * If @idx is one past the last peer, the behavior is the same
+ * as nm_setting_wireguard_append_peer().
+ * Otherwise, the peer will be at @idx and replace the peer
+ * instance at that index. Note that if a peer with the same
+ * public-key exists on another index, then that peer will also
+ * be replaced. In that case, the number of peers will shrink
+ * by one (because the one at @idx got replace and then one
+ * with the same public-key got removed). This also means,
+ * that the resulting index afterwards may be one less than
+ * @idx (if another peer with a lower index was dropped).
+ *
+ * Since: 1.16
+ */
+void
+nm_setting_wireguard_set_peer (NMSettingWireGuard *self,
+                               NMWireGuardPeer *peer,
+                               guint idx)
+{
+	NMSettingWireGuardPrivate *priv;
+
+	g_return_if_fail (NM_IS_SETTING_WIREGUARD (self));
+	g_return_if_fail (NM_IS_WIREGUARD_PEER (peer, TRUE));
+
+	priv = NM_SETTING_WIREGUARD_GET_PRIVATE (self);
+
+	g_return_if_fail (idx <= priv->peers_arr->len);
+
+	if (_peers_set (priv, peer, idx, TRUE))
+		_peers_notify (self);
+}
+
+/**
+ * nm_setting_wireguard_append_peer:
+ * @self: the #NMSettingWireGuard instance
+ * @peer: the #NMWireGuardPeer instance to append.
+ *   This seals @peer and keeps a reference on the
+ *   instance.
+ *
+ * If a peer with the same public-key already exists, that
+ * one is replaced by @peer. The new @peer is always appended
+ * (or moved to) the end, so in case a peer is replaced, the
+ * indexes are shifted and the number of peers stays unchanged.
+ *
+ * Since: 1.16
+ */
+void
+nm_setting_wireguard_append_peer (NMSettingWireGuard *self,
+                                  NMWireGuardPeer *peer)
+{
+	g_return_if_fail (NM_IS_SETTING_WIREGUARD (self));
+	g_return_if_fail (NM_IS_WIREGUARD_PEER (peer, TRUE));
+
+	if (_peers_append (NM_SETTING_WIREGUARD_GET_PRIVATE (self),
+	                   peer,
+	                   TRUE))
+		_peers_notify (self);
+}
+
+/**
+ * nm_setting_wireguard_remove_peer
+ * @self: the #NMSettingWireGuard instance
+ * @idx: the index to remove.
+ *
+ * Returns: %TRUE if @idx was in range and a peer
+ *   was removed. Otherwise, @self is unchanged.
+ *
+ * Since: 1.16
+ */
+gboolean
+nm_setting_wireguard_remove_peer (NMSettingWireGuard *self,
+                                  guint idx)
+{
+	NMSettingWireGuardPrivate *priv;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), FALSE);
+
+	priv = NM_SETTING_WIREGUARD_GET_PRIVATE (self);
+
+	if (idx >= priv->peers_arr->len)
+		return FALSE;
+
+	_peers_remove (priv, _peers_get (priv, idx), TRUE);
+	_peers_notify (self);
+	return TRUE;
+}
+
+static guint
+_peers_clear (NMSettingWireGuardPrivate *priv)
+{
+	guint l;
+
+	l = priv->peers_arr->len;
+	while (priv->peers_arr->len > 0) {
+		_peers_remove (priv,
+		               _peers_get (priv, priv->peers_arr->len - 1),
+		               TRUE);
+	}
+	return l;
+}
+
+/**
+ * nm_setting_wireguard_:
+ * @self: the #NMSettingWireGuard instance
+ *
+ * Returns: the number of cleared peers.
+ *
+ * Since: 1.16
+ */
+guint
+nm_setting_wireguard_clear_peers (NMSettingWireGuard *self)
+{
+	guint l;
+
+	g_return_val_if_fail (NM_IS_SETTING_WIREGUARD (self), 0);
+
+	l = _peers_clear (NM_SETTING_WIREGUARD_GET_PRIVATE (self));
+	if (l > 0)
+		_peers_notify (self);
+	return l;
+}
+
+/*****************************************************************************/
+
+static GVariant *
+_peers_dbus_only_synth (const NMSettInfoSetting *sett_info,
+                        guint property_idx,
+                        NMConnection *connection,
+                        NMSetting *setting,
+                        NMConnectionSerializationFlags flags)
+{
+	NMSettingWireGuard *self = NM_SETTING_WIREGUARD (setting);
+	NMSettingWireGuardPrivate *priv;
+	gboolean any_peers = FALSE;
+	GVariantBuilder peers_builder;
+	guint i_peer, n_peers;
+	guint i;
+
+	n_peers = nm_setting_wireguard_get_peers_len (self);
+	if (n_peers == 0)
+		return NULL;
+
+	priv = NM_SETTING_WIREGUARD_GET_PRIVATE (self);
+
+	for (i_peer = 0; i_peer < n_peers; i_peer++) {
+		const NMWireGuardPeer *peer = _peers_get (priv, i_peer)->peer;
+		GVariantBuilder builder;
+
+		if (!peer->public_key)
+			continue;
+
+		g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+		g_variant_builder_add (&builder, "{sv}", NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY, g_variant_new_string (peer->public_key));
+
+		if (   !NM_FLAGS_HAS (flags, NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		    && peer->endpoint)
+			g_variant_builder_add (&builder, "{sv}", NM_WIREGUARD_PEER_ATTR_ENDPOINT, g_variant_new_string (nm_sock_addr_endpoint_get_endpoint (peer->endpoint)));
+
+		if (   !NM_FLAGS_HAS (flags, NM_CONNECTION_SERIALIZE_NO_SECRETS)
+		    && peer->preshared_key)
+			g_variant_builder_add (&builder, "{sv}", NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, g_variant_new_string (peer->preshared_key));
+
+		if (   !NM_FLAGS_HAS (flags, NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		    && peer->preshared_key_flags != NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
+			g_variant_builder_add (&builder, "{sv}", NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS, g_variant_new_uint32 (peer->preshared_key_flags));
+
+		if (   !NM_FLAGS_HAS (flags, NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		    && peer->persistent_keepalive != 0)
+			g_variant_builder_add (&builder, "{sv}", NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE, g_variant_new_uint32 (peer->persistent_keepalive));
+
+		if (   !NM_FLAGS_HAS (flags, NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		    && peer->allowed_ips
+		    && peer->allowed_ips->len > 0) {
+			const char *const*strv = (const char *const*) peer->allowed_ips->pdata;
+			gs_free const char **strv_fixed = NULL;
+
+			for (i = 0; i < peer->allowed_ips->len; i++) {
+				if (strv[i][0] != ALLOWED_IP_INVALID_X)
+					continue;
+				if (!strv_fixed) {
+					strv_fixed = nm_memdup (strv, sizeof (strv[0]) * peer->allowed_ips->len);
+					strv = strv_fixed;
+				}
+				((const char **) strv)[i]++;
+			}
+			g_variant_builder_add (&builder, "{sv}", NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS,
+			                       g_variant_new_strv (strv, peer->allowed_ips->len));
+		}
+
+		if (!any_peers) {
+			g_variant_builder_init (&peers_builder, G_VARIANT_TYPE ("aa{sv}"));
+			any_peers = TRUE;
+		}
+		g_variant_builder_add (&peers_builder, "a{sv}", &builder);
+	}
+
+	return   any_peers
+	       ? g_variant_builder_end (&peers_builder)
+	       : NULL;
+}
+
+static gboolean
+_peers_dbus_only_set (NMSetting     *setting,
+                      GVariant      *connection_dict,
+                      const char    *property,
+                      GVariant      *value,
+                      NMSettingParseFlags parse_flags,
+                      GError       **error)
+{
+	GVariantIter iter_peers;
+	GVariant *peer_var;
+	guint i_peer;
+	gboolean success = FALSE;
+	gboolean peers_changed = FALSE;
+
+	nm_assert (g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}")));
+
+	g_variant_iter_init (&iter_peers, value);
+
+	i_peer = 0;
+	while (g_variant_iter_next (&iter_peers, "@a{sv}", &peer_var)) {
+		_nm_unused gs_unref_variant GVariant *peer_var_unref = peer_var;
+		nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL;
+		const char *cstr;
+		guint32 u32;
+		GVariant *var;
+
+		i_peer++;
+
+		if (!g_variant_lookup (peer_var, NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY, "&s", &cstr)) {
+			if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
+				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY,
+				             _("peer #%u has no public-key"),
+				             i_peer);
+				goto out;
+			}
+			continue;
+		}
+
+		peer = nm_wireguard_peer_new ();
+		if (!nm_wireguard_peer_set_public_key (peer, cstr, TRUE)) {
+			if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
+				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY,
+				             _("peer #%u has invalid public-key"),
+				             i_peer);
+				goto out;
+			}
+			continue;
+		}
+
+		if (g_variant_lookup (peer_var, NM_WIREGUARD_PEER_ATTR_ENDPOINT, "&s", &cstr)) {
+			nm_auto_unref_sockaddrendpoint NMSockAddrEndpoint *ep = NULL;
+
+			ep = nm_sock_addr_endpoint_new (cstr);
+			if (!nm_sock_addr_endpoint_get_host (ep)) {
+				if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
+					g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY,
+					             _("peer #%u has invalid endpoint"),
+					             i_peer);
+					goto out;
+				}
+			} else
+				_nm_wireguard_peer_set_endpoint (peer, ep);
+		}
+
+		if (g_variant_lookup (peer_var, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, "&s", &cstr))
+			nm_wireguard_peer_set_preshared_key (peer, cstr, TRUE);
+
+		if (g_variant_lookup (peer_var, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS, "u", &u32))
+			nm_wireguard_peer_set_preshared_key_flags (peer, u32);
+
+		if (g_variant_lookup (peer_var, NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE, "u", &u32))
+			nm_wireguard_peer_set_persistent_keepalive (peer, u32);
+
+		if (g_variant_lookup (peer_var, NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS, "@as", &var)) {
+			_nm_unused gs_unref_variant GVariant *var_free = var;
+			gs_free const char **allowed_ips = NULL;
+			gsize i, l;
+
+			allowed_ips = g_variant_get_strv (var, &l);
+			if (allowed_ips) {
+				for (i = 0; i < l; i++) {
+					if (_peer_append_allowed_ip (peer, allowed_ips[i], FALSE))
+						continue;
+					if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
+						continue;
+					g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY,
+					             _("peer #%u has invalid allowed-ips setting"),
+					             i_peer);
+					goto out;
+				}
+			}
+		}
+
+		if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
+			gs_free_error GError *local = NULL;
+
+			if (!nm_wireguard_peer_is_valid (peer, TRUE, FALSE, &local)) {
+				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_MISSING_PROPERTY,
+				             _("peer #%u is invalid: %s"),
+				             i_peer, local->message);
+				goto out;
+			}
+		}
+
+		/* we could easily reject duplicate peers (by public-key) or duplicate GVariant attributes.
+		 * However, don't do that. In case of duplicate values, the latter peer overwrite the earlier
+		 * and GVariant attributes are ignored by g_variant_lookup() above. */
+		if (_peers_append (NM_SETTING_WIREGUARD_GET_PRIVATE (setting),
+		                   peer,
+		                   TRUE))
+			peers_changed = TRUE;
+	}
+
+	success = TRUE;
+
+out:
+	if (peers_changed)
+		_peers_notify (setting);
+	return success;
+}
+
+/*****************************************************************************/
+
+static gboolean
+verify (NMSetting *setting, NMConnection *connection, GError **error)
+{
+	NMSettingWireGuard *s_wg = NM_SETTING_WIREGUARD (setting);
+	NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting);
+	guint i;
+
+	if (!_nm_connection_verify_required_interface_name (connection, error))
+		return FALSE;
+
+	if (!_nm_utils_secret_flags_validate (nm_setting_wireguard_get_private_key_flags (s_wg),
+	                                      NM_SETTING_WIREGUARD_SETTING_NAME,
+	                                      NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS,
+	                                      NM_SETTING_SECRET_FLAG_NOT_REQUIRED,
+	                                      error))
+		return FALSE;
+
+	for (i = 0; i < priv->peers_arr->len; i++) {
+		NMWireGuardPeer *peer = _peers_get (priv, i)->peer;
+
+		if (!nm_wireguard_peer_is_valid (peer, TRUE, FALSE, error)) {
+			g_prefix_error (error,
+			                "%s.%s[%u]: ",
+			                NM_SETTING_WIREGUARD_SETTING_NAME,
+			                NM_SETTING_WIREGUARD_PEERS,
+			                i);
+			return FALSE;
+		}
+	}
+
+	if (connection) {
+		NMSettingIPConfig *s_ip4;
+		NMSettingIPConfig *s_ip6;
+		const char *method;
+
+		/* WireGuard is Layer 3 only. For the moment, we only support a restricted set of
+		 * IP methods. We may relax that later, once we fix the implementations so they
+		 * actually work. */
+
+		if (   (s_ip4 = nm_connection_get_setting_ip4_config (connection))
+		    && (method = nm_setting_ip_config_get_method (s_ip4))
+		    && !NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED,
+		                              NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("method \"%s\" is not supported for WireGuard"),
+			             method);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_METHOD);
+			return FALSE;
+		}
+
+		if (   (s_ip6 = nm_connection_get_setting_ip6_config (connection))
+		    && (method = nm_setting_ip_config_get_method (s_ip6))
+		    && !NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
+		                              NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL,
+		                              NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
+			g_set_error (error,
+			             NM_CONNECTION_ERROR,
+			             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			             _("method \"%s\" is not supported for WireGuard"),
+			             method);
+			g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP_CONFIG_METHOD);
+			return FALSE;
+		}
+	}
+
+	/* private-key is a secret, hence we cannot verify it like a regular property. */
+	return TRUE;
+}
+
+static gboolean
+verify_secrets (NMSetting *setting, NMConnection *connection, GError **error)
+{
+	NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting);
+	guint i;
+
+	if (   priv->private_key
+	    && !priv->private_key_valid) {
+		g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("key must be 32 bytes base64 encoded"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIREGUARD_SETTING_NAME, NM_SETTING_WIREGUARD_PRIVATE_KEY);
+		return FALSE;
+	}
+
+	for (i = 0; i < priv->peers_arr->len; i++) {
+		NMWireGuardPeer *peer = _peers_get (priv, i)->peer;
+
+		if (!nm_wireguard_peer_is_valid (peer, FALSE, TRUE, error)) {
+			g_prefix_error (error,
+			                "%s.%s[%u]: ",
+			                NM_SETTING_WIREGUARD_SETTING_NAME,
+			                NM_SETTING_WIREGUARD_PEERS,
+			                i);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+static GPtrArray *
+need_secrets (NMSetting *setting)
+{
+	NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting);
+	GPtrArray *secrets = NULL;
+	guint i;
+
+	if (   !priv->private_key
+	    || !priv->private_key_valid) {
+		secrets = g_ptr_array_new_full (1, g_free);
+		g_ptr_array_add (secrets, g_strdup (NM_SETTING_WIREGUARD_PRIVATE_KEY));
+	}
+
+	for (i = 0; i < priv->peers_arr->len; i++) {
+		NMWireGuardPeer *peer = _peers_get (priv, i)->peer;
+
+		if (NM_FLAGS_HAS (peer->preshared_key_flags, NM_SETTING_SECRET_FLAG_NOT_REQUIRED))
+			continue;
+
+		if (peer->preshared_key_valid)
+			continue;
+
+		if (!peer->public_key_valid)
+			continue;
+
+		if (!secrets)
+			secrets = g_ptr_array_new_full (1, g_free);
+		g_ptr_array_add (secrets, peers_psk_get_secret_name_dup (peer->public_key));
+	}
+
+	return secrets;
+}
+
+static gboolean
+clear_secrets (const NMSettInfoSetting *sett_info,
+               guint property_idx,
+               NMSetting *setting,
+               NMSettingClearSecretsWithFlagsFn func,
+               gpointer user_data)
+{
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_WIREGUARD_PEERS)) {
+		NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting);
+		gboolean peers_changed = FALSE;
+		guint i, j;
+
+		j = 0;
+		for (i = 0; i < priv->peers_arr->len; i++) {
+			NMWireGuardPeer *peer = _peers_get (priv, i)->peer;
+
+			if (!peer->preshared_key)
+				continue;
+
+			if (func) {
+				gs_free char *name_free = NULL;
+				const char *name;
+
+				/* only stack-allocate (alloca) a few times. */
+				if (j++ < 5)
+					name = peers_psk_get_secret_name_a (peer->public_key, &name_free);
+				else {
+					name_free = peers_psk_get_secret_name_dup (peer->public_key);
+					name = name_free;
+				}
+
+				if (!func (setting, name, peer->preshared_key_flags, user_data))
+					continue;
+			}
+
+			{
+				nm_auto_unref_wgpeer NMWireGuardPeer *peer2 = NULL;
+
+				peer2 = nm_wireguard_peer_new_clone (peer, FALSE);
+
+				if (_peers_set (priv, peer2, i, FALSE))
+					peers_changed = TRUE;
+			}
+		}
+
+		if (peers_changed)
+			_peers_notify (setting);
+		return peers_changed;
+	}
+
+	return NM_SETTING_CLASS (nm_setting_wireguard_parent_class)->clear_secrets (sett_info,
+	                                                                            property_idx,
+	                                                                            setting,
+	                                                                            func,
+	                                                                            user_data);
+}
+
+static int
+update_one_secret (NMSetting *setting,
+                   const char *key,
+                   GVariant *value,
+                   GError **error)
+{
+	NMSettingWireGuard *self = NM_SETTING_WIREGUARD (setting);
+	NMSettingWireGuardPrivate *priv;
+	gboolean has_changes = FALSE;
+	gboolean has_error = FALSE;
+	GVariantIter iter_peers;
+	GVariant *peer_var;
+	guint i_peer;
+
+	if (!nm_streq (key, NM_SETTING_WIREGUARD_PEERS)) {
+		return NM_SETTING_CLASS (nm_setting_wireguard_parent_class)->update_one_secret (setting,
+		                                                                                key,
+		                                                                                value,
+		                                                                                error);
+	}
+
+	if (!g_variant_is_of_type (value, G_VARIANT_TYPE ("aa{sv}"))) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_PROPERTY_NOT_SECRET,
+		                     _("invalid peer secrets"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIREGUARD_SETTING_NAME, NM_SETTING_WIREGUARD_PEERS);
+		return NM_SETTING_UPDATE_SECRET_ERROR;
+	}
+
+	priv = NM_SETTING_WIREGUARD_GET_PRIVATE (self);
+
+	g_variant_iter_init (&iter_peers, value);
+
+	i_peer = 0;
+	while (g_variant_iter_next (&iter_peers, "@a{sv}", &peer_var)) {
+		_nm_unused gs_unref_variant GVariant *peer_var_unref = peer_var;
+		PeerData *pd;
+		NMWireGuardPeer *peer;
+		const char *cstr;
+
+		i_peer++;
+
+		if (!g_variant_lookup (peer_var, NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY, "&s", &cstr)) {
+			if (!has_error) {
+				g_set_error (error,
+				             NM_CONNECTION_ERROR,
+				             NM_CONNECTION_ERROR_PROPERTY_NOT_SECRET,
+				             _("peer #%u lacks public-key"),
+				             i_peer - 1);
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIREGUARD_SETTING_NAME, NM_SETTING_WIREGUARD_PEERS);
+				has_error = TRUE;
+			}
+			continue;
+		}
+
+		pd = _peers_get_by_public_key (priv, cstr, TRUE);
+		if (!pd) {
+			if (!has_error) {
+				g_set_error (error,
+				             NM_CONNECTION_ERROR,
+				             NM_CONNECTION_ERROR_PROPERTY_NOT_SECRET,
+				             _("non-existing peer '%s'"),
+				             cstr);
+				g_prefix_error (error, "%s.%s: ", NM_SETTING_WIREGUARD_SETTING_NAME, NM_SETTING_WIREGUARD_PEERS);
+				has_error = TRUE;
+			}
+			continue;
+		}
+
+		if (!g_variant_lookup (peer_var, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, "&s", &cstr)) {
+			/* no preshared-key. Ignore the rest.
+			 *
+			 * In particular, we don't reject all unknown fields. */
+			continue;
+		}
+
+		if (nm_streq0  (cstr, nm_wireguard_peer_get_preshared_key (pd->peer)))
+			continue;
+
+		peer = nm_wireguard_peer_new_clone (pd->peer, FALSE);
+		nm_wireguard_peer_set_preshared_key (peer, cstr, TRUE);
+
+		if (!_peers_set (priv, peer, pd->idx, FALSE))
+			nm_assert_not_reached ();
+		has_changes = TRUE;
+	}
+
+	if (has_error)
+		return NM_SETTING_UPDATE_SECRET_ERROR;
+	if (has_changes)
+		return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
+	return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
+}
+
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
+                  NMSetting *other,
+                  NMSettingCompareFlags flags)
+{
+	NMSettingWireGuardPrivate *a_priv;
+	NMSettingWireGuardPrivate *b_priv;
+	guint i;
+
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_WIREGUARD_PEERS)) {
+
+		if (NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+			return NM_TERNARY_DEFAULT;
+
+		if (!other)
+			return TRUE;
+
+		a_priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting);
+		b_priv = NM_SETTING_WIREGUARD_GET_PRIVATE (other);
+
+		if (a_priv->peers_arr->len != b_priv->peers_arr->len)
+			return FALSE;
+		for (i = 0; i < a_priv->peers_arr->len; i++) {
+			NMWireGuardPeer *a_peer = _peers_get (a_priv, i)->peer;
+			NMWireGuardPeer *b_peer = _peers_get (b_priv, i)->peer;
+
+			if (nm_wireguard_peer_cmp (a_peer,
+			                           b_peer,
+			                           flags) != 0)
+				return FALSE;
+		}
+
+		return TRUE;
+	}
+
+	return NM_SETTING_CLASS (nm_setting_wireguard_parent_class)->compare_property (sett_info,
+	                                                                               property_idx,
+	                                                                               setting,
+	                                                                               other,
+	                                                                               flags);
+}
+
+static void
+duplicate_copy_properties (const NMSettInfoSetting *sett_info,
+                           NMSetting *src,
+                           NMSetting *dst)
+{
+	NMSettingWireGuardPrivate *priv_src = NM_SETTING_WIREGUARD_GET_PRIVATE (src);
+	NMSettingWireGuardPrivate *priv_dst = NM_SETTING_WIREGUARD_GET_PRIVATE (dst);
+	guint i;
+	gboolean peers_changed = FALSE;
+
+	NM_SETTING_CLASS (nm_setting_wireguard_parent_class)->duplicate_copy_properties (sett_info,
+	                                                                                 src,
+	                                                                                 dst);
+
+	/* We don't bother comparing the existing peers with what we are about to set.
+	 * Always reset all. */
+	if (_peers_clear (priv_dst) > 0)
+		peers_changed = TRUE;
+	for (i = 0; i < priv_src->peers_arr->len; i++) {
+		if (_peers_append (priv_dst,
+		                   _peers_get (priv_src, i)->peer,
+		                   FALSE))
+			peers_changed = TRUE;
+	}
+	if (peers_changed)
+		_peers_notify (dst);
+}
+
+static void
+enumerate_values (const NMSettInfoProperty *property_info,
+                  NMSetting *setting,
+                  NMSettingValueIterFn func,
+                  gpointer user_data)
+{
+	if (nm_streq (property_info->name, NM_SETTING_WIREGUARD_PEERS)) {
+		NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting);
+		nm_auto_unset_gvalue GValue value = G_VALUE_INIT;
+		GPtrArray *ptr = NULL;
+		guint i;
+
+		if (priv->peers_arr && priv->peers_arr->len > 0) {
+			ptr = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_wireguard_peer_unref);
+			for (i = 0; i < priv->peers_arr->len; i++)
+				g_ptr_array_add (ptr, nm_wireguard_peer_ref (_peers_get (priv, i)->peer));
+		}
+		g_value_init (&value, G_TYPE_PTR_ARRAY);
+		g_value_take_boxed (&value, ptr);
+		func (setting,
+		      property_info->name,
+		      &value,
+		      0,
+		      user_data);
+		return;
+	}
+
+	NM_SETTING_CLASS (nm_setting_wireguard_parent_class)->enumerate_values (property_info,
+	                                                                        setting,
+	                                                                        func,
+	                                                                        user_data);
+}
+
+static gboolean
+aggregate (NMSetting *setting,
+           int type_i,
+           gpointer arg)
+{
+	NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting);
+	NMConnectionAggregateType type = type_i;
+	NMSettingSecretFlags secret_flags;
+	guint i;
+
+	nm_assert (NM_IN_SET (type, NM_CONNECTION_AGGREGATE_ANY_SECRETS,
+	                            NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS));
+
+	switch (type) {
+
+	case NM_CONNECTION_AGGREGATE_ANY_SECRETS:
+		if (priv->private_key)
+			goto out_done;
+		for (i = 0; i < priv->peers_arr->len; i++) {
+			if (nm_wireguard_peer_get_preshared_key (_peers_get (priv, i)->peer))
+				goto out_done;
+		}
+		break;
+
+	case NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS:
+#if NM_MORE_ASSERTS
+		if (!nm_setting_get_secret_flags (setting, NM_SETTING_WIREGUARD_PRIVATE_KEY, &secret_flags, NULL))
+			nm_assert_not_reached ();
+		nm_assert (secret_flags == priv->private_key_flags);
+#endif
+		if (priv->private_key_flags == NM_SETTING_SECRET_FLAG_NONE)
+			goto out_done;
+		for (i = 0; i < priv->peers_arr->len; i++) {
+			secret_flags = nm_wireguard_peer_get_preshared_key_flags (_peers_get (priv, i)->peer);
+			if (secret_flags == NM_SETTING_SECRET_FLAG_NONE)
+				goto out_done;
+		}
+		break;
+	}
+
+	return FALSE;
+
+out_done:
+	*((gboolean *) arg) = TRUE;
+	return TRUE;
+}
+
+static gboolean
+get_secret_flags (NMSetting *setting,
+                  const char *secret_name,
+                  NMSettingSecretFlags *out_flags,
+                  GError **error)
+{
+	if (NM_STR_HAS_PREFIX (secret_name, NM_SETTING_WIREGUARD_PEERS".")) {
+		NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting);
+		gs_free char *public_key_free = NULL;
+		const char *public_key;
+		PeerData *pd;
+
+		public_key = peers_psk_get_secret_parse_a (secret_name, &public_key_free);
+		if (   public_key
+		    && (pd = _peers_get_by_public_key (priv, public_key, FALSE))) {
+			NM_SET_OUT (out_flags, nm_wireguard_peer_get_preshared_key_flags (pd->peer));
+			return TRUE;
+		}
+	}
+
+	return NM_SETTING_CLASS (nm_setting_wireguard_parent_class)->get_secret_flags (setting,
+	                                                                               secret_name,
+	                                                                               out_flags,
+	                                                                               error);
+}
+
+static gboolean
+set_secret_flags (NMSetting *setting,
+                  const char *secret_name,
+                  NMSettingSecretFlags flags,
+                  GError **error)
+{
+	if (NM_STR_HAS_PREFIX (secret_name, NM_SETTING_WIREGUARD_PEERS".")) {
+		NMSettingWireGuard *self = NM_SETTING_WIREGUARD (setting);
+		NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (self);
+		gs_free char *public_key_free = NULL;
+		const char *public_key;
+		PeerData *pd;
+
+		public_key = peers_psk_get_secret_parse_a (secret_name, &public_key_free);
+		if (   public_key
+		    && (pd = _peers_get_by_public_key (priv, public_key, FALSE))) {
+
+			if (nm_wireguard_peer_get_preshared_key_flags (pd->peer) != flags) {
+				nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL;
+
+				peer = nm_wireguard_peer_new_clone (pd->peer, TRUE);
+				peer->preshared_key_flags = flags;
+				if (_peers_set (priv, peer, pd->idx, FALSE))
+					_peers_notify (self);
+			}
+
+			return TRUE;
+		}
+	}
+
+	return NM_SETTING_CLASS (nm_setting_wireguard_parent_class)->set_secret_flags (setting,
+	                                                                               secret_name,
+	                                                                               flags,
+	                                                                               error);
+}
+
+static void
+for_each_secret (NMSetting *setting,
+                 const char *data_key,
+                 GVariant *data_val,
+                 gboolean remove_non_secrets,
+                 _NMConnectionForEachSecretFunc callback,
+                 gpointer callback_data,
+                 GVariantBuilder *setting_builder)
+{
+	NMSettingWireGuard *s_wg;
+	NMSettingWireGuardPrivate *priv;
+	GVariantBuilder peers_builder;
+	GVariantIter *peer_iter;
+	GVariantIter data_iter;
+	const char *key;
+
+	if (!nm_streq (data_key, NM_SETTING_WIREGUARD_PEERS)) {
+		NM_SETTING_CLASS (nm_setting_wireguard_parent_class)->for_each_secret (setting,
+		                                                                       data_key,
+		                                                                       data_val,
+		                                                                       remove_non_secrets,
+		                                                                       callback,
+		                                                                       callback_data,
+		                                                                       setting_builder);
+		return;
+	}
+
+	if (!g_variant_is_of_type (data_val, G_VARIANT_TYPE ("aa{sv}"))) {
+		/* invalid type. Silently ignore content as we cannot find secret-keys
+		 * here. */
+		return;
+	}
+
+	s_wg = NM_SETTING_WIREGUARD (setting);
+	priv = NM_SETTING_WIREGUARD_GET_PRIVATE (s_wg);
+
+	g_variant_builder_init (&peers_builder, G_VARIANT_TYPE ("aa{sv}"));
+	g_variant_iter_init (&data_iter, data_val);
+	while (g_variant_iter_next (&data_iter, "a{sv}", &peer_iter)) {
+		_nm_unused nm_auto_free_variant_iter GVariantIter *peer_iter_free = peer_iter;
+		gs_unref_variant GVariant *preshared_key = NULL;
+		PeerData *pd = NULL;
+		NMSettingSecretFlags secret_flags;
+		GVariant *val;
+		GVariantBuilder peer_builder;
+
+		g_variant_builder_init (&peer_builder, G_VARIANT_TYPE ("a{sv}"));
+
+		while (g_variant_iter_next (peer_iter, "{&sv}", &key, &val)) {
+			_nm_unused gs_unref_variant GVariant *val_free = val;
+
+			if (nm_streq (key, NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY)) {
+				if (   !preshared_key
+				    && g_variant_is_of_type (val, G_VARIANT_TYPE_STRING))
+					preshared_key = g_variant_ref (val);
+				continue;
+			}
+
+			if (nm_streq (key, NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY)) {
+				if (   !pd
+				    && g_variant_is_of_type (val, G_VARIANT_TYPE_STRING))
+					pd = _peers_get_by_public_key (priv, g_variant_get_string (val, NULL), TRUE);
+			} else if (remove_non_secrets)
+				continue;
+
+			g_variant_builder_add (&peer_builder, "{sv}", key, val);
+		}
+
+		if (pd && preshared_key) {
+			/* without specifying a public-key of an existing peer, the secret is
+			 * ignored. */
+			secret_flags = nm_wireguard_peer_get_preshared_key_flags (pd->peer);
+			if (callback (secret_flags, callback_data))
+				g_variant_builder_add (&peer_builder, "{sv}", NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY, preshared_key);
+		}
+
+		g_variant_builder_add (&peers_builder, "a{sv}", &peer_builder);
+	}
+
+	g_variant_builder_add (setting_builder,
+	                       "{sv}",
+	                       NM_SETTING_WIREGUARD_PEERS,
+	                       g_variant_builder_end (&peers_builder));
+}
+
+/*****************************************************************************/
+
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
+{
+	NMSettingWireGuard *setting = NM_SETTING_WIREGUARD (object);
+	NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting);
+
+	switch (prop_id) {
+	case PROP_FWMARK:
+		g_value_set_uint (value, priv->fwmark);
+		break;
+	case PROP_LISTEN_PORT:
+		g_value_set_uint (value, priv->listen_port);
+		break;
+	case PROP_MTU:
+		g_value_set_uint (value, priv->mtu);
+		break;
+	case PROP_PEER_ROUTES:
+		g_value_set_boolean (value, priv->peer_routes);
+		break;
+	case PROP_PRIVATE_KEY:
+		g_value_set_string (value, priv->private_key);
+		break;
+	case PROP_PRIVATE_KEY_FLAGS:
+		g_value_set_flags (value, priv->private_key_flags);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
+{
+	NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (object);
+	const char *str;
+
+	switch (prop_id) {
+	case PROP_FWMARK:
+		priv->fwmark = g_value_get_uint (value);
+		break;
+	case PROP_LISTEN_PORT:
+		priv->listen_port = g_value_get_uint (value);
+		break;
+	case PROP_MTU:
+		priv->mtu = g_value_get_uint (value);
+		break;
+	case PROP_PEER_ROUTES:
+		priv->peer_routes = g_value_get_boolean (value);
+		break;
+	case PROP_PRIVATE_KEY:
+		nm_clear_pointer (&priv->private_key, nm_free_secret);
+		str = g_value_get_string (value);
+		if (str) {
+			if (nm_utils_base64secret_normalize (str,
+			                                     NM_WIREGUARD_PUBLIC_KEY_LEN,
+			                                     &priv->private_key))
+				priv->private_key_valid = TRUE;
+			else {
+				priv->private_key = g_strdup (str);
+				priv->private_key_valid = FALSE;
+			}
+		}
+		break;
+	case PROP_PRIVATE_KEY_FLAGS:
+		priv->private_key_flags = g_value_get_flags (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_wireguard_init (NMSettingWireGuard *setting)
+{
+	NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (setting);
+
+	priv->peers_arr = g_ptr_array_new ();
+	priv->peers_hash = g_hash_table_new (nm_pstr_hash, nm_pstr_equal);
+	priv->peer_routes = TRUE;
+}
+
+/**
+ * nm_setting_wireguard_new:
+ *
+ * Creates a new #NMSettingWireGuard object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingWireGuard object
+ *
+ * Since: 1.16
+ **/
+NMSetting *
+nm_setting_wireguard_new (void)
+{
+	return g_object_new (NM_TYPE_SETTING_WIREGUARD, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingWireGuardPrivate *priv = NM_SETTING_WIREGUARD_GET_PRIVATE (object);
+
+	nm_free_secret (priv->private_key);
+
+	_peers_clear (priv);
+	g_ptr_array_unref (priv->peers_arr);
+	g_hash_table_unref (priv->peers_hash);
+
+	G_OBJECT_CLASS (nm_setting_wireguard_parent_class)->finalize (object);
+}
+
+static void
+nm_setting_wireguard_class_init (NMSettingWireGuardClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	NMSettingClass *setting_class = NM_SETTING_CLASS (klass);
+	GArray *properties_override = _nm_sett_info_property_override_create_array ();
+
+	object_class->get_property = get_property;
+	object_class->set_property = set_property;
+	object_class->finalize     = finalize;
+
+	setting_class->verify                    = verify;
+	setting_class->verify_secrets            = verify_secrets;
+	setting_class->need_secrets              = need_secrets;
+	setting_class->clear_secrets             = clear_secrets;
+	setting_class->update_one_secret         = update_one_secret;
+	setting_class->compare_property          = compare_property;
+	setting_class->duplicate_copy_properties = duplicate_copy_properties;
+	setting_class->enumerate_values          = enumerate_values;
+	setting_class->aggregate                 = aggregate;
+	setting_class->get_secret_flags          = get_secret_flags;
+	setting_class->set_secret_flags          = set_secret_flags;
+	setting_class->for_each_secret           = for_each_secret;
+
+	/**
+	 * NMSettingWireGuard:private-key:
+	 *
+	 * The 256 bit private-key in base64 encoding.
+	 *
+	 * Since: 1.16
+	 **/
+	obj_properties[PROP_PRIVATE_KEY] =
+	    g_param_spec_string (NM_SETTING_WIREGUARD_PRIVATE_KEY, "", "",
+	                         NULL,
+	                           G_PARAM_READWRITE
+	                         | NM_SETTING_PARAM_SECRET
+	                         | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * NMSettingWireGuard:private-key-flags:
+	 *
+	 * Flags indicating how to handle the #NMSettingWirelessSecurity:private-key
+	 * property.
+	 *
+	 * Since: 1.16
+	 **/
+	obj_properties[PROP_PRIVATE_KEY_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                          G_PARAM_READWRITE
+	                        | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * NMSettingWireGuard:fwmark:
+	 *
+	 * The use of fwmark is optional and is by default off. Setting it to 0
+	 * disables it. Otherwise it is a 32-bit fwmark for outgoing packets.
+	 *
+	 * Since: 1.16
+	 **/
+	obj_properties[PROP_FWMARK] =
+	    g_param_spec_uint (NM_SETTING_WIREGUARD_FWMARK, "", "",
+	                       0, G_MAXUINT32, 0,
+	                         G_PARAM_READWRITE
+	                       | NM_SETTING_PARAM_INFERRABLE
+	                       | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * NMSettingWireGuard:listen-port:
+	 *
+	 * The listen-port. If listen-port is not specified, the port will be chosen
+	 * randomly when the interface comes up.
+	 *
+	 * Since: 1.16
+	 **/
+	obj_properties[PROP_LISTEN_PORT] =
+	    g_param_spec_uint (NM_SETTING_WIREGUARD_LISTEN_PORT, "", "",
+	                       0, 65535, 0,
+	                         G_PARAM_READWRITE
+	                       | NM_SETTING_PARAM_INFERRABLE
+	                       | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * NMSettingWireGuard:peer-routes:
+	 *
+	 * Whether to automatically add routes for the AllowedIPs ranges
+	 * of the peers. If %TRUE (the default), NetworkManager will automatically
+	 * add routes in the routing tables according to ipv4.route-table and
+	 * ipv6.route-table.
+	 * If %FALSE, no such routes are added automatically. In this case, the
+	 * user may want to configure static routes in ipv4.routes and ipv6.routes,
+	 * respectively.
+	 *
+	 * Since: 1.16
+	 **/
+	obj_properties[PROP_PEER_ROUTES] =
+	    g_param_spec_boolean (NM_SETTING_WIREGUARD_PEER_ROUTES, "", "",
+	                          TRUE,
+	                            G_PARAM_READWRITE
+	                          | NM_SETTING_PARAM_INFERRABLE
+	                          | G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * NMSettingWireGuard:mtu:
+	 *
+	 * If non-zero, only transmit packets of the specified size or smaller,
+	 * breaking larger packets up into multiple fragments.
+	 *
+	 * If zero a default MTU is used. Note that contrary to wg-quick's MTU
+	 * setting, this does not take into account the current routes at the
+	 * time of activation.
+	 *
+	 * Since: 1.16
+	 **/
+	obj_properties[PROP_MTU] =
+	    g_param_spec_uint (NM_SETTING_WIREGUARD_MTU, "", "",
+	                       0, G_MAXUINT32, 0,
+	                         G_PARAM_READWRITE
+	                       | NM_SETTING_PARAM_INFERRABLE
+	                       | G_PARAM_STATIC_STRINGS);
+
+	/* ---dbus---
+	 * property: peers
+	 * format: array of 'a{sv}'
+	 * description: Array of dictionaries for the WireGuard peers.
+	 * ---end---
+	 */
+	_properties_override_add_dbus_only (properties_override,
+	                                    NM_SETTING_WIREGUARD_PEERS,
+	                                    G_VARIANT_TYPE ("aa{sv}"),
+	                                    _peers_dbus_only_synth,
+	                                    _peers_dbus_only_set);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_WIREGUARD, NULL, properties_override);
+}
diff --git a/libnm-core/nm-setting-wireguard.h b/libnm-core/nm-setting-wireguard.h
new file mode 100644
index 00000000..017eb1f6
--- /dev/null
+++ b/libnm-core/nm-setting-wireguard.h
@@ -0,0 +1,213 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright 2018 - 2019 Red Hat, Inc.
+ */
+
+#ifndef __NM_SETTING_WIREGUARD_H__
+#define __NM_SETTING_WIREGUARD_H__
+
+#if !defined (__NETWORKMANAGER_H_INSIDE__) && !defined (NETWORKMANAGER_COMPILATION)
+#error "Only <NetworkManager.h> can be included directly."
+#endif
+
+#include "nm-setting.h"
+#include "nm-utils.h"
+
+G_BEGIN_DECLS
+
+/*****************************************************************************/
+
+#define NM_WIREGUARD_PUBLIC_KEY_LEN     32
+#define NM_WIREGUARD_SYMMETRIC_KEY_LEN  32
+
+/*****************************************************************************/
+
+typedef struct _NMWireGuardPeer NMWireGuardPeer;
+
+NM_AVAILABLE_IN_1_16
+GType nm_wireguard_peer_get_type (void);
+
+NM_AVAILABLE_IN_1_16
+NMWireGuardPeer *nm_wireguard_peer_new (void);
+
+NM_AVAILABLE_IN_1_16
+NMWireGuardPeer *nm_wireguard_peer_new_clone (const NMWireGuardPeer *self,
+                                              gboolean with_secrets);
+
+NM_AVAILABLE_IN_1_16
+NMWireGuardPeer *nm_wireguard_peer_ref (NMWireGuardPeer *self);
+NM_AVAILABLE_IN_1_16
+void nm_wireguard_peer_unref (NMWireGuardPeer *self);
+
+NM_AVAILABLE_IN_1_16
+void nm_wireguard_peer_seal (NMWireGuardPeer *self);
+NM_AVAILABLE_IN_1_16
+gboolean nm_wireguard_peer_is_sealed (const NMWireGuardPeer *self);
+
+NM_AVAILABLE_IN_1_16
+const char *nm_wireguard_peer_get_public_key (const NMWireGuardPeer *self);
+NM_AVAILABLE_IN_1_16
+gboolean nm_wireguard_peer_set_public_key (NMWireGuardPeer *self,
+                                           const char *public_key,
+                                           gboolean accept_invalid);
+
+NM_AVAILABLE_IN_1_16
+const char *nm_wireguard_peer_get_preshared_key (const NMWireGuardPeer *self);
+NM_AVAILABLE_IN_1_16
+gboolean nm_wireguard_peer_set_preshared_key (NMWireGuardPeer *self,
+                                              const char *preshared_key,
+                                              gboolean accept_invalid);
+
+NM_AVAILABLE_IN_1_16
+NMSettingSecretFlags nm_wireguard_peer_get_preshared_key_flags (const NMWireGuardPeer *self);
+NM_AVAILABLE_IN_1_16
+void nm_wireguard_peer_set_preshared_key_flags (NMWireGuardPeer *self,
+                                                NMSettingSecretFlags preshared_key_flags);
+
+NM_AVAILABLE_IN_1_16
+guint16 nm_wireguard_peer_get_persistent_keepalive (const NMWireGuardPeer *self);
+NM_AVAILABLE_IN_1_16
+void nm_wireguard_peer_set_persistent_keepalive (NMWireGuardPeer *self,
+                                                 guint16 persistent_keepalive);
+
+NM_AVAILABLE_IN_1_16
+const char *nm_wireguard_peer_get_endpoint (const NMWireGuardPeer *self);
+NM_AVAILABLE_IN_1_16
+gboolean nm_wireguard_peer_set_endpoint (NMWireGuardPeer *self,
+                                         const char *endpoint,
+                                         gboolean allow_invalid);
+
+NM_AVAILABLE_IN_1_16
+guint nm_wireguard_peer_get_allowed_ips_len (const NMWireGuardPeer *self);
+NM_AVAILABLE_IN_1_16
+const char *nm_wireguard_peer_get_allowed_ip (const NMWireGuardPeer *self,
+                                              guint idx,
+                                              gboolean *out_is_valid);
+NM_AVAILABLE_IN_1_16
+void nm_wireguard_peer_clear_allowed_ips (NMWireGuardPeer *self);
+NM_AVAILABLE_IN_1_16
+gboolean nm_wireguard_peer_append_allowed_ip (NMWireGuardPeer *self,
+                                              const char *allowed_ip,
+                                              gboolean accept_invalid);
+NM_AVAILABLE_IN_1_16
+gboolean nm_wireguard_peer_remove_allowed_ip (NMWireGuardPeer *self,
+                                              guint idx);
+
+NM_AVAILABLE_IN_1_16
+gboolean nm_wireguard_peer_is_valid (const NMWireGuardPeer *self,
+                                     gboolean check_non_secrets,
+                                     gboolean check_secrets,
+                                     GError **error);
+
+NM_AVAILABLE_IN_1_16
+int nm_wireguard_peer_cmp (const NMWireGuardPeer *a,
+                           const NMWireGuardPeer *b,
+                           NMSettingCompareFlags compare_flags);
+
+/*****************************************************************************/
+
+#define NM_TYPE_SETTING_WIREGUARD            (nm_setting_wireguard_get_type ())
+#define NM_SETTING_WIREGUARD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_WIREGUARD, NMSettingWireGuard))
+#define NM_SETTING_WIREGUARD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SETTING_WIREGUARD, NMSettingWireGuardClass))
+#define NM_IS_SETTING_WIREGUARD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SETTING_WIREGUARD))
+#define NM_IS_SETTING_WIREGUARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SETTING_WIREGUARD))
+#define NM_SETTING_WIREGUARD_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SETTING_WIREGUARD, NMSettingWireGuardClass))
+
+#define NM_SETTING_WIREGUARD_SETTING_NAME "wireguard"
+
+#define NM_SETTING_WIREGUARD_FWMARK            "fwmark"
+#define NM_SETTING_WIREGUARD_LISTEN_PORT       "listen-port"
+#define NM_SETTING_WIREGUARD_PRIVATE_KEY       "private-key"
+#define NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS "private-key-flags"
+
+#define NM_SETTING_WIREGUARD_PEERS             "peers"
+
+#define NM_SETTING_WIREGUARD_MTU               "mtu"
+#define NM_SETTING_WIREGUARD_PEER_ROUTES       "peer-routes"
+
+#define NM_WIREGUARD_PEER_ATTR_ALLOWED_IPS          "allowed-ips"
+#define NM_WIREGUARD_PEER_ATTR_ENDPOINT             "endpoint"
+#define NM_WIREGUARD_PEER_ATTR_PERSISTENT_KEEPALIVE "persistent-keepalive"
+#define NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY        "preshared-key"
+#define NM_WIREGUARD_PEER_ATTR_PRESHARED_KEY_FLAGS  "preshared-key-flags"
+#define NM_WIREGUARD_PEER_ATTR_PUBLIC_KEY           "public-key"
+
+/*****************************************************************************/
+
+typedef struct _NMSettingWireGuardClass NMSettingWireGuardClass;
+
+NM_AVAILABLE_IN_1_16
+GType nm_setting_wireguard_get_type (void);
+
+NM_AVAILABLE_IN_1_16
+NMSetting *nm_setting_wireguard_new (void);
+
+/*****************************************************************************/
+
+NM_AVAILABLE_IN_1_16
+const char *nm_setting_wireguard_get_private_key (NMSettingWireGuard *self);
+
+NM_AVAILABLE_IN_1_16
+NMSettingSecretFlags nm_setting_wireguard_get_private_key_flags (NMSettingWireGuard *self);
+
+NM_AVAILABLE_IN_1_16
+guint16 nm_setting_wireguard_get_listen_port (NMSettingWireGuard *self);
+
+NM_AVAILABLE_IN_1_16
+guint32 nm_setting_wireguard_get_fwmark (NMSettingWireGuard *self);
+
+/*****************************************************************************/
+
+NM_AVAILABLE_IN_1_16
+guint nm_setting_wireguard_get_peers_len (NMSettingWireGuard *self);
+
+NM_AVAILABLE_IN_1_16
+NMWireGuardPeer *nm_setting_wireguard_get_peer (NMSettingWireGuard *self,
+                                                guint idx);
+
+NM_AVAILABLE_IN_1_16
+NMWireGuardPeer *nm_setting_wireguard_get_peer_by_public_key (NMSettingWireGuard *self,
+                                                              const char *public_key,
+                                                              guint *out_idx);
+
+NM_AVAILABLE_IN_1_16
+void nm_setting_wireguard_set_peer (NMSettingWireGuard *self,
+                                    NMWireGuardPeer *peer,
+                                    guint idx);
+
+NM_AVAILABLE_IN_1_16
+void nm_setting_wireguard_append_peer (NMSettingWireGuard *self,
+                                       NMWireGuardPeer *peer);
+
+NM_AVAILABLE_IN_1_16
+gboolean nm_setting_wireguard_remove_peer (NMSettingWireGuard *self,
+                                           guint idx);
+
+NM_AVAILABLE_IN_1_16
+guint nm_setting_wireguard_clear_peers (NMSettingWireGuard *self);
+
+NM_AVAILABLE_IN_1_16
+gboolean nm_setting_wireguard_get_peer_routes (NMSettingWireGuard *self);
+
+NM_AVAILABLE_IN_1_16
+guint32 nm_setting_wireguard_get_mtu (NMSettingWireGuard *self);
+
+/*****************************************************************************/
+
+G_END_DECLS
+
+#endif /* __NM_SETTING_WIREGUARD_H__ */
diff --git a/libnm-core/nm-setting-wireless-security.c b/libnm-core/nm-setting-wireless-security.c
index d1527821..f689751e 100644
--- a/libnm-core/nm-setting-wireless-security.c
+++ b/libnm-core/nm-setting-wireless-security.c
@@ -22,9 +22,8 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-setting-wireless-security.h"
+
 #include "nm-setting-8021x.h"
 #include "nm-utils.h"
 #include "nm-utils-private.h"
@@ -53,9 +52,30 @@
  *       ISBN: 978-1587051548
  **/
 
-G_DEFINE_TYPE (NMSettingWirelessSecurity, nm_setting_wireless_security, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingWirelessSecurity,
+	PROP_KEY_MGMT,
+	PROP_WEP_TX_KEYIDX,
+	PROP_AUTH_ALG,
+	PROP_PROTO,
+	PROP_PAIRWISE,
+	PROP_GROUP,
+	PROP_PMF,
+	PROP_LEAP_USERNAME,
+	PROP_WEP_KEY0,
+	PROP_WEP_KEY1,
+	PROP_WEP_KEY2,
+	PROP_WEP_KEY3,
+	PROP_WEP_KEY_FLAGS,
+	PROP_WEP_KEY_TYPE,
+	PROP_PSK,
+	PROP_PSK_FLAGS,
+	PROP_LEAP_PASSWORD,
+	PROP_LEAP_PASSWORD_FLAGS,
+	PROP_WPS_METHOD,
+	PROP_FILS,
+);
 
 typedef struct {
 	char *key_mgmt;
@@ -90,44 +110,11 @@ typedef struct {
 	NMSettingWirelessSecurityFils fils;
 } NMSettingWirelessSecurityPrivate;
 
-enum {
-	PROP_0,
-	PROP_KEY_MGMT,
-	PROP_WEP_TX_KEYIDX,
-	PROP_AUTH_ALG,
-	PROP_PROTO,
-	PROP_PAIRWISE,
-	PROP_GROUP,
-	PROP_PMF,
-	PROP_LEAP_USERNAME,
-	PROP_WEP_KEY0,
-	PROP_WEP_KEY1,
-	PROP_WEP_KEY2,
-	PROP_WEP_KEY3,
-	PROP_WEP_KEY_FLAGS,
-	PROP_WEP_KEY_TYPE,
-	PROP_PSK,
-	PROP_PSK_FLAGS,
-	PROP_LEAP_PASSWORD,
-	PROP_LEAP_PASSWORD_FLAGS,
-	PROP_WPS_METHOD,
-	PROP_FILS,
+G_DEFINE_TYPE (NMSettingWirelessSecurity, nm_setting_wireless_security, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS_SECURITY, NMSettingWirelessSecurityPrivate))
 
-/**
- * nm_setting_wireless_security_new:
- *
- * Creates a new #NMSettingWirelessSecurity object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingWirelessSecurity object
- **/
-NMSetting *
-nm_setting_wireless_security_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRELESS_SECURITY, NULL);
-}
+/*****************************************************************************/
 
 /**
  * nm_setting_wireless_security_get_key_mgmt:
@@ -209,7 +196,7 @@ nm_setting_wireless_security_add_proto (NMSettingWirelessSecurity *setting, cons
 	}
 
 	priv->proto = g_slist_append (priv->proto, g_ascii_strdown (proto, -1));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO);
+	_notify (setting, PROP_PROTO);
 	return TRUE;
 }
 
@@ -234,7 +221,7 @@ nm_setting_wireless_security_remove_proto (NMSettingWirelessSecurity *setting, g
 
 	g_free (elt->data);
 	priv->proto = g_slist_delete_link (priv->proto, elt);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO);
+	_notify (setting, PROP_PROTO);
 }
 
 /**
@@ -260,7 +247,7 @@ nm_setting_wireless_security_remove_proto_by_value (NMSettingWirelessSecurity *s
 	for (iter = priv->proto; iter; iter = g_slist_next (iter)) {
 		if (strcasecmp (proto, (char *) iter->data) == 0) {
 			priv->proto = g_slist_delete_link (priv->proto, iter);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO);
+			_notify (setting, PROP_PROTO);
 			return TRUE;
 		}
 	}
@@ -284,7 +271,7 @@ nm_setting_wireless_security_clear_protos (NMSettingWirelessSecurity *setting)
 	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
 	g_slist_free_full (priv->proto, g_free);
 	priv->proto = NULL;
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PROTO);
+	_notify (setting, PROP_PROTO);
 }
 
 /**
@@ -353,7 +340,7 @@ nm_setting_wireless_security_add_pairwise (NMSettingWirelessSecurity *setting, c
 	}
 
 	priv->pairwise = g_slist_append (priv->pairwise, g_ascii_strdown (pairwise, -1));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+	_notify (setting, PROP_PAIRWISE);
 	return TRUE;
 }
 
@@ -379,7 +366,7 @@ nm_setting_wireless_security_remove_pairwise (NMSettingWirelessSecurity *setting
 
 	g_free (elt->data);
 	priv->pairwise = g_slist_delete_link (priv->pairwise, elt);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+	_notify (setting, PROP_PAIRWISE);
 }
 
 /**
@@ -390,7 +377,7 @@ nm_setting_wireless_security_remove_pairwise (NMSettingWirelessSecurity *setting
  * Removes an encryption algorithm from the allowed pairwise encryption
  * algorithm list.
  *
- * Returns: %TRUE if the encryption algorith was found and removed; %FALSE if it was not.
+ * Returns: %TRUE if the encryption algorithm was found and removed; %FALSE if it was not.
  **/
 gboolean
 nm_setting_wireless_security_remove_pairwise_by_value (NMSettingWirelessSecurity *setting,
@@ -406,7 +393,7 @@ nm_setting_wireless_security_remove_pairwise_by_value (NMSettingWirelessSecurity
 	for (iter = priv->pairwise; iter; iter = g_slist_next (iter)) {
 		if (strcasecmp (pairwise, (char *) iter->data) == 0) {
 			priv->pairwise = g_slist_delete_link (priv->pairwise, iter);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+			_notify (setting, PROP_PAIRWISE);
 			return TRUE;
 		}
 	}
@@ -430,7 +417,7 @@ nm_setting_wireless_security_clear_pairwise (NMSettingWirelessSecurity *setting)
 	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
 	g_slist_free_full (priv->pairwise, g_free);
 	priv->pairwise = NULL;
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_PAIRWISE);
+	_notify (setting, PROP_PAIRWISE);
 }
 
 /**
@@ -500,7 +487,7 @@ nm_setting_wireless_security_add_group (NMSettingWirelessSecurity *setting, cons
 	}
 
 	priv->group = g_slist_append (priv->group, g_ascii_strdown (group, -1));
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP);
+	_notify (setting, PROP_GROUP);
 	return TRUE;
 }
 
@@ -526,7 +513,7 @@ nm_setting_wireless_security_remove_group (NMSettingWirelessSecurity *setting, g
 
 	g_free (elt->data);
 	priv->group = g_slist_delete_link (priv->group, elt);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP);
+	_notify (setting, PROP_GROUP);
 }
 
 /**
@@ -554,7 +541,7 @@ nm_setting_wireless_security_remove_group_by_value (NMSettingWirelessSecurity *s
 	for (iter = priv->group; iter; iter = g_slist_next (iter)) {
 		if (strcasecmp (group, (char *) iter->data) == 0) {
 			priv->group = g_slist_delete_link (priv->group, iter);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP);
+			_notify (setting, PROP_GROUP);
 			return TRUE;
 		}
 	}
@@ -578,7 +565,7 @@ nm_setting_wireless_security_clear_groups (NMSettingWirelessSecurity *setting)
 	priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
 	g_slist_free_full (priv->group, g_free);
 	priv->group = NULL;
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_GROUP);
+	_notify (setting, PROP_GROUP);
 }
 
 /*
@@ -721,22 +708,22 @@ nm_setting_wireless_security_set_wep_key (NMSettingWirelessSecurity *setting, gu
 	case 0:
 		g_free (priv->wep_key0);
 		priv->wep_key0 = g_strdup (key);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY0);
+		_notify (setting, PROP_WEP_KEY0);
 		break;
 	case 1:
 		g_free (priv->wep_key1);
 		priv->wep_key1 = g_strdup (key);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY1);
+		_notify (setting, PROP_WEP_KEY1);
 		break;
 	case 2:
 		g_free (priv->wep_key2);
 		priv->wep_key2 = g_strdup (key);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY2);
+		_notify (setting, PROP_WEP_KEY2);
 		break;
 	case 3:
 		g_free (priv->wep_key3);
 		priv->wep_key3 = g_strdup (key);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SECURITY_WEP_KEY3);
+		_notify (setting, PROP_WEP_KEY3);
 		break;
 	default:
 		g_assert_not_reached ();
@@ -874,6 +861,15 @@ need_secrets (NMSetting *setting)
 		goto no_secrets;
 	}
 
+	/* SAE, used in MESH and WPA3-Personal */
+	if (strcmp (priv->key_mgmt, "sae") == 0) {
+		if (!priv->psk || !*priv->psk) {
+			g_ptr_array_add (secrets, NM_SETTING_WIRELESS_SECURITY_PSK);
+			return secrets;
+		}
+		goto no_secrets;
+	}
+
 	/* LEAP */
 	if (   priv->auth_alg
 	    && !strcmp (priv->auth_alg, "leap")
@@ -905,7 +901,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
 	NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (setting);
 	NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self);
-	const char *valid_key_mgmt[] = { "none", "ieee8021x", "wpa-none", "wpa-psk", "wpa-eap", NULL };
+	const char *valid_key_mgmt[] = { "none", "ieee8021x", "wpa-none", "wpa-psk", "wpa-eap", "sae", NULL };
 	const char *valid_auth_algs[] = { "open", "shared", "leap", NULL };
 	const char *valid_protos[] = { "wpa", "rsn", NULL };
 	const char *valid_pairwise[] = { "tkip", "ccmp", NULL };
@@ -1083,36 +1079,22 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 	if (   NM_IN_SET (priv->pmf,
 	                  NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL,
 	                  NM_SETTING_WIRELESS_SECURITY_PMF_REQUIRED)
-	    && !NM_IN_STRSET (priv->key_mgmt, "wpa-eap", "wpa-psk")) {
+	    && !NM_IN_STRSET (priv->key_mgmt, "wpa-eap", "wpa-psk", "sae")) {
 		g_set_error (error,
 		             NM_CONNECTION_ERROR,
 		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		             _("'%s' can only be used with '%s=%s' or '%s=%s'"),
-		             priv->pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL ? "optional" : "required",
-		             NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap",
-		             NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk");
+		             _("'%s' can only be used with 'wpa-eap', 'wpa-psk' or 'sae' key management "),
+		             priv->pmf == NM_SETTING_WIRELESS_SECURITY_PMF_OPTIONAL ? "optional" : "required");
 		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_PMF);
 		return FALSE;
 	}
 
-	/* WPS */
-	if (priv->wps_method > NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		                     _("property is invalid"));
-		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WPS_METHOD);
-		return FALSE;
-	}
-
-	if (priv->wps_method & NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED && priv->wps_method != NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
-		                     _("can't be simultaneously disabled and enabled"));
-		g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, NM_SETTING_WIRELESS_SECURITY_WPS_METHOD);
+	if (!_nm_utils_wps_method_validate (priv->wps_method,
+	                                    NM_SETTING_WIRELESS_SECURITY_SETTING_NAME,
+	                                    NM_SETTING_WIRELESS_SECURITY_WPS_METHOD,
+	                                    FALSE,
+	                                    error))
 		return FALSE;
-	}
 
 	return TRUE;
 }
@@ -1162,7 +1144,9 @@ verify_secrets (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 
 	/* WPA-PSK */
-	if (priv->psk && !nm_utils_wpa_psk_valid (priv->psk)) {
+	if (   priv->psk
+	    && strcmp (priv->key_mgmt, "sae") != 0
+	    && !nm_utils_wpa_psk_valid (priv->psk)) {
 		g_set_error_literal (error,
 		                     NM_CONNECTION_ERROR,
 		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -1177,83 +1161,56 @@ verify_secrets (NMSetting *setting, NMConnection *connection, GError **error)
 static gboolean
 get_secret_flags (NMSetting *setting,
                   const char *secret_name,
-                  gboolean verify_secret,
                   NMSettingSecretFlags *out_flags,
                   GError **error)
 {
-	NMSettingClass *setting_class;
-	gboolean verify_override = verify_secret;
-
-	/* There's only one 'flags' property for WEP keys, so alias all the WEP key
-	 * property names to that flags property.
-	 */
-	if (   !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0)
-	    || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1)
-	    || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2)
-	    || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) {
-		secret_name = "wep-key";
-		verify_override = FALSE; /* Already know it's a secret */
+	NMSettingSecretFlags flags;
+
+	if (NM_IN_STRSET (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0,
+	                               NM_SETTING_WIRELESS_SECURITY_WEP_KEY1,
+	                               NM_SETTING_WIRELESS_SECURITY_WEP_KEY2,
+	                               NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) {
+		/* There's only one 'flags' property for WEP keys, so alias all the WEP key
+		 * property names to that flags property. */
+		nm_assert (_nm_setting_property_is_regular_secret (setting, secret_name));
+		nm_assert (_nm_setting_property_is_regular_secret_flags (setting, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS));
+
+		g_object_get (G_OBJECT (setting),
+		              NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS,
+		              &flags,
+		              NULL);
+		NM_SET_OUT (out_flags, flags);
+		return TRUE;
 	}
 
-	/* Chain up to superclass with modified key name */
-	setting_class = NM_SETTING_CLASS (nm_setting_wireless_security_parent_class);
-	return setting_class->get_secret_flags (setting, secret_name, verify_override, out_flags, error);
+	return NM_SETTING_CLASS (nm_setting_wireless_security_parent_class)->get_secret_flags (setting, secret_name, out_flags, error);
 }
 
 static gboolean
 set_secret_flags (NMSetting *setting,
                   const char *secret_name,
-                  gboolean verify_secret,
                   NMSettingSecretFlags flags,
                   GError **error)
 {
-	NMSettingClass *setting_class;
-	gboolean verify_override = verify_secret;
-
-	/* There's only one 'flags' property for WEP keys, so alias all the WEP key
-	 * property names to that flags property.
-	 */
-	if (   !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0)
-	    || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY1)
-	    || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY2)
-	    || !g_strcmp0 (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) {
-		secret_name = "wep-key";
-		verify_override = FALSE; /* Already know it's a secret */
+	if (NM_IN_STRSET (secret_name, NM_SETTING_WIRELESS_SECURITY_WEP_KEY0,
+	                               NM_SETTING_WIRELESS_SECURITY_WEP_KEY1,
+	                               NM_SETTING_WIRELESS_SECURITY_WEP_KEY2,
+	                               NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)) {
+		/* There's only one 'flags' property for WEP keys, so alias all the WEP key
+		 * property names to that flags property. */
+		nm_assert (_nm_setting_property_is_regular_secret (setting, secret_name));
+		nm_assert (_nm_setting_property_is_regular_secret_flags (setting, NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS));
+
+		if (!nm_g_object_set_property_flags (G_OBJECT (setting),
+		                                     NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS,
+		                                     NM_TYPE_SETTING_SECRET_FLAGS,
+		                                     flags,
+		                                     error))
+			g_return_val_if_reached (FALSE);
+		return TRUE;
 	}
 
-	/* Chain up to superclass with modified key name */
-	setting_class = NM_SETTING_CLASS (nm_setting_wireless_security_parent_class);
-	return setting_class->set_secret_flags (setting, secret_name, verify_override, flags, error);
-}
-
-static void
-nm_setting_wireless_security_init (NMSettingWirelessSecurity *setting)
-{
-}
-
-static void
-finalize (GObject *object)
-{
-	NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (object);
-	NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self);
-
-	/* Strings first. g_free() already checks for NULLs so we don't have to */
-
-	g_free (priv->key_mgmt);
-	g_free (priv->auth_alg);
-	g_free (priv->leap_username);
-	g_free (priv->wep_key0);
-	g_free (priv->wep_key1);
-	g_free (priv->wep_key2);
-	g_free (priv->wep_key3);
-	g_free (priv->psk);
-	g_free (priv->leap_password);
-
-	g_slist_free_full (priv->proto, g_free);
-	g_slist_free_full (priv->pairwise, g_free);
-	g_slist_free_full (priv->group, g_free);
-
-	G_OBJECT_CLASS (nm_setting_wireless_security_parent_class)->finalize (object);
+	return NM_SETTING_CLASS (nm_setting_wireless_security_parent_class)->set_secret_flags (setting, secret_name, flags, error);
 }
 
 /* NMSettingWirelessSecurity:wep-key-type is an enum, but needs to be marshalled
@@ -1265,88 +1222,75 @@ wep_key_type_to_dbus (const GValue *from)
 	return g_variant_new_uint32 (g_value_get_enum (from));
 }
 
+/*****************************************************************************/
+
 static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
 	NMSettingWirelessSecurity *setting = NM_SETTING_WIRELESS_SECURITY (object);
 	NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
-	const char *str;
 
 	switch (prop_id) {
 	case PROP_KEY_MGMT:
-		g_free (priv->key_mgmt);
-		str = g_value_get_string (value);
-		priv->key_mgmt = str ? g_ascii_strdown (str, -1) : NULL;
+		g_value_set_string (value, priv->key_mgmt);
 		break;
 	case PROP_WEP_TX_KEYIDX:
-		priv->wep_tx_keyidx = g_value_get_uint (value);
+		g_value_set_uint (value, priv->wep_tx_keyidx);
 		break;
 	case PROP_AUTH_ALG:
-		g_free (priv->auth_alg);
-		str = g_value_get_string (value);
-		priv->auth_alg = str ? g_ascii_strdown (str, -1) : NULL;
+		g_value_set_string (value, priv->auth_alg);
 		break;
 	case PROP_PROTO:
-		g_slist_free_full (priv->proto, g_free);
-		priv->proto = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
+		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->proto, TRUE));
 		break;
 	case PROP_PAIRWISE:
-		g_slist_free_full (priv->pairwise, g_free);
-		priv->pairwise = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
+		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->pairwise, TRUE));
 		break;
 	case PROP_GROUP:
-		g_slist_free_full (priv->group, g_free);
-		priv->group = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
+		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->group, TRUE));
 		break;
 	case PROP_PMF:
-		priv->pmf = g_value_get_int (value);
+		g_value_set_int (value, nm_setting_wireless_security_get_pmf (setting));
 		break;
 	case PROP_LEAP_USERNAME:
-		g_free (priv->leap_username);
-		priv->leap_username = g_value_dup_string (value);
+		g_value_set_string (value, priv->leap_username);
 		break;
 	case PROP_WEP_KEY0:
-		g_free (priv->wep_key0);
-		priv->wep_key0 = g_value_dup_string (value);
+		g_value_set_string (value, priv->wep_key0);
 		break;
 	case PROP_WEP_KEY1:
-		g_free (priv->wep_key1);
-		priv->wep_key1 = g_value_dup_string (value);
+		g_value_set_string (value, priv->wep_key1);
 		break;
 	case PROP_WEP_KEY2:
-		g_free (priv->wep_key2);
-		priv->wep_key2 = g_value_dup_string (value);
+		g_value_set_string (value, priv->wep_key2);
 		break;
 	case PROP_WEP_KEY3:
-		g_free (priv->wep_key3);
-		priv->wep_key3 = g_value_dup_string (value);
+		g_value_set_string (value, priv->wep_key3);
 		break;
 	case PROP_WEP_KEY_FLAGS:
-		priv->wep_key_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->wep_key_flags);
 		break;
 	case PROP_PSK:
-		g_free (priv->psk);
-		priv->psk = g_value_dup_string (value);
+		g_value_set_string (value, priv->psk);
 		break;
 	case PROP_PSK_FLAGS:
-		priv->psk_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->psk_flags);
 		break;
 	case PROP_LEAP_PASSWORD:
-		g_free (priv->leap_password);
-		priv->leap_password = g_value_dup_string (value);
+		g_value_set_string (value, priv->leap_password);
 		break;
 	case PROP_LEAP_PASSWORD_FLAGS:
-		priv->leap_password_flags = g_value_get_flags (value);
+		g_value_set_flags (value, priv->leap_password_flags);
 		break;
 	case PROP_WEP_KEY_TYPE:
-		priv->wep_key_type = g_value_get_enum (value);
+		g_value_set_enum (value, priv->wep_key_type);
 		break;
 	case PROP_WPS_METHOD:
-		priv->wps_method = g_value_get_uint (value);
+		g_value_set_uint (value, priv->wps_method);
 		break;
 	case PROP_FILS:
-		priv->fils = g_value_get_int (value);
+		g_value_set_int (value, nm_setting_wireless_security_get_fils (setting));
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1355,72 +1299,87 @@ set_property (GObject *object, guint prop_id,
 }
 
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
 	NMSettingWirelessSecurity *setting = NM_SETTING_WIRELESS_SECURITY (object);
 	NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (setting);
+	const char *str;
 
 	switch (prop_id) {
 	case PROP_KEY_MGMT:
-		g_value_set_string (value, priv->key_mgmt);
+		g_free (priv->key_mgmt);
+		str = g_value_get_string (value);
+		priv->key_mgmt = str ? g_ascii_strdown (str, -1) : NULL;
 		break;
 	case PROP_WEP_TX_KEYIDX:
-		g_value_set_uint (value, priv->wep_tx_keyidx);
+		priv->wep_tx_keyidx = g_value_get_uint (value);
 		break;
 	case PROP_AUTH_ALG:
-		g_value_set_string (value, priv->auth_alg);
+		g_free (priv->auth_alg);
+		str = g_value_get_string (value);
+		priv->auth_alg = str ? g_ascii_strdown (str, -1) : NULL;
 		break;
 	case PROP_PROTO:
-		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->proto, TRUE));
+		g_slist_free_full (priv->proto, g_free);
+		priv->proto = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
 		break;
 	case PROP_PAIRWISE:
-		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->pairwise, TRUE));
+		g_slist_free_full (priv->pairwise, g_free);
+		priv->pairwise = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
 		break;
 	case PROP_GROUP:
-		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->group, TRUE));
+		g_slist_free_full (priv->group, g_free);
+		priv->group = _nm_utils_strv_to_slist (g_value_get_boxed (value), TRUE);
 		break;
 	case PROP_PMF:
-		g_value_set_int (value, nm_setting_wireless_security_get_pmf (setting));
+		priv->pmf = g_value_get_int (value);
 		break;
 	case PROP_LEAP_USERNAME:
-		g_value_set_string (value, priv->leap_username);
+		g_free (priv->leap_username);
+		priv->leap_username = g_value_dup_string (value);
 		break;
 	case PROP_WEP_KEY0:
-		g_value_set_string (value, priv->wep_key0);
+		g_free (priv->wep_key0);
+		priv->wep_key0 = g_value_dup_string (value);
 		break;
 	case PROP_WEP_KEY1:
-		g_value_set_string (value, priv->wep_key1);
+		g_free (priv->wep_key1);
+		priv->wep_key1 = g_value_dup_string (value);
 		break;
 	case PROP_WEP_KEY2:
-		g_value_set_string (value, priv->wep_key2);
+		g_free (priv->wep_key2);
+		priv->wep_key2 = g_value_dup_string (value);
 		break;
 	case PROP_WEP_KEY3:
-		g_value_set_string (value, priv->wep_key3);
+		g_free (priv->wep_key3);
+		priv->wep_key3 = g_value_dup_string (value);
 		break;
 	case PROP_WEP_KEY_FLAGS:
-		g_value_set_flags (value, priv->wep_key_flags);
+		priv->wep_key_flags = g_value_get_flags (value);
 		break;
 	case PROP_PSK:
-		g_value_set_string (value, priv->psk);
+		g_free (priv->psk);
+		priv->psk = g_value_dup_string (value);
 		break;
 	case PROP_PSK_FLAGS:
-		g_value_set_flags (value, priv->psk_flags);
+		priv->psk_flags = g_value_get_flags (value);
 		break;
 	case PROP_LEAP_PASSWORD:
-		g_value_set_string (value, priv->leap_password);
+		g_free (priv->leap_password);
+		priv->leap_password = g_value_dup_string (value);
 		break;
 	case PROP_LEAP_PASSWORD_FLAGS:
-		g_value_set_flags (value, priv->leap_password_flags);
+		priv->leap_password_flags = g_value_get_flags (value);
 		break;
 	case PROP_WEP_KEY_TYPE:
-		g_value_set_enum (value, priv->wep_key_type);
+		priv->wep_key_type = g_value_get_enum (value);
 		break;
 	case PROP_WPS_METHOD:
-		g_value_set_uint (value, priv->wps_method);
+		priv->wps_method = g_value_get_uint (value);
 		break;
 	case PROP_FILS:
-		g_value_set_int (value, nm_setting_wireless_security_get_fils (setting));
+		priv->fils = g_value_get_int (value);
 		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1428,6 +1387,49 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_wireless_security_init (NMSettingWirelessSecurity *setting)
+{
+}
+
+/**
+ * nm_setting_wireless_security_new:
+ *
+ * Creates a new #NMSettingWirelessSecurity object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingWirelessSecurity object
+ **/
+NMSetting *
+nm_setting_wireless_security_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRELESS_SECURITY, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+	NMSettingWirelessSecurity *self = NM_SETTING_WIRELESS_SECURITY (object);
+	NMSettingWirelessSecurityPrivate *priv = NM_SETTING_WIRELESS_SECURITY_GET_PRIVATE (self);
+
+	g_free (priv->key_mgmt);
+	g_free (priv->auth_alg);
+	g_free (priv->leap_username);
+	g_free (priv->wep_key0);
+	g_free (priv->wep_key1);
+	g_free (priv->wep_key2);
+	g_free (priv->wep_key3);
+	g_free (priv->psk);
+	g_free (priv->leap_password);
+
+	g_slist_free_full (priv->proto, g_free);
+	g_slist_free_full (priv->pairwise, g_free);
+	g_slist_free_full (priv->group, g_free);
+
+	G_OBJECT_CLASS (nm_setting_wireless_security_parent_class)->finalize (object);
+}
+
 static void
 nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 {
@@ -1437,8 +1439,8 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 
 	g_type_class_add_private (klass, sizeof (NMSettingWirelessSecurityPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify           = verify;
@@ -1452,8 +1454,8 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 *
 	 * Key management used for the connection.  One of "none" (WEP), "ieee8021x"
 	 * (Dynamic WEP), "wpa-none" (Ad-Hoc WPA-PSK), "wpa-psk" (infrastructure
-	 * WPA-PSK), or "wpa-eap" (WPA-Enterprise).  This property must be set for
-	 * any Wi-Fi connection that uses security.
+	 * WPA-PSK), "sae" (SAE) or "wpa-eap" (WPA-Enterprise).
+	 * This property must be set for any Wi-Fi connection that uses security.
 	 **/
 	/* ---ifcfg-rh---
 	 * property: key-mgmt
@@ -1462,13 +1464,12 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: Key management menthod.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_KEY_MGMT,
-		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_REQUIRED |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_KEY_MGMT] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_REQUIRED |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:wep-tx-keyidx:
@@ -1486,13 +1487,12 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: Index of active WEP key.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_WEP_TX_KEYIDX,
-		 g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, "", "",
-		                    0, 3, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_WEP_TX_KEYIDX] =
+	    g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, "", "",
+	                       0, 3, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:auth-alg:
@@ -1510,12 +1510,11 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: Authentication algorithm for WEP.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_AUTH_ALG,
-		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_AUTH_ALG] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:proto:
@@ -1532,12 +1531,11 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: Allowed WPA protocols, WPA and WPA2 (RSN).
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PROTO,
-		 g_param_spec_boxed (NM_SETTING_WIRELESS_SECURITY_PROTO, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PROTO] =
+	    g_param_spec_boxed (NM_SETTING_WIRELESS_SECURITY_PROTO, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:pairwise:
@@ -1555,12 +1553,11 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 *   separated list.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PAIRWISE,
-		 g_param_spec_boxed (NM_SETTING_WIRELESS_SECURITY_PAIRWISE, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PAIRWISE] =
+	    g_param_spec_boxed (NM_SETTING_WIRELESS_SECURITY_PAIRWISE, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:group:
@@ -1578,12 +1575,11 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 *   separated list.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_GROUP,
-		 g_param_spec_boxed (NM_SETTING_WIRELESS_SECURITY_GROUP, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_GROUP] =
+	    g_param_spec_boxed (NM_SETTING_WIRELESS_SECURITY_GROUP, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:pmf:
@@ -1607,14 +1603,13 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * example: PMF=required
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PMF,
-		 g_param_spec_int (NM_SETTING_WIRELESS_SECURITY_PMF, "", "",
-		                   G_MININT32, G_MAXINT32, 0,
-		                   G_PARAM_READWRITE |
-		                   G_PARAM_CONSTRUCT |
-		                   NM_SETTING_PARAM_FUZZY_IGNORE |
-		                   G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PMF] =
+	    g_param_spec_int (NM_SETTING_WIRELESS_SECURITY_PMF, "", "",
+	                      G_MININT32, G_MAXINT32, 0,
+	                      G_PARAM_READWRITE |
+	                      G_PARAM_CONSTRUCT |
+	                      NM_SETTING_PARAM_FUZZY_IGNORE |
+	                      G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:leap-username:
@@ -1628,12 +1623,11 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: Login name for LEAP.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_LEAP_USERNAME,
-		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LEAP_USERNAME] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:wep-key0:
@@ -1647,13 +1641,12 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: The first WEP key (used in most networks). See also DEFAULTKEY for key index.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_WEP_KEY0,
-		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_WEP_KEY0] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:wep-key1:
@@ -1667,13 +1660,12 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: WEP key with index 1. See also DEFAULTKEY for key index.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_WEP_KEY1,
-		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_WEP_KEY1] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:wep-key2:
@@ -1687,13 +1679,12 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: WEP key with index 2. See also DEFAULTKEY for key index.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_WEP_KEY2,
-		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_WEP_KEY2] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:wep-key3:
@@ -1707,13 +1698,12 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: WEP key with index 3. See also DEFAULTKEY for key index.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_WEP_KEY3,
-		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_WEP_KEY3] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:wep-key-flags:
@@ -1729,23 +1719,21 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: Password flags for KEY<i>, KEY_PASSPHRASE<i> password.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_WEP_KEY_FLAGS,
-		 g_param_spec_flags (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_WEP_KEY_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:psk:
 	 *
-	 * Pre-Shared-Key for WPA networks.  If the key is 64-characters long, it
-	 * must contain only hexadecimal characters and is interpreted as a
-	 * hexadecimal WPA key.  Otherwise, the key must be between 8 and 63 ASCII
-	 * characters (as specified in the 802.11i standard) and is interpreted as a
-	 * WPA passphrase, and is hashed to derive the actual WPA-PSK used when
-	 * connecting to the Wi-Fi network.
+	 * Pre-Shared-Key for WPA networks. For WPA-PSK, it's either an ASCII
+	 * passphrase of 8 to 63 characters that is (as specified in the 802.11i
+	 * standard) hashed to derive the actual key, or the key in form of 64
+	 * hexadecimal character. The WPA3-Personal networks use a passphrase
+	 * of any length for SAE authentication.
 	 **/
 	/* ---ifcfg-rh---
 	 * property: psk
@@ -1753,13 +1741,12 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: Pre-Shared-Key for WPA networks.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PSK,
-		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_PSK, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PSK] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_PSK, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:psk-flags:
@@ -1775,13 +1762,12 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * example: WPA_PSK_FLAGS=user
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_PSK_FLAGS,
-		 g_param_spec_flags (NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PSK_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:leap-password:
@@ -1796,13 +1782,12 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 *  lookaside file, or it can be owned by a secret agent.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_LEAP_PASSWORD,
-		 g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      NM_SETTING_PARAM_SECRET |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LEAP_PASSWORD] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_SECRET |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:leap-password-flags:
@@ -1817,13 +1802,12 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * description: Password flags for IEEE_8021X_PASSWORD_FLAGS.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_LEAP_PASSWORD_FLAGS,
-		 g_param_spec_flags (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, "", "",
-		                     NM_TYPE_SETTING_SECRET_FLAGS,
-		                     NM_SETTING_SECRET_FLAG_NONE,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_LEAP_PASSWORD_FLAGS] =
+	    g_param_spec_flags (NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, "", "",
+	                        NM_TYPE_SETTING_SECRET_FLAGS,
+	                        NM_SETTING_SECRET_FLAG_NONE,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:wep-key-type:
@@ -1845,18 +1829,16 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * example: KEY1=s:ahoj, KEY1=0a1c45bc02, KEY_PASSPHRASE1=mysupersecretkey
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_WEP_KEY_TYPE,
-		 g_param_spec_enum (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, "", "",
-		                    NM_TYPE_WEP_KEY_TYPE,
-		                    NM_WEP_KEY_TYPE_UNKNOWN,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_WEP_KEY_TYPE] =
+	    g_param_spec_enum (NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, "", "",
+	                       NM_TYPE_WEP_KEY_TYPE,
+	                       NM_WEP_KEY_TYPE_UNKNOWN,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE),
+	                                    obj_properties[PROP_WEP_KEY_TYPE],
 	                                    G_VARIANT_TYPE_UINT32,
 	                                    wep_key_type_to_dbus,
 	                                    NULL);
@@ -1882,14 +1864,13 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * example: WPS_METHOD=disabled, WPS_METHOD="pin pbc"
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_WPS_METHOD,
-		 g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WPS_METHOD, "", "",
-		                    0, G_MAXUINT32, NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_WPS_METHOD] =
+	    g_param_spec_uint (NM_SETTING_WIRELESS_SECURITY_WPS_METHOD, "", "",
+	                       0, G_MAXUINT32, NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWirelessSecurity:fils:
@@ -1913,14 +1894,15 @@ nm_setting_wireless_security_class_init (NMSettingWirelessSecurityClass *klass)
 	 * 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));
+	obj_properties[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);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_WIRELESS_SECURITY,
 	                               NULL, properties_override);
diff --git a/libnm-core/nm-setting-wireless-security.h b/libnm-core/nm-setting-wireless-security.h
index 5a732ac0..c560f348 100644
--- a/libnm-core/nm-setting-wireless-security.h
+++ b/libnm-core/nm-setting-wireless-security.h
@@ -54,9 +54,9 @@ G_BEGIN_DECLS
  * @NM_WEP_KEY_TYPE_LAST: placeholder value for bounds-checking
  *
  * The #NMWepKeyType values specify how any WEP keys present in the setting
- * are intepreted.  There are no standards governing how to hash the various WEP
+ * are interpreted.  There are no standards governing how to hash the various WEP
  * key/passphrase formats into the actual WEP key.  Unfortunately some WEP keys
- * can be interpreted in multiple ways, requring the setting to specify how to
+ * can be interpreted in multiple ways, requiring the setting to specify how to
  * interpret the any WEP keys.  For example, the key "732f2d712e4a394a375d366931"
  * is both a valid Hexadecimal WEP key and a WEP passphrase.  Further, many
  * ASCII keys are also valid WEP passphrases, but since passphrases and ASCII
@@ -99,9 +99,12 @@ typedef enum {
  *
  * Configure the use of WPS by a connection while it activates.
  *
+ * Note: prior to 1.16, this was a GEnum type instead of a GFlags type
+ * although, with the same numeric values.
+ *
  * Since: 1.10
  **/
-typedef enum {
+typedef enum { /*< flags >*/
 	NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT     = 0x00000000,
 	NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED    = 0x00000001,
 	NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_AUTO        = 0x00000002,
diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c
index 6f6175b7..b4cb1056 100644
--- a/libnm-core/nm-setting-wireless.c
+++ b/libnm-core/nm-setting-wireless.c
@@ -24,7 +24,6 @@
 
 #include "nm-setting-wireless.h"
 
-#include <string.h>
 #include <net/ethernet.h>
 
 #include "nm-utils.h"
@@ -40,9 +39,27 @@
  * necessary for connection to 802.11 Wi-Fi networks.
  **/
 
-G_DEFINE_TYPE (NMSettingWireless, nm_setting_wireless, NM_TYPE_SETTING)
+/*****************************************************************************/
 
-#define NM_SETTING_WIRELESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessPrivate))
+NM_GOBJECT_PROPERTIES_DEFINE (NMSettingWireless,
+	PROP_SSID,
+	PROP_MODE,
+	PROP_BAND,
+	PROP_CHANNEL,
+	PROP_BSSID,
+	PROP_RATE,
+	PROP_TX_POWER,
+	PROP_MAC_ADDRESS,
+	PROP_CLONED_MAC_ADDRESS,
+	PROP_GENERATE_MAC_ADDRESS_MASK,
+	PROP_MAC_ADDRESS_BLACKLIST,
+	PROP_MTU,
+	PROP_SEEN_BSSIDS,
+	PROP_HIDDEN,
+	PROP_POWERSAVE,
+	PROP_MAC_ADDRESS_RANDOMIZATION,
+	PROP_WAKE_ON_WLAN,
+);
 
 typedef struct {
 	GBytes *ssid;
@@ -64,28 +81,11 @@ typedef struct {
 	guint32 wowl;
 } NMSettingWirelessPrivate;
 
-enum {
-	PROP_0,
-	PROP_SSID,
-	PROP_MODE,
-	PROP_BAND,
-	PROP_CHANNEL,
-	PROP_BSSID,
-	PROP_RATE,
-	PROP_TX_POWER,
-	PROP_MAC_ADDRESS,
-	PROP_CLONED_MAC_ADDRESS,
-	PROP_GENERATE_MAC_ADDRESS_MASK,
-	PROP_MAC_ADDRESS_BLACKLIST,
-	PROP_MTU,
-	PROP_SEEN_BSSIDS,
-	PROP_HIDDEN,
-	PROP_POWERSAVE,
-	PROP_MAC_ADDRESS_RANDOMIZATION,
-	PROP_WAKE_ON_WLAN,
+G_DEFINE_TYPE (NMSettingWireless, nm_setting_wireless, NM_TYPE_SETTING)
 
-	LAST_PROP
-};
+#define NM_SETTING_WIRELESS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WIRELESS, NMSettingWirelessPrivate))
+
+/*****************************************************************************/
 
 static gboolean
 match_cipher (const char *cipher,
@@ -285,19 +285,6 @@ nm_setting_wireless_ap_security_compatible (NMSettingWireless *s_wireless,
 }
 
 /**
- * nm_setting_wireless_new:
- *
- * Creates a new #NMSettingWireless object with default values.
- *
- * Returns: (transfer full): the new empty #NMSettingWireless object
- **/
-NMSetting *
-nm_setting_wireless_new (void)
-{
-	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRELESS, NULL);
-}
-
-/**
  * nm_setting_wireless_get_ssid:
  * @setting: the #NMSettingWireless
  *
@@ -523,7 +510,7 @@ nm_setting_wireless_add_mac_blacklist_item (NMSettingWireless *setting, const ch
 
 	mac = nm_utils_hwaddr_canonical (mac, ETH_ALEN);
 	g_array_append_val (priv->mac_address_blacklist, mac);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
+	_notify (setting, PROP_MAC_ADDRESS_BLACKLIST);
 	return TRUE;
 }
 
@@ -545,7 +532,7 @@ nm_setting_wireless_remove_mac_blacklist_item (NMSettingWireless *setting, guint
 	g_return_if_fail (idx < priv->mac_address_blacklist->len);
 
 	g_array_remove_index (priv->mac_address_blacklist, idx);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
+	_notify (setting, PROP_MAC_ADDRESS_BLACKLIST);
 }
 
 /**
@@ -573,7 +560,7 @@ nm_setting_wireless_remove_mac_blacklist_item_by_value (NMSettingWireless *setti
 		candidate = g_array_index (priv->mac_address_blacklist, char *, i);
 		if (!nm_utils_hwaddr_matches (mac, -1, candidate, -1)) {
 			g_array_remove_index (priv->mac_address_blacklist, i);
-			g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
+			_notify (setting, PROP_MAC_ADDRESS_BLACKLIST);
 			return TRUE;
 		}
 	}
@@ -592,7 +579,7 @@ nm_setting_wireless_clear_mac_blacklist_items (NMSettingWireless *setting)
 	g_return_if_fail (NM_IS_SETTING_WIRELESS (setting));
 
 	g_array_set_size (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->mac_address_blacklist, 0);
-	g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST);
+	_notify (setting, PROP_MAC_ADDRESS_BLACKLIST);
 }
 
 /**
@@ -695,7 +682,7 @@ nm_setting_wireless_add_seen_bssid (NMSettingWireless *setting,
 
 	if (!found) {
 		priv->seen_bssids = g_slist_prepend (priv->seen_bssids, lower_bssid);
-		g_object_notify (G_OBJECT (setting), NM_SETTING_WIRELESS_SEEN_BSSIDS);
+		_notify (setting, PROP_SEEN_BSSIDS);
 	} else
 		g_free (lower_bssid);
 
@@ -929,34 +916,45 @@ mac_addr_rand_ok:
 	return TRUE;
 }
 
-static gboolean
-compare_property (NMSetting *setting,
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
                   NMSetting *other,
-                  const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	NMSettingClass *setting_class;
-
-	if (nm_streq (prop_spec->name, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS)) {
-		return nm_streq0 (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->cloned_mac_address,
-		                  NM_SETTING_WIRELESS_GET_PRIVATE (other)->cloned_mac_address);
+	if (nm_streq (sett_info->property_infos[property_idx].name, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS)) {
+		return    !other
+		       || nm_streq0 (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->cloned_mac_address,
+		                     NM_SETTING_WIRELESS_GET_PRIVATE (other)->cloned_mac_address);
 	}
 
-	setting_class = NM_SETTING_CLASS (nm_setting_wireless_parent_class);
-	return setting_class->compare_property (setting, other, prop_spec, flags);
+	return NM_SETTING_CLASS (nm_setting_wireless_parent_class)->compare_property (sett_info,
+	                                                                              property_idx,
+	                                                                              setting,
+	                                                                              other,
+	                                                                              flags);
 }
 
 /*****************************************************************************/
 
 static GVariant *
-nm_setting_wireless_get_security (NMSetting    *setting,
+nm_setting_wireless_get_security (const NMSettInfoSetting *sett_info,
+                                  guint property_idx,
                                   NMConnection *connection,
-                                  const char   *property_name)
+                                  NMSetting *setting,
+                                  NMConnectionSerializationFlags flags)
 {
-	if (nm_connection_get_setting_wireless_security (connection))
-		return g_variant_new_string (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
-	else
+	if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		return NULL;
+
+	if (!connection)
 		return NULL;
+
+	if (!nm_connection_get_setting_wireless_security (connection))
+		return NULL;
+
+	return g_variant_new_string (NM_SETTING_WIRELESS_SECURITY_SETTING_NAME);
 }
 
 /**
@@ -983,34 +981,71 @@ clear_blacklist_item (char **item_p)
 	g_free (*item_p);
 }
 
-static void
-nm_setting_wireless_init (NMSettingWireless *setting)
-{
-	NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting);
-
-	/* We use GArray rather than GPtrArray so it will automatically be NULL-terminated */
-	priv->mac_address_blacklist = g_array_new (TRUE, FALSE, sizeof (char *));
-	g_array_set_clear_func (priv->mac_address_blacklist, (GDestroyNotify) clear_blacklist_item);
-}
+/*****************************************************************************/
 
 static void
-finalize (GObject *object)
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
+	NMSettingWireless *setting = NM_SETTING_WIRELESS (object);
 	NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (object);
 
-	g_free (priv->mode);
-	g_free (priv->band);
-
-	if (priv->ssid)
-		g_bytes_unref (priv->ssid);
-	g_free (priv->bssid);
-	g_free (priv->device_mac_address);
-	g_free (priv->cloned_mac_address);
-	g_free (priv->generate_mac_address_mask);
-	g_array_unref (priv->mac_address_blacklist);
-	g_slist_free_full (priv->seen_bssids, g_free);
-
-	G_OBJECT_CLASS (nm_setting_wireless_parent_class)->finalize (object);
+	switch (prop_id) {
+	case PROP_SSID:
+		g_value_set_boxed (value, nm_setting_wireless_get_ssid (setting));
+		break;
+	case PROP_MODE:
+		g_value_set_string (value, nm_setting_wireless_get_mode (setting));
+		break;
+	case PROP_BAND:
+		g_value_set_string (value, nm_setting_wireless_get_band (setting));
+		break;
+	case PROP_CHANNEL:
+		g_value_set_uint (value, nm_setting_wireless_get_channel (setting));
+		break;
+	case PROP_BSSID:
+		g_value_set_string (value, nm_setting_wireless_get_bssid (setting));
+		break;
+	case PROP_RATE:
+		g_value_set_uint (value, nm_setting_wireless_get_rate (setting));
+		break;
+	case PROP_TX_POWER:
+		g_value_set_uint (value, nm_setting_wireless_get_tx_power (setting));
+		break;
+	case PROP_MAC_ADDRESS:
+		g_value_set_string (value, nm_setting_wireless_get_mac_address (setting));
+		break;
+	case PROP_CLONED_MAC_ADDRESS:
+		g_value_set_string (value, nm_setting_wireless_get_cloned_mac_address (setting));
+		break;
+	case PROP_GENERATE_MAC_ADDRESS_MASK:
+		g_value_set_string (value, nm_setting_wireless_get_generate_mac_address_mask (setting));
+		break;
+	case PROP_MAC_ADDRESS_BLACKLIST:
+		g_value_set_boxed (value, (char **) priv->mac_address_blacklist->data);
+		break;
+	case PROP_MTU:
+		g_value_set_uint (value, nm_setting_wireless_get_mtu (setting));
+		break;
+	case PROP_SEEN_BSSIDS:
+		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->seen_bssids, TRUE));
+		break;
+	case PROP_HIDDEN:
+		g_value_set_boolean (value, nm_setting_wireless_get_hidden (setting));
+		break;
+	case PROP_POWERSAVE:
+		g_value_set_uint (value, nm_setting_wireless_get_powersave (setting));
+		break;
+	case PROP_MAC_ADDRESS_RANDOMIZATION:
+		g_value_set_uint (value, nm_setting_wireless_get_mac_address_randomization (setting));
+		break;
+	case PROP_WAKE_ON_WLAN:
+		g_value_set_uint (value, nm_setting_wireless_get_wake_on_wlan (setting));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 static void
@@ -1065,7 +1100,7 @@ set_property (GObject *object, guint prop_id,
 			 * In this case, we also clear mac-address-randomization flag */
 			if (priv->mac_address_randomization != NM_SETTING_MAC_RANDOMIZATION_DEFAULT) {
 				priv->mac_address_randomization = NM_SETTING_MAC_RANDOMIZATION_DEFAULT;
-				g_object_notify (object, NM_SETTING_WIRELESS_MAC_ADDRESS);
+				_notify (NM_SETTING_WIRELESS (object), PROP_MAC_ADDRESS_RANDOMIZATION);
 			}
 		}
 		break;
@@ -1108,69 +1143,49 @@ set_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
 static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+nm_setting_wireless_init (NMSettingWireless *setting)
+{
+	NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (setting);
+
+	/* We use GArray rather than GPtrArray so it will automatically be NULL-terminated */
+	priv->mac_address_blacklist = g_array_new (TRUE, FALSE, sizeof (char *));
+	g_array_set_clear_func (priv->mac_address_blacklist, (GDestroyNotify) clear_blacklist_item);
+}
+
+/**
+ * nm_setting_wireless_new:
+ *
+ * Creates a new #NMSettingWireless object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingWireless object
+ **/
+NMSetting *
+nm_setting_wireless_new (void)
+{
+	return (NMSetting *) g_object_new (NM_TYPE_SETTING_WIRELESS, NULL);
+}
+
+static void
+finalize (GObject *object)
 {
-	NMSettingWireless *setting = NM_SETTING_WIRELESS (object);
 	NMSettingWirelessPrivate *priv = NM_SETTING_WIRELESS_GET_PRIVATE (object);
 
-	switch (prop_id) {
-	case PROP_SSID:
-		g_value_set_boxed (value, nm_setting_wireless_get_ssid (setting));
-		break;
-	case PROP_MODE:
-		g_value_set_string (value, nm_setting_wireless_get_mode (setting));
-		break;
-	case PROP_BAND:
-		g_value_set_string (value, nm_setting_wireless_get_band (setting));
-		break;
-	case PROP_CHANNEL:
-		g_value_set_uint (value, nm_setting_wireless_get_channel (setting));
-		break;
-	case PROP_BSSID:
-		g_value_set_string (value, nm_setting_wireless_get_bssid (setting));
-		break;
-	case PROP_RATE:
-		g_value_set_uint (value, nm_setting_wireless_get_rate (setting));
-		break;
-	case PROP_TX_POWER:
-		g_value_set_uint (value, nm_setting_wireless_get_tx_power (setting));
-		break;
-	case PROP_MAC_ADDRESS:
-		g_value_set_string (value, nm_setting_wireless_get_mac_address (setting));
-		break;
-	case PROP_CLONED_MAC_ADDRESS:
-		g_value_set_string (value, nm_setting_wireless_get_cloned_mac_address (setting));
-		break;
-	case PROP_GENERATE_MAC_ADDRESS_MASK:
-		g_value_set_string (value, nm_setting_wireless_get_generate_mac_address_mask (setting));
-		break;
-	case PROP_MAC_ADDRESS_BLACKLIST:
-		g_value_set_boxed (value, (char **) priv->mac_address_blacklist->data);
-		break;
-	case PROP_MTU:
-		g_value_set_uint (value, nm_setting_wireless_get_mtu (setting));
-		break;
-	case PROP_SEEN_BSSIDS:
-		g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->seen_bssids, TRUE));
-		break;
-	case PROP_HIDDEN:
-		g_value_set_boolean (value, nm_setting_wireless_get_hidden (setting));
-		break;
-	case PROP_POWERSAVE:
-		g_value_set_uint (value, nm_setting_wireless_get_powersave (setting));
-		break;
-	case PROP_MAC_ADDRESS_RANDOMIZATION:
-		g_value_set_uint (value, nm_setting_wireless_get_mac_address_randomization (setting));
-		break;
-	case PROP_WAKE_ON_WLAN:
-		g_value_set_uint (value, nm_setting_wireless_get_wake_on_wlan (setting));
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	g_free (priv->mode);
+	g_free (priv->band);
+
+	if (priv->ssid)
+		g_bytes_unref (priv->ssid);
+	g_free (priv->bssid);
+	g_free (priv->device_mac_address);
+	g_free (priv->cloned_mac_address);
+	g_free (priv->generate_mac_address_mask);
+	g_array_unref (priv->mac_address_blacklist);
+	g_slist_free_full (priv->seen_bssids, g_free);
+
+	G_OBJECT_CLASS (nm_setting_wireless_parent_class)->finalize (object);
 }
 
 static void
@@ -1207,12 +1222,11 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * example: ESSID="Quick Net"
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_SSID,
-		 g_param_spec_boxed (NM_SETTING_WIRELESS_SSID, "", "",
-		                     G_TYPE_BYTES,
-		                     G_PARAM_READWRITE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SSID] =
+	    g_param_spec_boxed (NM_SETTING_WIRELESS_SSID, "", "",
+	                        G_TYPE_BYTES,
+	                        G_PARAM_READWRITE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:mode:
@@ -1227,12 +1241,11 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * description: Wi-Fi network mode.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MODE,
-		 g_param_spec_string (NM_SETTING_WIRELESS_MODE, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MODE] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_MODE, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:band:
@@ -1253,12 +1266,11 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * example: BAND=bg
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_BAND,
-		 g_param_spec_string (NM_SETTING_WIRELESS_BAND, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_BAND] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_BAND, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:channel:
@@ -1277,13 +1289,12 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * example: CHANNEL=6
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CHANNEL,
-		 g_param_spec_uint (NM_SETTING_WIRELESS_CHANNEL, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CHANNEL] =
+	    g_param_spec_uint (NM_SETTING_WIRELESS_CHANNEL, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:bssid:
@@ -1300,16 +1311,14 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * example: BSSID=00:1E:BD:64:83:21
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_BSSID,
-		 g_param_spec_string (NM_SETTING_WIRELESS_BSSID, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_BSSID] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_BSSID, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_WIRELESS_BSSID),
+	                                    obj_properties[PROP_BSSID],
 	                                    G_VARIANT_TYPE_BYTESTRING,
 	                                    _nm_utils_hwaddr_to_dbus,
 	                                    _nm_utils_hwaddr_from_dbus);
@@ -1328,14 +1337,13 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * description: This property is not handled by ifcfg-rh plugin.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_RATE,
-		 g_param_spec_uint (NM_SETTING_WIRELESS_RATE, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_RATE] =
+	    g_param_spec_uint (NM_SETTING_WIRELESS_RATE, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:tx-power:
@@ -1350,14 +1358,13 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * description: This property is not handled by ifcfg-rh plugin.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_TX_POWER,
-		 g_param_spec_uint (NM_SETTING_WIRELESS_TX_POWER, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_TX_POWER] =
+	    g_param_spec_uint (NM_SETTING_WIRELESS_TX_POWER, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:mac-address:
@@ -1383,16 +1390,14 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 *    permanent MAC address exists, the MAC address initially configured on the device.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MAC_ADDRESS,
-		 g_param_spec_string (NM_SETTING_WIRELESS_MAC_ADDRESS, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MAC_ADDRESS] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_MAC_ADDRESS, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_transform (properties_override,
-	                                    g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                  NM_SETTING_WIRELESS_MAC_ADDRESS),
+	                                    obj_properties[PROP_MAC_ADDRESS],
 	                                    G_VARIANT_TYPE_BYTESTRING,
 	                                    _nm_utils_hwaddr_to_dbus,
 	                                    _nm_utils_hwaddr_from_dbus);
@@ -1439,16 +1444,15 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 *    For libnm and nmcli, this field is called "cloned-mac-address".
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_CLONED_MAC_ADDRESS,
-		 g_param_spec_string (NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "", "",
-		                      NULL,
-		                      G_PARAM_READWRITE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_CLONED_MAC_ADDRESS] =
+	    g_param_spec_string (NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         NM_SETTING_PARAM_INFERRABLE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	_properties_override_add_override (properties_override,
-	                                   g_object_class_find_property (G_OBJECT_CLASS (setting_class),
-	                                                                 NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS),
+	                                   obj_properties[PROP_CLONED_MAC_ADDRESS],
 	                                   G_VARIANT_TYPE_BYTESTRING,
 	                                   _nm_utils_hwaddr_cloned_get,
 	                                   _nm_utils_hwaddr_cloned_set,
@@ -1461,7 +1465,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 *   a hardware address in ASCII representation, or one of the special values
 	 *   "preserve", "permanent", "random" or "stable".
 	 *   This field replaces the deprecated "cloned-mac-address" on D-Bus, which
-	 *   can only contain explict hardware addresses. Note that this property
+	 *   can only contain explicit hardware addresses. Note that this property
 	 *   only exists in D-Bus API. libnm and nmcli continue to call this property
 	 *   "cloned-mac-address".
 	 * ---end---
@@ -1511,13 +1515,12 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 *   cloned-mac-address.
 	 * ---end---
 	 */
-	g_object_class_install_property
-	    (object_class, PROP_GENERATE_MAC_ADDRESS_MASK,
+	obj_properties[PROP_GENERATE_MAC_ADDRESS_MASK] =
 	     g_param_spec_string (NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK, "", "",
 	                          NULL,
 	                          G_PARAM_READWRITE |
 	                          NM_SETTING_PARAM_FUZZY_IGNORE |
-	                          G_PARAM_STATIC_STRINGS));
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:mac-address-blacklist:
@@ -1539,13 +1542,12 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 *   is listed.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MAC_ADDRESS_BLACKLIST,
-		 g_param_spec_boxed (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_FUZZY_IGNORE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MAC_ADDRESS_BLACKLIST] =
+	    g_param_spec_boxed (NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_FUZZY_IGNORE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:seen-bssids:
@@ -1563,13 +1565,12 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * description: This property is not handled by ifcfg-rh plugin.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_SEEN_BSSIDS,
-		 g_param_spec_boxed (NM_SETTING_WIRELESS_SEEN_BSSIDS, "", "",
-		                     G_TYPE_STRV,
-		                     G_PARAM_READWRITE |
-		                     NM_SETTING_PARAM_FUZZY_IGNORE |
-		                     G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SEEN_BSSIDS] =
+	    g_param_spec_boxed (NM_SETTING_WIRELESS_SEEN_BSSIDS, "", "",
+	                        G_TYPE_STRV,
+	                        G_PARAM_READWRITE |
+	                        NM_SETTING_PARAM_FUZZY_IGNORE |
+	                        G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:mtu:
@@ -1583,14 +1584,13 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * description: MTU of the wireless interface.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MTU,
-		 g_param_spec_uint (NM_SETTING_WIRELESS_MTU, "", "",
-		                    0, G_MAXUINT32, 0,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_CONSTRUCT |
-		                    NM_SETTING_PARAM_FUZZY_IGNORE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MTU] =
+	    g_param_spec_uint (NM_SETTING_WIRELESS_MTU, "", "",
+	                       0, G_MAXUINT32, 0,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_CONSTRUCT |
+	                       NM_SETTING_PARAM_FUZZY_IGNORE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:hidden:
@@ -1616,12 +1616,11 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * description: Whether the network hides the SSID.
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_HIDDEN,
-		 g_param_spec_boolean (NM_SETTING_WIRELESS_HIDDEN, "", "",
-		                       FALSE,
-		                       G_PARAM_READWRITE |
-		                       G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_HIDDEN] =
+	    g_param_spec_boolean (NM_SETTING_WIRELESS_HIDDEN, "", "",
+	                          FALSE,
+	                          G_PARAM_READWRITE |
+	                          G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:powersave:
@@ -1642,12 +1641,11 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * example: POWERSAVE=enable
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_POWERSAVE,
-		 g_param_spec_uint (NM_SETTING_WIRELESS_POWERSAVE, "", "",
-		                    0, G_MAXUINT32, NM_SETTING_WIRELESS_POWERSAVE_DEFAULT,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_POWERSAVE] =
+	    g_param_spec_uint (NM_SETTING_WIRELESS_POWERSAVE, "", "",
+	                       0, G_MAXUINT32, NM_SETTING_WIRELESS_POWERSAVE_DEFAULT,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWireless:mac-address-randomization:
@@ -1670,12 +1668,11 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 * example: MAC_ADDRESS_RANDOMIZATION=always
 	 * ---end---
 	 */
-	g_object_class_install_property
-		(object_class, PROP_MAC_ADDRESS_RANDOMIZATION,
-		 g_param_spec_uint (NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION, "", "",
-		                    0, G_MAXUINT32, NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MAC_ADDRESS_RANDOMIZATION] =
+	    g_param_spec_uint (NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION, "", "",
+	                       0, G_MAXUINT32, NM_SETTING_MAC_RANDOMIZATION_DEFAULT,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/* Compatibility for deprecated property */
 	/* ---ifcfg-rh---
@@ -1715,13 +1712,14 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *klass)
 	 *
 	 * Since: 1.12
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_WAKE_ON_WLAN,
-		 g_param_spec_uint (NM_SETTING_WIRELESS_WAKE_ON_WLAN, "", "",
-		                    0, G_MAXUINT32, NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT,
-		                    G_PARAM_CONSTRUCT |
-		                    G_PARAM_READWRITE |
-		                    G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_WAKE_ON_WLAN] =
+	    g_param_spec_uint (NM_SETTING_WIRELESS_WAKE_ON_WLAN, "", "",
+	                       0, G_MAXUINT32, NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT,
+	                       G_PARAM_CONSTRUCT |
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit_full (setting_class, NM_META_SETTING_TYPE_WIRELESS,
 	                               NULL, properties_override);
diff --git a/libnm-core/nm-setting-wireless.h b/libnm-core/nm-setting-wireless.h
index e9afe2c6..a2ae38a4 100644
--- a/libnm-core/nm-setting-wireless.h
+++ b/libnm-core/nm-setting-wireless.h
@@ -49,7 +49,7 @@ G_BEGIN_DECLS
  * @NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC: Wake on magic packet
  * @NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE: Wake on GTK rekey failure
  * @NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST: Wake on EAP identity request
- * @NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE: Wake on 4way hanshake
+ * @NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE: Wake on 4way handshake
  * @NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE: Wake on rfkill release
  * @NM_SETTING_WIRELESS_WAKE_ON_WLAN_ALL: Wake on all events. This does not
  *   include the exclusive flags @NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT or
@@ -66,21 +66,21 @@ G_BEGIN_DECLS
  */
 typedef enum { /*< flags >*/
 	NM_SETTING_WIRELESS_WAKE_ON_WLAN_NONE                 = 0, /*< skip >*/
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY                  = (1 << 1),
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT           = (1 << 2),
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC                = (1 << 3),
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE    = (1 << 4),
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST = (1 << 5),
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE       = (1 << 6),
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE       = (1 << 7),
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_TCP                  = (1 << 8),
-	_NM_SETTING_WIRELESS_WAKE_ON_WLAN_NUM, /*< skip >*/
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_LAST                 = _NM_SETTING_WIRELESS_WAKE_ON_WLAN_NUM - 1, /*< skip >*/
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_ALL                  = ((NM_SETTING_WIRELESS_WAKE_ON_WLAN_LAST << 1) - 1) - (1 << 0 /*DEFAULT*/), /*< skip >*/
-
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT              = (1 << 0),
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE               = (1 << 15),
-	NM_SETTING_WIRELESS_WAKE_ON_WLAN_EXCLUSIVE_FLAGS = NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT | NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE, /*< skip >*/
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_ANY                  = 0x2,
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_DISCONNECT           = 0x4,
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_MAGIC                = 0x8,
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_GTK_REKEY_FAILURE    = 0x10,
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_EAP_IDENTITY_REQUEST = 0x20,
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_4WAY_HANDSHAKE       = 0x40,
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_RFKILL_RELEASE       = 0x80,
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_TCP                  = 0x100,
+
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_ALL                  = 0x1FE,
+
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT              = 0x1,
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE               = 0x8000,
+
+	NM_SETTING_WIRELESS_WAKE_ON_WLAN_EXCLUSIVE_FLAGS      = NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT | NM_SETTING_WIRELESS_WAKE_ON_WLAN_IGNORE, /*< skip >*/
 } NMSettingWirelessWakeOnWLan;
 
 #define NM_SETTING_WIRELESS_SSID        "ssid"
diff --git a/libnm-core/nm-setting-wpan.c b/libnm-core/nm-setting-wpan.c
index 6e1aaadc..f4270032 100644
--- a/libnm-core/nm-setting-wpan.c
+++ b/libnm-core/nm-setting-wpan.c
@@ -26,22 +26,43 @@
 #include "nm-setting-private.h"
 #include "nm-utils-private.h"
 
-/* Ideally we'll be able to get this from a public header. */
+/**
+ * SECTION:nm-setting-wpan
+ * @short_description: Describes connection properties for IEEE 802.15.4 (WPAN) MAC
+ *
+ * The #NMSettingWpan object is a #NMSetting subclass that describes properties
+ * necessary for configuring IEEE 802.15.4 (WPAN) MAC layer devices.
+ **/
+
+/* Ideally we'll be able to get these from a public header. */
 #ifndef IEEE802154_ADDR_LEN
 #define IEEE802154_ADDR_LEN 8
 #endif
 
-enum {
-	PROP_0,
+#ifndef IEEE802154_MAX_PAGE
+#define IEEE802154_MAX_PAGE 31
+#endif
+
+#ifndef IEEE802154_MAX_CHANNEL
+#define IEEE802154_MAX_CHANNEL 26
+#endif
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE_BASE (
 	PROP_MAC_ADDRESS,
 	PROP_PAN_ID,
 	PROP_SHORT_ADDRESS,
-};
+	PROP_PAGE,
+	PROP_CHANNEL,
+);
 
 typedef struct {
 	char *mac_address;
 	guint16 pan_id;
 	guint16 short_address;
+	gint16 page;
+	gint16 channel;
 } NMSettingWpanPrivate;
 
 /**
@@ -57,18 +78,12 @@ struct _NMSettingWpanClass {
         NMSettingClass parent;
 };
 
-/**
- * SECTION:nm-setting-wpan
- * @short_description: Describes connection properties for IEEE 802.15.4 (WPAN) MAC
- *
- * The #NMSettingWpan object is a #NMSetting subclass that describes properties
- * necessary for configuring IEEE 802.15.4 (WPAN) MAC layer devices.
- **/
-
 G_DEFINE_TYPE (NMSettingWpan, nm_setting_wpan, NM_TYPE_SETTING)
 
 #define NM_SETTING_WPAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SETTING_WPAN, NMSettingWpanPrivate))
 
+/*****************************************************************************/
+
 /**
  * nm_setting_wpan_get_mac_address:
  * @setting: the #NMSettingWpan
@@ -117,6 +132,38 @@ nm_setting_wpan_get_short_address (NMSettingWpan *setting)
 	return NM_SETTING_WPAN_GET_PRIVATE (setting)->short_address;
 }
 
+/**
+ * nm_setting_wpan_get_page:
+ * @setting: the #NMSettingWpan
+ *
+ * Returns: the #NMSettingWpan:page property of the setting
+ *
+ * Since: 1.16
+ **/
+gint16
+nm_setting_wpan_get_page (NMSettingWpan *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WPAN (setting), NM_SETTING_WPAN_PAGE_DEFAULT);
+
+	return NM_SETTING_WPAN_GET_PRIVATE (setting)->page;
+}
+
+/**
+ * nm_setting_wpan_get_channel:
+ * @setting: the #NMSettingWpan
+ *
+ * Returns: the #NMSettingWpan:channel property of the setting
+ *
+ * Since: 1.16
+ **/
+gint16
+nm_setting_wpan_get_channel (NMSettingWpan *setting)
+{
+	g_return_val_if_fail (NM_IS_SETTING_WPAN (setting), NM_SETTING_WPAN_CHANNEL_DEFAULT);
+
+	return NM_SETTING_WPAN_GET_PRIVATE (setting)->channel;
+}
+
 static gboolean
 verify (NMSetting *setting, NMConnection *connection, GError **error)
 {
@@ -131,9 +178,42 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
 		return FALSE;
 	}
 
+	if ((priv->page == NM_SETTING_WPAN_PAGE_DEFAULT) != (priv->channel == NM_SETTING_WPAN_CHANNEL_DEFAULT)) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("page must be defined along with a channel"));
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WPAN_SETTING_NAME, NM_SETTING_WPAN_PAGE);
+		return FALSE;
+	}
+
+	if (priv->page < NM_SETTING_WPAN_PAGE_DEFAULT || priv->page > IEEE802154_MAX_PAGE) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("page must be between %d and %d"),
+		             NM_SETTING_WPAN_PAGE_DEFAULT,
+		             IEEE802154_MAX_PAGE);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WPAN_SETTING_NAME, NM_SETTING_WPAN_PAGE);
+		return FALSE;
+	}
+
+	if (priv->channel < NM_SETTING_WPAN_CHANNEL_DEFAULT || priv->channel > IEEE802154_MAX_CHANNEL) {
+		g_set_error (error,
+		             NM_CONNECTION_ERROR,
+		             NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		             _("channel must not be between %d and %d"),
+		             NM_SETTING_WPAN_CHANNEL_DEFAULT,
+		             IEEE802154_MAX_CHANNEL);
+		g_prefix_error (error, "%s.%s: ", NM_SETTING_WPAN_SETTING_NAME, NM_SETTING_WPAN_CHANNEL);
+		return FALSE;
+	}
+
 	return TRUE;
 }
 
+/*****************************************************************************/
+
 static void
 get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 {
@@ -149,6 +229,12 @@ get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 	case PROP_SHORT_ADDRESS:
 		g_value_set_uint (value, nm_setting_wpan_get_short_address (setting));
 		break;
+	case PROP_PAGE:
+		g_value_set_int (value, nm_setting_wpan_get_page (setting));
+		break;
+	case PROP_CHANNEL:
+		g_value_set_int (value, nm_setting_wpan_get_channel (setting));
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -172,19 +258,29 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *p
 	case PROP_SHORT_ADDRESS:
 		priv->short_address = g_value_get_uint (value);
 		break;
+	case PROP_PAGE:
+		priv->page = g_value_get_int (value);
+		break;
+	case PROP_CHANNEL:
+		priv->channel = g_value_get_int (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
 	}
 }
 
+/*****************************************************************************/
+
 static void
 nm_setting_wpan_init (NMSettingWpan *setting)
 {
 	NMSettingWpanPrivate *priv = NM_SETTING_WPAN_GET_PRIVATE (setting);
 
-	priv->short_address = G_MAXUINT16;
 	priv->pan_id = G_MAXUINT16;
+	priv->short_address = G_MAXUINT16;
+	priv->page = NM_SETTING_WPAN_PAGE_DEFAULT;
+	priv->channel = NM_SETTING_WPAN_CHANNEL_DEFAULT;
 }
 
 /**
@@ -220,8 +316,8 @@ nm_setting_wpan_class_init (NMSettingWpanClass *klass)
 
 	g_type_class_add_private (setting_class, sizeof (NMSettingWpanPrivate));
 
-	object_class->set_property = set_property;
 	object_class->get_property = get_property;
+	object_class->set_property = set_property;
 	object_class->finalize     = finalize;
 
 	setting_class->verify = verify;
@@ -239,36 +335,67 @@ nm_setting_wpan_class_init (NMSettingWpanClass *klass)
 	 *   (e.g. 76:d8:9b:87:66:60:84:ee).
 	 * ---end---
 	 */
-	g_object_class_install_property
-	        (object_class, PROP_MAC_ADDRESS,
-	         g_param_spec_string (NM_SETTING_WPAN_MAC_ADDRESS, "", "",
-	                              NULL,
-	                              G_PARAM_READWRITE |
-	                              G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_MAC_ADDRESS] =
+	    g_param_spec_string (NM_SETTING_WPAN_MAC_ADDRESS, "", "",
+	                         NULL,
+	                         G_PARAM_READWRITE |
+	                         G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWpan:pan-id:
 	 *
 	 * IEEE 802.15.4 Personal Area Network (PAN) identifier.
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_PAN_ID,
-	         g_param_spec_uint (NM_SETTING_WPAN_PAN_ID, "", "",
-	                            0, G_MAXUINT16, G_MAXUINT16,
-	                            G_PARAM_READWRITE |
-	                            G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_PAN_ID] =
+	    g_param_spec_uint (NM_SETTING_WPAN_PAN_ID, "", "",
+	                       0, G_MAXUINT16, G_MAXUINT16,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
 
 	/**
 	 * NMSettingWpan:short-address:
 	 *
 	 * Short IEEE 802.15.4 address to be used within a restricted environment.
 	 **/
-	g_object_class_install_property
-	        (object_class, PROP_SHORT_ADDRESS,
-	         g_param_spec_uint (NM_SETTING_WPAN_SHORT_ADDRESS, "", "",
-	                            0, G_MAXUINT16, G_MAXUINT16,
-	                            G_PARAM_READWRITE |
-	                            G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_SHORT_ADDRESS] =
+	    g_param_spec_uint (NM_SETTING_WPAN_SHORT_ADDRESS, "", "",
+	                       0, G_MAXUINT16, G_MAXUINT16,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * NMSettingWpan:page:
+	 *
+	 * IEEE 802.15.4 channel page. A positive integer or -1, meaning "do not
+	 * set, use whatever the device is already set to".
+	 *
+	 * Since: 1.16
+	 **/
+	obj_properties[PROP_PAGE] =
+	    g_param_spec_int (NM_SETTING_WPAN_PAGE, "", "",
+	                       G_MININT16,
+	                       G_MAXINT16,
+	                       NM_SETTING_WPAN_PAGE_DEFAULT,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	/**
+	 * NMSettingWpan:channel:
+	 *
+	 * IEEE 802.15.4 channel. A positive integer or -1, meaning "do not
+	 * set, use whatever the device is already set to".
+	 *
+	 * Since: 1.16
+	 **/
+	obj_properties[PROP_CHANNEL] =
+	    g_param_spec_int (NM_SETTING_WPAN_CHANNEL, "", "",
+	                       G_MININT16,
+	                       G_MAXINT16,
+	                       NM_SETTING_WPAN_CHANNEL_DEFAULT,
+	                       G_PARAM_READWRITE |
+	                       G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
 	_nm_setting_class_commit (setting_class, NM_META_SETTING_TYPE_WPAN);
 }
diff --git a/libnm-core/nm-setting-wpan.h b/libnm-core/nm-setting-wpan.h
index a39e377e..aaea1086 100644
--- a/libnm-core/nm-setting-wpan.h
+++ b/libnm-core/nm-setting-wpan.h
@@ -39,6 +39,11 @@ G_BEGIN_DECLS
 #define NM_SETTING_WPAN_MAC_ADDRESS        "mac-address"
 #define NM_SETTING_WPAN_PAN_ID             "pan-id"
 #define NM_SETTING_WPAN_SHORT_ADDRESS      "short-address"
+#define NM_SETTING_WPAN_PAGE               "page"
+#define NM_SETTING_WPAN_CHANNEL            "channel"
+
+#define NM_SETTING_WPAN_PAGE_DEFAULT    -1
+#define NM_SETTING_WPAN_CHANNEL_DEFAULT -1
 
 typedef struct _NMSettingWpanClass NMSettingWpanClass;
 
@@ -53,6 +58,10 @@ NM_AVAILABLE_IN_1_14
 guint16     nm_setting_wpan_get_pan_id             (NMSettingWpan *setting);
 NM_AVAILABLE_IN_1_14
 guint16     nm_setting_wpan_get_short_address      (NMSettingWpan *setting);
+NM_AVAILABLE_IN_1_16
+gint16      nm_setting_wpan_get_page               (NMSettingWpan *setting);
+NM_AVAILABLE_IN_1_16
+gint16      nm_setting_wpan_get_channel            (NMSettingWpan *setting);
 
 G_END_DECLS
 
diff --git a/libnm-core/nm-setting.c b/libnm-core/nm-setting.c
index 047391ce..2e9081df 100644
--- a/libnm-core/nm-setting.c
+++ b/libnm-core/nm-setting.c
@@ -24,23 +24,12 @@
 
 #include "nm-setting.h"
 
-#include <string.h>
-
 #include "nm-setting-private.h"
 #include "nm-utils.h"
 #include "nm-core-internal.h"
 #include "nm-utils-private.h"
 #include "nm-property-compare.h"
 
-#include "nm-setting-connection.h"
-#include "nm-setting-bond.h"
-#include "nm-setting-bridge.h"
-#include "nm-setting-bridge-port.h"
-#include "nm-setting-pppoe.h"
-#include "nm-setting-team.h"
-#include "nm-setting-team-port.h"
-#include "nm-setting-vpn.h"
-
 /**
  * SECTION:nm-setting
  * @short_description: Describes related configuration information
@@ -67,12 +56,9 @@ typedef struct {
 	NMSettingPriority priority;
 } SettingInfo;
 
-enum {
-	PROP_0,
+NM_GOBJECT_PROPERTIES_DEFINE (NMSetting,
 	PROP_NAME,
-
-	PROP_LAST
-};
+);
 
 typedef struct {
 	GenData *gendata;
@@ -354,6 +340,77 @@ _properties_override_add_transform (GArray *properties_override,
 
 static NMSettInfoSetting _sett_info_settings[_NM_META_SETTING_TYPE_NUM];
 
+static int
+_property_infos_sort_cmp_setting_connection (gconstpointer p_a,
+                                             gconstpointer p_b,
+                                             gpointer user_data)
+{
+	const NMSettInfoProperty *a = *((const NMSettInfoProperty *const*) p_a);
+	const NMSettInfoProperty *b = *((const NMSettInfoProperty *const*) p_b);
+	int c_name;
+
+	c_name = strcmp (a->name, b->name);
+	nm_assert (c_name != 0);
+
+#define CMP_AND_RETURN(n_a, n_b, name) \
+	G_STMT_START { \
+		gboolean _is = nm_streq (n_a, ""name); \
+		\
+		if (   _is \
+		    || nm_streq (n_b, ""name)) \
+			return _is ? -1 : 1; \
+	} G_STMT_END
+
+	/* for [connection], report first id, uuid, type in that order. */
+	if (c_name != 0) {
+		CMP_AND_RETURN (a->name, b->name, NM_SETTING_CONNECTION_ID);
+		CMP_AND_RETURN (a->name, b->name, NM_SETTING_CONNECTION_UUID);
+		CMP_AND_RETURN (a->name, b->name, NM_SETTING_CONNECTION_TYPE);
+	}
+
+#undef CMP_AND_RETURN
+
+	return c_name;
+}
+
+static const NMSettInfoProperty *const*
+_property_infos_sort (const NMSettInfoProperty *property_infos,
+                      guint property_infos_len,
+                      NMSettingClass *setting_class)
+{
+	const NMSettInfoProperty **arr;
+	guint i;
+
+#if NM_MORE_ASSERTS > 5
+	/* assert that the property names are all unique and sorted. */
+	for (i = 0; i < property_infos_len; i++) {
+		if (property_infos[i].param_spec)
+			nm_assert (nm_streq (property_infos[i].name, property_infos[i].param_spec->name));
+		if (i > 0)
+			nm_assert (strcmp (property_infos[i - 1].name, property_infos[i].name) < 0);
+	}
+#endif
+
+	if (property_infos_len <= 1)
+		return NULL;
+	if (G_TYPE_FROM_CLASS (setting_class) != NM_TYPE_SETTING_CONNECTION) {
+		/* we only do something special for certain setting types. This one,
+		 * has just alphabetical sorting. */
+		return NULL;
+	}
+
+	arr = g_new (const NMSettInfoProperty *, property_infos_len);
+	for (i = 0; i < property_infos_len; i++)
+		arr[i] = &property_infos[i];
+
+	g_qsort_with_data (arr,
+	                   property_infos_len,
+	                   sizeof (const NMSettInfoProperty *),
+	                   _property_infos_sort_cmp_setting_connection,
+	                   NULL);
+	return arr;
+}
+
 void
 _nm_setting_class_commit_full (NMSettingClass *setting_class,
                                NMMetaSettingType meta_type,
@@ -387,19 +444,21 @@ _nm_setting_class_commit_full (NMSettingClass *setting_class,
 #if NM_MORE_ASSERTS > 10
 	/* assert that properties_override is constructed consistently. */
 	for (i = 0; i < override_len; i++) {
-		guint j;
 		const NMSettInfoProperty *p = &g_array_index (properties_override, NMSettInfoProperty, i);
+		gboolean found = FALSE;
+		guint j;
 
 		nm_assert (!_nm_sett_info_property_find_in_array ((NMSettInfoProperty *) properties_override->data,
 		                                                  i,
 		                                                  p->name));
 		for (j = 0; j < n_property_specs; j++) {
-			if (nm_streq (property_specs[j]->name, p->name)) {
-				nm_assert (p->param_spec == property_specs[j]);
-				break;
-			}
+			if (!nm_streq (property_specs[j]->name, p->name))
+				continue;
+			nm_assert (!found);
+			found = TRUE;
+			nm_assert (p->param_spec == property_specs[j]);
 		}
-		nm_assert ((j == n_property_specs) == (p->param_spec == NULL));
+		nm_assert (found == (p->param_spec != NULL));
 	}
 #endif
 
@@ -429,27 +488,21 @@ _nm_setting_class_commit_full (NMSettingClass *setting_class,
 	sett_info->property_infos_len = properties_override->len;
 	sett_info->property_infos = (const NMSettInfoProperty *) g_array_free (properties_override,
 	                                                                       properties_override->len == 0);
-}
 
-const NMSettInfoSetting *
-_nm_sett_info_setting_get (NMSettingClass *setting_class)
-{
-	if (   NM_IS_SETTING_CLASS (setting_class)
-	    && setting_class->setting_info) {
-		nm_assert (setting_class->setting_info->meta_type < G_N_ELEMENTS (_sett_info_settings));
-		return &_sett_info_settings[setting_class->setting_info->meta_type];
-	}
-	return NULL;
+	sett_info->property_infos_sorted = _property_infos_sort (sett_info->property_infos,
+	                                                         sett_info->property_infos_len,
+	                                                         setting_class);
 }
 
 const NMSettInfoProperty *
-_nm_sett_info_property_get (NMSettingClass *setting_class,
-                            const char *property_name)
+_nm_sett_info_setting_get_property_info (const NMSettInfoSetting *sett_info,
+                                         const char *property_name)
 {
-	const NMSettInfoSetting *sett_info = _nm_sett_info_setting_get (setting_class);
 	const NMSettInfoProperty *property;
 	gssize idx;
 
+	nm_assert (property_name);
+
 	if (!sett_info)
 		return NULL;
 
@@ -472,6 +525,50 @@ _nm_sett_info_property_get (NMSettingClass *setting_class,
 	return property;
 }
 
+const NMSettInfoSetting *
+_nm_setting_class_get_sett_info (NMSettingClass *setting_class)
+{
+	const NMSettInfoSetting *sett_info;
+
+	if (   !NM_IS_SETTING_CLASS (setting_class)
+	    || !setting_class->setting_info)
+		return NULL;
+
+	nm_assert (setting_class->setting_info->meta_type < G_N_ELEMENTS (_sett_info_settings));
+	sett_info = &_sett_info_settings[setting_class->setting_info->meta_type];
+	nm_assert (sett_info->setting_class == setting_class);
+	return sett_info;
+}
+
+/*****************************************************************************/
+
+void
+_nm_setting_emit_property_changed (NMSetting *setting)
+{
+	/* Some settings have "properties" that are not implemented as GObject properties.
+	 *
+	 * For example:
+	 *
+	 *   - gendata-base settings like NMSettingEthtool. Here properties are just
+	 *     GVariant values in the gendata hash.
+	 *
+	 *   - NMSettingWireGuard's peers are not backed by a GObject property. Instead
+	 *     there is C-API to access/modify peers.
+	 *
+	 * We still want to emit property-changed notifications for such properties,
+	 * in particular because NMConnection registers to such signals to re-emit
+	 * it as NM_CONNECTION_CHANGED signal. In fact, there are unlikely any other
+	 * uses of such a property-changed signal, because generally it doesn't make
+	 * too much sense.
+	 *
+	 * So, instead of adding yet another (artificial) signal "setting-changed",
+	 * hijack the "notify" signal and just notify about changes of the "name".
+	 * Of course, the "name" doesn't really ever change, because it's tied to
+	 * the GObject's type.
+	 */
+	_notify (setting, PROP_NAME);
+}
+
 /*****************************************************************************/
 
 gboolean
@@ -654,7 +751,7 @@ _nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionS
 		                       g_hash_table_lookup (priv->gendata->hash, gendata_keys[i]));
 	}
 
-	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+	sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (setting));
 	for (i = 0; i < sett_info->property_infos_len; i++) {
 		const NMSettInfoProperty *property = &sett_info->property_infos[i];
 		GParamSpec *prop_spec = property->param_spec;
@@ -662,10 +759,13 @@ _nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionS
 		if (!prop_spec) {
 			if (!property->synth_func)
 				continue;
-
-			if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
-				continue;
 		} else {
+
+			/* For the moment, properties backed by a GObject property don't
+			 * define a synth function. There is no problem supporting that,
+			 * however, for now just disallow it. */
+			nm_assert (!property->synth_func);
+
 			if (!(prop_spec->flags & G_PARAM_WRITABLE))
 				continue;
 
@@ -685,14 +785,10 @@ _nm_setting_to_dbus (NMSetting *setting, NMConnection *connection, NMConnectionS
 				continue;
 		}
 
-		if (property->synth_func) {
-			if (!(flags & NM_CONNECTION_SERIALIZE_NO_SYNTH))
-				dbus_value = property->synth_func (setting, connection, property->name);
-			else
-				dbus_value = NULL;
-		} else {
+		if (property->synth_func)
+			dbus_value = property->synth_func (sett_info, i, connection, setting, flags);
+		else
 			dbus_value = get_property_for_dbus (setting, property, TRUE);
-		}
 
 		if (dbus_value) {
 			/* Allow dbus_value to be either floating or not. */
@@ -780,7 +876,7 @@ _nm_setting_new_from_dbus (GType setting_type,
 		}
 	}
 
-	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+	sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (setting));
 
 	if (sett_info->detail.gendata_info) {
 		GHashTable *hash;
@@ -802,89 +898,94 @@ _nm_setting_new_from_dbus (GType setting_type,
 	}
 
 	for (i = 0; i < sett_info->property_infos_len; i++) {
-		const NMSettInfoProperty *property = &sett_info->property_infos[i];
+		const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
 		gs_unref_variant GVariant *value = NULL;
 		gs_free_error GError *local = NULL;
 
-		if (property->param_spec && !(property->param_spec->flags & G_PARAM_WRITABLE))
+		if (   property_info->param_spec
+		    && !(property_info->param_spec->flags & G_PARAM_WRITABLE))
 			continue;
 
-		value = g_variant_lookup_value (setting_dict, property->name, NULL);
+		value = g_variant_lookup_value (setting_dict, property_info->name, NULL);
 
 		if (value && keys)
-			g_hash_table_remove (keys, property->name);
+			g_hash_table_remove (keys, property_info->name);
 
-		if (value && property->set_func) {
+		if (   value
+		    && property_info->set_func) {
 
-			if (!g_variant_type_equal (g_variant_get_type (value), property->dbus_type)) {
+			if (!g_variant_type_equal (g_variant_get_type (value), property_info->dbus_type)) {
 				/* for backward behavior, fail unless best-effort is chosen. */
 				if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
 					continue;
 				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
 				             _("can't set property of type '%s' from value of type '%s'"),
-				             property->dbus_type ?
-				                 g_variant_type_peek_string (property->dbus_type) :
-				                 property->param_spec ?
-				                     g_type_name (property->param_spec->value_type) : "(unknown)",
+				             property_info->dbus_type ?
+				                 g_variant_type_peek_string (property_info->dbus_type) :
+				                 property_info->param_spec ?
+				                     g_type_name (property_info->param_spec->value_type) : "(unknown)",
 				             g_variant_get_type_string (value));
-				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
+				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property_info->name);
 				return NULL;
 			}
 
-			if (!property->set_func (setting,
-			                         connection_dict,
-			                         property->name,
-			                         value,
-			                         parse_flags,
-			                         &local)) {
+			if (!property_info->set_func (setting,
+			                             connection_dict,
+			                             property_info->name,
+			                             value,
+			                             parse_flags,
+			                             &local)) {
 				if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
 					continue;
 				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
 				             _("failed to set property: %s"),
 				             local->message);
-				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
+				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property_info->name);
 				return NULL;
 			}
-		} else if (!value && property->not_set_func) {
-			if (!property->not_set_func (setting,
-			                             connection_dict,
-			                             property->name,
-			                             parse_flags,
-			                             &local)) {
+		} else if (   !value
+		           && property_info->not_set_func) {
+			if (!property_info->not_set_func (setting,
+			                                  connection_dict,
+			                                  property_info->name,
+			                                  parse_flags,
+			                                  &local)) {
 				if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
 					continue;
 				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
 				             _("failed to set property: %s"),
 				             local->message);
-				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
+				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property_info->name);
 				return NULL;
 			}
-		} else if (value && property->param_spec) {
+		} else if (   value
+		           && property_info->param_spec) {
 			nm_auto_unset_gvalue GValue object_value = G_VALUE_INIT;
 
-			g_value_init (&object_value, property->param_spec->value_type);
-			if (!set_property_from_dbus (property, value, &object_value)) {
+			g_value_init (&object_value, property_info->param_spec->value_type);
+			if (!set_property_from_dbus (property_info, value, &object_value)) {
 				/* for backward behavior, fail unless best-effort is chosen. */
 				if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
 					continue;
 				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
 				             _("can't set property of type '%s' from value of type '%s'"),
-				             property->dbus_type ?
-				                 g_variant_type_peek_string (property->dbus_type) :
-				                 property->param_spec ?
-				                     g_type_name (property->param_spec->value_type) : "(unknown)",
+				               property_info->dbus_type
+				             ? g_variant_type_peek_string (property_info->dbus_type)
+				             : (  property_info->param_spec
+				                ? g_type_name (property_info->param_spec->value_type)
+				                : "(unknown)"),
 				             g_variant_get_type_string (value));
-				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
+				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property_info->name);
 				return NULL;
 			}
 
-			if (!nm_g_object_set_property (G_OBJECT (setting), property->param_spec->name, &object_value, &local)) {
+			if (!nm_g_object_set_property (G_OBJECT (setting), property_info->param_spec->name, &object_value, &local)) {
 				if (!NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
 					continue;
 				g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
 				             _("can not set property: %s"),
 				             local->message);
-				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property->name);
+				g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), property_info->name);
 				return NULL;
 			}
 		}
@@ -926,7 +1027,7 @@ nm_setting_get_dbus_property_type (NMSetting *setting,
 	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
 	g_return_val_if_fail (property_name != NULL, NULL);
 
-	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), property_name);
+	property = _nm_setting_class_get_property_info (NM_SETTING_GET_CLASS (setting), property_name);
 	g_return_val_if_fail (property != NULL, NULL);
 
 	if (property->dbus_type)
@@ -939,13 +1040,13 @@ gboolean
 _nm_setting_get_property (NMSetting *setting, const char *property_name, GValue *value)
 {
 	const NMSettInfoSetting *sett_info;
-	GParamSpec *prop_spec;
+	const NMSettInfoProperty *property_info;
 
 	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
 	g_return_val_if_fail (property_name, FALSE);
 	g_return_val_if_fail (value, FALSE);
 
-	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+	sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (setting));
 
 	if (sett_info->detail.gendata_info) {
 		GVariant *variant;
@@ -963,26 +1064,86 @@ _nm_setting_get_property (NMSetting *setting, const char *property_name, GValue
 		return TRUE;
 	}
 
-	prop_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (setting), property_name);
-	if (!prop_spec) {
+	property_info = _nm_sett_info_setting_get_property_info (sett_info, property_name);
+	if (   !property_info
+	    || !property_info->param_spec) {
 		g_value_unset (value);
 		return FALSE;
 	}
 
-	g_value_init (value, prop_spec->value_type);
+	g_value_init (value, property_info->param_spec->value_type);
 	g_object_get_property (G_OBJECT (setting), property_name, value);
 	return TRUE;
 }
 
 static void
-duplicate_setting (NMSetting *setting,
-                   const char *name,
-                   const GValue *value,
-                   GParamFlags flags,
-                   gpointer user_data)
+_gobject_copy_property (GObject *src,
+                        GObject *dst,
+                        const char *property_name,
+                        GType gtype)
 {
-	if ((flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) == G_PARAM_WRITABLE)
-		g_object_set_property (G_OBJECT (user_data), name, value);
+	nm_auto_unset_gvalue GValue value = G_VALUE_INIT;
+
+	nm_assert (G_IS_OBJECT (src));
+	nm_assert (G_IS_OBJECT (dst));
+
+	g_value_init (&value, gtype);
+	g_object_get_property (src, property_name, &value);
+	g_object_set_property (dst, property_name, &value);
+}
+
+static void
+duplicate_copy_properties (const NMSettInfoSetting *sett_info,
+                           NMSetting *src,
+                           NMSetting *dst)
+{
+	if (sett_info->detail.gendata_info) {
+		GenData *gendata = _gendata_hash (src, FALSE);
+
+		nm_assert (!_gendata_hash (dst, FALSE));
+
+		if (   gendata
+		    && g_hash_table_size (gendata->hash) > 0) {
+			GHashTableIter iter;
+			GHashTable *h = _gendata_hash (dst, TRUE)->hash;
+			const char *key;
+			GVariant *val;
+
+			g_hash_table_iter_init (&iter, gendata->hash);
+			while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) {
+				g_hash_table_insert (h,
+				                     g_strdup (key),
+				                     g_variant_ref (val));
+			}
+		}
+	}
+
+	if (sett_info->property_infos_len > 0) {
+		gboolean frozen = FALSE;
+		guint i;
+
+		for (i = 0; i < sett_info->property_infos_len; i++) {
+			const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
+
+			if (property_info->param_spec) {
+				if ((property_info->param_spec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)) != G_PARAM_WRITABLE)
+					continue;
+
+				if (!frozen) {
+					g_object_freeze_notify (G_OBJECT (dst));
+					frozen = TRUE;
+				}
+				_gobject_copy_property (G_OBJECT (src),
+				                        G_OBJECT (dst),
+				                        property_info->param_spec->name,
+				                        G_PARAM_SPEC_VALUE_TYPE (property_info->param_spec));
+				continue;
+			}
+		}
+
+		if (frozen)
+			g_object_thaw_notify (G_OBJECT (dst));
+	}
 }
 
 /**
@@ -998,37 +1159,22 @@ NMSetting *
 nm_setting_duplicate (NMSetting *setting)
 {
 	const NMSettInfoSetting *sett_info;
-	GObject *dup;
+	NMSettingClass *klass;
+	NMSetting *dst;
 
 	g_return_val_if_fail (NM_IS_SETTING (setting), NULL);
 
-	dup = g_object_new (G_OBJECT_TYPE (setting), NULL);
+	klass = NM_SETTING_GET_CLASS (setting);
+	nm_assert (NM_IS_SETTING_CLASS (klass));
+	nm_assert (klass->duplicate_copy_properties);
 
-	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+	dst = g_object_new (G_TYPE_FROM_CLASS (klass), NULL);
 
-	if (sett_info->detail.gendata_info) {
-		GenData *gendata = _gendata_hash (setting, FALSE);
+	sett_info = _nm_setting_class_get_sett_info (klass);
+	nm_assert (sett_info);
 
-		if (   gendata
-		    && g_hash_table_size (gendata->hash) > 0) {
-			GHashTableIter iter;
-			GHashTable *h = _gendata_hash (NM_SETTING (dup), TRUE)->hash;
-			const char *key;
-			GVariant *val;
-
-			g_hash_table_iter_init (&iter, gendata->hash);
-			while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &val)) {
-				g_hash_table_insert (h,
-				                     g_strdup (key),
-				                     g_variant_ref (val));
-			}
-		}
-	} else {
-		g_object_freeze_notify (dup);
-		nm_setting_enumerate_values (setting, duplicate_setting, dup);
-		g_object_thaw_notify (dup);
-	}
-	return NM_SETTING (dup);
+	klass->duplicate_copy_properties (sett_info, setting, dst);
+	return dst;
 }
 
 /**
@@ -1136,58 +1282,148 @@ _nm_setting_verify_secret_string (const char *str,
 	return TRUE;
 }
 
-static gboolean
-compare_property (NMSetting *setting,
+gboolean
+_nm_setting_should_compare_secret_property (NMSetting *setting,
+                                            NMSetting *other,
+                                            const char *secret_name,
+                                            NMSettingCompareFlags flags)
+{
+	NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+	NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+
+	nm_assert (NM_IS_SETTING (setting));
+	nm_assert (!other || G_OBJECT_TYPE (setting) == G_OBJECT_TYPE (other));
+
+	/* secret_name must be a valid secret for @setting. */
+	nm_assert (nm_setting_get_secret_flags (setting, secret_name, NULL, NULL));
+
+	if (!NM_FLAGS_ANY (flags,   NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS
+	                          | NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS))
+		return TRUE;
+
+	nm_setting_get_secret_flags (setting, secret_name, &a_secret_flags, NULL);
+	if (other) {
+		if (!nm_setting_get_secret_flags (other, secret_name, &b_secret_flags, NULL)) {
+			/* secret-name may not be a valid secret for @other. That is fine, we ignore that
+			 * and treat @b_secret_flags as NM_SETTING_SECRET_FLAG_NONE.
+			 *
+			 * This can happen with VPN secrets, where the caller knows that @secret_name
+			 * is a secret for setting, but it may not be a secret for @other. Accept that.
+			 *
+			 * Mark @other as missing. */
+			other = NULL;
+		}
+	}
+
+	/* when @setting has the secret-flags that should be ignored,
+	 * we skip the comparisong if:
+	 *
+	 *   - @other is not present,
+	 *   - @other does not have a secret named @secret_name
+	 *   - @other also has the secret flat to be ignored.
+	 *
+	 * This makes the check symmetric (aside the fact that @setting must
+	 * have the secret while @other may not -- which is asymmetric). */
+	if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
+	    && NM_FLAGS_HAS (a_secret_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED)
+	    && (   !other
+	        || NM_FLAGS_HAS (b_secret_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED)))
+		return FALSE;
+
+	if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
+	    && NM_FLAGS_HAS (a_secret_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED)
+	    && (   !other
+	        || NM_FLAGS_HAS (b_secret_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED)))
+		return FALSE;
+
+	return TRUE;
+}
+
+static NMTernary
+compare_property (const NMSettInfoSetting *sett_info,
+                  guint property_idx,
+                  NMSetting *setting,
                   NMSetting *other,
-                  const GParamSpec *prop_spec,
                   NMSettingCompareFlags flags)
 {
-	const NMSettInfoProperty *property;
-	GVariant *value1, *value2;
-	int cmp;
+	const NMSettInfoProperty *property_info = &sett_info->property_infos[property_idx];
+	const GParamSpec *param_spec = property_info->param_spec;
 
-	/* Handle compare flags */
-	if (prop_spec->flags & NM_SETTING_PARAM_SECRET) {
-		NMSettingSecretFlags a_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-		NMSettingSecretFlags b_secret_flags = NM_SETTING_SECRET_FLAG_NONE;
+	if (!param_spec)
+		return NM_TERNARY_DEFAULT;
 
-		g_return_val_if_fail (!NM_IS_SETTING_VPN (setting), FALSE);
+	if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_FUZZY)
+	    && NM_FLAGS_ANY (param_spec->flags, NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET))
+		return NM_TERNARY_DEFAULT;
 
-		if (!nm_setting_get_secret_flags (setting, prop_spec->name, &a_secret_flags, NULL))
-			g_return_val_if_reached (FALSE);
-		if (!nm_setting_get_secret_flags (other, prop_spec->name, &b_secret_flags, NULL))
-			g_return_val_if_reached (FALSE);
+	if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)
+	    && !NM_FLAGS_HAS (param_spec->flags, NM_SETTING_PARAM_INFERRABLE))
+		return NM_TERNARY_DEFAULT;
 
-		/* If the secret flags aren't the same the settings aren't the same */
-		if (a_secret_flags != b_secret_flags)
-			return FALSE;
+	if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY)
+	    && NM_FLAGS_HAS (param_spec->flags, NM_SETTING_PARAM_REAPPLY_IMMEDIATELY))
+		return NM_TERNARY_DEFAULT;
 
-		/* Check for various secret flags that might cause us to ignore comparing
-		 * this property.
-		 */
-		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
-		    && (a_secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
-			return TRUE;
+	if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
+	    && NM_FLAGS_HAS (param_spec->flags, NM_SETTING_PARAM_SECRET))
+		return NM_TERNARY_DEFAULT;
 
-		if (   (flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
-		    && (a_secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
-			return TRUE;
-	}
+	if (nm_streq (param_spec->name, NM_SETTING_NAME))
+		return NM_TERNARY_DEFAULT;
+
+	if (   NM_FLAGS_HAS (param_spec->flags, NM_SETTING_PARAM_SECRET)
+	    && !_nm_setting_should_compare_secret_property (setting,
+	                                                    other,
+	                                                    param_spec->name,
+	                                                    flags))
+		return NM_TERNARY_DEFAULT;
 
-	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), prop_spec->name);
-	g_return_val_if_fail (property != NULL, FALSE);
+	if (other) {
+		gs_unref_variant GVariant *value1  = NULL;
+		gs_unref_variant GVariant *value2  = NULL;
 
-	value1 = get_property_for_dbus (setting, property, TRUE);
-	value2 = get_property_for_dbus (other, property, TRUE);
+		value1 = get_property_for_dbus (setting, property_info, TRUE);
+		value2 = get_property_for_dbus (other, property_info, TRUE);
 
-	cmp = nm_property_compare (value1, value2);
+		if (nm_property_compare (value1, value2) != 0)
+			return NM_TERNARY_FALSE;
+	}
 
-	if (value1)
-		g_variant_unref (value1);
-	if (value2)
-		g_variant_unref (value2);
+	return NM_TERNARY_TRUE;
+}
 
-	return cmp == 0;
+static NMTernary
+_compare_property (const NMSettInfoSetting *sett_info,
+                   guint property_idx,
+                   NMSetting *setting,
+                   NMSetting *other,
+                   NMSettingCompareFlags flags)
+{
+	NMTernary compare_result;
+
+	nm_assert (sett_info);
+	nm_assert (NM_IS_SETTING_CLASS (sett_info->setting_class));
+	nm_assert (property_idx < sett_info->property_infos_len);
+	nm_assert (NM_SETTING_GET_CLASS (setting) == sett_info->setting_class);
+	nm_assert (!other || NM_SETTING_GET_CLASS (other) == sett_info->setting_class);
+
+	compare_result = NM_SETTING_GET_CLASS (setting)->compare_property (sett_info,
+	                                                                   property_idx,
+	                                                                   setting,
+	                                                                   other,
+	                                                                   flags);
+
+	nm_assert (NM_IN_SET (compare_result, NM_TERNARY_DEFAULT,
+	                                      NM_TERNARY_FALSE,
+	                                      NM_TERNARY_TRUE));
+
+	/* check that the inferable flag and the GObject property flag corresponds. */
+	nm_assert (   !NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)
+	           || !sett_info->property_infos[property_idx].param_spec
+	           || NM_FLAGS_HAS (sett_info->property_infos[property_idx].param_spec->flags, NM_SETTING_PARAM_INFERRABLE)
+	           || compare_result == NM_TERNARY_DEFAULT);
+
+	return compare_result;
 }
 
 /**
@@ -1208,9 +1444,6 @@ nm_setting_compare (NMSetting *a,
                     NMSettingCompareFlags flags)
 {
 	const NMSettInfoSetting *sett_info;
-	GParamSpec **property_specs;
-	guint n_property_specs;
-	int same = TRUE;
 	guint i;
 
 	g_return_val_if_fail (NM_IS_SETTING (a), FALSE);
@@ -1220,7 +1453,7 @@ nm_setting_compare (NMSetting *a,
 	if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
 		return FALSE;
 
-	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (a));
+	sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (a));
 
 	if (sett_info->detail.gendata_info) {
 		GenData *a_gendata = _gendata_hash (a, FALSE);
@@ -1232,88 +1465,11 @@ nm_setting_compare (NMSetting *a,
 		                                  g_variant_equal);
 	}
 
-	/* And now all properties */
-	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
-	for (i = 0; i < n_property_specs && same; i++) {
-		GParamSpec *prop_spec = property_specs[i];
-
-		/* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */
-		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_FUZZY)
-		    && !NM_FLAGS_ANY (prop_spec->flags, NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET))
-			continue;
-
-		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)
-		    && !NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_INFERRABLE))
-			continue;
-
-		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY)
-		    && NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_REAPPLY_IMMEDIATELY))
-			continue;
-
-		if (   NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
-		    && NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_SECRET))
-			continue;
-
-		same = NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
-	}
-	g_free (property_specs);
-
-	return same;
-}
-
-static inline gboolean
-should_compare_prop (NMSetting *setting,
-                     const char *prop_name,
-                     NMSettingCompareFlags comp_flags,
-                     GParamFlags prop_flags)
-{
-	/* Fuzzy compare ignores secrets and properties defined with the FUZZY_IGNORE flag */
-	if (   (comp_flags & NM_SETTING_COMPARE_FLAG_FUZZY)
-	    && (prop_flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
-		return FALSE;
-
-	if ((comp_flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_flags & NM_SETTING_PARAM_INFERRABLE))
-		return FALSE;
-
-	if ((comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_REAPPLY_IMMEDIATELY) && !(prop_flags & NM_SETTING_PARAM_REAPPLY_IMMEDIATELY))
-		return FALSE;
-
-	if (prop_flags & NM_SETTING_PARAM_SECRET) {
-		NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
-
-		if (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
-			return FALSE;
-
-		if (   NM_IS_SETTING_VPN (setting)
-		    && g_strcmp0 (prop_name, NM_SETTING_VPN_SECRETS) == 0) {
-			/* FIXME: NMSettingVPN:NM_SETTING_VPN_SECRETS has NM_SETTING_PARAM_SECRET.
-			 * nm_setting_get_secret_flags() quite possibly fails, but it might succeed if the
-			 * setting accidently uses a key "secrets". */
-			return TRUE;
-		}
-
-		if (!nm_setting_get_secret_flags (setting, prop_name, &secret_flags, NULL))
-			g_return_val_if_reached (FALSE);
-
-		if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS)
-		    && (secret_flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED))
-			return FALSE;
-
-		if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
-		    && (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
+	for (i = 0; i < sett_info->property_infos_len; i++) {
+		if (_compare_property (sett_info, i, a, b, flags) == NM_TERNARY_FALSE)
 			return FALSE;
 	}
 
-	if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_ID)
-	    && NM_IS_SETTING_CONNECTION (setting)
-	    && !strcmp (prop_name, NM_SETTING_CONNECTION_ID))
-		return FALSE;
-
-	if (   (comp_flags & NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP)
-	    && NM_IS_SETTING_CONNECTION (setting)
-	    && !strcmp (prop_name, NM_SETTING_CONNECTION_TIMESTAMP))
-		return FALSE;
-
 	return TRUE;
 }
 
@@ -1404,7 +1560,7 @@ nm_setting_diff (NMSetting *a,
 		results_created = TRUE;
 	}
 
-	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (a));
+	sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (a));
 
 	if (sett_info->detail.gendata_info) {
 		const char *key;
@@ -1447,71 +1603,93 @@ nm_setting_diff (NMSetting *a,
 			}
 		}
 	} else {
-		gs_free GParamSpec **property_specs = NULL;
-		guint n_property_specs;
-
-		property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a), &n_property_specs);
-
-		for (i = 0; i < n_property_specs; i++) {
-			GParamSpec *prop_spec = property_specs[i];
+		for (i = 0; i < sett_info->property_infos_len; i++) {
 			NMSettingDiffResult r = NM_SETTING_DIFF_RESULT_UNKNOWN;
+			const NMSettInfoProperty *property_info;
+			NMTernary compare_result;
+			GParamSpec *prop_spec;
 
-			/* Handle compare flags */
-			if (!should_compare_prop (a, prop_spec->name, flags, prop_spec->flags))
-				continue;
-			if (strcmp (prop_spec->name, NM_SETTING_NAME) == 0)
+			compare_result = _compare_property (sett_info, i, a, b, flags);
+			if (compare_result == NM_TERNARY_DEFAULT)
 				continue;
 
-			compared_any = TRUE;
-
-			if (b) {
-				gboolean different;
-
-				different = !NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
-				if (different) {
-					gboolean a_is_default, b_is_default;
-					GValue value = G_VALUE_INIT;
+			if (   NM_FLAGS_ANY (flags,   NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS
+			                            | NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
+			    && b
+			    && compare_result == NM_TERNARY_FALSE) {
+				/* we have setting @b and the property is not the same. But we also are instructed
+				 * to ignore secrets based on the flags.
+				 *
+				 * Note that compare_property() called with two settings will ignore secrets
+				 * based on the flags, but it will do so if *both* settings have the flag we
+				 * look for. So that is symmetric behavior and good.
+				 *
+				 * But for the purpose of diff(), we do a asymmetric comparison because and
+				 * we want to skip testing the property if setting @a alone indicates to do
+				 * so.
+				 *
+				 * We need to double-check whether the property should be ignored by
+				 * looking at @a alone. */
+				if (_compare_property (sett_info, i, a, NULL, flags) == NM_TERNARY_DEFAULT)
+					continue;
+			}
 
-					g_value_init (&value, prop_spec->value_type);
-					g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
-					a_is_default = g_param_value_defaults (prop_spec, &value);
+			compared_any = TRUE;
 
-					g_value_reset (&value);
-					g_object_get_property (G_OBJECT (b), prop_spec->name, &value);
-					b_is_default = g_param_value_defaults (prop_spec, &value);
+			property_info = &sett_info->property_infos[i];
+			prop_spec = property_info->param_spec;
 
-					g_value_unset (&value);
-					if ((flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT) == 0) {
-						if (!a_is_default)
-							r |= a_result;
-						if (!b_is_default)
-							r |= b_result;
-					} else {
+			if (b) {
+				if (compare_result == NM_TERNARY_FALSE) {
+					if (prop_spec) {
+						gboolean a_is_default, b_is_default;
+						GValue value = G_VALUE_INIT;
+
+						g_value_init (&value, prop_spec->value_type);
+						g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
+						a_is_default = g_param_value_defaults (prop_spec, &value);
+
+						g_value_reset (&value);
+						g_object_get_property (G_OBJECT (b), prop_spec->name, &value);
+						b_is_default = g_param_value_defaults (prop_spec, &value);
+
+						g_value_unset (&value);
+						if (!NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT)) {
+							if (!a_is_default)
+								r |= a_result;
+							if (!b_is_default)
+								r |= b_result;
+						} else {
+							r |= a_result | b_result;
+							if (a_is_default)
+								r |= a_result_default;
+							if (b_is_default)
+								r |= b_result_default;
+						}
+					} else
 						r |= a_result | b_result;
-						if (a_is_default)
-							r |= a_result_default;
-						if (b_is_default)
-							r |= b_result_default;
-					}
 				}
 			} else if ((flags & (NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT)) == 0)
 				r = a_result;  /* only in A */
 			else {
-				GValue value = G_VALUE_INIT;
+				if (prop_spec) {
+					GValue value = G_VALUE_INIT;
 
-				g_value_init (&value, prop_spec->value_type);
-				g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
-				if (!g_param_value_defaults (prop_spec, &value))
-					r |= a_result;
-				else if (flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT)
-					r |= a_result | a_result_default;
+					g_value_init (&value, prop_spec->value_type);
+					g_object_get_property (G_OBJECT (a), prop_spec->name, &value);
+					if (!g_param_value_defaults (prop_spec, &value))
+						r |= a_result;
+					else if (flags & NM_SETTING_COMPARE_FLAG_DIFF_RESULT_WITH_DEFAULT)
+						r |= a_result | a_result_default;
 
-				g_value_unset (&value);
+					g_value_unset (&value);
+				} else
+					r |= a_result;
 			}
 
 			if (r != NM_SETTING_DIFF_RESULT_UNKNOWN) {
 				diff_found = TRUE;
-				_setting_diff_add_result (*results, prop_spec->name, r);
+				_setting_diff_add_result (*results, property_info->name, r);
 			}
 		}
 	}
@@ -1540,32 +1718,26 @@ nm_setting_diff (NMSetting *a,
 	}
 }
 
-#define CMP_AND_RETURN(n_a, n_b, name) \
-	G_STMT_START { \
-		gboolean _is = (strcmp (n_a, ""name) == 0); \
-		\
-		if (_is || (strcmp (n_b, ""name) == 0)) \
-			return _is ? -1 : 1; \
-	} G_STMT_END
+static void
+enumerate_values (const NMSettInfoProperty *property_info,
+                  NMSetting *setting,
+                  NMSettingValueIterFn func,
+                  gpointer user_data)
+{
+	GValue value = G_VALUE_INIT;
 
-static int
-_enumerate_values_sort (GParamSpec **p_a, GParamSpec **p_b, GType *p_type)
-{
-	const char *n_a = (*p_a)->name;
-	const char *n_b = (*p_b)->name;
-	int c = strcmp (n_a, n_b);
-
-	if (c) {
-		if (*p_type == NM_TYPE_SETTING_CONNECTION) {
-			/* for [connection], report first id, uuid, type in that order. */
-			CMP_AND_RETURN (n_a, n_b, NM_SETTING_CONNECTION_ID);
-			CMP_AND_RETURN (n_a, n_b, NM_SETTING_CONNECTION_UUID);
-			CMP_AND_RETURN (n_a, n_b, NM_SETTING_CONNECTION_TYPE);
-		}
-	}
-	return c;
+	if (!property_info->param_spec)
+		return;
+
+	g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (property_info->param_spec));
+	g_object_get_property (G_OBJECT (setting), property_info->param_spec->name, &value);
+	func (setting,
+	      property_info->param_spec->name,
+	      &value,
+	      property_info->param_spec->flags,
+	      user_data);
+	g_value_unset (&value);
 }
-#undef CMP_AND_RETURN
 
 /**
  * nm_setting_enumerate_values:
@@ -1582,18 +1754,16 @@ nm_setting_enumerate_values (NMSetting *setting,
                              gpointer user_data)
 {
 	const NMSettInfoSetting *sett_info;
-	GParamSpec **property_specs;
-	guint n_properties;
 	guint i;
-	GType type;
 
 	g_return_if_fail (NM_IS_SETTING (setting));
 	g_return_if_fail (func != NULL);
 
-	sett_info = _nm_sett_info_setting_get (NM_SETTING_GET_CLASS (setting));
+	sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (setting));
 
 	if (sett_info->detail.gendata_info) {
 		const char *const*names;
+		guint n_properties;
 
 		/* the properties of this setting are not real GObject properties.
 		 * Hence, this API makes little sense (or does it?). Still, call
@@ -1623,100 +1793,138 @@ nm_setting_enumerate_values (NMSetting *setting,
 		return;
 	}
 
-	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_properties);
-
-	/* sort the properties. This has an effect on the order in which keyfile
-	 * prints them. */
-	type = G_OBJECT_TYPE (setting);
-	g_qsort_with_data (property_specs, n_properties, sizeof (gpointer),
-	                   (GCompareDataFunc) _enumerate_values_sort, &type);
-
-	for (i = 0; i < n_properties; i++) {
-		GParamSpec *prop_spec = property_specs[i];
-		GValue value = G_VALUE_INIT;
-
-		g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop_spec));
-		g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
-		func (setting, prop_spec->name, &value, prop_spec->flags, user_data);
-		g_value_unset (&value);
+	for (i = 0; i < sett_info->property_infos_len; i++) {
+		NM_SETTING_GET_CLASS (setting)->enumerate_values (_nm_sett_info_property_info_get_sorted (sett_info, i),
+		                                                  setting,
+		                                                  func,
+		                                                  user_data);
 	}
-
-	g_free (property_specs);
 }
 
-/**
- * _nm_setting_clear_secrets:
- * @setting: the #NMSetting
- *
- * Resets and clears any secrets in the setting.  Secrets should be added to the
- * setting only when needed, and cleared immediately after use to prevent
- * leakage of information.
- *
- * Returns: %TRUE if the setting changed at all
- **/
-gboolean
-_nm_setting_clear_secrets (NMSetting *setting)
+static gboolean
+aggregate (NMSetting *setting,
+           int type_i,
+           gpointer arg)
 {
-	gs_free GParamSpec **property_specs = NULL;
-	guint n_property_specs;
+	NMConnectionAggregateType type = type_i;
+	const NMSettInfoSetting *sett_info;
 	guint i;
-	gboolean changed = FALSE;
 
-	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
+	nm_assert (NM_IN_SET (type, NM_CONNECTION_AGGREGATE_ANY_SECRETS,
+	                            NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS));
 
-	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
-	for (i = 0; i < n_property_specs; i++) {
-		GParamSpec *prop_spec = property_specs[i];
+	sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (setting));
+	for (i = 0; i < sett_info->property_infos_len; i++) {
+		const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
+		GParamSpec *prop_spec = property_info->param_spec;
+		nm_auto_unset_gvalue GValue value = G_VALUE_INIT;
+		NMSettingSecretFlags secret_flags;
+
+		if (   !prop_spec
+		    || !NM_FLAGS_HAS (prop_spec->flags, NM_SETTING_PARAM_SECRET)) {
+			nm_assert (!nm_setting_get_secret_flags (setting, property_info->name, NULL, NULL));
+			continue;
+		}
 
-		if (prop_spec->flags & NM_SETTING_PARAM_SECRET) {
-			GValue value = G_VALUE_INIT;
+		/* for the moment, all aggregate types only care about secrets. */
+		nm_assert (nm_setting_get_secret_flags (setting, property_info->name, NULL, NULL));
 
-			g_value_init (&value, prop_spec->value_type);
+		switch (type) {
+
+		case NM_CONNECTION_AGGREGATE_ANY_SECRETS:
+			g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (prop_spec));
 			g_object_get_property (G_OBJECT (setting), prop_spec->name, &value);
 			if (!g_param_value_defaults (prop_spec, &value)) {
-				g_param_value_set_default (prop_spec, &value);
-				g_object_set_property (G_OBJECT (setting), prop_spec->name, &value);
-				changed = TRUE;
+				*((gboolean *) arg) = TRUE;
+				return TRUE;
+			}
+			break;
+
+		case NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS:
+			if (!nm_setting_get_secret_flags (setting, prop_spec->name, &secret_flags, NULL))
+				nm_assert_not_reached ();
+			if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) {
+				*((gboolean *) arg) = TRUE;
+				return TRUE;
 			}
-			g_value_unset (&value);
+			break;
+
 		}
 	}
-	return changed;
+
+	return FALSE;
+}
+
+/**
+ * _nm_setting_aggregate:
+ * @setting: the #NMSetting to aggregate.
+ * @type: the #NMConnectionAggregateType aggregate type.
+ * @arg: the in/out arguments for aggregation. They depend on @type.
+ *
+ * This is the implementation detail of _nm_connection_aggregate(). It
+ * makes no sense to call this function directly outside of _nm_connection_aggregate().
+ *
+ * Returns: %TRUE if afterwards the aggregation is complete. That means,
+ *   the only caller _nm_connection_aggregate() will not visit other settings
+ *   after a setting returns %TRUE (indicating that there is nothing further
+ *   to aggregate). Note that is very different from the boolean return
+ *   argument of _nm_connection_aggregate(), which serves a different purpose.
+ */
+gboolean
+_nm_setting_aggregate (NMSetting *setting,
+                       NMConnectionAggregateType type,
+                       gpointer arg)
+{
+	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
+	g_return_val_if_fail (arg, FALSE);
+	g_return_val_if_fail (NM_IN_SET (type, NM_CONNECTION_AGGREGATE_ANY_SECRETS,
+	                                       NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS),
+	                      FALSE);
+
+	return NM_SETTING_GET_CLASS (setting)->aggregate (setting, type, arg);
 }
 
 static gboolean
-clear_secrets_with_flags (NMSetting *setting,
-                          GParamSpec *pspec,
-                          NMSettingClearSecretsWithFlagsFn func,
-                          gpointer user_data)
+clear_secrets (const NMSettInfoSetting *sett_info,
+               guint property_idx,
+               NMSetting *setting,
+               NMSettingClearSecretsWithFlagsFn func,
+               gpointer user_data)
 {
 	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
-	gboolean changed = FALSE;
+	GParamSpec *param_spec = sett_info->property_infos[property_idx].param_spec;
 
-	g_return_val_if_fail (!NM_IS_SETTING_VPN (setting), FALSE);
+	if (!param_spec)
+		return FALSE;
 
-	/* Clear the secret if the user function says to do so */
-	if (!nm_setting_get_secret_flags (setting, pspec->name, &flags, NULL))
-		g_return_val_if_reached (FALSE);
+	if (!NM_FLAGS_HAS (param_spec->flags, NM_SETTING_PARAM_SECRET))
+		return FALSE;
+
+	if (func) {
+		if (!nm_setting_get_secret_flags (setting, param_spec->name, &flags, NULL))
+			nm_assert_not_reached ();
+		if (!func (setting, param_spec->name, flags, user_data))
+			return FALSE;
+	} else
+		nm_assert (nm_setting_get_secret_flags (setting, param_spec->name, NULL, NULL));
 
-	if (func (setting, pspec->name, flags, user_data) == TRUE) {
-		GValue value = G_VALUE_INIT;
+	{
+		nm_auto_unset_gvalue GValue value = G_VALUE_INIT;
 
-		g_value_init (&value, pspec->value_type);
-		g_object_get_property (G_OBJECT (setting), pspec->name, &value);
-		if (!g_param_value_defaults (pspec, &value)) {
-			g_param_value_set_default (pspec, &value);
-			g_object_set_property (G_OBJECT (setting), pspec->name, &value);
-			changed = TRUE;
-		}
-		g_value_unset (&value);
+		g_value_init (&value, param_spec->value_type);
+		g_object_get_property (G_OBJECT (setting), param_spec->name, &value);
+		if (g_param_value_defaults (param_spec, &value))
+			return FALSE;
+
+		g_param_value_set_default (param_spec, &value);
+		g_object_set_property (G_OBJECT (setting), param_spec->name, &value);
 	}
 
-	return changed;
+	return TRUE;
 }
 
 /**
- * _nm_setting_clear_secrets_with_flags:
+ * _nm_setting_clear_secrets:
  * @setting: the #NMSetting
  * @func: (scope call): function to be called to determine whether a
  *     specific secret should be cleared or not
@@ -1727,27 +1935,30 @@ clear_secrets_with_flags (NMSetting *setting,
  * Returns: %TRUE if the setting changed at all
  **/
 gboolean
-_nm_setting_clear_secrets_with_flags (NMSetting *setting,
-                                      NMSettingClearSecretsWithFlagsFn func,
-                                      gpointer user_data)
+_nm_setting_clear_secrets (NMSetting *setting,
+                           NMSettingClearSecretsWithFlagsFn func,
+                           gpointer user_data)
 {
-	gs_free GParamSpec **property_specs = NULL;
-	guint n_property_specs;
-	guint i;
+	const NMSettInfoSetting *sett_info;
 	gboolean changed = FALSE;
+	guint i;
+	gboolean (*my_clear_secrets) (const struct _NMSettInfoSetting *sett_info,
+	                              guint property_idx,
+	                              NMSetting *setting,
+	                              NMSettingClearSecretsWithFlagsFn func,
+	                              gpointer user_data);
 
-	g_return_val_if_fail (setting, FALSE);
 	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
-	g_return_val_if_fail (func != NULL, FALSE);
 
-	property_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (setting), &n_property_specs);
-	for (i = 0; i < n_property_specs; i++) {
-		if (property_specs[i]->flags & NM_SETTING_PARAM_SECRET) {
-			changed |= NM_SETTING_GET_CLASS (setting)->clear_secrets_with_flags (setting,
-			                                                                     property_specs[i],
-			                                                                     func,
-			                                                                     user_data);
-		}
+	my_clear_secrets = NM_SETTING_GET_CLASS (setting)->clear_secrets;
+
+	sett_info = _nm_setting_class_get_sett_info (NM_SETTING_GET_CLASS (setting));
+	for (i = 0; i < sett_info->property_infos_len; i++) {
+		changed |= my_clear_secrets (sett_info,
+		                             i,
+		                             setting,
+		                             func,
+		                             user_data);
 	}
 	return changed;
 }
@@ -1786,7 +1997,7 @@ update_one_secret (NMSetting *setting, const char *key, GVariant *value, GError
 	GParamSpec *prop_spec;
 	GValue prop_value = { 0, };
 
-	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), key);
+	property = _nm_setting_class_get_property_info (NM_SETTING_GET_CLASS (setting), key);
 	if (!property) {
 		g_set_error_literal (error,
 		                     NM_CONNECTION_ERROR,
@@ -1856,7 +2067,7 @@ _nm_setting_update_secrets (NMSetting *setting, GVariant *secrets, GError **erro
 		int success;
 
 		success = NM_SETTING_GET_CLASS (setting)->update_one_secret (setting, secret_key, secret_value, &tmp_error);
-		g_assert (!((success == NM_SETTING_UPDATE_SECRET_ERROR) ^ (!!tmp_error)));
+		nm_assert (!((success == NM_SETTING_UPDATE_SECRET_ERROR) ^ (!!tmp_error)));
 
 		g_variant_unref (secret_value);
 
@@ -1872,52 +2083,92 @@ _nm_setting_update_secrets (NMSetting *setting, GVariant *secrets, GError **erro
 	return result;
 }
 
-static gboolean
-is_secret_prop (NMSetting *setting, const char *secret_name, GError **error)
+static void
+for_each_secret (NMSetting *setting,
+                 const char *secret_name,
+                 GVariant *val,
+                 gboolean remove_non_secrets,
+                 _NMConnectionForEachSecretFunc callback,
+                 gpointer callback_data,
+                 GVariantBuilder *setting_builder)
 {
-	const NMSettInfoProperty *property;
-	GParamSpec *pspec;
+	NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
 
-	property = _nm_sett_info_property_get (NM_SETTING_GET_CLASS (setting), secret_name);
-	if (!property) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_PROPERTY_NOT_FOUND,
-		                     _("secret is not set"));
-		g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), secret_name);
-		return FALSE;
+	if (!nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL)) {
+		if (!remove_non_secrets)
+			g_variant_builder_add (setting_builder, "{sv}", secret_name, val);
+		return;
 	}
+	if (callback (secret_flags, callback_data))
+		g_variant_builder_add (setting_builder, "{sv}", secret_name, val);
+}
 
-	pspec = property->param_spec;
-	if (!pspec || !(pspec->flags & NM_SETTING_PARAM_SECRET)) {
-		g_set_error_literal (error,
-		                     NM_CONNECTION_ERROR,
-		                     NM_CONNECTION_ERROR_PROPERTY_NOT_SECRET,
-		                     _("not a secret property"));
-		g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), secret_name);
-		return FALSE;
-	}
+static void
+_set_error_secret_property_not_found (GError **error,
+                                      NMSetting *setting,
+                                      const char *secret_name)
+{
+	g_set_error_literal (error,
+	                     NM_CONNECTION_ERROR,
+	                     NM_CONNECTION_ERROR_PROPERTY_NOT_FOUND,
+	                     _("not a secret property"));
+	g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), secret_name);
+}
 
-	return TRUE;
+gboolean
+_nm_setting_property_is_regular_secret (NMSetting *setting,
+                                        const char *secret_name)
+{
+	const NMSettInfoProperty *property;
+
+	nm_assert (NM_IS_SETTING (setting));
+	nm_assert (secret_name);
+
+	property = _nm_setting_class_get_property_info (NM_SETTING_GET_CLASS (setting), secret_name);
+	return    property
+	       && property->param_spec
+	       && NM_FLAGS_HAS (property->param_spec->flags, NM_SETTING_PARAM_SECRET);
+}
+
+gboolean
+_nm_setting_property_is_regular_secret_flags (NMSetting *setting,
+                                              const char *secret_flags_name)
+{
+	const NMSettInfoProperty *property;
+
+	nm_assert (NM_IS_SETTING (setting));
+	nm_assert (secret_flags_name);
+
+	property = _nm_setting_class_get_property_info (NM_SETTING_GET_CLASS (setting), secret_flags_name);
+	return    property
+	       && property->param_spec
+	       && !NM_FLAGS_HAS (property->param_spec->flags, NM_SETTING_PARAM_SECRET)
+	       && G_PARAM_SPEC_VALUE_TYPE (property->param_spec) == NM_TYPE_SETTING_SECRET_FLAGS;
 }
 
 static gboolean
 get_secret_flags (NMSetting *setting,
                   const char *secret_name,
-                  gboolean verify_secret,
                   NMSettingSecretFlags *out_flags,
                   GError **error)
 {
-	gs_free char *name_to_free = NULL;
-	NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+	gs_free char *secret_flags_name_free = NULL;
+	const char *secret_flags_name;
+	NMSettingSecretFlags flags;
 
-	if (verify_secret && !is_secret_prop (setting, secret_name, error)) {
+	if (!_nm_setting_property_is_regular_secret (setting,
+	                                             secret_name)) {
+		_set_error_secret_property_not_found (error, setting, secret_name);
 		NM_SET_OUT (out_flags, NM_SETTING_SECRET_FLAG_NONE);
 		return FALSE;
 	}
 
+	secret_flags_name = nm_construct_name_a ("%s-flags", secret_name, &secret_flags_name_free);
+
+	nm_assert (_nm_setting_property_is_regular_secret_flags (setting, secret_flags_name));
+
 	g_object_get (G_OBJECT (setting),
-	              nm_construct_name_a ("%s-flags", secret_name, &name_to_free),
+	              secret_flags_name,
 	              &flags,
 	              NULL);
 	NM_SET_OUT (out_flags, flags);
@@ -1946,25 +2197,34 @@ nm_setting_get_secret_flags (NMSetting *setting,
 	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
 	g_return_val_if_fail (secret_name != NULL, FALSE);
 
-	return NM_SETTING_GET_CLASS (setting)->get_secret_flags (setting, secret_name, TRUE, out_flags, error);
+	return NM_SETTING_GET_CLASS (setting)->get_secret_flags (setting, secret_name, out_flags, error);
 }
 
 static gboolean
 set_secret_flags (NMSetting *setting,
                   const char *secret_name,
-                  gboolean verify_secret,
                   NMSettingSecretFlags flags,
                   GError **error)
 {
-	gs_free char *name_to_free = NULL;
+	gs_free char *secret_flags_name_free = NULL;
+	const char *secret_flags_name;
 
-	if (verify_secret)
-		g_return_val_if_fail (is_secret_prop (setting, secret_name, error), FALSE);
+	if (!_nm_setting_property_is_regular_secret (setting,
+	                                             secret_name)) {
+		_set_error_secret_property_not_found (error, setting, secret_name);
+		return FALSE;
+	}
 
-	g_object_set (G_OBJECT (setting),
-	              nm_construct_name_a ("%s-flags", secret_name, &name_to_free),
-	              flags,
-	              NULL);
+	secret_flags_name = nm_construct_name_a ("%s-flags", secret_name, &secret_flags_name_free);
+
+	nm_assert (_nm_setting_property_is_regular_secret_flags (setting, secret_flags_name));
+
+	if (!nm_g_object_set_property_flags (G_OBJECT (setting),
+	                                     secret_flags_name,
+	                                     NM_TYPE_SETTING_SECRET_FLAGS,
+	                                     flags,
+	                                     error))
+		g_return_val_if_reached (FALSE);
 	return TRUE;
 }
 
@@ -1989,9 +2249,9 @@ nm_setting_set_secret_flags (NMSetting *setting,
 {
 	g_return_val_if_fail (NM_IS_SETTING (setting), FALSE);
 	g_return_val_if_fail (secret_name != NULL, FALSE);
-	g_return_val_if_fail (flags <= NM_SETTING_SECRET_FLAGS_ALL, FALSE);
+	g_return_val_if_fail (_nm_setting_secret_flags_valid (flags), FALSE);
 
-	return NM_SETTING_GET_CLASS (setting)->set_secret_flags (setting, secret_name, TRUE, flags, error);
+	return NM_SETTING_GET_CLASS (setting)->set_secret_flags (setting, secret_name, flags, error);
 }
 
 /**
@@ -2018,8 +2278,7 @@ nm_setting_to_string (NMSetting *setting)
 	string = g_string_new (nm_setting_get_name (setting));
 	g_string_append_c (string, '\n');
 
-	variant = _nm_setting_to_dbus (setting, NULL,   NM_CONNECTION_SERIALIZE_ALL
-	                                              | NM_CONNECTION_SERIALIZE_NO_SYNTH);
+	variant = _nm_setting_to_dbus (setting, NULL, NM_CONNECTION_SERIALIZE_ALL);
 
 	g_variant_iter_init (&iter, variant);
 	while ((child = g_variant_iter_next_value (&iter))) {
@@ -2037,9 +2296,11 @@ nm_setting_to_string (NMSetting *setting)
 }
 
 GVariant *
-_nm_setting_get_deprecated_virtual_interface_name (NMSetting *setting,
+_nm_setting_get_deprecated_virtual_interface_name (const NMSettInfoSetting *sett_info,
+                                                   guint property_idx,
                                                    NMConnection *connection,
-                                                   const char *property)
+                                                   NMSetting *setting,
+                                                   NMConnectionSerializationFlags flags)
 {
 	NMSettingConnection *s_con;
 
@@ -2096,7 +2357,7 @@ _nm_setting_gendata_notify (NMSetting *setting,
 
 	gendata = _gendata_hash (setting, FALSE);
 	if (!gendata)
-		return;
+		goto out;
 
 	nm_clear_g_free (&gendata->values);
 
@@ -2106,7 +2367,7 @@ _nm_setting_gendata_notify (NMSetting *setting,
 		nm_clear_g_free (&gendata->names);
 	}
 
-	/* Note, that currently there is now way to notify the subclass when gendata changed.
+	/* Note, currently there is no way to notify the subclass when gendata changed.
 	 * gendata is only changed in two situations:
 	 *   1) from within NMSetting itself, for example when creating a NMSetting instance
 	 *      from keyfile or a D-Bus GVariant.
@@ -2119,6 +2380,9 @@ _nm_setting_gendata_notify (NMSetting *setting,
 	 *
 	 * If we ever need it, then we would need to call a virtual function to notify the subclass
 	 * that gendata changed. */
+
+out:
+	_nm_setting_emit_property_changed (setting);
 }
 
 GVariant *
@@ -2185,7 +2449,7 @@ out_zero:
 /**
  * nm_setting_gendata_get_all_names:
  * @setting: the #NMSetting
- * @out_len: (allow-none): (out):
+ * @out_len: (allow-none) (out):
  *
  * Gives the number of generic data elements and optionally returns all their
  * key names and values. This API is low level access and unless you know what you
@@ -2241,7 +2505,7 @@ nm_setting_gendata_get_all_values (NMSetting *setting)
 
 void
 _nm_setting_gendata_to_gvalue (NMSetting *setting,
-                                GValue *value)
+                               GValue *value)
 {
 	GenData *gendata;
 	GHashTable *new;
@@ -2293,7 +2557,7 @@ _nm_setting_gendata_reset_from_hash (NMSetting *setting,
 	}
 
 	/* let's not bother to find out whether the new hash has any different
-	 * content the the current gendata. Just replace it. */
+	 * content the current gendata. Just replace it. */
 	g_hash_table_remove_all (gendata->hash);
 	if (num > 0) {
 		g_hash_table_iter_init (&iter, new);
@@ -2307,11 +2571,6 @@ _nm_setting_gendata_reset_from_hash (NMSetting *setting,
 /*****************************************************************************/
 
 static void
-nm_setting_init (NMSetting *setting)
-{
-}
-
-static void
 get_property (GObject *object, guint prop_id,
               GValue *value, GParamSpec *pspec)
 {
@@ -2327,6 +2586,13 @@ get_property (GObject *object, guint prop_id,
 	}
 }
 
+/*****************************************************************************/
+
+static void
+nm_setting_init (NMSetting *setting)
+{
+}
+
 static void
 finalize (GObject *object)
 {
@@ -2367,11 +2633,15 @@ nm_setting_class_init (NMSettingClass *setting_class)
 	object_class->get_property = get_property;
 	object_class->finalize     = finalize;
 
-	setting_class->update_one_secret = update_one_secret;
-	setting_class->get_secret_flags = get_secret_flags;
-	setting_class->set_secret_flags = set_secret_flags;
-	setting_class->compare_property = compare_property;
-	setting_class->clear_secrets_with_flags = clear_secrets_with_flags;
+	setting_class->update_one_secret         = update_one_secret;
+	setting_class->get_secret_flags          = get_secret_flags;
+	setting_class->set_secret_flags          = set_secret_flags;
+	setting_class->compare_property          = compare_property;
+	setting_class->clear_secrets             = clear_secrets;
+	setting_class->for_each_secret           = for_each_secret;
+	setting_class->duplicate_copy_properties = duplicate_copy_properties;
+	setting_class->enumerate_values          = enumerate_values;
+	setting_class->aggregate                 = aggregate;
 
 	/**
 	 * NMSetting:name:
@@ -2380,10 +2650,11 @@ nm_setting_class_init (NMSettingClass *setting_class)
 	 * connection.  Each setting type has a name unique to that type, for
 	 * example "ppp" or "802-11-wireless" or "802-3-ethernet".
 	 **/
-	g_object_class_install_property
-		(object_class, PROP_NAME,
-		 g_param_spec_string (NM_SETTING_NAME, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	obj_properties[PROP_NAME] =
+	    g_param_spec_string (NM_SETTING_NAME, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 }
diff --git a/libnm-core/nm-setting.h b/libnm-core/nm-setting.h
index a7a0f81f..fdf4a4c5 100644
--- a/libnm-core/nm-setting.h
+++ b/libnm-core/nm-setting.h
@@ -64,7 +64,7 @@ G_BEGIN_DECLS
  * should be requested from the user each time it is needed
  * @NM_SETTING_SECRET_FLAG_NOT_REQUIRED: in situations where it cannot be
  * automatically determined that the secret is required (some VPNs and PPP
- * providers dont require all secrets) this flag indicates that the specific
+ * providers don't require all secrets) this flag indicates that the specific
  * secret is not required
  *
  * These flags indicate specific behavior related to handling of a secret.  Each
@@ -169,6 +169,27 @@ typedef gboolean (*NMSettingClearSecretsWithFlagsFn) (NMSetting *setting,
                                                       gpointer user_data);
 
 struct _NMMetaSettingInfo;
+struct _NMSettInfoSetting;
+struct _NMSettInfoProperty;
+
+/**
+ * NMSettingValueIterFn:
+ * @setting: The setting for which properties are being iterated, given to
+ * nm_setting_enumerate_values()
+ * @key: The value/property name
+ * @value: The property's value
+ * @flags: The property's flags, like %NM_SETTING_PARAM_SECRET
+ * @user_data: User data passed to nm_setting_enumerate_values()
+ */
+typedef void (*NMSettingValueIterFn) (NMSetting *setting,
+                                      const char *key,
+                                      const GValue *value,
+                                      GParamFlags flags,
+                                      gpointer user_data);
+
+/*< private >*/
+typedef gboolean (*_NMConnectionForEachSecretFunc) (NMSettingSecretFlags flags,
+                                                    gpointer user_data);
 
 typedef struct {
 	GObjectClass parent;
@@ -191,48 +212,65 @@ typedef struct {
 
 	gboolean    (*get_secret_flags)  (NMSetting  *setting,
 	                                  const char *secret_name,
-	                                  gboolean verify_secret,
 	                                  NMSettingSecretFlags *out_flags,
 	                                  GError **error);
 
 	gboolean    (*set_secret_flags)  (NMSetting  *setting,
 	                                  const char *secret_name,
-	                                  gboolean verify_secret,
 	                                  NMSettingSecretFlags flags,
 	                                  GError **error);
 
-	gboolean    (*clear_secrets_with_flags) (NMSetting *setting,
-	                                         GParamSpec *pspec,
-	                                         NMSettingClearSecretsWithFlagsFn func,
-	                                         gpointer user_data);
+	/*< private >*/
+	gboolean    (*clear_secrets) (const struct _NMSettInfoSetting *sett_info,
+	                              guint property_idx,
+	                              NMSetting *setting,
+	                              NMSettingClearSecretsWithFlagsFn func,
+	                              gpointer user_data);
+
+	/* compare_property() returns a ternary, where DEFAULT means that the property should not
+	 * be compared due to the compare @flags. A TRUE/FALSE result means that the property is
+	 * equal/not-equal.
+	 *
+	 * @other may be %NULL, in which case the function only determines whether
+	 * the setting should be compared (TRUE) or not (DEFAULT). */
+	/*< private >*/
+	NMTernary  (*compare_property)  (const struct _NMSettInfoSetting *sett_info,
+	                                 guint property_idx,
+	                                 NMSetting *setting,
+	                                 NMSetting *other,
+	                                 NMSettingCompareFlags flags);
 
-	/* Returns TRUE if the given property contains the same value in both settings */
-	gboolean    (*compare_property)  (NMSetting *setting,
-	                                  NMSetting *other,
-	                                  const GParamSpec *prop_spec,
-	                                  NMSettingCompareFlags flags);
+	/*< private >*/
+	void (*duplicate_copy_properties) (const struct _NMSettInfoSetting *sett_info,
+	                                   NMSetting *src,
+	                                   NMSetting *dst);
 
 	/*< private >*/
-	const struct _NMMetaSettingInfo *setting_info;
+	void (*enumerate_values) (const struct _NMSettInfoProperty *property_info,
+	                          NMSetting *setting,
+	                          NMSettingValueIterFn func,
+	                          gpointer user_data);
 
 	/*< private >*/
-	gpointer padding[6];
-} NMSettingClass;
+	gboolean (*aggregate) (NMSetting *setting,
+	                       int type_i,
+	                       gpointer arg);
 
-/**
- * NMSettingValueIterFn:
- * @setting: The setting for which properties are being iterated, given to
- * nm_setting_enumerate_values()
- * @key: The value/property name
- * @value: The property's value
- * @flags: The property's flags, like %NM_SETTING_PARAM_SECRET
- * @user_data: User data passed to nm_setting_enumerate_values()
- */
-typedef void (*NMSettingValueIterFn) (NMSetting *setting,
-                                      const char *key,
-                                      const GValue *value,
-                                      GParamFlags flags,
-                                      gpointer user_data);
+	/*< private >*/
+	void (*for_each_secret) (NMSetting *setting,
+	                         const char *secret_name,
+	                         GVariant *val,
+	                         gboolean remove_non_secrets,
+	                         _NMConnectionForEachSecretFunc callback,
+	                         gpointer callback_data,
+	                         GVariantBuilder *setting_builder);
+
+	/*< private >*/
+	gpointer padding[2];
+
+	/*< private >*/
+	const struct _NMMetaSettingInfo *setting_info;
+} NMSettingClass;
 
 GType nm_setting_get_type (void);
 
@@ -272,7 +310,7 @@ typedef enum {
 	NM_SETTING_DIFF_RESULT_IN_A =    0x00000001,
 	NM_SETTING_DIFF_RESULT_IN_B =    0x00000002,
 	NM_SETTING_DIFF_RESULT_IN_A_DEFAULT = 0x00000004,
-	NM_SETTING_DIFF_RESULT_IN_B_DEFAULT = 0x00000004,
+	NM_SETTING_DIFF_RESULT_IN_B_DEFAULT = 0x00000008,
 } NMSettingDiffResult;
 
 gboolean    nm_setting_diff          (NMSetting *a,
diff --git a/libnm-core/nm-simple-connection.c b/libnm-core/nm-simple-connection.c
index 9ccdbb21..55e8229f 100644
--- a/libnm-core/nm-simple-connection.c
+++ b/libnm-core/nm-simple-connection.c
@@ -22,6 +22,7 @@
 #include "nm-default.h"
 
 #include "nm-simple-connection.h"
+
 #include "nm-setting-private.h"
 
 /**
@@ -32,12 +33,16 @@
  * but might be used in the process of creating a new one.
  **/
 
+/*****************************************************************************/
+
 static void nm_simple_connection_interface_init (NMConnectionInterface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (NMSimpleConnection, nm_simple_connection, G_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (NM_TYPE_CONNECTION, nm_simple_connection_interface_init);
                          )
 
+/*****************************************************************************/
+
 static void
 nm_simple_connection_init (NMSimpleConnection *self)
 {
diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h
index b886730a..46af26ed 100644
--- a/libnm-core/nm-utils-private.h
+++ b/libnm-core/nm-utils-private.h
@@ -41,6 +41,18 @@ struct _NMVariantAttributeSpec {
 gboolean    _nm_utils_string_slist_validate (GSList *list,
                                              const char **valid_values);
 
+gboolean _nm_utils_secret_flags_validate (NMSettingSecretFlags secret_flags,
+                                          const char *setting_name,
+                                          const char *property_name,
+                                          NMSettingSecretFlags disallowed_flags,
+                                          GError **error);
+
+gboolean _nm_utils_wps_method_validate (NMSettingWirelessSecurityWpsMethod wps_method,
+                                        const char *setting_name,
+                                        const char *property_name,
+                                        gboolean wps_required,
+                                        GError **error);
+
 /* D-Bus transform funcs */
 
 GVariant   *_nm_utils_hwaddr_cloned_get (NMSetting     *setting,
@@ -56,9 +68,11 @@ gboolean    _nm_utils_hwaddr_cloned_not_set (NMSetting *setting,
                                              const char    *property,
                                              NMSettingParseFlags parse_flags,
                                              GError       **error);
-GVariant *  _nm_utils_hwaddr_cloned_data_synth (NMSetting *setting,
+GVariant *  _nm_utils_hwaddr_cloned_data_synth (const NMSettInfoSetting *sett_info,
+                                                guint property_idx,
                                                 NMConnection *connection,
-                                                const char *property);
+                                                NMSetting *setting,
+                                                NMConnectionSerializationFlags flags);
 gboolean    _nm_utils_hwaddr_cloned_data_set (NMSetting *setting,
                                               GVariant *connection_dict,
                                               const char *property,
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index f75276c7..d276cfe6 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -23,8 +23,6 @@
 
 #include "nm-utils.h"
 
-#include <string.h>
-#include <errno.h>
 #include <stdlib.h>
 #include <netinet/ether.h>
 #include <arpa/inet.h>
@@ -40,6 +38,8 @@
 #endif
 
 #include "nm-utils/nm-enum-utils.h"
+#include "nm-utils/nm-secret-utils.h"
+#include "systemd/nm-sd-utils-shared.h"
 #include "nm-common-macros.h"
 #include "nm-utils-private.h"
 #include "nm-setting-private.h"
@@ -61,13 +61,284 @@
  * access points and devices, among other things.
  */
 
+/*****************************************************************************/
+
+struct _NMSockAddrEndpoint {
+	const char *host;
+	guint16 port;
+	guint refcount;
+	char endpoint[];
+};
+
+static gboolean
+NM_IS_SOCK_ADDR_ENDPOINT (const NMSockAddrEndpoint *self)
+{
+	return self && self->refcount > 0;
+}
+
+static const char *
+_parse_endpoint (char *str,
+                 guint16 *out_port)
+{
+	char *s;
+	const char *s_port;
+	gint16 port;
+
+	/* Like
+	 * - https://git.zx2c4.com/WireGuard/tree/src/tools/config.c?id=5e99a6d43fe2351adf36c786f5ea2086a8fe7ab8#n192
+	 * - https://github.com/systemd/systemd/blob/911649fdd43f3a9158b847947724a772a5a45c34/src/network/netdev/wireguard.c#L614
+	 */
+
+	g_strstrip (str);
+
+	if (!str[0])
+		return NULL;
+
+	if (str[0] == '[') {
+		str++;
+		s = strchr (str, ']');
+		if (!s)
+			return NULL;
+		if (s == str)
+			return NULL;
+		if (s[1] != ':')
+			return NULL;
+		if (!s[2])
+			return NULL;
+		*s = '\0';
+		s_port = &s[2];
+	} else {
+		s = strrchr (str, ':');
+		if (!s)
+			return NULL;
+		if (s == str)
+			return NULL;
+		if (!s[1])
+			return NULL;
+		*s = '\0';
+		s_port = &s[1];
+	}
+
+	if (!NM_STRCHAR_ALL (s_port, ch, (ch >= '0' && ch <= '9')))
+		return NULL;
+
+	port = _nm_utils_ascii_str_to_int64 (s_port, 10, 1, G_MAXUINT16, 0);
+	if (port == 0)
+		return NULL;
+
+	*out_port = port;
+	return str;
+}
+
+/**
+ * nm_sock_addr_endpoint_new:
+ * @endpoint: the endpoint string.
+ *
+ * This function cannot fail, even if the @endpoint is invalid.
+ * The reason is to allow NMSockAddrEndpoint also to be used
+ * for tracking invalid endpoints. Use nm_sock_addr_endpoint_get_host()
+ * to determine whether the endpoint is valid.
+ *
+ * Returns: (transfer full): the new #NMSockAddrEndpoint endpoint.
+ */
+NMSockAddrEndpoint *
+nm_sock_addr_endpoint_new (const char *endpoint)
+{
+	NMSockAddrEndpoint *ep;
+	gsize l_endpoint;
+	gsize l_host = 0;
+	gsize i;
+	gs_free char *host_clone = NULL;
+	const char *host;
+	guint16 port;
+
+	g_return_val_if_fail (endpoint, NULL);
+
+	l_endpoint = strlen (endpoint) + 1;
+
+	host = _parse_endpoint (nm_strndup_a (200, endpoint, l_endpoint - 1, &host_clone),
+	                        &port);
+
+	if (host)
+		l_host = strlen (host) + 1;
+
+	ep = g_malloc (sizeof (NMSockAddrEndpoint) + l_endpoint + l_host);
+	ep->refcount = 1;
+	memcpy (ep->endpoint, endpoint, l_endpoint);
+	if (host) {
+		i = l_endpoint;
+		memcpy (&ep->endpoint[i], host, l_host);
+		ep->host = &ep->endpoint[i];
+		ep->port = port;
+	} else {
+		ep->host = NULL;
+		ep->port = 0;
+	}
+	return ep;
+}
+
+/**
+ * nm_sock_addr_endpoint_ref:
+ * @self: (allow-none): the #NMSockAddrEndpoint
+ */
+NMSockAddrEndpoint *
+nm_sock_addr_endpoint_ref (NMSockAddrEndpoint *self)
+{
+	if (!self)
+		return NULL;
+
+	g_return_val_if_fail (NM_IS_SOCK_ADDR_ENDPOINT (self), NULL);
+
+	nm_assert (self->refcount < G_MAXUINT);
+
+	self->refcount++;
+	return self;
+}
+
+/**
+ * nm_sock_addr_endpoint_unref:
+ * @self: (allow-none): the #NMSockAddrEndpoint
+ */
+void
+nm_sock_addr_endpoint_unref (NMSockAddrEndpoint *self)
+{
+	if (!self)
+		return;
+
+	g_return_if_fail (NM_IS_SOCK_ADDR_ENDPOINT (self));
+
+	if (--self->refcount == 0)
+		g_free (self);
+}
+
+/**
+ * nm_sock_addr_endpoint_get_endpoint:
+ * @self: the #NMSockAddrEndpoint
+ *
+ * Gives the endpoint string. Since #NMSockAddrEndpoint's only
+ * information is the endpoint string, this can be used for comparing
+ * to instances for equality and order them lexically.
+ *
+ * Returns: (transfer none): the endpoint.
+ */
+const char *
+nm_sock_addr_endpoint_get_endpoint (NMSockAddrEndpoint *self)
+{
+	g_return_val_if_fail (NM_IS_SOCK_ADDR_ENDPOINT (self), NULL);
+
+	return self->endpoint;
+}
+
+/**
+ * nm_sock_addr_endpoint_get_host:
+ * @self: the #NMSockAddrEndpoint
+ *
+ * Returns: (transfer none): the parsed host part of the endpoint.
+ *   If the endpoint is invalid, %NULL will be returned.
+ */
+const char *
+nm_sock_addr_endpoint_get_host (NMSockAddrEndpoint *self)
+{
+	g_return_val_if_fail (NM_IS_SOCK_ADDR_ENDPOINT (self), NULL);
+
+	return self->host;
+}
+
+/**
+ * nm_sock_addr_endpoint_get_port:
+ * @self: the #NMSockAddrEndpoint
+ *
+ * Returns: the parsed port part of the endpoint (the service).
+ *   If the endpoint is invalid, -1 will be returned.
+ */
+gint32
+nm_sock_addr_endpoint_get_port (NMSockAddrEndpoint *self)
+{
+	g_return_val_if_fail (NM_IS_SOCK_ADDR_ENDPOINT (self), -1);
+
+	return self->host ? (int) self->port : -1;
+}
+
+gboolean
+nm_sock_addr_endpoint_get_fixed_sockaddr (NMSockAddrEndpoint *self,
+                                          gpointer sockaddr)
+{
+	int addr_family;
+	NMIPAddr addrbin;
+	const char *s;
+	guint scope_id = 0;
+
+	g_return_val_if_fail (NM_IS_SOCK_ADDR_ENDPOINT (self), FALSE);
+	g_return_val_if_fail (sockaddr, FALSE);
+
+	if (!self->host)
+		return FALSE;
+
+	if (nm_utils_parse_inaddr_bin (AF_UNSPEC, self->host, &addr_family, &addrbin))
+		goto good;
+
+	/* See if there is an IPv6 scope-id...
+	 *
+	 * Note that it does not make sense to persist connection profiles to disk,
+	 * that refenrence a scope-id (because the interface's ifindex changes on
+	 * reboot). However, we also support runtime only changes like `nmcli device modify`
+	 * where nothing is persisted to disk. At least in that case, passing a scope-id
+	 * might be reasonable. So, parse that too. */
+	s = strchr (self->host, '%');
+	if (!s)
+		return FALSE;
+
+	if (   s[1] == '\0'
+	    || !NM_STRCHAR_ALL (&s[1], ch, (ch >= '0' && ch <= '9')))
+		return FALSE;
+
+	scope_id = _nm_utils_ascii_str_to_int64 (&s[1], 10, 0, G_MAXINT32, G_MAXUINT);
+	if (scope_id == G_MAXUINT && errno)
+		return FALSE;
+
+	{
+		gs_free char *tmp_str = NULL;
+		const char *host_part;
+
+		host_part = nm_strndup_a (200, self->host, s - self->host, &tmp_str);
+		if (nm_utils_parse_inaddr_bin (AF_INET6, host_part, &addr_family, &addrbin))
+			goto good;
+	}
+
+	return FALSE;
+
+good:
+	switch (addr_family) {
+	case AF_INET:
+		*((struct sockaddr_in *) sockaddr) = (struct sockaddr_in) {
+			.sin_family = AF_INET,
+			.sin_addr   = addrbin.addr4_struct,
+			.sin_port   = htons (self->port),
+		};
+		return TRUE;
+	case AF_INET6:
+		*((struct sockaddr_in6 *) sockaddr) = (struct sockaddr_in6) {
+			.sin6_family   = AF_INET6,
+			.sin6_addr     = addrbin.addr6,
+			.sin6_port     = htons (self->port),
+			.sin6_scope_id = scope_id,
+			.sin6_flowinfo = 0,
+		};
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+/*****************************************************************************/
+
 struct IsoLangToEncodings
 {
 	const char *lang;
 	const char *const *encodings;
 };
 
-#define LANG_ENCODINGS(l, ...) { .lang = l, .encodings = (const char *[]) { __VA_ARGS__, NULL }}
+#define LANG_ENCODINGS(l, ...) { .lang = l, .encodings = NM_MAKE_STRV (__VA_ARGS__), }
 
 /* 5-letter language codes */
 static const struct IsoLangToEncodings isoLangEntries5[] =
@@ -112,7 +383,7 @@ static const struct IsoLangToEncodings isoLangEntries2[] =
 	LANG_ENCODINGS ("bg",      "windows-1251","koi8-r", "iso-8859-5"), /* Bulgarian */
 	LANG_ENCODINGS ("mk",      "koi8-r", "windows-1251", "iso-8859-5"),/* Macedonian */
 	LANG_ENCODINGS ("sr",      "koi8-r", "windows-1251", "iso-8859-5"),/* Serbian */
-	LANG_ENCODINGS ("uk",      "koi8-u", "koi8-r", "windows-1251"),    /* Ukranian */
+	LANG_ENCODINGS ("uk",      "koi8-u", "koi8-r", "windows-1251"),    /* Ukrainian */
 
 	/* Arabic */
 	LANG_ENCODINGS ("ar",      "iso-8859-6","windows-1256"),
@@ -836,7 +1107,7 @@ device_supports_ap_ciphers (guint32 dev_caps,
 
 /**
  * nm_utils_ap_mode_security_valid:
- * @type: the security type to check device capabilties against,
+ * @type: the security type to check device capabilities against,
  * e.g. #NMU_SEC_STATIC_WEP
  * @wifi_caps: bitfield of the capabilities of the specific Wi-Fi device, e.g.
  * #NM_WIFI_DEVICE_CAP_CIPHER_WEP40
@@ -872,16 +1143,16 @@ nm_utils_ap_mode_security_valid (NMUtilsSecurityType type,
 
 /**
  * nm_utils_security_valid:
- * @type: the security type to check AP flags and device capabilties against,
+ * @type: the security type to check AP flags and device capabilities against,
  * e.g. #NMU_SEC_STATIC_WEP
  * @wifi_caps: bitfield of the capabilities of the specific Wi-Fi device, e.g.
  * #NM_WIFI_DEVICE_CAP_CIPHER_WEP40
  * @have_ap: whether the @ap_flags, @ap_wpa, and @ap_rsn arguments are valid
  * @adhoc: whether the capabilities being tested are from an Ad-Hoc AP (IBSS)
  * @ap_flags: bitfield of AP capabilities, e.g. #NM_802_11_AP_FLAGS_PRIVACY
- * @ap_wpa: bitfield of AP capabilties derived from the AP's WPA beacon,
+ * @ap_wpa: bitfield of AP capabilities derived from the AP's WPA beacon,
  * e.g. (#NM_802_11_AP_SEC_PAIR_TKIP | #NM_802_11_AP_SEC_KEY_MGMT_PSK)
- * @ap_rsn: bitfield of AP capabilties derived from the AP's RSN/WPA2 beacon,
+ * @ap_rsn: bitfield of AP capabilities derived from the AP's RSN/WPA2 beacon,
  * e.g. (#NM_802_11_AP_SEC_PAIR_CCMP | #NM_802_11_AP_SEC_PAIR_TKIP)
  *
  * Given a set of device capabilities, and a desired security type to check
@@ -891,7 +1162,7 @@ nm_utils_ap_mode_security_valid (NMUtilsSecurityType type,
  * NOTE: this function cannot handle checking security for AP/Hotspot mode;
  * use nm_utils_ap_mode_security_valid() instead.
  *
- * Returns: %TRUE if the device capabilities and AP capabilties intersect and are
+ * Returns: %TRUE if the device capabilities and AP capabilities intersect and are
  * compatible with the desired @type, %FALSE if they are not
  **/
 gboolean
@@ -1173,7 +1444,7 @@ nm_utils_ip4_dns_from_variant (GVariant *value)
 	dns = g_new (char *, length + 1);
 
 	for (i = 0; i < length; i++)
-		dns[i] = g_strdup (nm_utils_inet4_ntop (array[i], NULL));
+		dns[i] = nm_utils_inet4_ntop_dup (array[i]);
 	dns[i] = NULL;
 
 	return dns;
@@ -1271,7 +1542,7 @@ nm_utils_ip4_addresses_from_variant (GVariant *value, char **out_gateway)
 			g_ptr_array_add (addresses, addr);
 
 			if (addr_array[2] && out_gateway && !*out_gateway)
-				*out_gateway = g_strdup (nm_utils_inet4_ntop (addr_array[2], NULL));
+				*out_gateway = nm_utils_inet4_ntop_dup (addr_array[2]);
 		} else {
 			g_warning ("Ignoring invalid IP4 address: %s", error->message);
 			g_clear_error (&error);
@@ -1389,30 +1660,13 @@ nm_utils_ip4_routes_from_variant (GVariant *value)
 guint32
 nm_utils_ip4_netmask_to_prefix (guint32 netmask)
 {
-	guint32 prefix;
-	guint8 v;
-	const guint8 *p = (guint8 *) &netmask;
-
-	if (p[3]) {
-		prefix = 24;
-		v = p[3];
-	} else if (p[2]) {
-		prefix = 16;
-		v = p[2];
-	} else if (p[1]) {
-		prefix = 8;
-		v = p[1];
-	} else {
-		prefix = 0;
-		v = p[0];
-	}
-
-	while (v) {
-		prefix++;
-		v <<= 1;
-	}
+	G_STATIC_ASSERT_EXPR (__SIZEOF_INT__   == 4);
+	G_STATIC_ASSERT_EXPR (sizeof (int)     == 4);
+	G_STATIC_ASSERT_EXPR (sizeof (netmask) == 4);
 
-	return prefix;
+	return  (  (netmask != 0)
+	         ? (32 - __builtin_ctz (ntohl (netmask)))
+	         : 0);
 }
 
 /**
@@ -1510,7 +1764,7 @@ nm_utils_ip6_dns_from_variant (GVariant *value)
 			continue;
 		}
 
-		dns[i++] = g_strdup (nm_utils_inet6_ntop (ip, NULL));
+		dns[i++] = nm_utils_inet6_ntop_dup (ip);
 		g_variant_unref (ip_var);
 	}
 	dns[i] = NULL;
@@ -1629,7 +1883,7 @@ nm_utils_ip6_addresses_from_variant (GVariant *value, char **out_gateway)
 					goto next;
 				}
 				if (!IN6_IS_ADDR_UNSPECIFIED (gateway_bytes))
-					*out_gateway = g_strdup (nm_utils_inet6_ntop (gateway_bytes, NULL));
+					*out_gateway = nm_utils_inet6_ntop_dup (gateway_bytes);
 			}
 		} else {
 			g_warning ("Ignoring invalid IP6 address: %s", error->message);
@@ -2017,7 +2271,7 @@ _string_append_tc_handle (GString *string, guint32 handle)
  * or to pretty-format (use symbolic name for root) the key in keyfile.
  * The presence of prefix determnines which one is the case.
  *
- * Private API due to general uglyness and overall uselessness for anything
+ * Private API due to general ugliness and overall uselessness for anything
  * sensible.
  */
 void
@@ -2909,7 +3163,7 @@ nm_utils_uuid_generate_from_string_bin (NMUuid *uuid, const char *s, gssize slen
 		break;
 	case NM_UTILS_UUID_TYPE_VERSION3:
 	case NM_UTILS_UUID_TYPE_VERSION5: {
-		NMUuid ns_uuid = { 0 };
+		NMUuid ns_uuid = { };
 
 		if (type_args) {
 			/* type_args can be a name space UUID. Interpret it as (char *) */
@@ -3124,7 +3378,7 @@ _nm_utils_check_file (const char *filename,
 		g_set_error (error,
 		             NM_VPN_PLUGIN_ERROR,
 		             NM_VPN_PLUGIN_ERROR_FAILED,
-		             _("failed stat file %s: %s"), filename, strerror (errsv));
+		             _("failed stat file %s: %s"), filename, nm_strerror_native (errsv));
 		return FALSE;
 	}
 
@@ -3243,7 +3497,7 @@ _nm_utils_check_module_file (const char *name,
  * @predicate: (scope call): if given, pass the file name to this function
  *   for additional checks. This check is performed after the check for
  *   @file_test_flags. You cannot omit both @file_test_flags and @predicate.
- * @user_data: (closure): (allow-none): user data for @predicate function.
+ * @user_data: (closure) (allow-none): user data for @predicate function.
  * @error: (allow-none): on failure, set a "not found" error %G_IO_ERROR %G_IO_ERROR_NOT_FOUND.
  *
  * Searches for a @progname file in a list of search @paths.
@@ -3620,147 +3874,6 @@ nm_utils_hwaddr_len (int type)
 	g_return_val_if_reached (0);
 }
 
-guint8 *
-_nm_utils_hexstr2bin_full (const char *hexstr,
-                           gboolean allow_0x_prefix,
-                           gboolean delimiter_required,
-                           const char *delimiter_candidates,
-                           gsize required_len,
-                           guint8 *buffer,
-                           gsize buffer_len,
-                           gsize *out_len)
-{
-	const char *in = hexstr;
-	guint8 *out = buffer;
-	gboolean delimiter_has = TRUE;
-	guint8 delimiter = '\0';
-	gsize len;
-
-	nm_assert (hexstr);
-	nm_assert (buffer);
-	nm_assert (required_len > 0 || out_len);
-
-	if (   allow_0x_prefix
-	    && in[0] == '0'
-	    && in[1] == 'x')
-		in += 2;
-
-	while (TRUE) {
-		const guint8 d1 = in[0];
-		guint8 d2;
-		int i1, i2;
-
-		i1 = nm_utils_hexchar_to_int (d1);
-		if (i1 < 0)
-			goto fail;
-
-		/* If there's no leading zero (ie "aa:b:cc") then fake it */
-		d2 = in[1];
-		if (   d2
-		    && (i2 = nm_utils_hexchar_to_int (d2)) >= 0) {
-			*out++ = (i1 << 4) + i2;
-			d2 = in[2];
-			if (!d2)
-				break;
-			in += 2;
-		} else {
-			/* Fake leading zero */
-			*out++ = i1;
-			if (!d2) {
-				if (!delimiter_has) {
-					/* when using no delimiter, there must be pairs of hex chars */
-					goto fail;
-				}
-				break;
-			}
-			in += 1;
-		}
-
-		if (--buffer_len == 0)
-			goto fail;
-
-		if (delimiter_has) {
-			if (d2 != delimiter) {
-				if (delimiter)
-					goto fail;
-				if (delimiter_candidates) {
-					while (delimiter_candidates[0]) {
-						if (delimiter_candidates++[0] == d2)
-							delimiter = d2;
-					}
-				}
-				if (!delimiter) {
-					if (delimiter_required)
-						goto fail;
-					delimiter_has = FALSE;
-					continue;
-				}
-			}
-			in++;
-		}
-	}
-
-	len = out - buffer;
-	if (   required_len == 0
-	    || len == required_len) {
-		NM_SET_OUT (out_len, len);
-		return buffer;
-	}
-
-fail:
-	NM_SET_OUT (out_len, 0);
-	return NULL;
-}
-
-guint8 *
-_nm_utils_hexstr2bin_alloc (const char *hexstr,
-                            gboolean allow_0x_prefix,
-                            gboolean delimiter_required,
-                            const char *delimiter_candidates,
-                            gsize required_len,
-                            gsize *out_len)
-{
-	guint8 *buffer;
-	gsize buffer_len, len;
-
-	g_return_val_if_fail (hexstr, NULL);
-
-	nm_assert (required_len > 0 || out_len);
-
-	if (   allow_0x_prefix
-	    && hexstr[0] == '0'
-	    && hexstr[1] == 'x')
-		hexstr += 2;
-
-	if (!hexstr[0])
-		goto fail;
-
-	if (required_len > 0)
-		buffer_len = required_len;
-	else
-		buffer_len = strlen (hexstr) / 2 + 3;
-
-	buffer = g_malloc (buffer_len);
-
-	if (_nm_utils_hexstr2bin_full (hexstr,
-	                               FALSE,
-	                               delimiter_required,
-	                               delimiter_candidates,
-	                               required_len,
-	                               buffer,
-	                               buffer_len,
-	                               &len)) {
-		NM_SET_OUT (out_len, len);
-		return buffer;
-	}
-
-	g_free (buffer);
-
-fail:
-	NM_SET_OUT (out_len, 0);
-	return NULL;
-}
-
 /**
  * nm_utils_hexstr2bin:
  * @hex: a string of hexadecimal characters with optional ':' separators
@@ -3778,14 +3891,14 @@ nm_utils_hexstr2bin (const char *hex)
 	guint8 *buffer;
 	gsize len;
 
-	buffer = _nm_utils_hexstr2bin_alloc (hex, TRUE, FALSE, ":", 0, &len);
+	buffer = nm_utils_hexstr2bin_alloc (hex, TRUE, FALSE, ":", 0, &len);
 	if (!buffer)
 		return NULL;
 	buffer = g_realloc (buffer, len);
 	return g_bytes_new_take (buffer, len);
 }
 
-#define hwaddr_aton(asc, buffer, buffer_len, out_len) _nm_utils_hexstr2bin_full ((asc), FALSE, TRUE, ":-", 0, (buffer), (buffer_len), (out_len))
+#define hwaddr_aton(asc, buffer, buffer_len, out_len) nm_utils_hexstr2bin_full ((asc), FALSE, TRUE, ":-", 0, (buffer), (buffer_len), (out_len))
 
 /**
  * nm_utils_hwaddr_atoba:
@@ -3879,64 +3992,7 @@ nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize length)
 	return buffer;
 }
 
-/**
- * _nm_utils_bin2hexstr_full:
- * @addr: pointer of @length bytes.
- * @length: number of bytes in @addr
- * @delimiter: either '\0', otherwise the output string will have the
- *   given delimiter character between each two hex numbers.
- * @upper_case: if TRUE, use upper case ASCII characters for hex.
- * @out: if %NULL, the function will allocate a new buffer of
- *   either (@length*2+1) or (@length*3) bytes, depending on whether
- *   a @delimiter is specified. In that case, the allocated buffer will
- *   be returned and must be freed by the caller.
- *   If not %NULL, the buffer must already be preallocated and contain
- *   at least (@length*2+1) or (@length*3) bytes, depending on the delimiter.
- *
- * Returns: the binary value converted to a hex string. If @out is given,
- *   this always returns @out. If @out is %NULL, a newly allocated string
- *   is returned.
- */
-char *
-_nm_utils_bin2hexstr_full (gconstpointer addr,
-                           gsize length,
-                           char delimiter,
-                           gboolean upper_case,
-                           char *out)
-{
-	const guint8 *in = addr;
-	const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef";
-	char *out0;
-
-	nm_assert (addr);
-	nm_assert (length > 0);
-
-	if (out)
-		out0 = out;
-	else {
-		out0 = out = g_new (char, delimiter == '\0'
-		                          ? length * 2 + 1
-		                          : length * 3);
-	}
-
-	/* @out must contain at least @length*3 bytes if @delimiter is set,
-	 * otherwise, @length*2+1. */
-
-	for (;;) {
-		const guint8 v = *in++;
 
-		*out++ = LOOKUP[v >> 4];
-		*out++ = LOOKUP[v & 0x0F];
-		length--;
-		if (!length)
-			break;
-		if (delimiter)
-			*out++ = delimiter;
-	}
-
-	*out = 0;
-	return out0;
-}
 
 /**
  * nm_utils_bin2hexstr:
@@ -3962,7 +4018,7 @@ nm_utils_bin2hexstr (gconstpointer src, gsize len, int final_len)
 
 	result = g_malloc (buflen);
 
-	_nm_utils_bin2hexstr_full (src, len, '\0', FALSE, result);
+	nm_utils_bin2hexstr_full (src, len, '\0', FALSE, result);
 
 	/* Cut converted key off at the correct length for this cipher type */
 	if (final_len >= 0 && (gsize) final_len < buflen)
@@ -3986,7 +4042,7 @@ nm_utils_hwaddr_ntoa (gconstpointer addr, gsize length)
 	g_return_val_if_fail (addr, g_strdup (""));
 	g_return_val_if_fail (length > 0, g_strdup (""));
 
-	return _nm_utils_bin2hexstr_full (addr, length, ':', TRUE, NULL);
+	return nm_utils_bin2hexstr_full (addr, length, ':', TRUE, NULL);
 }
 
 const char *
@@ -3998,7 +4054,7 @@ nm_utils_hwaddr_ntoa_buf (gconstpointer addr, gsize addr_len, gboolean upper_cas
 	if (buf_len < addr_len * 3)
 		g_return_val_if_reached (NULL);
 
-	return _nm_utils_bin2hexstr_full (addr, addr_len, ':', upper_case, buf);
+	return nm_utils_bin2hexstr_full (addr, addr_len, ':', upper_case, buf);
 }
 
 /**
@@ -4251,13 +4307,18 @@ _nm_utils_hwaddr_cloned_not_set (NMSetting *setting,
 }
 
 GVariant *
-_nm_utils_hwaddr_cloned_data_synth (NMSetting *setting,
+_nm_utils_hwaddr_cloned_data_synth (const NMSettInfoSetting *sett_info,
+                                    guint property_idx,
                                     NMConnection *connection,
-                                    const char *property)
+                                    NMSetting *setting,
+                                    NMConnectionSerializationFlags flags)
 {
 	gs_free char *addr = NULL;
 
-	nm_assert (nm_streq0 (property, "assigned-mac-address"));
+	if (flags & NM_CONNECTION_SERIALIZE_ONLY_SECRETS)
+		return NULL;
+
+	nm_assert (nm_streq0 (sett_info->property_infos[property_idx].name, "assigned-mac-address"));
 
 	g_object_get (setting,
 	              "cloned-mac-address",
@@ -4278,7 +4339,9 @@ _nm_utils_hwaddr_cloned_data_synth (NMSetting *setting,
 	 * To preserve that behavior, serialize "" as NULL.
 	 */
 
-	return addr && addr[0] ? g_variant_new_string (addr) : NULL;
+	return addr && addr[0]
+	       ? g_variant_new_take_string (g_steal_pointer (&addr))
+	       : NULL;
 }
 
 gboolean
@@ -4321,6 +4384,102 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value,
 
 /*****************************************************************************/
 
+/* Validate secret-flags. Most settings don't validate them, which is a bug.
+ * But we possibly cannot enforce a strict validation now.
+ *
+ * For new settings, they shall validate the secret-flags strictly. */
+gboolean
+_nm_utils_secret_flags_validate (NMSettingSecretFlags secret_flags,
+                                 const char *setting_name,
+                                 const char *property_name,
+                                 NMSettingSecretFlags disallowed_flags,
+                                 GError **error)
+{
+	if (secret_flags == NM_SETTING_SECRET_FLAG_NONE)
+		return TRUE;
+
+	if (NM_FLAGS_ANY (secret_flags, ~NM_SETTING_SECRET_FLAG_ALL)) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("unknown secret flags"));
+		if (setting_name)
+			g_prefix_error (error, "%s.%s: ", setting_name, property_name);
+		return FALSE;
+	}
+
+	if (!nm_utils_is_power_of_two (secret_flags)) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("conflicting secret flags"));
+		if (setting_name)
+			g_prefix_error (error, "%s.%s: ", setting_name, property_name);
+		return FALSE;
+	}
+
+	if (NM_FLAGS_ANY (secret_flags, disallowed_flags)) {
+		if (NM_FLAGS_HAS (secret_flags, NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("secret flags must not be \"not-required\""));
+			if (setting_name)
+				g_prefix_error (error, "%s.%s: ", setting_name, property_name);
+			return FALSE;
+		}
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("unsupported secret flags"));
+		if (setting_name)
+			g_prefix_error (error, "%s.%s: ", setting_name, property_name);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gboolean
+_nm_utils_wps_method_validate (NMSettingWirelessSecurityWpsMethod wps_method,
+                               const char *setting_name,
+                               const char *property_name,
+                               gboolean wps_required,
+                               GError **error)
+{
+	if (wps_method > NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_PIN) {
+		g_set_error_literal (error,
+		                     NM_CONNECTION_ERROR,
+		                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+		                     _("property is invalid"));
+		g_prefix_error (error, "%s.%s: ", setting_name, property_name);
+		return FALSE;
+	}
+
+	if (NM_FLAGS_HAS (wps_method, NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED)) {
+		if (wps_method != NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DISABLED) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("can't be simultaneously disabled and enabled"));
+			g_prefix_error (error, "%s.%s: ", setting_name, property_name);
+			return FALSE;
+		}
+		if (wps_required) {
+			g_set_error_literal (error,
+			                     NM_CONNECTION_ERROR,
+			                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
+			                     _("WPS is required"));
+			g_prefix_error (error, "%s.%s: ", setting_name, property_name);
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+/*****************************************************************************/
+
 static char *
 _split_word (char *s)
 {
@@ -4517,10 +4676,11 @@ nm_utils_inet_ntop (int addr_family, gconstpointer addr, char *dst)
 
 	nm_assert_addr_family (addr_family);
 	nm_assert (addr);
+	nm_assert (dst);
 
 	s = inet_ntop (addr_family,
 	               addr,
-	               dst ?: _nm_utils_inet_ntop_buffer,
+	               dst,
 	               addr_family == AF_INET6 ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN);
 	nm_assert (s);
 	return s;
@@ -4546,6 +4706,11 @@ nm_utils_inet_ntop (int addr_family, gconstpointer addr, char *dst)
 const char *
 nm_utils_inet4_ntop (in_addr_t inaddr, char *dst)
 {
+	/* relying on the static buffer (by leaving @dst as %NULL) is discouraged.
+	 * Don't do that!
+	 *
+	 * However, still support it to be lenient against mistakes and because
+	 * this is public API of libnm. */
 	return inet_ntop (AF_INET, &inaddr, dst ?: _nm_utils_inet_ntop_buffer,
 	                  INET_ADDRSTRLEN);
 }
@@ -4571,6 +4736,11 @@ nm_utils_inet4_ntop (in_addr_t inaddr, char *dst)
 const char *
 nm_utils_inet6_ntop (const struct in6_addr *in6addr, char *dst)
 {
+	/* relying on the static buffer (by leaving @dst as %NULL) is discouraged.
+	 * Don't do that!
+	 *
+	 * However, still support it to be lenient against mistakes and because
+	 * this is public API of libnm. */
 	g_return_val_if_fail (in6addr, NULL);
 	return inet_ntop (AF_INET6, in6addr, dst ?: _nm_utils_inet_ntop_buffer,
 	                  INET6_ADDRSTRLEN);
@@ -4665,7 +4835,7 @@ _nm_utils_dhcp_duid_valid (const char *duid, GBytes **out_duid_bin)
 		return TRUE;
 	}
 
-	if (_nm_utils_hexstr2bin_full (duid, FALSE, FALSE, ":", 0, duid_arr, sizeof (duid_arr), &duid_len)) {
+	if (nm_utils_hexstr2bin_full (duid, FALSE, FALSE, ":", 0, duid_arr, sizeof (duid_arr), &duid_len)) {
 		/* MAX DUID length is 128 octects + the type code (2 octects). */
 		if (   duid_len > 2
 		    && duid_len <= (128 + 2)) {
@@ -5315,7 +5485,7 @@ _json_find_object (json_t *json,
 	return json_element;
 }
 
-static inline void
+static void
 _json_delete_object_on_int_match (json_t *json,
                                   const char *key1,
                                   const char *key2,
@@ -5331,7 +5501,7 @@ _json_delete_object_on_int_match (json_t *json,
 		_json_del_object (json, key1, key2, key3);
 }
 
-static inline void
+static void
 _json_delete_object_on_bool_match (json_t *json,
                                    const char *key1,
                                    const char *key2,
@@ -5347,7 +5517,7 @@ _json_delete_object_on_bool_match (json_t *json,
 		_json_del_object (json, key1, key2, key3);
 }
 
-static inline void
+static void
 _json_delete_object_on_string_match (json_t *json,
                                      const char *key1,
                                      const char *key2,
@@ -5435,7 +5605,7 @@ _nm_utils_team_link_watcher_from_json (json_t *json_element)
 	const char *j_key;
 	json_t *j_val;
 	gs_free char *name = NULL, *target_host = NULL, *source_host = NULL;
-	int val1 = 0, val2 = 0, val3 = 3;
+	int val1 = 0, val2 = 0, val3 = 3, val4 = -1;
 	NMTeamLinkWatcherArpPingFlags flags = 0;
 
 	g_return_val_if_fail (json_element, NULL);
@@ -5456,6 +5626,8 @@ _nm_utils_team_link_watcher_from_json (json_t *json_element)
 			val2 = json_integer_value (j_val);
 		else if (nm_streq (j_key, "missed_max"))
 			val3 = json_integer_value (j_val);
+		else if (nm_streq (j_key, "vlanid"))
+			val4 = json_integer_value (j_val);
 		else if (nm_streq (j_key, "validate_active")) {
 			if (json_is_true (j_val))
 				flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_VALIDATE_ACTIVE;
@@ -5473,8 +5645,8 @@ _nm_utils_team_link_watcher_from_json (json_t *json_element)
 	else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_NSNA_PING))
 		return nm_team_link_watcher_new_nsna_ping (val1, val2, val3, target_host, NULL);
 	else if (nm_streq0 (name, NM_TEAM_LINK_WATCHER_ARP_PING)) {
-		return nm_team_link_watcher_new_arp_ping (val1, val2, val3, target_host,
-		                                          source_host, flags, NULL);
+		return nm_team_link_watcher_new_arp_ping2 (val1, val2, val3, val4, target_host,
+		                                           source_host, flags, NULL);
 	} else
 		return NULL;
 }
@@ -5524,6 +5696,9 @@ _nm_utils_team_link_watcher_to_json (NMTeamLinkWatcher *watcher)
 	if (nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING))
 		return json_element;
 
+	int_val = nm_team_link_watcher_get_vlanid (watcher);
+	if (int_val != -1)
+		json_object_set_new (json_element, "vlanid", json_integer (int_val));
 	str_val = nm_team_link_watcher_get_source_host (watcher);
 	if (!str_val)
 		goto fail;
@@ -5976,15 +6151,15 @@ _nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers)
 
 		name = nm_team_link_watcher_get_name (watcher);
 		g_variant_builder_add (&watcher_builder, "{sv}",
-				       "name",
-				       g_variant_new_string (name));
+		                       "name",
+		                       g_variant_new_string (name));
 
-		if nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL) {
+		if (nm_streq (name, NM_TEAM_LINK_WATCHER_ETHTOOL)) {
 			int_val = nm_team_link_watcher_get_delay_up (watcher);
 			if (int_val) {
 				g_variant_builder_add (&watcher_builder, "{sv}",
-						       "delay-up",
-						       g_variant_new_int32 (int_val));
+				                       "delay-up",
+				                       g_variant_new_int32 (int_val));
 			}
 			int_val = nm_team_link_watcher_get_delay_down (watcher);
 			if (int_val) {
@@ -6019,12 +6194,18 @@ _nm_utils_team_link_watchers_to_variant (GPtrArray *link_watchers)
 		                       "target-host",
 		                       g_variant_new_string (nm_team_link_watcher_get_target_host (watcher)));
 
-		if nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING) {
+		if (nm_streq (name, NM_TEAM_LINK_WATCHER_NSNA_PING)) {
 			g_variant_builder_add (&builder, "a{sv}", &watcher_builder);
 			continue;
 		}
 
 		/* arp_ping watcher only */
+		int_val = nm_team_link_watcher_get_vlanid (watcher);
+		if (int_val != -1) {
+			g_variant_builder_add (&watcher_builder, "{sv}",
+			                       "vlanid",
+			                       g_variant_new_int32 (int_val));
+		}
 		g_variant_builder_add (&watcher_builder, "{sv}",
 		                       "source-host",
 		                       g_variant_new_string (nm_team_link_watcher_get_source_host (watcher)));
@@ -6075,7 +6256,7 @@ _nm_utils_team_link_watchers_from_variant (GVariant *value)
 	while (g_variant_iter_next (&iter, "@a{sv}", &watcher_var)) {
 		NMTeamLinkWatcher *watcher;
 		const char *name;
-		int val1, val2, val3 = 0;
+		int val1, val2, val3 = 0, val4 = -1;
 		const char *target_host = NULL, *source_host = NULL;
 		gboolean bval;
 		NMTeamLinkWatcherArpPingFlags flags = NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_NONE;
@@ -6105,7 +6286,9 @@ _nm_utils_team_link_watchers_from_variant (GVariant *value)
 				val2 = 0;
 			if (!g_variant_lookup (watcher_var, "missed-max", "i", &val3))
 				val3 = 3;
-			if nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING) {
+			if (nm_streq (name, NM_TEAM_LINK_WATCHER_ARP_PING)) {
+				if (!g_variant_lookup (watcher_var, "vlanid", "i", &val4))
+					val4 = -1;
 				if (!g_variant_lookup (watcher_var, "source-host", "&s", &source_host))
 					goto next;
 				if (!g_variant_lookup (watcher_var, "validate-active", "b", &bval))
@@ -6120,9 +6303,9 @@ _nm_utils_team_link_watchers_from_variant (GVariant *value)
 					bval = FALSE;
 				if (bval)
 					flags |= NM_TEAM_LINK_WATCHER_ARP_PING_FLAG_SEND_ALWAYS;
-				watcher = nm_team_link_watcher_new_arp_ping (val1, val2, val3,
-				                                             target_host, source_host,
-				                                             flags, &error);
+				watcher = nm_team_link_watcher_new_arp_ping2 (val1, val2, val3, val4,
+				                                              target_host, source_host,
+				                                              flags, &error);
 			} else
 				watcher = nm_team_link_watcher_new_nsna_ping (val1, val2, val3,
 				                                              target_host, &error);
@@ -6487,3 +6670,73 @@ nm_utils_version (void)
 	return NM_VERSION;
 }
 
+/*****************************************************************************/
+
+/**
+ * nm_utils_base64secret_decode:
+ * @base64_key: the (possibly invalid) base64 encode key.
+ * @required_key_len: the expected (binary) length of the key after
+ *   decoding. If the length does not match, the validation fails.
+ * @out_key: (allow-none): (out): an optional output buffer for the binary
+ *   key. If given, it will be filled with exactly @required_key_len
+ *   bytes.
+ *
+ * Returns: %TRUE if the input key is a valid base64 encoded key
+ *   with @required_key_len bytes.
+ *
+ * Since: 1.16
+ */
+gboolean
+nm_utils_base64secret_decode (const char *base64_key,
+                              gsize required_key_len,
+                              guint8 *out_key)
+{
+	gs_free guint8 *bin_arr = NULL;
+	gsize base64_key_len;
+	gsize bin_len;
+	int r;
+
+	if (!base64_key)
+		return FALSE;
+
+	base64_key_len = strlen (base64_key);
+
+	r = nm_sd_utils_unbase64mem (base64_key, base64_key_len, &bin_arr, &bin_len);
+	if (r < 0)
+		return FALSE;
+	if (bin_len != required_key_len) {
+		nm_explicit_bzero (bin_arr, bin_len);
+		return FALSE;
+	}
+
+	if (out_key)
+		memcpy (out_key, bin_arr, required_key_len);
+
+	nm_explicit_bzero (bin_arr, bin_len);
+	return TRUE;
+}
+
+gboolean
+nm_utils_base64secret_normalize (const char *base64_key,
+                                 gsize required_key_len,
+                                 char **out_base64_key_norm)
+{
+	gs_free guint8 *buf_free = NULL;
+	guint8 buf_static[200];
+	guint8 *buf;
+
+	if (required_key_len > sizeof (buf_static)) {
+		buf_free = g_new (guint8, required_key_len);
+		buf = buf_free;
+	} else
+		buf = buf_static;
+
+	if (!nm_utils_base64secret_decode (base64_key, required_key_len, buf)) {
+		NM_SET_OUT (out_base64_key_norm, NULL);
+		return FALSE;
+	}
+
+	NM_SET_OUT (out_base64_key_norm, g_base64_encode (buf, required_key_len));
+	nm_explicit_bzero (buf, required_key_len);
+	return TRUE;
+}
diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h
index cf9572b9..2b5baba4 100644
--- a/libnm-core/nm-utils.h
+++ b/libnm-core/nm-utils.h
@@ -40,6 +40,8 @@
 
 G_BEGIN_DECLS
 
+/*****************************************************************************/
+
 typedef struct _NMVariantAttributeSpec NMVariantAttributeSpec;
 
 /* SSID helpers */
@@ -261,6 +263,10 @@ NMSriovVF *nm_utils_sriov_vf_from_str (const char *str, GError **error);
 NM_AVAILABLE_IN_1_12
 gint64 nm_utils_get_timestamp_msec         (void);
 
+NM_AVAILABLE_IN_1_16
+gboolean nm_utils_base64secret_decode (const char *base64_key,
+                                       gsize required_key_len,
+                                       guint8 *out_key);
 
 G_END_DECLS
 
diff --git a/libnm-core/nm-version.h b/libnm-core/nm-version.h
index 59482d4e..7fe2e45b 100644
--- a/libnm-core/nm-version.h
+++ b/libnm-core/nm-version.h
@@ -174,4 +174,18 @@
 # define NM_AVAILABLE_IN_1_14
 #endif
 
+#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_16
+# define NM_DEPRECATED_IN_1_16           G_DEPRECATED
+# define NM_DEPRECATED_IN_1_16_FOR(f)    G_DEPRECATED_FOR(f)
+#else
+# define NM_DEPRECATED_IN_1_16
+# define NM_DEPRECATED_IN_1_16_FOR(f)
+#endif
+
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_16
+# define NM_AVAILABLE_IN_1_16            G_UNAVAILABLE(1,16)
+#else
+# define NM_AVAILABLE_IN_1_16
+#endif
+
 #endif  /* NM_VERSION_H */
diff --git a/libnm-core/nm-vpn-editor-plugin.c b/libnm-core/nm-vpn-editor-plugin.c
index d690d9b0..30213ea0 100644
--- a/libnm-core/nm-vpn-editor-plugin.c
+++ b/libnm-core/nm-vpn-editor-plugin.c
@@ -29,6 +29,8 @@
 
 #include "nm-core-internal.h"
 
+/*****************************************************************************/
+
 static void nm_vpn_editor_plugin_default_init (NMVpnEditorPluginInterface *iface);
 
 G_DEFINE_INTERFACE (NMVpnEditorPlugin, nm_vpn_editor_plugin, G_TYPE_OBJECT)
@@ -36,18 +38,16 @@ G_DEFINE_INTERFACE (NMVpnEditorPlugin, nm_vpn_editor_plugin, G_TYPE_OBJECT)
 static void
 nm_vpn_editor_plugin_default_init (NMVpnEditorPluginInterface *iface)
 {
-	/* Properties */
-
 	/**
 	 * NMVpnEditorPlugin:name:
 	 *
 	 * Short display name of the VPN plugin.
 	 */
 	g_object_interface_install_property (iface,
-		 g_param_spec_string (NM_VPN_EDITOR_PLUGIN_NAME, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	    g_param_spec_string (NM_VPN_EDITOR_PLUGIN_NAME, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS));
 
 	/**
 	 * NMVpnEditorPlugin:description:
@@ -55,10 +55,10 @@ nm_vpn_editor_plugin_default_init (NMVpnEditorPluginInterface *iface)
 	 * Longer description of the VPN plugin.
 	 */
 	g_object_interface_install_property (iface,
-		 g_param_spec_string (NM_VPN_EDITOR_PLUGIN_DESCRIPTION, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	    g_param_spec_string (NM_VPN_EDITOR_PLUGIN_DESCRIPTION, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS));
 
 	/**
 	 * NMVpnEditorPlugin:service:
@@ -66,10 +66,10 @@ nm_vpn_editor_plugin_default_init (NMVpnEditorPluginInterface *iface)
 	 * D-Bus service name of the plugin's VPN service.
 	 */
 	g_object_interface_install_property (iface,
-		 g_param_spec_string (NM_VPN_EDITOR_PLUGIN_SERVICE, "", "",
-		                      NULL,
-		                      G_PARAM_READABLE |
-		                      G_PARAM_STATIC_STRINGS));
+	    g_param_spec_string (NM_VPN_EDITOR_PLUGIN_SERVICE, "", "",
+	                         NULL,
+	                         G_PARAM_READABLE |
+	                         G_PARAM_STATIC_STRINGS));
 }
 
 /*****************************************************************************/
@@ -379,8 +379,8 @@ _nm_vpn_editor_plugin_load (const char *plugin_name,
  * @user_data: user data for @check_file
  * @error: on failure the error reason.
  *
- * Load the shared libary @plugin_name and create a new
- * #NMVpnEditorPlugin instace via the #NMVpnEditorPluginFactory
+ * Load the shared library @plugin_name and create a new
+ * #NMVpnEditorPlugin instance via the #NMVpnEditorPluginFactory
  * function.
  *
  * If @plugin_name is not an absolute path name, it assumes the file
@@ -419,8 +419,8 @@ nm_vpn_editor_plugin_load_from_file  (const char *plugin_name,
  *  the given service.
  * @error: on failure the error reason.
  *
- * Load the shared libary @plugin_name and create a new
- * #NMVpnEditorPlugin instace via the #NMVpnEditorPluginFactory
+ * Load the shared library @plugin_name and create a new
+ * #NMVpnEditorPlugin instance via the #NMVpnEditorPluginFactory
  * function.
  *
  * This is similar to nm_vpn_editor_plugin_load_from_file(), but
diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c
index 79542279..bfcd70a5 100644
--- a/libnm-core/nm-vpn-plugin-info.c
+++ b/libnm-core/nm-vpn-plugin-info.c
@@ -22,8 +22,6 @@
 
 #include "nm-vpn-plugin-info.h"
 
-#include <string.h>
-#include <errno.h>
 #include <sys/stat.h>
 
 #include "nm-errors.h"
@@ -148,7 +146,7 @@ nm_vpn_plugin_info_check_file_full (const char *filename,
  *   other users.
  * @check_file: pass a callback to do your own validation.
  * @user_data: user data for @check_file.
- * @error: (allow-none): (out): the error reason if the check fails.
+ * @error: (allow-none) (out): the error reason if the check fails.
  *
  * Check whether the file exists and is a valid name file (in keyfile format).
  * Additionally, also check for file permissions.
@@ -1095,60 +1093,42 @@ nm_vpn_plugin_info_load_editor_plugin (NMVpnPluginInfo *self, GError **error)
 
 /*****************************************************************************/
 
-/**
- * nm_vpn_plugin_info_new_from_file:
- * @filename: filename to read.
- * @error: on failure, the error reason.
- *
- * Read the plugin info from file @filename. Does not do
- * any further verification on the file. You might want to check
- * file permissions and ownership of the file.
- *
- * Returns: %NULL if there is any error or a newly created
- * #NMVpnPluginInfo instance.
- *
- * Since: 1.2
- */
-NMVpnPluginInfo *
-nm_vpn_plugin_info_new_from_file (const char *filename,
-                                  GError **error)
+static void
+get_property (GObject *object, guint prop_id,
+              GValue *value, GParamSpec *pspec)
 {
-	g_return_val_if_fail (filename, NULL);
+	NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (object);
 
-	return NM_VPN_PLUGIN_INFO (g_initable_new (NM_TYPE_VPN_PLUGIN_INFO,
-	                                           NULL,
-	                                           error,
-	                                           NM_VPN_PLUGIN_INFO_FILENAME, filename,
-	                                           NULL));
+	switch (prop_id) {
+	case PROP_NAME:
+		g_value_set_string (value, priv->name);
+		break;
+	case PROP_FILENAME:
+		g_value_set_string (value, priv->filename);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
-/**
- * nm_vpn_plugin_info_new_with_data:
- * @filename: optional filename.
- * @keyfile: inject data for the plugin info instance.
- * @error: construction may fail if the keyfile lacks mandatory fields.
- *   In this case, return the error reason.
- *
- * This constructor does not read any data from file but
- * takes instead a @keyfile argument.
- *
- * Returns: new plugin info instance.
- *
- * Since: 1.2
- */
-NMVpnPluginInfo *
-nm_vpn_plugin_info_new_with_data (const char *filename,
-                                  GKeyFile *keyfile,
-                                  GError **error)
+static void
+set_property (GObject *object, guint prop_id,
+              const GValue *value, GParamSpec *pspec)
 {
-	g_return_val_if_fail (keyfile, NULL);
+	NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (object);
 
-	return NM_VPN_PLUGIN_INFO (g_initable_new (NM_TYPE_VPN_PLUGIN_INFO,
-	                                           NULL,
-	                                           error,
-	                                           NM_VPN_PLUGIN_INFO_FILENAME, filename,
-	                                           NM_VPN_PLUGIN_INFO_KEYFILE, keyfile,
-	                                           NULL));
+	switch (prop_id) {
+	case PROP_FILENAME:
+		priv->filename = g_value_dup_string (value);
+		break;
+	case PROP_KEYFILE:
+		priv->keyfile = g_value_dup_boxed (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
 }
 
 /*****************************************************************************/
@@ -1224,42 +1204,60 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
 	return TRUE;
 }
 
-static void
-set_property (GObject *object, guint prop_id,
-              const GValue *value, GParamSpec *pspec)
+/**
+ * nm_vpn_plugin_info_new_from_file:
+ * @filename: filename to read.
+ * @error: on failure, the error reason.
+ *
+ * Read the plugin info from file @filename. Does not do
+ * any further verification on the file. You might want to check
+ * file permissions and ownership of the file.
+ *
+ * Returns: %NULL if there is any error or a newly created
+ * #NMVpnPluginInfo instance.
+ *
+ * Since: 1.2
+ */
+NMVpnPluginInfo *
+nm_vpn_plugin_info_new_from_file (const char *filename,
+                                  GError **error)
 {
-	NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (object);
+	g_return_val_if_fail (filename, NULL);
 
-	switch (prop_id) {
-	case PROP_FILENAME:
-		priv->filename = g_value_dup_string (value);
-		break;
-	case PROP_KEYFILE:
-		priv->keyfile = g_value_dup_boxed (value);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	return NM_VPN_PLUGIN_INFO (g_initable_new (NM_TYPE_VPN_PLUGIN_INFO,
+	                                           NULL,
+	                                           error,
+	                                           NM_VPN_PLUGIN_INFO_FILENAME, filename,
+	                                           NULL));
 }
 
-static void
-get_property (GObject *object, guint prop_id,
-              GValue *value, GParamSpec *pspec)
+/**
+ * nm_vpn_plugin_info_new_with_data:
+ * @filename: optional filename.
+ * @keyfile: inject data for the plugin info instance.
+ * @error: construction may fail if the keyfile lacks mandatory fields.
+ *   In this case, return the error reason.
+ *
+ * This constructor does not read any data from file but
+ * takes instead a @keyfile argument.
+ *
+ * Returns: new plugin info instance.
+ *
+ * Since: 1.2
+ */
+NMVpnPluginInfo *
+nm_vpn_plugin_info_new_with_data (const char *filename,
+                                  GKeyFile *keyfile,
+                                  GError **error)
 {
-	NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (object);
+	g_return_val_if_fail (keyfile, NULL);
 
-	switch (prop_id) {
-	case PROP_NAME:
-		g_value_set_string (value, priv->name);
-		break;
-	case PROP_FILENAME:
-		g_value_set_string (value, priv->filename);
-		break;
-	default:
-		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-		break;
-	}
+	return NM_VPN_PLUGIN_INFO (g_initable_new (NM_TYPE_VPN_PLUGIN_INFO,
+	                                           NULL,
+	                                           error,
+	                                           NM_VPN_PLUGIN_INFO_FILENAME, filename,
+	                                           NM_VPN_PLUGIN_INFO_KEYFILE, keyfile,
+	                                           NULL));
 }
 
 static void
@@ -1298,14 +1296,11 @@ nm_vpn_plugin_info_class_init (NMVpnPluginInfoClass *plugin_class)
 
 	g_type_class_add_private (object_class, sizeof (NMVpnPluginInfoPrivate));
 
-	/* virtual methods */
 	object_class->set_property = set_property;
 	object_class->get_property = get_property;
 	object_class->dispose      = dispose;
 	object_class->finalize     = finalize;
 
-	/* properties */
-
 	/**
 	 * NMVpnPluginInfo:name:
 	 *
@@ -1341,7 +1336,7 @@ nm_vpn_plugin_info_class_init (NMVpnPluginInfoClass *plugin_class)
 	/**
 	 * NMVpnPluginInfo:keyfile:
 	 *
-	 * Initalize the instance with a different keyfile instance.
+	 * Initialize the instance with a different keyfile instance.
 	 * When passing a keyfile instance, the constructor will not
 	 * try to read from filename.
 	 *
@@ -1361,4 +1356,3 @@ nm_vpn_plugin_info_initable_iface_init (GInitableIface *iface)
 {
 	iface->init = init_sync;
 }
-
diff --git a/libnm-core/tests/meson.build b/libnm-core/tests/meson.build
index 627e6b53..7d772c80 100644
--- a/libnm-core/tests/meson.build
+++ b/libnm-core/tests/meson.build
@@ -14,7 +14,7 @@ enum = gnome.mkenums(
   sources: 'test-general-enums.h',
   identifier_prefix: nm_id_prefix,
   c_template: enum_types + '.c.template',
-  h_template: enum_types + '.h.template'
+  h_template: enum_types + '.h.template',
 )
 
 test_units = [
@@ -24,7 +24,7 @@ test_units = [
   'test-keyfile',
   'test-secrets',
   'test-setting',
-  'test-settings-defaults'
+  'test-settings-defaults',
 ]
 
 test_cert_dir = join_paths(meson.current_source_dir(), 'certs')
@@ -33,16 +33,20 @@ foreach test_unit: test_units
   exe = executable(
     'libnm-core-' + test_unit,
     [test_unit + '.c'] + enum,
-    dependencies: nm_core_dep,
+    dependencies: [
+      nm_core_dep,
+      libnm_systemd_shared_no_logging_dep,
+    ],
     c_args: [
         '-DNETWORKMANAGER_COMPILATION_TEST',
         '-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIBNM_CORE',
       ],
-    link_with: libnm_core
+    link_with: libnm_core,
   )
   test(
     'libnm-core/' + test_unit,
     test_script,
-    args: test_args + [exe.full_path()]
+    args: test_args + [exe.full_path()],
+    timeout: default_test_timeout,
   )
 endforeach
diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c
index 6b63e6fd..4ac9679b 100644
--- a/libnm-core/tests/test-crypto.c
+++ b/libnm-core/tests/test-crypto.c
@@ -26,7 +26,6 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <string.h>
 
 #include "nm-crypto-impl.h"
 #include "nm-utils.h"
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index 67b80098..7d97296a 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -23,8 +23,6 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-utils/c-list-util.h"
 #include "nm-utils/nm-enum-utils.h"
 
@@ -755,10 +753,12 @@ vpn_check_empty_func (const char *key, const char *value, gpointer user_data)
 static void
 test_setting_vpn_items (void)
 {
-	gs_unref_object NMSettingVpn *s_vpn = NULL;
+	gs_unref_object NMConnection *connection = NULL;
+	NMSettingVpn *s_vpn;
 
-	s_vpn = (NMSettingVpn *) nm_setting_vpn_new ();
-	g_assert (s_vpn);
+	connection = nmtst_create_minimal_connection ("vpn-items", NULL, NM_SETTING_VPN_SETTING_NAME, NULL);
+
+	s_vpn = nm_connection_get_setting_vpn (connection);
 
 	nm_setting_vpn_add_data_item (s_vpn, "foobar1", "blahblah1");
 	nm_setting_vpn_add_data_item (s_vpn, "foobar2", "blahblah2");
@@ -772,7 +772,14 @@ test_setting_vpn_items (void)
 	nm_setting_vpn_remove_data_item (s_vpn, "foobar3");
 	nm_setting_vpn_remove_data_item (s_vpn, "foobar4");
 
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
 	nm_setting_vpn_add_secret (s_vpn, "foobar1", "blahblah1");
+
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
 	nm_setting_vpn_add_secret (s_vpn, "foobar2", "blahblah2");
 	nm_setting_vpn_add_secret (s_vpn, "foobar3", "blahblah3");
 	nm_setting_vpn_add_secret (s_vpn, "foobar4", "blahblah4");
@@ -782,8 +789,25 @@ test_setting_vpn_items (void)
 	nm_setting_vpn_remove_secret (s_vpn, "foobar1");
 	nm_setting_vpn_remove_secret (s_vpn, "foobar2");
 	nm_setting_vpn_remove_secret (s_vpn, "foobar3");
+
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
+	nm_setting_vpn_add_data_item (s_vpn, "foobar4-flags", "blahblah4");
+
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
+	nm_setting_vpn_add_data_item (s_vpn, "foobar4-flags", "2");
+
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
 	nm_setting_vpn_remove_secret (s_vpn, "foobar4");
 
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
+	nm_setting_vpn_remove_data_item (s_vpn, "foobar4-flags");
+
 	/* Try to add some blank values and make sure they are rejected */
 	NMTST_EXPECT_LIBNM_CRITICAL (NMTST_G_RETURN_MSG (key != NULL));
 	nm_setting_vpn_add_data_item (s_vpn, NULL, NULL);
@@ -1351,8 +1375,6 @@ test_setting_gsm_apn_bad_chars (void)
 	s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
 	g_assert (s_gsm);
 
-	g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
-
 	/* Make sure a valid APN works */
 	g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar123.-baz", NULL);
 	g_assert (nm_setting_verify (NM_SETTING (s_gsm), NULL, NULL));
@@ -1382,8 +1404,6 @@ test_setting_gsm_apn_underscore (void)
 	s_gsm = (NMSettingGsm *) nm_setting_gsm_new ();
 	g_assert (s_gsm);
 
-	g_object_set (s_gsm, NM_SETTING_GSM_NUMBER, "*99#", NULL);
-
 	/* 65-character long */
 	g_object_set (s_gsm, NM_SETTING_GSM_APN, "foobar_baz", NULL);
 	nmtst_assert_setting_verifies (NM_SETTING (s_gsm));
@@ -1636,6 +1656,25 @@ test_connection_to_dbus_setting_name (void)
 	s_wsec = make_test_wsec_setting ("connection-to-dbus-setting-name");
 	nm_connection_add_setting (connection, NM_SETTING (s_wsec));
 
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
+	g_object_set (s_wsec,
+	              NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED,
+	              NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NOT_SAVED,
+	              NULL);
+
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (!_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
+	g_object_set (s_wsec,
+	              NM_SETTING_WIRELESS_SECURITY_WEP_KEY_FLAGS, NM_SETTING_SECRET_FLAG_NONE,
+	              NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD_FLAGS, NM_SETTING_SECRET_FLAG_NONE,
+	              NULL);
+
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SECRETS, NULL));
+	g_assert (_nm_connection_aggregate (connection, NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS, NULL));
+
 	dict = nm_connection_to_dbus (connection, NM_CONNECTION_SERIALIZE_ALL);
 
 	/* Make sure the keys of the first level dict are setting names, not
@@ -2946,7 +2985,6 @@ test_connection_good_base_types (void)
 
 	setting = nm_setting_gsm_new ();
 	g_object_set (setting,
-	              NM_SETTING_GSM_NUMBER, "*99#",
 	              NM_SETTING_GSM_APN, "metered.billing.sucks",
 	              NULL);
 	nm_connection_add_setting (connection, setting);
@@ -3267,11 +3305,110 @@ test_data_compare_secrets_new (NMSettingSecretFlags secret_flags,
 }
 
 static void
+_test_compare_secrets_check_diff (NMSetting *a,
+                                  NMSetting *b,
+                                  NMSettingCompareFlags flags,
+                                  gboolean exp_same_psk,
+                                  gboolean exp_same_psk_flags)
+{
+	gs_unref_hashtable GHashTable *h = NULL;
+	NMSettingDiffResult _RESULT_IN_A = NM_SETTING_DIFF_RESULT_IN_A;
+	NMSettingDiffResult _RESULT_IN_B = NM_SETTING_DIFF_RESULT_IN_B;
+	gboolean invert_results;
+	gboolean diff_result;
+	NMSettingSecretFlags a_psk_flags = nm_setting_wireless_security_get_psk_flags (NM_SETTING_WIRELESS_SECURITY (a));
+	NMSettingSecretFlags b_psk_flags = nm_setting_wireless_security_get_psk_flags (NM_SETTING_WIRELESS_SECURITY (b));
+	const char *a_psk = nm_setting_wireless_security_get_psk (NM_SETTING_WIRELESS_SECURITY (a));
+	const char *b_psk = nm_setting_wireless_security_get_psk (NM_SETTING_WIRELESS_SECURITY (b));
+
+	g_assert (NM_IS_SETTING_WIRELESS_SECURITY (a));
+	g_assert (NM_IS_SETTING_WIRELESS_SECURITY (b));
+
+	invert_results = nmtst_get_rand_bool ();
+	if (invert_results) {
+		_RESULT_IN_A = NM_SETTING_DIFF_RESULT_IN_B;
+		_RESULT_IN_B = NM_SETTING_DIFF_RESULT_IN_A;
+	}
+
+	diff_result = nm_setting_diff (a, b, flags, invert_results, &h);
+
+	g_assert (exp_same_psk_flags == (a_psk_flags == b_psk_flags));
+
+	if (nm_streq0 (a_psk, b_psk))
+		g_assert (exp_same_psk);
+	else {
+		if (flags == NM_SETTING_COMPARE_FLAG_EXACT)
+			g_assert (!exp_same_psk);
+		else if (flags == NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS) {
+			if (   !NM_FLAGS_HAS (a_psk_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED)
+			    && !NM_FLAGS_HAS (b_psk_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED))
+				g_assert (!exp_same_psk);
+			else if (   !NM_FLAGS_HAS (a_psk_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED)
+			         && NM_FLAGS_HAS (b_psk_flags, NM_SETTING_SECRET_FLAG_AGENT_OWNED))
+				g_assert (!exp_same_psk);
+			else
+				g_assert (exp_same_psk);
+		} else if (flags == NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS) {
+			if (   !NM_FLAGS_HAS (a_psk_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED)
+			    && !NM_FLAGS_HAS (b_psk_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED))
+				g_assert (!exp_same_psk);
+			else if (   !NM_FLAGS_HAS (a_psk_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED)
+			         && NM_FLAGS_HAS (b_psk_flags, NM_SETTING_SECRET_FLAG_NOT_SAVED))
+				g_assert (!exp_same_psk);
+			else
+				g_assert (exp_same_psk);
+		} else if (flags == NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
+			g_assert (exp_same_psk);
+		else
+			g_assert_not_reached ();
+	}
+
+	g_assert (diff_result == (exp_same_psk && exp_same_psk_flags));
+	g_assert (diff_result == (!h));
+
+	if (!diff_result) {
+		if (flags == NM_SETTING_COMPARE_FLAG_EXACT)
+			g_assert (!exp_same_psk);
+		else if (   NM_IN_SET (flags, NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS,
+		                              NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
+		         && (a_psk_flags != b_psk_flags)
+		         && nm_setting_wireless_security_get_psk_flags (NM_SETTING_WIRELESS_SECURITY (a)) == NM_SETTING_SECRET_FLAG_NONE)
+			g_assert (!exp_same_psk);
+		else
+			g_assert (exp_same_psk);
+
+		g_assert ((!exp_same_psk) == g_hash_table_contains (h, NM_SETTING_WIRELESS_SECURITY_PSK));
+		if (!exp_same_psk) {
+			if (nm_setting_wireless_security_get_psk (NM_SETTING_WIRELESS_SECURITY (a)))
+				g_assert_cmpint (GPOINTER_TO_UINT (g_hash_table_lookup (h, NM_SETTING_WIRELESS_SECURITY_PSK)), ==, _RESULT_IN_A);
+			else
+				g_assert_cmpint (GPOINTER_TO_UINT (g_hash_table_lookup (h, NM_SETTING_WIRELESS_SECURITY_PSK)), ==, _RESULT_IN_B);
+		}
+
+		g_assert ((!exp_same_psk_flags) == g_hash_table_contains (h, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS));
+		if (!exp_same_psk_flags) {
+			if (nm_setting_wireless_security_get_psk_flags (NM_SETTING_WIRELESS_SECURITY (a)) != NM_SETTING_SECRET_FLAG_NONE)
+				g_assert_cmpint (GPOINTER_TO_UINT (g_hash_table_lookup (h, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS)), ==, _RESULT_IN_A);
+			else
+				g_assert_cmpint (GPOINTER_TO_UINT (g_hash_table_lookup (h, NM_SETTING_WIRELESS_SECURITY_PSK_FLAGS)), ==, _RESULT_IN_B);
+		}
+
+		g_assert_cmpint (g_hash_table_size (h), ==, (!exp_same_psk) + (!exp_same_psk_flags));
+	}
+
+	g_assert (diff_result == nm_setting_compare (a, b, flags));
+	g_assert (diff_result == nm_setting_compare (b, a, flags));
+
+}
+
+static void
 test_setting_compare_secrets (gconstpointer test_data)
 {
 	const TestDataCompareSecrets *data = test_data;
-	gs_unref_object NMSetting *old = NULL, *new = NULL;
-	gboolean success;
+	gs_unref_object NMConnection *conn_old = NULL;
+	gs_unref_object NMConnection *conn_new = NULL;
+	gs_unref_object NMSetting *old = NULL;
+	gs_unref_object NMSetting *new = NULL;
 
 	/* Make sure that a connection with transient/unsaved secrets compares
 	 * successfully to the same connection without those secrets.
@@ -3284,17 +3421,45 @@ test_setting_compare_secrets (gconstpointer test_data)
 	              NULL);
 	nm_setting_set_secret_flags (old, NM_SETTING_WIRELESS_SECURITY_PSK, data->secret_flags, NULL);
 
-	/* Clear the PSK from the duplicated setting */
 	new = nm_setting_duplicate (old);
-	if (data->remove_secret) {
+	if (data->remove_secret)
 		g_object_set (new, NM_SETTING_WIRELESS_SECURITY_PSK, NULL, NULL);
 
-		success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT);
-		g_assert (success == FALSE);
+	g_assert ((!data->remove_secret) == nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT));
+	g_assert ((!data->remove_secret) == nm_setting_compare (new, old, NM_SETTING_COMPARE_FLAG_EXACT));
+
+	_test_compare_secrets_check_diff (old, new, NM_SETTING_COMPARE_FLAG_EXACT, !data->remove_secret, TRUE);
+	_test_compare_secrets_check_diff (new, old, NM_SETTING_COMPARE_FLAG_EXACT, !data->remove_secret, TRUE);
+
+	g_assert (nm_setting_compare (old, new, data->comp_flags));
+	g_assert (nm_setting_compare (new, old, data->comp_flags));
+
+	_test_compare_secrets_check_diff (old, new, data->comp_flags, TRUE, TRUE);
+	_test_compare_secrets_check_diff (new, old, data->comp_flags, TRUE, TRUE);
+
+	/* OK. Try again, but this time not only change the secret, also let the secret flags differ... */
+	if (data->secret_flags != NM_SETTING_SECRET_FLAG_NONE) {
+		nm_setting_set_secret_flags (new, NM_SETTING_WIRELESS_SECURITY_PSK, NM_SETTING_SECRET_FLAG_NONE, NULL);
+
+		_test_compare_secrets_check_diff (old, new, NM_SETTING_COMPARE_FLAG_EXACT, FALSE, FALSE);
+		_test_compare_secrets_check_diff (new, old, NM_SETTING_COMPARE_FLAG_EXACT, FALSE, FALSE);
+
+		_test_compare_secrets_check_diff (old, new, data->comp_flags, TRUE, FALSE);
+		_test_compare_secrets_check_diff (new, old, data->comp_flags, FALSE, FALSE);
+
+		nm_setting_set_secret_flags (new, NM_SETTING_WIRELESS_SECURITY_PSK, data->secret_flags, NULL);
 	}
 
-	success = nm_setting_compare (old, new, data->comp_flags);
-	g_assert (success);
+	conn_old = nmtst_create_minimal_connection ("test-compare-secrets", NULL, NM_SETTING_WIRELESS_SETTING_NAME, NULL);
+	nm_connection_add_setting (conn_old, nm_setting_duplicate (old));
+	conn_new = nm_simple_connection_new_clone (conn_old);
+	nm_connection_add_setting (conn_new, nm_setting_duplicate (new));
+
+	g_assert ((!data->remove_secret) == nm_connection_compare (conn_old, conn_new, NM_SETTING_COMPARE_FLAG_EXACT));
+	g_assert ((!data->remove_secret) == nm_connection_compare (conn_new, conn_old, NM_SETTING_COMPARE_FLAG_EXACT));
+
+	g_assert (nm_connection_compare (conn_old, conn_new, data->comp_flags));
+	g_assert (nm_connection_compare (conn_new, conn_old, data->comp_flags));
 }
 
 static void
@@ -3473,6 +3638,41 @@ test_connection_changed_cb (NMConnection *connection, gboolean *data)
 	*data = TRUE;
 }
 
+static guint32
+_netmask_to_prefix (guint32 netmask)
+{
+	guint32 prefix;
+	guint8 v;
+	const guint8 *p = (guint8 *) &netmask;
+
+	if (p[3]) {
+		prefix = 24;
+		v = p[3];
+	} else if (p[2]) {
+		prefix = 16;
+		v = p[2];
+	} else if (p[1]) {
+		prefix = 8;
+		v = p[1];
+	} else {
+		prefix = 0;
+		v = p[0];
+	}
+
+	while (v) {
+		prefix++;
+		v <<= 1;
+	}
+
+	g_assert_cmpint (prefix, <=, 32);
+
+	/* we re-implemented the netmask-to-prefix code differently. Check
+	 * that they agree. */
+	g_assert_cmpint (prefix, ==, nm_utils_ip4_netmask_to_prefix (netmask));
+
+	return prefix;
+}
+
 static void
 test_ip4_prefix_to_netmask (void)
 {
@@ -3480,7 +3680,7 @@ test_ip4_prefix_to_netmask (void)
 
 	for (i = 0; i<=32; i++) {
 		guint32 netmask = _nm_utils_ip4_prefix_to_netmask (i);
-		int plen = nm_utils_ip4_netmask_to_prefix (netmask);
+		int plen = _netmask_to_prefix (netmask);
 
 		g_assert_cmpint (i, ==, plen);
 		{
@@ -3510,7 +3710,7 @@ test_ip4_netmask_to_prefix (void)
 		guint32 netmask = _nm_utils_ip4_prefix_to_netmask (i);
 		guint32 netmask_lowest_bit = netmask & ~_nm_utils_ip4_prefix_to_netmask (i-1);
 
-		g_assert_cmpint (i, ==, nm_utils_ip4_netmask_to_prefix (netmask));
+		g_assert_cmpint (i, ==, _netmask_to_prefix (netmask));
 
 		for (j = 0; j < 2*i; j++) {
 			guint32 r = g_rand_int (rand);
@@ -3524,7 +3724,7 @@ test_ip4_netmask_to_prefix (void)
 
 			/* create an invalid netmask with holes and check that the function
 			 * returns the longest prefix. */
-			prefix_holey = nm_utils_ip4_netmask_to_prefix (netmask_holey);
+			prefix_holey = _netmask_to_prefix (netmask_holey);
 
 			g_assert_cmpint (i, ==, prefix_holey);
 		}
@@ -5054,7 +5254,7 @@ test_setting_ip4_gateway (void)
 	GVariantBuilder addrs_builder;
 	GError *error = NULL;
 
-	g_assert_cmpstr (nm_utils_inet4_ntop (addr_vals_0[0], NULL), ==, "192.168.1.10");
+	nmtst_assert_ip4_address (addr_vals_0[0], "192.168.1.10");
 
 	/* When serializing on the daemon side, ipv4.gateway is copied to the first
 	 * entry of ipv4.addresses
@@ -5096,7 +5296,7 @@ test_setting_ip4_gateway (void)
 
 		addr_array = g_variant_get_fixed_array (addr_var, &length, sizeof (guint32));
 		g_assert_cmpint (length, ==, 3);
-		g_assert_cmpstr (nm_utils_inet4_ntop (addr_array[2], NULL), ==, "192.168.1.1");
+		nmtst_assert_ip4_address (addr_array[2], "192.168.1.1");
 		g_variant_unref (addr_var);
 	}
 	g_variant_unref (value);
@@ -5203,7 +5403,7 @@ test_setting_ip6_gateway (void)
 
 		gateway_bytes = g_variant_get_fixed_array (gateway_var, &length, 1);
 		g_assert_cmpint (length, ==, 16);
-		g_assert_cmpstr (nm_utils_inet6_ntop ((struct in6_addr *) gateway_bytes, NULL), ==, "abcd::1");
+		nmtst_assert_ip6_address ((struct in6_addr *) gateway_bytes, "abcd::1");
 		g_variant_unref (gateway_var);
 	}
 	g_variant_unref (value);
@@ -5322,6 +5522,158 @@ test_setting_user_data (void)
 
 /*****************************************************************************/
 
+typedef union {
+	struct sockaddr     sa;
+	struct sockaddr_in  in;
+	struct sockaddr_in6 in6;
+} SockAddrUnion;
+
+static void
+_sock_addr_endpoint (const char *endpoint,
+                     const char *host,
+                     gint32 port)
+{
+	nm_auto_unref_sockaddrendpoint NMSockAddrEndpoint *ep = NULL;
+	const char *s_endpoint;
+	const char *s_host;
+	gint32 s_port;
+	SockAddrUnion sockaddr = { };
+
+	g_assert (endpoint);
+	g_assert (!host == (port == -1));
+	g_assert (port >= -1 && port <= G_MAXUINT16);
+
+	ep = nm_sock_addr_endpoint_new (endpoint);
+	g_assert (ep);
+
+	s_endpoint = nm_sock_addr_endpoint_get_endpoint (ep);
+	s_host = nm_sock_addr_endpoint_get_host (ep);
+	s_port = nm_sock_addr_endpoint_get_port (ep);
+	g_assert_cmpstr (endpoint, ==, s_endpoint);
+	g_assert_cmpstr (host,     ==, s_host);
+	g_assert_cmpint (port,     ==, s_port);
+
+	g_assert (!nm_sock_addr_endpoint_get_fixed_sockaddr (ep, &sockaddr));
+
+	if (endpoint[0] != ' ') {
+		gs_free char *endpoint2 = NULL;
+
+		/* also test with a leading space */
+		endpoint2 = g_strdup_printf (" %s", endpoint);
+		_sock_addr_endpoint (endpoint2, host, port);
+	}
+
+	if (endpoint[0] && endpoint[strlen (endpoint) - 1] != ' ') {
+		gs_free char *endpoint2 = NULL;
+
+		/* also test with a trailing space */
+		endpoint2 = g_strdup_printf ("%s ", endpoint);
+		_sock_addr_endpoint (endpoint2, host, port);
+	}
+}
+
+static void
+_sock_addr_endpoint_fixed (const char *endpoint,
+                           const char *host,
+                           guint16 port,
+                           guint scope_id)
+{
+	nm_auto_unref_sockaddrendpoint NMSockAddrEndpoint *ep = NULL;
+	const char *s_endpoint;
+	const char *s_host;
+	gint32 s_port;
+	int addr_family;
+	NMIPAddr addrbin;
+	SockAddrUnion sockaddr = { };
+
+	g_assert (endpoint);
+	g_assert (host);
+	g_assert (port > 0);
+
+	if (!nm_utils_parse_inaddr_bin (AF_UNSPEC, host, &addr_family, &addrbin))
+		g_assert_not_reached ();
+
+	ep = nm_sock_addr_endpoint_new (endpoint);
+	g_assert (ep);
+
+	s_endpoint = nm_sock_addr_endpoint_get_endpoint (ep);
+	s_host = nm_sock_addr_endpoint_get_host (ep);
+	s_port = nm_sock_addr_endpoint_get_port (ep);
+	g_assert_cmpstr (endpoint, ==, s_endpoint);
+	g_assert_cmpstr (NULL,     !=, s_host);
+	g_assert_cmpint (port,     ==, s_port);
+
+	if (!nm_sock_addr_endpoint_get_fixed_sockaddr (ep, &sockaddr))
+		g_assert_not_reached ();
+
+	g_assert_cmpint (sockaddr.sa.sa_family, ==, addr_family);
+	if (addr_family == AF_INET) {
+		const SockAddrUnion s = {
+			.in = {
+				.sin_family = AF_INET,
+				.sin_addr   = addrbin.addr4_struct,
+				.sin_port   = htons (port),
+			},
+		};
+
+		g_assert_cmpint (sockaddr.in.sin_addr.s_addr, ==, addrbin.addr4);
+		g_assert_cmpint (sockaddr.in.sin_port, ==, htons (port));
+		g_assert (memcmp (&s, &sockaddr, sizeof (s.in)) == 0);
+	} else if (addr_family == AF_INET6) {
+		const SockAddrUnion s = {
+			.in6 = {
+				.sin6_family   = AF_INET6,
+				.sin6_addr     = addrbin.addr6,
+				.sin6_scope_id = scope_id,
+				.sin6_port     = htons (port),
+			},
+		};
+
+		g_assert (memcmp (&sockaddr.in6.sin6_addr, &addrbin, sizeof (addrbin.addr6)) == 0);
+		g_assert_cmpint (sockaddr.in6.sin6_port, ==, htons (port));
+		g_assert_cmpint (sockaddr.in6.sin6_scope_id, ==, scope_id);
+		g_assert_cmpint (sockaddr.in6.sin6_flowinfo, ==, 0);
+		g_assert (memcmp (&s, &sockaddr, sizeof (s.in6)) == 0);
+	} else
+		g_assert_not_reached ();
+}
+
+static void
+test_sock_addr_endpoint (void)
+{
+	_sock_addr_endpoint ("",                NULL, -1);
+	_sock_addr_endpoint (":",               NULL, -1);
+	_sock_addr_endpoint ("a",               NULL, -1);
+	_sock_addr_endpoint ("a:",              NULL, -1);
+	_sock_addr_endpoint (":a",              NULL, -1);
+	_sock_addr_endpoint ("[]:a",            NULL, -1);
+	_sock_addr_endpoint ("[]a",             NULL, -1);
+	_sock_addr_endpoint ("[]:",             NULL, -1);
+	_sock_addr_endpoint ("[a]b",            NULL, -1);
+	_sock_addr_endpoint ("[a:b",            NULL, -1);
+	_sock_addr_endpoint ("[a[:b",           NULL, -1);
+	_sock_addr_endpoint ("a:6",             "a",  6);
+	_sock_addr_endpoint ("a:6",             "a",  6);
+	_sock_addr_endpoint ("[a]:6",           "a",  6);
+	_sock_addr_endpoint ("[a]:6",           "a",  6);
+	_sock_addr_endpoint ("[a]:655",         "a",  655);
+	_sock_addr_endpoint ("[ab]:][6",        NULL, -1);
+	_sock_addr_endpoint ("[ab]:]:[6",       NULL, -1);
+	_sock_addr_endpoint ("[a[]:b",          NULL, -1);
+	_sock_addr_endpoint ("[192.169.6.x]:6", "192.169.6.x", 6);
+	_sock_addr_endpoint ("[192.169.6.x]:0", NULL, -1);
+	_sock_addr_endpoint ("192.169.6.7:0",   NULL, -1);
+
+	_sock_addr_endpoint_fixed ("192.169.6.7:6",   "192.169.6.7", 6, 0);
+	_sock_addr_endpoint_fixed ("[192.169.6.7]:6", "192.169.6.7", 6, 0);
+	_sock_addr_endpoint_fixed ("[a:b::]:6", "a:b::", 6, 0);
+	_sock_addr_endpoint_fixed ("[a:b::%7]:6", "a:b::", 6, 7);
+	_sock_addr_endpoint_fixed ("a:b::1%75:6", "a:b::1", 6, 75);
+	_sock_addr_endpoint_fixed ("a:b::1%0:64", "a:b::1", 64, 0);
+}
+
+/*****************************************************************************/
+
 static void
 test_hexstr2bin (void)
 {
@@ -5687,7 +6039,7 @@ __test_uuid (const char *expected_uuid, const char *str, gssize slen, char *uuid
 static void
 test_nm_utils_uuid_generate_from_strings (void)
 {
-	const NMUuid uuid0 = { 0 };
+	const NMUuid uuid0 = { };
 
 	g_assert_cmpmem (&uuid0, sizeof (uuid0), _uuid ("00000000-0000-0000-0000-000000000000"), 16);
 
@@ -7535,6 +7887,8 @@ int main (int argc, char **argv)
 	g_test_add_func ("/core/general/test_setting_compare_default_strv", test_setting_compare_default_strv);
 	g_test_add_func ("/core/general/test_setting_user_data", test_setting_user_data);
 
+	g_test_add_func ("/core/general/test_sock_addr_endpoint", test_sock_addr_endpoint);
+
 	g_test_add_func ("/core/general/hexstr2bin", test_hexstr2bin);
 	g_test_add_func ("/core/general/nm_strquote", test_nm_strquote);
 	g_test_add_func ("/core/general/test_nm_utils_uuid_generate_from_string", test_nm_utils_uuid_generate_from_string);
@@ -7564,4 +7918,3 @@ int main (int argc, char **argv)
 
 	return g_test_run ();
 }
-
diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c
index 78e01ce9..9b9d1c04 100644
--- a/libnm-core/tests/test-keyfile.c
+++ b/libnm-core/tests/test-keyfile.c
@@ -249,7 +249,7 @@ _keyfile_convert (NMConnection **con,
 		k0_c1_k2 = _nm_keyfile_write (k0_c1, write_handler, write_data);
 		k0_c1_k2_c3 = _nm_keyfile_read (k0_c1_k2, keyfile_name, read_handler, read_data, FALSE);
 
-		/* It is a expeced behavior, that if @k0 contains a relative path ca-cert, @k0_c1 will
+		/* It is a expected behavior, that if @k0 contains a relative path ca-cert, @k0_c1 will
 		 * contain that path as relative. But @k0_c1_k2 and @k0_c1_k2_c3 will have absolute paths.
 		 * In this case, hack up @k0_c1_k2_c3 to contain the same relative path. */
 		s1 = nm_connection_get_setting_802_1x (k0_c1);
@@ -417,7 +417,7 @@ test_8021x_cert (void)
 	g_assert_no_error (error);
 	g_assert (success);
 
-	/* test reseting ca-cert to different values and see whether we can write/read. */
+	/* test resetting ca-cert to different values and see whether we can write/read. */
 
 	nm_connection_add_setting (con, NM_SETTING (s_8021x));
 	nmtst_assert_connection_verifies_and_normalizable (con);
diff --git a/libnm-core/tests/test-secrets.c b/libnm-core/tests/test-secrets.c
index d31c43d2..5debe5cf 100644
--- a/libnm-core/tests/test-secrets.c
+++ b/libnm-core/tests/test-secrets.c
@@ -21,8 +21,6 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-setting-8021x.h"
 #include "nm-setting-cdma.h"
 #include "nm-setting-connection.h"
diff --git a/libnm-core/tests/test-setting.c b/libnm-core/tests/test-setting.c
index 4e010feb..2011273a 100644
--- a/libnm-core/tests/test-setting.c
+++ b/libnm-core/tests/test-setting.c
@@ -20,7 +20,6 @@
 #include "nm-default.h"
 
 #include <linux/pkt_sched.h>
-#include <string.h>
 
 #include "nm-utils.h"
 #include "nm-utils-private.h"
@@ -45,6 +44,101 @@
 
 /*****************************************************************************/
 
+/* converts @dict to a connection. In this case, @dict must be good, without warnings, so that
+ * NM_SETTING_PARSE_FLAGS_STRICT and NM_SETTING_PARSE_FLAGS_BEST_EFFORT yield the exact same results. */
+static NMConnection *
+_connection_new_from_dbus_strict (GVariant *dict,
+                                  gboolean normalize)
+{
+	gs_unref_object NMConnection *con_x_0 = NULL;
+	gs_unref_object NMConnection *con_x_s = NULL;
+	gs_unref_object NMConnection *con_x_e = NULL;
+	gs_unref_object NMConnection *con_n_0 = NULL;
+	gs_unref_object NMConnection *con_n_s = NULL;
+	gs_unref_object NMConnection *con_n_e = NULL;
+	gs_free_error GError *error = NULL;
+	guint i;
+
+	g_assert (g_variant_is_of_type (dict, NM_VARIANT_TYPE_CONNECTION));
+
+	con_x_0 = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_NONE, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_x_0), error);
+
+	con_x_s = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_STRICT, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_x_s), error);
+
+	con_x_e = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_x_e), error);
+
+	con_n_0 = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_NORMALIZE, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_n_0), error);
+
+	con_n_s = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_STRICT | NM_SETTING_PARSE_FLAGS_NORMALIZE, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_n_s), error);
+
+	con_n_e = _nm_simple_connection_new_from_dbus (dict, NM_SETTING_PARSE_FLAGS_BEST_EFFORT | NM_SETTING_PARSE_FLAGS_NORMALIZE, &error);
+	nmtst_assert_success (NM_IS_CONNECTION (con_n_e), error);
+
+	nmtst_assert_connection_verifies (con_x_0);
+	nmtst_assert_connection_verifies (con_x_e);
+	nmtst_assert_connection_verifies (con_x_s);
+
+	nmtst_assert_connection_verifies_without_normalization (con_n_0);
+	nmtst_assert_connection_verifies_without_normalization (con_n_e);
+	nmtst_assert_connection_verifies_without_normalization (con_n_s);
+
+	/* randomly compare some pairs that we created. They must all be equal,
+	 * after accounting for normalization. */
+	for (i = 0; i < 10; i++) {
+		NMConnection *cons[] = { con_x_0, con_x_s, con_x_e, con_n_0, con_n_s, con_n_e };
+		guint idx_a = (nmtst_get_rand_int () % G_N_ELEMENTS (cons));
+		guint idx_b = (nmtst_get_rand_int () % G_N_ELEMENTS (cons));
+		gboolean normalize_a, normalize_b;
+
+		if (idx_a <= 2 && idx_b <= 2) {
+			normalize_a = nmtst_get_rand_bool ();
+			normalize_b = normalize_a;
+		} else if (idx_a > 2 && idx_b > 2) {
+			normalize_a = nmtst_get_rand_bool ();
+			normalize_b = nmtst_get_rand_bool ();
+		} else {
+			normalize_a = (idx_a <= 2) ? TRUE : nmtst_get_rand_bool ();
+			normalize_b = (idx_b <= 2) ? TRUE : nmtst_get_rand_bool ();
+		}
+		nmtst_assert_connection_equals (cons[idx_a], normalize_a, cons[idx_b], normalize_b);
+	}
+
+	return (normalize)
+	       ? g_steal_pointer (&con_x_0)
+	       : g_steal_pointer (&con_n_0);
+}
+
+/*****************************************************************************/
+
+static char *
+_create_random_ipaddr (int addr_family, gboolean as_service)
+{
+	char delimiter = as_service ? ':' : '/';
+	int num;
+
+	if (addr_family == AF_UNSPEC)
+		addr_family = nmtst_rand_select (AF_INET, AF_INET6);
+
+	g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6));
+
+	if (as_service)
+		num = (nmtst_get_rand_int () % 1000) + 30000;
+	else
+		num = addr_family == AF_INET ? 32 : 128;
+
+	if (addr_family == AF_INET)
+		return g_strdup_printf ("192.168.%u.%u%c%d", nmtst_get_rand_int () % 256, nmtst_get_rand_int () % 256, delimiter, num);
+	else
+		return g_strdup_printf ("a:b:c::%02x:%02x%c%d", nmtst_get_rand_int () % 256, nmtst_get_rand_int () % 256, delimiter, num);
+}
+
+/*****************************************************************************/
+
 static void
 compare_blob_data (const char *test,
                    const char *key_path,
@@ -421,20 +515,17 @@ create_bond_connection (NMConnection **con, NMSettingBond **s_bond)
 }
 
 #define test_verify_options(exp, ...) \
-	G_STMT_START { \
-		const char *__opts[] = { __VA_ARGS__ , NULL }; \
-		\
-		_test_verify_options (__opts, exp); \
-	} G_STMT_END
+	_test_verify_options (NM_MAKE_STRV (__VA_ARGS__), exp)
 
 static void
-_test_verify_options (const char **options, gboolean expected_result)
+_test_verify_options (const char *const *options,
+                      gboolean expected_result)
 {
 	gs_unref_object NMConnection *con = NULL;
 	NMSettingBond *s_bond;
 	GError *error = NULL;
 	gboolean success;
-	const char **option;
+	const char *const *option;
 
 	create_bond_connection (&con, &s_bond);
 
@@ -1946,6 +2037,587 @@ test_tc_config_dbus (void)
 
 /*****************************************************************************/
 
+static GPtrArray *
+_rndt_wg_peers_create (void)
+{
+	GPtrArray *wg_peers;
+	guint i, n;
+
+	wg_peers = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_wireguard_peer_unref);
+
+	n = nmtst_get_rand_int () % 10;
+	for (i = 0; i < n; i++) {
+		NMWireGuardPeer *peer;
+		guint8 public_key_buf[NM_WIREGUARD_PUBLIC_KEY_LEN];
+		guint8 preshared_key_buf[NM_WIREGUARD_SYMMETRIC_KEY_LEN];
+		gs_free char *public_key = NULL;
+		gs_free char *preshared_key = NULL;
+		gs_free char *s_endpoint = NULL;
+		guint i_aip, n_aip;
+
+		/* we don't bother to create a valid curve25519 public key. Of course, libnm cannot
+		 * check whether the public key is bogus or not. Hence, for our purpose a random
+		 * bogus key is good enough. */
+		public_key = g_base64_encode (nmtst_rand_buf (NULL, public_key_buf, sizeof (public_key_buf)), sizeof (public_key_buf));
+
+		preshared_key = g_base64_encode (nmtst_rand_buf (NULL, preshared_key_buf, sizeof (preshared_key_buf)), sizeof (preshared_key_buf));
+
+		s_endpoint = _create_random_ipaddr (AF_UNSPEC, TRUE);
+
+		peer = nm_wireguard_peer_new ();
+		if (!nm_wireguard_peer_set_public_key (peer, public_key, TRUE))
+			g_assert_not_reached ();
+
+		if (!nm_wireguard_peer_set_preshared_key (peer, nmtst_rand_select (NULL, preshared_key), TRUE))
+			g_assert_not_reached ();
+
+		nm_wireguard_peer_set_preshared_key_flags (peer, nmtst_rand_select (NM_SETTING_SECRET_FLAG_NONE,
+		                                                                    NM_SETTING_SECRET_FLAG_NOT_SAVED,
+		                                                                    NM_SETTING_SECRET_FLAG_AGENT_OWNED));
+
+		nm_wireguard_peer_set_persistent_keepalive (peer,
+		                                            nmtst_rand_select ((guint32) 0, nmtst_get_rand_int ()));
+
+		if (!nm_wireguard_peer_set_endpoint (peer, nmtst_rand_select (s_endpoint, NULL), TRUE))
+			g_assert_not_reached ();
+
+		n_aip = nmtst_rand_select (0, nmtst_get_rand_int () % 10);
+		for (i_aip = 0; i_aip < n_aip; i_aip++) {
+			gs_free char *aip = NULL;
+
+			aip = _create_random_ipaddr (AF_UNSPEC, FALSE);
+			if (!nm_wireguard_peer_append_allowed_ip (peer, aip, FALSE))
+				g_assert_not_reached ();
+		}
+
+		g_assert (nm_wireguard_peer_is_valid (peer, TRUE, TRUE, NULL));
+
+		nm_wireguard_peer_seal (peer);
+		g_ptr_array_add (wg_peers, peer);
+	}
+
+	return wg_peers;
+}
+
+static const char *
+_rndt_wg_peers_to_keyfile (GPtrArray *wg_peers,
+                           gboolean strict,
+                           char **out_str)
+{
+	nm_auto_free_gstring GString *gstr = NULL;
+	nm_auto_free_gstring GString *gstr_aip = NULL;
+	guint i, j;
+
+	g_assert (wg_peers);
+	g_assert (out_str && !*out_str);
+
+	nm_gstring_prepare (&gstr);
+	for (i = 0; i < wg_peers->len; i++) {
+		const NMWireGuardPeer *peer = wg_peers->pdata[i];
+		gs_free char *s_endpoint = NULL;
+		gs_free char *s_preshared_key = NULL;
+		gs_free char *s_preshared_key_flags = NULL;
+		gs_free char *s_persistent_keepalive = NULL;
+		gs_free char *s_allowed_ips = NULL;
+
+		if (nm_wireguard_peer_get_endpoint (peer))
+			s_endpoint = g_strdup_printf ("endpoint=%s\n", nm_wireguard_peer_get_endpoint (peer));
+		else if (!strict)
+			s_endpoint = g_strdup_printf ("endpoint=\n");
+
+		if (   nm_wireguard_peer_get_preshared_key (peer)
+		    || !strict) {
+			if (nm_wireguard_peer_get_preshared_key_flags (peer) == NM_SETTING_SECRET_FLAG_NONE)
+				s_preshared_key = g_strdup_printf ("preshared-key=%s\n", nm_wireguard_peer_get_preshared_key (peer) ?: "");
+		}
+
+		if (   nm_wireguard_peer_get_preshared_key_flags (peer) != NM_SETTING_SECRET_FLAG_NOT_REQUIRED
+		    || !strict)
+			s_preshared_key_flags = g_strdup_printf ("preshared-key-flags=%d\n", (int) nm_wireguard_peer_get_preshared_key_flags (peer));
+
+		if (   nm_wireguard_peer_get_persistent_keepalive (peer) != 0
+		    || !strict)
+			s_persistent_keepalive = g_strdup_printf ("persistent-keepalive=%u\n", nm_wireguard_peer_get_persistent_keepalive (peer));
+
+		if (   nm_wireguard_peer_get_allowed_ips_len (peer) > 0
+		    || !strict) {
+			nm_gstring_prepare (&gstr_aip);
+			for (j = 0; j < nm_wireguard_peer_get_allowed_ips_len (peer); j++)
+				g_string_append_printf (gstr_aip, "%s;", nm_wireguard_peer_get_allowed_ip (peer, j, NULL));
+			s_allowed_ips = g_strdup_printf ("allowed-ips=%s\n", gstr_aip->str);
+		}
+
+		if (   !s_endpoint
+		    && !s_preshared_key
+		    && !s_preshared_key_flags
+		    && !s_persistent_keepalive
+		    && !s_allowed_ips)
+			s_endpoint = g_strdup_printf ("endpoint=\n");
+
+		g_string_append_printf (gstr,
+		                        "\n"
+		                        "[wireguard-peer.%s]\n"
+		                        "%s" /* endpoint */
+		                        "%s" /* preshared-key */
+		                        "%s" /* preshared-key-flags */
+		                        "%s" /* persistent-keepalive */
+		                        "%s" /* allowed-ips */
+		                        "",
+		                        nm_wireguard_peer_get_public_key (peer),
+		                        s_endpoint ?: "",
+		                        s_preshared_key ?: "",
+		                        s_preshared_key_flags ?: "",
+		                        s_persistent_keepalive ?: "",
+		                        s_allowed_ips ?: "");
+	}
+
+	return (*out_str = g_string_free (g_steal_pointer (&gstr), FALSE));
+}
+
+static void
+_rndt_wg_peers_assert_equal (NMSettingWireGuard *s_wg,
+                             GPtrArray *peers,
+                             gboolean consider_persistent_secrets,
+                             gboolean consider_all_secrets,
+                             gboolean expect_no_secrets)
+{
+	guint i;
+
+	g_assert (NM_IS_SETTING_WIREGUARD (s_wg));
+	g_assert (peers);
+
+	g_assert_cmpint (peers->len, ==, nm_setting_wireguard_get_peers_len (s_wg));
+
+	for (i = 0; i < peers->len; i++) {
+		const NMWireGuardPeer *a = peers->pdata[i];
+		const NMWireGuardPeer *b = nm_setting_wireguard_get_peer (s_wg, i);
+		gboolean consider_secrets;
+
+		g_assert (a);
+		g_assert (b);
+
+		g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS), ==, 0);
+
+		if (   consider_all_secrets
+		    || !nm_wireguard_peer_get_preshared_key (a))
+			consider_secrets = TRUE;
+		else if (nm_wireguard_peer_get_preshared_key (b))
+			consider_secrets = TRUE;
+		else if (   consider_persistent_secrets
+		         && nm_wireguard_peer_get_preshared_key_flags (b) == NM_SETTING_SECRET_FLAG_NONE)
+			consider_secrets = TRUE;
+		else
+			consider_secrets = FALSE;
+
+		if (consider_secrets) {
+			g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), ==, nm_wireguard_peer_get_preshared_key (b));
+			g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_EXACT), ==, 0);
+		}
+
+		if (expect_no_secrets)
+			g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (b), ==, NULL);
+	}
+}
+
+static void
+_rndt_wg_peers_fix_secrets (NMSettingWireGuard *s_wg,
+                            GPtrArray *peers)
+{
+	guint i;
+
+	g_assert (NM_IS_SETTING_WIREGUARD (s_wg));
+	g_assert (peers);
+
+	g_assert_cmpint (peers->len, ==, nm_setting_wireguard_get_peers_len (s_wg));
+
+	for (i = 0; i < peers->len; i++) {
+		const NMWireGuardPeer *a = peers->pdata[i];
+		const NMWireGuardPeer *b = nm_setting_wireguard_get_peer (s_wg, i);
+		nm_auto_unref_wgpeer NMWireGuardPeer *b_clone = NULL;
+
+		g_assert (a);
+		g_assert (b);
+
+		g_assert_cmpint (nm_wireguard_peer_get_preshared_key_flags (a), ==, nm_wireguard_peer_get_preshared_key_flags (b));
+		g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS), ==, 0);
+
+		if (!nm_streq0 (nm_wireguard_peer_get_preshared_key (a),
+		                nm_wireguard_peer_get_preshared_key (b))) {
+			g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), !=, NULL);
+			g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (b), ==, NULL);
+			g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_AGENT_OWNED,
+			                                                                    NM_SETTING_SECRET_FLAG_NOT_SAVED));
+			b_clone = nm_wireguard_peer_new_clone (b, TRUE);
+			if (!nm_wireguard_peer_set_preshared_key (b_clone, nm_wireguard_peer_get_preshared_key (a), TRUE))
+				g_assert_not_reached ();
+			nm_setting_wireguard_set_peer (s_wg, b_clone, i);
+			b = nm_setting_wireguard_get_peer (s_wg, i);
+			g_assert (b == b_clone);
+		} else {
+			if (nm_wireguard_peer_get_preshared_key (a)) {
+				g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_NONE,
+				                                                                    NM_SETTING_SECRET_FLAG_NOT_REQUIRED));
+			} else {
+				g_assert (NM_IN_SET (nm_wireguard_peer_get_preshared_key_flags (a), NM_SETTING_SECRET_FLAG_AGENT_OWNED,
+				                                                                    NM_SETTING_SECRET_FLAG_NONE,
+				                                                                    NM_SETTING_SECRET_FLAG_NOT_SAVED,
+				                                                                    NM_SETTING_SECRET_FLAG_NOT_REQUIRED));
+			}
+		}
+
+		g_assert_cmpstr (nm_wireguard_peer_get_preshared_key (a), ==, nm_wireguard_peer_get_preshared_key (b));
+		g_assert_cmpint (nm_wireguard_peer_cmp (a, b, NM_SETTING_COMPARE_FLAG_EXACT), ==, 0);
+	}
+}
+
+static void
+test_roundtrip_conversion (gconstpointer test_data)
+{
+	const int MODE = GPOINTER_TO_INT (test_data);
+	const char *ID= nm_sprintf_bufa (100, "roundtip-conversion-%d", MODE);
+	const char *UUID= "63376701-b61e-4318-bf7e-664a1c1eeaab";
+	const char *INTERFACE_NAME = nm_sprintf_bufa (100, "ifname%d", MODE);
+	guint32 ETH_MTU = nmtst_rand_select ((guint32) 0u,
+	                                     nmtst_get_rand_int ());
+	const char *WG_PRIVATE_KEY = nmtst_get_rand_bool ()
+	                             ? "yGXGK+5bVnxSJUejH4vbpXbq+ZtaG4NB8IHRK/aVtE0="
+	                             : NULL;
+	const NMSettingSecretFlags WG_PRIVATE_KEY_FLAGS = nmtst_rand_select (NM_SETTING_SECRET_FLAG_NONE,
+	                                                                     NM_SETTING_SECRET_FLAG_NOT_SAVED,
+	                                                                     NM_SETTING_SECRET_FLAG_AGENT_OWNED);
+	const guint WG_LISTEN_PORT = nmtst_rand_select (0u,
+	                                                nmtst_get_rand_int () % 0x10000);
+	const guint WG_FWMARK = nmtst_rand_select (0u,
+	                                           nmtst_get_rand_int ());
+	gs_unref_ptrarray GPtrArray *kf_data_arr = g_ptr_array_new_with_free_func (g_free);
+	gs_unref_ptrarray GPtrArray *wg_peers = NULL;
+	const NMConnectionSerializationFlags dbus_serialization_flags[] = {
+		NM_CONNECTION_SERIALIZE_ALL,
+		NM_CONNECTION_SERIALIZE_NO_SECRETS,
+		NM_CONNECTION_SERIALIZE_ONLY_SECRETS,
+	};
+	guint dbus_serialization_flags_idx;
+	gs_unref_object NMConnection *con = NULL;
+	gs_free_error GError *error = NULL;
+	gs_free char *tmp_str = NULL;
+	guint kf_data_idx;
+	NMSettingConnection *s_con = NULL;
+	NMSettingWired *s_eth = NULL;
+	NMSettingWireGuard *s_wg = NULL;
+	guint i;
+
+	switch (MODE) {
+	case 0:
+		con = nmtst_create_minimal_connection (ID, UUID, NM_SETTING_WIRED_SETTING_NAME, &s_con);
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_INTERFACE_NAME,
+		              INTERFACE_NAME,
+		              NULL);
+		nmtst_connection_normalize (con);
+
+		s_eth = NM_SETTING_WIRED (nm_connection_get_setting (con, NM_TYPE_SETTING_WIRED));
+		g_assert (NM_IS_SETTING_WIRED (s_eth));
+
+		g_object_set (s_eth,
+		              NM_SETTING_WIRED_MTU,
+		              ETH_MTU,
+		              NULL);
+
+		g_ptr_array_add (kf_data_arr,
+		    g_strdup_printf ("[connection]\n"
+		                     "id=%s\n"
+		                     "uuid=%s\n"
+		                     "type=ethernet\n"
+		                     "interface-name=%s\n"
+		                     "permissions=\n"
+		                     "\n"
+		                     "[ethernet]\n"
+		                     "mac-address-blacklist=\n"
+		                     "%s" /* mtu */
+		                     "\n"
+		                     "[ipv4]\n"
+		                     "dns-search=\n"
+		                     "method=auto\n"
+		                     "\n"
+		                     "[ipv6]\n"
+		                     "addr-gen-mode=stable-privacy\n"
+		                     "dns-search=\n"
+		                     "method=auto\n"
+		                     "",
+		                     ID,
+		                     UUID,
+		                     INTERFACE_NAME,
+		                       (ETH_MTU != 0)
+		                     ? nm_sprintf_bufa (100, "mtu=%u\n", ETH_MTU)
+		                     : ""));
+
+		g_ptr_array_add (kf_data_arr,
+		    g_strdup_printf ("[connection]\n"
+		                     "id=%s\n"
+		                     "uuid=%s\n"
+		                     "type=ethernet\n"
+		                     "interface-name=%s\n"
+		                     "permissions=\n"
+		                     "\n"
+		                     "[ethernet]\n"
+		                     "mac-address-blacklist=\n"
+		                     "%s" /* mtu */
+		                     "\n"
+		                     "[ipv4]\n"
+		                     "dns-search=\n"
+		                     "method=auto\n"
+		                     "\n"
+		                     "[ipv6]\n"
+		                     "addr-gen-mode=stable-privacy\n"
+		                     "dns-search=\n"
+		                     "method=auto\n"
+		                     "",
+		                     ID,
+		                     UUID,
+		                     INTERFACE_NAME,
+		                       (ETH_MTU != 0)
+		                     ? nm_sprintf_bufa (100, "mtu=%d\n", (int) ETH_MTU)
+		                     : ""));
+
+		break;
+
+	case 1:
+		con = nmtst_create_minimal_connection (ID, UUID, "wireguard", &s_con);
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_INTERFACE_NAME,
+		              INTERFACE_NAME,
+		              NULL);
+		nmtst_connection_normalize (con);
+
+		s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (con, NM_TYPE_SETTING_WIREGUARD));
+
+		g_ptr_array_add (kf_data_arr,
+		    g_strdup_printf ("[connection]\n"
+		                     "id=%s\n"
+		                     "uuid=%s\n"
+		                     "type=wireguard\n"
+		                     "interface-name=%s\n"
+		                     "permissions=\n"
+		                     "\n"
+		                     "[ipv4]\n"
+		                     "dns-search=\n"
+		                     "method=disabled\n"
+		                     "\n"
+		                     "[ipv6]\n"
+		                     "addr-gen-mode=stable-privacy\n"
+		                     "dns-search=\n"
+		                     "method=ignore\n"
+		                     "",
+		                     ID,
+		                     UUID,
+		                     INTERFACE_NAME));
+		break;
+
+	case 2:
+		con = nmtst_create_minimal_connection (ID, UUID, "wireguard", &s_con);
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_INTERFACE_NAME,
+		              INTERFACE_NAME,
+		              NULL);
+		nmtst_connection_normalize (con);
+
+		s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (con, NM_TYPE_SETTING_WIREGUARD));
+		g_object_set (s_wg,
+		              NM_SETTING_WIREGUARD_PRIVATE_KEY,
+		              WG_PRIVATE_KEY,
+		              NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS,
+		              WG_PRIVATE_KEY_FLAGS,
+		              NM_SETTING_WIREGUARD_LISTEN_PORT,
+		              WG_LISTEN_PORT,
+		              NM_SETTING_WIREGUARD_FWMARK,
+		              WG_FWMARK,
+		              NULL);
+
+		wg_peers = _rndt_wg_peers_create ();
+
+		for (i = 0; i < wg_peers->len; i++)
+			nm_setting_wireguard_append_peer (s_wg, wg_peers->pdata[i]);
+
+		nm_clear_g_free (&tmp_str);
+
+		g_ptr_array_add (kf_data_arr,
+		    g_strdup_printf ("[connection]\n"
+		                     "id=%s\n"
+		                     "uuid=%s\n"
+		                     "type=wireguard\n"
+		                     "interface-name=%s\n"
+		                     "permissions=\n"
+		                     "%s" /* [wireguard] */
+		                     "%s" /* fwmark */
+		                     "%s" /* listen-port */
+		                     "%s" /* private-key-flags */
+		                     "%s" /* private-key */
+		                     "%s" /* [wireguard-peers*] */
+		                     "\n"
+		                     "[ipv4]\n"
+		                     "dns-search=\n"
+		                     "method=disabled\n"
+		                     "\n"
+		                     "[ipv6]\n"
+		                     "addr-gen-mode=stable-privacy\n"
+		                     "dns-search=\n"
+		                     "method=ignore\n"
+		                     "",
+		                     ID,
+		                     UUID,
+		                     INTERFACE_NAME,
+		                     (  (   (WG_FWMARK != 0)
+		                         || (WG_LISTEN_PORT != 0)
+		                         || (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE)
+		                         || (   WG_PRIVATE_KEY
+		                             && WG_PRIVATE_KEY_FLAGS == NM_SETTING_SECRET_FLAG_NONE))
+		                      ? "\n[wireguard]\n"
+		                      : ""),
+		                     (  (WG_FWMARK != 0)
+		                      ? nm_sprintf_bufa (100, "fwmark=%u\n", WG_FWMARK)
+		                      : ""),
+		                     (  (WG_LISTEN_PORT != 0)
+		                      ? nm_sprintf_bufa (100, "listen-port=%u\n", WG_LISTEN_PORT)
+		                      : ""),
+		                     (  (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE)
+		                      ? nm_sprintf_bufa (100, "private-key-flags=%u\n", (guint) WG_PRIVATE_KEY_FLAGS)
+		                      : ""),
+		                     (  (   WG_PRIVATE_KEY
+		                         && WG_PRIVATE_KEY_FLAGS == NM_SETTING_SECRET_FLAG_NONE)
+		                      ? nm_sprintf_bufa (100, "private-key=%s\n", WG_PRIVATE_KEY)
+		                      : ""),
+		                     _rndt_wg_peers_to_keyfile (wg_peers, TRUE, &tmp_str)));
+
+		_rndt_wg_peers_assert_equal (s_wg, wg_peers, TRUE, TRUE, FALSE);
+		break;
+
+	default:
+		g_assert_not_reached ();
+	}
+
+	/* the first kf_data_arr entry is special: it is the exact result of what we expect
+	 * when converting @con to keyfile. Write @con to keyfile and compare the expected result
+	 * literally. */
+	{
+		gs_unref_keyfile GKeyFile *kf = NULL;
+
+		kf = nm_keyfile_write (con, NULL, NULL, &error);
+		nmtst_assert_success (kf, error);
+
+		/* the first kf_data_arr entry is special: it must be what the writer would
+		 * produce again. */
+		nmtst_keyfile_assert_data (kf, kf_data_arr->pdata[0], -1);
+	}
+
+	/* check that reading any of kf_data_arr yields the same result that we expect. */
+	for (kf_data_idx = 0; kf_data_idx < kf_data_arr->len; kf_data_idx++) {
+		gs_unref_object NMConnection *con2 = NULL;
+		NMSettingWireGuard *s_wg2 = NULL;
+		NMSettingWired *s_eth2 = NULL;
+
+		con2 = nmtst_create_connection_from_keyfile (kf_data_arr->pdata[kf_data_idx], "/no/where/file.nmconnection");
+
+		switch (MODE) {
+		case 0:
+			s_eth2 = NM_SETTING_WIRED (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIRED));
+			g_assert (NM_IS_SETTING_WIRED (s_eth2));
+
+			if (   ETH_MTU > (guint32) G_MAXINT
+			    && kf_data_idx == 1) {
+				/* older versions wrote values > 2^21 as signed integers, but the reader would
+				 * always reject such negative values for G_TYPE_UINT.
+				 *
+				 * The test case kf_data_idx #1 still writes the values in the old style.
+				 * The behavior was fixed, but such values are still rejected as invalid.
+				 *
+				 * Patch the setting so that the comparison below succeeds are usual. */
+				g_assert_cmpint (nm_setting_wired_get_mtu (s_eth2), ==, 0);
+				g_object_set (s_eth2,
+				              NM_SETTING_WIRED_MTU,
+				              ETH_MTU,
+				              NULL);
+			}
+
+			g_assert_cmpint (nm_setting_wired_get_mtu (s_eth), ==, ETH_MTU);
+			g_assert_cmpint (nm_setting_wired_get_mtu (s_eth2), ==, ETH_MTU);
+			break;
+
+		case 1:
+			s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
+			g_assert (NM_IS_SETTING_WIREGUARD (s_wg2));
+
+			g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg), ==, NULL);
+			g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, NULL);
+			break;
+
+		case 2:
+			s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
+			g_assert (NM_IS_SETTING_WIREGUARD (s_wg2));
+
+			/* the private key was lost due to the secret-flags. Patch it. */
+			if (WG_PRIVATE_KEY_FLAGS != NM_SETTING_SECRET_FLAG_NONE) {
+				g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, NULL);
+				g_object_set (s_wg2,
+				              NM_SETTING_WIREGUARD_PRIVATE_KEY,
+				              WG_PRIVATE_KEY,
+				              NULL);
+			}
+
+			g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg), ==, WG_PRIVATE_KEY);
+			g_assert_cmpstr (nm_setting_wireguard_get_private_key (s_wg2), ==, WG_PRIVATE_KEY);
+
+			_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, FALSE, FALSE);
+			_rndt_wg_peers_fix_secrets (s_wg2, wg_peers);
+			_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, TRUE, FALSE);
+			break;
+		}
+
+		nmtst_assert_connection_equals (con, nmtst_get_rand_bool (), con2, nmtst_get_rand_bool ());
+	}
+
+	for (dbus_serialization_flags_idx = 0; dbus_serialization_flags_idx < G_N_ELEMENTS (dbus_serialization_flags); dbus_serialization_flags_idx++) {
+		NMConnectionSerializationFlags flag = dbus_serialization_flags[dbus_serialization_flags_idx];
+		gs_unref_variant GVariant *con_var = NULL;
+		gs_unref_object NMConnection *con2 = NULL;
+		NMSettingWireGuard *s_wg2 = NULL;
+
+		con_var = nm_connection_to_dbus (con, flag);
+		g_assert (g_variant_is_of_type (con_var, NM_VARIANT_TYPE_CONNECTION));
+		g_assert (g_variant_is_floating (con_var));
+		g_variant_ref_sink (con_var);
+
+		if (flag == NM_CONNECTION_SERIALIZE_ALL) {
+			con2 = _connection_new_from_dbus_strict (con_var, TRUE);
+			nmtst_assert_connection_equals (con, nmtst_get_rand_bool (), con2, nmtst_get_rand_bool ());
+
+			{
+				gs_unref_keyfile GKeyFile *kf = NULL;
+
+				kf = nm_keyfile_write (con2, NULL, NULL, &error);
+				nmtst_assert_success (kf, error);
+				nmtst_keyfile_assert_data (kf, kf_data_arr->pdata[0], -1);
+			}
+		}
+
+		switch (MODE) {
+		case 2:
+			if (flag == NM_CONNECTION_SERIALIZE_ALL) {
+				s_wg2 = NM_SETTING_WIREGUARD (nm_connection_get_setting (con2, NM_TYPE_SETTING_WIREGUARD));
+
+				if (flag == NM_CONNECTION_SERIALIZE_ALL)
+					_rndt_wg_peers_assert_equal (s_wg2, wg_peers, TRUE, TRUE, FALSE);
+				else if (flag == NM_CONNECTION_SERIALIZE_NO_SECRETS)
+					_rndt_wg_peers_assert_equal (s_wg2, wg_peers, FALSE, FALSE, TRUE);
+				else
+					g_assert_not_reached ();
+			}
+			break;
+		}
+	}
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE ();
 
 int
@@ -2022,5 +2694,9 @@ main (int argc, char **argv)
 	g_test_add_func ("/libnm/settings/team-port/sycn_from_config_full", test_team_port_full_config);
 #endif
 
+	g_test_add_data_func ("/libnm/settings/roundtrip-conversion/general/0", GINT_TO_POINTER (0), test_roundtrip_conversion);
+	g_test_add_data_func ("/libnm/settings/roundtrip-conversion/wireguard/1", GINT_TO_POINTER (1), test_roundtrip_conversion);
+	g_test_add_data_func ("/libnm/settings/roundtrip-conversion/wireguard/2", GINT_TO_POINTER (2), test_roundtrip_conversion);
+
 	return g_test_run ();
 }
diff --git a/libnm-core/tests/test-settings-defaults.c b/libnm-core/tests/test-settings-defaults.c
index 95570b03..9e640543 100644
--- a/libnm-core/tests/test-settings-defaults.c
+++ b/libnm-core/tests/test-settings-defaults.c
@@ -21,8 +21,6 @@
 
 #include "nm-default.h"
 
-#include <string.h>
-
 #include "nm-utils.h"
 #include "nm-setting-8021x.h"
 #include "nm-setting-cdma.h"