about summary refs log tree commit diff
path: root/src/libnm-core-impl
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2022-01-13 22:30:39 +0100
committerMichael Biebl <biebl@debian.org>2022-01-13 22:30:39 +0100
commit88c227d90a6b7b388c5c85d72802a0ca8f05ed5c (patch)
tree71f32df6617802270e8a78574bd8e1637dc532f4 /src/libnm-core-impl
parente74c568b07b50b97873fb4ee1d776dedefbd54d6 (diff)
New upstream version 1.34.0 upstream/1.34.0
Diffstat (limited to 'src/libnm-core-impl')
-rw-r--r--src/libnm-core-impl/meson.build1
-rw-r--r--src/libnm-core-impl/nm-connection-private.h2
-rw-r--r--src/libnm-core-impl/nm-connection.c78
-rw-r--r--src/libnm-core-impl/nm-crypto.c2
-rw-r--r--src/libnm-core-impl/nm-dbus-utils.c2
-rw-r--r--src/libnm-core-impl/nm-keyfile.c16
-rw-r--r--src/libnm-core-impl/nm-meta-setting-base-impl.c13
-rw-r--r--src/libnm-core-impl/nm-property-compare.h2
-rw-r--r--src/libnm-core-impl/nm-setting-6lowpan.c73
-rw-r--r--src/libnm-core-impl/nm-setting-8021x.c1077
-rw-r--r--src/libnm-core-impl/nm-setting-adsl.c217
-rw-r--r--src/libnm-core-impl/nm-setting-bluetooth.c97
-rw-r--r--src/libnm-core-impl/nm-setting-bond-port.c173
-rw-r--r--src/libnm-core-impl/nm-setting-bond.c40
-rw-r--r--src/libnm-core-impl/nm-setting-bridge-port.c137
-rw-r--r--src/libnm-core-impl/nm-setting-bridge.c713
-rw-r--r--src/libnm-core-impl/nm-setting-cdma.c185
-rw-r--r--src/libnm-core-impl/nm-setting-connection.c722
-rw-r--r--src/libnm-core-impl/nm-setting-dcb.c297
-rw-r--r--src/libnm-core-impl/nm-setting-dummy.c2
-rw-r--r--src/libnm-core-impl/nm-setting-ethtool.c5
-rw-r--r--src/libnm-core-impl/nm-setting-generic.c2
-rw-r--r--src/libnm-core-impl/nm-setting-gsm.c43
-rw-r--r--src/libnm-core-impl/nm-setting-hostname.c136
-rw-r--r--src/libnm-core-impl/nm-setting-infiniband.c28
-rw-r--r--src/libnm-core-impl/nm-setting-ip-config.c361
-rw-r--r--src/libnm-core-impl/nm-setting-ip-tunnel.c113
-rw-r--r--src/libnm-core-impl/nm-setting-ip4-config.c193
-rw-r--r--src/libnm-core-impl/nm-setting-ip6-config.c172
-rw-r--r--src/libnm-core-impl/nm-setting-macsec.c45
-rw-r--r--src/libnm-core-impl/nm-setting-macvlan.c49
-rw-r--r--src/libnm-core-impl/nm-setting-match.c2
-rw-r--r--src/libnm-core-impl/nm-setting-olpc-mesh.c121
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-bridge.c60
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-dpdk.c2
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-external-ids.c54
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-interface.c2
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-patch.c2
-rw-r--r--src/libnm-core-impl/nm-setting-ovs-port.c198
-rw-r--r--src/libnm-core-impl/nm-setting-ppp.c465
-rw-r--r--src/libnm-core-impl/nm-setting-pppoe.c188
-rw-r--r--src/libnm-core-impl/nm-setting-private.h713
-rw-r--r--src/libnm-core-impl/nm-setting-proxy.c147
-rw-r--r--src/libnm-core-impl/nm-setting-serial.c136
-rw-r--r--src/libnm-core-impl/nm-setting-sriov.c65
-rw-r--r--src/libnm-core-impl/nm-setting-tc-config.c481
-rw-r--r--src/libnm-core-impl/nm-setting-team-port.c95
-rw-r--r--src/libnm-core-impl/nm-setting-team.c97
-rw-r--r--src/libnm-core-impl/nm-setting-tun.c198
-rw-r--r--src/libnm-core-impl/nm-setting-user.c74
-rw-r--r--src/libnm-core-impl/nm-setting-veth.c2
-rw-r--r--src/libnm-core-impl/nm-setting-vlan.c58
-rw-r--r--src/libnm-core-impl/nm-setting-vpn.c90
-rw-r--r--src/libnm-core-impl/nm-setting-vrf.c61
-rw-r--r--src/libnm-core-impl/nm-setting-vxlan.c96
-rw-r--r--src/libnm-core-impl/nm-setting-wifi-p2p.c128
-rw-r--r--src/libnm-core-impl/nm-setting-wimax.c27
-rw-r--r--src/libnm-core-impl/nm-setting-wired.c164
-rw-r--r--src/libnm-core-impl/nm-setting-wireguard.c91
-rw-r--r--src/libnm-core-impl/nm-setting-wireless-security.c43
-rw-r--r--src/libnm-core-impl/nm-setting-wireless.c198
-rw-r--r--src/libnm-core-impl/nm-setting-wpan.c191
-rw-r--r--src/libnm-core-impl/nm-setting.c1951
-rw-r--r--src/libnm-core-impl/nm-simple-connection.c2
-rw-r--r--src/libnm-core-impl/nm-team-utils.c77
-rw-r--r--src/libnm-core-impl/nm-team-utils.h3
-rw-r--r--src/libnm-core-impl/nm-utils-private.h24
-rw-r--r--src/libnm-core-impl/nm-utils.c275
-rw-r--r--src/libnm-core-impl/nm-vpn-plugin-info.c8
-rw-r--r--src/libnm-core-impl/tests/test-crypto.c2
-rw-r--r--src/libnm-core-impl/tests/test-general.c618
-rw-r--r--src/libnm-core-impl/tests/test-keyfile.c4
-rw-r--r--src/libnm-core-impl/tests/test-setting.c504
73 files changed, 6846 insertions, 5867 deletions
diff --git a/src/libnm-core-impl/meson.build b/src/libnm-core-impl/meson.build
index f175ea3b..2e1f1751 100644
--- a/src/libnm-core-impl/meson.build
+++ b/src/libnm-core-impl/meson.build
@@ -37,6 +37,7 @@ libnm_core_settings_sources = files(
   'nm-setting-adsl.c',
   'nm-setting-bluetooth.c',
   'nm-setting-bond.c',
+  'nm-setting-bond-port.c',
   'nm-setting-bridge-port.c',
   'nm-setting-bridge.c',
   'nm-setting-cdma.c',
diff --git a/src/libnm-core-impl/nm-connection-private.h b/src/libnm-core-impl/nm-connection-private.h
index 0580625f..27769ec1 100644
--- a/src/libnm-core-impl/nm-connection-private.h
+++ b/src/libnm-core-impl/nm-connection-private.h
@@ -7,7 +7,7 @@
 #define __NM_CONNECTION_PRIVATE_H__
 
 #if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-    #error Cannot use this header.
+#error Cannot use this header.
 #endif
 
 #include "nm-setting.h"
diff --git a/src/libnm-core-impl/nm-connection.c b/src/libnm-core-impl/nm-connection.c
index 4ac7b322..dc85ea0c 100644
--- a/src/libnm-core-impl/nm-connection.c
+++ b/src/libnm-core-impl/nm-connection.c
@@ -944,8 +944,8 @@ _nm_setting_connection_verify_secondaries(GArray *secondaries, GError **error)
         gs_free const char **strv_to_free = NULL;
         const char **        strv2;
 
-        strv2 = nm_utils_strv_dup_shallow_maybe_a(20, strv, len, &strv_to_free);
-        nm_utils_strv_sort(strv2, len);
+        strv2 = nm_strv_dup_shallow_maybe_a(20, strv, len, &strv_to_free);
+        nm_strv_sort(strv2, len);
         has_duplicate = nm_strv_has_duplicate(strv2, len, TRUE);
     }
 
@@ -1002,7 +1002,7 @@ _normalize_connection_secondaries(NMConnection *self)
         if (!nm_uuid_is_valid_nm(s, &uuid_is_normalized, uuid_normalized))
             continue;
 
-        if (nm_utils_strv_find_first(strv, j, uuid_is_normalized ? uuid_normalized : s) >= 0)
+        if (nm_strv_find_first(strv, j, uuid_is_normalized ? uuid_normalized : s) >= 0)
             continue;
 
         strv[j++] = uuid_is_normalized ? g_strdup(uuid_normalized) : g_steal_pointer(&s);
@@ -1074,6 +1074,7 @@ _nm_connection_detect_slave_type(NMConnection *connection, NMSetting **out_s_por
         const char *      controller_type_name;
     } infos[] = {
         {NM_META_SETTING_TYPE_BRIDGE_PORT, NM_SETTING_BRIDGE_SETTING_NAME},
+        {NM_META_SETTING_TYPE_BOND_PORT, NM_SETTING_BOND_SETTING_NAME},
         {NM_META_SETTING_TYPE_TEAM_PORT, NM_SETTING_TEAM_SETTING_NAME},
         {NM_META_SETTING_TYPE_OVS_PORT, NM_SETTING_OVS_BRIDGE_SETTING_NAME},
         {NM_META_SETTING_TYPE_OVS_INTERFACE, NM_SETTING_OVS_PORT_SETTING_NAME},
@@ -1208,10 +1209,23 @@ _normalize_ip_config(NMConnection *self, GHashTable *parameters)
 
     if (_supports_addr_family(self, AF_INET)) {
         if (!s_ip4) {
-            const char *default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
+            const char *default_ip4_method = NULL;
 
-            if (nm_connection_is_type(self, NM_SETTING_WIREGUARD_SETTING_NAME))
-                default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+            if (parameters) {
+                default_ip4_method =
+                    g_hash_table_lookup(parameters,
+                                        NM_CONNECTION_NORMALIZE_PARAM_IP4_CONFIG_METHOD);
+            }
+            if (!default_ip4_method) {
+                const char *type = nm_connection_get_connection_type(self);
+
+                if (NM_IN_STRSET(type,
+                                 NM_SETTING_WIREGUARD_SETTING_NAME,
+                                 NM_SETTING_DUMMY_SETTING_NAME))
+                    default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
+                else
+                    default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
+            }
 
             /* But if no IP4 setting was specified, assume the caller was just
              * being lazy and use the default method.
@@ -1255,13 +1269,18 @@ _normalize_ip_config(NMConnection *self, GHashTable *parameters)
         if (!s_ip6) {
             const char *default_ip6_method = NULL;
 
-            if (parameters)
+            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;
+                const char *type = nm_connection_get_connection_type(self);
+
+                if (NM_IN_STRSET(type,
+                                 NM_SETTING_WIREGUARD_SETTING_NAME,
+                                 NM_SETTING_DUMMY_SETTING_NAME))
+                    default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_DISABLED;
                 else
                     default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
             }
@@ -1661,6 +1680,10 @@ _normalize_invalid_slave_port_settings(NMConnection *self)
         && _nm_connection_remove_setting(self, NM_TYPE_SETTING_BRIDGE_PORT))
         changed = TRUE;
 
+    if (!nm_streq0(slave_type, NM_SETTING_BOND_SETTING_NAME)
+        && _nm_connection_remove_setting(self, NM_TYPE_SETTING_BOND_PORT))
+        changed = TRUE;
+
     if (!nm_streq0(slave_type, NM_SETTING_TEAM_SETTING_NAME)
         && _nm_connection_remove_setting(self, NM_TYPE_SETTING_TEAM_PORT))
         changed = TRUE;
@@ -2092,23 +2115,23 @@ _nm_connection_ensure_normalized(NMConnection * connection,
 
 #if NM_MORE_ASSERTS
 static void
-_nmtst_connection_unchanging_changed_cb(NMConnection *connection, gpointer user_data)
+_nm_assert_connection_unchanging_changed_cb(NMConnection *connection, gpointer user_data)
 {
     nm_assert_not_reached();
 }
 
 static void
-_nmtst_connection_unchanging_secrets_updated_cb(NMConnection *connection,
-                                                const char *  setting_name,
-                                                gpointer      user_data)
+_nm_assert_connection_unchanging_secrets_updated_cb(NMConnection *connection,
+                                                    const char *  setting_name,
+                                                    gpointer      user_data)
 {
     nm_assert_not_reached();
 }
 
-const char _nmtst_connection_unchanging_user_data = 0;
+const char _nm_assert_connection_unchanging_user_data = 0;
 
 void
-nmtst_connection_assert_unchanging(NMConnection *connection)
+nm_assert_connection_unchanging(NMConnection *connection)
 {
     if (!connection)
         return;
@@ -2121,7 +2144,7 @@ nmtst_connection_assert_unchanging(NMConnection *connection)
                               0,
                               NULL,
                               NULL,
-                              (gpointer) &_nmtst_connection_unchanging_user_data)
+                              (gpointer) &_nm_assert_connection_unchanging_user_data)
         != 0) {
         /* avoid connecting the assertion handler multiple times. */
         return;
@@ -2129,16 +2152,16 @@ nmtst_connection_assert_unchanging(NMConnection *connection)
 
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     G_CALLBACK(_nmtst_connection_unchanging_changed_cb),
-                     (gpointer) &_nmtst_connection_unchanging_user_data);
+                     G_CALLBACK(_nm_assert_connection_unchanging_changed_cb),
+                     (gpointer) &_nm_assert_connection_unchanging_user_data);
     g_signal_connect(connection,
                      NM_CONNECTION_SECRETS_CLEARED,
-                     G_CALLBACK(_nmtst_connection_unchanging_changed_cb),
-                     (gpointer) &_nmtst_connection_unchanging_user_data);
+                     G_CALLBACK(_nm_assert_connection_unchanging_changed_cb),
+                     (gpointer) &_nm_assert_connection_unchanging_user_data);
     g_signal_connect(connection,
                      NM_CONNECTION_SECRETS_UPDATED,
-                     G_CALLBACK(_nmtst_connection_unchanging_secrets_updated_cb),
-                     (gpointer) &_nmtst_connection_unchanging_user_data);
+                     G_CALLBACK(_nm_assert_connection_unchanging_secrets_updated_cb),
+                     (gpointer) &_nm_assert_connection_unchanging_user_data);
 }
 #endif
 
@@ -2315,7 +2338,7 @@ nm_connection_need_secrets(NMConnection *connection, GPtrArray **hints)
         if (!setting)
             continue;
 
-        nm_assert(!setting_before || _nmtst_nm_setting_sort(setting_before, setting) < 0);
+        nm_assert(!setting_before || _nm_setting_sort_for_nm_assert(setting_before, setting) < 0);
         nm_assert(!setting_before || _nm_setting_compare_priority(setting_before, setting) <= 0);
         setting_before = setting;
 
@@ -2552,8 +2575,7 @@ nm_connection_serialization_options_equal(const NMConnectionSerializationOptions
         return FALSE;
     if (a->timestamp.has && a->timestamp.val != b->timestamp.val)
         return FALSE;
-    if (!nm_utils_strv_equal(a->seen_bssids ?: NM_STRV_EMPTY_CC(),
-                             b->seen_bssids ?: NM_STRV_EMPTY_CC()))
+    if (!nm_strv_equal(a->seen_bssids ?: NM_STRV_EMPTY_CC(), b->seen_bssids ?: NM_STRV_EMPTY_CC()))
         return FALSE;
 
     return TRUE;
@@ -2638,7 +2660,7 @@ nm_connection_is_type(NMConnection *connection, const char *type)
 }
 
 int
-_nmtst_nm_setting_sort(NMSetting *a, NMSetting *b)
+_nm_setting_sort_for_nm_assert(NMSetting *a, NMSetting *b)
 {
     g_assert(NM_IS_SETTING(a));
     g_assert(NM_IS_SETTING(b));
@@ -2705,7 +2727,7 @@ nm_connection_get_settings(NMConnection *connection, guint *out_length)
         NMSetting *setting = priv->settings[nm_meta_setting_types_by_priority[i]];
 
         if (setting) {
-            nm_assert(j == 0 || _nmtst_nm_setting_sort(arr[j - 1], setting) < 0);
+            nm_assert(j == 0 || _nm_setting_sort_for_nm_assert(arr[j - 1], setting) < 0);
             arr[j++] = setting;
         }
     }
@@ -3099,6 +3121,8 @@ nm_connection_get_virtual_device_description(NMConnection *connection)
             nm_connection_get_setting_infiniband(connection));
     } else if (!strcmp(type, NM_SETTING_IP_TUNNEL_SETTING_NAME))
         display_type = _("IP Tunnel");
+    else if (!strcmp(type, NM_SETTING_WIREGUARD_SETTING_NAME))
+        display_type = _("WireGuard");
 
     if (!iface || !display_type)
         return NULL;
diff --git a/src/libnm-core-impl/nm-crypto.c b/src/libnm-core-impl/nm-crypto.c
index b14b681e..ea17449f 100644
--- a/src/libnm-core-impl/nm-crypto.c
+++ b/src/libnm-core-impl/nm-crypto.c
@@ -310,7 +310,7 @@ parse_old_openssl_key_file(const guint8 *      data,
                             "Malformed PEM file: both Proc-Type and DEK-Info tags are required.");
                 return FALSE;
             }
-            nm_utils_strbuf_append_str(&str_p, &str_len, p);
+            nm_strbuf_append_str(&str_p, &str_len, p);
             nm_assert(str_len > 0);
         }
     }
diff --git a/src/libnm-core-impl/nm-dbus-utils.c b/src/libnm-core-impl/nm-dbus-utils.c
index bcc8cf9c..a272f08c 100644
--- a/src/libnm-core-impl/nm-dbus-utils.c
+++ b/src/libnm-core-impl/nm-dbus-utils.c
@@ -132,7 +132,7 @@ _nm_dbus_signal_connect_data(GDBusProxy *        proxy,
     sd->signal_name = g_strdup(signal_name);
     sd->signature   = signature;
 
-    closure = (swapped ? g_cclosure_new_swap : g_cclosure_new) (c_handler, data, destroy_data);
+    closure = (swapped ? g_cclosure_new_swap : g_cclosure_new)(c_handler, data, destroy_data);
     g_closure_set_marshal(closure, g_cclosure_marshal_generic);
     g_closure_set_meta_marshal(closure, sd, dbus_signal_meta_marshal);
     g_closure_add_finalize_notifier(closure, sd, dbus_signal_data_free);
diff --git a/src/libnm-core-impl/nm-keyfile.c b/src/libnm-core-impl/nm-keyfile.c
index c6c4a2eb..1171ab03 100644
--- a/src/libnm-core-impl/nm-keyfile.c
+++ b/src/libnm-core-impl/nm-keyfile.c
@@ -23,7 +23,7 @@
 #include "libnm-glib-aux/nm-secret-utils.h"
 #include "libnm-systemd-shared/nm-sd-utils-shared.h"
 #include "libnm-core-aux-intern/nm-common-macros.h"
-
+#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
 #include "libnm-core-intern/nm-core-internal.h"
 #include "nm-keyfile.h"
 #include "nm-setting-private.h"
@@ -2483,7 +2483,7 @@ write_hash_of_string(GKeyFile *file, NMSetting *setting, const char *key, const
 
     hash = g_value_get_boxed(value);
 
-    keys = nm_utils_strdict_get_keys(hash, TRUE, &l);
+    keys = nm_strdict_get_keys(hash, TRUE, &l);
     for (i = 0; i < l; i++) {
         gs_free char *to_free = NULL;
         const char *  property, *data;
@@ -3351,7 +3351,7 @@ _read_setting(KeyfileReaderInfo *info)
     gs_unref_object NMSetting *setting = NULL;
     const char *               alias;
     GType                      type;
-    guint                      i;
+    guint16                    i;
 
     alias = nm_keyfile_plugin_get_setting_name_for_alias(info->group);
     if (!alias)
@@ -3384,7 +3384,7 @@ _read_setting(KeyfileReaderInfo *info)
         if (n_keys > 0) {
             GHashTable *h = _nm_setting_option_hash(setting, TRUE);
 
-            nm_utils_strv_sort(keys, n_keys);
+            nm_strv_sort(keys, n_keys);
             for (k = 0; k < n_keys; k++) {
                 gs_free char *key           = keys[k];
                 gs_free_error GError *local = NULL;
@@ -3779,11 +3779,7 @@ nm_keyfile_read(GKeyFile *            keyfile,
             goto out_with_info_error;
     }
 
-    s_con = nm_connection_get_setting_connection(connection);
-    if (!s_con) {
-        s_con = NM_SETTING_CONNECTION(nm_setting_connection_new());
-        nm_connection_add_setting(connection, NM_SETTING(s_con));
-    }
+    s_con = _nm_connection_ensure_setting(connection, NM_TYPE_SETTING_CONNECTION);
 
     /* Make sure that we have 'interface-name' even if it was specified in the
      * "wrong" (ie, deprecated) group.
@@ -4088,7 +4084,7 @@ nm_keyfile_write(NMConnection *        connection,
     KeyfileWriterInfo               info;
     NMSetting **                    settings;
     int                             i;
-    guint                           j;
+    guint16                         j;
 
     g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL);
     g_return_val_if_fail(!error || !*error, NULL);
diff --git a/src/libnm-core-impl/nm-meta-setting-base-impl.c b/src/libnm-core-impl/nm-meta-setting-base-impl.c
index a952ced9..8c1ad10e 100644
--- a/src/libnm-core-impl/nm-meta-setting-base-impl.c
+++ b/src/libnm-core-impl/nm-meta-setting-base-impl.c
@@ -8,7 +8,7 @@
 #include "nm-meta-setting-base.h"
 
 #if _NM_META_SETTING_BASE_IMPL_LIBNM
-    #include "libnm-core-impl/nm-default-libnm-core.h"
+#include "libnm-core-impl/nm-default-libnm-core.h"
 #endif
 
 #include "nm-setting-6lowpan.h"
@@ -16,6 +16,7 @@
 #include "nm-setting-adsl.h"
 #include "nm-setting-bluetooth.h"
 #include "nm-setting-bond.h"
+#include "nm-setting-bond-port.h"
 #include "nm-setting-bridge-port.h"
 #include "nm-setting-bridge.h"
 #include "nm-setting-cdma.h"
@@ -64,7 +65,7 @@
 #include "nm-setting-wpan.h"
 
 #if _NM_META_SETTING_BASE_IMPL_LIBNM
-    #include "nm-setting-private.h"
+#include "nm-setting-private.h"
 #endif
 
 /*****************************************************************************/
@@ -190,6 +191,13 @@ const NMMetaSettingInfo nm_meta_setting_infos[] = {
             .setting_name      = NM_SETTING_BOND_SETTING_NAME,
             .get_setting_gtype = nm_setting_bond_get_type,
         },
+    [NM_META_SETTING_TYPE_BOND_PORT] =
+        {
+            .meta_type         = NM_META_SETTING_TYPE_BOND_PORT,
+            .setting_priority  = NM_SETTING_PRIORITY_AUX,
+            .setting_name      = NM_SETTING_BOND_PORT_SETTING_NAME,
+            .get_setting_gtype = nm_setting_bond_port_get_type,
+        },
     [NM_META_SETTING_TYPE_BRIDGE] =
         {
             .meta_type         = NM_META_SETTING_TYPE_BRIDGE,
@@ -568,6 +576,7 @@ const NMMetaSettingType nm_meta_setting_types_by_priority[] = {
     NM_META_SETTING_TYPE_SRIOV,
 
     /* NM_SETTING_PRIORITY_AUX  */
+    NM_META_SETTING_TYPE_BOND_PORT,
     NM_META_SETTING_TYPE_BRIDGE_PORT,
     NM_META_SETTING_TYPE_ETHTOOL,
     NM_META_SETTING_TYPE_MATCH,
diff --git a/src/libnm-core-impl/nm-property-compare.h b/src/libnm-core-impl/nm-property-compare.h
index d422b1c8..2400bc42 100644
--- a/src/libnm-core-impl/nm-property-compare.h
+++ b/src/libnm-core-impl/nm-property-compare.h
@@ -8,7 +8,7 @@
 #define __NM_PROPERTY_COMPARE_H__
 
 #if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-    #error Cannot use this header.
+#error Cannot use this header.
 #endif
 
 int nm_property_compare(GVariant *value1, GVariant *value2);
diff --git a/src/libnm-core-impl/nm-setting-6lowpan.c b/src/libnm-core-impl/nm-setting-6lowpan.c
index c07fb38e..606fa824 100644
--- a/src/libnm-core-impl/nm-setting-6lowpan.c
+++ b/src/libnm-core-impl/nm-setting-6lowpan.c
@@ -129,41 +129,6 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 static void
-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_value_set_string(value, priv->parent);
-        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)
-{
-    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);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static void
 nm_setting_6lowpan_init(NMSetting6Lowpan *setting)
 {}
 
@@ -183,17 +148,6 @@ nm_setting_6lowpan_new(void)
 }
 
 static void
-finalize(GObject *object)
-{
-    NMSetting6Lowpan *       setting = NM_SETTING_6LOWPAN(object);
-    NMSetting6LowpanPrivate *priv    = NM_SETTING_6LOWPAN_GET_PRIVATE(setting);
-
-    g_free(priv->parent);
-
-    G_OBJECT_CLASS(nm_setting_6lowpan_parent_class)->finalize(object);
-}
-
-static void
 nm_setting_6lowpan_class_init(NMSetting6LowpanClass *klass)
 {
     GObjectClass *  object_class        = G_OBJECT_CLASS(klass);
@@ -202,9 +156,8 @@ nm_setting_6lowpan_class_init(NMSetting6LowpanClass *klass)
 
     g_type_class_add_private(klass, sizeof(NMSetting6LowpanPrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-    object_class->finalize     = finalize;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify = verify;
 
@@ -216,17 +169,19 @@ nm_setting_6lowpan_class_init(NMSetting6LowpanClass *klass)
      *
      * Since: 1.14
      **/
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_6LOWPAN_PARENT,
-                                       PROP_PARENT,
-                                       NM_SETTING_PARAM_INFERRABLE,
-                                       nm_setting_6lowpan_get_parent);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_6LOWPAN_PARENT,
+                                              PROP_PARENT,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSetting6LowpanPrivate,
+                                              parent);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_6LOWPAN,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_6LOWPAN,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-8021x.c b/src/libnm-core-impl/nm-setting-8021x.c
index 6ddbd680..103443ba 100644
--- a/src/libnm-core-impl/nm-setting-8021x.c
+++ b/src/libnm-core-impl/nm-setting-8021x.c
@@ -131,53 +131,53 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSetting8021x,
                              PROP_AUTH_TIMEOUT, );
 
 typedef struct {
-    GSList *                eap; /* GSList of strings */
-    char *                  identity;
-    char *                  anonymous_identity;
-    char *                  pac_file;
-    GBytes *                ca_cert;
-    char *                  ca_cert_password;
-    char *                  ca_path;
-    char *                  subject_match;
-    GSList *                altsubject_matches;
-    char *                  domain_suffix_match;
-    char *                  domain_match;
-    GBytes *                client_cert;
-    char *                  client_cert_password;
-    char *                  phase1_peapver;
-    char *                  phase1_peaplabel;
-    char *                  phase1_fast_provisioning;
-    char *                  phase2_auth;
-    char *                  phase2_autheap;
-    GBytes *                phase2_ca_cert;
-    char *                  phase2_ca_cert_password;
-    char *                  phase2_ca_path;
-    char *                  phase2_subject_match;
-    GSList *                phase2_altsubject_matches;
-    char *                  phase2_domain_suffix_match;
-    char *                  phase2_domain_match;
-    GBytes *                phase2_client_cert;
-    char *                  phase2_client_cert_password;
-    char *                  password;
-    GBytes *                password_raw;
-    char *                  pin;
-    GBytes *                private_key;
-    char *                  private_key_password;
-    GBytes *                phase2_private_key;
-    char *                  phase2_private_key_password;
-    int                     auth_timeout;
-    NMSetting8021xAuthFlags phase1_auth_flags;
-    NMSettingSecretFlags    ca_cert_password_flags;
-    NMSettingSecretFlags    client_cert_password_flags;
-    NMSettingSecretFlags    phase2_ca_cert_password_flags;
-    NMSettingSecretFlags    phase2_client_cert_password_flags;
-    NMSettingSecretFlags    password_flags;
-    NMSettingSecretFlags    password_raw_flags;
-    NMSettingSecretFlags    pin_flags;
-    NMSettingSecretFlags    private_key_password_flags;
-    NMSettingSecretFlags    phase2_private_key_password_flags;
-    bool                    optional : 1;
-    bool                    system_ca_certs : 1;
+    GSList *eap; /* GSList of strings */
+    char *  identity;
+    char *  anonymous_identity;
+    char *  pac_file;
+    GBytes *ca_cert;
+    char *  ca_cert_password;
+    char *  ca_path;
+    char *  subject_match;
+    GSList *altsubject_matches;
+    char *  domain_suffix_match;
+    char *  domain_match;
+    GBytes *client_cert;
+    char *  client_cert_password;
+    char *  phase1_peapver;
+    char *  phase1_peaplabel;
+    char *  phase1_fast_provisioning;
+    char *  phase2_auth;
+    char *  phase2_autheap;
+    GBytes *phase2_ca_cert;
+    char *  phase2_ca_cert_password;
+    char *  phase2_ca_path;
+    char *  phase2_subject_match;
+    GSList *phase2_altsubject_matches;
+    char *  phase2_domain_suffix_match;
+    char *  phase2_domain_match;
+    GBytes *phase2_client_cert;
+    char *  phase2_client_cert_password;
+    char *  password;
+    GBytes *password_raw;
+    char *  pin;
+    GBytes *private_key;
+    char *  private_key_password;
+    GBytes *phase2_private_key;
+    char *  phase2_private_key_password;
+    guint   ca_cert_password_flags;
+    guint   client_cert_password_flags;
+    guint   phase2_ca_cert_password_flags;
+    guint   phase2_client_cert_password_flags;
+    guint   password_flags;
+    guint   password_raw_flags;
+    guint   pin_flags;
+    guint   private_key_password_flags;
+    guint   phase2_private_key_password_flags;
+    guint32 phase1_auth_flags;
+    gint32  auth_timeout;
+    bool    optional;
+    bool    system_ca_certs;
 } NMSetting8021xPrivate;
 
 /**
@@ -594,13 +594,13 @@ _cert_impl_set(NMSetting8021x *        setting,
      * property to the same PKCS#12 data.
      */
     if (cert && p_client_cert && format == NM_CRYPTO_FILE_FORMAT_PKCS12
-        && !nm_gbytes_equal0(cert, *p_client_cert)) {
+        && !nm_g_bytes_equal0(cert, *p_client_cert)) {
         g_bytes_unref(*p_client_cert);
         *p_client_cert = g_bytes_ref(cert);
     } else
         notify_client_cert = PROP_0;
 
-    if (p_cert && !nm_gbytes_equal0(cert, *p_cert)) {
+    if (p_cert && !nm_g_bytes_equal0(cert, *p_cert)) {
         g_bytes_unref(*p_cert);
         *p_cert = g_steal_pointer(&cert);
     } else
@@ -2516,7 +2516,7 @@ need_secrets_sim(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2)
 {
     NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
 
-    if (!priv->pin || !strlen(priv->pin))
+    if (nm_str_is_empty(priv->pin))
         g_ptr_array_add(secrets, NM_SETTING_802_1X_PIN);
 }
 
@@ -2532,9 +2532,6 @@ need_private_key_password(GBytes *               blob,
     if (flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
         return FALSE;
 
-    if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11 && flags == NM_SETTING_SECRET_FLAG_NONE)
-        return FALSE;
-
     /* Private key password is required */
     if (password) {
         if (path)
@@ -2578,15 +2575,13 @@ need_secrets_tls(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2)
 
         scheme = nm_setting_802_1x_get_phase2_ca_cert_scheme(self);
         if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11
-            && !(priv->phase2_ca_cert_password_flags == NM_SETTING_SECRET_FLAG_NONE
-                 || priv->phase2_ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
+            && !(priv->phase2_ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
             && !priv->phase2_ca_cert_password)
             g_ptr_array_add(secrets, NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD);
 
         scheme = nm_setting_802_1x_get_phase2_client_cert_scheme(self);
         if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11
-            && !(priv->phase2_client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED
-                 || priv->phase2_client_cert_password_flags == NM_SETTING_SECRET_FLAG_NONE)
+            && !(priv->phase2_client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
             && !priv->phase2_client_cert_password)
             g_ptr_array_add(secrets, NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD);
     } else {
@@ -2607,15 +2602,13 @@ need_secrets_tls(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2)
 
         scheme = nm_setting_802_1x_get_ca_cert_scheme(self);
         if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11
-            && !(priv->ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED
-                 || priv->ca_cert_password_flags == NM_SETTING_SECRET_FLAG_NONE)
+            && !(priv->ca_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
             && !priv->ca_cert_password)
             g_ptr_array_add(secrets, NM_SETTING_802_1X_CA_CERT_PASSWORD);
 
         scheme = nm_setting_802_1x_get_client_cert_scheme(self);
         if (scheme == NM_SETTING_802_1X_CK_SCHEME_PKCS11
-            && !(priv->client_cert_password_flags == NM_SETTING_SECRET_FLAG_NONE
-                 || priv->client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
+            && !(priv->client_cert_password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
             && !priv->client_cert_password)
             g_ptr_array_add(secrets, NM_SETTING_802_1X_CLIENT_CERT_PASSWORD);
     }
@@ -2762,7 +2755,7 @@ verify_ttls(NMSetting8021x *self, gboolean phase2, GError **error)
 {
     NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
 
-    if (!priv->identity || !strlen(priv->identity)) {
+    if (nm_str_is_empty(priv->identity)) {
         if (!priv->identity) {
             g_set_error_literal(error,
                                 NM_CONNECTION_ERROR,
@@ -2804,21 +2797,18 @@ verify_identity(NMSetting8021x *self, gboolean phase2, GError **error)
 {
     NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
 
-    if (!priv->identity) {
-        g_set_error_literal(error,
-                            NM_CONNECTION_ERROR,
-                            NM_CONNECTION_ERROR_MISSING_PROPERTY,
-                            _("property is missing"));
-        g_prefix_error(error,
-                       "%s.%s: ",
-                       NM_SETTING_802_1X_SETTING_NAME,
-                       NM_SETTING_802_1X_IDENTITY);
-        return FALSE;
-    } else if (!strlen(priv->identity)) {
-        g_set_error_literal(error,
-                            NM_CONNECTION_ERROR,
-                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
-                            _("property is empty"));
+    if (nm_str_is_empty(priv->identity)) {
+        if (!priv->identity) {
+            g_set_error_literal(error,
+                                NM_CONNECTION_ERROR,
+                                NM_CONNECTION_ERROR_MISSING_PROPERTY,
+                                _("property is missing"));
+        } else {
+            g_set_error_literal(error,
+                                NM_CONNECTION_ERROR,
+                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                                _("property is empty"));
+        }
         g_prefix_error(error,
                        "%s.%s: ",
                        NM_SETTING_802_1X_SETTING_NAME,
@@ -2840,7 +2830,7 @@ need_secrets_phase2(NMSetting8021x *self, GPtrArray *secrets, gboolean phase2)
 
     /* Check phase2_auth and phase2_autheap */
     method = priv->phase2_auth;
-    if (!method && priv->phase2_autheap)
+    if (!method)
         method = priv->phase2_autheap;
 
     if (!method) {
@@ -2931,7 +2921,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
             if (eap_methods_table[i].v_func == NULL)
                 continue;
             if (!strcmp(eap_methods_table[i].method, method)) {
-                if (!(*eap_methods_table[i].v_func) (self, FALSE, error))
+                if (!(*eap_methods_table[i].v_func)(self, FALSE, error))
                     return FALSE;
                 break;
             }
@@ -2977,7 +2967,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         return FALSE;
     }
 
-    if (NM_FLAGS_ANY(priv->phase1_auth_flags, ~NM_SETTING_802_1X_AUTH_FLAGS_ALL)) {
+    if (NM_FLAGS_ANY(priv->phase1_auth_flags, ~((guint32) NM_SETTING_802_1X_AUTH_FLAGS_ALL))) {
         g_set_error_literal(error,
                             NM_CONNECTION_ERROR,
                             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -3116,146 +3106,14 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     case PROP_EAP:
         g_value_take_boxed(value, _nm_utils_slist_to_strv(priv->eap, TRUE));
         break;
-    case PROP_IDENTITY:
-        g_value_set_string(value, priv->identity);
-        break;
-    case PROP_ANONYMOUS_IDENTITY:
-        g_value_set_string(value, priv->anonymous_identity);
-        break;
-    case PROP_PAC_FILE:
-        g_value_set_string(value, priv->pac_file);
-        break;
-    case PROP_CA_CERT:
-        g_value_set_boxed(value, priv->ca_cert);
-        break;
-    case PROP_CA_CERT_PASSWORD:
-        g_value_set_string(value, priv->ca_cert_password);
-        break;
-    case PROP_CA_CERT_PASSWORD_FLAGS:
-        g_value_set_flags(value, priv->ca_cert_password_flags);
-        break;
-    case PROP_CA_PATH:
-        g_value_set_string(value, priv->ca_path);
-        break;
-    case PROP_SUBJECT_MATCH:
-        g_value_set_string(value, priv->subject_match);
-        break;
     case PROP_ALTSUBJECT_MATCHES:
         g_value_take_boxed(value, _nm_utils_slist_to_strv(priv->altsubject_matches, TRUE));
         break;
-    case PROP_DOMAIN_SUFFIX_MATCH:
-        g_value_set_string(value, priv->domain_suffix_match);
-        break;
-    case PROP_DOMAIN_MATCH:
-        g_value_set_string(value, priv->domain_match);
-        break;
-    case PROP_CLIENT_CERT:
-        g_value_set_boxed(value, priv->client_cert);
-        break;
-    case PROP_CLIENT_CERT_PASSWORD:
-        g_value_set_string(value, priv->client_cert_password);
-        break;
-    case PROP_CLIENT_CERT_PASSWORD_FLAGS:
-        g_value_set_flags(value, priv->client_cert_password_flags);
-        break;
-    case PROP_PHASE1_PEAPVER:
-        g_value_set_string(value, priv->phase1_peapver);
-        break;
-    case PROP_PHASE1_PEAPLABEL:
-        g_value_set_string(value, priv->phase1_peaplabel);
-        break;
-    case PROP_PHASE1_FAST_PROVISIONING:
-        g_value_set_string(value, priv->phase1_fast_provisioning);
-        break;
-    case PROP_PHASE1_AUTH_FLAGS:
-        g_value_set_uint(value, priv->phase1_auth_flags);
-        break;
-    case PROP_PHASE2_AUTH:
-        g_value_set_string(value, priv->phase2_auth);
-        break;
-    case PROP_PHASE2_AUTHEAP:
-        g_value_set_string(value, priv->phase2_autheap);
-        break;
-    case PROP_PHASE2_CA_CERT:
-        g_value_set_boxed(value, priv->phase2_ca_cert);
-        break;
-    case PROP_PHASE2_CA_CERT_PASSWORD:
-        g_value_set_string(value, priv->phase2_ca_cert_password);
-        break;
-    case PROP_PHASE2_CA_CERT_PASSWORD_FLAGS:
-        g_value_set_flags(value, priv->phase2_ca_cert_password_flags);
-        break;
-    case PROP_PHASE2_CA_PATH:
-        g_value_set_string(value, priv->phase2_ca_path);
-        break;
-    case PROP_PHASE2_SUBJECT_MATCH:
-        g_value_set_string(value, priv->phase2_subject_match);
-        break;
     case PROP_PHASE2_ALTSUBJECT_MATCHES:
         g_value_take_boxed(value, _nm_utils_slist_to_strv(priv->phase2_altsubject_matches, TRUE));
         break;
-    case PROP_PHASE2_DOMAIN_SUFFIX_MATCH:
-        g_value_set_string(value, priv->phase2_domain_suffix_match);
-        break;
-    case PROP_PHASE2_DOMAIN_MATCH:
-        g_value_set_string(value, priv->phase2_domain_match);
-        break;
-    case PROP_PHASE2_CLIENT_CERT:
-        g_value_set_boxed(value, priv->phase2_client_cert);
-        break;
-    case PROP_PHASE2_CLIENT_CERT_PASSWORD:
-        g_value_set_string(value, priv->phase2_client_cert_password);
-        break;
-    case PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS:
-        g_value_set_flags(value, priv->phase2_client_cert_password_flags);
-        break;
-    case PROP_PASSWORD:
-        g_value_set_string(value, priv->password);
-        break;
-    case PROP_PASSWORD_FLAGS:
-        g_value_set_flags(value, priv->password_flags);
-        break;
-    case PROP_PASSWORD_RAW:
-        g_value_set_boxed(value, priv->password_raw);
-        break;
-    case PROP_PASSWORD_RAW_FLAGS:
-        g_value_set_flags(value, priv->password_raw_flags);
-        break;
-    case PROP_PRIVATE_KEY:
-        g_value_set_boxed(value, priv->private_key);
-        break;
-    case PROP_PRIVATE_KEY_PASSWORD:
-        g_value_set_string(value, priv->private_key_password);
-        break;
-    case PROP_PRIVATE_KEY_PASSWORD_FLAGS:
-        g_value_set_flags(value, priv->private_key_password_flags);
-        break;
-    case PROP_PHASE2_PRIVATE_KEY:
-        g_value_set_boxed(value, priv->phase2_private_key);
-        break;
-    case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
-        g_value_set_string(value, priv->phase2_private_key_password);
-        break;
-    case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS:
-        g_value_set_flags(value, priv->phase2_private_key_password_flags);
-        break;
-    case PROP_PIN:
-        g_value_set_string(value, priv->pin);
-        break;
-    case PROP_PIN_FLAGS:
-        g_value_set_flags(value, priv->pin_flags);
-        break;
-    case PROP_SYSTEM_CA_CERTS:
-        g_value_set_boolean(value, priv->system_ca_certs);
-        break;
-    case PROP_AUTH_TIMEOUT:
-        g_value_set_int(value, priv->auth_timeout);
-        break;
-    case PROP_OPTIONAL:
-        g_value_set_boolean(value, priv->optional);
-        break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_get_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -3269,181 +3127,18 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
     switch (prop_id) {
     case PROP_EAP:
         g_slist_free_full(priv->eap, g_free);
-        priv->eap = _nm_utils_strv_to_slist(g_value_get_boxed(value), TRUE);
-        break;
-    case PROP_IDENTITY:
-        g_free(priv->identity);
-        priv->identity = g_value_dup_string(value);
-        break;
-    case PROP_ANONYMOUS_IDENTITY:
-        g_free(priv->anonymous_identity);
-        priv->anonymous_identity = g_value_dup_string(value);
-        break;
-    case PROP_PAC_FILE:
-        g_free(priv->pac_file);
-        priv->pac_file = g_value_dup_string(value);
-        break;
-    case PROP_CA_CERT:
-        g_bytes_unref(priv->ca_cert);
-        priv->ca_cert = g_value_dup_boxed(value);
-        break;
-    case PROP_CA_CERT_PASSWORD:
-        g_free(priv->ca_cert_password);
-        priv->ca_cert_password = g_value_dup_string(value);
-        break;
-    case PROP_CA_CERT_PASSWORD_FLAGS:
-        priv->ca_cert_password_flags = g_value_get_flags(value);
-        break;
-    case PROP_CA_PATH:
-        g_free(priv->ca_path);
-        priv->ca_path = g_value_dup_string(value);
-        break;
-    case PROP_SUBJECT_MATCH:
-        g_free(priv->subject_match);
-        priv->subject_match = nm_strdup_not_empty(g_value_get_string(value));
+        priv->eap = nm_strv_to_gslist(g_value_get_boxed(value), TRUE);
         break;
     case PROP_ALTSUBJECT_MATCHES:
         g_slist_free_full(priv->altsubject_matches, g_free);
-        priv->altsubject_matches = _nm_utils_strv_to_slist(g_value_get_boxed(value), TRUE);
-        break;
-    case PROP_DOMAIN_SUFFIX_MATCH:
-        g_free(priv->domain_suffix_match);
-        priv->domain_suffix_match = nm_strdup_not_empty(g_value_get_string(value));
-        break;
-    case PROP_DOMAIN_MATCH:
-        g_free(priv->domain_match);
-        priv->domain_match = nm_strdup_not_empty(g_value_get_string(value));
-        break;
-    case PROP_CLIENT_CERT:
-        g_bytes_unref(priv->client_cert);
-        priv->client_cert = g_value_dup_boxed(value);
-        break;
-    case PROP_CLIENT_CERT_PASSWORD:
-        g_free(priv->client_cert_password);
-        priv->client_cert_password = g_value_dup_string(value);
-        break;
-    case PROP_CLIENT_CERT_PASSWORD_FLAGS:
-        priv->client_cert_password_flags = g_value_get_flags(value);
-        break;
-    case PROP_PHASE1_PEAPVER:
-        g_free(priv->phase1_peapver);
-        priv->phase1_peapver = g_value_dup_string(value);
-        break;
-    case PROP_PHASE1_PEAPLABEL:
-        g_free(priv->phase1_peaplabel);
-        priv->phase1_peaplabel = g_value_dup_string(value);
-        break;
-    case PROP_PHASE1_FAST_PROVISIONING:
-        g_free(priv->phase1_fast_provisioning);
-        priv->phase1_fast_provisioning = g_value_dup_string(value);
-        break;
-    case PROP_PHASE1_AUTH_FLAGS:
-        priv->phase1_auth_flags = g_value_get_uint(value);
-        break;
-    case PROP_PHASE2_AUTH:
-        g_free(priv->phase2_auth);
-        priv->phase2_auth = g_value_dup_string(value);
-        break;
-    case PROP_PHASE2_AUTHEAP:
-        g_free(priv->phase2_autheap);
-        priv->phase2_autheap = g_value_dup_string(value);
-        break;
-    case PROP_PHASE2_CA_CERT:
-        g_bytes_unref(priv->phase2_ca_cert);
-        priv->phase2_ca_cert = g_value_dup_boxed(value);
-        break;
-    case PROP_PHASE2_CA_CERT_PASSWORD:
-        g_free(priv->phase2_ca_cert_password);
-        priv->phase2_ca_cert_password = g_value_dup_string(value);
-        break;
-    case PROP_PHASE2_CA_CERT_PASSWORD_FLAGS:
-        priv->phase2_ca_cert_password_flags = g_value_get_flags(value);
-        break;
-    case PROP_PHASE2_CA_PATH:
-        g_free(priv->phase2_ca_path);
-        priv->phase2_ca_path = g_value_dup_string(value);
-        break;
-    case PROP_PHASE2_SUBJECT_MATCH:
-        g_free(priv->phase2_subject_match);
-        priv->phase2_subject_match = nm_strdup_not_empty(g_value_get_string(value));
+        priv->altsubject_matches = nm_strv_to_gslist(g_value_get_boxed(value), TRUE);
         break;
     case PROP_PHASE2_ALTSUBJECT_MATCHES:
         g_slist_free_full(priv->phase2_altsubject_matches, g_free);
-        priv->phase2_altsubject_matches = _nm_utils_strv_to_slist(g_value_get_boxed(value), TRUE);
-        break;
-    case PROP_PHASE2_DOMAIN_SUFFIX_MATCH:
-        g_free(priv->phase2_domain_suffix_match);
-        priv->phase2_domain_suffix_match = nm_strdup_not_empty(g_value_get_string(value));
-        break;
-    case PROP_PHASE2_DOMAIN_MATCH:
-        g_free(priv->phase2_domain_match);
-        priv->phase2_domain_match = nm_strdup_not_empty(g_value_get_string(value));
-        break;
-    case PROP_PHASE2_CLIENT_CERT:
-        g_bytes_unref(priv->phase2_client_cert);
-        priv->phase2_client_cert = g_value_dup_boxed(value);
-        break;
-    case PROP_PHASE2_CLIENT_CERT_PASSWORD:
-        g_free(priv->phase2_client_cert_password);
-        priv->phase2_client_cert_password = g_value_dup_string(value);
-        break;
-    case PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS:
-        priv->phase2_client_cert_password_flags = g_value_get_flags(value);
-        break;
-    case PROP_PASSWORD:
-        g_free(priv->password);
-        priv->password = g_value_dup_string(value);
-        break;
-    case PROP_PASSWORD_FLAGS:
-        priv->password_flags = g_value_get_flags(value);
-        break;
-    case PROP_PASSWORD_RAW:
-        g_bytes_unref(priv->password_raw);
-        priv->password_raw = g_value_dup_boxed(value);
-        break;
-    case PROP_PASSWORD_RAW_FLAGS:
-        priv->password_raw_flags = g_value_get_flags(value);
-        break;
-    case PROP_PRIVATE_KEY:
-        g_bytes_unref(priv->private_key);
-        priv->private_key = g_value_dup_boxed(value);
-        break;
-    case PROP_PRIVATE_KEY_PASSWORD:
-        nm_free_secret(priv->private_key_password);
-        priv->private_key_password = g_value_dup_string(value);
-        break;
-    case PROP_PRIVATE_KEY_PASSWORD_FLAGS:
-        priv->private_key_password_flags = g_value_get_flags(value);
-        break;
-    case PROP_PHASE2_PRIVATE_KEY:
-        g_bytes_unref(priv->phase2_private_key);
-        priv->phase2_private_key = g_value_dup_boxed(value);
-        break;
-    case PROP_PHASE2_PRIVATE_KEY_PASSWORD:
-        nm_free_secret(priv->phase2_private_key_password);
-        priv->phase2_private_key_password = g_value_dup_string(value);
-        break;
-    case PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS:
-        priv->phase2_private_key_password_flags = g_value_get_flags(value);
-        break;
-    case PROP_PIN:
-        g_free(priv->pin);
-        priv->pin = g_value_dup_string(value);
-        break;
-    case PROP_PIN_FLAGS:
-        priv->pin_flags = g_value_get_flags(value);
-        break;
-    case PROP_SYSTEM_CA_CERTS:
-        priv->system_ca_certs = g_value_get_boolean(value);
-        break;
-    case PROP_AUTH_TIMEOUT:
-        priv->auth_timeout = g_value_get_int(value);
-        break;
-    case PROP_OPTIONAL:
-        priv->optional = g_value_get_boolean(value);
+        priv->phase2_altsubject_matches = nm_strv_to_gslist(g_value_get_boxed(value), TRUE);
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_set_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -3473,40 +3168,10 @@ finalize(GObject *object)
     NMSetting8021x *       self = NM_SETTING_802_1X(object);
     NMSetting8021xPrivate *priv = NM_SETTING_802_1X_GET_PRIVATE(self);
 
-    g_free(priv->identity);
-    g_free(priv->anonymous_identity);
-    g_free(priv->ca_path);
-    g_free(priv->subject_match);
-    g_free(priv->domain_suffix_match);
-    g_free(priv->phase1_peapver);
-    g_free(priv->phase1_peaplabel);
-    g_free(priv->phase1_fast_provisioning);
-    g_free(priv->phase2_auth);
-    g_free(priv->phase2_autheap);
-    g_free(priv->phase2_ca_path);
-    g_free(priv->phase2_subject_match);
-    g_free(priv->phase2_domain_suffix_match);
-    g_free(priv->password);
-    g_bytes_unref(priv->password_raw);
-    g_free(priv->pin);
-
     g_slist_free_full(priv->eap, g_free);
     g_slist_free_full(priv->altsubject_matches, g_free);
     g_slist_free_full(priv->phase2_altsubject_matches, g_free);
 
-    g_bytes_unref(priv->ca_cert);
-    g_free(priv->ca_cert_password);
-    g_bytes_unref(priv->client_cert);
-    g_free(priv->client_cert_password);
-    g_bytes_unref(priv->private_key);
-    nm_free_secret(priv->private_key_password);
-    g_bytes_unref(priv->phase2_ca_cert);
-    g_free(priv->phase2_ca_cert_password);
-    g_bytes_unref(priv->phase2_client_cert);
-    g_free(priv->phase2_client_cert_password);
-    g_bytes_unref(priv->phase2_private_key);
-    nm_free_secret(priv->phase2_private_key_password);
-
     G_OBJECT_CLASS(nm_setting_802_1x_parent_class)->finalize(object);
 }
 
@@ -3562,11 +3227,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_IDENTITY=itsme
      * ---end---
      */
-    obj_properties[PROP_IDENTITY] = g_param_spec_string(NM_SETTING_802_1X_IDENTITY,
-                                                        "",
-                                                        "",
-                                                        NULL,
-                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_IDENTITY,
+                                              PROP_IDENTITY,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              identity);
 
     /**
      * NMSetting8021x:anonymous-identity:
@@ -3581,12 +3248,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: Anonymous identity for EAP authentication methods.
      * ---end---
      */
-    obj_properties[PROP_ANONYMOUS_IDENTITY] =
-        g_param_spec_string(NM_SETTING_802_1X_ANONYMOUS_IDENTITY,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_ANONYMOUS_IDENTITY,
+                                              PROP_ANONYMOUS_IDENTITY,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              anonymous_identity);
 
     /**
      * NMSetting8021x:pac-file:
@@ -3600,11 +3268,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_PAC_FILE=/home/joe/my-fast.pac
      * ---end---
      */
-    obj_properties[PROP_PAC_FILE] = g_param_spec_string(NM_SETTING_802_1X_PAC_FILE,
-                                                        "",
-                                                        "",
-                                                        NULL,
-                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PAC_FILE,
+                                              PROP_PAC_FILE,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              pac_file);
 
     /**
      * NMSetting8021x:ca-cert:
@@ -3634,11 +3304,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_CA_CERT=/home/joe/cacert.crt
      * ---end---
      */
-    obj_properties[PROP_CA_CERT] = g_param_spec_boxed(NM_SETTING_802_1X_CA_CERT,
-                                                      "",
-                                                      "",
-                                                      G_TYPE_BYTES,
-                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_bytes(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_802_1X_CA_CERT,
+                                             PROP_CA_CERT,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSetting8021xPrivate,
+                                             ca_cert);
 
     /**
      * NMSetting8021x:ca-cert-password:
@@ -3652,12 +3324,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
     /* ---ifcfg-rh---
      * ---end---
      */
-    obj_properties[PROP_CA_CERT_PASSWORD] =
-        g_param_spec_string(NM_SETTING_802_1X_CA_CERT_PASSWORD,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_CA_CERT_PASSWORD,
+                                              PROP_CA_CERT_PASSWORD,
+                                              NM_SETTING_PARAM_SECRET,
+                                              NMSetting8021xPrivate,
+                                              ca_cert_password);
 
     /**
      * NMSetting8021x:ca-cert-password-flags:
@@ -3669,13 +3342,12 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
     /* ---ifcfg-rh---
      * ---end---
      */
-    obj_properties[PROP_CA_CERT_PASSWORD_FLAGS] =
-        g_param_spec_flags(NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS,
-                           "",
-                           "",
-                           NM_TYPE_SETTING_SECRET_FLAGS,
-                           NM_SETTING_SECRET_FLAG_NONE,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_secret_flags(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_802_1X_CA_CERT_PASSWORD_FLAGS,
+                                                    PROP_CA_CERT_PASSWORD_FLAGS,
+                                                    NMSetting8021xPrivate,
+                                                    ca_cert_password_flags);
 
     /**
      * NMSetting8021x:ca-path:
@@ -3693,11 +3365,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: The search path for the certificate.
      * ---end---
      */
-    obj_properties[PROP_CA_PATH] = g_param_spec_string(NM_SETTING_802_1X_CA_PATH,
-                                                       "",
-                                                       "",
-                                                       NULL,
-                                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_CA_PATH,
+                                              PROP_CA_PATH,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              ca_path);
 
     /**
      * NMSetting8021x:subject-match:
@@ -3715,12 +3389,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_SUBJECT_MATCH="Red Hat"
      * ---end---
      */
-    obj_properties[PROP_SUBJECT_MATCH] =
-        g_param_spec_string(NM_SETTING_802_1X_SUBJECT_MATCH,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_SUBJECT_MATCH,
+                                              PROP_SUBJECT_MATCH,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              subject_match);
 
     /**
      * NMSetting8021x:altsubject-matches:
@@ -3762,12 +3437,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * variable: IEEE_8021X_DOMAIN_SUFFIX_MATCH(+)
      * ---end---
      */
-    obj_properties[PROP_DOMAIN_SUFFIX_MATCH] =
-        g_param_spec_string(NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_DOMAIN_SUFFIX_MATCH,
+                                              PROP_DOMAIN_SUFFIX_MATCH,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              domain_suffix_match);
 
     /**
      * NMSetting8021x:domain-match:
@@ -3787,12 +3463,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * variable: IEEE_8021X_DOMAIN_MATCH(+)
      * ---end---
      */
-    obj_properties[PROP_DOMAIN_MATCH] =
-        g_param_spec_string(NM_SETTING_802_1X_DOMAIN_MATCH,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_DOMAIN_MATCH,
+                                              PROP_DOMAIN_MATCH,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              domain_match);
 
     /**
      * NMSetting8021x:client-cert:
@@ -3817,12 +3494,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_CLIENT_CERT=/home/joe/mycert.crt
      * ---end---
      */
-    obj_properties[PROP_CLIENT_CERT] =
-        g_param_spec_boxed(NM_SETTING_802_1X_CLIENT_CERT,
-                           "",
-                           "",
-                           G_TYPE_BYTES,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_bytes(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_802_1X_CLIENT_CERT,
+                                             PROP_CLIENT_CERT,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSetting8021xPrivate,
+                                             client_cert);
 
     /**
      * NMSetting8021x:client-cert-password:
@@ -3836,12 +3514,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
     /* ---ifcfg-rh---
      * ---end---
      */
-    obj_properties[PROP_CLIENT_CERT_PASSWORD] =
-        g_param_spec_string(NM_SETTING_802_1X_CLIENT_CERT_PASSWORD,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_CLIENT_CERT_PASSWORD,
+                                              PROP_CLIENT_CERT_PASSWORD,
+                                              NM_SETTING_PARAM_SECRET,
+                                              NMSetting8021xPrivate,
+                                              client_cert_password);
 
     /**
      * NMSetting8021x:client-cert-password-flags:
@@ -3853,13 +3532,12 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
     /* ---ifcfg-rh---
      * ---end---
      */
-    obj_properties[PROP_CLIENT_CERT_PASSWORD_FLAGS] =
-        g_param_spec_flags(NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS,
-                           "",
-                           "",
-                           NM_TYPE_SETTING_SECRET_FLAGS,
-                           NM_SETTING_SECRET_FLAG_NONE,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_secret_flags(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_802_1X_CLIENT_CERT_PASSWORD_FLAGS,
+                                                    PROP_CLIENT_CERT_PASSWORD_FLAGS,
+                                                    NMSetting8021xPrivate,
+                                                    client_cert_password_flags);
 
     /**
      * NMSetting8021x:phase1-peapver:
@@ -3878,12 +3556,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: Use to force a specific PEAP version.
      * ---end---
      */
-    obj_properties[PROP_PHASE1_PEAPVER] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE1_PEAPVER,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE1_PEAPVER,
+                                              PROP_PHASE1_PEAPVER,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              phase1_peapver);
 
     /**
      * NMSetting8021x:phase1-peaplabel:
@@ -3901,12 +3580,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: Use to force the new PEAP label during key derivation.
      * ---end---
      */
-    obj_properties[PROP_PHASE1_PEAPLABEL] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE1_PEAPLABEL,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE1_PEAPLABEL,
+                                              PROP_PHASE1_PEAPLABEL,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              phase1_peaplabel);
 
     /**
      * NMSetting8021x:phase1-fast-provisioning:
@@ -3926,12 +3606,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_FAST_PROVISIONING="allow-auth allow-unauth"
      * ---end---
      */
-    obj_properties[PROP_PHASE1_FAST_PROVISIONING] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING,
+                                              PROP_PHASE1_FAST_PROVISIONING,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              phase1_fast_provisioning);
 
     /**
      * NMSetting8021x:phase1-auth-flags:
@@ -3953,14 +3634,16 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_PHASE1_AUTH_FLAGS="tls-1-0-disable tls-1-1-disable"
      * ---end---
      */
-    obj_properties[PROP_PHASE1_AUTH_FLAGS] =
-        g_param_spec_uint(NM_SETTING_802_1X_PHASE1_AUTH_FLAGS,
-                          "",
-                          "",
-                          0,
-                          G_MAXUINT32,
-                          NM_SETTING_802_1X_AUTH_FLAGS_NONE,
-                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE1_AUTH_FLAGS,
+                                              PROP_PHASE1_AUTH_FLAGS,
+                                              0,
+                                              G_MAXUINT32,
+                                              NM_SETTING_802_1X_AUTH_FLAGS_NONE,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              phase1_auth_flags);
 
     /**
      * NMSetting8021x:phase2-auth:
@@ -3986,12 +3669,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_INNER_AUTH_METHODS=PAP
      * ---end---
      */
-    obj_properties[PROP_PHASE2_AUTH] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE2_AUTH,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE2_AUTH,
+                                              PROP_PHASE2_AUTH,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              phase2_auth);
 
     /**
      * NMSetting8021x:phase2-autheap:
@@ -4012,12 +3696,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_INNER_AUTH_METHODS="MSCHAPV2 EAP-TLS"
      * ---end---
      */
-    obj_properties[PROP_PHASE2_AUTHEAP] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE2_AUTHEAP,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE2_AUTHEAP,
+                                              PROP_PHASE2_AUTHEAP,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              phase2_autheap);
 
     /**
      * NMSetting8021x:phase2-ca-cert:
@@ -4041,12 +3726,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * Setting this property directly is discouraged; use the
      * nm_setting_802_1x_set_phase2_ca_cert() function instead.
      **/
-    obj_properties[PROP_PHASE2_CA_CERT] =
-        g_param_spec_boxed(NM_SETTING_802_1X_PHASE2_CA_CERT,
-                           "",
-                           "",
-                           G_TYPE_BYTES,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_bytes(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_802_1X_PHASE2_CA_CERT,
+                                             PROP_PHASE2_CA_CERT,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSetting8021xPrivate,
+                                             phase2_ca_cert);
 
     /**
      * NMSetting8021x:phase2-ca-cert-password:
@@ -4060,12 +3746,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
     /* ---ifcfg-rh---
      * ---end---
      */
-    obj_properties[PROP_PHASE2_CA_CERT_PASSWORD] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD,
+                                              PROP_PHASE2_CA_CERT_PASSWORD,
+                                              NM_SETTING_PARAM_SECRET,
+                                              NMSetting8021xPrivate,
+                                              phase2_ca_cert_password);
 
     /**
      * NMSetting8021x:phase2-ca-cert-password-flags:
@@ -4077,13 +3764,12 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
     /* ---ifcfg-rh---
      * ---end---
      */
-    obj_properties[PROP_PHASE2_CA_CERT_PASSWORD_FLAGS] =
-        g_param_spec_flags(NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS,
-                           "",
-                           "",
-                           NM_TYPE_SETTING_SECRET_FLAGS,
-                           NM_SETTING_SECRET_FLAG_NONE,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_secret_flags(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_802_1X_PHASE2_CA_CERT_PASSWORD_FLAGS,
+                                                    PROP_PHASE2_CA_CERT_PASSWORD_FLAGS,
+                                                    NMSetting8021xPrivate,
+                                                    phase2_ca_cert_password_flags);
 
     /**
      * NMSetting8021x:phase2-ca-path:
@@ -4101,12 +3787,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: The search path for the certificate.
      * ---end---
      */
-    obj_properties[PROP_PHASE2_CA_PATH] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE2_CA_PATH,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE2_CA_PATH,
+                                              PROP_PHASE2_CA_PATH,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              phase2_ca_path);
 
     /**
      * NMSetting8021x:phase2-subject-match:
@@ -4125,12 +3812,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_PHASE2_SUBJECT_MATCH="Red Hat"
      * ---end---
      */
-    obj_properties[PROP_PHASE2_SUBJECT_MATCH] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE2_SUBJECT_MATCH,
+                                              PROP_PHASE2_SUBJECT_MATCH,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              phase2_subject_match);
 
     /**
      * NMSetting8021x:phase2-altsubject-matches:
@@ -4172,12 +3860,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * variable: IEEE_8021X_PHASE2_DOMAIN_SUFFIX_MATCH(+)
      * ---end---
      */
-    obj_properties[PROP_PHASE2_DOMAIN_SUFFIX_MATCH] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE2_DOMAIN_SUFFIX_MATCH,
+                                              PROP_PHASE2_DOMAIN_SUFFIX_MATCH,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              phase2_domain_suffix_match);
 
     /**
      * NMSetting8021x:phase2-domain-match:
@@ -4198,12 +3887,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * variable: IEEE_8021X_PHASE2_DOMAIN_MATCH(+)
      * ---end---
      */
-    obj_properties[PROP_PHASE2_DOMAIN_MATCH] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE2_DOMAIN_MATCH,
+                                              PROP_PHASE2_DOMAIN_MATCH,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSetting8021xPrivate,
+                                              phase2_domain_match);
 
     /**
      * NMSetting8021x:phase2-client-cert:
@@ -4231,12 +3921,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_INNER_CLIENT_CERT=/home/joe/mycert.crt
      * ---end---
      */
-    obj_properties[PROP_PHASE2_CLIENT_CERT] =
-        g_param_spec_boxed(NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
-                           "",
-                           "",
-                           G_TYPE_BYTES,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_bytes(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_802_1X_PHASE2_CLIENT_CERT,
+                                             PROP_PHASE2_CLIENT_CERT,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSetting8021xPrivate,
+                                             phase2_client_cert);
 
     /**
      * NMSetting8021x:phase2-client-cert-password:
@@ -4250,12 +3941,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
     /* ---ifcfg-rh---
      * ---end---
      */
-    obj_properties[PROP_PHASE2_CLIENT_CERT_PASSWORD] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD,
+                                              PROP_PHASE2_CLIENT_CERT_PASSWORD,
+                                              NM_SETTING_PARAM_SECRET,
+                                              NMSetting8021xPrivate,
+                                              phase2_client_cert_password);
 
     /**
      * NMSetting8021x:phase2-client-cert-password-flags:
@@ -4267,13 +3959,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
     /* ---ifcfg-rh---
      * ---end---
      */
-    obj_properties[PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS] =
-        g_param_spec_flags(NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS,
-                           "",
-                           "",
-                           NM_TYPE_SETTING_SECRET_FLAGS,
-                           NM_SETTING_SECRET_FLAG_NONE,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_secret_flags(
+        properties_override,
+        obj_properties,
+        NM_SETTING_802_1X_PHASE2_CLIENT_CERT_PASSWORD_FLAGS,
+        PROP_PHASE2_CLIENT_CERT_PASSWORD_FLAGS,
+        NMSetting8021xPrivate,
+        phase2_client_cert_password_flags);
 
     /**
      * NMSetting8021x:password:
@@ -4289,12 +3981,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *   lookaside file, or it can be owned by a secret agent.
      * ---end---
      */
-    obj_properties[PROP_PASSWORD] =
-        g_param_spec_string(NM_SETTING_802_1X_PASSWORD,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PASSWORD,
+                                              PROP_PASSWORD,
+                                              NM_SETTING_PARAM_SECRET,
+                                              NMSetting8021xPrivate,
+                                              password);
 
     /**
      * NMSetting8021x:password-flags:
@@ -4308,13 +4001,12 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: Password flags for IEEE_8021X_PASSWORD password.
      * ---end---
      */
-    obj_properties[PROP_PASSWORD_FLAGS] =
-        g_param_spec_flags(NM_SETTING_802_1X_PASSWORD_FLAGS,
-                           "",
-                           "",
-                           NM_TYPE_SETTING_SECRET_FLAGS,
-                           NM_SETTING_SECRET_FLAG_NONE,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_secret_flags(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_802_1X_PASSWORD_FLAGS,
+                                                    PROP_PASSWORD_FLAGS,
+                                                    NMSetting8021xPrivate,
+                                                    password_flags);
 
     /**
      * NMSetting8021x:password-raw:
@@ -4332,12 +4024,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_PASSWORD_RAW=041c8320083aa4bf
      * ---end---
      */
-    obj_properties[PROP_PASSWORD_RAW] =
-        g_param_spec_boxed(NM_SETTING_802_1X_PASSWORD_RAW,
-                           "",
-                           "",
-                           G_TYPE_BYTES,
-                           G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_bytes(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_802_1X_PASSWORD_RAW,
+                                             PROP_PASSWORD_RAW,
+                                             NM_SETTING_PARAM_SECRET,
+                                             NMSetting8021xPrivate,
+                                             password_raw);
 
     /**
      * NMSetting8021x:password-raw-flags:
@@ -4350,13 +4043,12 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: The secret flags for password-raw.
      * ---end---
      */
-    obj_properties[PROP_PASSWORD_RAW_FLAGS] =
-        g_param_spec_flags(NM_SETTING_802_1X_PASSWORD_RAW_FLAGS,
-                           "",
-                           "",
-                           NM_TYPE_SETTING_SECRET_FLAGS,
-                           NM_SETTING_SECRET_FLAG_NONE,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_secret_flags(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_802_1X_PASSWORD_RAW_FLAGS,
+                                                    PROP_PASSWORD_RAW_FLAGS,
+                                                    NMSetting8021xPrivate,
+                                                    password_raw_flags);
 
     /**
      * NMSetting8021x:private-key:
@@ -4396,12 +4088,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * example: IEEE_8021X_PRIVATE_KEY=/home/joe/mykey.p12
      * ---end---
      */
-    obj_properties[PROP_PRIVATE_KEY] =
-        g_param_spec_boxed(NM_SETTING_802_1X_PRIVATE_KEY,
-                           "",
-                           "",
-                           G_TYPE_BYTES,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_bytes(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_802_1X_PRIVATE_KEY,
+                                             PROP_PRIVATE_KEY,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSetting8021xPrivate,
+                                             private_key);
 
     /**
      * NMSetting8021x:private-key-password:
@@ -4420,12 +4113,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *   lookaside file, or it can be owned by a secret agent.
      * ---end---
      */
-    obj_properties[PROP_PRIVATE_KEY_PASSWORD] =
-        g_param_spec_string(NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
+                                              PROP_PRIVATE_KEY_PASSWORD,
+                                              NM_SETTING_PARAM_SECRET,
+                                              NMSetting8021xPrivate,
+                                              private_key_password);
 
     /**
      * NMSetting8021x:private-key-password-flags:
@@ -4440,13 +4134,12 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: Password flags for IEEE_8021X_PRIVATE_KEY_PASSWORD password.
      * ---end---
      */
-    obj_properties[PROP_PRIVATE_KEY_PASSWORD_FLAGS] =
-        g_param_spec_flags(NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS,
-                           "",
-                           "",
-                           NM_TYPE_SETTING_SECRET_FLAGS,
-                           NM_SETTING_SECRET_FLAG_NONE,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_secret_flags(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD_FLAGS,
+                                                    PROP_PRIVATE_KEY_PASSWORD_FLAGS,
+                                                    NMSetting8021xPrivate,
+                                                    private_key_password_flags);
 
     /**
      * NMSetting8021x:phase2-private-key:
@@ -4480,12 +4173,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: Private key for inner authentication method for EAP-TLS.
      * ---end---
      */
-    obj_properties[PROP_PHASE2_PRIVATE_KEY] =
-        g_param_spec_boxed(NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
-                           "",
-                           "",
-                           G_TYPE_BYTES,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_bytes(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_802_1X_PHASE2_PRIVATE_KEY,
+                                             PROP_PHASE2_PRIVATE_KEY,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSetting8021xPrivate,
+                                             phase2_private_key);
 
     /**
      * NMSetting8021x:phase2-private-key-password:
@@ -4505,12 +4199,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      *   lookaside file, or it can be owned by a secret agent.
      * ---end---
      */
-    obj_properties[PROP_PHASE2_PRIVATE_KEY_PASSWORD] =
-        g_param_spec_string(NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD,
+                                              PROP_PHASE2_PRIVATE_KEY_PASSWORD,
+                                              NM_SETTING_PARAM_SECRET,
+                                              NMSetting8021xPrivate,
+                                              phase2_private_key_password);
 
     /**
      * NMSetting8021x:phase2-private-key-password-flags:
@@ -4525,13 +4220,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: Password flags for IEEE_8021X_INNER_PRIVATE_KEY_PASSWORD password.
      * ---end---
      */
-    obj_properties[PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS] =
-        g_param_spec_flags(NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS,
-                           "",
-                           "",
-                           NM_TYPE_SETTING_SECRET_FLAGS,
-                           NM_SETTING_SECRET_FLAG_NONE,
-                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_secret_flags(
+        properties_override,
+        obj_properties,
+        NM_SETTING_802_1X_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS,
+        PROP_PHASE2_PRIVATE_KEY_PASSWORD_FLAGS,
+        NMSetting8021xPrivate,
+        phase2_private_key_password_flags);
 
     /**
      * NMSetting8021x:pin:
@@ -4544,12 +4239,13 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: The pin secret used for EAP authentication methods.
      * ---end---
      */
-    obj_properties[PROP_PIN] =
-        g_param_spec_string(NM_SETTING_802_1X_PIN,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_802_1X_PIN,
+                                              PROP_PIN,
+                                              NM_SETTING_PARAM_SECRET,
+                                              NMSetting8021xPrivate,
+                                              pin);
 
     /**
      * NMSetting8021x:pin-flags:
@@ -4562,12 +4258,12 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: The secret flags for the pin property.
      * ---end---
      */
-    obj_properties[PROP_PIN_FLAGS] = g_param_spec_flags(NM_SETTING_802_1X_PIN_FLAGS,
-                                                        "",
-                                                        "",
-                                                        NM_TYPE_SETTING_SECRET_FLAGS,
-                                                        NM_SETTING_SECRET_FLAG_NONE,
-                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_secret_flags(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_802_1X_PIN_FLAGS,
+                                                    PROP_PIN_FLAGS,
+                                                    NMSetting8021xPrivate,
+                                                    pin_flags);
 
     /**
      * NMSetting8021x:system-ca-certs:
@@ -4588,13 +4284,14 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: a boolean value.
      * ---end---
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_802_1X_SYSTEM_CA_CERTS,
-                                        PROP_SYSTEM_CA_CERTS,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_802_1x_get_system_ca_certs);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_802_1X_SYSTEM_CA_CERTS,
+                                               PROP_SYSTEM_CA_CERTS,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSetting8021xPrivate,
+                                               system_ca_certs);
 
     /**
      * NMSetting8021x:auth-timeout:
@@ -4611,14 +4308,16 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: Timeout in seconds for the 802.1X authentication. Zero means the global default or 25.
      * ---end---
      */
-    obj_properties[PROP_AUTH_TIMEOUT] = g_param_spec_int(
-        NM_SETTING_802_1X_AUTH_TIMEOUT,
-        "",
-        "",
-        0,
-        G_MAXINT32,
-        0,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_802_1X_AUTH_TIMEOUT,
+                                             PROP_AUTH_TIMEOUT,
+                                             0,
+                                             G_MAXINT32,
+                                             0,
+                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                             NMSetting8021xPrivate,
+                                             auth_timeout);
 
     /**
      * NMSetting8021x:optional:
@@ -4638,18 +4337,20 @@ nm_setting_802_1x_class_init(NMSetting8021xClass *klass)
      * description: whether the 802.1X authentication is optional
      * ---end---
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_802_1X_OPTIONAL,
-                                        PROP_OPTIONAL,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_802_1x_get_optional);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_802_1X_OPTIONAL,
+                                               PROP_OPTIONAL,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSetting8021xPrivate,
+                                               optional);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_802_1X,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_802_1X,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-adsl.c b/src/libnm-core-impl/nm-setting-adsl.c
index 9f45163d..8e5dc8cf 100644
--- a/src/libnm-core-impl/nm-setting-adsl.c
+++ b/src/libnm-core-impl/nm-setting-adsl.c
@@ -31,13 +31,13 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_USERNAME,
                                   PROP_VCI, );
 
 typedef struct {
-    char *               username;
-    char *               password;
-    NMSettingSecretFlags password_flags;
-    char *               protocol;
-    char *               encapsulation;
-    guint32              vpi;
-    guint32              vci;
+    char *  username;
+    char *  password;
+    char *  protocol;
+    char *  encapsulation;
+    guint   password_flags;
+    guint32 vpi;
+    guint32 vci;
 } NMSettingAdslPrivate;
 
 /**
@@ -244,81 +244,6 @@ need_secrets(NMSetting *setting)
 /*****************************************************************************/
 
 static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    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;
-    }
-}
-
-static void
-set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
-{
-    NMSettingAdslPrivate *priv = NM_SETTING_ADSL_GET_PRIVATE(object);
-    const char *          str;
-
-    switch (prop_id) {
-    case PROP_USERNAME:
-        g_free(priv->username);
-        priv->username = g_value_dup_string(value);
-        break;
-    case PROP_PASSWORD:
-        g_free(priv->password);
-        priv->password = g_value_dup_string(value);
-        break;
-    case PROP_PASSWORD_FLAGS:
-        priv->password_flags = g_value_get_flags(value);
-        break;
-    case PROP_PROTOCOL:
-        g_free(priv->protocol);
-        str            = g_value_get_string(value);
-        priv->protocol = str ? g_ascii_strdown(str, -1) : NULL;
-        break;
-    case PROP_ENCAPSULATION:
-        g_free(priv->encapsulation);
-        str                 = g_value_get_string(value);
-        priv->encapsulation = str ? g_ascii_strdown(str, -1) : NULL;
-        break;
-    case PROP_VPI:
-        priv->vpi = g_value_get_uint(value);
-        break;
-    case PROP_VCI:
-        priv->vci = g_value_get_uint(value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static void
 nm_setting_adsl_init(NMSettingAdsl *setting)
 {}
 
@@ -336,19 +261,6 @@ nm_setting_adsl_new(void)
 }
 
 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
 nm_setting_adsl_class_init(NMSettingAdslClass *klass)
 {
     GObjectClass *  object_class        = G_OBJECT_CLASS(klass);
@@ -357,9 +269,8 @@ nm_setting_adsl_class_init(NMSettingAdslClass *klass)
 
     g_type_class_add_private(klass, sizeof(NMSettingAdslPrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-    object_class->finalize     = finalize;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify         = verify;
     setting_class->verify_secrets = verify_secrets;
@@ -370,92 +281,104 @@ nm_setting_adsl_class_init(NMSettingAdslClass *klass)
      *
      * Username used to authenticate with the ADSL service.
      **/
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_ADSL_USERNAME,
-                                       PROP_USERNAME,
-                                       NM_SETTING_PARAM_NONE,
-                                       nm_setting_adsl_get_username);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_ADSL_USERNAME,
+                                              PROP_USERNAME,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingAdslPrivate,
+                                              username);
 
     /**
      * NMSettingAdsl:password:
      *
      * Password used to authenticate with the ADSL service.
      **/
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_ADSL_PASSWORD,
-                                       PROP_PASSWORD,
-                                       NM_SETTING_PARAM_SECRET,
-                                       nm_setting_adsl_get_password);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_ADSL_PASSWORD,
+                                              PROP_PASSWORD,
+                                              NM_SETTING_PARAM_SECRET,
+                                              NMSettingAdslPrivate,
+                                              password);
 
     /**
      * NMSettingAdsl:password-flags:
      *
      * Flags indicating how to handle the #NMSettingAdsl:password property.
      **/
-    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);
+    _nm_setting_property_define_direct_secret_flags(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_ADSL_PASSWORD_FLAGS,
+                                                    PROP_PASSWORD_FLAGS,
+                                                    NMSettingAdslPrivate,
+                                                    password_flags);
 
     /**
      * NMSettingAdsl:protocol:
      *
      * ADSL connection protocol.  Can be "pppoa", "pppoe" or "ipoatm".
      **/
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_ADSL_PROTOCOL,
-                                       PROP_PROTOCOL,
-                                       NM_SETTING_PARAM_NONE,
-                                       nm_setting_adsl_get_protocol);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_ADSL_PROTOCOL,
+                                              PROP_PROTOCOL,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingAdslPrivate,
+                                              protocol,
+                                              .direct_set_string_ascii_strdown = TRUE);
 
     /**
      * NMSettingAdsl:encapsulation:
      *
      * Encapsulation of ADSL connection.  Can be "vcmux" or "llc".
      **/
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_ADSL_ENCAPSULATION,
-                                       PROP_ENCAPSULATION,
-                                       NM_SETTING_PARAM_NONE,
-                                       nm_setting_adsl_get_encapsulation);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_ADSL_ENCAPSULATION,
+                                              PROP_ENCAPSULATION,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingAdslPrivate,
+                                              encapsulation,
+                                              .direct_set_string_ascii_strdown = TRUE);
 
     /**
      * NMSettingAdsl:vpi:
      *
      * VPI of ADSL connection
      **/
-    obj_properties[PROP_VPI] = g_param_spec_uint(NM_SETTING_ADSL_VPI,
-                                                 "",
-                                                 "",
-                                                 0,
-                                                 65536,
-                                                 0,
-                                                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_ADSL_VPI,
+                                              PROP_VPI,
+                                              0,
+                                              65536,
+                                              0,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingAdslPrivate,
+                                              vpi);
 
     /**
      * NMSettingAdsl:vci:
      *
      * VCI of ADSL connection
      **/
-    obj_properties[PROP_VCI] = g_param_spec_uint(NM_SETTING_ADSL_VCI,
-                                                 "",
-                                                 "",
-                                                 0,
-                                                 65536,
-                                                 0,
-                                                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_ADSL_VCI,
+                                              PROP_VCI,
+                                              0,
+                                              65536,
+                                              0,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingAdslPrivate,
+                                              vci);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_ADSL,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_ADSL,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-bluetooth.c b/src/libnm-core-impl/nm-setting-bluetooth.c
index 4bd91a69..53e4912f 100644
--- a/src/libnm-core-impl/nm-setting-bluetooth.c
+++ b/src/libnm-core-impl/nm-setting-bluetooth.c
@@ -94,6 +94,8 @@ nm_setting_bluetooth_get_bdaddr(NMSettingBluetooth *setting)
     return NM_SETTING_BLUETOOTH_GET_PRIVATE(setting)->bdaddr;
 }
 
+/*****************************************************************************/
+
 static gboolean
 verify(NMSetting *setting, NMConnection *connection, GError **error)
 {
@@ -229,46 +231,6 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    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;
-    }
-}
-
-static void
-set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
-{
-    NMSettingBluetoothPrivate *priv = NM_SETTING_BLUETOOTH_GET_PRIVATE(object);
-
-    switch (prop_id) {
-    case PROP_BDADDR:
-        g_free(priv->bdaddr);
-        priv->bdaddr = g_value_dup_string(value);
-        break;
-    case PROP_TYPE:
-        g_free(priv->type);
-        priv->type = g_value_dup_string(value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static void
 nm_setting_bluetooth_init(NMSettingBluetooth *setting)
 {}
 
@@ -286,17 +248,6 @@ nm_setting_bluetooth_new(void)
 }
 
 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
 nm_setting_bluetooth_class_init(NMSettingBluetoothClass *klass)
 {
     GObjectClass *  object_class        = G_OBJECT_CLASS(klass);
@@ -305,9 +256,8 @@ nm_setting_bluetooth_class_init(NMSettingBluetoothClass *klass)
 
     g_type_class_add_private(klass, sizeof(NMSettingBluetoothPrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-    object_class->finalize     = finalize;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify = verify;
 
@@ -316,15 +266,14 @@ nm_setting_bluetooth_class_init(NMSettingBluetoothClass *klass)
      *
      * The Bluetooth address of the device.
      **/
-    obj_properties[PROP_BDADDR] = g_param_spec_string(
-        NM_SETTING_BLUETOOTH_BDADDR,
-        "",
-        "",
-        NULL,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_BDADDR],
-                                 &nm_sett_info_propert_type_mac_address);
+    _nm_setting_property_define_direct_mac_address(properties_override,
+                                                   obj_properties,
+                                                   NM_SETTING_BLUETOOTH_BDADDR,
+                                                   PROP_BDADDR,
+                                                   NM_SETTING_PARAM_INFERRABLE,
+                                                   NMSettingBluetoothPrivate,
+                                                   bdaddr,
+                                                   .direct_set_string_mac_address_len = ETH_ALEN);
 
     /**
      * NMSettingBluetooth:type:
@@ -332,17 +281,19 @@ 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.
      **/
-    obj_properties[PROP_TYPE] = g_param_spec_string(NM_SETTING_BLUETOOTH_TYPE,
-                                                    "",
-                                                    "",
-                                                    NULL,
-                                                    G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE
-                                                        | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BLUETOOTH_TYPE,
+                                              PROP_TYPE,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBluetoothPrivate,
+                                              type);
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_BLUETOOTH,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-bond-port.c b/src/libnm-core-impl/nm-setting-bond-port.c
new file mode 100644
index 00000000..4538e8b9
--- /dev/null
+++ b/src/libnm-core-impl/nm-setting-bond-port.c
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021 Red Hat, Inc.
+ */
+
+#include "libnm-core-impl/nm-default-libnm-core.h"
+
+#include "nm-connection-private.h"
+#include "nm-setting-bond-port.h"
+#include "nm-setting-bond.h"
+#include "nm-setting-connection.h"
+#include "nm-utils-private.h"
+#include "nm-utils.h"
+
+/**
+ * SECTION:nm-setting-bond-port
+ * @short_description: Describes connection properties for bond ports
+ *
+ * The #NMSettingBondPort object is a #NMSetting subclass that describes
+ * optional properties that apply to bond ports.
+ **/
+
+/*****************************************************************************/
+
+NM_GOBJECT_PROPERTIES_DEFINE(NMSettingBondPort, PROP_QUEUE_ID, );
+
+typedef struct {
+    guint32 queue_id;
+} NMSettingBondPortPrivate;
+
+/**
+ * NMSettingBondPort:
+ *
+ * Bond Port Settings
+ */
+struct _NMSettingBondPort {
+    NMSetting                parent;
+    NMSettingBondPortPrivate _priv;
+};
+
+struct _NMSettingBondPortClass {
+    NMSettingClass parent;
+};
+
+G_DEFINE_TYPE(NMSettingBondPort, nm_setting_bond_port, NM_TYPE_SETTING)
+
+#define NM_SETTING_BOND_PORT_GET_PRIVATE(self) \
+    _NM_GET_PRIVATE(self, NMSettingBondPort, NM_IS_SETTING_BOND_PORT, NMSetting)
+
+/*****************************************************************************/
+
+/**
+ * nm_setting_bond_port_get_queue_id:
+ * @setting: the #NMSettingBondPort
+ *
+ * Returns: the #NMSettingBondPort:queue_id property of the setting
+ *
+ * Since: 1.34
+ **/
+guint32
+nm_setting_bond_port_get_queue_id(NMSettingBondPort *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_BOND_PORT(setting), 0);
+
+    return NM_SETTING_BOND_PORT_GET_PRIVATE(setting)->queue_id;
+}
+
+/*****************************************************************************/
+
+static gboolean
+verify(NMSetting *setting, NMConnection *connection, GError **error)
+{
+    if (connection) {
+        NMSettingConnection *s_con;
+        const char *         slave_type;
+
+        s_con = nm_connection_get_setting_connection(connection);
+        if (!s_con) {
+            g_set_error(error,
+                        NM_CONNECTION_ERROR,
+                        NM_CONNECTION_ERROR_MISSING_SETTING,
+                        _("missing setting"));
+            g_prefix_error(error, "%s: ", NM_SETTING_CONNECTION_SETTING_NAME);
+            return FALSE;
+        }
+
+        slave_type = nm_setting_connection_get_slave_type(s_con);
+        if (!nm_streq0(slave_type, NM_SETTING_BOND_SETTING_NAME)) {
+            g_set_error(error,
+                        NM_CONNECTION_ERROR,
+                        NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                        _("A connection with a '%s' setting must have the slave-type set to '%s'"),
+                        NM_SETTING_BOND_PORT_SETTING_NAME,
+                        NM_SETTING_BOND_SETTING_NAME);
+            g_prefix_error(error,
+                           "%s.%s: ",
+                           NM_SETTING_CONNECTION_SETTING_NAME,
+                           NM_SETTING_CONNECTION_SLAVE_TYPE);
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+/*****************************************************************************/
+
+static void
+nm_setting_bond_port_init(NMSettingBondPort *setting)
+{}
+
+/**
+ * nm_setting_bond_port_new:
+ *
+ * Creates a new #NMSettingBondPort object with default values.
+ *
+ * Returns: (transfer full): the new empty #NMSettingBondPort object
+ *
+ * Since: 1.34
+ **/
+NMSetting *
+nm_setting_bond_port_new(void)
+{
+    return g_object_new(NM_TYPE_SETTING_BOND_PORT, NULL);
+}
+
+static void
+nm_setting_bond_port_class_init(NMSettingBondPortClass *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 = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
+
+    setting_class->verify = verify;
+
+    /**
+     * NMSettingBondPort:queue-id:
+     *
+     * The queue ID of this bond port. The maximum value of queue ID is
+     * the number of TX queues currently active in device.
+     *
+     * Since: 1.34
+     **/
+    /* ---ifcfg-rh---
+     * property: queue-id
+     * variable: BONDING_OPTS: queue-id=
+     * values: 0 - 65535
+     * default: 0
+     * description: Queue ID.
+     * ---end---
+     */
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BOND_PORT_QUEUE_ID,
+                                              PROP_QUEUE_ID,
+                                              0,
+                                              G_MAXUINT16,
+                                              NM_BOND_PORT_QUEUE_ID_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBondPort,
+                                              _priv.queue_id);
+
+    g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
+
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_BOND_PORT,
+                             NULL,
+                             properties_override,
+                             0);
+}
diff --git a/src/libnm-core-impl/nm-setting-bond.c b/src/libnm-core-impl/nm-setting-bond.c
index 252adc73..e81130cd 100644
--- a/src/libnm-core-impl/nm-setting-bond.c
+++ b/src/libnm-core-impl/nm-setting-bond.c
@@ -1090,20 +1090,9 @@ options_equal(NMSettingBond *s_bond, NMSettingBond *s_bond2, NMSettingCompareFla
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_options(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_BOND_OPTIONS)) {
-        return (!set_b || options_equal(NM_SETTING_BOND(set_a), NM_SETTING_BOND(set_b), flags));
-    }
-
-    return NM_SETTING_CLASS(nm_setting_bond_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    return (!set_b || options_equal(NM_SETTING_BOND(set_a), NM_SETTING_BOND(set_b), flags));
 }
 
 /*****************************************************************************/
@@ -1190,8 +1179,7 @@ nm_setting_bond_class_init(NMSettingBondClass *klass)
     object_class->set_property = set_property;
     object_class->finalize     = finalize;
 
-    setting_class->verify           = verify;
-    setting_class->compare_property = compare_property;
+    setting_class->verify = verify;
 
     /**
      * NMSettingBond:options: (type GHashTable(utf8,utf8)):
@@ -1213,9 +1201,16 @@ nm_setting_bond_class_init(NMSettingBondClass *klass)
         "",
         G_TYPE_HASH_TABLE,
         G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_OPTIONS],
-                                 &nm_sett_info_propert_type_strdict);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_OPTIONS],
+        NM_SETT_INFO_PROPERT_TYPE_GPROP(NM_G_VARIANT_TYPE("a{ss}"),
+                                        .typdata_from_dbus.gprop_fcn = _nm_utils_strdict_from_dbus,
+                                        .typdata_to_dbus.gprop_type =
+                                            NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT,
+                                        .compare_fcn   = compare_fcn_options,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                        .from_dbus_is_full = TRUE));
 
     /* ---dbus---
      * property: interface-name
@@ -1231,8 +1226,9 @@ nm_setting_bond_class_init(NMSettingBondClass *klass)
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_BOND,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-bridge-port.c b/src/libnm-core-impl/nm-setting-bridge-port.c
index 33d0c009..48ad74a1 100644
--- a/src/libnm-core-impl/nm-setting-bridge-port.c
+++ b/src/libnm-core-impl/nm-setting-bridge-port.c
@@ -34,9 +34,9 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingBridgePort,
 
 typedef struct {
     GPtrArray *vlans;
-    guint16    priority;
-    guint16    path_cost;
-    bool       hairpin_mode : 1;
+    guint32    priority;
+    guint32    path_cost;
+    bool       hairpin_mode;
 } NMSettingBridgePortPrivate;
 
 /**
@@ -368,35 +368,13 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_vlans(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
-    NMSettingBridgePortPrivate *priv_a;
-    NMSettingBridgePortPrivate *priv_b;
-    guint                       i;
-
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_BRIDGE_PORT_VLANS)) {
-        if (set_b) {
-            priv_a = NM_SETTING_BRIDGE_PORT_GET_PRIVATE(set_a);
-            priv_b = NM_SETTING_BRIDGE_PORT_GET_PRIVATE(set_b);
-
-            if (priv_a->vlans->len != priv_b->vlans->len)
-                return FALSE;
-            for (i = 0; i < priv_a->vlans->len; i++) {
-                if (nm_bridge_vlan_cmp(priv_a->vlans->pdata[i], priv_b->vlans->pdata[i]))
-                    return FALSE;
-            }
-        }
-        return TRUE;
+    if (set_b) {
+        return _nm_utils_bridge_compare_vlans(NM_SETTING_BRIDGE_PORT_GET_PRIVATE(set_a)->vlans,
+                                              NM_SETTING_BRIDGE_PORT_GET_PRIVATE(set_b)->vlans);
     }
-
-    return NM_SETTING_CLASS(nm_setting_bridge_port_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    return TRUE;
 }
 
 /*****************************************************************************/
@@ -407,15 +385,6 @@ 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:
-        g_value_set_uint(value, priv->priority);
-        break;
-    case PROP_PATH_COST:
-        g_value_set_uint(value, priv->path_cost);
-        break;
-    case PROP_HAIRPIN_MODE:
-        g_value_set_boolean(value, priv->hairpin_mode);
-        break;
     case PROP_VLANS:
         g_value_take_boxed(value,
                            _nm_utils_copy_array(priv->vlans,
@@ -423,7 +392,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
                                                 (GDestroyNotify) nm_bridge_vlan_unref));
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_get_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -434,15 +403,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
     NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE(object);
 
     switch (prop_id) {
-    case PROP_PRIORITY:
-        priv->priority = g_value_get_uint(value);
-        break;
-    case PROP_PATH_COST:
-        priv->path_cost = g_value_get_uint(value);
-        break;
-    case PROP_HAIRPIN_MODE:
-        priv->hairpin_mode = g_value_get_boolean(value);
-        break;
     case PROP_VLANS:
         g_ptr_array_unref(priv->vlans);
         priv->vlans = _nm_utils_copy_array(g_value_get_boxed(value),
@@ -450,7 +410,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
                                            (GDestroyNotify) nm_bridge_vlan_unref);
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_set_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -463,9 +423,6 @@ nm_setting_bridge_port_init(NMSettingBridgePort *setting)
     NMSettingBridgePortPrivate *priv = NM_SETTING_BRIDGE_PORT_GET_PRIVATE(setting);
 
     priv->vlans = g_ptr_array_new_with_free_func((GDestroyNotify) nm_bridge_vlan_unref);
-
-    priv->priority  = NM_BRIDGE_PORT_PRIORITY_DEF;
-    priv->path_cost = NM_BRIDGE_PORT_PATH_COST_DEF;
 }
 
 /**
@@ -500,12 +457,11 @@ nm_setting_bridge_port_class_init(NMSettingBridgePortClass *klass)
 
     g_type_class_add_private(klass, sizeof(NMSettingBridgePortPrivate));
 
-    object_class->finalize     = finalize;
     object_class->get_property = get_property;
     object_class->set_property = set_property;
+    object_class->finalize     = finalize;
 
-    setting_class->compare_property = compare_property;
-    setting_class->verify           = verify;
+    setting_class->verify = verify;
 
     /**
      * NMSettingBridgePort:priority:
@@ -520,14 +476,16 @@ nm_setting_bridge_port_class_init(NMSettingBridgePortClass *klass)
      * description: STP priority.
      * ---end---
      */
-    obj_properties[PROP_PRIORITY] =
-        g_param_spec_uint(NM_SETTING_BRIDGE_PORT_PRIORITY,
-                          "",
-                          "",
-                          NM_BRIDGE_PORT_PRIORITY_MIN,
-                          NM_BRIDGE_PORT_PRIORITY_MAX,
-                          NM_BRIDGE_PORT_PRIORITY_DEF,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_PORT_PRIORITY,
+                                              PROP_PRIORITY,
+                                              NM_BRIDGE_PORT_PRIORITY_MIN,
+                                              NM_BRIDGE_PORT_PRIORITY_MAX,
+                                              NM_BRIDGE_PORT_PRIORITY_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePortPrivate,
+                                              priority);
 
     /**
      * NMSettingBridgePort:path-cost:
@@ -543,13 +501,16 @@ nm_setting_bridge_port_class_init(NMSettingBridgePortClass *klass)
      * description: STP cost.
      * ---end---
      */
-    obj_properties[PROP_PATH_COST] = g_param_spec_uint(NM_SETTING_BRIDGE_PORT_PATH_COST,
-                                                       "",
-                                                       "",
-                                                       NM_BRIDGE_PORT_PATH_COST_MIN,
-                                                       NM_BRIDGE_PORT_PATH_COST_MAX,
-                                                       NM_BRIDGE_PORT_PATH_COST_DEF,
-                                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_PORT_PATH_COST,
+                                              PROP_PATH_COST,
+                                              NM_BRIDGE_PORT_PATH_COST_MIN,
+                                              NM_BRIDGE_PORT_PATH_COST_MAX,
+                                              NM_BRIDGE_PORT_PATH_COST_DEF,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingBridgePortPrivate,
+                                              path_cost);
 
     /**
      * NMSettingBridgePort:hairpin-mode:
@@ -564,13 +525,14 @@ nm_setting_bridge_port_class_init(NMSettingBridgePortClass *klass)
      * description: Hairpin mode of the bridge port.
      * ---end---
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE,
-                                        PROP_HAIRPIN_MODE,
-                                        FALSE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_bridge_port_get_hairpin_mode);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE,
+                                               PROP_HAIRPIN_MODE,
+                                               FALSE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingBridgePortPrivate,
+                                               hairpin_mode);
 
     /**
      * NMSettingBridgePort:vlans: (type GPtrArray(NMBridgeVlan))
@@ -603,14 +565,19 @@ nm_setting_bridge_port_class_init(NMSettingBridgePortClass *klass)
                                                     G_TYPE_PTR_ARRAY,
                                                     G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE
                                                         | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_VLANS],
-                                 &nm_sett_info_propert_type_bridge_vlans);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_VLANS],
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
+                                       .compare_fcn   = compare_fcn_vlans,
+                                       .to_dbus_fcn   = _nm_utils_bridge_vlans_to_dbus,
+                                       .from_dbus_fcn = _nm_utils_bridge_vlans_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_BRIDGE_PORT,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_BRIDGE_PORT,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-bridge.c b/src/libnm-core-impl/nm-setting-bridge.c
index e987310f..9045d619 100644
--- a/src/libnm-core-impl/nm-setting-bridge.c
+++ b/src/libnm-core-impl/nm-setting-bridge.c
@@ -71,18 +71,18 @@ typedef struct {
     guint32    multicast_hash_max;
     guint32    multicast_last_member_count;
     guint32    multicast_startup_query_count;
-    guint16    priority;
-    guint16    forward_delay;
-    guint16    hello_time;
-    guint16    max_age;
-    guint16    vlan_default_pvid;
-    guint16    group_forward_mask;
-    bool       multicast_snooping : 1;
-    bool       vlan_filtering : 1;
-    bool       stp : 1;
-    bool       vlan_stats_enabled : 1;
-    bool       multicast_query_use_ifaddr : 1;
-    bool       multicast_querier : 1;
+    guint32    priority;
+    guint32    forward_delay;
+    guint32    hello_time;
+    guint32    max_age;
+    guint32    group_forward_mask;
+    guint32    vlan_default_pvid;
+    bool       stp;
+    bool       multicast_snooping;
+    bool       vlan_filtering;
+    bool       vlan_stats_enabled;
+    bool       multicast_query_use_ifaddr;
+    bool       multicast_querier;
 } NMSettingBridgePrivate;
 
 /**
@@ -1231,7 +1231,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
                      error))
         return FALSE;
 
-    if (priv->group_forward_mask & 7) {
+    if (priv->group_forward_mask & 7u) {
         g_set_error_literal(error,
                             NM_CONNECTION_ERROR,
                             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -1315,35 +1315,13 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_vlans(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
-    NMSettingBridgePrivate *priv_a;
-    NMSettingBridgePrivate *priv_b;
-    guint                   i;
-
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_BRIDGE_VLANS)) {
-        if (set_b) {
-            priv_a = NM_SETTING_BRIDGE_GET_PRIVATE(set_a);
-            priv_b = NM_SETTING_BRIDGE_GET_PRIVATE(set_b);
-
-            if (priv_a->vlans->len != priv_b->vlans->len)
-                return FALSE;
-            for (i = 0; i < priv_a->vlans->len; i++) {
-                if (nm_bridge_vlan_cmp(priv_a->vlans->pdata[i], priv_b->vlans->pdata[i]))
-                    return FALSE;
-            }
-        }
-        return TRUE;
+    if (set_b) {
+        return _nm_utils_bridge_compare_vlans(NM_SETTING_BRIDGE_GET_PRIVATE(set_a)->vlans,
+                                              NM_SETTING_BRIDGE_GET_PRIVATE(set_b)->vlans);
     }
-
-    return NM_SETTING_CLASS(nm_setting_bridge_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    return TRUE;
 }
 
 /*****************************************************************************/
@@ -1351,88 +1329,9 @@ compare_property(const NMSettInfoSetting *sett_info,
 static void
 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);
+    NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE(object);
 
     switch (prop_id) {
-    case PROP_MAC_ADDRESS:
-        g_value_set_string(value, nm_setting_bridge_get_mac_address(setting));
-        break;
-    case PROP_STP:
-        g_value_set_boolean(value, priv->stp);
-        break;
-    case PROP_PRIORITY:
-        g_value_set_uint(value, priv->priority);
-        break;
-    case PROP_FORWARD_DELAY:
-        g_value_set_uint(value, priv->forward_delay);
-        break;
-    case PROP_HELLO_TIME:
-        g_value_set_uint(value, priv->hello_time);
-        break;
-    case PROP_MAX_AGE:
-        g_value_set_uint(value, priv->max_age);
-        break;
-    case PROP_AGEING_TIME:
-        g_value_set_uint(value, priv->ageing_time);
-        break;
-    case PROP_GROUP_ADDRESS:
-        g_value_set_string(value, priv->group_address);
-        break;
-    case PROP_GROUP_FORWARD_MASK:
-        g_value_set_uint(value, priv->group_forward_mask);
-        break;
-    case PROP_MULTICAST_HASH_MAX:
-        g_value_set_uint(value, priv->multicast_hash_max);
-        break;
-    case PROP_MULTICAST_LAST_MEMBER_COUNT:
-        g_value_set_uint(value, priv->multicast_last_member_count);
-        break;
-    case PROP_MULTICAST_LAST_MEMBER_INTERVAL:
-        g_value_set_uint64(value, priv->multicast_last_member_interval);
-        break;
-    case PROP_MULTICAST_MEMBERSHIP_INTERVAL:
-        g_value_set_uint64(value, priv->multicast_membership_interval);
-        break;
-    case PROP_MULTICAST_SNOOPING:
-        g_value_set_boolean(value, priv->multicast_snooping);
-        break;
-    case PROP_MULTICAST_ROUTER:
-        g_value_set_string(value, priv->multicast_router);
-        break;
-    case PROP_MULTICAST_QUERIER:
-        g_value_set_boolean(value, priv->multicast_querier);
-        break;
-    case PROP_MULTICAST_QUERIER_INTERVAL:
-        g_value_set_uint64(value, priv->multicast_querier_interval);
-        break;
-    case PROP_MULTICAST_QUERY_INTERVAL:
-        g_value_set_uint64(value, priv->multicast_query_interval);
-        break;
-    case PROP_MULTICAST_QUERY_RESPONSE_INTERVAL:
-        g_value_set_uint64(value, priv->multicast_query_response_interval);
-        break;
-    case PROP_MULTICAST_QUERY_USE_IFADDR:
-        g_value_set_boolean(value, priv->multicast_query_use_ifaddr);
-        break;
-    case PROP_MULTICAST_STARTUP_QUERY_COUNT:
-        g_value_set_uint(value, priv->multicast_startup_query_count);
-        break;
-    case PROP_MULTICAST_STARTUP_QUERY_INTERVAL:
-        g_value_set_uint64(value, priv->multicast_startup_query_interval);
-        break;
-    case PROP_VLAN_FILTERING:
-        g_value_set_boolean(value, priv->vlan_filtering);
-        break;
-    case PROP_VLAN_DEFAULT_PVID:
-        g_value_set_uint(value, priv->vlan_default_pvid);
-        break;
-    case PROP_VLAN_PROTOCOL:
-        g_value_set_string(value, priv->vlan_protocol);
-        break;
-    case PROP_VLAN_STATS_ENABLED:
-        g_value_set_boolean(value, priv->vlan_stats_enabled);
-        break;
     case PROP_VLANS:
         g_value_take_boxed(value,
                            _nm_utils_copy_array(priv->vlans,
@@ -1440,7 +1339,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
                                                 (GDestroyNotify) nm_bridge_vlan_unref));
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_get_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -1451,90 +1350,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
     NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE(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);
-        break;
-    case PROP_STP:
-        priv->stp = g_value_get_boolean(value);
-        break;
-    case PROP_PRIORITY:
-        priv->priority = (guint16) g_value_get_uint(value);
-        break;
-    case PROP_FORWARD_DELAY:
-        priv->forward_delay = (guint16) g_value_get_uint(value);
-        break;
-    case PROP_HELLO_TIME:
-        priv->hello_time = (guint16) g_value_get_uint(value);
-        break;
-    case PROP_MAX_AGE:
-        priv->max_age = (guint16) g_value_get_uint(value);
-        break;
-    case PROP_AGEING_TIME:
-        priv->ageing_time = g_value_get_uint(value);
-        break;
-    case PROP_GROUP_ADDRESS:
-        g_free(priv->group_address);
-        priv->group_address =
-            _nm_utils_hwaddr_canonical_or_invalid(g_value_get_string(value), ETH_ALEN);
-        break;
-    case PROP_GROUP_FORWARD_MASK:
-        priv->group_forward_mask = (guint16) g_value_get_uint(value);
-        break;
-    case PROP_MULTICAST_HASH_MAX:
-        priv->multicast_hash_max = g_value_get_uint(value);
-        break;
-    case PROP_MULTICAST_LAST_MEMBER_COUNT:
-        priv->multicast_last_member_count = g_value_get_uint(value);
-        break;
-    case PROP_MULTICAST_LAST_MEMBER_INTERVAL:
-        priv->multicast_last_member_interval = g_value_get_uint64(value);
-        break;
-    case PROP_MULTICAST_MEMBERSHIP_INTERVAL:
-        priv->multicast_membership_interval = g_value_get_uint64(value);
-        break;
-    case PROP_MULTICAST_SNOOPING:
-        priv->multicast_snooping = g_value_get_boolean(value);
-        break;
-    case PROP_MULTICAST_ROUTER:
-        g_free(priv->multicast_router);
-        priv->multicast_router = g_value_dup_string(value);
-        break;
-    case PROP_MULTICAST_QUERIER:
-        priv->multicast_querier = g_value_get_boolean(value);
-        break;
-    case PROP_MULTICAST_QUERIER_INTERVAL:
-        priv->multicast_querier_interval = g_value_get_uint64(value);
-        break;
-    case PROP_MULTICAST_QUERY_INTERVAL:
-        priv->multicast_query_interval = g_value_get_uint64(value);
-        break;
-    case PROP_MULTICAST_QUERY_RESPONSE_INTERVAL:
-        priv->multicast_query_response_interval = g_value_get_uint64(value);
-        break;
-    case PROP_MULTICAST_QUERY_USE_IFADDR:
-        priv->multicast_query_use_ifaddr = g_value_get_boolean(value);
-        break;
-    case PROP_MULTICAST_STARTUP_QUERY_COUNT:
-        priv->multicast_startup_query_count = g_value_get_uint(value);
-        break;
-    case PROP_MULTICAST_STARTUP_QUERY_INTERVAL:
-        priv->multicast_startup_query_interval = g_value_get_uint64(value);
-        break;
-    case PROP_VLAN_FILTERING:
-        priv->vlan_filtering = g_value_get_boolean(value);
-        break;
-    case PROP_VLAN_DEFAULT_PVID:
-        priv->vlan_default_pvid = g_value_get_uint(value);
-        break;
-    case PROP_VLAN_PROTOCOL:
-        g_free(priv->vlan_protocol);
-        priv->vlan_protocol = g_value_dup_string(value);
-        break;
-    case PROP_VLAN_STATS_ENABLED:
-        priv->vlan_stats_enabled = g_value_get_boolean(value);
-        break;
     case PROP_VLANS:
         g_ptr_array_unref(priv->vlans);
         priv->vlans = _nm_utils_copy_array(g_value_get_boxed(value),
@@ -1542,7 +1357,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
                                            (GDestroyNotify) nm_bridge_vlan_unref);
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_set_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -1555,28 +1370,6 @@ nm_setting_bridge_init(NMSettingBridge *setting)
     NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE(setting);
 
     priv->vlans = g_ptr_array_new_with_free_func((GDestroyNotify) nm_bridge_vlan_unref);
-
-    priv->ageing_time                       = NM_BRIDGE_AGEING_TIME_DEF;
-    priv->forward_delay                     = NM_BRIDGE_FORWARD_DELAY_DEF;
-    priv->hello_time                        = NM_BRIDGE_HELLO_TIME_DEF;
-    priv->max_age                           = NM_BRIDGE_MAX_AGE_DEF;
-    priv->multicast_last_member_count       = NM_BRIDGE_MULTICAST_LAST_MEMBER_COUNT_DEF;
-    priv->multicast_last_member_interval    = NM_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL_DEF;
-    priv->multicast_membership_interval     = NM_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL_DEF;
-    priv->multicast_hash_max                = NM_BRIDGE_MULTICAST_HASH_MAX_DEF;
-    priv->multicast_snooping                = NM_BRIDGE_MULTICAST_SNOOPING_DEF;
-    priv->priority                          = NM_BRIDGE_PRIORITY_DEF;
-    priv->stp                               = NM_BRIDGE_STP_DEF;
-    priv->vlan_default_pvid                 = NM_BRIDGE_VLAN_DEFAULT_PVID_DEF;
-    priv->multicast_query_interval          = NM_BRIDGE_MULTICAST_QUERY_INTERVAL_DEF;
-    priv->multicast_query_response_interval = NM_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL_DEF;
-    priv->multicast_querier_interval        = NM_BRIDGE_MULTICAST_QUERIER_INTERVAL_DEF;
-    priv->multicast_startup_query_count     = NM_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT_DEF;
-    priv->multicast_startup_query_interval  = NM_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL_DEF;
-
-    nm_assert(priv->multicast_querier == NM_BRIDGE_MULTICAST_QUERIER_DEF);
-    nm_assert(priv->multicast_query_use_ifaddr == NM_BRIDGE_MULTICAST_QUERY_USE_IFADDR_DEF);
-    nm_assert(priv->vlan_stats_enabled == NM_BRIDGE_VLAN_STATS_ENABLED_DEF);
 }
 
 /**
@@ -1597,10 +1390,6 @@ finalize(GObject *object)
 {
     NMSettingBridgePrivate *priv = NM_SETTING_BRIDGE_GET_PRIVATE(object);
 
-    g_free(priv->mac_address);
-    g_free(priv->multicast_router);
-    g_free(priv->group_address);
-    g_free(priv->vlan_protocol);
     g_ptr_array_unref(priv->vlans);
 
     G_OBJECT_CLASS(nm_setting_bridge_parent_class)->finalize(object);
@@ -1617,8 +1406,7 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
     object_class->set_property = set_property;
     object_class->finalize     = finalize;
 
-    setting_class->compare_property = compare_property;
-    setting_class->verify           = verify;
+    setting_class->verify = verify;
 
     /**
      * NMSettingBridge:mac-address:
@@ -1650,15 +1438,14 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *   BRIDGE_MACADDR for bridges is an NM extension.
      * ---end---
      */
-    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);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_MAC_ADDRESS],
-                                 &nm_sett_info_propert_type_mac_address);
+    _nm_setting_property_define_direct_mac_address(properties_override,
+                                                   obj_properties,
+                                                   NM_SETTING_BRIDGE_MAC_ADDRESS,
+                                                   PROP_MAC_ADDRESS,
+                                                   NM_SETTING_PARAM_INFERRABLE,
+                                                   NMSettingBridgePrivate,
+                                                   mac_address,
+                                                   .direct_set_string_mac_address_len = ETH_ALEN);
 
     /**
      * NMSettingBridge:stp:
@@ -1672,13 +1459,14 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * description: Span tree protocol participation.
      * ---end---
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_BRIDGE_STP,
-                                        PROP_STP,
-                                        NM_BRIDGE_STP_DEF,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_bridge_get_stp);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_BRIDGE_STP,
+                                               PROP_STP,
+                                               NM_BRIDGE_STP_DEF,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingBridgePrivate,
+                                               stp);
 
     /**
      * NMSettingBridge:priority:
@@ -1695,14 +1483,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * description: STP priority.
      * ---end---
      */
-    obj_properties[PROP_PRIORITY] =
-        g_param_spec_uint(NM_SETTING_BRIDGE_PRIORITY,
-                          "",
-                          "",
-                          NM_BRIDGE_PRIORITY_MIN,
-                          NM_BRIDGE_PRIORITY_MAX,
-                          NM_BRIDGE_PRIORITY_DEF,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_PRIORITY,
+                                              PROP_PRIORITY,
+                                              NM_BRIDGE_PRIORITY_MIN,
+                                              NM_BRIDGE_PRIORITY_MAX,
+                                              NM_BRIDGE_PRIORITY_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              priority);
 
     /**
      * NMSettingBridge:forward-delay:
@@ -1717,14 +1507,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * description: STP forwarding delay.
      * ---end---
      */
-    obj_properties[PROP_FORWARD_DELAY] =
-        g_param_spec_uint(NM_SETTING_BRIDGE_FORWARD_DELAY,
-                          "",
-                          "",
-                          0,
-                          NM_BRIDGE_FORWARD_DELAY_MAX,
-                          NM_BRIDGE_FORWARD_DELAY_DEF,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_FORWARD_DELAY,
+                                              PROP_FORWARD_DELAY,
+                                              0,
+                                              NM_BRIDGE_FORWARD_DELAY_MAX,
+                                              NM_BRIDGE_FORWARD_DELAY_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              forward_delay);
 
     /**
      * NMSettingBridge:hello-time:
@@ -1739,14 +1531,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * description: STP hello time.
      * ---end---
      */
-    obj_properties[PROP_HELLO_TIME] =
-        g_param_spec_uint(NM_SETTING_BRIDGE_HELLO_TIME,
-                          "",
-                          "",
-                          0,
-                          NM_BRIDGE_HELLO_TIME_MAX,
-                          NM_BRIDGE_HELLO_TIME_DEF,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_HELLO_TIME,
+                                              PROP_HELLO_TIME,
+                                              0,
+                                              NM_BRIDGE_HELLO_TIME_MAX,
+                                              NM_BRIDGE_HELLO_TIME_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              hello_time);
 
     /**
      * NMSettingBridge:max-age:
@@ -1761,14 +1555,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * description: STP maximum message age.
      * ---end---
      */
-    obj_properties[PROP_MAX_AGE] =
-        g_param_spec_uint(NM_SETTING_BRIDGE_MAX_AGE,
-                          "",
-                          "",
-                          0,
-                          NM_BRIDGE_MAX_AGE_MAX,
-                          NM_BRIDGE_MAX_AGE_DEF,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_MAX_AGE,
+                                              PROP_MAX_AGE,
+                                              0,
+                                              NM_BRIDGE_MAX_AGE_MAX,
+                                              NM_BRIDGE_MAX_AGE_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              max_age);
 
     /**
      * NMSettingBridge:ageing-time:
@@ -1783,14 +1579,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * description: Ethernet MAC ageing time.
      * ---end---
      */
-    obj_properties[PROP_AGEING_TIME] =
-        g_param_spec_uint(NM_SETTING_BRIDGE_AGEING_TIME,
-                          "",
-                          "",
-                          NM_BRIDGE_AGEING_TIME_MIN,
-                          NM_BRIDGE_AGEING_TIME_MAX,
-                          NM_BRIDGE_AGEING_TIME_DEF,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_AGEING_TIME,
+                                              PROP_AGEING_TIME,
+                                              NM_BRIDGE_AGEING_TIME_MIN,
+                                              NM_BRIDGE_AGEING_TIME_MAX,
+                                              NM_BRIDGE_AGEING_TIME_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              ageing_time);
 
     /**
      * NMSettingBridge:group-forward-mask:
@@ -1804,14 +1602,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.10
      **/
-    obj_properties[PROP_GROUP_FORWARD_MASK] =
-        g_param_spec_uint(NM_SETTING_BRIDGE_GROUP_FORWARD_MASK,
-                          "",
-                          "",
-                          0,
-                          0xFFFF,
-                          0,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_GROUP_FORWARD_MASK,
+                                              PROP_GROUP_FORWARD_MASK,
+                                              0,
+                                              0xFFFF,
+                                              0,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              group_forward_mask);
 
     /**
      * NMSettingBridge:multicast-snooping:
@@ -1831,13 +1631,14 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * description: IGMP snooping support.
      * ---end---
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_BRIDGE_MULTICAST_SNOOPING,
-                                        PROP_MULTICAST_SNOOPING,
-                                        NM_BRIDGE_MULTICAST_SNOOPING_DEF,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_bridge_get_multicast_snooping);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_BRIDGE_MULTICAST_SNOOPING,
+                                               PROP_MULTICAST_SNOOPING,
+                                               NM_BRIDGE_MULTICAST_SNOOPING_DEF,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingBridgePrivate,
+                                               multicast_snooping);
 
     /**
      * NMSettingBridge:vlan-filtering:
@@ -1854,13 +1655,14 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * description: VLAN filtering support.
      * ---end---
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_BRIDGE_VLAN_FILTERING,
-                                        PROP_VLAN_FILTERING,
-                                        FALSE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_bridge_get_vlan_filtering);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_BRIDGE_VLAN_FILTERING,
+                                               PROP_VLAN_FILTERING,
+                                               FALSE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingBridgePrivate,
+                                               vlan_filtering);
 
     /**
      * NMSettingBridge:vlan-default-pvid:
@@ -1878,14 +1680,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * description: default VLAN PVID.
      * ---end---
      */
-    obj_properties[PROP_VLAN_DEFAULT_PVID] =
-        g_param_spec_uint(NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID,
-                          "",
-                          "",
-                          0,
-                          NM_BRIDGE_VLAN_VID_MAX,
-                          NM_BRIDGE_VLAN_DEFAULT_PVID_DEF,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_VLAN_DEFAULT_PVID,
+                                              PROP_VLAN_DEFAULT_PVID,
+                                              0,
+                                              NM_BRIDGE_VLAN_VID_MAX,
+                                              NM_BRIDGE_VLAN_DEFAULT_PVID_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              vlan_default_pvid);
 
     /**
      * NMSettingBridge:vlans: (type GPtrArray(NMBridgeVlan))
@@ -1917,9 +1721,13 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
                                                     G_TYPE_PTR_ARRAY,
                                                     G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE
                                                         | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_VLANS],
-                                 &nm_sett_info_propert_type_bridge_vlans);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_VLANS],
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
+                                       .to_dbus_fcn   = _nm_utils_bridge_vlans_to_dbus,
+                                       .compare_fcn   = compare_fcn_vlans,
+                                       .from_dbus_fcn = _nm_utils_bridge_vlans_from_dbus, ));
 
     /* ---dbus---
      * property: interface-name
@@ -1951,15 +1759,14 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      * example: BRIDGING_OPTS="group_address=01:80:C2:00:00:0A"
      * ---end---
      */
-    obj_properties[PROP_GROUP_ADDRESS] = g_param_spec_string(
-        NM_SETTING_BRIDGE_GROUP_ADDRESS,
-        "",
-        "",
-        NULL,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_GROUP_ADDRESS],
-                                 &nm_sett_info_propert_type_mac_address);
+    _nm_setting_property_define_direct_mac_address(properties_override,
+                                                   obj_properties,
+                                                   NM_SETTING_BRIDGE_GROUP_ADDRESS,
+                                                   PROP_GROUP_ADDRESS,
+                                                   NM_SETTING_PARAM_INFERRABLE,
+                                                   NMSettingBridgePrivate,
+                                                   group_address,
+                                                   .direct_set_string_mac_address_len = ETH_ALEN);
 
     /**
      * NMSettingBridge:vlan-protocol:
@@ -1980,12 +1787,13 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.24
      */
-    obj_properties[PROP_VLAN_PROTOCOL] = g_param_spec_string(
-        NM_SETTING_BRIDGE_VLAN_PROTOCOL,
-        "",
-        "",
-        NULL,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_VLAN_PROTOCOL,
+                                              PROP_VLAN_PROTOCOL,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              vlan_protocol);
 
     /**
      * NMSettingBridge:vlan-stats-enabled:
@@ -2001,13 +1809,14 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.24
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_BRIDGE_VLAN_STATS_ENABLED,
-                                        PROP_VLAN_STATS_ENABLED,
-                                        NM_BRIDGE_VLAN_STATS_ENABLED_DEF,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_bridge_get_vlan_stats_enabled);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_BRIDGE_VLAN_STATS_ENABLED,
+                                               PROP_VLAN_STATS_ENABLED,
+                                               NM_BRIDGE_VLAN_STATS_ENABLED_DEF,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingBridgePrivate,
+                                               vlan_stats_enabled);
 
     /**
      * NMSettingBridge:multicast-router:
@@ -2029,12 +1838,13 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.24
      */
-    obj_properties[PROP_MULTICAST_ROUTER] = g_param_spec_string(
-        NM_SETTING_BRIDGE_MULTICAST_ROUTER,
-        "",
-        "",
-        NULL,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_MULTICAST_ROUTER,
+                                              PROP_MULTICAST_ROUTER,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              multicast_router);
 
     /**
      * NMSettingBridge:multicast-query-use-ifaddr:
@@ -2052,13 +1862,14 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.24
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_BRIDGE_MULTICAST_QUERY_USE_IFADDR,
-                                        PROP_MULTICAST_QUERY_USE_IFADDR,
-                                        NM_BRIDGE_MULTICAST_QUERY_USE_IFADDR_DEF,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_bridge_get_multicast_query_use_ifaddr);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_BRIDGE_MULTICAST_QUERY_USE_IFADDR,
+                                               PROP_MULTICAST_QUERY_USE_IFADDR,
+                                               NM_BRIDGE_MULTICAST_QUERY_USE_IFADDR_DEF,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingBridgePrivate,
+                                               multicast_query_use_ifaddr);
 
     /**
      * NMSettingBridge:multicast-querier:
@@ -2075,13 +1886,14 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.24
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_BRIDGE_MULTICAST_QUERIER,
-                                        PROP_MULTICAST_QUERIER,
-                                        NM_BRIDGE_MULTICAST_QUERIER_DEF,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_bridge_get_multicast_querier);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_BRIDGE_MULTICAST_QUERIER,
+                                               PROP_MULTICAST_QUERIER,
+                                               NM_BRIDGE_MULTICAST_QUERIER_DEF,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingBridgePrivate,
+                                               multicast_querier);
 
     /**
      * NMSettingBridge:multicast-hash-max:
@@ -2097,14 +1909,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.26
      */
-    obj_properties[PROP_MULTICAST_HASH_MAX] =
-        g_param_spec_uint(NM_SETTING_BRIDGE_MULTICAST_HASH_MAX,
-                          "",
-                          "",
-                          NM_BRIDGE_MULTICAST_HASH_MAX_MIN,
-                          NM_BRIDGE_MULTICAST_HASH_MAX_MAX,
-                          NM_BRIDGE_MULTICAST_HASH_MAX_DEF,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_MULTICAST_HASH_MAX,
+                                              PROP_MULTICAST_HASH_MAX,
+                                              NM_BRIDGE_MULTICAST_HASH_MAX_MIN,
+                                              NM_BRIDGE_MULTICAST_HASH_MAX_MAX,
+                                              NM_BRIDGE_MULTICAST_HASH_MAX_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              multicast_hash_max);
 
     /**
      * NMSettingBridge:multicast-last-member-count:
@@ -2122,14 +1936,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.26
      */
-    obj_properties[PROP_MULTICAST_LAST_MEMBER_COUNT] =
-        g_param_spec_uint(NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_COUNT,
-                          "",
-                          "",
-                          NM_BRIDGE_MULTICAST_LAST_MEMBER_COUNT_MIN,
-                          NM_BRIDGE_MULTICAST_LAST_MEMBER_COUNT_MAX,
-                          NM_BRIDGE_MULTICAST_LAST_MEMBER_COUNT_DEF,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_COUNT,
+                                              PROP_MULTICAST_LAST_MEMBER_COUNT,
+                                              NM_BRIDGE_MULTICAST_LAST_MEMBER_COUNT_MIN,
+                                              NM_BRIDGE_MULTICAST_LAST_MEMBER_COUNT_MAX,
+                                              NM_BRIDGE_MULTICAST_LAST_MEMBER_COUNT_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              multicast_last_member_count);
 
     /**
      * NMSettingBridge:multicast-last-member-interval:
@@ -2146,14 +1962,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.26
      */
-    obj_properties[PROP_MULTICAST_LAST_MEMBER_INTERVAL] = g_param_spec_uint64(
-        NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL,
-        "",
-        "",
-        NM_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL_MIN,
-        NM_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL_MAX,
-        NM_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL_DEF,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint64(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL,
+                                              PROP_MULTICAST_LAST_MEMBER_INTERVAL,
+                                              NM_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL_MIN,
+                                              NM_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL_MAX,
+                                              NM_BRIDGE_MULTICAST_LAST_MEMBER_INTERVAL_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              multicast_last_member_interval);
 
     /**
      * NMSettingBridge:multicast-membership-interval:
@@ -2171,14 +1989,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.26
      */
-    obj_properties[PROP_MULTICAST_MEMBERSHIP_INTERVAL] = g_param_spec_uint64(
-        NM_SETTING_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL,
-        "",
-        "",
-        NM_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL_MIN,
-        NM_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL_MAX,
-        NM_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL_DEF,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint64(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL,
+                                              PROP_MULTICAST_MEMBERSHIP_INTERVAL,
+                                              NM_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL_MIN,
+                                              NM_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL_MAX,
+                                              NM_BRIDGE_MULTICAST_MEMBERSHIP_INTERVAL_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              multicast_membership_interval);
 
     /**
      * NMSettingBridge:multicast-querier-interval:
@@ -2195,14 +2015,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.26
      */
-    obj_properties[PROP_MULTICAST_QUERIER_INTERVAL] = g_param_spec_uint64(
-        NM_SETTING_BRIDGE_MULTICAST_QUERIER_INTERVAL,
-        "",
-        "",
-        NM_BRIDGE_MULTICAST_QUERIER_INTERVAL_MIN,
-        NM_BRIDGE_MULTICAST_QUERIER_INTERVAL_MAX,
-        NM_BRIDGE_MULTICAST_QUERIER_INTERVAL_DEF,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint64(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_MULTICAST_QUERIER_INTERVAL,
+                                              PROP_MULTICAST_QUERIER_INTERVAL,
+                                              NM_BRIDGE_MULTICAST_QUERIER_INTERVAL_MIN,
+                                              NM_BRIDGE_MULTICAST_QUERIER_INTERVAL_MAX,
+                                              NM_BRIDGE_MULTICAST_QUERIER_INTERVAL_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              multicast_querier_interval);
 
     /**
      * NMSettingBridge:multicast-query-interval:
@@ -2219,14 +2041,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.26
      */
-    obj_properties[PROP_MULTICAST_QUERY_INTERVAL] = g_param_spec_uint64(
-        NM_SETTING_BRIDGE_MULTICAST_QUERY_INTERVAL,
-        "",
-        "",
-        NM_BRIDGE_MULTICAST_QUERY_INTERVAL_MIN,
-        NM_BRIDGE_MULTICAST_QUERY_INTERVAL_MAX,
-        NM_BRIDGE_MULTICAST_QUERY_INTERVAL_DEF,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint64(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_MULTICAST_QUERY_INTERVAL,
+                                              PROP_MULTICAST_QUERY_INTERVAL,
+                                              NM_BRIDGE_MULTICAST_QUERY_INTERVAL_MIN,
+                                              NM_BRIDGE_MULTICAST_QUERY_INTERVAL_MAX,
+                                              NM_BRIDGE_MULTICAST_QUERY_INTERVAL_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              multicast_query_interval);
 
     /**
      * NMSettingBridge:multicast-query-response-interval:
@@ -2243,14 +2067,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.26
      */
-    obj_properties[PROP_MULTICAST_QUERY_RESPONSE_INTERVAL] = g_param_spec_uint64(
-        NM_SETTING_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL,
-        "",
-        "",
-        NM_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL_MIN,
-        NM_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL_MAX,
-        NM_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL_DEF,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint64(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL,
+                                              PROP_MULTICAST_QUERY_RESPONSE_INTERVAL,
+                                              NM_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL_MIN,
+                                              NM_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL_MAX,
+                                              NM_BRIDGE_MULTICAST_QUERY_RESPONSE_INTERVAL_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              multicast_query_response_interval);
 
     /**
      * NMSettingBridge:multicast-startup-query-count:
@@ -2266,14 +2092,16 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.26
      */
-    obj_properties[PROP_MULTICAST_STARTUP_QUERY_COUNT] =
-        g_param_spec_uint(NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT,
-                          "",
-                          "",
-                          NM_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT_MIN,
-                          NM_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT_MAX,
-                          NM_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT_DEF,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT,
+                                              PROP_MULTICAST_STARTUP_QUERY_COUNT,
+                                              NM_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT_MIN,
+                                              NM_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT_MAX,
+                                              NM_BRIDGE_MULTICAST_STARTUP_QUERY_COUNT_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              multicast_startup_query_count);
 
     /**
      * NMSettingBridge:multicast-startup-query-interval:
@@ -2290,19 +2118,22 @@ nm_setting_bridge_class_init(NMSettingBridgeClass *klass)
      *
      * Since: 1.26
      */
-    obj_properties[PROP_MULTICAST_STARTUP_QUERY_INTERVAL] = g_param_spec_uint64(
-        NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL,
-        "",
-        "",
-        NM_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL_MIN,
-        NM_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL_MAX,
-        NM_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL_DEF,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint64(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL,
+                                              PROP_MULTICAST_STARTUP_QUERY_INTERVAL,
+                                              NM_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL_MIN,
+                                              NM_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL_MAX,
+                                              NM_BRIDGE_MULTICAST_STARTUP_QUERY_INTERVAL_DEF,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingBridgePrivate,
+                                              multicast_startup_query_interval);
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_BRIDGE,
+                             NULL,
+                             properties_override,
+                             G_STRUCT_OFFSET(NMSettingBridge, _priv));
 }
diff --git a/src/libnm-core-impl/nm-setting-cdma.c b/src/libnm-core-impl/nm-setting-cdma.c
index 6dc61e83..214f3b91 100644
--- a/src/libnm-core-impl/nm-setting-cdma.c
+++ b/src/libnm-core-impl/nm-setting-cdma.c
@@ -29,7 +29,7 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_NUMBER,
                                   PROP_MTU, );
 
 typedef struct {
-    char *               number; /* For dialing, duh */
+    char *               number;
     char *               username;
     char *               password;
     guint32              mtu;
@@ -136,23 +136,23 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 {
     NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE(setting);
 
-    if (!priv->number) {
-        g_set_error_literal(error,
-                            NM_CONNECTION_ERROR,
-                            NM_CONNECTION_ERROR_MISSING_PROPERTY,
-                            _("property is missing"));
-        g_prefix_error(error, "%s.%s: ", NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_NUMBER);
-        return FALSE;
-    } else if (!strlen(priv->number)) {
-        g_set_error_literal(error,
-                            NM_CONNECTION_ERROR,
-                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
-                            _("property is empty"));
+    if (nm_str_is_empty(priv->number)) {
+        if (!priv->number) {
+            g_set_error_literal(error,
+                                NM_CONNECTION_ERROR,
+                                NM_CONNECTION_ERROR_MISSING_PROPERTY,
+                                _("property is missing"));
+        } else {
+            g_set_error_literal(error,
+                                NM_CONNECTION_ERROR,
+                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                                _("property is empty"));
+        }
         g_prefix_error(error, "%s.%s: ", NM_SETTING_CDMA_SETTING_NAME, NM_SETTING_CDMA_NUMBER);
         return FALSE;
     }
 
-    if (priv->username && !strlen(priv->username)) {
+    if (priv->username && nm_str_is_empty(priv->username)) {
         g_set_error_literal(error,
                             NM_CONNECTION_ERROR,
                             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -179,7 +179,7 @@ need_secrets(NMSetting *setting)
     NMSettingCdmaPrivate *priv    = NM_SETTING_CDMA_GET_PRIVATE(setting);
     GPtrArray *           secrets = NULL;
 
-    if (priv->password && *priv->password)
+    if (!nm_str_is_empty(priv->password))
         return NULL;
 
     if (priv->username) {
@@ -195,65 +195,6 @@ need_secrets(NMSetting *setting)
 /*****************************************************************************/
 
 static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    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;
-    }
-}
-
-static void
-set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
-{
-    NMSettingCdmaPrivate *priv = NM_SETTING_CDMA_GET_PRIVATE(object);
-
-    switch (prop_id) {
-    case PROP_NUMBER:
-        g_free(priv->number);
-        priv->number = g_value_dup_string(value);
-        break;
-    case PROP_USERNAME:
-        g_free(priv->username);
-        priv->username = g_value_dup_string(value);
-        break;
-    case PROP_PASSWORD:
-        g_free(priv->password);
-        priv->password = g_value_dup_string(value);
-        break;
-    case PROP_PASSWORD_FLAGS:
-        priv->password_flags = g_value_get_flags(value);
-        break;
-    case PROP_MTU:
-        priv->mtu = g_value_get_uint(value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static void
 nm_setting_cdma_init(NMSettingCdma *setting)
 {}
 
@@ -271,28 +212,16 @@ nm_setting_cdma_new(void)
 }
 
 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
 nm_setting_cdma_class_init(NMSettingCdmaClass *klass)
 {
-    GObjectClass *  object_class  = G_OBJECT_CLASS(klass);
-    NMSettingClass *setting_class = NM_SETTING_CLASS(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();
 
     g_type_class_add_private(klass, sizeof(NMSettingCdmaPrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-    object_class->finalize     = finalize;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify         = verify;
     setting_class->verify_secrets = verify_secrets;
@@ -305,11 +234,13 @@ nm_setting_cdma_class_init(NMSettingCdmaClass *klass)
      * broadband network, if any.  If not specified, the default number (#777)
      * is used when required.
      **/
-    obj_properties[PROP_NUMBER] = g_param_spec_string(NM_SETTING_CDMA_NUMBER,
-                                                      "",
-                                                      "",
-                                                      NULL,
-                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CDMA_NUMBER,
+                                              PROP_NUMBER,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingCdmaPrivate,
+                                              number);
 
     /**
      * NMSettingCdma:username:
@@ -318,11 +249,13 @@ 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.
      **/
-    obj_properties[PROP_USERNAME] = g_param_spec_string(NM_SETTING_CDMA_USERNAME,
-                                                        "",
-                                                        "",
-                                                        NULL,
-                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CDMA_USERNAME,
+                                              PROP_USERNAME,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingCdmaPrivate,
+                                              username);
 
     /**
      * NMSettingCdma:password:
@@ -331,25 +264,25 @@ 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.
      **/
-    obj_properties[PROP_PASSWORD] =
-        g_param_spec_string(NM_SETTING_CDMA_PASSWORD,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CDMA_PASSWORD,
+                                              PROP_PASSWORD,
+                                              NM_SETTING_PARAM_SECRET,
+                                              NMSettingCdmaPrivate,
+                                              password);
 
     /**
      * NMSettingCdma:password-flags:
      *
      * Flags indicating how to handle the #NMSettingCdma:password property.
      **/
-    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);
+    _nm_setting_property_define_direct_secret_flags(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_CDMA_PASSWORD_FLAGS,
+                                                    PROP_PASSWORD_FLAGS,
+                                                    NMSettingCdmaPrivate,
+                                                    password_flags);
 
     /**
      * NMSettingCdma:mtu:
@@ -359,16 +292,22 @@ nm_setting_cdma_class_init(NMSettingCdmaClass *klass)
      *
      * Since: 1.8
      **/
-    obj_properties[PROP_MTU] = g_param_spec_uint(NM_SETTING_CDMA_MTU,
-                                                 "",
-                                                 "",
-                                                 0,
-                                                 G_MAXUINT32,
-                                                 0,
-                                                 G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE
-                                                     | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CDMA_MTU,
+                                              PROP_MTU,
+                                              0,
+                                              G_MAXUINT32,
+                                              0,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE,
+                                              NMSettingCdmaPrivate,
+                                              mtu);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_CDMA);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_CDMA,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-connection.c b/src/libnm-core-impl/nm-setting-connection.c
index 3d57bc3e..322250f8 100644
--- a/src/libnm-core-impl/nm-setting-connection.c
+++ b/src/libnm-core-impl/nm-setting-connection.c
@@ -64,37 +64,39 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingConnection,
                              PROP_LLDP,
                              PROP_MDNS,
                              PROP_LLMNR,
+                             PROP_DNS_OVER_TLS,
                              PROP_STABLE_ID,
                              PROP_AUTH_RETRIES,
                              PROP_WAIT_DEVICE_TIMEOUT,
                              PROP_MUD_URL, );
 
 typedef struct {
-    GArray *                             permissions;
-    GArray *                             secondaries;
-    char *                               id;
-    char *                               uuid;
-    char *                               stable_id;
-    char *                               interface_name;
-    char *                               type;
-    char *                               master;
-    char *                               slave_type;
-    char *                               zone;
-    char *                               mud_url;
-    guint64                              timestamp;
-    int                                  autoconnect_priority;
-    int                                  autoconnect_retries;
-    int                                  multi_connect;
-    int                                  auth_retries;
-    int                                  mdns;
-    int                                  llmnr;
-    int                                  wait_device_timeout;
-    guint                                gateway_ping_timeout;
-    NMSettingConnectionAutoconnectSlaves autoconnect_slaves;
-    NMMetered                            metered;
-    NMSettingConnectionLldp              lldp;
-    bool                                 read_only : 1;
-    bool                                 autoconnect : 1;
+    GArray *permissions;
+    GArray *secondaries;
+    char *  id;
+    char *  uuid;
+    char *  stable_id;
+    char *  interface_name;
+    char *  type;
+    char *  master;
+    char *  slave_type;
+    char *  zone;
+    char *  mud_url;
+    guint64 timestamp;
+    int     autoconnect_slaves;
+    int     metered;
+    gint32  autoconnect_priority;
+    gint32  autoconnect_retries;
+    gint32  multi_connect;
+    gint32  auth_retries;
+    gint32  mdns;
+    gint32  llmnr;
+    gint32  dns_over_tls;
+    gint32  wait_device_timeout;
+    gint32  lldp;
+    guint32 gateway_ping_timeout;
+    bool    autoconnect;
+    bool    read_only;
 } NMSettingConnectionPrivate;
 
 /**
@@ -621,12 +623,7 @@ nm_setting_connection_get_timestamp(NMSettingConnection *setting)
 }
 
 static GVariant *
-_to_dbus_fcn_timestamp(const NMSettInfoSetting *               sett_info,
-                       guint                                   property_idx,
-                       NMConnection *                          connection,
-                       NMSetting *                             setting,
-                       NMConnectionSerializationFlags          flags,
-                       const NMConnectionSerializationOptions *options)
+_to_dbus_fcn_timestamp(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     guint64 v;
 
@@ -982,6 +979,23 @@ nm_setting_connection_get_llmnr(NMSettingConnection *setting)
     return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->llmnr;
 }
 
+/**
+ * nm_setting_connection_get_dns_over_tls:
+ * @setting: the #NMSettingConnection
+ *
+ * Returns: the #NMSettingConnection:dns-over-tls property of the setting.
+ *
+ * Since: 1.34
+ **/
+NMSettingConnectionDnsOverTls
+nm_setting_connection_get_dns_over_tls(NMSettingConnection *setting)
+{
+    g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting),
+                         NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT);
+
+    return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->dns_over_tls;
+}
+
 static void
 _set_error_missing_base_setting(GError **error, const char *type)
 {
@@ -1279,8 +1293,7 @@ after_interface_name:
         return FALSE;
     }
 
-    if (priv->metered != NM_METERED_UNKNOWN && priv->metered != NM_METERED_YES
-        && priv->metered != NM_METERED_NO) {
+    if (!NM_IN_SET(priv->metered, NM_METERED_UNKNOWN, NM_METERED_NO, NM_METERED_YES)) {
         g_set_error(error,
                     NM_CONNECTION_ERROR,
                     NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -1321,6 +1334,20 @@ after_interface_name:
         return FALSE;
     }
 
+    if (priv->dns_over_tls < (int) NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT
+        || priv->dns_over_tls > (int) NM_SETTING_CONNECTION_DNS_OVER_TLS_YES) {
+        g_set_error(error,
+                    NM_CONNECTION_ERROR,
+                    NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                    _("value %d is not valid"),
+                    priv->dns_over_tls);
+        g_prefix_error(error,
+                       "%s.%s: ",
+                       NM_SETTING_CONNECTION_SETTING_NAME,
+                       NM_SETTING_CONNECTION_DNS_OVER_TLS);
+        return FALSE;
+    }
+
     if (!NM_IN_SET(priv->multi_connect,
                    (int) NM_CONNECTION_MULTI_CONNECT_DEFAULT,
                    (int) NM_CONNECTION_MULTI_CONNECT_SINGLE,
@@ -1540,11 +1567,7 @@ find_virtual_interface_name(GVariant *connection_dict, GVariant **variant_to_fre
 }
 
 static gboolean
-nm_setting_connection_no_interface_name(NMSetting *         setting,
-                                        GVariant *          connection_dict,
-                                        const char *        property,
-                                        NMSettingParseFlags parse_flags,
-                                        GError **           error)
+nm_setting_connection_no_interface_name(_NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     const char *     virtual_interface_name;
     gs_unref_variant GVariant *variant_to_free = NULL;
@@ -1558,24 +1581,33 @@ nm_setting_connection_no_interface_name(NMSetting *         setting,
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_id(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
-    if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_IGNORE_ID)
-        && nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_CONNECTION_ID))
+    if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_IGNORE_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_setting_property_compare_fcn_direct(sett_info,
+                                                   property_info,
+                                                   con_a,
+                                                   set_a,
+                                                   con_b,
+                                                   set_b,
+                                                   flags);
+}
+
+static NMTernary
+compare_fcn_timestamp(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_IGNORE_TIMESTAMP))
         return NM_TERNARY_DEFAULT;
 
-    return NM_SETTING_CLASS(nm_setting_connection_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    return _nm_setting_property_compare_fcn_default(sett_info,
+                                                    property_info,
+                                                    con_a,
+                                                    set_a,
+                                                    con_b,
+                                                    set_b,
+                                                    flags);
 }
 
 /*****************************************************************************/
@@ -1587,21 +1619,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     NMSettingConnectionPrivate *priv    = NM_SETTING_CONNECTION_GET_PRIVATE(setting);
 
     switch (prop_id) {
-    case PROP_ID:
-        g_value_set_string(value, nm_setting_connection_get_id(setting));
-        break;
-    case PROP_UUID:
-        g_value_set_string(value, nm_setting_connection_get_uuid(setting));
-        break;
-    case PROP_STABLE_ID:
-        g_value_set_string(value, nm_setting_connection_get_stable_id(setting));
-        break;
-    case PROP_INTERFACE_NAME:
-        g_value_set_string(value, nm_setting_connection_get_interface_name(setting));
-        break;
-    case PROP_TYPE:
-        g_value_set_string(value, nm_setting_connection_get_connection_type(setting));
-        break;
     case PROP_PERMISSIONS:
     {
         char **strv;
@@ -1617,65 +1634,14 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_take_boxed(value, strv);
         break;
     }
-    case PROP_AUTOCONNECT:
-        g_value_set_boolean(value, nm_setting_connection_get_autoconnect(setting));
-        break;
-    case PROP_AUTOCONNECT_PRIORITY:
-        g_value_set_int(value, nm_setting_connection_get_autoconnect_priority(setting));
-        break;
-    case PROP_AUTOCONNECT_RETRIES:
-        g_value_set_int(value, nm_setting_connection_get_autoconnect_retries(setting));
-        break;
-    case PROP_MULTI_CONNECT:
-        g_value_set_int(value, priv->multi_connect);
-        break;
     case PROP_TIMESTAMP:
         g_value_set_uint64(value, nm_setting_connection_get_timestamp(setting));
         break;
-    case PROP_READ_ONLY:
-        g_value_set_boolean(value, nm_setting_connection_get_read_only(setting));
-        break;
-    case PROP_ZONE:
-        g_value_set_string(value, nm_setting_connection_get_zone(setting));
-        break;
-    case PROP_MASTER:
-        g_value_set_string(value, nm_setting_connection_get_master(setting));
-        break;
-    case PROP_SLAVE_TYPE:
-        g_value_set_string(value, nm_setting_connection_get_slave_type(setting));
-        break;
-    case PROP_AUTOCONNECT_SLAVES:
-        g_value_set_enum(value, nm_setting_connection_get_autoconnect_slaves(setting));
-        break;
     case PROP_SECONDARIES:
         g_value_take_boxed(value, nm_strvarray_get_strv_non_empty_dup(priv->secondaries, NULL));
         break;
-    case PROP_GATEWAY_PING_TIMEOUT:
-        g_value_set_uint(value, priv->gateway_ping_timeout);
-        break;
-    case PROP_METERED:
-        g_value_set_enum(value, priv->metered);
-        break;
-    case PROP_LLDP:
-        g_value_set_int(value, priv->lldp);
-        break;
-    case PROP_AUTH_RETRIES:
-        g_value_set_int(value, priv->auth_retries);
-        break;
-    case PROP_MDNS:
-        g_value_set_int(value, priv->mdns);
-        break;
-    case PROP_LLMNR:
-        g_value_set_int(value, priv->llmnr);
-        break;
-    case PROP_WAIT_DEVICE_TIMEOUT:
-        g_value_set_int(value, priv->wait_device_timeout);
-        break;
-    case PROP_MUD_URL:
-        g_value_set_string(value, priv->mud_url);
-        break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_get_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -1686,26 +1652,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
     NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE(object);
 
     switch (prop_id) {
-    case PROP_ID:
-        g_free(priv->id);
-        priv->id = g_value_dup_string(value);
-        break;
-    case PROP_UUID:
-        g_free(priv->uuid);
-        priv->uuid = g_value_dup_string(value);
-        break;
-    case PROP_STABLE_ID:
-        g_free(priv->stable_id);
-        priv->stable_id = g_value_dup_string(value);
-        break;
-    case PROP_INTERFACE_NAME:
-        g_free(priv->interface_name);
-        priv->interface_name = g_value_dup_string(value);
-        break;
-    case PROP_TYPE:
-        g_free(priv->type);
-        priv->type = g_value_dup_string(value);
-        break;
     case PROP_PERMISSIONS:
     {
         const char *const *strv;
@@ -1726,69 +1672,14 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         }
         break;
     }
-    case PROP_AUTOCONNECT:
-        priv->autoconnect = g_value_get_boolean(value);
-        break;
-    case PROP_AUTOCONNECT_PRIORITY:
-        priv->autoconnect_priority = g_value_get_int(value);
-        break;
-    case PROP_AUTOCONNECT_RETRIES:
-        priv->autoconnect_retries = g_value_get_int(value);
-        break;
-    case PROP_MULTI_CONNECT:
-        priv->multi_connect = g_value_get_int(value);
-        break;
     case PROP_TIMESTAMP:
         priv->timestamp = g_value_get_uint64(value);
         break;
-    case PROP_READ_ONLY:
-        priv->read_only = g_value_get_boolean(value);
-        break;
-    case PROP_ZONE:
-        g_free(priv->zone);
-        priv->zone = g_value_dup_string(value);
-        break;
-    case PROP_MASTER:
-        g_free(priv->master);
-        priv->master = g_value_dup_string(value);
-        break;
-    case PROP_SLAVE_TYPE:
-        g_free(priv->slave_type);
-        priv->slave_type = g_value_dup_string(value);
-        break;
-    case PROP_AUTOCONNECT_SLAVES:
-        priv->autoconnect_slaves = g_value_get_enum(value);
-        break;
     case PROP_SECONDARIES:
         nm_strvarray_set_strv(&priv->secondaries, g_value_get_boxed(value));
         break;
-    case PROP_GATEWAY_PING_TIMEOUT:
-        priv->gateway_ping_timeout = g_value_get_uint(value);
-        break;
-    case PROP_METERED:
-        priv->metered = g_value_get_enum(value);
-        break;
-    case PROP_LLDP:
-        priv->lldp = g_value_get_int(value);
-        break;
-    case PROP_AUTH_RETRIES:
-        priv->auth_retries = g_value_get_int(value);
-        break;
-    case PROP_MDNS:
-        priv->mdns = g_value_get_int(value);
-        break;
-    case PROP_LLMNR:
-        priv->llmnr = g_value_get_int(value);
-        break;
-    case PROP_WAIT_DEVICE_TIMEOUT:
-        priv->wait_device_timeout = g_value_get_int(value);
-        break;
-    case PROP_MUD_URL:
-        g_free(priv->mud_url);
-        priv->mud_url = g_value_dup_string(value);
-        break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_set_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -1797,19 +1688,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 
 static void
 nm_setting_connection_init(NMSettingConnection *setting)
-{
-    NMSettingConnectionPrivate *priv = NM_SETTING_CONNECTION_GET_PRIVATE(setting);
-
-    priv->auth_retries         = -1;
-    priv->autoconnect          = TRUE;
-    priv->autoconnect_priority = NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT;
-    priv->autoconnect_retries  = -1;
-    priv->autoconnect_slaves   = NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT;
-    priv->lldp                 = NM_SETTING_CONNECTION_LLDP_DEFAULT;
-    priv->llmnr                = NM_SETTING_CONNECTION_LLMNR_DEFAULT;
-    priv->mdns                 = NM_SETTING_CONNECTION_MDNS_DEFAULT;
-    priv->wait_device_timeout  = -1;
-}
+{}
 
 /**
  * nm_setting_connection_new:
@@ -1829,15 +1708,6 @@ 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_free(priv->mud_url);
     nm_clear_pointer(&priv->permissions, g_array_unref);
     nm_clear_pointer(&priv->secondaries, g_array_unref);
 
@@ -1857,8 +1727,7 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
     object_class->set_property = set_property;
     object_class->finalize     = finalize;
 
-    setting_class->verify           = verify;
-    setting_class->compare_property = compare_property;
+    setting_class->verify = verify;
 
     /**
      * NMSettingConnection:id:
@@ -1872,12 +1741,21 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * description: User friendly name for the connection profile.
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_CONNECTION_ID,
-                                       PROP_ID,
-                                       NM_SETTING_PARAM_FUZZY_IGNORE,
-                                       nm_setting_connection_get_id);
+    _nm_setting_property_define_direct_string_full(
+        properties_override,
+        obj_properties,
+        NM_SETTING_CONNECTION_ID,
+        PROP_ID,
+        NM_SETTING_PARAM_FUZZY_IGNORE,
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING,
+                                       .direct_type   = NM_VALUE_TYPE_STRING,
+                                       .compare_fcn   = compare_fcn_id,
+                                       .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_direct,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
+                                       .from_dbus_is_full                = TRUE,
+                                       .from_dbus_direct_allow_transform = TRUE),
+        NMSettingConnectionPrivate,
+        id);
 
     /**
      * NMSettingConnection:uuid:
@@ -1902,12 +1780,13 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      *   creates the UUID itself (by hashing the filename).
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_CONNECTION_UUID,
-                                       PROP_UUID,
-                                       NM_SETTING_PARAM_FUZZY_IGNORE,
-                                       nm_setting_connection_get_uuid);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CONNECTION_UUID,
+                                              PROP_UUID,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE,
+                                              NMSettingConnectionPrivate,
+                                              uuid);
 
     /**
      * NMSettingConnection:stable-id:
@@ -1956,12 +1835,13 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * description: Token to generate stable IDs.
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_CONNECTION_STABLE_ID,
-                                       PROP_STABLE_ID,
-                                       NM_SETTING_PARAM_FUZZY_IGNORE,
-                                       nm_setting_connection_get_stable_id);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CONNECTION_STABLE_ID,
+                                              PROP_STABLE_ID,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE,
+                                              NMSettingConnectionPrivate,
+                                              stable_id);
 
     /**
      * NMSettingConnection:interface-name:
@@ -1986,17 +1866,23 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      *   can be required for some connection types.
      * ---end---
      */
-    _nm_setting_property_define_string_full(
+    _nm_setting_property_define_direct_string_full(
         properties_override,
         obj_properties,
         NM_SETTING_CONNECTION_INTERFACE_NAME,
         PROP_INTERFACE_NAME,
         NM_SETTING_PARAM_INFERRABLE,
         NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING,
-                                       .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_get_string,
+                                       .direct_type = NM_VALUE_TYPE_STRING,
+                                       .compare_fcn = _nm_setting_property_compare_fcn_direct,
+                                       .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_direct,
                                        .missing_from_dbus_fcn =
-                                           nm_setting_connection_no_interface_name),
-        nm_setting_connection_get_interface_name);
+                                           nm_setting_connection_no_interface_name,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
+                                       .from_dbus_is_full                = TRUE,
+                                       .from_dbus_direct_allow_transform = TRUE),
+        NMSettingConnectionPrivate,
+        interface_name);
 
     /**
      * NMSettingConnection:type:
@@ -2016,12 +1902,13 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: TYPE=Ethernet; TYPE=Bond; TYPE=Bridge; DEVICETYPE=TeamPort
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_CONNECTION_TYPE,
-                                       PROP_TYPE,
-                                       NM_SETTING_PARAM_INFERRABLE,
-                                       nm_setting_connection_get_connection_type);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CONNECTION_TYPE,
+                                              PROP_TYPE,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingConnectionPrivate,
+                                              type);
 
     /**
      * NMSettingConnection:permissions:
@@ -2063,6 +1950,10 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * %TRUE to automatically activate the connection, %FALSE to require manual
      * intervention to activate the connection.
      *
+     * Autoconnect happens when the circumstances are suitable. That means for
+     * example that the device is currently managed and not active. Autoconnect
+     * thus never replaces or competes with an already active profile.
+     *
      * Note that autoconnect is not implemented for VPN profiles. See
      * #NMSettingConnection:secondaries as an alternative to automatically
      * connect VPN profiles.
@@ -2074,20 +1965,24 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * description: Whether the connection should be autoconnected (not only while booting).
      * ---end---
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_CONNECTION_AUTOCONNECT,
-                                        PROP_AUTOCONNECT,
-                                        TRUE,
-                                        NM_SETTING_PARAM_FUZZY_IGNORE,
-                                        nm_setting_connection_get_autoconnect);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_CONNECTION_AUTOCONNECT,
+                                               PROP_AUTOCONNECT,
+                                               TRUE,
+                                               NM_SETTING_PARAM_FUZZY_IGNORE,
+                                               NMSettingConnectionPrivate,
+                                               autoconnect);
 
     /**
      * NMSettingConnection:autoconnect-priority:
      *
-     * The autoconnect priority. If the connection is set to autoconnect,
-     * connections with higher priority will be preferred. Defaults to 0.
-     * The higher number means higher priority.
+     * The autoconnect priority in range -999 to 999. If the connection is set
+     * to autoconnect, connections with higher priority will be preferred.
+     * The higher number means higher priority. Defaults to 0.
+     * Note that this property only matters if there are more than one candidate
+     * profile to select for autoconnect. In case of equal priority, the profile
+     * used most recently is chosen.
      **/
     /* ---ifcfg-rh---
      * property: autoconnect-priority
@@ -2099,14 +1994,16 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: AUTOCONNECT_PRIORITY=20
      * ---end---
      */
-    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,
-        NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY,
+                                             PROP_AUTOCONNECT_PRIORITY,
+                                             NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MIN,
+                                             NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX,
+                                             NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_DEFAULT,
+                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                             NMSettingConnectionPrivate,
+                                             autoconnect_priority);
 
     /**
      * NMSettingConnection:autoconnect-retries:
@@ -2126,14 +2023,16 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: AUTOCONNECT_RETRIES=1
      * ---end---
      */
-    obj_properties[PROP_AUTOCONNECT_RETRIES] = g_param_spec_int(
-        NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,
-        "",
-        "",
-        -1,
-        G_MAXINT32,
-        -1,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_CONNECTION_AUTOCONNECT_RETRIES,
+                                             PROP_AUTOCONNECT_RETRIES,
+                                             -1,
+                                             G_MAXINT32,
+                                             -1,
+                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                             NMSettingConnectionPrivate,
+                                             autoconnect_retries);
 
     /**
      * NMSettingConnection:multi-connect:
@@ -2151,14 +2050,16 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: MULTI_CONNECT=3
      * ---end---
      */
-    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);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_CONNECTION_MULTI_CONNECT,
+                                             PROP_MULTI_CONNECT,
+                                             G_MININT32,
+                                             G_MAXINT32,
+                                             NM_CONNECTION_MULTI_CONNECT_DEFAULT,
+                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                             NMSettingConnectionPrivate,
+                                             multi_connect);
 
     /**
      * NMSettingConnection:timestamp:
@@ -2183,7 +2084,10 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
         properties_override,
         obj_properties[PROP_TIMESTAMP],
         NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_UINT64,
-                                       .to_dbus_fcn = _to_dbus_fcn_timestamp, ));
+                                       .compare_fcn   = compare_fcn_timestamp,
+                                       .to_dbus_fcn   = _to_dbus_fcn_timestamp,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                       .from_dbus_is_full = TRUE));
 
     /**
      * NMSettingConnection:read-only:
@@ -2192,13 +2096,14 @@ 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.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_CONNECTION_READ_ONLY,
-                                        PROP_READ_ONLY,
-                                        FALSE,
-                                        NM_SETTING_PARAM_FUZZY_IGNORE,
-                                        nm_setting_connection_get_read_only);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_CONNECTION_READ_ONLY,
+                                               PROP_READ_ONLY,
+                                               FALSE,
+                                               NM_SETTING_PARAM_FUZZY_IGNORE,
+                                               NMSettingConnectionPrivate,
+                                               read_only);
 
     /**
      * NMSettingConnection:zone:
@@ -2219,13 +2124,14 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: ZONE=Work
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_CONNECTION_ZONE,
-                                       PROP_ZONE,
-                                       NM_SETTING_PARAM_FUZZY_IGNORE
-                                           | NM_SETTING_PARAM_REAPPLY_IMMEDIATELY,
-                                       nm_setting_connection_get_zone);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CONNECTION_ZONE,
+                                              PROP_ZONE,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE
+                                                  | NM_SETTING_PARAM_REAPPLY_IMMEDIATELY,
+                                              NMSettingConnectionPrivate,
+                                              zone);
 
     /**
      * NMSettingConnection:master:
@@ -2241,12 +2147,14 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      *   for compatibility with legacy tooling.
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_CONNECTION_MASTER,
-                                       PROP_MASTER,
-                                       NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_INFERRABLE,
-                                       nm_setting_connection_get_master);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CONNECTION_MASTER,
+                                              PROP_MASTER,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE
+                                                  | NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingConnectionPrivate,
+                                              master);
 
     /**
      * NMSettingConnection:slave-type:
@@ -2265,12 +2173,14 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      *   and BRIDGE_UUID for bridging.
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_CONNECTION_SLAVE_TYPE,
-                                       PROP_SLAVE_TYPE,
-                                       NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_INFERRABLE,
-                                       nm_setting_connection_get_slave_type);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CONNECTION_SLAVE_TYPE,
+                                              PROP_SLAVE_TYPE,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE
+                                                  | NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingConnectionPrivate,
+                                              slave_type);
 
     /**
      * NMSettingConnection:autoconnect-slaves:
@@ -2295,13 +2205,15 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      *   when this connection is activated.
      * ---end---
      */
-    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 | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_enum(properties_override,
+                                            obj_properties,
+                                            NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
+                                            PROP_AUTOCONNECT_SLAVES,
+                                            NM_TYPE_SETTING_CONNECTION_AUTOCONNECT_SLAVES,
+                                            NM_SETTING_CONNECTION_AUTOCONNECT_SLAVES_DEFAULT,
+                                            NM_SETTING_PARAM_FUZZY_IGNORE,
+                                            NMSettingConnectionPrivate,
+                                            autoconnect_slaves);
 
     /**
      * NMSettingConnection:secondaries:
@@ -2339,14 +2251,16 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: GATEWAY_PING_TIMEOUT=5
      * ---end---
      */
-    obj_properties[PROP_GATEWAY_PING_TIMEOUT] =
-        g_param_spec_uint(NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT,
-                          "",
-                          "",
-                          0,
-                          600,
-                          0,
-                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CONNECTION_GATEWAY_PING_TIMEOUT,
+                                              PROP_GATEWAY_PING_TIMEOUT,
+                                              0,
+                                              600,
+                                              0,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingConnectionPrivate,
+                                              gateway_ping_timeout);
 
     /**
      * NMSettingConnection:metered:
@@ -2366,13 +2280,15 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: CONNECTION_METERED=yes
      * ---end---
      */
-    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);
+    _nm_setting_property_define_direct_enum(properties_override,
+                                            obj_properties,
+                                            NM_SETTING_CONNECTION_METERED,
+                                            PROP_METERED,
+                                            NM_TYPE_METERED,
+                                            NM_METERED_UNKNOWN,
+                                            NM_SETTING_PARAM_REAPPLY_IMMEDIATELY,
+                                            NMSettingConnectionPrivate,
+                                            metered);
 
     /**
      * NMSettingConnection:lldp:
@@ -2390,14 +2306,16 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: LLDP=no
      * ---end---
      */
-    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_STATIC_STRINGS);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_CONNECTION_LLDP,
+                                             PROP_LLDP,
+                                             G_MININT32,
+                                             G_MAXINT32,
+                                             NM_SETTING_CONNECTION_LLDP_DEFAULT,
+                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                             NMSettingConnectionPrivate,
+                                             lldp);
 
     /**
      * NMSettingConnection:auth-retries:
@@ -2417,14 +2335,16 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * description: Number of retries for authentication.
      * ---end---
      */
-    obj_properties[PROP_AUTH_RETRIES] = g_param_spec_int(
-        NM_SETTING_CONNECTION_AUTH_RETRIES,
-        "",
-        "",
-        -1,
-        G_MAXINT32,
-        -1,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_CONNECTION_AUTH_RETRIES,
+                                             PROP_AUTH_RETRIES,
+                                             -1,
+                                             G_MAXINT32,
+                                             -1,
+                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                             NMSettingConnectionPrivate,
+                                             auth_retries);
 
     /**
      * NMSettingConnection:mdns:
@@ -2440,7 +2360,7 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      *
      * This feature requires a plugin which supports mDNS. Otherwise, the
      * setting has no effect. One such plugin is dns-systemd-resolved.
-*
+     *
      * Since: 1.12
      **/
     /* ---ifcfg-rh---
@@ -2452,13 +2372,16 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: MDNS=yes
      * ---end---
      */
-    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);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_CONNECTION_MDNS,
+                                             PROP_MDNS,
+                                             G_MININT32,
+                                             G_MAXINT32,
+                                             NM_SETTING_CONNECTION_MDNS_DEFAULT,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingConnectionPrivate,
+                                             mdns);
 
     /**
      * NMSettingConnection:llmnr:
@@ -2488,13 +2411,52 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: LLMNR=yes
      * ---end---
      */
-    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);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_CONNECTION_LLMNR,
+                                             PROP_LLMNR,
+                                             G_MININT32,
+                                             G_MAXINT32,
+                                             NM_SETTING_CONNECTION_LLMNR_DEFAULT,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingConnectionPrivate,
+                                             llmnr);
+
+    /**
+     * NMSettingConnection:dns-over-tls:
+     *
+     * Whether DNSOverTls (dns-over-tls) is enabled for the connection.
+     * DNSOverTls is a technology which uses TLS to encrypt dns traffic.
+     *
+     * The permitted values are: "yes" (2) use DNSOverTls and disabled fallback,
+     * "opportunistic" (1) use DNSOverTls but allow fallback to unencrypted resolution,
+     * "no" (0) don't ever use DNSOverTls.
+     * If unspecified "default" depends on the plugin used. Systemd-resolved
+     * uses global setting.
+     *
+     * This feature requires a plugin which supports DNSOverTls. Otherwise, the
+     * setting has no effect. One such plugin is dns-systemd-resolved.
+     *
+     * Since: 1.34
+     **/
+    /* ---ifcfg-rh---
+     * property: dns-over-tls
+     * variable: DNS_OVER_TLS(+)
+     * values: yes,no,opportunistic
+     * default: missing variable means global default
+     * description: Whether or not DNSOverTls is enabled for the connection
+     * ---end---
+     */
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_CONNECTION_DNS_OVER_TLS,
+                                             PROP_DNS_OVER_TLS,
+                                             G_MININT32,
+                                             G_MAXINT32,
+                                             NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingConnectionPrivate,
+                                             dns_over_tls);
 
     /**
      * NMSettingConnection:wait-device-timeout:
@@ -2521,14 +2483,16 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: DEVTIMEOUT=5
      * ---end---
      */
-    obj_properties[PROP_WAIT_DEVICE_TIMEOUT] =
-        g_param_spec_int(NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT,
-                         "",
-                         "",
-                         -1,
-                         G_MAXINT32,
-                         -1,
-                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT,
+                                             PROP_WAIT_DEVICE_TIMEOUT,
+                                             -1,
+                                             G_MAXINT32,
+                                             -1,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingConnectionPrivate,
+                                             wait_device_timeout);
 
     /**
      * NMSettingConnection:mud-url:
@@ -2552,17 +2516,19 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
      * example: https://yourdevice.example.com/model.json
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_CONNECTION_MUD_URL,
-                                       PROP_MUD_URL,
-                                       NM_SETTING_PARAM_NONE,
-                                       nm_setting_connection_get_mud_url);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_CONNECTION_MUD_URL,
+                                              PROP_MUD_URL,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingConnectionPrivate,
+                                              mud_url);
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_CONNECTION,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-dcb.c b/src/libnm-core-impl/nm-setting-dcb.c
index f9602c3b..a6f4a973 100644
--- a/src/libnm-core-impl/nm-setting-dcb.c
+++ b/src/libnm-core-impl/nm-setting-dcb.c
@@ -48,21 +48,21 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingDcb,
                              PROP_PRIORITY_TRAFFIC_CLASS, );
 
 typedef struct {
-    char *            app_fcoe_mode;
-    guint             pfc[8];
-    guint             priority_group_id[8];
-    guint             priority_group_bandwidth[8];
-    guint             priority_bandwidth[8];
-    guint             priority_strict[8];
-    guint             priority_traffic_class[8];
-    int               app_fcoe_priority;
-    int               app_iscsi_priority;
-    int               app_fip_priority;
-    NMSettingDcbFlags app_fcoe_flags;
-    NMSettingDcbFlags app_iscsi_flags;
-    NMSettingDcbFlags app_fip_flags;
-    NMSettingDcbFlags pfc_flags;
-    NMSettingDcbFlags priority_group_flags;
+    char * app_fcoe_mode;
+    guint  pfc[8];
+    guint  priority_group_id[8];
+    guint  priority_group_bandwidth[8];
+    guint  priority_bandwidth[8];
+    guint  priority_strict[8];
+    guint  priority_traffic_class[8];
+    guint  app_fcoe_flags;
+    guint  app_iscsi_flags;
+    guint  app_fip_flags;
+    guint  pfc_flags;
+    guint  priority_group_flags;
+    gint32 app_fcoe_priority;
+    gint32 app_iscsi_priority;
+    gint32 app_fip_priority;
 } NMSettingDcbPrivate;
 
 /**
@@ -607,17 +607,10 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
                         error))
         return FALSE;
 
-    if (!priv->app_fcoe_mode) {
-        g_set_error_literal(error,
-                            NM_CONNECTION_ERROR,
-                            NM_CONNECTION_ERROR_MISSING_PROPERTY,
-                            _("property missing"));
-        g_prefix_error(error, "%s.%s: ", NM_SETTING_DCB_SETTING_NAME, NM_SETTING_DCB_APP_FCOE_MODE);
-        return FALSE;
-    }
-
-    if (strcmp(priv->app_fcoe_mode, NM_SETTING_DCB_FCOE_MODE_FABRIC)
-        && strcmp(priv->app_fcoe_mode, NM_SETTING_DCB_FCOE_MODE_VN2VN)) {
+    if (!NM_IN_STRSET(priv->app_fcoe_mode,
+                      NULL,
+                      NM_SETTING_DCB_FCOE_MODE_FABRIC,
+                      NM_SETTING_DCB_FCOE_MODE_VN2VN)) {
         g_set_error_literal(error,
                             NM_CONNECTION_ERROR,
                             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -745,20 +738,23 @@ set_gvalue_from_array(GValue *v, uint *a, size_t len)
 #define SET_GVALUE_FROM_ARRAY(v, a) set_gvalue_from_array(v, a, G_N_ELEMENTS(a))
 
 static void
-_nm_setting_dcb_uint_array_from_dbus(GVariant *dbus_value, GValue *prop_value)
+_nm_setting_dcb_uint_array_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil)
 {
     gconstpointer array;
     gsize         length;
 
-    array = g_variant_get_fixed_array(dbus_value, &length, sizeof(guint32));
-    set_gvalue_from_array(prop_value, (guint *) array, length);
+    array = g_variant_get_fixed_array(from, &length, sizeof(guint32));
+    set_gvalue_from_array(to, (guint *) array, length);
 }
 
 static const NMSettInfoPropertType nm_sett_info_propert_type_dcb_au =
     NM_SETT_INFO_PROPERT_TYPE_GPROP_INIT(
         NM_G_VARIANT_TYPE("au"),
-        .typdata_to_dbus.gprop_type = NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_GARRAY_UINT,
-        .gprop_from_dbus_fcn        = _nm_setting_dcb_uint_array_from_dbus, );
+        .typdata_to_dbus.gprop_type  = NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_GARRAY_UINT,
+        .typdata_from_dbus.gprop_fcn = _nm_setting_dcb_uint_array_from_dbus,
+        .compare_fcn                 = _nm_setting_property_compare_fcn_default,
+        .from_dbus_fcn               = _nm_setting_property_from_dbus_fcn_gprop,
+        .from_dbus_is_full           = TRUE);
 
 /*****************************************************************************/
 
@@ -769,36 +765,9 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     NMSettingDcbPrivate *priv    = NM_SETTING_DCB_GET_PRIVATE(setting);
 
     switch (prop_id) {
-    case PROP_APP_FCOE_FLAGS:
-        g_value_set_flags(value, priv->app_fcoe_flags);
-        break;
-    case PROP_APP_FCOE_PRIORITY:
-        g_value_set_int(value, priv->app_fcoe_priority);
-        break;
-    case PROP_APP_FCOE_MODE:
-        g_value_set_string(value, priv->app_fcoe_mode);
-        break;
-    case PROP_APP_ISCSI_FLAGS:
-        g_value_set_flags(value, priv->app_iscsi_flags);
-        break;
-    case PROP_APP_ISCSI_PRIORITY:
-        g_value_set_int(value, priv->app_iscsi_priority);
-        break;
-    case PROP_APP_FIP_FLAGS:
-        g_value_set_flags(value, priv->app_fip_flags);
-        break;
-    case PROP_APP_FIP_PRIORITY:
-        g_value_set_int(value, priv->app_fip_priority);
-        break;
-    case PROP_PFC_FLAGS:
-        g_value_set_flags(value, priv->pfc_flags);
-        break;
     case PROP_PRIORITY_FLOW_CONTROL:
         SET_GVALUE_FROM_ARRAY(value, priv->pfc);
         break;
-    case PROP_PRIORITY_GROUP_FLAGS:
-        g_value_set_flags(value, priv->priority_group_flags);
-        break;
     case PROP_PRIORITY_GROUP_ID:
         SET_GVALUE_FROM_ARRAY(value, priv->priority_group_id);
         break;
@@ -815,7 +784,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         SET_GVALUE_FROM_ARRAY(value, priv->priority_traffic_class);
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_get_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -826,37 +795,9 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
     NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE(object);
 
     switch (prop_id) {
-    case PROP_APP_FCOE_FLAGS:
-        priv->app_fcoe_flags = g_value_get_flags(value);
-        break;
-    case PROP_APP_FCOE_PRIORITY:
-        priv->app_fcoe_priority = g_value_get_int(value);
-        break;
-    case PROP_APP_FCOE_MODE:
-        g_free(priv->app_fcoe_mode);
-        priv->app_fcoe_mode = g_value_dup_string(value);
-        break;
-    case PROP_APP_ISCSI_FLAGS:
-        priv->app_iscsi_flags = g_value_get_flags(value);
-        break;
-    case PROP_APP_ISCSI_PRIORITY:
-        priv->app_iscsi_priority = g_value_get_int(value);
-        break;
-    case PROP_APP_FIP_FLAGS:
-        priv->app_fip_flags = g_value_get_flags(value);
-        break;
-    case PROP_APP_FIP_PRIORITY:
-        priv->app_fip_priority = g_value_get_int(value);
-        break;
-    case PROP_PFC_FLAGS:
-        priv->pfc_flags = g_value_get_flags(value);
-        break;
     case PROP_PRIORITY_FLOW_CONTROL:
         SET_ARRAY_FROM_GVALUE(value, priv->pfc);
         break;
-    case PROP_PRIORITY_GROUP_FLAGS:
-        priv->priority_group_flags = g_value_get_flags(value);
-        break;
     case PROP_PRIORITY_GROUP_ID:
         SET_ARRAY_FROM_GVALUE(value, priv->priority_group_id);
         break;
@@ -873,7 +814,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         SET_ARRAY_FROM_GVALUE(value, priv->priority_traffic_class);
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_set_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -882,14 +823,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 
 static void
 nm_setting_dcb_init(NMSettingDcb *self)
-{
-    NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE(self);
-
-    priv->app_fcoe_mode      = g_strdup(NM_SETTING_DCB_FCOE_MODE_FABRIC);
-    priv->app_fcoe_priority  = -1;
-    priv->app_fip_priority   = -1;
-    priv->app_iscsi_priority = -1;
-}
+{}
 
 /**
  * nm_setting_dcb_new:
@@ -905,16 +839,6 @@ nm_setting_dcb_new(void)
 }
 
 static void
-finalize(GObject *object)
-{
-    NMSettingDcbPrivate *priv = NM_SETTING_DCB_GET_PRIVATE(object);
-
-    g_free(priv->app_fcoe_mode);
-
-    G_OBJECT_CLASS(nm_setting_dcb_parent_class)->finalize(object);
-}
-
-static void
 nm_setting_dcb_class_init(NMSettingDcbClass *klass)
 {
     GObjectClass *  object_class        = G_OBJECT_CLASS(klass);
@@ -925,7 +849,6 @@ nm_setting_dcb_class_init(NMSettingDcbClass *klass)
 
     object_class->get_property = get_property;
     object_class->set_property = set_property;
-    object_class->finalize     = finalize;
 
     setting_class->verify = verify;
 
@@ -944,13 +867,15 @@ nm_setting_dcb_class_init(NMSettingDcbClass *klass)
      * example: DCB_APP_FCOE_ENABLE=yes DCB_APP_FCOE_ADVERTISE=yes
      * ---end---
      */
-    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);
+    _nm_setting_property_define_direct_flags(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_DCB_APP_FCOE_FLAGS,
+                                             PROP_APP_FCOE_FLAGS,
+                                             NM_TYPE_SETTING_DCB_FLAGS,
+                                             NM_SETTING_DCB_FLAG_NONE,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingDcbPrivate,
+                                             app_fcoe_flags);
 
     /**
      * NMSettingDcb:app-fcoe-priority:
@@ -966,20 +891,25 @@ nm_setting_dcb_class_init(NMSettingDcbClass *klass)
      * description: Priority of FCoE frames.
      * ---end---
      */
-    obj_properties[PROP_APP_FCOE_PRIORITY] =
-        g_param_spec_int(NM_SETTING_DCB_APP_FCOE_PRIORITY,
-                         "",
-                         "",
-                         -1,
-                         7,
-                         -1,
-                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_DCB_APP_FCOE_PRIORITY,
+                                             PROP_APP_FCOE_PRIORITY,
+                                             -1,
+                                             7,
+                                             -1,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingDcbPrivate,
+                                             app_fcoe_priority);
 
     /**
      * NMSettingDcb:app-fcoe-mode:
      *
      * The FCoE controller mode; either %NM_SETTING_DCB_FCOE_MODE_FABRIC
-     * (default) or %NM_SETTING_DCB_FCOE_MODE_VN2VN.
+     * or %NM_SETTING_DCB_FCOE_MODE_VN2VN.
+     *
+     * Since 1.34, %NULL is the default and means %NM_SETTING_DCB_FCOE_MODE_FABRIC.
+     * Before 1.34, %NULL was rejected as invalid and the default was %NM_SETTING_DCB_FCOE_MODE_FABRIC.
      **/
     /* ---ifcfg-rh---
      * property: app-fcoe-mode
@@ -989,12 +919,13 @@ nm_setting_dcb_class_init(NMSettingDcbClass *klass)
      * description: FCoE controller mode.
      * ---end---
      */
-    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_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_DCB_APP_FCOE_MODE,
+                                              PROP_APP_FCOE_MODE,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingDcbPrivate,
+                                              app_fcoe_mode);
 
     /**
      * NMSettingDcb:app-iscsi-flags:
@@ -1010,13 +941,15 @@ nm_setting_dcb_class_init(NMSettingDcbClass *klass)
      * description: iSCSI flags.
      * ---end---
      */
-    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);
+    _nm_setting_property_define_direct_flags(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_DCB_APP_ISCSI_FLAGS,
+                                             PROP_APP_ISCSI_FLAGS,
+                                             NM_TYPE_SETTING_DCB_FLAGS,
+                                             NM_SETTING_DCB_FLAG_NONE,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingDcbPrivate,
+                                             app_iscsi_flags);
 
     /**
      * NMSettingDcb:app-iscsi-priority:
@@ -1032,14 +965,16 @@ nm_setting_dcb_class_init(NMSettingDcbClass *klass)
      * description: Priority of iSCSI frames.
      * ---end---
      */
-    obj_properties[PROP_APP_ISCSI_PRIORITY] =
-        g_param_spec_int(NM_SETTING_DCB_APP_ISCSI_PRIORITY,
-                         "",
-                         "",
-                         -1,
-                         7,
-                         -1,
-                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_DCB_APP_ISCSI_PRIORITY,
+                                             PROP_APP_ISCSI_PRIORITY,
+                                             -1,
+                                             7,
+                                             -1,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingDcbPrivate,
+                                             app_iscsi_priority);
 
     /**
      * NMSettingDcb:app-fip-flags:
@@ -1055,13 +990,15 @@ nm_setting_dcb_class_init(NMSettingDcbClass *klass)
      * description: FIP flags.
      * ---end---
      */
-    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);
+    _nm_setting_property_define_direct_flags(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_DCB_APP_FIP_FLAGS,
+                                             PROP_APP_FIP_FLAGS,
+                                             NM_TYPE_SETTING_DCB_FLAGS,
+                                             NM_SETTING_DCB_FLAG_NONE,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingDcbPrivate,
+                                             app_fip_flags);
 
     /**
      * NMSettingDcb:app-fip-priority:
@@ -1077,14 +1014,16 @@ nm_setting_dcb_class_init(NMSettingDcbClass *klass)
      * description: Priority of FIP frames.
      * ---end---
      */
-    obj_properties[PROP_APP_FIP_PRIORITY] =
-        g_param_spec_int(NM_SETTING_DCB_APP_FIP_PRIORITY,
-                         "",
-                         "",
-                         -1,
-                         7,
-                         -1,
-                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_DCB_APP_FIP_PRIORITY,
+                                             PROP_APP_FIP_PRIORITY,
+                                             -1,
+                                             7,
+                                             -1,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingDcbPrivate,
+                                             app_fip_priority);
 
     /**
      * NMSettingDcb:priority-flow-control-flags:
@@ -1100,12 +1039,15 @@ nm_setting_dcb_class_init(NMSettingDcbClass *klass)
      * description: Priority flow control flags.
      * ---end---
      */
-    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);
+    _nm_setting_property_define_direct_flags(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_DCB_PRIORITY_FLOW_CONTROL_FLAGS,
+                                             PROP_PFC_FLAGS,
+                                             NM_TYPE_SETTING_DCB_FLAGS,
+                                             NM_SETTING_DCB_FLAG_NONE,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingDcbPrivate,
+                                             pfc_flags);
 
     /**
      * NMSettingDcb:priority-flow-control: (type GArray(gboolean))
@@ -1146,13 +1088,15 @@ nm_setting_dcb_class_init(NMSettingDcbClass *klass)
      * description: Priority groups flags.
      * ---end---
      */
-    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);
+    _nm_setting_property_define_direct_flags(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_DCB_PRIORITY_GROUP_FLAGS,
+                                             PROP_PRIORITY_GROUP_FLAGS,
+                                             NM_TYPE_SETTING_DCB_FLAGS,
+                                             NM_SETTING_DCB_FLAG_NONE,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingDcbPrivate,
+                                             priority_group_flags);
 
     /**
      * NMSettingDcb:priority-group-id: (type GArray(guint))
@@ -1282,8 +1226,9 @@ nm_setting_dcb_class_init(NMSettingDcbClass *klass)
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_DCB,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-dummy.c b/src/libnm-core-impl/nm-setting-dummy.c
index a998a27b..bfc82aea 100644
--- a/src/libnm-core-impl/nm-setting-dummy.c
+++ b/src/libnm-core-impl/nm-setting-dummy.c
@@ -78,5 +78,5 @@ nm_setting_dummy_class_init(NMSettingDummyClass *klass)
 
     setting_class->verify = verify;
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_DUMMY);
+    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_DUMMY, NULL, NULL, 0);
 }
diff --git a/src/libnm-core-impl/nm-setting-ethtool.c b/src/libnm-core-impl/nm-setting-ethtool.c
index abedcae3..901dab48 100644
--- a/src/libnm-core-impl/nm-setting-ethtool.c
+++ b/src/libnm-core-impl/nm-setting-ethtool.c
@@ -407,11 +407,12 @@ nm_setting_ethtool_class_init(NMSettingEthtoolClass *klass)
 
     setting_class->verify = verify;
 
-    _nm_setting_class_commit_full(
+    _nm_setting_class_commit(
         setting_class,
         NM_META_SETTING_TYPE_ETHTOOL,
         NM_SETT_INFO_SETT_DETAIL(.gendata_info =
                                      NM_SETT_INFO_SETT_GENDATA(.get_variant_type =
                                                                    get_variant_type, ), ),
-        NULL);
+        NULL,
+        0);
 }
diff --git a/src/libnm-core-impl/nm-setting-generic.c b/src/libnm-core-impl/nm-setting-generic.c
index 0eff1ddf..ce212554 100644
--- a/src/libnm-core-impl/nm-setting-generic.c
+++ b/src/libnm-core-impl/nm-setting-generic.c
@@ -74,5 +74,5 @@ nm_setting_generic_class_init(NMSettingGenericClass *klass)
 
     g_type_class_add_private(klass, sizeof(NMSettingGenericPrivate));
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_GENERIC);
+    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_GENERIC, NULL, NULL, 0);
 }
diff --git a/src/libnm-core-impl/nm-setting-gsm.c b/src/libnm-core-impl/nm-setting-gsm.c
index a177f7fb..8b2749d9 100644
--- a/src/libnm-core-impl/nm-setting-gsm.c
+++ b/src/libnm-core-impl/nm-setting-gsm.c
@@ -51,8 +51,8 @@ typedef struct {
     NMSettingSecretFlags password_flags;
     NMSettingSecretFlags pin_flags;
     guint32              mtu;
-    bool                 auto_config : 1;
-    bool                 home_only : 1;
+    bool                 auto_config;
+    bool                 home_only;
 } NMSettingGsmPrivate;
 
 /**
@@ -667,13 +667,14 @@ nm_setting_gsm_class_init(NMSettingGsmClass *klass)
      *
      * Since: 1.22
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_GSM_AUTO_CONFIG,
-                                        PROP_AUTO_CONFIG,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_gsm_get_auto_config);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_GSM_AUTO_CONFIG,
+                                               PROP_AUTO_CONFIG,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingGsmPrivate,
+                                               auto_config);
 
     /**
      * NMSettingGsm:number:
@@ -794,13 +795,14 @@ 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.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_GSM_HOME_ONLY,
-                                        PROP_HOME_ONLY,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_gsm_get_home_only);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_GSM_HOME_ONLY,
+                                               PROP_HOME_ONLY,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingGsmPrivate,
+                                               home_only);
 
     /**
      * NMSettingGsm:device-id:
@@ -879,8 +881,9 @@ nm_setting_gsm_class_init(NMSettingGsmClass *klass)
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_GSM,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-hostname.c b/src/libnm-core-impl/nm-setting-hostname.c
index 248ae556..d0a15684 100644
--- a/src/libnm-core-impl/nm-setting-hostname.c
+++ b/src/libnm-core-impl/nm-setting-hostname.c
@@ -34,9 +34,9 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingHostname,
 struct _NMSettingHostname {
     NMSetting parent;
     int       priority;
-    NMTernary from_dhcp;
-    NMTernary from_dns_lookup;
-    NMTernary only_from_default;
+    int       from_dhcp;
+    int       from_dns_lookup;
+    int       only_from_default;
 };
 
 struct _NMSettingHostnameClass {
@@ -124,62 +124,8 @@ nm_setting_hostname_get_only_from_default(NMSettingHostname *setting)
 /*****************************************************************************/
 
 static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    NMSettingHostname *self = NM_SETTING_HOSTNAME(object);
-
-    switch (prop_id) {
-    case PROP_PRIORITY:
-        g_value_set_int(value, self->priority);
-        break;
-    case PROP_FROM_DHCP:
-        g_value_set_enum(value, self->from_dhcp);
-        break;
-    case PROP_FROM_DNS_LOOKUP:
-        g_value_set_enum(value, self->from_dns_lookup);
-        break;
-    case PROP_ONLY_FROM_DEFAULT:
-        g_value_set_enum(value, self->only_from_default);
-        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)
-{
-    NMSettingHostname *self = NM_SETTING_HOSTNAME(object);
-
-    switch (prop_id) {
-    case PROP_PRIORITY:
-        self->priority = g_value_get_int(value);
-        break;
-    case PROP_FROM_DHCP:
-        self->from_dhcp = g_value_get_enum(value);
-        break;
-    case PROP_FROM_DNS_LOOKUP:
-        self->from_dns_lookup = g_value_get_enum(value);
-        break;
-    case PROP_ONLY_FROM_DEFAULT:
-        self->only_from_default = g_value_get_enum(value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static void
 nm_setting_hostname_init(NMSettingHostname *setting)
-{
-    setting->from_dhcp         = NM_TERNARY_DEFAULT;
-    setting->from_dns_lookup   = NM_TERNARY_DEFAULT;
-    setting->only_from_default = NM_TERNARY_DEFAULT;
-}
+{}
 
 /**
  * nm_setting_hostname_new:
@@ -199,11 +145,12 @@ nm_setting_hostname_new(void)
 static void
 nm_setting_hostname_class_init(NMSettingHostnameClass *klass)
 {
-    GObjectClass *  object_class  = G_OBJECT_CLASS(klass);
-    NMSettingClass *setting_class = NM_SETTING_CLASS(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->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     /**
      * NMSettingHostname:priority
@@ -234,13 +181,16 @@ nm_setting_hostname_class_init(NMSettingHostnameClass *klass)
      * example: HOSTNAME_PRIORITY=50
      * ---end---
      */
-    obj_properties[PROP_PRIORITY] = g_param_spec_int(NM_SETTING_HOSTNAME_PRIORITY,
-                                                     "",
-                                                     "",
-                                                     G_MININT32,
-                                                     G_MAXINT32,
-                                                     0,
-                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_HOSTNAME_PRIORITY,
+                                             PROP_PRIORITY,
+                                             G_MININT32,
+                                             G_MAXINT32,
+                                             0,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingHostname,
+                                             priority);
 
     /**
      * NMSettingHostname:from-dhcp
@@ -262,13 +212,13 @@ nm_setting_hostname_class_init(NMSettingHostnameClass *klass)
      * example: HOSTNAME_FROM_DHCP=0,1
      * ---end---
      */
-    obj_properties[PROP_FROM_DHCP] = g_param_spec_enum(
-        NM_SETTING_HOSTNAME_FROM_DHCP,
-        "",
-        "",
-        NM_TYPE_TERNARY,
-        NM_TERNARY_DEFAULT,
-        NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_ternary_enum(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_HOSTNAME_FROM_DHCP,
+                                                    PROP_FROM_DHCP,
+                                                    NM_SETTING_PARAM_FUZZY_IGNORE,
+                                                    NMSettingHostname,
+                                                    from_dhcp);
 
     /**
      * NMSettingHostname:from-dns-lookup
@@ -291,13 +241,13 @@ nm_setting_hostname_class_init(NMSettingHostnameClass *klass)
      * example: HOSTNAME_FROM_DNS_LOOKUP=0,1
      * ---end---
      */
-    obj_properties[PROP_FROM_DNS_LOOKUP] = g_param_spec_enum(
-        NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP,
-        "",
-        "",
-        NM_TYPE_TERNARY,
-        NM_TERNARY_DEFAULT,
-        NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_ternary_enum(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_HOSTNAME_FROM_DNS_LOOKUP,
+                                                    PROP_FROM_DNS_LOOKUP,
+                                                    NM_SETTING_PARAM_FUZZY_IGNORE,
+                                                    NMSettingHostname,
+                                                    from_dns_lookup);
 
     /**
      * NMSettingHostname:only-from-default
@@ -325,15 +275,19 @@ nm_setting_hostname_class_init(NMSettingHostnameClass *klass)
      * example: HOSTNAME_ONLY_FROM_DEFAULT=0,1
      * ---end---
      */
-    obj_properties[PROP_ONLY_FROM_DEFAULT] = g_param_spec_enum(
-        NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT,
-        "",
-        "",
-        NM_TYPE_TERNARY,
-        NM_TERNARY_DEFAULT,
-        NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_ternary_enum(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_HOSTNAME_ONLY_FROM_DEFAULT,
+                                                    PROP_ONLY_FROM_DEFAULT,
+                                                    NM_SETTING_PARAM_FUZZY_IGNORE,
+                                                    NMSettingHostname,
+                                                    only_from_default);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_HOSTNAME);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_HOSTNAME,
+                             NULL,
+                             properties_override,
+                             0);
 }
diff --git a/src/libnm-core-impl/nm-setting-infiniband.c b/src/libnm-core-impl/nm-setting-infiniband.c
index 95dc7b66..adbf3636 100644
--- a/src/libnm-core-impl/nm-setting-infiniband.c
+++ b/src/libnm-core-impl/nm-setting-infiniband.c
@@ -379,7 +379,6 @@ 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);
 
@@ -426,15 +425,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---
      */
-    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);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_MAC_ADDRESS],
-                                 &nm_sett_info_propert_type_mac_address);
+    _nm_setting_property_define_direct_mac_address(properties_override,
+                                                   obj_properties,
+                                                   NM_SETTING_INFINIBAND_MAC_ADDRESS,
+                                                   PROP_MAC_ADDRESS,
+                                                   NM_SETTING_PARAM_INFERRABLE,
+                                                   NMSettingInfinibandPrivate,
+                                                   mac_address,
+                                                   .direct_set_string_mac_address_len =
+                                                       INFINIBAND_ALEN);
 
     /**
      * NMSettingInfiniband:mtu:
@@ -530,8 +529,9 @@ nm_setting_infiniband_class_init(NMSettingInfinibandClass *klass)
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_INFINIBAND,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-ip-config.c b/src/libnm-core-impl/nm-setting-ip-config.c
index f37e9d35..33779ed9 100644
--- a/src/libnm-core-impl/nm-setting-ip-config.c
+++ b/src/libnm-core-impl/nm-setting-ip-config.c
@@ -527,7 +527,7 @@ _nm_ip_address_get_attribute_names(const NMIPAddress *address, gboolean sorted,
 {
     nm_assert(address);
 
-    return nm_utils_strdict_get_keys(address->attributes, sorted, out_length);
+    return nm_strdict_get_keys(address->attributes, sorted, out_length);
 }
 
 /**
@@ -546,7 +546,7 @@ nm_ip_address_get_attribute_names(NMIPAddress *address)
     g_return_val_if_fail(address, NULL);
 
     names = _nm_ip_address_get_attribute_names(address, TRUE, NULL);
-    return nm_utils_strv_make_deep_copied_nonnull(names);
+    return nm_strv_make_deep_copied_nonnull(names);
 }
 
 /**
@@ -1134,7 +1134,7 @@ _nm_ip_route_get_attribute_names(const NMIPRoute *route, gboolean sorted, guint
 {
     nm_assert(route);
 
-    return nm_utils_strdict_get_keys(route->attributes, sorted, out_length);
+    return nm_strdict_get_keys(route->attributes, sorted, out_length);
 }
 
 /**
@@ -1153,7 +1153,7 @@ nm_ip_route_get_attribute_names(NMIPRoute *route)
     g_return_val_if_fail(route != NULL, NULL);
 
     names = _nm_ip_route_get_attribute_names(route, TRUE, NULL);
-    return nm_utils_strv_make_deep_copied_nonnull(names);
+    return nm_strv_make_deep_copied_nonnull(names);
 }
 
 /**
@@ -1440,7 +1440,7 @@ _nm_ip_route_attribute_validate_all(const NMIPRoute *route, GError **error)
         const char *key  = attrs[i].name;
         GVariant *  val2 = attrs[i].value_ptr;
 
-        if (!nm_ip_route_attribute_validate(key, val2, route->family, NULL, NULL))
+        if (!nm_ip_route_attribute_validate(key, val2, route->family, NULL, error))
             return FALSE;
     }
 
@@ -2441,9 +2441,10 @@ nm_ip_routing_rule_set_suppress_prefixlength(NMIPRoutingRule *self, gint32 suppr
  *
  * Returns: %TRUE if a uid range is set.
  *
- * Since: 1.32. Due to a bug, the symbols of this function
- *   are only exported starting with version 1.34.0. The function
- *   is unusable in 1.32.
+ * Since: 1.34
+ *
+ * This API was wrongly introduced in the header files for 1.32, but the
+ * symbols were not exported. The API only works since 1.34 and newer.
  */
 gboolean
 nm_ip_routing_rule_get_uid_range(const NMIPRoutingRule *self,
@@ -2468,9 +2469,10 @@ nm_ip_routing_rule_get_uid_range(const NMIPRoutingRule *self,
  * For a valid range, start must be less or equal to end.
  * If set to an invalid range, the range gets unset.
  *
- * Since: 1.32. Due to a bug, the symbols of this function
- *   are only exported starting with version 1.34.0. The function
- *   is unusable in 1.32.
+ * Since: 1.34
+ *
+ * This API was wrongly introduced in the header files for 1.32, but the
+ * symbols were not exported. The API only works since 1.34 and newer.
  */
 void
 nm_ip_routing_rule_set_uid_range(NMIPRoutingRule *self,
@@ -3909,36 +3911,25 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingIPConfig,
                              PROP_DHCP_IAID,
                              PROP_DHCP_REJECT_SERVERS, );
 
-typedef struct {
-    GPtrArray *dns;         /* array of IP address strings */
-    GPtrArray *dns_search;  /* array of domain name strings */
-    GPtrArray *dns_options; /* array of DNS options */
-    GPtrArray *addresses;   /* array of NMIPAddress */
-    GPtrArray *routes;      /* array of NMIPRoute */
-    GPtrArray *routing_rules;
-    GArray *   dhcp_reject_servers;
-    char *     method;
-    char *     gateway;
-    char *     dhcp_hostname;
-    char *     dhcp_iaid;
-    gint64     route_metric;
-    guint      dhcp_hostname_flags;
-    int        dns_priority;
-    int        dad_timeout;
-    int        dhcp_timeout;
-    int        required_timeout;
-    guint32    route_table;
-    bool       ignore_auto_routes : 1;
-    bool       ignore_auto_dns : 1;
-    bool       dhcp_send_hostname : 1;
-    bool       never_default : 1;
-    bool       may_fail : 1;
-} NMSettingIPConfigPrivate;
-
 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))
+static inline NMSettingIPConfigPrivate *
+_NM_SETTING_IP_CONFIG_GET_PRIVATE(NMSettingIPConfig *self)
+{
+    NMSettingIPConfigClass *klass;
+
+    nm_assert(NM_IS_SETTING_IP_CONFIG(self));
+
+    klass = NM_SETTING_IP_CONFIG_GET_CLASS(self);
+
+    nm_assert(klass->private_offset < 0);
+
+    return (gpointer) (((char *) ((gpointer) self)) + klass->private_offset);
+}
+
+#define NM_SETTING_IP_CONFIG_GET_PRIVATE(self) \
+    _NM_SETTING_IP_CONFIG_GET_PRIVATE(         \
+        NM_GOBJECT_CAST_NON_NULL(NMSettingIPConfig, self, NM_IS_SETTING_IP_CONFIG, NMSetting))
 
 /*****************************************************************************/
 
@@ -4110,6 +4101,12 @@ nm_setting_ip_config_clear_dns(NMSettingIPConfig *setting)
     }
 }
 
+GPtrArray *
+_nm_setting_ip_config_get_dns_array(NMSettingIPConfig *setting)
+{
+    return NM_SETTING_IP_CONFIG_GET_PRIVATE(setting)->dns;
+}
+
 /**
  * nm_setting_ip_config_get_num_dns_searches:
  * @setting: the #NMSettingIPConfig
@@ -4952,12 +4949,7 @@ nm_setting_ip_config_clear_routing_rules(NMSettingIPConfig *setting)
 }
 
 static GVariant *
-_routing_rules_dbus_only_synth(const NMSettInfoSetting *               sett_info,
-                               guint                                   property_idx,
-                               NMConnection *                          connection,
-                               NMSetting *                             setting,
-                               NMConnectionSerializationFlags          flags,
-                               const NMConnectionSerializationOptions *options)
+_routing_rules_dbus_only_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     NMSettingIPConfig *       self = NM_SETTING_IP_CONFIG(setting);
     NMSettingIPConfigPrivate *priv;
@@ -4988,12 +4980,7 @@ _routing_rules_dbus_only_synth(const NMSettInfoSetting *               sett_info
 }
 
 static gboolean
-_routing_rules_dbus_only_set(NMSetting *         setting,
-                             GVariant *          connection_dict,
-                             const char *        property,
-                             GVariant *          value,
-                             NMSettingParseFlags parse_flags,
-                             GError **           error)
+_routing_rules_dbus_only_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GVariantIter iter_rules;
     GVariant *   rule_var;
@@ -5037,6 +5024,7 @@ _routing_rules_dbus_only_set(NMSetting *         setting,
     success = TRUE;
 
 out:
+    *out_is_modified = rules_changed;
     if (rules_changed)
         _routing_rules_notify(NM_SETTING_IP_CONFIG(setting));
     return success;
@@ -5216,7 +5204,7 @@ nm_setting_ip_config_get_dhcp_timeout(NMSettingIPConfig *setting)
  *
  * Returns: the required timeout for the address family
  *
- * Since: 1.34, 1.32.4
+ * Since: 1.34
  **/
 int
 nm_setting_ip_config_get_required_timeout(NMSettingIPConfig *setting)
@@ -5628,77 +5616,77 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
     return TRUE;
 }
 
-static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+NMTernary
+_nm_setting_ip_config_compare_fcn_addresses(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
     NMSettingIPConfigPrivate *a_priv;
     NMSettingIPConfigPrivate *b_priv;
     guint                     i;
 
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_IP_CONFIG_ADDRESSES)) {
-        if (set_b) {
-            a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_a);
-            b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_b);
+    if (set_b) {
+        a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_a);
+        b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_b);
 
-            if (a_priv->addresses->len != b_priv->addresses->len)
+        if (a_priv->addresses->len != b_priv->addresses->len)
+            return FALSE;
+        for (i = 0; i < a_priv->addresses->len; i++) {
+            if (nm_ip_address_cmp_full(a_priv->addresses->pdata[i],
+                                       b_priv->addresses->pdata[i],
+                                       NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS)
+                != 0)
                 return FALSE;
-            for (i = 0; i < a_priv->addresses->len; i++) {
-                if (nm_ip_address_cmp_full(a_priv->addresses->pdata[i],
-                                           b_priv->addresses->pdata[i],
-                                           NM_IP_ADDRESS_CMP_FLAGS_WITH_ATTRS)
-                    != 0)
-                    return FALSE;
-            }
         }
-        return TRUE;
     }
+    return TRUE;
+}
 
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_IP_CONFIG_ROUTES)) {
-        if (set_b) {
-            a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_a);
-            b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_b);
+NMTernary
+_nm_setting_ip_config_compare_fcn_routes(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    NMSettingIPConfigPrivate *a_priv;
+    NMSettingIPConfigPrivate *b_priv;
+    guint                     i;
 
-            if (a_priv->routes->len != b_priv->routes->len)
+    if (set_b) {
+        a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_a);
+        b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_b);
+
+        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;
-            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;
     }
+    return TRUE;
+}
+
+static NMTernary
+compare_fcn_routing_rules(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    NMSettingIPConfigPrivate *a_priv;
+    NMSettingIPConfigPrivate *b_priv;
+    guint                     i;
 
-    if (nm_streq(sett_info->property_infos[property_idx].name,
-                 NM_SETTING_IP_CONFIG_ROUTING_RULES)) {
-        if (set_b) {
-            guint n;
+    if (set_b) {
+        guint n;
 
-            a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_a);
-            b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_b);
+        a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_a);
+        b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(set_b);
 
-            n = (a_priv->routing_rules) ? a_priv->routing_rules->len : 0u;
-            if (n != (b_priv->routing_rules ? b_priv->routing_rules->len : 0u))
+        n = (a_priv->routing_rules) ? a_priv->routing_rules->len : 0u;
+        if (n != (b_priv->routing_rules ? b_priv->routing_rules->len : 0u))
+            return FALSE;
+        for (i = 0; i < n; i++) {
+            if (nm_ip_routing_rule_cmp(a_priv->routing_rules->pdata[i],
+                                       b_priv->routing_rules->pdata[i])
+                != 0)
                 return FALSE;
-            for (i = 0; i < n; i++) {
-                if (nm_ip_routing_rule_cmp(a_priv->routing_rules->pdata[i],
-                                           b_priv->routing_rules->pdata[i])
-                    != 0)
-                    return FALSE;
-            }
         }
-        return TRUE;
     }
-
-    return NM_SETTING_CLASS(nm_setting_ip_config_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    return TRUE;
 }
 
 static void
@@ -5759,58 +5747,61 @@ enumerate_values(const NMSettInfoProperty *property_info,
 
 /*****************************************************************************/
 
-static gboolean
-ip_gateway_set(NMSetting *         setting,
-               GVariant *          connection_dict,
-               const char *        property,
-               GVariant *          value,
-               NMSettingParseFlags parse_flags,
-               GError **           error)
+gboolean
+_nm_setting_property_from_dbus_fcn_direct_ip_config_gateway(
+    _NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    /* 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;
+    return _nm_setting_property_from_dbus_fcn_direct(sett_info,
+                                                     property_info,
+                                                     setting,
+                                                     connection_dict,
+                                                     value,
+                                                     parse_flags,
+                                                     out_is_modified,
+                                                     error);
 }
 
 GArray *
-_nm_sett_info_property_override_create_array_ip_config(void)
+_nm_sett_info_property_override_create_array_ip_config(int addr_family)
 {
     GArray *properties_override = _nm_sett_info_property_override_create_array();
 
+    nm_assert_addr_family(addr_family);
+
     _nm_properties_override_gobj(
         properties_override,
         obj_properties[PROP_METHOD],
-        &nm_sett_info_propert_type_string,
-        .to_dbus_data.get_string =
-            (const char *(*) (NMSetting *) ) nm_setting_ip_config_get_method);
+        &nm_sett_info_propert_type_direct_string,
+        .direct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(char *, NMSettingIPConfigPrivate, method));
 
     _nm_properties_override_gobj(
         properties_override,
         obj_properties[PROP_GATEWAY],
-        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING,
-                                       .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_get_string,
-                                       .from_dbus_fcn = ip_gateway_set),
-        .to_dbus_data.get_string =
-            (const char *(*) (NMSetting *) ) nm_setting_ip_config_get_gateway);
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(
+            G_VARIANT_TYPE_STRING,
+            .direct_type   = NM_VALUE_TYPE_STRING,
+            .compare_fcn   = _nm_setting_property_compare_fcn_direct,
+            .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_direct,
+            .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct_ip_config_gateway),
+        .direct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(char *, NMSettingIPConfigPrivate, gateway),
+        .direct_set_string_ip_address_addr_family = addr_family);
 
     _nm_properties_override_gobj(
         properties_override,
         obj_properties[PROP_DHCP_HOSTNAME],
-        &nm_sett_info_propert_type_string,
-        .to_dbus_data.get_string =
-            (const char *(*) (NMSetting *) ) nm_setting_ip_config_get_dhcp_hostname);
+        &nm_sett_info_propert_type_direct_string,
+        .direct_offset =
+            NM_STRUCT_OFFSET_ENSURE_TYPE(char *, NMSettingIPConfigPrivate, dhcp_hostname));
 
     _nm_properties_override_gobj(
         properties_override,
         obj_properties[PROP_DHCP_IAID],
-        &nm_sett_info_propert_type_string,
-        .to_dbus_data.get_string =
-            (const char *(*) (NMSetting *) ) nm_setting_ip_config_get_dhcp_iaid);
+        &nm_sett_info_propert_type_direct_string,
+        .direct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(char *, NMSettingIPConfigPrivate, dhcp_iaid));
 
     /* ---dbus---
      * property: routing-rules
@@ -5823,37 +5814,42 @@ _nm_sett_info_property_override_create_array_ip_config(void)
         NM_SETTING_IP_CONFIG_ROUTING_RULES,
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
                                        .to_dbus_fcn   = _routing_rules_dbus_only_synth,
+                                       .compare_fcn   = compare_fcn_routing_rules,
                                        .from_dbus_fcn = _routing_rules_dbus_only_set, ));
 
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_IGNORE_AUTO_ROUTES],
-                                 &nm_sett_info_propert_type_boolean,
-                                 .to_dbus_data.get_boolean = (gboolean(*)(
-                                     NMSetting *)) nm_setting_ip_config_get_ignore_auto_routes);
-
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_IGNORE_AUTO_DNS],
-                                 &nm_sett_info_propert_type_boolean,
-                                 .to_dbus_data.get_boolean = (gboolean(*)(
-                                     NMSetting *)) nm_setting_ip_config_get_ignore_auto_dns);
-
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_DHCP_SEND_HOSTNAME],
-                                 &nm_sett_info_propert_type_boolean,
-                                 .to_dbus_data.get_boolean = (gboolean(*)(
-                                     NMSetting *)) nm_setting_ip_config_get_dhcp_send_hostname);
-
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_NEVER_DEFAULT],
-                                 &nm_sett_info_propert_type_boolean,
-                                 .to_dbus_data.get_boolean = (gboolean(*)(
-                                     NMSetting *)) nm_setting_ip_config_get_never_default);
-
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_MAY_FAIL],
-                                 &nm_sett_info_propert_type_boolean,
-                                 .to_dbus_data.get_boolean =
-                                     (gboolean(*)(NMSetting *)) nm_setting_ip_config_get_may_fail);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_IGNORE_AUTO_ROUTES],
+        &nm_sett_info_propert_type_direct_boolean,
+        .direct_offset =
+            NM_STRUCT_OFFSET_ENSURE_TYPE(bool, NMSettingIPConfigPrivate, ignore_auto_routes));
+
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_IGNORE_AUTO_DNS],
+        &nm_sett_info_propert_type_direct_boolean,
+        .direct_offset =
+            NM_STRUCT_OFFSET_ENSURE_TYPE(bool, NMSettingIPConfigPrivate, ignore_auto_dns));
+
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_DHCP_SEND_HOSTNAME],
+        &nm_sett_info_propert_type_direct_boolean,
+        .direct_offset =
+            NM_STRUCT_OFFSET_ENSURE_TYPE(bool, NMSettingIPConfigPrivate, dhcp_send_hostname));
+
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_NEVER_DEFAULT],
+        &nm_sett_info_propert_type_direct_boolean,
+        .direct_offset =
+            NM_STRUCT_OFFSET_ENSURE_TYPE(bool, NMSettingIPConfigPrivate, never_default));
+
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_MAY_FAIL],
+        &nm_sett_info_propert_type_direct_boolean,
+        .direct_offset = NM_STRUCT_OFFSET_ENSURE_TYPE(bool, NMSettingIPConfigPrivate, may_fail));
 
     return properties_override;
 }
@@ -5952,7 +5948,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 {
     NMSettingIPConfig *       setting = NM_SETTING_IP_CONFIG(object);
     NMSettingIPConfigPrivate *priv    = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
-    const char *              gateway;
     char **                   strv;
     guint                     i;
 
@@ -5963,11 +5958,11 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         break;
     case PROP_DNS:
         g_ptr_array_unref(priv->dns);
-        priv->dns = _nm_utils_strv_to_ptrarray(g_value_get_boxed(value));
+        priv->dns = nm_strv_to_ptrarray(g_value_get_boxed(value));
         break;
     case PROP_DNS_SEARCH:
         g_ptr_array_unref(priv->dns_search);
-        priv->dns_search = _nm_utils_strv_to_ptrarray(g_value_get_boxed(value));
+        priv->dns_search = nm_strv_to_ptrarray(g_value_get_boxed(value));
         break;
     case PROP_DNS_OPTIONS:
         strv = g_value_get_boxed(value);
@@ -5998,12 +5993,10 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
                                                (GDestroyNotify) nm_ip_address_unref);
         break;
     case PROP_GATEWAY:
-        gateway = g_value_get_string(value);
-        g_return_if_fail(
-            !gateway
-            || nm_utils_ipaddr_is_valid(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), gateway));
         g_free(priv->gateway);
-        priv->gateway = canonicalize_ip(NM_SETTING_IP_CONFIG_GET_FAMILY(setting), gateway, TRUE);
+        priv->gateway =
+            _nm_utils_ipaddr_canonical_or_invalid(NM_SETTING_IP_CONFIG_GET_FAMILY(setting),
+                                                  g_value_get_string(value));
         break;
     case PROP_ROUTES:
         g_ptr_array_unref(priv->routes);
@@ -6063,20 +6056,24 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 
 /*****************************************************************************/
 
+void
+_nm_setting_ip_config_private_init(gpointer self, NMSettingIPConfigPrivate *priv)
+{
+    nm_assert(NM_IS_SETTING_IP_CONFIG(self));
+
+    priv->dns              = g_ptr_array_new_with_free_func(g_free);
+    priv->dns_search       = g_ptr_array_new_with_free_func(g_free);
+    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);
+    priv->route_metric     = -1;
+    priv->dad_timeout      = -1;
+    priv->required_timeout = -1;
+}
+
 static void
 nm_setting_ip_config_init(NMSettingIPConfig *setting)
 {
-    NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(setting);
-
-    priv->dns                = g_ptr_array_new_with_free_func(g_free);
-    priv->dns_search         = g_ptr_array_new_with_free_func(g_free);
-    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);
-    priv->route_metric       = -1;
-    priv->dhcp_send_hostname = TRUE;
-    priv->may_fail           = TRUE;
-    priv->dad_timeout        = -1;
-    priv->required_timeout   = -1;
+    /* cannot yet access NM_SETTING_IP_CONFIG_GET_PRIVATE(). */
 }
 
 static void
@@ -6085,11 +6082,6 @@ finalize(GObject *object)
     NMSettingIPConfig *       self = NM_SETTING_IP_CONFIG(object);
     NMSettingIPConfigPrivate *priv = NM_SETTING_IP_CONFIG_GET_PRIVATE(self);
 
-    g_free(priv->method);
-    g_free(priv->gateway);
-    g_free(priv->dhcp_hostname);
-    g_free(priv->dhcp_iaid);
-
     g_ptr_array_unref(priv->dns);
     g_ptr_array_unref(priv->dns_search);
     if (priv->dns_options)
@@ -6109,14 +6101,11 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass)
     GObjectClass *  object_class  = G_OBJECT_CLASS(klass);
     NMSettingClass *setting_class = NM_SETTING_CLASS(klass);
 
-    g_type_class_add_private(klass, sizeof(NMSettingIPConfigPrivate));
-
     object_class->get_property = get_property;
     object_class->set_property = set_property;
     object_class->finalize     = finalize;
 
     setting_class->verify                    = verify;
-    setting_class->compare_property          = compare_property;
     setting_class->duplicate_copy_properties = duplicate_copy_properties;
     setting_class->enumerate_values          = enumerate_values;
 
@@ -6532,7 +6521,7 @@ nm_setting_ip_config_class_init(NMSettingIPConfigClass *klass)
      * default value (either configuration ipvx.required-timeout override or
      * zero).
      *
-     * Since: 1.34, 1.32.4
+     * Since: 1.34
      **/
     obj_properties[PROP_REQUIRED_TIMEOUT] = g_param_spec_int(
         NM_SETTING_IP_CONFIG_REQUIRED_TIMEOUT,
diff --git a/src/libnm-core-impl/nm-setting-ip-tunnel.c b/src/libnm-core-impl/nm-setting-ip-tunnel.c
index d87a59f4..38f9047d 100644
--- a/src/libnm-core-impl/nm-setting-ip-tunnel.c
+++ b/src/libnm-core-impl/nm-setting-ip-tunnel.c
@@ -44,7 +44,7 @@ typedef struct {
     NMIPTunnelMode mode;
     guint32        mtu;
     guint32        flags;
-    bool           path_mtu_discovery : 1;
+    bool           path_mtu_discovery;
 } NMSettingIPTunnelPrivate;
 
 /**
@@ -615,11 +615,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 
 static void
 nm_setting_ip_tunnel_init(NMSettingIPTunnel *self)
-{
-    NMSettingIPTunnelPrivate *priv = NM_SETTING_IP_TUNNEL_GET_PRIVATE(self);
-
-    priv->path_mtu_discovery = TRUE;
-}
+{}
 
 /**
  * nm_setting_ip_tunnel_new:
@@ -637,21 +633,6 @@ nm_setting_ip_tunnel_new(void)
 }
 
 static void
-finalize(GObject *object)
-{
-    NMSettingIPTunnel *       setting = NM_SETTING_IP_TUNNEL(object);
-    NMSettingIPTunnelPrivate *priv    = NM_SETTING_IP_TUNNEL_GET_PRIVATE(setting);
-
-    g_free(priv->parent);
-    g_free(priv->local);
-    g_free(priv->remote);
-    g_free(priv->input_key);
-    g_free(priv->output_key);
-
-    G_OBJECT_CLASS(nm_setting_ip_tunnel_parent_class)->finalize(object);
-}
-
-static void
 nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass)
 {
     GObjectClass *  object_class        = G_OBJECT_CLASS(klass);
@@ -662,7 +643,6 @@ nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass)
 
     object_class->get_property = get_property;
     object_class->set_property = set_property;
-    object_class->finalize     = finalize;
 
     setting_class->verify = verify;
 
@@ -675,12 +655,13 @@ nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_IP_TUNNEL_PARENT,
-                                       PROP_PARENT,
-                                       NM_SETTING_PARAM_INFERRABLE,
-                                       nm_setting_ip_tunnel_get_parent);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_IP_TUNNEL_PARENT,
+                                              PROP_PARENT,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingIPTunnelPrivate,
+                                              parent);
 
     /**
      * NMSettingIPTunnel:mode:
@@ -707,12 +688,13 @@ nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_IP_TUNNEL_LOCAL,
-                                       PROP_LOCAL,
-                                       NM_SETTING_PARAM_INFERRABLE,
-                                       nm_setting_ip_tunnel_get_local);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_IP_TUNNEL_LOCAL,
+                                              PROP_LOCAL,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingIPTunnelPrivate,
+                                              local);
 
     /**
      * NMSettingIPTunnel:remote:
@@ -722,12 +704,13 @@ nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_IP_TUNNEL_REMOTE,
-                                       PROP_REMOTE,
-                                       NM_SETTING_PARAM_INFERRABLE,
-                                       nm_setting_ip_tunnel_get_remote);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_IP_TUNNEL_REMOTE,
+                                              PROP_REMOTE,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingIPTunnelPrivate,
+                                              remote);
 
     /**
      * NMSettingIPTunnel:ttl
@@ -770,13 +753,14 @@ nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_IP_TUNNEL_PATH_MTU_DISCOVERY,
-                                        PROP_PATH_MTU_DISCOVERY,
-                                        TRUE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_ip_tunnel_get_path_mtu_discovery);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_IP_TUNNEL_PATH_MTU_DISCOVERY,
+                                               PROP_PATH_MTU_DISCOVERY,
+                                               TRUE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingIPTunnelPrivate,
+                                               path_mtu_discovery);
 
     /**
      * NMSettingIPTunnel:input-key:
@@ -786,12 +770,13 @@ nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_IP_TUNNEL_INPUT_KEY,
-                                       PROP_INPUT_KEY,
-                                       NM_SETTING_PARAM_INFERRABLE,
-                                       nm_setting_ip_tunnel_get_input_key);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_IP_TUNNEL_INPUT_KEY,
+                                              PROP_INPUT_KEY,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingIPTunnelPrivate,
+                                              input_key);
 
     /**
      * NMSettingIPTunnel:output-key:
@@ -801,12 +786,13 @@ nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_IP_TUNNEL_OUTPUT_KEY,
-                                       PROP_OUTPUT_KEY,
-                                       NM_SETTING_PARAM_INFERRABLE,
-                                       nm_setting_ip_tunnel_get_output_key);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_IP_TUNNEL_OUTPUT_KEY,
+                                              PROP_OUTPUT_KEY,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingIPTunnelPrivate,
+                                              output_key);
 
     /**
      * NMSettingIPTunnel:encapsulation-limit:
@@ -881,8 +867,9 @@ nm_setting_ip_tunnel_class_init(NMSettingIPTunnelClass *klass)
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_IP_TUNNEL,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_IP_TUNNEL,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-ip4-config.c b/src/libnm-core-impl/nm-setting-ip4-config.c
index a24ebcfb..a7b3d68b 100644
--- a/src/libnm-core-impl/nm-setting-ip4-config.c
+++ b/src/libnm-core-impl/nm-setting-ip4-config.c
@@ -8,6 +8,7 @@
 #include "nm-setting-ip4-config.h"
 
 #include "nm-setting-private.h"
+#include "nm-utils-private.h"
 
 /**
  * SECTION:nm-setting-ip4-config
@@ -40,6 +41,8 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_DHCP_CLIENT_ID,
                                   PROP_DHCP_VENDOR_CLASS_IDENTIFIER, );
 
 typedef struct {
+    NMSettingIPConfigPrivate parent;
+
     char *dhcp_client_id;
     char *dhcp_fqdn;
     char *dhcp_vendor_class_identifier;
@@ -319,24 +322,26 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static GVariant *
-ip4_dns_to_dbus(const GValue *prop_value)
+ip4_dns_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
-    return nm_utils_ip4_dns_to_variant(g_value_get_boxed(prop_value));
+    GPtrArray *dns;
+
+    dns = _nm_setting_ip_config_get_dns_array(NM_SETTING_IP_CONFIG(setting));
+
+    if (nm_g_ptr_array_len(dns) == 0)
+        return NULL;
+
+    return _nm_utils_ip4_dns_to_variant((const char *const *) dns->pdata, dns->len);
 }
 
 static void
-ip4_dns_from_dbus(GVariant *dbus_value, GValue *prop_value)
+ip4_dns_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil)
 {
-    g_value_take_boxed(prop_value, nm_utils_ip4_dns_from_variant(dbus_value));
+    g_value_take_boxed(to, nm_utils_ip4_dns_from_variant(from));
 }
 
 static GVariant *
-ip4_addresses_get(const NMSettInfoSetting *               sett_info,
-                  guint                                   property_idx,
-                  NMConnection *                          connection,
-                  NMSetting *                             setting,
-                  NMConnectionSerializationFlags          flags,
-                  const NMConnectionSerializationOptions *options)
+ip4_addresses_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *addrs = NULL;
     const char *                 gateway;
@@ -347,12 +352,7 @@ ip4_addresses_get(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-ip4_addresses_set(NMSetting *         setting,
-                  GVariant *          connection_dict,
-                  const char *        property,
-                  GVariant *          value,
-                  NMSettingParseFlags parse_flags,
-                  GError **           error)
+ip4_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GPtrArray *addrs;
     GVariant * s_ip4;
@@ -361,8 +361,10 @@ ip4_addresses_set(NMSetting *         setting,
 
     /* FIXME: properly handle errors */
 
-    if (!_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data"))
+    if (!_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data")) {
+        *out_is_modified = FALSE;
         return TRUE;
+    }
 
     addrs = nm_utils_ip4_addresses_from_variant(value, &gateway);
 
@@ -391,12 +393,7 @@ ip4_addresses_set(NMSetting *         setting,
 }
 
 static GVariant *
-ip4_address_labels_get(const NMSettInfoSetting *               sett_info,
-                       guint                                   property_idx,
-                       NMConnection *                          connection,
-                       NMSetting *                             setting,
-                       NMConnectionSerializationFlags          flags,
-                       const NMConnectionSerializationOptions *options)
+ip4_address_labels_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     NMSettingIPConfig *s_ip        = NM_SETTING_IP_CONFIG(setting);
     gboolean           have_labels = FALSE;
@@ -435,12 +432,7 @@ ip4_address_labels_get(const NMSettInfoSetting *               sett_info,
 }
 
 static GVariant *
-ip4_address_data_get(const NMSettInfoSetting *               sett_info,
-                     guint                                   property_idx,
-                     NMConnection *                          connection,
-                     NMSetting *                             setting,
-                     NMConnectionSerializationFlags          flags,
-                     const NMConnectionSerializationOptions *options)
+ip4_address_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *addrs = NULL;
 
@@ -452,20 +444,17 @@ ip4_address_data_get(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-ip4_address_data_set(NMSetting *         setting,
-                     GVariant *          connection_dict,
-                     const char *        property,
-                     GVariant *          value,
-                     NMSettingParseFlags parse_flags,
-                     GError **           error)
+ip4_address_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GPtrArray *addrs;
 
     /* FIXME: properly handle errors */
 
     /* Ignore 'address-data' if we're going to process 'addresses' */
-    if (_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data"))
+    if (_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data")) {
+        *out_is_modified = FALSE;
         return TRUE;
+    }
 
     addrs = nm_utils_ip_addresses_from_variant(value, AF_INET);
     g_object_set(setting, NM_SETTING_IP_CONFIG_ADDRESSES, addrs, NULL);
@@ -474,12 +463,7 @@ ip4_address_data_set(NMSetting *         setting,
 }
 
 static GVariant *
-ip4_routes_get(const NMSettInfoSetting *               sett_info,
-               guint                                   property_idx,
-               NMConnection *                          connection,
-               NMSetting *                             setting,
-               NMConnectionSerializationFlags          flags,
-               const NMConnectionSerializationOptions *options)
+ip4_routes_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *routes = NULL;
 
@@ -488,33 +472,25 @@ ip4_routes_get(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-ip4_routes_set(NMSetting *         setting,
-               GVariant *          connection_dict,
-               const char *        property,
-               GVariant *          value,
-               NMSettingParseFlags parse_flags,
-               GError **           error)
+ip4_routes_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GPtrArray *routes;
 
     /* FIXME: properly handle errors */
 
-    if (!_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data"))
+    if (!_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data")) {
+        *out_is_modified = FALSE;
         return TRUE;
+    }
 
     routes = nm_utils_ip4_routes_from_variant(value);
-    g_object_set(setting, property, routes, NULL);
+    g_object_set(setting, property_info->name, routes, NULL);
     g_ptr_array_unref(routes);
     return TRUE;
 }
 
 static GVariant *
-ip4_route_data_get(const NMSettInfoSetting *               sett_info,
-                   guint                                   property_idx,
-                   NMConnection *                          connection,
-                   NMSetting *                             setting,
-                   NMConnectionSerializationFlags          flags,
-                   const NMConnectionSerializationOptions *options)
+ip4_route_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *routes = NULL;
 
@@ -526,20 +502,17 @@ ip4_route_data_get(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-ip4_route_data_set(NMSetting *         setting,
-                   GVariant *          connection_dict,
-                   const char *        property,
-                   GVariant *          value,
-                   NMSettingParseFlags parse_flags,
-                   GError **           error)
+ip4_route_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GPtrArray *routes;
 
     /* FIXME: properly handle errors */
 
     /* Ignore 'route-data' if we're going to process 'routes' */
-    if (_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data"))
+    if (_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data")) {
+        *out_is_modified = FALSE;
         return TRUE;
+    }
 
     routes = nm_utils_ip_routes_from_variant(value, AF_INET);
     g_object_set(setting, NM_SETTING_IP_CONFIG_ROUTES, routes, NULL);
@@ -598,7 +571,11 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 
 static void
 nm_setting_ip4_config_init(NMSettingIP4Config *setting)
-{}
+{
+    NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE(setting);
+
+    _nm_setting_ip_config_private_init(setting, &priv->parent);
+}
 
 /**
  * nm_setting_ip4_config_new:
@@ -614,32 +591,22 @@ nm_setting_ip4_config_new(void)
 }
 
 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_free(priv->dhcp_vendor_class_identifier);
-
-    G_OBJECT_CLASS(nm_setting_ip4_config_parent_class)->finalize(object);
-}
-
-static void
 nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *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_ip_config();
+    GObjectClass *          object_class            = G_OBJECT_CLASS(klass);
+    NMSettingClass *        setting_class           = NM_SETTING_CLASS(klass);
+    NMSettingIPConfigClass *setting_ip_config_class = NM_SETTING_IP_CONFIG_CLASS(klass);
+    GArray *properties_override = _nm_sett_info_property_override_create_array_ip_config(AF_INET);
 
-    g_type_class_add_private(setting_class, sizeof(NMSettingIP4ConfigPrivate));
+    g_type_class_add_private(klass, sizeof(NMSettingIP4ConfigPrivate));
 
     object_class->get_property = get_property;
     object_class->set_property = set_property;
-    object_class->finalize     = finalize;
 
     setting_class->verify = verify;
 
+    setting_ip_config_class->private_offset = g_type_class_get_instance_private_offset(klass);
+
     /* ---ifcfg-rh---
      * property: method
      * variable: BOOTPROTO
@@ -847,12 +814,13 @@ 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---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID,
-                                       PROP_DHCP_CLIENT_ID,
-                                       NM_SETTING_PARAM_NONE,
-                                       nm_setting_ip4_config_get_dhcp_client_id);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID,
+                                              PROP_DHCP_CLIENT_ID,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingIP4ConfigPrivate,
+                                              dhcp_client_id);
 
     /* ---ifcfg-rh---
      * property: dad-timeout
@@ -898,12 +866,13 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
      * example: DHCP_FQDN=foo.bar.com
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_IP4_CONFIG_DHCP_FQDN,
-                                       PROP_DHCP_FQDN,
-                                       NM_SETTING_PARAM_NONE,
-                                       nm_setting_ip4_config_get_dhcp_fqdn);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_IP4_CONFIG_DHCP_FQDN,
+                                              PROP_DHCP_FQDN,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingIP4ConfigPrivate,
+                                              dhcp_fqdn);
 
     /**
      * NMSettingIP4Config:dhcp-vendor-class-identifier:
@@ -924,12 +893,13 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
      * example: DHCP_VENDOR_CLASS_IDENTIFIER=foo
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER,
-                                       PROP_DHCP_VENDOR_CLASS_IDENTIFIER,
-                                       NM_SETTING_PARAM_NONE,
-                                       nm_setting_ip4_config_get_dhcp_vendor_class_identifier);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_IP4_CONFIG_DHCP_VENDOR_CLASS_IDENTIFIER,
+                                              PROP_DHCP_VENDOR_CLASS_IDENTIFIER,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingIP4ConfigPrivate,
+                                              dhcp_vendor_class_identifier);
 
     /* IP4-specific property overrides */
 
@@ -943,9 +913,12 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
     _nm_properties_override_gobj(
         properties_override,
         g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_DNS),
-        NM_SETT_INFO_PROPERT_TYPE_GPROP(NM_G_VARIANT_TYPE("au"),
-                                        .gprop_from_dbus_fcn = ip4_dns_from_dbus, ),
-        .to_dbus_data.gprop_to_dbus_fcn = ip4_dns_to_dbus);
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("au"),
+                                       .compare_fcn = _nm_setting_property_compare_fcn_default,
+                                       .to_dbus_fcn = ip4_dns_to_dbus,
+                                       .typdata_from_dbus.gprop_fcn = ip4_dns_from_dbus,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                       .from_dbus_is_full = TRUE), );
 
     /* ---dbus---
      * property: addresses
@@ -976,12 +949,14 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
         g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_ADDRESSES),
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aau"),
                                        .to_dbus_fcn   = ip4_addresses_get,
+                                       .compare_fcn   = _nm_setting_ip_config_compare_fcn_addresses,
                                        .from_dbus_fcn = ip4_addresses_set, ));
     _nm_properties_override_dbus(
         properties_override,
         "address-labels",
         NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING_ARRAY,
-                                       .to_dbus_fcn = ip4_address_labels_get, ));
+                                       .to_dbus_fcn = ip4_address_labels_get,
+                                       .compare_fcn = _nm_setting_property_compare_fcn_ignore, ));
 
     /* ---dbus---
      * property: address-data
@@ -997,6 +972,7 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
         "address-data",
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
                                        .to_dbus_fcn   = ip4_address_data_get,
+                                       .compare_fcn   = _nm_setting_property_compare_fcn_ignore,
                                        .from_dbus_fcn = ip4_address_data_set, ));
 
     /* ---dbus---
@@ -1030,6 +1006,7 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
         g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_ROUTES),
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aau"),
                                        .to_dbus_fcn   = ip4_routes_get,
+                                       .compare_fcn   = _nm_setting_ip_config_compare_fcn_routes,
                                        .from_dbus_fcn = ip4_routes_set, ));
 
     /* ---dbus---
@@ -1050,12 +1027,14 @@ nm_setting_ip4_config_class_init(NMSettingIP4ConfigClass *klass)
         "route-data",
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
                                        .to_dbus_fcn   = ip4_route_data_get,
+                                       .compare_fcn   = _nm_setting_property_compare_fcn_ignore,
                                        .from_dbus_fcn = 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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_IP4_CONFIG,
+                             NULL,
+                             properties_override,
+                             setting_ip_config_class->private_offset);
 }
diff --git a/src/libnm-core-impl/nm-setting-ip6-config.c b/src/libnm-core-impl/nm-setting-ip6-config.c
index ee72de57..87b9d958 100644
--- a/src/libnm-core-impl/nm-setting-ip6-config.c
+++ b/src/libnm-core-impl/nm-setting-ip6-config.c
@@ -10,6 +10,7 @@
 #include <arpa/inet.h>
 
 #include "nm-setting-private.h"
+#include "nm-utils-private.h"
 #include "nm-core-enum-types.h"
 #include "libnm-core-intern/nm-core-internal.h"
 
@@ -45,6 +46,8 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_IP6_PRIVACY,
                                   PROP_RA_TIMEOUT, );
 
 typedef struct {
+    NMSettingIPConfigPrivate parent;
+
     char *                        token;
     char *                        dhcp_duid;
     NMSettingIP6ConfigPrivacy     ip6_privacy;
@@ -360,24 +363,26 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static GVariant *
-ip6_dns_to_dbus(const GValue *prop_value)
+ip6_dns_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
-    return nm_utils_ip6_dns_to_variant(g_value_get_boxed(prop_value));
+    GPtrArray *dns;
+
+    dns = _nm_setting_ip_config_get_dns_array(NM_SETTING_IP_CONFIG(setting));
+
+    if (nm_g_ptr_array_len(dns) == 0)
+        return NULL;
+
+    return _nm_utils_ip6_dns_to_variant((const char *const *) dns->pdata, dns->len);
 }
 
 static void
-ip6_dns_from_dbus(GVariant *dbus_value, GValue *prop_value)
+ip6_dns_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil)
 {
-    g_value_take_boxed(prop_value, nm_utils_ip6_dns_from_variant(dbus_value));
+    g_value_take_boxed(to, nm_utils_ip6_dns_from_variant(from));
 }
 
 static GVariant *
-ip6_addresses_get(const NMSettInfoSetting *               sett_info,
-                  guint                                   property_idx,
-                  NMConnection *                          connection,
-                  NMSetting *                             setting,
-                  NMConnectionSerializationFlags          flags,
-                  const NMConnectionSerializationOptions *options)
+ip6_addresses_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *addrs = NULL;
     const char *                 gateway;
@@ -388,20 +393,15 @@ ip6_addresses_get(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-ip6_addresses_set(NMSetting *         setting,
-                  GVariant *          connection_dict,
-                  const char *        property,
-                  GVariant *          value,
-                  NMSettingParseFlags parse_flags,
-                  GError **           error)
+ip6_addresses_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GPtrArray *addrs;
     char *     gateway = NULL;
 
-    /* FIXME: properly handle errors */
-
-    if (!_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data"))
+    if (!_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data")) {
+        *out_is_modified = FALSE;
         return TRUE;
+    }
 
     addrs = nm_utils_ip6_addresses_from_variant(value, &gateway);
 
@@ -417,12 +417,7 @@ ip6_addresses_set(NMSetting *         setting,
 }
 
 static GVariant *
-ip6_address_data_get(const NMSettInfoSetting *               sett_info,
-                     guint                                   property_idx,
-                     NMConnection *                          connection,
-                     NMSetting *                             setting,
-                     NMConnectionSerializationFlags          flags,
-                     const NMConnectionSerializationOptions *options)
+ip6_address_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *addrs = NULL;
 
@@ -434,20 +429,15 @@ ip6_address_data_get(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-ip6_address_data_set(NMSetting *         setting,
-                     GVariant *          connection_dict,
-                     const char *        property,
-                     GVariant *          value,
-                     NMSettingParseFlags parse_flags,
-                     GError **           error)
+ip6_address_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GPtrArray *addrs;
 
-    /* FIXME: properly handle errors */
-
     /* Ignore 'address-data' if we're going to process 'addresses' */
-    if (_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data"))
+    if (_nm_setting_use_legacy_property(setting, connection_dict, "addresses", "address-data")) {
+        *out_is_modified = FALSE;
         return TRUE;
+    }
 
     addrs = nm_utils_ip_addresses_from_variant(value, AF_INET6);
     g_object_set(setting, NM_SETTING_IP_CONFIG_ADDRESSES, addrs, NULL);
@@ -456,12 +446,7 @@ ip6_address_data_set(NMSetting *         setting,
 }
 
 static GVariant *
-ip6_routes_get(const NMSettInfoSetting *               sett_info,
-               guint                                   property_idx,
-               NMConnection *                          connection,
-               NMSetting *                             setting,
-               NMConnectionSerializationFlags          flags,
-               const NMConnectionSerializationOptions *options)
+ip6_routes_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *routes = NULL;
 
@@ -470,33 +455,23 @@ ip6_routes_get(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-ip6_routes_set(NMSetting *         setting,
-               GVariant *          connection_dict,
-               const char *        property,
-               GVariant *          value,
-               NMSettingParseFlags parse_flags,
-               GError **           error)
+ip6_routes_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GPtrArray *routes;
 
-    /* FIXME: properly handle errors */
-
-    if (!_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data"))
+    if (!_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data")) {
+        *out_is_modified = FALSE;
         return TRUE;
+    }
 
     routes = nm_utils_ip6_routes_from_variant(value);
-    g_object_set(setting, property, routes, NULL);
+    g_object_set(setting, property_info->name, routes, NULL);
     g_ptr_array_unref(routes);
     return TRUE;
 }
 
 static GVariant *
-ip6_route_data_get(const NMSettInfoSetting *               sett_info,
-                   guint                                   property_idx,
-                   NMConnection *                          connection,
-                   NMSetting *                             setting,
-                   NMConnectionSerializationFlags          flags,
-                   const NMConnectionSerializationOptions *options)
+ip6_route_data_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *routes = NULL;
 
@@ -508,20 +483,15 @@ ip6_route_data_get(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-ip6_route_data_set(NMSetting *         setting,
-                   GVariant *          connection_dict,
-                   const char *        property,
-                   GVariant *          value,
-                   NMSettingParseFlags parse_flags,
-                   GError **           error)
+ip6_route_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GPtrArray *routes;
 
-    /* FIXME: properly handle errors */
-
     /* Ignore 'route-data' if we're going to process 'routes' */
-    if (_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data"))
+    if (_nm_setting_use_legacy_property(setting, connection_dict, "routes", "route-data")) {
+        *out_is_modified = FALSE;
         return TRUE;
+    }
 
     routes = nm_utils_ip_routes_from_variant(value, AF_INET6);
     g_object_set(setting, NM_SETTING_IP_CONFIG_ROUTES, routes, NULL);
@@ -594,6 +564,8 @@ nm_setting_ip6_config_init(NMSettingIP6Config *setting)
 {
     NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE(setting);
 
+    _nm_setting_ip_config_private_init(setting, &priv->parent);
+
     priv->ip6_privacy   = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
     priv->addr_gen_mode = NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_STABLE_PRIVACY;
 }
@@ -612,32 +584,22 @@ nm_setting_ip6_config_new(void)
 }
 
 static void
-finalize(GObject *object)
-{
-    NMSettingIP6Config *       self = NM_SETTING_IP6_CONFIG(object);
-    NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE(self);
-
-    g_free(priv->token);
-    g_free(priv->dhcp_duid);
-
-    G_OBJECT_CLASS(nm_setting_ip6_config_parent_class)->finalize(object);
-}
-
-static void
 nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *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_ip_config();
+    GObjectClass *          object_class            = G_OBJECT_CLASS(klass);
+    NMSettingClass *        setting_class           = NM_SETTING_CLASS(klass);
+    NMSettingIPConfigClass *setting_ip_config_class = NM_SETTING_IP_CONFIG_CLASS(klass);
+    GArray *properties_override = _nm_sett_info_property_override_create_array_ip_config(AF_INET6);
 
     g_type_class_add_private(klass, sizeof(NMSettingIP6ConfigPrivate));
 
     object_class->get_property = get_property;
     object_class->set_property = set_property;
-    object_class->finalize     = finalize;
 
     setting_class->verify = verify;
 
+    setting_ip_config_class->private_offset = g_type_class_get_instance_private_offset(klass);
+
     /* ---ifcfg-rh---
      * property: method
      * variable: IPV6INIT, IPV6FORWARDING, IPV6_AUTOCONF, DHCPV6C, IPV6_DISABLED
@@ -914,12 +876,13 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
      * example: IPV6_TOKEN=::53
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_IP6_CONFIG_TOKEN,
-                                       PROP_TOKEN,
-                                       NM_SETTING_PARAM_INFERRABLE,
-                                       nm_setting_ip6_config_get_token);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_IP6_CONFIG_TOKEN,
+                                              PROP_TOKEN,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingIP6ConfigPrivate,
+                                              token);
 
     /**
      * NMSettingIP6Config:ra-timeout:
@@ -994,12 +957,13 @@ 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---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_IP6_CONFIG_DHCP_DUID,
-                                       PROP_DHCP_DUID,
-                                       NM_SETTING_PARAM_NONE,
-                                       nm_setting_ip6_config_get_dhcp_duid);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_IP6_CONFIG_DHCP_DUID,
+                                              PROP_DHCP_DUID,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingIP6ConfigPrivate,
+                                              dhcp_duid);
 
     /* IP6-specific property overrides */
 
@@ -1012,9 +976,12 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
     _nm_properties_override_gobj(
         properties_override,
         g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_DNS),
-        NM_SETT_INFO_PROPERT_TYPE_GPROP(NM_G_VARIANT_TYPE("aay"),
-                                        .gprop_from_dbus_fcn = ip6_dns_from_dbus, ),
-        .to_dbus_data.gprop_to_dbus_fcn = ip6_dns_to_dbus);
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aay"),
+                                       .compare_fcn = _nm_setting_property_compare_fcn_default,
+                                       .to_dbus_fcn = ip6_dns_to_dbus,
+                                       .typdata_from_dbus.gprop_fcn = ip6_dns_from_dbus,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                       .from_dbus_is_full = TRUE));
 
     /* ---dbus---
      * property: addresses
@@ -1044,6 +1011,7 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
         g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_ADDRESSES),
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("a(ayuay)"),
                                        .to_dbus_fcn   = ip6_addresses_get,
+                                       .compare_fcn   = _nm_setting_ip_config_compare_fcn_addresses,
                                        .from_dbus_fcn = ip6_addresses_set, ));
 
     /* ---dbus---
@@ -1060,6 +1028,7 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
         "address-data",
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
                                        .to_dbus_fcn   = ip6_address_data_get,
+                                       .compare_fcn   = _nm_setting_property_compare_fcn_ignore,
                                        .from_dbus_fcn = ip6_address_data_set, ));
 
     /* ---dbus---
@@ -1081,6 +1050,7 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
         g_object_class_find_property(G_OBJECT_CLASS(setting_class), NM_SETTING_IP_CONFIG_ROUTES),
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("a(ayuayu)"),
                                        .to_dbus_fcn   = ip6_routes_get,
+                                       .compare_fcn   = _nm_setting_ip_config_compare_fcn_routes,
                                        .from_dbus_fcn = ip6_routes_set, ));
 
     /* ---dbus---
@@ -1101,12 +1071,14 @@ nm_setting_ip6_config_class_init(NMSettingIP6ConfigClass *klass)
         "route-data",
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
                                        .to_dbus_fcn   = ip6_route_data_get,
+                                       .compare_fcn   = _nm_setting_property_compare_fcn_ignore,
                                        .from_dbus_fcn = 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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_IP6_CONFIG,
+                             NULL,
+                             properties_override,
+                             setting_ip_config_class->private_offset);
 }
diff --git a/src/libnm-core-impl/nm-setting-macsec.c b/src/libnm-core-impl/nm-setting-macsec.c
index 55024bb4..1a9e9bd8 100644
--- a/src/libnm-core-impl/nm-setting-macsec.c
+++ b/src/libnm-core-impl/nm-setting-macsec.c
@@ -45,8 +45,8 @@ typedef struct {
     NMSettingMacsecMode       mode;
     NMSettingSecretFlags      mka_cak_flags;
     NMSettingMacsecValidation validation;
-    bool                      encrypt : 1;
-    bool                      send_sci : 1;
+    bool                      encrypt;
+    bool                      send_sci;
 } NMSettingMacsecPrivate;
 
 /**
@@ -492,9 +492,7 @@ nm_setting_macsec_init(NMSettingMacsec *self)
     NMSettingMacsecPrivate *priv = NM_SETTING_MACSEC_GET_PRIVATE(self);
 
     nm_assert(priv->mode == NM_SETTING_MACSEC_MODE_PSK);
-    priv->encrypt    = TRUE;
     priv->port       = 1;
-    priv->send_sci   = TRUE;
     priv->validation = NM_SETTING_MACSEC_VALIDATION_STRICT;
 }
 
@@ -583,13 +581,14 @@ nm_setting_macsec_class_init(NMSettingMacsecClass *klass)
      *
      * Since: 1.6
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_MACSEC_ENCRYPT,
-                                        PROP_ENCRYPT,
-                                        TRUE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_macsec_get_encrypt);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_MACSEC_ENCRYPT,
+                                               PROP_ENCRYPT,
+                                               TRUE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingMacsecPrivate,
+                                               encrypt);
 
     /**
      * NMSettingMacsec:mka-cak:
@@ -676,18 +675,20 @@ nm_setting_macsec_class_init(NMSettingMacsecClass *klass)
      *
      * Since: 1.12
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_MACSEC_SEND_SCI,
-                                        PROP_SEND_SCI,
-                                        TRUE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_macsec_get_send_sci);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_MACSEC_SEND_SCI,
+                                               PROP_SEND_SCI,
+                                               TRUE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingMacsecPrivate,
+                                               send_sci);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_MACSEC,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_MACSEC,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-macvlan.c b/src/libnm-core-impl/nm-setting-macvlan.c
index 9cb2b262..32b30c2c 100644
--- a/src/libnm-core-impl/nm-setting-macvlan.c
+++ b/src/libnm-core-impl/nm-setting-macvlan.c
@@ -30,8 +30,8 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT, PROP_MODE, PROP_PROMISCUOUS, PROP
 typedef struct {
     char *               parent;
     NMSettingMacvlanMode mode;
-    bool                 promiscuous : 1;
-    bool                 tap : 1;
+    bool                 promiscuous;
+    bool                 tap;
 } NMSettingMacvlanPrivate;
 
 /**
@@ -234,11 +234,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
 
 static void
 nm_setting_macvlan_init(NMSettingMacvlan *self)
-{
-    NMSettingMacvlanPrivate *priv = NM_SETTING_MACVLAN_GET_PRIVATE(self);
-
-    priv->promiscuous = TRUE;
-}
+{}
 
 /**
  * nm_setting_macvlan_new:
@@ -322,13 +318,14 @@ nm_setting_macvlan_class_init(NMSettingMacvlanClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_MACVLAN_PROMISCUOUS,
-                                        PROP_PROMISCUOUS,
-                                        TRUE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_macvlan_get_promiscuous);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_MACVLAN_PROMISCUOUS,
+                                               PROP_PROMISCUOUS,
+                                               TRUE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingMacvlanPrivate,
+                                               promiscuous);
 
     /**
      * NMSettingMacvlan:tap:
@@ -337,18 +334,20 @@ nm_setting_macvlan_class_init(NMSettingMacvlanClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_MACVLAN_TAP,
-                                        PROP_TAP,
-                                        FALSE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_macvlan_get_tap);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_MACVLAN_TAP,
+                                               PROP_TAP,
+                                               FALSE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingMacvlanPrivate,
+                                               tap);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_MACVLAN,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_MACVLAN,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-match.c b/src/libnm-core-impl/nm-setting-match.c
index e4f7c1a8..20c6b798 100644
--- a/src/libnm-core-impl/nm-setting-match.c
+++ b/src/libnm-core-impl/nm-setting-match.c
@@ -901,5 +901,5 @@ nm_setting_match_class_init(NMSettingMatchClass *klass)
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_MATCH);
+    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_MATCH, NULL, NULL, 0);
 }
diff --git a/src/libnm-core-impl/nm-setting-olpc-mesh.c b/src/libnm-core-impl/nm-setting-olpc-mesh.c
index 103f6195..0da1d5b4 100644
--- a/src/libnm-core-impl/nm-setting-olpc-mesh.c
+++ b/src/libnm-core-impl/nm-setting-olpc-mesh.c
@@ -148,53 +148,6 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    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;
-    }
-}
-
-static void
-set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
-{
-    NMSettingOlpcMeshPrivate *priv = NM_SETTING_OLPC_MESH_GET_PRIVATE(object);
-
-    switch (prop_id) {
-    case PROP_SSID:
-        if (priv->ssid)
-            g_bytes_unref(priv->ssid);
-        priv->ssid = g_value_dup_boxed(value);
-        break;
-    case PROP_CHANNEL:
-        priv->channel = g_value_get_uint(value);
-        break;
-    case PROP_DHCP_ANYCAST_ADDRESS:
-        g_free(priv->dhcp_anycast_addr);
-        priv->dhcp_anycast_addr = g_value_dup_string(value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static void
 nm_setting_olpc_mesh_init(NMSettingOlpcMesh *setting)
 {}
 
@@ -212,18 +165,6 @@ nm_setting_olpc_mesh_new(void)
 }
 
 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
 nm_setting_olpc_mesh_class_init(NMSettingOlpcMeshClass *klass)
 {
     GObjectClass *  object_class        = G_OBJECT_CLASS(klass);
@@ -232,9 +173,8 @@ nm_setting_olpc_mesh_class_init(NMSettingOlpcMeshClass *klass)
 
     g_type_class_add_private(klass, sizeof(NMSettingOlpcMeshPrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-    object_class->finalize     = finalize;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify = verify;
 
@@ -243,26 +183,29 @@ nm_setting_olpc_mesh_class_init(NMSettingOlpcMeshClass *klass)
      *
      * SSID of the mesh network to join.
      **/
-    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);
+    _nm_setting_property_define_direct_bytes(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_OLPC_MESH_SSID,
+                                             PROP_SSID,
+                                             NM_SETTING_PARAM_INFERRABLE,
+                                             NMSettingOlpcMeshPrivate,
+                                             ssid);
 
     /**
      * NMSettingOlpcMesh:channel:
      *
      * Channel on which the mesh network to join is located.
      **/
-    obj_properties[PROP_CHANNEL] =
-        g_param_spec_uint(NM_SETTING_OLPC_MESH_CHANNEL,
-                          "",
-                          "",
-                          0,
-                          G_MAXUINT32,
-                          0,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_OLPC_MESH_CHANNEL,
+                                              PROP_CHANNEL,
+                                              0,
+                                              G_MAXUINT32,
+                                              0,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingOlpcMeshPrivate,
+                                              channel);
 
     /**
      * NMSettingOlpcMesh:dhcp-anycast-address:
@@ -273,20 +216,20 @@ nm_setting_olpc_mesh_class_init(NMSettingOlpcMeshClass *klass)
      *
      * This is currently only implemented by dhclient DHCP plugin.
      **/
-    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);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_DHCP_ANYCAST_ADDRESS],
-                                 &nm_sett_info_propert_type_mac_address);
+    _nm_setting_property_define_direct_mac_address(properties_override,
+                                                   obj_properties,
+                                                   NM_SETTING_OLPC_MESH_DHCP_ANYCAST_ADDRESS,
+                                                   PROP_DHCP_ANYCAST_ADDRESS,
+                                                   NM_SETTING_PARAM_NONE,
+                                                   NMSettingOlpcMeshPrivate,
+                                                   dhcp_anycast_addr,
+                                                   .direct_set_string_mac_address_len = ETH_ALEN);
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_OLPC_MESH,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-ovs-bridge.c b/src/libnm-core-impl/nm-setting-ovs-bridge.c
index 22738d20..711d5461 100644
--- a/src/libnm-core-impl/nm-setting-ovs-bridge.c
+++ b/src/libnm-core-impl/nm-setting-ovs-bridge.c
@@ -37,9 +37,9 @@ struct _NMSettingOvsBridge {
 
     char *fail_mode;
     char *datapath_type;
-    bool  mcast_snooping_enable : 1;
-    bool  rstp_enable : 1;
-    bool  stp_enable : 1;
+    bool  mcast_snooping_enable;
+    bool  rstp_enable;
+    bool  stp_enable;
 };
 
 struct _NMSettingOvsBridgeClass {
@@ -320,13 +320,14 @@ nm_setting_ovs_bridge_class_init(NMSettingOvsBridgeClass *klass)
      *
      * Since: 1.10
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE,
-                                        PROP_MCAST_SNOOPING_ENABLE,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ovs_bridge_get_mcast_snooping_enable);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_OVS_BRIDGE_MCAST_SNOOPING_ENABLE,
+                                               PROP_MCAST_SNOOPING_ENABLE,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingOvsBridge,
+                                               mcast_snooping_enable);
 
     /**
      * NMSettingOvsBridge:rstp-enable:
@@ -335,13 +336,14 @@ nm_setting_ovs_bridge_class_init(NMSettingOvsBridgeClass *klass)
      *
      * Since: 1.10
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_OVS_BRIDGE_RSTP_ENABLE,
-                                        PROP_RSTP_ENABLE,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ovs_bridge_get_rstp_enable);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_OVS_BRIDGE_RSTP_ENABLE,
+                                               PROP_RSTP_ENABLE,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingOvsBridge,
+                                               rstp_enable);
 
     /**
      * NMSettingOvsBridge:stp-enable:
@@ -350,13 +352,14 @@ nm_setting_ovs_bridge_class_init(NMSettingOvsBridgeClass *klass)
      *
      * Since: 1.10
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_OVS_BRIDGE_STP_ENABLE,
-                                        PROP_STP_ENABLE,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ovs_bridge_get_stp_enable);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_OVS_BRIDGE_STP_ENABLE,
+                                               PROP_STP_ENABLE,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingOvsBridge,
+                                               stp_enable);
 
     /**
      * NMSettingOvsBridge:datapath-type:
@@ -374,8 +377,9 @@ nm_setting_ovs_bridge_class_init(NMSettingOvsBridgeClass *klass)
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_OVS_BRIDGE,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_OVS_BRIDGE,
+                             NULL,
+                             properties_override,
+                             0);
 }
diff --git a/src/libnm-core-impl/nm-setting-ovs-dpdk.c b/src/libnm-core-impl/nm-setting-ovs-dpdk.c
index 957b0f57..3107531b 100644
--- a/src/libnm-core-impl/nm-setting-ovs-dpdk.c
+++ b/src/libnm-core-impl/nm-setting-ovs-dpdk.c
@@ -148,5 +148,5 @@ nm_setting_ovs_dpdk_class_init(NMSettingOvsDpdkClass *klass)
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_OVS_DPDK);
+    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_OVS_DPDK, NULL, NULL, 0);
 }
diff --git a/src/libnm-core-impl/nm-setting-ovs-external-ids.c b/src/libnm-core-impl/nm-setting-ovs-external-ids.c
index b73c1af4..ea4004f0 100644
--- a/src/libnm-core-impl/nm-setting-ovs-external-ids.c
+++ b/src/libnm-core-impl/nm-setting-ovs-external-ids.c
@@ -211,7 +211,7 @@ nm_setting_ovs_external_ids_get_data_keys(NMSettingOvsExternalIDs *setting, guin
         return priv->data_keys;
     }
 
-    priv->data_keys = nm_utils_strdict_get_keys(priv->data, TRUE, out_len);
+    priv->data_keys = nm_strdict_get_keys(priv->data, TRUE, out_len);
 
     /* don't return %NULL, but hijack the @data_keys fields as a pseudo
      * empty strv array. */
@@ -387,31 +387,20 @@ connection_type_is_good:
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_data(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
     NMSettingOvsExternalIDsPrivate *priv;
     NMSettingOvsExternalIDsPrivate *pri2;
 
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_OVS_EXTERNAL_IDS_DATA)) {
-        if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
-            return NM_TERNARY_DEFAULT;
+    if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+        return NM_TERNARY_DEFAULT;
 
-        if (!set_b)
-            return TRUE;
+    if (!set_b)
+        return TRUE;
 
-        priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(NM_SETTING_OVS_EXTERNAL_IDS(set_a));
-        pri2 = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(NM_SETTING_OVS_EXTERNAL_IDS(set_b));
-        return nm_utils_hashtable_equal(priv->data, pri2->data, TRUE, g_str_equal);
-    }
-
-    return NM_SETTING_CLASS(nm_setting_ovs_external_ids_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    priv = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(NM_SETTING_OVS_EXTERNAL_IDS(set_a));
+    pri2 = NM_SETTING_OVS_EXTERNAL_IDS_GET_PRIVATE(NM_SETTING_OVS_EXTERNAL_IDS(set_b));
+    return nm_utils_hashtable_equal(priv->data, pri2->data, TRUE, g_str_equal);
 }
 
 /*****************************************************************************/
@@ -523,8 +512,7 @@ nm_setting_ovs_external_ids_class_init(NMSettingOvsExternalIDsClass *klass)
     object_class->set_property = set_property;
     object_class->finalize     = finalize;
 
-    setting_class->compare_property = compare_property;
-    setting_class->verify           = verify;
+    setting_class->verify = verify;
 
     /**
      * NMSettingOvsExternalIDs:data: (type GHashTable(utf8,utf8))
@@ -538,14 +526,22 @@ nm_setting_ovs_external_ids_class_init(NMSettingOvsExternalIDsClass *klass)
                                                    "",
                                                    G_TYPE_HASH_TABLE,
                                                    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_DATA],
-                                 &nm_sett_info_propert_type_strdict);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_DATA],
+        NM_SETT_INFO_PROPERT_TYPE_GPROP(NM_G_VARIANT_TYPE("a{ss}"),
+                                        .typdata_from_dbus.gprop_fcn = _nm_utils_strdict_from_dbus,
+                                        .typdata_to_dbus.gprop_type =
+                                            NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT,
+                                        .compare_fcn   = compare_fcn_data,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                        .from_dbus_is_full = TRUE));
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_OVS_EXTERNAL_IDS,
+                             NULL,
+                             properties_override,
+                             G_STRUCT_OFFSET(NMSettingOvsExternalIDs, _priv));
 }
diff --git a/src/libnm-core-impl/nm-setting-ovs-interface.c b/src/libnm-core-impl/nm-setting-ovs-interface.c
index 1fde9605..58a01fcc 100644
--- a/src/libnm-core-impl/nm-setting-ovs-interface.c
+++ b/src/libnm-core-impl/nm-setting-ovs-interface.c
@@ -423,5 +423,5 @@ nm_setting_ovs_interface_class_init(NMSettingOvsInterfaceClass *klass)
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_OVS_INTERFACE);
+    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_OVS_INTERFACE, NULL, NULL, 0);
 }
diff --git a/src/libnm-core-impl/nm-setting-ovs-patch.c b/src/libnm-core-impl/nm-setting-ovs-patch.c
index a226a26d..578f5ee2 100644
--- a/src/libnm-core-impl/nm-setting-ovs-patch.c
+++ b/src/libnm-core-impl/nm-setting-ovs-patch.c
@@ -184,5 +184,5 @@ nm_setting_ovs_patch_class_init(NMSettingOvsPatchClass *klass)
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_OVS_PATCH);
+    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_OVS_PATCH, NULL, NULL, 0);
 }
diff --git a/src/libnm-core-impl/nm-setting-ovs-port.c b/src/libnm-core-impl/nm-setting-ovs-port.c
index 821f29ab..fc593268 100644
--- a/src/libnm-core-impl/nm-setting-ovs-port.c
+++ b/src/libnm-core-impl/nm-setting-ovs-port.c
@@ -36,12 +36,12 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_VLAN_MODE,
 struct _NMSettingOvsPort {
     NMSetting parent;
 
-    char *vlan_mode;
-    char *lacp;
-    char *bond_mode;
-    guint tag;
-    guint bond_updelay;
-    guint bond_downdelay;
+    char *  vlan_mode;
+    char *  lacp;
+    char *  bond_mode;
+    guint32 tag;
+    guint32 bond_updelay;
+    guint32 bond_downdelay;
 };
 
 struct _NMSettingOvsPortClass {
@@ -263,71 +263,6 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    NMSettingOvsPort *self = NM_SETTING_OVS_PORT(object);
-
-    switch (prop_id) {
-    case PROP_VLAN_MODE:
-        g_value_set_string(value, self->vlan_mode);
-        break;
-    case PROP_TAG:
-        g_value_set_uint(value, self->tag);
-        break;
-    case PROP_LACP:
-        g_value_set_string(value, self->lacp);
-        break;
-    case PROP_BOND_MODE:
-        g_value_set_string(value, self->bond_mode);
-        break;
-    case PROP_BOND_UPDELAY:
-        g_value_set_uint(value, self->bond_updelay);
-        break;
-    case PROP_BOND_DOWNDELAY:
-        g_value_set_uint(value, self->bond_downdelay);
-        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)
-{
-    NMSettingOvsPort *self = NM_SETTING_OVS_PORT(object);
-
-    switch (prop_id) {
-    case PROP_VLAN_MODE:
-        g_free(self->vlan_mode);
-        self->vlan_mode = g_value_dup_string(value);
-        break;
-    case PROP_TAG:
-        self->tag = g_value_get_uint(value);
-        break;
-    case PROP_LACP:
-        g_free(self->lacp);
-        self->lacp = g_value_dup_string(value);
-        break;
-    case PROP_BOND_MODE:
-        g_free(self->bond_mode);
-        self->bond_mode = g_value_dup_string(value);
-        break;
-    case PROP_BOND_UPDELAY:
-        self->bond_updelay = g_value_get_uint(value);
-        break;
-    case PROP_BOND_DOWNDELAY:
-        self->bond_downdelay = g_value_get_uint(value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static void
 nm_setting_ovs_port_init(NMSettingOvsPort *self)
 {}
 
@@ -347,26 +282,14 @@ nm_setting_ovs_port_new(void)
 }
 
 static void
-finalize(GObject *object)
-{
-    NMSettingOvsPort *self = NM_SETTING_OVS_PORT(object);
-
-    g_free(self->vlan_mode);
-    g_free(self->lacp);
-    g_free(self->bond_mode);
-
-    G_OBJECT_CLASS(nm_setting_ovs_port_parent_class)->finalize(object);
-}
-
-static void
 nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass)
 {
-    GObjectClass *  object_class  = G_OBJECT_CLASS(klass);
-    NMSettingClass *setting_class = NM_SETTING_CLASS(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;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify = verify;
 
@@ -378,12 +301,13 @@ nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass)
      *
      * Since: 1.10
      **/
-    obj_properties[PROP_VLAN_MODE] = g_param_spec_string(
-        NM_SETTING_OVS_PORT_VLAN_MODE,
-        "",
-        "",
-        NULL,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_OVS_PORT_VLAN_MODE,
+                                              PROP_VLAN_MODE,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingOvsPort,
+                                              vlan_mode);
 
     /**
      * NMSettingOvsPort:tag:
@@ -392,14 +316,16 @@ nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass)
      *
      * Since: 1.10
      **/
-    obj_properties[PROP_TAG] =
-        g_param_spec_uint(NM_SETTING_OVS_PORT_TAG,
-                          "",
-                          "",
-                          0,
-                          4095,
-                          0,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_OVS_PORT_TAG,
+                                              PROP_TAG,
+                                              0,
+                                              4095,
+                                              0,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingOvsPort,
+                                              tag);
 
     /**
      * NMSettingOvsPort:lacp:
@@ -408,12 +334,13 @@ nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass)
      *
      * Since: 1.10
      **/
-    obj_properties[PROP_LACP] = g_param_spec_string(NM_SETTING_OVS_PORT_LACP,
-                                                    "",
-                                                    "",
-                                                    NULL,
-                                                    G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE
-                                                        | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_OVS_PORT_LACP,
+                                              PROP_LACP,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingOvsPort,
+                                              lacp);
 
     /**
      * NMSettingOvsPort:bond-mode:
@@ -422,12 +349,13 @@ nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass)
      *
      * Since: 1.10
      **/
-    obj_properties[PROP_BOND_MODE] = g_param_spec_string(
-        NM_SETTING_OVS_PORT_BOND_MODE,
-        "",
-        "",
-        NULL,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_OVS_PORT_BOND_MODE,
+                                              PROP_BOND_MODE,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingOvsPort,
+                                              bond_mode);
 
     /**
      * NMSettingOvsPort:bond-updelay:
@@ -436,14 +364,16 @@ nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass)
      *
      * Since: 1.10
      **/
-    obj_properties[PROP_BOND_UPDELAY] =
-        g_param_spec_uint(NM_SETTING_OVS_PORT_BOND_UPDELAY,
-                          "",
-                          "",
-                          0,
-                          G_MAXUINT,
-                          0,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_OVS_PORT_BOND_UPDELAY,
+                                              PROP_BOND_UPDELAY,
+                                              0,
+                                              G_MAXUINT32,
+                                              0,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingOvsPort,
+                                              bond_updelay);
 
     /**
      * NMSettingOvsPort:bond-downdelay:
@@ -452,16 +382,22 @@ nm_setting_ovs_port_class_init(NMSettingOvsPortClass *klass)
      *
      * Since: 1.10
      **/
-    obj_properties[PROP_BOND_DOWNDELAY] =
-        g_param_spec_uint(NM_SETTING_OVS_PORT_BOND_DOWNDELAY,
-                          "",
-                          "",
-                          0,
-                          G_MAXUINT,
-                          0,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_OVS_PORT_BOND_DOWNDELAY,
+                                              PROP_BOND_DOWNDELAY,
+                                              0,
+                                              G_MAXUINT32,
+                                              0,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingOvsPort,
+                                              bond_downdelay);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_OVS_PORT);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_OVS_PORT,
+                             NULL,
+                             properties_override,
+                             0);
 }
diff --git a/src/libnm-core-impl/nm-setting-ppp.c b/src/libnm-core-impl/nm-setting-ppp.c
index cc722c44..e4e4951e 100644
--- a/src/libnm-core-impl/nm-setting-ppp.c
+++ b/src/libnm-core-impl/nm-setting-ppp.c
@@ -47,19 +47,19 @@ typedef struct {
     guint32 mtu;
     guint32 lcp_echo_failure;
     guint32 lcp_echo_interval;
-    bool    noauth : 1;
-    bool    refuse_eap : 1;
-    bool    refuse_pap : 1;
-    bool    refuse_chap : 1;
-    bool    refuse_mschap : 1;
-    bool    refuse_mschapv2 : 1;
-    bool    nobsdcomp : 1;
-    bool    nodeflate : 1;
-    bool    no_vj_comp : 1;
-    bool    require_mppe : 1;
-    bool    require_mppe_128 : 1;
-    bool    mppe_stateful : 1;
-    bool    crtscts : 1;
+    bool    noauth;
+    bool    refuse_eap;
+    bool    refuse_pap;
+    bool    refuse_chap;
+    bool    refuse_mschap;
+    bool    refuse_mschapv2;
+    bool    nobsdcomp;
+    bool    nodeflate;
+    bool    no_vj_comp;
+    bool    require_mppe;
+    bool    require_mppe_128;
+    bool    mppe_stateful;
+    bool    crtscts;
 } NMSettingPppPrivate;
 
 /**
@@ -337,6 +337,8 @@ nm_setting_ppp_get_lcp_echo_interval(NMSettingPpp *setting)
     return NM_SETTING_PPP_GET_PRIVATE(setting)->lcp_echo_interval;
 }
 
+/*****************************************************************************/
+
 static gboolean
 verify(NMSetting *setting, NMConnection *connection, GError **error)
 {
@@ -377,146 +379,8 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    NMSettingPpp *setting = NM_SETTING_PPP(object);
-
-    switch (prop_id) {
-    case PROP_NOAUTH:
-        g_value_set_boolean(value, nm_setting_ppp_get_noauth(setting));
-        break;
-    case PROP_REFUSE_EAP:
-        g_value_set_boolean(value, nm_setting_ppp_get_refuse_eap(setting));
-        break;
-    case PROP_REFUSE_PAP:
-        g_value_set_boolean(value, nm_setting_ppp_get_refuse_pap(setting));
-        break;
-    case PROP_REFUSE_CHAP:
-        g_value_set_boolean(value, nm_setting_ppp_get_refuse_chap(setting));
-        break;
-    case PROP_REFUSE_MSCHAP:
-        g_value_set_boolean(value, nm_setting_ppp_get_refuse_mschap(setting));
-        break;
-    case PROP_REFUSE_MSCHAPV2:
-        g_value_set_boolean(value, nm_setting_ppp_get_refuse_mschapv2(setting));
-        break;
-    case PROP_NOBSDCOMP:
-        g_value_set_boolean(value, nm_setting_ppp_get_nobsdcomp(setting));
-        break;
-    case PROP_NODEFLATE:
-        g_value_set_boolean(value, nm_setting_ppp_get_nodeflate(setting));
-        break;
-    case PROP_NO_VJ_COMP:
-        g_value_set_boolean(value, nm_setting_ppp_get_no_vj_comp(setting));
-        break;
-    case PROP_REQUIRE_MPPE:
-        g_value_set_boolean(value, nm_setting_ppp_get_require_mppe(setting));
-        break;
-    case PROP_REQUIRE_MPPE_128:
-        g_value_set_boolean(value, nm_setting_ppp_get_require_mppe_128(setting));
-        break;
-    case PROP_MPPE_STATEFUL:
-        g_value_set_boolean(value, nm_setting_ppp_get_mppe_stateful(setting));
-        break;
-    case PROP_CRTSCTS:
-        g_value_set_boolean(value, nm_setting_ppp_get_crtscts(setting));
-        break;
-    case PROP_BAUD:
-        g_value_set_uint(value, nm_setting_ppp_get_baud(setting));
-        break;
-    case PROP_MRU:
-        g_value_set_uint(value, nm_setting_ppp_get_mru(setting));
-        break;
-    case PROP_MTU:
-        g_value_set_uint(value, nm_setting_ppp_get_mtu(setting));
-        break;
-    case PROP_LCP_ECHO_FAILURE:
-        g_value_set_uint(value, nm_setting_ppp_get_lcp_echo_failure(setting));
-        break;
-    case PROP_LCP_ECHO_INTERVAL:
-        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);
-        break;
-    }
-}
-
-static void
-set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
-{
-    NMSettingPppPrivate *priv = NM_SETTING_PPP_GET_PRIVATE(object);
-
-    switch (prop_id) {
-    case PROP_NOAUTH:
-        priv->noauth = g_value_get_boolean(value);
-        break;
-    case PROP_REFUSE_EAP:
-        priv->refuse_eap = g_value_get_boolean(value);
-        break;
-    case PROP_REFUSE_PAP:
-        priv->refuse_pap = g_value_get_boolean(value);
-        break;
-    case PROP_REFUSE_CHAP:
-        priv->refuse_chap = g_value_get_boolean(value);
-        break;
-    case PROP_REFUSE_MSCHAP:
-        priv->refuse_mschap = g_value_get_boolean(value);
-        break;
-    case PROP_REFUSE_MSCHAPV2:
-        priv->refuse_mschapv2 = g_value_get_boolean(value);
-        break;
-    case PROP_NOBSDCOMP:
-        priv->nobsdcomp = g_value_get_boolean(value);
-        break;
-    case PROP_NODEFLATE:
-        priv->nodeflate = g_value_get_boolean(value);
-        break;
-    case PROP_NO_VJ_COMP:
-        priv->no_vj_comp = g_value_get_boolean(value);
-        break;
-    case PROP_REQUIRE_MPPE:
-        priv->require_mppe = g_value_get_boolean(value);
-        break;
-    case PROP_REQUIRE_MPPE_128:
-        priv->require_mppe_128 = g_value_get_boolean(value);
-        break;
-    case PROP_MPPE_STATEFUL:
-        priv->mppe_stateful = g_value_get_boolean(value);
-        break;
-    case PROP_CRTSCTS:
-        priv->crtscts = g_value_get_boolean(value);
-        break;
-    case PROP_BAUD:
-        priv->baud = g_value_get_uint(value);
-        break;
-    case PROP_MRU:
-        priv->mru = g_value_get_uint(value);
-        break;
-    case PROP_MTU:
-        priv->mtu = g_value_get_uint(value);
-        break;
-    case PROP_LCP_ECHO_FAILURE:
-        priv->lcp_echo_failure = g_value_get_uint(value);
-        break;
-    case PROP_LCP_ECHO_INTERVAL:
-        priv->lcp_echo_interval = g_value_get_uint(value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static void
 nm_setting_ppp_init(NMSettingPpp *self)
-{
-    NMSettingPppPrivate *priv = NM_SETTING_PPP_GET_PRIVATE(self);
-
-    priv->noauth = TRUE;
-}
+{}
 
 /**
  * nm_setting_ppp_new:
@@ -540,8 +404,8 @@ nm_setting_ppp_class_init(NMSettingPppClass *klass)
 
     g_type_class_add_private(klass, sizeof(NMSettingPppPrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify = verify;
 
@@ -552,117 +416,126 @@ 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.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_NOAUTH,
-                                        PROP_NOAUTH,
-                                        TRUE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ppp_get_noauth);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_NOAUTH,
+                                               PROP_NOAUTH,
+                                               TRUE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingPppPrivate,
+                                               noauth);
 
     /**
      * NMSettingPpp:refuse-eap:
      *
      * If %TRUE, the EAP authentication method will not be used.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_REFUSE_EAP,
-                                        PROP_REFUSE_EAP,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ppp_get_refuse_eap);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_REFUSE_EAP,
+                                               PROP_REFUSE_EAP,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingPppPrivate,
+                                               refuse_eap);
 
     /**
      * NMSettingPpp:refuse-pap:
      *
      * If %TRUE, the PAP authentication method will not be used.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_REFUSE_PAP,
-                                        PROP_REFUSE_PAP,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ppp_get_refuse_pap);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_REFUSE_PAP,
+                                               PROP_REFUSE_PAP,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingPppPrivate,
+                                               refuse_pap);
 
     /**
      * NMSettingPpp:refuse-chap:
      *
      * If %TRUE, the CHAP authentication method will not be used.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_REFUSE_CHAP,
-                                        PROP_REFUSE_CHAP,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ppp_get_refuse_chap);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_REFUSE_CHAP,
+                                               PROP_REFUSE_CHAP,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingPppPrivate,
+                                               refuse_chap);
 
     /**
      * NMSettingPpp:refuse-mschap:
      *
      * If %TRUE, the MSCHAP authentication method will not be used.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_REFUSE_MSCHAP,
-                                        PROP_REFUSE_MSCHAP,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ppp_get_refuse_mschap);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_REFUSE_MSCHAP,
+                                               PROP_REFUSE_MSCHAP,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingPppPrivate,
+                                               refuse_mschap);
 
     /**
      * NMSettingPpp:refuse-mschapv2:
      *
      * If %TRUE, the MSCHAPv2 authentication method will not be used.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_REFUSE_MSCHAPV2,
-                                        PROP_REFUSE_MSCHAPV2,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ppp_get_refuse_mschapv2);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_REFUSE_MSCHAPV2,
+                                               PROP_REFUSE_MSCHAPV2,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingPppPrivate,
+                                               refuse_mschapv2);
 
     /**
      * NMSettingPpp:nobsdcomp:
      *
      * If %TRUE, BSD compression will not be requested.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_NOBSDCOMP,
-                                        PROP_NOBSDCOMP,
-                                        FALSE,
-                                        NM_SETTING_PARAM_FUZZY_IGNORE,
-                                        nm_setting_ppp_get_nobsdcomp);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_NOBSDCOMP,
+                                               PROP_NOBSDCOMP,
+                                               FALSE,
+                                               NM_SETTING_PARAM_FUZZY_IGNORE,
+                                               NMSettingPppPrivate,
+                                               nobsdcomp);
 
     /**
      * NMSettingPpp:nodeflate:
      *
      * If %TRUE, "deflate" compression will not be requested.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_NODEFLATE,
-                                        PROP_NODEFLATE,
-                                        FALSE,
-                                        NM_SETTING_PARAM_FUZZY_IGNORE,
-                                        nm_setting_ppp_get_nodeflate);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_NODEFLATE,
+                                               PROP_NODEFLATE,
+                                               FALSE,
+                                               NM_SETTING_PARAM_FUZZY_IGNORE,
+                                               NMSettingPppPrivate,
+                                               nodeflate);
 
     /**
      * NMSettingPpp:no-vj-comp:
      *
      * If %TRUE, Van Jacobsen TCP header compression will not be requested.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_NO_VJ_COMP,
-                                        PROP_NO_VJ_COMP,
-                                        FALSE,
-                                        NM_SETTING_PARAM_FUZZY_IGNORE,
-                                        nm_setting_ppp_get_no_vj_comp);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_NO_VJ_COMP,
+                                               PROP_NO_VJ_COMP,
+                                               FALSE,
+                                               NM_SETTING_PARAM_FUZZY_IGNORE,
+                                               NMSettingPppPrivate,
+                                               no_vj_comp);
 
     /**
      * NMSettingPpp:require-mppe:
@@ -672,13 +545,14 @@ nm_setting_ppp_class_init(NMSettingPppClass *klass)
      * session will fail.  Note that MPPE is not used on mobile broadband
      * connections.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_REQUIRE_MPPE,
-                                        PROP_REQUIRE_MPPE,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ppp_get_require_mppe);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_REQUIRE_MPPE,
+                                               PROP_REQUIRE_MPPE,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingPppPrivate,
+                                               require_mppe);
 
     /**
      * NMSettingPpp:require-mppe-128:
@@ -687,13 +561,14 @@ 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.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_REQUIRE_MPPE_128,
-                                        PROP_REQUIRE_MPPE_128,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ppp_get_require_mppe_128);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_REQUIRE_MPPE_128,
+                                               PROP_REQUIRE_MPPE_128,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingPppPrivate,
+                                               require_mppe_128);
 
     /**
      * NMSettingPpp:mppe-stateful:
@@ -701,13 +576,14 @@ nm_setting_ppp_class_init(NMSettingPppClass *klass)
      * If %TRUE, stateful MPPE is used.  See pppd documentation for more
      * information on stateful MPPE.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_MPPE_STATEFUL,
-                                        PROP_MPPE_STATEFUL,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ppp_get_mppe_stateful);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_MPPE_STATEFUL,
+                                               PROP_MPPE_STATEFUL,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingPppPrivate,
+                                               mppe_stateful);
 
     /**
      * NMSettingPpp:crtscts:
@@ -716,13 +592,14 @@ nm_setting_ppp_class_init(NMSettingPppClass *klass)
      * flow control with RTS and CTS signals.  This value should normally be set
      * to %FALSE.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PPP_CRTSCTS,
-                                        PROP_CRTSCTS,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_ppp_get_crtscts);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PPP_CRTSCTS,
+                                               PROP_CRTSCTS,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingPppPrivate,
+                                               crtscts);
 
     /**
      * NMSettingPpp:baud:
@@ -731,14 +608,16 @@ nm_setting_ppp_class_init(NMSettingPppClass *klass)
      * baudrate.  This value should normally be left as 0 to automatically
      * choose the speed.
      **/
-    obj_properties[PROP_BAUD] = g_param_spec_uint(NM_SETTING_PPP_BAUD,
-                                                  "",
-                                                  "",
-                                                  0,
-                                                  G_MAXUINT32,
-                                                  0,
-                                                  G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE
-                                                      | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_PPP_BAUD,
+                                              PROP_BAUD,
+                                              0,
+                                              G_MAXUINT32,
+                                              0,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE,
+                                              NMSettingPppPrivate,
+                                              baud);
 
     /**
      * NMSettingPpp:mru:
@@ -747,13 +626,16 @@ nm_setting_ppp_class_init(NMSettingPppClass *klass)
      * larger than the specified size.  If non-zero, the MRU should be between
      * 128 and 16384.
      */
-    obj_properties[PROP_MRU] = g_param_spec_uint(NM_SETTING_PPP_MRU,
-                                                 "",
-                                                 "",
-                                                 0,
-                                                 16384,
-                                                 0,
-                                                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_PPP_MRU,
+                                              PROP_MRU,
+                                              0,
+                                              16384,
+                                              0,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingPppPrivate,
+                                              mru);
 
     /**
      * NMSettingPpp:mtu:
@@ -761,14 +643,16 @@ nm_setting_ppp_class_init(NMSettingPppClass *klass)
      * If non-zero, instruct pppd to send packets no larger than the specified
      * size.
      **/
-    obj_properties[PROP_MTU] = g_param_spec_uint(NM_SETTING_PPP_MTU,
-                                                 "",
-                                                 "",
-                                                 0,
-                                                 G_MAXUINT32,
-                                                 0,
-                                                 G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE
-                                                     | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_PPP_MTU,
+                                              PROP_MTU,
+                                              0,
+                                              G_MAXUINT32,
+                                              0,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE,
+                                              NMSettingPppPrivate,
+                                              mtu);
 
     /**
      * NMSettingPpp:lcp-echo-failure:
@@ -778,14 +662,16 @@ 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.
      **/
-    obj_properties[PROP_LCP_ECHO_FAILURE] = g_param_spec_uint(
-        NM_SETTING_PPP_LCP_ECHO_FAILURE,
-        "",
-        "",
-        0,
-        G_MAXUINT32,
-        0,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_PPP_LCP_ECHO_FAILURE,
+                                              PROP_LCP_ECHO_FAILURE,
+                                              0,
+                                              G_MAXUINT32,
+                                              0,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE,
+                                              NMSettingPppPrivate,
+                                              lcp_echo_failure);
 
     /**
      * NMSettingPpp:lcp-echo-interval:
@@ -795,19 +681,22 @@ 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.
      **/
-    obj_properties[PROP_LCP_ECHO_INTERVAL] = g_param_spec_uint(
-        NM_SETTING_PPP_LCP_ECHO_INTERVAL,
-        "",
-        "",
-        0,
-        G_MAXUINT32,
-        0,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_PPP_LCP_ECHO_INTERVAL,
+                                              PROP_LCP_ECHO_INTERVAL,
+                                              0,
+                                              G_MAXUINT32,
+                                              0,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE,
+                                              NMSettingPppPrivate,
+                                              lcp_echo_interval);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_PPP,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_PPP,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-pppoe.c b/src/libnm-core-impl/nm-setting-pppoe.c
index 12beb51a..3ae547a2 100644
--- a/src/libnm-core-impl/nm-setting-pppoe.c
+++ b/src/libnm-core-impl/nm-setting-pppoe.c
@@ -30,11 +30,11 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PARENT,
                                   PROP_PASSWORD_FLAGS, );
 
 typedef struct {
-    char *               parent;
-    char *               service;
-    char *               username;
-    char *               password;
-    NMSettingSecretFlags password_flags;
+    char *parent;
+    char *service;
+    char *username;
+    char *password;
+    guint password_flags;
 } NMSettingPppoePrivate;
 
 /**
@@ -138,23 +138,23 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
     NMSettingPppoePrivate *priv       = NM_SETTING_PPPOE_GET_PRIVATE(setting);
     gs_free_error GError *local_error = NULL;
 
-    if (!priv->username) {
-        g_set_error_literal(error,
-                            NM_CONNECTION_ERROR,
-                            NM_CONNECTION_ERROR_MISSING_PROPERTY,
-                            _("property is missing"));
-        g_prefix_error(error, "%s.%s: ", NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_USERNAME);
-        return FALSE;
-    } else if (!strlen(priv->username)) {
-        g_set_error_literal(error,
-                            NM_CONNECTION_ERROR,
-                            NM_CONNECTION_ERROR_INVALID_PROPERTY,
-                            _("property is empty"));
+    if (nm_str_is_empty(priv->username)) {
+        if (!priv->username) {
+            g_set_error_literal(error,
+                                NM_CONNECTION_ERROR,
+                                NM_CONNECTION_ERROR_MISSING_PROPERTY,
+                                _("property is missing"));
+        } else {
+            g_set_error_literal(error,
+                                NM_CONNECTION_ERROR,
+                                NM_CONNECTION_ERROR_INVALID_PROPERTY,
+                                _("property is empty"));
+        }
         g_prefix_error(error, "%s.%s: ", NM_SETTING_PPPOE_SETTING_NAME, NM_SETTING_PPPOE_USERNAME);
         return FALSE;
     }
 
-    if (priv->service && !strlen(priv->service)) {
+    if (priv->service && nm_str_is_empty(priv->service)) {
         g_set_error_literal(error,
                             NM_CONNECTION_ERROR,
                             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -197,66 +197,6 @@ need_secrets(NMSetting *setting)
 /*****************************************************************************/
 
 static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    NMSettingPppoe *setting = NM_SETTING_PPPOE(object);
-
-    switch (prop_id) {
-    case PROP_PARENT:
-        g_value_set_string(value, nm_setting_pppoe_get_parent(setting));
-        break;
-    case PROP_SERVICE:
-        g_value_set_string(value, nm_setting_pppoe_get_service(setting));
-        break;
-    case PROP_USERNAME:
-        g_value_set_string(value, nm_setting_pppoe_get_username(setting));
-        break;
-    case PROP_PASSWORD:
-        g_value_set_string(value, nm_setting_pppoe_get_password(setting));
-        break;
-    case PROP_PASSWORD_FLAGS:
-        g_value_set_flags(value, nm_setting_pppoe_get_password_flags(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)
-{
-    NMSettingPppoePrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE(object);
-
-    switch (prop_id) {
-    case PROP_PARENT:
-        g_free(priv->parent);
-        priv->parent = g_value_dup_string(value);
-        break;
-    case PROP_SERVICE:
-        g_free(priv->service);
-        priv->service = g_value_dup_string(value);
-        break;
-    case PROP_USERNAME:
-        g_free(priv->username);
-        priv->username = g_value_dup_string(value);
-        break;
-    case PROP_PASSWORD:
-        g_free(priv->password);
-        priv->password = g_value_dup_string(value);
-        break;
-    case PROP_PASSWORD_FLAGS:
-        priv->password_flags = g_value_get_flags(value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static void
 nm_setting_pppoe_init(NMSettingPppoe *setting)
 {}
 
@@ -274,29 +214,16 @@ nm_setting_pppoe_new(void)
 }
 
 static void
-finalize(GObject *object)
-{
-    NMSettingPppoePrivate *priv = NM_SETTING_PPPOE_GET_PRIVATE(object);
-
-    g_free(priv->parent);
-    g_free(priv->username);
-    g_free(priv->password);
-    g_free(priv->service);
-
-    G_OBJECT_CLASS(nm_setting_pppoe_parent_class)->finalize(object);
-}
-
-static void
 nm_setting_pppoe_class_init(NMSettingPppoeClass *klass)
 {
-    GObjectClass *  object_class  = G_OBJECT_CLASS(klass);
-    NMSettingClass *setting_class = NM_SETTING_CLASS(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();
 
     g_type_class_add_private(klass, sizeof(NMSettingPppoePrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-    object_class->finalize     = finalize;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify       = verify;
     setting_class->need_secrets = need_secrets;
@@ -311,12 +238,13 @@ nm_setting_pppoe_class_init(NMSettingPppoeClass *klass)
      *
      * Since: 1.10
      **/
-    obj_properties[PROP_PARENT] = g_param_spec_string(
-        NM_SETTING_PPPOE_PARENT,
-        "",
-        "",
-        NULL,
-        G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_PPPOE_PARENT,
+                                              PROP_PARENT,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingPppoePrivate,
+                                              parent);
 
     /**
      * NMSettingPppoe:service:
@@ -326,49 +254,57 @@ 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.
      **/
-    obj_properties[PROP_SERVICE] = g_param_spec_string(NM_SETTING_PPPOE_SERVICE,
-                                                       "",
-                                                       "",
-                                                       NULL,
-                                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_PPPOE_SERVICE,
+                                              PROP_SERVICE,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingPppoePrivate,
+                                              service);
 
     /**
      * NMSettingPppoe:username:
      *
      * Username used to authenticate with the PPPoE service.
      **/
-    obj_properties[PROP_USERNAME] = g_param_spec_string(NM_SETTING_PPPOE_USERNAME,
-                                                        "",
-                                                        "",
-                                                        NULL,
-                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_PPPOE_USERNAME,
+                                              PROP_USERNAME,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingPppoePrivate,
+                                              username);
 
     /**
      * NMSettingPppoe:password:
      *
      * Password used to authenticate with the PPPoE service.
      **/
-    obj_properties[PROP_PASSWORD] =
-        g_param_spec_string(NM_SETTING_PPPOE_PASSWORD,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | NM_SETTING_PARAM_SECRET | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_PPPOE_PASSWORD,
+                                              PROP_PASSWORD,
+                                              NM_SETTING_PARAM_SECRET,
+                                              NMSettingPppoePrivate,
+                                              password);
 
     /**
      * NMSettingPppoe:password-flags:
      *
      * Flags indicating how to handle the #NMSettingPppoe:password property.
      **/
-    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);
+    _nm_setting_property_define_direct_secret_flags(properties_override,
+                                                    obj_properties,
+                                                    NM_SETTING_PPPOE_PASSWORD_FLAGS,
+                                                    PROP_PASSWORD_FLAGS,
+                                                    NMSettingPppoePrivate,
+                                                    password_flags);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_PPPOE);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_PPPOE,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-private.h b/src/libnm-core-impl/nm-setting-private.h
index 061fc901..7faf5d1c 100644
--- a/src/libnm-core-impl/nm-setting-private.h
+++ b/src/libnm-core-impl/nm-setting-private.h
@@ -7,7 +7,7 @@
 #define __NM_SETTING_PRIVATE_H__
 
 #if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-    #error Cannot use this header.
+#error Cannot use this header.
 #endif
 
 #include "nm-setting.h"
@@ -70,24 +70,12 @@ struct _NMSettingClass {
                                  GError **            error);
 
     gboolean (*clear_secrets)(const struct _NMSettInfoSetting *sett_info,
-                              guint                            property_idx,
+                              const NMSettInfoProperty *       property_info,
                               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). */
-    NMTernary (*compare_property)(const struct _NMSettInfoSetting *sett_info,
-                                  guint                            property_idx,
-                                  NMConnection *                   con_a,
-                                  NMSetting *                      set_a,
-                                  NMConnection *                   con_b,
-                                  NMSetting *                      set_b,
-                                  NMSettingCompareFlags            flags);
+    void (*padding_1)(void);
 
     void (*duplicate_copy_properties)(const struct _NMSettInfoSetting *sett_info,
                                       NMSetting *                      src,
@@ -115,8 +103,6 @@ struct _NMSettingClass {
                                guint /* NMSettingParseFlags */ parse_flags,
                                GError **                       error);
 
-    gpointer padding[1];
-
     const struct _NMMetaSettingInfo *setting_info;
 };
 
@@ -127,21 +113,54 @@ struct _NMSettingClass {
  */
 struct _NMSettingIPConfig {
     NMSetting parent;
+    /* In the past, this struct was public API. Preserve ABI! */
 };
 
 struct _NMSettingIPConfigClass {
     NMSettingClass parent;
 
-    /* Padding for future expansion */
-    gpointer padding[8];
+    /* In the past, this struct was public API. Preserve ABI! */
+    union {
+        gpointer _dummy;
+        int      private_offset;
+    };
+    gpointer padding[7];
 };
 
+typedef struct {
+    GPtrArray *dns;         /* array of IP address strings */
+    GPtrArray *dns_search;  /* array of domain name strings */
+    GPtrArray *dns_options; /* array of DNS options */
+    GPtrArray *addresses;   /* array of NMIPAddress */
+    GPtrArray *routes;      /* array of NMIPRoute */
+    GPtrArray *routing_rules;
+    GArray *   dhcp_reject_servers;
+    char *     method;
+    char *     gateway;
+    char *     dhcp_hostname;
+    char *     dhcp_iaid;
+    gint64     route_metric;
+    guint      dhcp_hostname_flags;
+    int        dns_priority;
+    int        dad_timeout;
+    int        dhcp_timeout;
+    int        required_timeout;
+    guint32    route_table;
+    bool       ignore_auto_routes;
+    bool       ignore_auto_dns;
+    bool       dhcp_send_hostname;
+    bool       never_default;
+    bool       may_fail;
+} NMSettingIPConfigPrivate;
+
+void _nm_setting_ip_config_private_init(gpointer self, NMSettingIPConfigPrivate *priv);
+
 /*****************************************************************************/
 
 NMSettingPriority _nm_setting_get_base_type_priority(NMSetting *setting);
 int               _nm_setting_compare_priority(gconstpointer a, gconstpointer b);
 
-int _nmtst_nm_setting_sort(NMSetting *a, NMSetting *b);
+int _nm_setting_sort_for_nm_assert(NMSetting *a, NMSetting *b);
 
 /*****************************************************************************/
 
@@ -246,6 +265,7 @@ gboolean _nm_setting_clear_secrets(NMSetting *                      setting,
  */
 #define NM_SETTING_PARAM_TO_DBUS_IGNORE_FLAGS (1 << (7 + G_PARAM_USER_SHIFT))
 
+extern const NMSettInfoPropertType nm_sett_info_propert_type_setting_name;
 extern const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_interface_name;
 extern const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_ignore_i;
 extern const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_ignore_u;
@@ -253,8 +273,15 @@ extern const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_ignore_u
 extern const NMSettInfoPropertType nm_sett_info_propert_type_plain_i;
 extern const NMSettInfoPropertType nm_sett_info_propert_type_plain_u;
 
-extern const NMSettInfoPropertType nm_sett_info_propert_type_boolean;
-extern const NMSettInfoPropertType nm_sett_info_propert_type_string;
+extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_boolean;
+extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_int32;
+extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_uint32;
+extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_uint64;
+extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_string;
+extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_bytes;
+extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_enum;
+extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_flags;
+extern const NMSettInfoPropertType nm_sett_info_propert_type_direct_mac_address;
 
 NMSettingVerifyResult
 _nm_setting_verify(NMSetting *setting, NMConnection *connection, GError **error);
@@ -268,28 +295,47 @@ gboolean _nm_setting_aggregate(NMSetting *setting, NMConnectionAggregateType typ
 
 gboolean _nm_setting_slave_type_is_valid(const char *slave_type, const char **out_port_type);
 
-GVariant *_nm_setting_property_to_dbus_fcn_gprop(const NMSettInfoSetting *      sett_info,
-                                                 guint                          property_idx,
-                                                 NMConnection *                 connection,
-                                                 NMSetting *                    setting,
-                                                 NMConnectionSerializationFlags flags,
-                                                 const NMConnectionSerializationOptions *options);
+gboolean _nm_setting_compare_flags_check(const GParamSpec *    param_spec,
+                                         NMSettingCompareFlags flags,
+                                         NMSetting *           set_a,
+                                         NMSetting *           set_b);
 
-GVariant *
-_nm_setting_property_to_dbus_fcn_get_boolean(const NMSettInfoSetting *               sett_info,
-                                             guint                                   property_idx,
-                                             NMConnection *                          connection,
-                                             NMSetting *                             setting,
-                                             NMConnectionSerializationFlags          flags,
-                                             const NMConnectionSerializationOptions *options);
+NMTernary _nm_setting_property_compare_fcn_ignore(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil);
+
+NMTernary _nm_setting_property_compare_fcn_direct(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil);
+
+NMTernary _nm_setting_property_compare_fcn_default(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil);
+
+void _nm_setting_property_get_property_direct(GObject *   object,
+                                              guint       prop_id,
+                                              GValue *    value,
+                                              GParamSpec *pspec);
+
+void _nm_setting_property_set_property_direct(GObject *     object,
+                                              guint         prop_id,
+                                              const GValue *value,
+                                              GParamSpec *  pspec);
+
+GVariant *_nm_setting_property_to_dbus_fcn_ignore(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil);
+
+GVariant *_nm_setting_property_to_dbus_fcn_gprop(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil);
+
+GVariant *_nm_setting_property_to_dbus_fcn_direct(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil);
 
 GVariant *
-_nm_setting_property_to_dbus_fcn_get_string(const NMSettInfoSetting *               sett_info,
-                                            guint                                   property_idx,
-                                            NMConnection *                          connection,
-                                            NMSetting *                             setting,
-                                            NMConnectionSerializationFlags          flags,
-                                            const NMConnectionSerializationOptions *options);
+_nm_setting_property_to_dbus_fcn_direct_mac_address(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil);
+
+gboolean _nm_setting_property_from_dbus_fcn_ignore(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
+
+gboolean _nm_setting_property_from_dbus_fcn_direct_ip_config_gateway(
+    _NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
+
+gboolean _nm_setting_property_from_dbus_fcn_direct_mac_address(
+    _NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
+
+gboolean _nm_setting_property_from_dbus_fcn_direct(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
+
+gboolean _nm_setting_property_from_dbus_fcn_gprop(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
 
 GVariant *_nm_setting_to_dbus(NMSetting *                             setting,
                               NMConnection *                          connection,
@@ -308,25 +354,33 @@ gboolean _nm_setting_property_is_regular_secret_flags(NMSetting * setting,
 
 /*****************************************************************************/
 
+const NMSettInfoProperty *
+_nm_sett_info_property_lookup_by_param_spec(const NMSettInfoSetting *sett_info,
+                                            const GParamSpec *       param_spec);
+
 static inline GArray *
-_nm_sett_info_property_override_create_array(void)
+_nm_sett_info_property_override_create_array_sized(guint reserved_size)
 {
-    return g_array_new(FALSE, FALSE, sizeof(NMSettInfoProperty));
+    return g_array_sized_new(FALSE, FALSE, sizeof(NMSettInfoProperty), reserved_size);
 }
 
-GArray *_nm_sett_info_property_override_create_array_ip_config(void);
-
-void _nm_setting_class_commit_full(NMSettingClass *            setting_class,
-                                   NMMetaSettingType           meta_type,
-                                   const NMSettInfoSettDetail *detail,
-                                   GArray *                    properties_override);
-
-static inline void
-_nm_setting_class_commit(NMSettingClass *setting_class, NMMetaSettingType meta_type)
+static inline GArray *
+_nm_sett_info_property_override_create_array(void)
 {
-    _nm_setting_class_commit_full(setting_class, meta_type, NULL, NULL);
+    /* pre-allocate a relatively large buffer to avoid frequent re-allocations.
+     * Note that the buffer is only short-lived and will be destroyed by
+     * _nm_setting_class_commit(). */
+    return _nm_sett_info_property_override_create_array_sized(20);
 }
 
+GArray *_nm_sett_info_property_override_create_array_ip_config(int addr_family);
+
+void _nm_setting_class_commit(NMSettingClass *            setting_class,
+                              NMMetaSettingType           meta_type,
+                              const NMSettInfoSettDetail *detail,
+                              GArray *                    properties_override,
+                              gint16                      private_offset);
+
 #define NM_SETT_INFO_SETT_GENDATA(...)                         \
     ({                                                         \
         static const NMSettInfoSettGendata _g = {__VA_ARGS__}; \
@@ -341,11 +395,10 @@ _nm_setting_class_commit(NMSettingClass *setting_class, NMMetaSettingType meta_t
         .dbus_type = _dbus_type, __VA_ARGS__                 \
     }
 
-#define NM_SETT_INFO_PROPERT_TYPE_GPROP_INIT(_dbus_type, ...)                           \
-    {                                                                                   \
-        .dbus_type = _dbus_type, .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_gprop, \
-        __VA_ARGS__                                                                     \
-    }
+#define NM_SETT_INFO_PROPERT_TYPE_GPROP_INIT(_dbus_type, ...)                                  \
+    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(_dbus_type,                                            \
+                                        .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_gprop, \
+                                        __VA_ARGS__)
 
 #define NM_SETT_INFO_PROPERT_TYPE(init)               \
     ({                                                \
@@ -372,11 +425,14 @@ _nm_properties_override(GArray *properties_override, const NMSettInfoProperty *p
     g_array_append_vals(properties_override, prop_info, 1);
 }
 
-#define _nm_properties_override_gobj(properties_override, p_param_spec, p_property_type, ...) \
-    _nm_properties_override((properties_override),                                            \
-                            NM_SETT_INFO_PROPERTY(.name          = NULL,                      \
-                                                  .param_spec    = (p_param_spec),            \
-                                                  .property_type = (p_property_type),         \
+#define _nm_properties_override_gobj(properties_override,                             \
+                                     p_param_spec,                                    \
+                                     p_property_type,                                 \
+                                     ... /* extra NMSettInfoProperty fields */)       \
+    _nm_properties_override((properties_override),                                    \
+                            NM_SETT_INFO_PROPERTY(.name          = NULL,              \
+                                                  .param_spec    = (p_param_spec),    \
+                                                  .property_type = (p_property_type), \
                                                   __VA_ARGS__))
 
 #define _nm_properties_override_dbus(properties_override, p_name, p_property_type) \
@@ -386,123 +442,452 @@ _nm_properties_override(GArray *properties_override, const NMSettInfoProperty *p
 
 /*****************************************************************************/
 
-#define _nm_setting_property_define_boolean_full(properties_override,                           \
+/* Define "direct" properties. These are properties that have a GParamSpec and
+ * NMSettInfoPropertType.direct_type != NM_VALUE_TYPE_NONE.
+ *
+ * With this, the location of the data is known at
+ *
+ *    _nm_setting_get_private(setting, sett_info, property_info->direct_offset)
+ *
+ * which allows to generically handle the property operations (like get, set, compare).
+ */
+
+#define _nm_setting_property_define_direct_boolean(properties_override,                        \
+                                                   obj_properties,                             \
+                                                   prop_name,                                  \
+                                                   prop_id,                                    \
+                                                   default_value,                              \
+                                                   param_flags,                                \
+                                                   private_struct_type,                        \
+                                                   private_struct_field,                       \
+                                                   ... /* extra NMSettInfoProperty fields */)  \
+    G_STMT_START                                                                               \
+    {                                                                                          \
+        const gboolean _default_value = (default_value);                                       \
+        GParamSpec *   _param_spec;                                                            \
+                                                                                               \
+        G_STATIC_ASSERT(                                                                       \
+            !NM_FLAGS_ANY((param_flags),                                                       \
+                          ~(NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_INFERRABLE        \
+                            | NM_SETTING_PARAM_REAPPLY_IMMEDIATELY)));                         \
+                                                                                               \
+        nm_assert(NM_IN_SET(_default_value, 0, 1));                                            \
+                                                                                               \
+        _param_spec =                                                                          \
+            g_param_spec_boolean("" prop_name "",                                              \
+                                 "",                                                           \
+                                 "",                                                           \
+                                 _default_value,                                               \
+                                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags));  \
+                                                                                               \
+        (obj_properties)[(prop_id)] = _param_spec;                                             \
+                                                                                               \
+        _nm_properties_override_gobj(                                                          \
+            (properties_override),                                                             \
+            _param_spec,                                                                       \
+            &nm_sett_info_propert_type_direct_boolean,                                         \
+            .direct_offset =                                                                   \
+                NM_STRUCT_OFFSET_ENSURE_TYPE(bool, private_struct_type, private_struct_field), \
+            __VA_ARGS__);                                                                      \
+    }                                                                                          \
+    G_STMT_END
+
+/*****************************************************************************/
+
+#define _nm_setting_property_define_direct_uint32(properties_override,                            \
+                                                  obj_properties,                                 \
+                                                  prop_name,                                      \
+                                                  prop_id,                                        \
+                                                  min_value,                                      \
+                                                  max_value,                                      \
+                                                  default_value,                                  \
+                                                  param_flags,                                    \
+                                                  private_struct_type,                            \
+                                                  private_struct_field,                           \
+                                                  ... /* extra NMSettInfoProperty fields */)      \
+    G_STMT_START                                                                                  \
+    {                                                                                             \
+        GParamSpec *_param_spec;                                                                  \
+                                                                                                  \
+        G_STATIC_ASSERT(                                                                          \
+            !NM_FLAGS_ANY((param_flags),                                                          \
+                          ~(NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_INFERRABLE)));       \
+        G_STATIC_ASSERT((min_value) <= (guint64) (default_value));                                \
+        G_STATIC_ASSERT((default_value) <= (guint64) (max_value));                                \
+        G_STATIC_ASSERT((max_value) <= (guint64) G_MAXUINT32);                                    \
+                                                                                                  \
+        _param_spec =                                                                             \
+            g_param_spec_uint("" prop_name "",                                                    \
+                              "",                                                                 \
+                              "",                                                                 \
+                              (min_value),                                                        \
+                              (max_value),                                                        \
+                              (default_value),                                                    \
+                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags));        \
+                                                                                                  \
+        (obj_properties)[(prop_id)] = _param_spec;                                                \
+                                                                                                  \
+        _nm_properties_override_gobj(                                                             \
+            (properties_override),                                                                \
+            _param_spec,                                                                          \
+            &nm_sett_info_propert_type_direct_uint32,                                             \
+            .direct_offset =                                                                      \
+                NM_STRUCT_OFFSET_ENSURE_TYPE(guint32, private_struct_type, private_struct_field), \
+            __VA_ARGS__);                                                                         \
+    }                                                                                             \
+    G_STMT_END
+
+/*****************************************************************************/
+
+#define _nm_setting_property_define_direct_int32(properties_override,                            \
+                                                 obj_properties,                                 \
+                                                 prop_name,                                      \
+                                                 prop_id,                                        \
+                                                 min_value,                                      \
+                                                 max_value,                                      \
+                                                 default_value,                                  \
+                                                 param_flags,                                    \
+                                                 private_struct_type,                            \
+                                                 private_struct_field,                           \
+                                                 ... /* extra NMSettInfoProperty fields */)      \
+    G_STMT_START                                                                                 \
+    {                                                                                            \
+        GParamSpec *_param_spec;                                                                 \
+                                                                                                 \
+        G_STATIC_ASSERT(                                                                         \
+            !NM_FLAGS_ANY((param_flags),                                                         \
+                          ~(NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_INFERRABLE)));      \
+        G_STATIC_ASSERT((min_value) >= (gint64) (G_MININT32));                                   \
+        G_STATIC_ASSERT((min_value) <= (gint64) (default_value));                                \
+        G_STATIC_ASSERT((default_value) <= (gint64) (max_value));                                \
+        G_STATIC_ASSERT((max_value) <= (gint64) G_MAXUINT32);                                    \
+                                                                                                 \
+        _param_spec =                                                                            \
+            g_param_spec_int("" prop_name "",                                                    \
+                             "",                                                                 \
+                             "",                                                                 \
+                             (min_value),                                                        \
+                             (max_value),                                                        \
+                             (default_value),                                                    \
+                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags));        \
+                                                                                                 \
+        (obj_properties)[(prop_id)] = _param_spec;                                               \
+                                                                                                 \
+        _nm_properties_override_gobj(                                                            \
+            (properties_override),                                                               \
+            _param_spec,                                                                         \
+            &nm_sett_info_propert_type_direct_int32,                                             \
+            .direct_offset =                                                                     \
+                NM_STRUCT_OFFSET_ENSURE_TYPE(gint32, private_struct_type, private_struct_field), \
+            __VA_ARGS__);                                                                        \
+    }                                                                                            \
+    G_STMT_END
+
+/*****************************************************************************/
+
+#define _nm_setting_property_define_direct_uint64(properties_override,                            \
+                                                  obj_properties,                                 \
+                                                  prop_name,                                      \
+                                                  prop_id,                                        \
+                                                  min_value,                                      \
+                                                  max_value,                                      \
+                                                  default_value,                                  \
+                                                  param_flags,                                    \
+                                                  private_struct_type,                            \
+                                                  private_struct_field,                           \
+                                                  ... /* extra NMSettInfoProperty fields */)      \
+    G_STMT_START                                                                                  \
+    {                                                                                             \
+        GParamSpec *_param_spec;                                                                  \
+                                                                                                  \
+        G_STATIC_ASSERT(                                                                          \
+            !NM_FLAGS_ANY((param_flags),                                                          \
+                          ~(NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_INFERRABLE)));       \
+        G_STATIC_ASSERT((min_value) <= (default_value));                                          \
+        G_STATIC_ASSERT((default_value) == 0 || (default_value) -1u < (max_value));               \
+        G_STATIC_ASSERT((max_value) <= G_MAXUINT64);                                              \
+                                                                                                  \
+        _param_spec =                                                                             \
+            g_param_spec_uint64("" prop_name "",                                                  \
+                                "",                                                               \
+                                "",                                                               \
+                                (min_value),                                                      \
+                                (max_value),                                                      \
+                                (default_value),                                                  \
+                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags));      \
+                                                                                                  \
+        (obj_properties)[(prop_id)] = _param_spec;                                                \
+                                                                                                  \
+        _nm_properties_override_gobj(                                                             \
+            (properties_override),                                                                \
+            _param_spec,                                                                          \
+            &nm_sett_info_propert_type_direct_uint64,                                             \
+            .direct_offset =                                                                      \
+                NM_STRUCT_OFFSET_ENSURE_TYPE(guint64, private_struct_type, private_struct_field), \
+            __VA_ARGS__);                                                                         \
+    }                                                                                             \
+    G_STMT_END
+
+/*****************************************************************************/
+
+#define _nm_setting_property_define_direct_string_full(properties_override,                       \
+                                                       obj_properties,                            \
+                                                       prop_name,                                 \
+                                                       prop_id,                                   \
+                                                       param_flags,                               \
+                                                       property_type,                             \
+                                                       private_struct_type,                       \
+                                                       private_struct_field,                      \
+                                                       ... /* extra NMSettInfoProperty fields */) \
+    G_STMT_START                                                                                  \
+    {                                                                                             \
+        GParamSpec *                 _param_spec;                                                 \
+        const NMSettInfoPropertType *_property_type = (property_type);                            \
+                                                                                                  \
+        G_STATIC_ASSERT(!NM_FLAGS_ANY((param_flags),                                              \
+                                      ~(NM_SETTING_PARAM_SECRET | NM_SETTING_PARAM_FUZZY_IGNORE   \
+                                        | NM_SETTING_PARAM_INFERRABLE                             \
+                                        | NM_SETTING_PARAM_REAPPLY_IMMEDIATELY)));                \
+                                                                                                  \
+        nm_assert(_property_type);                                                                \
+        nm_assert(g_variant_type_equal(_property_type->dbus_type, "s"));                          \
+        nm_assert(_property_type->direct_type == NM_VALUE_TYPE_STRING);                           \
+        nm_assert(_property_type->to_dbus_fcn == _nm_setting_property_to_dbus_fcn_direct);        \
+                                                                                                  \
+        _param_spec =                                                                             \
+            g_param_spec_string("" prop_name "",                                                  \
+                                "",                                                               \
+                                "",                                                               \
+                                NULL,                                                             \
+                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags));      \
+                                                                                                  \
+        (obj_properties)[(prop_id)] = _param_spec;                                                \
+                                                                                                  \
+        _nm_properties_override_gobj(                                                             \
+            (properties_override),                                                                \
+            _param_spec,                                                                          \
+            _property_type,                                                                       \
+            .direct_offset =                                                                      \
+                NM_STRUCT_OFFSET_ENSURE_TYPE(char *, private_struct_type, private_struct_field),  \
+            __VA_ARGS__);                                                                         \
+    }                                                                                             \
+    G_STMT_END
+
+#define _nm_setting_property_define_direct_string(properties_override,                       \
+                                                  obj_properties,                            \
+                                                  prop_name,                                 \
+                                                  prop_id,                                   \
+                                                  param_flags,                               \
+                                                  private_struct_type,                       \
+                                                  private_struct_field,                      \
+                                                  ... /* extra NMSettInfoProperty fields */) \
+    _nm_setting_property_define_direct_string_full((properties_override),                    \
+                                                   (obj_properties),                         \
+                                                   prop_name,                                \
+                                                   (prop_id),                                \
+                                                   (param_flags),                            \
+                                                   &nm_sett_info_propert_type_direct_string, \
+                                                   private_struct_type,                      \
+                                                   private_struct_field,                     \
+                                                   __VA_ARGS__)
+
+/*****************************************************************************/
+
+#define _nm_setting_property_define_direct_bytes(properties_override,                              \
+                                                 obj_properties,                                   \
+                                                 prop_name,                                        \
+                                                 prop_id,                                          \
+                                                 param_flags,                                      \
+                                                 private_struct_type,                              \
+                                                 private_struct_field,                             \
+                                                 ... /* extra NMSettInfoProperty fields */)        \
+    G_STMT_START                                                                                   \
+    {                                                                                              \
+        GParamSpec *_param_spec;                                                                   \
+                                                                                                   \
+        G_STATIC_ASSERT(!NM_FLAGS_ANY((param_flags),                                               \
+                                      ~(NM_SETTING_PARAM_SECRET | NM_SETTING_PARAM_INFERRABLE      \
+                                        | NM_SETTING_PARAM_FUZZY_IGNORE)));                        \
+                                                                                                   \
+        _param_spec =                                                                              \
+            g_param_spec_boxed("" prop_name "",                                                    \
+                               "",                                                                 \
+                               "",                                                                 \
+                               G_TYPE_BYTES,                                                       \
+                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags));        \
+                                                                                                   \
+        (obj_properties)[(prop_id)] = _param_spec;                                                 \
+                                                                                                   \
+        _nm_properties_override_gobj(                                                              \
+            (properties_override),                                                                 \
+            _param_spec,                                                                           \
+            &nm_sett_info_propert_type_direct_bytes,                                               \
+            .direct_offset =                                                                       \
+                NM_STRUCT_OFFSET_ENSURE_TYPE(GBytes *, private_struct_type, private_struct_field), \
+            __VA_ARGS__);                                                                          \
+    }                                                                                              \
+    G_STMT_END
+
+/*****************************************************************************/
+
+#define _nm_setting_property_define_direct_enum(properties_override,                             \
+                                                obj_properties,                                  \
+                                                prop_name,                                       \
+                                                prop_id,                                         \
+                                                gtype_enum,                                      \
+                                                default_value,                                   \
+                                                param_flags,                                     \
+                                                private_struct_type,                             \
+                                                private_struct_field,                            \
+                                                ... /* extra NMSettInfoProperty fields */)       \
+    G_STMT_START                                                                                 \
+    {                                                                                            \
+        GParamSpec *_param_spec;                                                                 \
+                                                                                                 \
+        G_STATIC_ASSERT(                                                                         \
+            !NM_FLAGS_ANY((param_flags),                                                         \
+                          ~(NM_SETTING_PARAM_REAPPLY_IMMEDIATELY | NM_SETTING_PARAM_FUZZY_IGNORE \
+                            | NM_SETTING_PARAM_INFERRABLE)));                                    \
+                                                                                                 \
+        _param_spec =                                                                            \
+            g_param_spec_enum("" prop_name "",                                                   \
+                              "",                                                                \
+                              "",                                                                \
+                              (gtype_enum),                                                      \
+                              (default_value),                                                   \
+                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags));       \
+                                                                                                 \
+        (obj_properties)[(prop_id)] = _param_spec;                                               \
+                                                                                                 \
+        _nm_properties_override_gobj(                                                            \
+            (properties_override),                                                               \
+            _param_spec,                                                                         \
+            &nm_sett_info_propert_type_direct_enum,                                              \
+            .direct_offset =                                                                     \
+                NM_STRUCT_OFFSET_ENSURE_TYPE(int, private_struct_type, private_struct_field),    \
+            __VA_ARGS__);                                                                        \
+    }                                                                                            \
+    G_STMT_END
+
+/*****************************************************************************/
+
+#define _nm_setting_property_define_direct_ternary_enum(properties_override,  \
+                                                        obj_properties,       \
+                                                        prop_name,            \
+                                                        prop_id,              \
+                                                        param_flags,          \
+                                                        private_struct_type,  \
+                                                        private_struct_field, \
+                                                        ...)                  \
+    _nm_setting_property_define_direct_enum((properties_override),            \
+                                            (obj_properties),                 \
+                                            prop_name,                        \
+                                            (prop_id),                        \
+                                            NM_TYPE_TERNARY,                  \
+                                            NM_TERNARY_DEFAULT,               \
+                                            (param_flags),                    \
+                                            private_struct_type,              \
+                                            private_struct_field,             \
+                                            __VA_ARGS__)
+
+/*****************************************************************************/
+
+#define _nm_setting_property_define_direct_flags(properties_override,                           \
                                                  obj_properties,                                \
                                                  prop_name,                                     \
                                                  prop_id,                                       \
+                                                 gtype_flags,                                   \
                                                  default_value,                                 \
                                                  param_flags,                                   \
-                                                 property_type,                                 \
-                                                 get_fcn,                                       \
-                                                 ...)                                           \
+                                                 private_struct_type,                           \
+                                                 private_struct_field,                          \
+                                                 ... /* extra NMSettInfoProperty fields */)     \
     G_STMT_START                                                                                \
     {                                                                                           \
-        const gboolean                     _default_value = (default_value);                    \
-        GParamSpec *                       _param_spec;                                         \
-        const NMSettInfoPropertType *const _property_type = (property_type);                    \
+        GParamSpec *_param_spec;                                                                \
                                                                                                 \
         G_STATIC_ASSERT(                                                                        \
             !NM_FLAGS_ANY((param_flags),                                                        \
-                          ~(NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_INFERRABLE         \
-                            | NM_SETTING_PARAM_REAPPLY_IMMEDIATELY)));                          \
-                                                                                                \
-        nm_assert(_property_type);                                                              \
-        nm_assert(_property_type->to_dbus_fcn == _nm_setting_property_to_dbus_fcn_get_boolean); \
-                                                                                                \
-        nm_assert(NM_IN_SET(_default_value, 0, 1));                                             \
+                          ~(NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_INFERRABLE)));     \
                                                                                                 \
         _param_spec =                                                                           \
-            g_param_spec_boolean("" prop_name "",                                               \
-                                 "",                                                            \
-                                 "",                                                            \
-                                 _default_value,                                                \
-                                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags));   \
+            g_param_spec_flags("" prop_name "",                                                 \
+                               "",                                                              \
+                               "",                                                              \
+                               (gtype_flags),                                                   \
+                               (default_value),                                                 \
+                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags));     \
                                                                                                 \
         (obj_properties)[(prop_id)] = _param_spec;                                              \
                                                                                                 \
-        _nm_properties_override_gobj((properties_override),                                     \
-                                     _param_spec,                                               \
-                                     _property_type,                                            \
-                                     .to_dbus_data.get_boolean =                                \
-                                         (gboolean(*)(NMSetting *)) (get_fcn),                  \
-                                     __VA_ARGS__);                                              \
+        _nm_properties_override_gobj(                                                           \
+            (properties_override),                                                              \
+            _param_spec,                                                                        \
+            &nm_sett_info_propert_type_direct_flags,                                            \
+            .direct_offset =                                                                    \
+                NM_STRUCT_OFFSET_ENSURE_TYPE(guint, private_struct_type, private_struct_field), \
+            __VA_ARGS__);                                                                       \
     }                                                                                           \
     G_STMT_END
 
-#define _nm_setting_property_define_boolean(properties_override,                 \
-                                            obj_properties,                      \
-                                            prop_name,                           \
-                                            prop_id,                             \
-                                            default_value,                       \
-                                            param_flags,                         \
-                                            get_fcn,                             \
-                                            ...)                                 \
-    _nm_setting_property_define_boolean_full((properties_override),              \
-                                             (obj_properties),                   \
-                                             prop_name,                          \
-                                             (prop_id),                          \
-                                             (default_value),                    \
-                                             (param_flags),                      \
-                                             &nm_sett_info_propert_type_boolean, \
-                                             (get_fcn),                          \
-                                             __VA_ARGS__)
+/*****************************************************************************/
+
+#define _nm_setting_property_define_direct_secret_flags(properties_override,                       \
+                                                        obj_properties,                            \
+                                                        prop_name,                                 \
+                                                        prop_id,                                   \
+                                                        private_struct_type,                       \
+                                                        private_struct_field,                      \
+                                                        ... /* extra NMSettInfoProperty fields */) \
+    _nm_setting_property_define_direct_flags((properties_override),                                \
+                                             (obj_properties),                                     \
+                                             prop_name,                                            \
+                                             (prop_id),                                            \
+                                             NM_TYPE_SETTING_SECRET_FLAGS,                         \
+                                             NM_SETTING_SECRET_FLAG_NONE,                          \
+                                             NM_SETTING_PARAM_NONE,                                \
+                                             private_struct_type,                                  \
+                                             private_struct_field,                                 \
+                                             ##__VA_ARGS__)
 
 /*****************************************************************************/
 
-#define _nm_setting_property_define_string_full(properties_override,                            \
-                                                obj_properties,                                 \
-                                                prop_name,                                      \
-                                                prop_id,                                        \
-                                                param_flags,                                    \
-                                                property_type,                                  \
-                                                get_fcn,                                        \
-                                                ...)                                            \
-    G_STMT_START                                                                                \
-    {                                                                                           \
-        GParamSpec *                       _param_spec;                                         \
-        const NMSettInfoPropertType *const _property_type = (property_type);                    \
-                                                                                                \
-        G_STATIC_ASSERT(!NM_FLAGS_ANY((param_flags),                                            \
-                                      ~(NM_SETTING_PARAM_SECRET | NM_SETTING_PARAM_FUZZY_IGNORE \
-                                        | NM_SETTING_PARAM_INFERRABLE                           \
-                                        | NM_SETTING_PARAM_REAPPLY_IMMEDIATELY)));              \
-        nm_assert(_property_type);                                                              \
-        nm_assert(_property_type->to_dbus_fcn == _nm_setting_property_to_dbus_fcn_get_string);  \
-                                                                                                \
-        _param_spec =                                                                           \
-            g_param_spec_string("" prop_name "",                                                \
-                                "",                                                             \
-                                "",                                                             \
-                                NULL,                                                           \
-                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags));    \
-                                                                                                \
-        (obj_properties)[(prop_id)] = _param_spec;                                              \
-                                                                                                \
-        _nm_properties_override_gobj((properties_override),                                     \
-                                     _param_spec,                                               \
-                                     _property_type,                                            \
-                                     .to_dbus_data.get_string =                                 \
-                                         (const char *(*) (NMSetting *) ) (get_fcn),            \
-                                     __VA_ARGS__);                                              \
-    }                                                                                           \
+#define _nm_setting_property_define_direct_mac_address(properties_override,                       \
+                                                       obj_properties,                            \
+                                                       prop_name,                                 \
+                                                       prop_id,                                   \
+                                                       param_flags,                               \
+                                                       private_struct_type,                       \
+                                                       private_struct_field,                      \
+                                                       ... /* extra NMSettInfoProperty fields */) \
+    G_STMT_START                                                                                  \
+    {                                                                                             \
+        GParamSpec *_param_spec;                                                                  \
+                                                                                                  \
+        G_STATIC_ASSERT(!NM_FLAGS_ANY((param_flags),                                              \
+                                      ~(NM_SETTING_PARAM_SECRET | NM_SETTING_PARAM_FUZZY_IGNORE   \
+                                        | NM_SETTING_PARAM_INFERRABLE                             \
+                                        | NM_SETTING_PARAM_REAPPLY_IMMEDIATELY)));                \
+                                                                                                  \
+        _param_spec =                                                                             \
+            g_param_spec_string("" prop_name "",                                                  \
+                                "",                                                               \
+                                "",                                                               \
+                                NULL,                                                             \
+                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | (param_flags));      \
+                                                                                                  \
+        (obj_properties)[(prop_id)] = _param_spec;                                                \
+                                                                                                  \
+        _nm_properties_override_gobj(                                                             \
+            (properties_override),                                                                \
+            _param_spec,                                                                          \
+            &nm_sett_info_propert_type_direct_mac_address,                                        \
+            .direct_offset =                                                                      \
+                NM_STRUCT_OFFSET_ENSURE_TYPE(char *, private_struct_type, private_struct_field),  \
+            __VA_ARGS__);                                                                         \
+    }                                                                                             \
     G_STMT_END
 
-#define _nm_setting_property_define_string(properties_override,                \
-                                           obj_properties,                     \
-                                           prop_name,                          \
-                                           prop_id,                            \
-                                           param_flags,                        \
-                                           get_fcn,                            \
-                                           ...)                                \
-    _nm_setting_property_define_string_full((properties_override),             \
-                                            (obj_properties),                  \
-                                            prop_name,                         \
-                                            (prop_id),                         \
-                                            (param_flags),                     \
-                                            &nm_sett_info_propert_type_string, \
-                                            (get_fcn),                         \
-                                            __VA_ARGS__)
-
 /*****************************************************************************/
 
 gboolean _nm_setting_use_legacy_property(NMSetting * setting,
@@ -520,6 +905,20 @@ gboolean _nm_setting_should_compare_secret_property(NMSetting *           settin
 NMBridgeVlan *_nm_bridge_vlan_dup(const NMBridgeVlan *vlan);
 NMBridgeVlan *_nm_bridge_vlan_dup_and_seal(const NMBridgeVlan *vlan);
 
+gboolean _nm_utils_bridge_vlans_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
+
+GVariant *_nm_utils_bridge_vlans_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil);
+
+NMTernary _nm_setting_ip_config_compare_fcn_addresses(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil);
+
+NMTernary _nm_setting_ip_config_compare_fcn_routes(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil);
+
+gboolean _nm_utils_hwaddr_cloned_not_set(_NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil);
+
+GVariant *_nm_utils_hwaddr_cloned_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil);
+
+gboolean _nm_utils_hwaddr_cloned_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil);
+
 /*****************************************************************************/
 
 #endif /* NM_SETTING_PRIVATE_H */
diff --git a/src/libnm-core-impl/nm-setting-proxy.c b/src/libnm-core-impl/nm-setting-proxy.c
index fd09e1d4..0425ec45 100644
--- a/src/libnm-core-impl/nm-setting-proxy.c
+++ b/src/libnm-core-impl/nm-setting-proxy.c
@@ -29,10 +29,10 @@
 NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_METHOD, PROP_BROWSER_ONLY, PROP_PAC_URL, PROP_PAC_SCRIPT, );
 
 typedef struct {
-    char *pac_url;
-    char *pac_script;
-    int   method;
-    bool  browser_only : 1;
+    char * pac_url;
+    char * pac_script;
+    gint32 method;
+    bool   browser_only;
 } NMSettingProxyPrivate;
 
 /**
@@ -208,62 +208,8 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    NMSettingProxy *setting = NM_SETTING_PROXY(object);
-
-    switch (prop_id) {
-    case PROP_METHOD:
-        g_value_set_int(value, nm_setting_proxy_get_method(setting));
-        break;
-    case PROP_BROWSER_ONLY:
-        g_value_set_boolean(value, nm_setting_proxy_get_browser_only(setting));
-        break;
-    case PROP_PAC_URL:
-        g_value_set_string(value, nm_setting_proxy_get_pac_url(setting));
-        break;
-    case PROP_PAC_SCRIPT:
-        g_value_set_string(value, nm_setting_proxy_get_pac_script(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)
-{
-    NMSettingProxyPrivate *priv = NM_SETTING_PROXY_GET_PRIVATE(object);
-
-    switch (prop_id) {
-    case PROP_METHOD:
-        priv->method = g_value_get_int(value);
-        break;
-    case PROP_BROWSER_ONLY:
-        priv->browser_only = g_value_get_boolean(value);
-        break;
-    case PROP_PAC_URL:
-        g_free(priv->pac_url);
-        priv->pac_url = g_value_dup_string(value);
-        break;
-    case PROP_PAC_SCRIPT:
-        g_free(priv->pac_script);
-        priv->pac_script = g_value_dup_string(value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static void
 nm_setting_proxy_init(NMSettingProxy *self)
-{
-    nm_assert(NM_SETTING_PROXY_GET_PRIVATE(self)->method == NM_SETTING_PROXY_METHOD_NONE);
-}
+{}
 
 /**
  * nm_setting_proxy_new:
@@ -281,18 +227,6 @@ nm_setting_proxy_new(void)
 }
 
 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)
 {
     GObjectClass *  object_class        = G_OBJECT_CLASS(klass);
@@ -301,9 +235,8 @@ nm_setting_proxy_class_init(NMSettingProxyClass *klass)
 
     g_type_class_add_private(klass, sizeof(NMSettingProxyPrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-    object_class->finalize     = finalize;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify = verify;
 
@@ -323,13 +256,16 @@ nm_setting_proxy_class_init(NMSettingProxyClass *klass)
      * values: none, auto
      * ---end---
      */
-    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_STATIC_STRINGS);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_PROXY_METHOD,
+                                             PROP_METHOD,
+                                             G_MININT32,
+                                             G_MAXINT32,
+                                             NM_SETTING_PROXY_METHOD_NONE,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingProxyPrivate,
+                                             method);
 
     /**
      * NMSettingProxy:browser-only:
@@ -345,13 +281,14 @@ nm_setting_proxy_class_init(NMSettingProxyClass *klass)
      * description: Whether the proxy configuration is for browser only.
      * ---end---
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_PROXY_BROWSER_ONLY,
-                                        PROP_BROWSER_ONLY,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_proxy_get_browser_only);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_PROXY_BROWSER_ONLY,
+                                               PROP_BROWSER_ONLY,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingProxyPrivate,
+                                               browser_only);
 
     /**
      * NMSettingProxy:pac-url:
@@ -367,11 +304,13 @@ nm_setting_proxy_class_init(NMSettingProxyClass *klass)
      * example: PAC_URL=http://wpad.mycompany.com/wpad.dat
      * ---end---
      */
-    obj_properties[PROP_PAC_URL] = g_param_spec_string(NM_SETTING_PROXY_PAC_URL,
-                                                       "",
-                                                       "",
-                                                       NULL,
-                                                       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_PROXY_PAC_URL,
+                                              PROP_PAC_URL,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingProxyPrivate,
+                                              pac_url);
 
     /**
      * NMSettingProxy:pac-script:
@@ -387,17 +326,19 @@ nm_setting_proxy_class_init(NMSettingProxyClass *klass)
      * example: PAC_SCRIPT=/home/joe/proxy.pac
      * ---end---
      */
-    obj_properties[PROP_PAC_SCRIPT] =
-        g_param_spec_string(NM_SETTING_PROXY_PAC_SCRIPT,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_PROXY_PAC_SCRIPT,
+                                              PROP_PAC_SCRIPT,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingProxyPrivate,
+                                              pac_script);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_PROXY,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_PROXY,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-serial.c b/src/libnm-core-impl/nm-setting-serial.c
index c87de166..3ebcf062 100644
--- a/src/libnm-core-impl/nm-setting-serial.c
+++ b/src/libnm-core-impl/nm-setting-serial.c
@@ -30,9 +30,9 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_BAUD,
 
 typedef struct {
     guint64 send_delay;
-    guint   baud;
-    guint   bits;
-    guint   stopbits;
+    guint32 baud;
+    guint32 bits;
+    guint32 stopbits;
     char    parity;
 } NMSettingSerialPrivate;
 
@@ -129,22 +129,26 @@ nm_setting_serial_get_send_delay(NMSettingSerial *setting)
     return NM_SETTING_SERIAL_GET_PRIVATE(setting)->send_delay;
 }
 
+/*****************************************************************************/
+
 static GVariant *
-parity_to_dbus(const GValue *from)
+parity_to_dbus_fcn(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
-    switch (g_value_get_enum(from)) {
+    switch (nm_setting_serial_get_parity(NM_SETTING_SERIAL(setting))) {
     case NM_SETTING_SERIAL_PARITY_EVEN:
         return g_variant_new_byte('E');
     case NM_SETTING_SERIAL_PARITY_ODD:
         return g_variant_new_byte('o');
     case NM_SETTING_SERIAL_PARITY_NONE:
+        /* the default, serializes to NULL. */
+        return NULL;
     default:
-        return g_variant_new_byte('n');
+        return NULL;
     }
 }
 
 static void
-parity_from_dbus(GVariant *from, GValue *to)
+parity_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil)
 {
     switch (g_variant_get_byte(from)) {
     case 'E':
@@ -168,23 +172,11 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     NMSettingSerial *setting = NM_SETTING_SERIAL(object);
 
     switch (prop_id) {
-    case PROP_BAUD:
-        g_value_set_uint(value, nm_setting_serial_get_baud(setting));
-        break;
-    case PROP_BITS:
-        g_value_set_uint(value, nm_setting_serial_get_bits(setting));
-        break;
     case PROP_PARITY:
         g_value_set_enum(value, nm_setting_serial_get_parity(setting));
         break;
-    case PROP_STOPBITS:
-        g_value_set_uint(value, nm_setting_serial_get_stopbits(setting));
-        break;
-    case PROP_SEND_DELAY:
-        g_value_set_uint64(value, nm_setting_serial_get_send_delay(setting));
-        break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_get_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -195,23 +187,11 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
     NMSettingSerialPrivate *priv = NM_SETTING_SERIAL_GET_PRIVATE(object);
 
     switch (prop_id) {
-    case PROP_BAUD:
-        priv->baud = g_value_get_uint(value);
-        break;
-    case PROP_BITS:
-        priv->bits = g_value_get_uint(value);
-        break;
     case PROP_PARITY:
         priv->parity = g_value_get_enum(value);
         break;
-    case PROP_STOPBITS:
-        priv->stopbits = g_value_get_uint(value);
-        break;
-    case PROP_SEND_DELAY:
-        priv->send_delay = g_value_get_uint64(value);
-        break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_set_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -224,9 +204,6 @@ nm_setting_serial_init(NMSettingSerial *self)
     NMSettingSerialPrivate *priv = NM_SETTING_SERIAL_GET_PRIVATE(self);
 
     nm_assert(priv->parity == NM_SETTING_SERIAL_PARITY_NONE);
-    priv->stopbits = 1;
-    priv->baud     = 57600;
-    priv->bits     = 8;
 }
 
 /**
@@ -261,26 +238,32 @@ 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.
      **/
-    obj_properties[PROP_BAUD] = g_param_spec_uint(NM_SETTING_SERIAL_BAUD,
-                                                  "",
-                                                  "",
-                                                  0,
-                                                  G_MAXUINT,
-                                                  57600,
-                                                  G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_SERIAL_BAUD,
+                                              PROP_BAUD,
+                                              0,
+                                              G_MAXUINT32,
+                                              57600,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingSerialPrivate,
+                                              baud);
 
     /**
      * NMSettingSerial:bits:
      *
      * Byte-width of the serial communication. The 8 in "8n1" for example.
      **/
-    obj_properties[PROP_BITS] = g_param_spec_uint(NM_SETTING_SERIAL_BITS,
-                                                  "",
-                                                  "",
-                                                  5,
-                                                  8,
-                                                  8,
-                                                  G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_SERIAL_BITS,
+                                              PROP_BITS,
+                                              5,
+                                              8,
+                                              8,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingSerialPrivate,
+                                              bits);
 
     /**
      * NMSettingSerial:parity:
@@ -311,9 +294,12 @@ nm_setting_serial_class_init(NMSettingSerialClass *klass)
     _nm_properties_override_gobj(
         properties_override,
         obj_properties[PROP_PARITY],
-        NM_SETT_INFO_PROPERT_TYPE_GPROP(G_VARIANT_TYPE_BYTE,
-                                        .gprop_from_dbus_fcn = parity_from_dbus, ),
-        .to_dbus_data.gprop_to_dbus_fcn = parity_to_dbus, );
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_BYTE,
+                                       .compare_fcn = _nm_setting_property_compare_fcn_default,
+                                       .to_dbus_fcn = parity_to_dbus_fcn,
+                                       .typdata_from_dbus.gprop_fcn = parity_from_dbus,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                       .from_dbus_is_full = TRUE));
 
     /**
      * NMSettingSerial:stopbits:
@@ -321,32 +307,38 @@ 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.
      **/
-    obj_properties[PROP_STOPBITS] = g_param_spec_uint(NM_SETTING_SERIAL_STOPBITS,
-                                                      "",
-                                                      "",
-                                                      1,
-                                                      2,
-                                                      1,
-                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_SERIAL_STOPBITS,
+                                              PROP_STOPBITS,
+                                              1,
+                                              2,
+                                              1,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingSerialPrivate,
+                                              stopbits);
 
     /**
      * NMSettingSerial:send-delay:
      *
      * Time to delay between each byte sent to the modem, in microseconds.
      **/
-    obj_properties[PROP_SEND_DELAY] =
-        g_param_spec_uint64(NM_SETTING_SERIAL_SEND_DELAY,
-                            "",
-                            "",
-                            0,
-                            G_MAXUINT64,
-                            0,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint64(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_SERIAL_SEND_DELAY,
+                                              PROP_SEND_DELAY,
+                                              0,
+                                              G_MAXUINT64,
+                                              0,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingSerialPrivate,
+                                              send_delay);
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_SERIAL,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-sriov.c b/src/libnm-core-impl/nm-setting-sriov.c
index b18d184a..a024dba6 100644
--- a/src/libnm-core-impl/nm-setting-sriov.c
+++ b/src/libnm-core-impl/nm-setting-sriov.c
@@ -322,7 +322,7 @@ nm_sriov_vf_get_attribute_names(const NMSriovVF *vf)
     g_return_val_if_fail(vf, NULL);
     g_return_val_if_fail(vf->refcount > 0, NULL);
 
-    return nm_utils_strdict_get_keys(vf->attributes, TRUE, NULL);
+    return nm_strdict_get_keys(vf->attributes, TRUE, NULL);
 }
 
 /**
@@ -471,7 +471,7 @@ _nm_sriov_vf_attribute_validate_all(const NMSriovVF *vf, GError **error)
  * @vf: the #NMSriovVF
  * @vlan_id: the VLAN id
  *
- * Adds a VLAN to the VF.
+ * Adds a VLAN to the VF. Currently kernel only supports one VLAN per VF.
  *
  * Returns: %TRUE if the VLAN was added; %FALSE if it already existed
  *
@@ -535,7 +535,8 @@ vlan_id_compare(gconstpointer a, gconstpointer b, gpointer user_data)
  * @vf: the #NMSriovVF
  * @length: (out) (allow-none): on return, the number of VLANs configured
  *
- * Returns the VLANs currently configured on the VF.
+ * Returns the VLANs currently configured on the VF. Currently kernel only
+ * supports one VLAN per VF.
  *
  * Returns: (transfer none) (array length=length): a list of VLAN ids configured on the VF.
  *
@@ -875,12 +876,7 @@ _nm_setting_sriov_sort_vfs(NMSettingSriov *setting)
 /*****************************************************************************/
 
 static GVariant *
-vfs_to_dbus(const NMSettInfoSetting *               sett_info,
-            guint                                   property_idx,
-            NMConnection *                          connection,
-            NMSetting *                             setting,
-            NMConnectionSerializationFlags          flags,
-            const NMConnectionSerializationOptions *options)
+vfs_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *vfs = NULL;
     GVariantBuilder              builder;
@@ -904,7 +900,7 @@ vfs_to_dbus(const NMSettInfoSetting *               sett_info,
                                   "index",
                                   g_variant_new_uint32(nm_sriov_vf_get_index(vf)));
 
-            attr_names = nm_utils_strdict_get_keys(vf->attributes, TRUE, NULL);
+            attr_names = nm_strdict_get_keys(vf->attributes, TRUE, NULL);
             if (attr_names) {
                 for (name = attr_names; *name; name++) {
                     g_variant_builder_add(&vf_builder,
@@ -956,12 +952,7 @@ vfs_to_dbus(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-vfs_from_dbus(NMSetting *         setting,
-              GVariant *          connection_dict,
-              const char *        property,
-              GVariant *          value,
-              NMSettingParseFlags parse_flags,
-              GError **           error)
+vfs_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GPtrArray *  vfs;
     GVariantIter vf_iter;
@@ -1119,35 +1110,24 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_vfs(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
     NMSettingSriov *a;
     NMSettingSriov *b;
     guint           i;
 
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_SRIOV_VFS)) {
-        if (set_b) {
-            a = NM_SETTING_SRIOV(set_a);
-            b = NM_SETTING_SRIOV(set_b);
+    if (set_b) {
+        a = NM_SETTING_SRIOV(set_a);
+        b = NM_SETTING_SRIOV(set_b);
 
-            if (a->vfs->len != b->vfs->len)
+        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;
-            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, con_a, set_a, con_b, set_b, flags);
+    return TRUE;
 }
 
 /*****************************************************************************/
@@ -1246,8 +1226,7 @@ nm_setting_sriov_class_init(NMSettingSriovClass *klass)
     object_class->set_property = set_property;
     object_class->finalize     = finalize;
 
-    setting_class->compare_property = compare_property;
-    setting_class->verify           = verify;
+    setting_class->verify = verify;
 
     /**
      * NMSettingSriov:total-vfs
@@ -1327,6 +1306,7 @@ nm_setting_sriov_class_init(NMSettingSriovClass *klass)
                                  obj_properties[PROP_VFS],
                                  NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
                                                                 .to_dbus_fcn   = vfs_to_dbus,
+                                                                .compare_fcn   = compare_fcn_vfs,
                                                                 .from_dbus_fcn = vfs_from_dbus, ));
 
     /**
@@ -1365,8 +1345,9 @@ nm_setting_sriov_class_init(NMSettingSriovClass *klass)
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_SRIOV,
+                             NULL,
+                             properties_override,
+                             0);
 }
diff --git a/src/libnm-core-impl/nm-setting-tc-config.c b/src/libnm-core-impl/nm-setting-tc-config.c
index f44c89cb..cd13709a 100644
--- a/src/libnm-core-impl/nm-setting-tc-config.c
+++ b/src/libnm-core-impl/nm-setting-tc-config.c
@@ -320,7 +320,7 @@ nm_tc_qdisc_get_attribute_names(NMTCQdisc *qdisc)
 {
     g_return_val_if_fail(qdisc, NULL);
 
-    return nm_utils_strdict_get_keys(qdisc->attributes, TRUE, NULL);
+    return nm_strdict_get_keys(qdisc->attributes, TRUE, NULL);
 }
 
 GHashTable *
@@ -594,8 +594,8 @@ nm_tc_action_get_attribute_names(NMTCAction *action)
 
     g_return_val_if_fail(action, NULL);
 
-    names = nm_utils_strdict_get_keys(action->attributes, TRUE, NULL);
-    return nm_utils_strv_make_deep_copied_nonnull(names);
+    names = nm_strdict_get_keys(action->attributes, TRUE, NULL);
+    return nm_strv_make_deep_copied_nonnull(names);
 }
 
 GHashTable *
@@ -1314,46 +1314,40 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_qdiscs(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
     NMSettingTCConfig *a_tc_config = NM_SETTING_TC_CONFIG(set_a);
     NMSettingTCConfig *b_tc_config = NM_SETTING_TC_CONFIG(set_b);
     guint              i;
 
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_TC_CONFIG_QDISCS)) {
-        if (set_b) {
-            if (a_tc_config->qdiscs->len != b_tc_config->qdiscs->len)
+    if (set_b) {
+        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;
-            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;
     }
+    return TRUE;
+}
+
+static NMTernary
+compare_fcn_tfilter(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    NMSettingTCConfig *a_tc_config = NM_SETTING_TC_CONFIG(set_a);
+    NMSettingTCConfig *b_tc_config = NM_SETTING_TC_CONFIG(set_b);
+    guint              i;
 
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_TC_CONFIG_TFILTERS)) {
-        if (set_b) {
-            if (a_tc_config->tfilters->len != b_tc_config->tfilters->len)
+    if (set_b) {
+        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;
-            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;
     }
-
-    return NM_SETTING_CLASS(nm_setting_tc_config_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    return TRUE;
 }
 
 /**
@@ -1481,12 +1475,7 @@ next:
 }
 
 static GVariant *
-tc_qdiscs_get(const NMSettInfoSetting *               sett_info,
-              guint                                   property_idx,
-              NMConnection *                          connection,
-              NMSetting *                             setting,
-              NMConnectionSerializationFlags          flags,
-              const NMConnectionSerializationOptions *options)
+tc_qdiscs_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *qdiscs = NULL;
 
@@ -1495,19 +1484,12 @@ tc_qdiscs_get(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-tc_qdiscs_set(NMSetting *         setting,
-              GVariant *          connection_dict,
-              const char *        property,
-              GVariant *          value,
-              NMSettingParseFlags parse_flags,
-              GError **           error)
+tc_qdiscs_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    GPtrArray *qdiscs;
+    gs_unref_ptrarray GPtrArray *qdiscs = NULL;
 
     qdiscs = _qdiscs_from_variant(value);
     g_object_set(setting, NM_SETTING_TC_CONFIG_QDISCS, qdiscs, NULL);
-    g_ptr_array_unref(qdiscs);
-
     return TRUE;
 }
 
@@ -1679,12 +1661,7 @@ next:
 }
 
 static GVariant *
-tc_tfilters_get(const NMSettInfoSetting *               sett_info,
-                guint                                   property_idx,
-                NMConnection *                          connection,
-                NMSetting *                             setting,
-                NMConnectionSerializationFlags          flags,
-                const NMConnectionSerializationOptions *options)
+tc_tfilters_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *tfilters = NULL;
 
@@ -1693,12 +1670,7 @@ tc_tfilters_get(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-tc_tfilters_set(NMSetting *         setting,
-                GVariant *          connection_dict,
-                const char *        property,
-                GVariant *          value,
-                NMSettingParseFlags parse_flags,
-                GError **           error)
+tc_tfilters_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *tfilters = NULL;
 
@@ -1803,8 +1775,7 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass)
     object_class->set_property = set_property;
     object_class->finalize     = finalize;
 
-    setting_class->compare_property = compare_property;
-    setting_class->verify           = verify;
+    setting_class->verify = verify;
 
     /**
      * NMSettingTCConfig:qdiscs: (type GPtrArray(NMTCQdisc))
@@ -1820,6 +1791,282 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass)
      * If the #NMSettingTCConfig setting is not present, NetworkManager
      * doesn't touch the qdiscs present on the interface.
      **/
+    /* ---nmcli---
+     * property: qdiscs
+     * format: GPtrArray(NMTCQdisc)
+     * description-docbook:
+     *  <para>
+     *  Array of TC queueing disciplines. qdisc is a basic block in the
+     *  Linux traffic control subsystem
+     *  </para>
+     *  <para>
+     *   Each qdisc can be specified by the following attributes:
+     *  </para>
+     *  <variablelist>
+     *    <varlistentry>
+     *      <term><varname>handle HANDLE</varname></term>
+     *      <listitem>
+     *        <para>
+     *          specifies the qdisc handle. A qdisc, which potentially can have children, gets
+     *          assigned a major number, called a 'handle', leaving the minor number namespace
+     *          available for classes. The handle is expressed as '10:'. It is customary to
+     *          explicitly assign a handle to qdiscs expected to have children.
+     *        </para>
+     *      </listitem>
+     *  </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>parent HANDLE</varname></term>
+     *      <listitem>
+     *        <para>
+     *          specifies the handle of the parent qdisc the current qdisc must be
+     *          attached to.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>root</varname></term>
+     *      <listitem>
+     *        <para>
+     *          specifies that the qdisc is attached to the root of device.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>KIND</varname></term>
+     *      <listitem>
+     *        <para>
+     *          this is the qdisc kind. NetworkManager currently supports the
+     *          following kinds: fq_codel, sfq, tbf. Each qdisc kind has a
+     *          different set of parameters, described below. There are also some
+     *          kinds like pfifo, pfifo_fast, prio supported by NetworkManager
+     *          but their parameters are not supported by NetworkManager.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *  </variablelist>
+     *  <para>
+     *   Parameters for 'fq_codel':
+     *  </para>
+     *  <variablelist>
+     *    <varlistentry>
+     *      <term><varname>limit U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *          the hard limit on the real queue size.  When this limit is
+     *          reached, incoming packets are dropped. Default is 10240 packets.
+     *        </para>
+     *      </listitem>
+     *  </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>memory_limit U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *          sets a limit on the total number of bytes that can be queued in
+     *          this FQ-CoDel instance. The lower of the packet limit of the
+     *          limit parameter and the memory limit will be enforced. Default is
+     *          32 MB.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>flows U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     the number of flows into which the incoming packets are
+     *     classified. Due to the stochastic nature of hashing, multiple
+     *     flows may end up being hashed into the same slot. Newer flows
+     *     have priority over older ones. This parameter can be set only at
+     *     load time since memory has to be allocated for the hash table.
+     *     Default value is 1024.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>target U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     the acceptable minimum standing/persistent queue delay. This minimum
+     *     delay is identified by tracking the local minimum queue delay that packets
+     *     experience. The unit of measurement is microsecond(us). Default value is 5ms.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>interval U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     used to ensure that the measured minimum delay does not become too stale.
+     *     The minimum delay must be experienced in the last epoch of length .B
+     *     interval.  It should be set on the order of the worst-case RTT
+     *     through the bottleneck to give endpoints sufficient time to
+     *     react. Default value is 100ms.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>quantum U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     the number of bytes used as 'deficit' in the fair queuing
+     *     algorithm. Default is set to 1514 bytes which corresponds to the
+     *     Ethernet MTU plus the hardware header length of 14 bytes.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>ecn BOOL</varname></term>
+     *      <listitem>
+     *        <para>
+     *     can be used to mark packets instead of dropping them. ecn is turned
+     *     on by default.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>ce_threshold U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     sets a threshold above which all packets are marked with ECN
+     *     Congestion Experienced. This is useful for DCTCP-style congestion
+     *     control algorithms that require marking at very shallow queueing
+     *     thresholds.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *  </variablelist>
+     *  <para>
+     *   Parameters for 'sfq':
+     *  </para>
+     *  <variablelist>
+     *    <varlistentry>
+     *      <term><varname>divisor U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     can be used to set a different hash table size, available
+     *     from kernel 2.6.39 onwards.  The specified divisor must be
+     *     a power of two and cannot be larger than 65536.  Default
+     *     value: 1024.
+     *        </para>
+     *      </listitem>
+     *  </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>limit U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     Upper limit of the SFQ. Can be used to reduce the default
+     *     length of 127 packets.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>depth U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     Limit of packets per flow. Default to
+     *     127 and can be lowered.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>perturb_period U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     Interval in seconds for queue algorithm perturbation.
+     *     Defaults to 0, which means that no perturbation occurs. Do
+     *     not set too low for each perturbation may cause some
+     *     packet reordering or losses. Advised value: 60 This value
+     *     has no effect when external flow classification is used.
+     *     Its better to increase divisor value to lower risk of hash
+     *     collisions.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>quantum U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     Amount of bytes a flow is allowed to dequeue during a
+     *     round of the round robin process.  Defaults to the MTU of
+     *     the interface which is also the advised value and the
+     *     minimum value.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>flows U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     Default value is 127.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *  </variablelist>
+     *  <para>
+     *   Parameters for 'tbf':
+     *  </para>
+     *  <variablelist>
+     *    <varlistentry>
+     *      <term><varname>rate U64</varname></term>
+     *      <listitem>
+     *        <para>
+     *     Bandwidth or rate.  These parameters accept a floating
+     *     point number, possibly followed by either a unit (both SI
+     *     and IEC units supported), or a float followed by a percent
+     *     character to specify the rate as a percentage of the
+     *     device's speed.
+     *        </para>
+     *      </listitem>
+     *  </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>burst U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     Also known as buffer or maxburst.  Size of the bucket, in
+     *     bytes. This is the maximum amount of bytes that tokens can
+     *     be available for instantaneously.  In general, larger
+     *     shaping rates require a larger buffer. For 10mbit/s on
+     *     Intel, you need at least 10kbyte buffer if you want to
+     *     reach your configured rate!
+     *        </para>
+     *        <para>
+     *     If your buffer is too small, packets may be dropped
+     *     because more tokens arrive per timer tick than fit in your
+     *     bucket.  The minimum buffer size can be calculated by
+     *     dividing the rate by HZ.
+     *        </para>
+     *        <para>
+     *     Token usage calculations are performed using a table which
+     *     by default has a resolution of 8 packets.  This resolution
+     *     can be changed by specifying the cell size with the burst.
+     *     For example, to specify a 6000 byte buffer with a 16 byte
+     *     cell size, set a burst of 6000/16. You will probably never
+     *     have to set this. Must be an integral power of 2.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>limit U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     Limit is the number of bytes that can be queued waiting
+     *     for tokens to become available.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>latency U32</varname></term>
+     *      <listitem>
+     *        <para>
+     *     specifies the maximum amount of time a packet can
+     *     sit in the TBF. The latency calculation takes into account
+     *     the size of the bucket, the rate and possibly the peakrate
+     *     (if set). The latency and limit are mutually exclusive.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *  </variablelist>
+     * ---end---
+     **/
     /* ---ifcfg-rh---
      * property: qdiscs
      * variable: QDISC1(+), QDISC2(+), ..., TC_COMMIT(+)
@@ -1840,6 +2087,7 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass)
                                  obj_properties[PROP_QDISCS],
                                  NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
                                                                 .to_dbus_fcn   = tc_qdiscs_get,
+                                                                .compare_fcn   = compare_fcn_qdiscs,
                                                                 .from_dbus_fcn = tc_qdiscs_set, ));
 
     /**
@@ -1854,6 +2102,109 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass)
      * If the #NMSettingTCConfig setting is not present, NetworkManager
      * doesn't touch the filters present on the interface.
      **/
+    /* ---nmcli---
+     * property: tfilters
+     * format: GPtrArray(NMTCTfilter)
+     * description-docbook:
+     *  <para>
+     * Array of TC traffic filters. Traffic control can manage the packet content during
+     * classification by using filters.
+     *  </para>
+     *  <para>
+     *   Each tfilters can be specified by the following attributes:
+     *  </para>
+     *  <variablelist>
+     *    <varlistentry>
+     *      <term><varname>handle HANDLE</varname></term>
+     *      <listitem>
+     *        <para>
+     *          specifies the tfilters handle. A filter is used by a classful qdisc to determine in which class
+     *          a packet will be enqueued. It is important to notice that filters reside within qdiscs. Therefore,
+     *          see qdiscs handle for detailed information.
+     *        </para>
+     *      </listitem>
+     *  </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>parent HANDLE</varname></term>
+     *      <listitem>
+     *        <para>
+     *          specifies the handle of the parent qdisc the current qdisc must be
+     *          attached to.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>root</varname></term>
+     *      <listitem>
+     *        <para>
+     *          specifies that the qdisc is attached to the root of device.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>KIND</varname></term>
+     *      <listitem>
+     *        <para>
+     *          this is the tfilters kind. NetworkManager currently supports
+     *          following kinds: mirred, simple. Each filter kind has a
+     *          different set of actions, described below. There are also some
+     *          other kinds like matchall, basic, u32 supported by NetworkManager.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *  </variablelist>
+     *  <para>
+     *   Actions for 'mirred':
+     *  </para>
+     *  <variablelist>
+     *    <varlistentry>
+     *      <term><varname>egress bool</varname></term>
+     *      <listitem>
+     *        <para>
+     *          Define whether the packet should exit from the interface.
+     *        </para>
+     *      </listitem>
+     *  </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>ingress bool</varname></term>
+     *      <listitem>
+     *        <para>
+     *          Define whether the packet should come into the interface.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>mirror bool</varname></term>
+     *      <listitem>
+     *        <para>
+     *     Define whether the packet should be copied to the destination space.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *      <varlistentry>
+     *      <term><varname>redirect bool</varname></term>
+     *      <listitem>
+     *        <para>
+     *     Define whether the packet should be moved to the destination space.
+     *        </para>
+     *      </listitem>
+     *    </varlistentry>
+     *  </variablelist>
+     *  <para>
+     *   Action for 'simple':
+     *  </para>
+     *  <variablelist>
+     *    <varlistentry>
+     *      <term><varname>sdata char[32]</varname></term>
+     *      <listitem>
+     *        <para>
+     *      The actual string to print.
+     *        </para>
+     *      </listitem>
+     *  </varlistentry>
+     *  </variablelist>
+     * ---end---
+     **/
     /* ---ifcfg-rh---
      * property: qdiscs
      * variable: FILTER1(+), FILTER2(+), ..., TC_COMMIT(+)
@@ -1875,12 +2226,14 @@ nm_setting_tc_config_class_init(NMSettingTCConfigClass *klass)
         obj_properties[PROP_TFILTERS],
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
                                        .to_dbus_fcn   = tc_tfilters_get,
+                                       .compare_fcn   = compare_fcn_tfilter,
                                        .from_dbus_fcn = tc_tfilters_set, ));
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_TC_CONFIG,
+                             NULL,
+                             properties_override,
+                             0);
 }
diff --git a/src/libnm-core-impl/nm-setting-team-port.c b/src/libnm-core-impl/nm-setting-team-port.c
index 316fc67c..5e5f8910 100644
--- a/src/libnm-core-impl/nm-setting-team-port.c
+++ b/src/libnm-core-impl/nm-setting-team-port.c
@@ -350,52 +350,45 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_link_watchers(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
     NMSettingTeamPortPrivate *a_priv;
     NMSettingTeamPortPrivate *b_priv;
 
-    if (nm_streq(sett_info->property_infos[property_idx].name,
-                 NM_SETTING_TEAM_PORT_LINK_WATCHERS)) {
-        if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
-            return NM_TERNARY_DEFAULT;
-        if (!set_b)
-            return TRUE;
-        a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE(set_a);
-        b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE(set_b);
-        return nm_team_link_watchers_equal(a_priv->team_setting->d.link_watchers,
-                                           b_priv->team_setting->d.link_watchers,
-                                           TRUE);
-    }
+    if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+        return NM_TERNARY_DEFAULT;
+    if (!set_b)
+        return TRUE;
+    a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE(set_a);
+    b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE(set_b);
+    return nm_team_link_watchers_equal(a_priv->team_setting->d.link_watchers,
+                                       b_priv->team_setting->d.link_watchers,
+                                       TRUE);
+}
+
+static NMTernary
+compare_fcn_config(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    NMSettingTeamPortPrivate *a_priv;
+    NMSettingTeamPortPrivate *b_priv;
 
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_PORT_CONFIG)) {
-        if (set_b) {
-            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
+    if (set_b) {
+        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 TRUE;
-            }
-
-            a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE(set_a);
-            b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE(set_b);
-
-            return nm_streq0(nm_team_setting_config_get(a_priv->team_setting),
-                             nm_team_setting_config_get(b_priv->team_setting));
+            return TRUE;
         }
 
-        return TRUE;
+        a_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE(set_a);
+        b_priv = NM_SETTING_TEAM_PORT_GET_PRIVATE(set_b);
+
+        return nm_streq0(nm_team_setting_config_get(a_priv->team_setting),
+                         nm_team_setting_config_get(b_priv->team_setting));
     }
 
-    return NM_SETTING_CLASS(nm_setting_team_port_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    return TRUE;
 }
 
 static void
@@ -545,7 +538,6 @@ nm_setting_team_port_class_init(NMSettingTeamPortClass *klass)
     object_class->set_property = set_property;
     object_class->finalize     = finalize;
 
-    setting_class->compare_property          = compare_property;
     setting_class->verify                    = verify;
     setting_class->duplicate_copy_properties = duplicate_copy_properties;
     setting_class->init_from_dbus            = init_from_dbus;
@@ -570,9 +562,14 @@ nm_setting_team_port_class_init(NMSettingTeamPortClass *klass)
         "",
         NULL,
         G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[NM_TEAM_ATTRIBUTE_CONFIG],
-                                 &nm_sett_info_propert_type_team_s);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[NM_TEAM_ATTRIBUTE_CONFIG],
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING,
+                                       .compare_fcn   = compare_fcn_config,
+                                       .to_dbus_fcn   = _nm_team_settings_property_to_dbus,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                       .from_dbus_is_full = TRUE));
 
     /**
      * NMSettingTeamPort:queue-id:
@@ -688,14 +685,22 @@ nm_setting_team_port_class_init(NMSettingTeamPortClass *klass)
                            "",
                            G_TYPE_PTR_ARRAY,
                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[NM_TEAM_ATTRIBUTE_LINK_WATCHERS],
-                                 &nm_sett_info_propert_type_team_link_watchers);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[NM_TEAM_ATTRIBUTE_LINK_WATCHERS],
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
+                                       .compare_fcn = compare_fcn_link_watchers,
+                                       .to_dbus_fcn = _nm_team_settings_property_to_dbus,
+                                       .typdata_from_dbus.gprop_fcn =
+                                           _nm_team_settings_property_from_dbus_link_watchers,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                       .from_dbus_is_full = TRUE));
 
     g_object_class_install_properties(object_class, G_N_ELEMENTS(obj_properties), obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_TEAM_PORT,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_TEAM_PORT,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-team.c b/src/libnm-core-impl/nm-setting-team.c
index b9a368c5..1a11b043 100644
--- a/src/libnm-core-impl/nm-setting-team.c
+++ b/src/libnm-core-impl/nm-setting-team.c
@@ -1275,50 +1275,45 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_link_watchers(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
-    NMSettingTeamPrivate *a_priv, *b_priv;
+    NMSettingTeamPrivate *a_priv;
+    NMSettingTeamPrivate *b_priv;
 
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_LINK_WATCHERS)) {
-        if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
-            return NM_TERNARY_DEFAULT;
-        if (!set_b)
-            return TRUE;
-        a_priv = NM_SETTING_TEAM_GET_PRIVATE(set_a);
-        b_priv = NM_SETTING_TEAM_GET_PRIVATE(set_b);
-        return nm_team_link_watchers_equal(a_priv->team_setting->d.link_watchers,
-                                           b_priv->team_setting->d.link_watchers,
-                                           TRUE);
-    }
+    if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+        return NM_TERNARY_DEFAULT;
+    if (!set_b)
+        return TRUE;
+    a_priv = NM_SETTING_TEAM_GET_PRIVATE(set_a);
+    b_priv = NM_SETTING_TEAM_GET_PRIVATE(set_b);
+    return nm_team_link_watchers_equal(a_priv->team_setting->d.link_watchers,
+                                       b_priv->team_setting->d.link_watchers,
+                                       TRUE);
+}
 
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_TEAM_CONFIG)) {
-        if (set_b) {
-            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
+static NMTernary
+compare_fcn_config(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    NMSettingTeamPrivate *a_priv;
+    NMSettingTeamPrivate *b_priv;
+
+    if (set_b) {
+        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 TRUE;
-            }
-
-            a_priv = NM_SETTING_TEAM_GET_PRIVATE(set_a);
-            b_priv = NM_SETTING_TEAM_GET_PRIVATE(set_b);
-
-            return nm_streq0(nm_team_setting_config_get(a_priv->team_setting),
-                             nm_team_setting_config_get(b_priv->team_setting));
+            return TRUE;
         }
 
-        return TRUE;
+        a_priv = NM_SETTING_TEAM_GET_PRIVATE(set_a);
+        b_priv = NM_SETTING_TEAM_GET_PRIVATE(set_b);
+
+        return nm_streq0(nm_team_setting_config_get(a_priv->team_setting),
+                         nm_team_setting_config_get(b_priv->team_setting));
     }
 
-    return NM_SETTING_CLASS(nm_setting_team_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    return TRUE;
 }
 
 static void
@@ -1505,7 +1500,6 @@ nm_setting_team_class_init(NMSettingTeamClass *klass)
     object_class->set_property = set_property;
     object_class->finalize     = finalize;
 
-    setting_class->compare_property          = compare_property;
     setting_class->verify                    = verify;
     setting_class->duplicate_copy_properties = duplicate_copy_properties;
     setting_class->init_from_dbus            = init_from_dbus;
@@ -1530,9 +1524,14 @@ nm_setting_team_class_init(NMSettingTeamClass *klass)
         "",
         NULL,
         G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[NM_TEAM_ATTRIBUTE_CONFIG],
-                                 &nm_sett_info_propert_type_team_s);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[NM_TEAM_ATTRIBUTE_CONFIG],
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING,
+                                       .compare_fcn   = compare_fcn_config,
+                                       .to_dbus_fcn   = _nm_team_settings_property_to_dbus,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                       .from_dbus_is_full = TRUE));
 
     /**
      * NMSettingTeam:notify-peers-count:
@@ -1809,9 +1808,16 @@ nm_setting_team_class_init(NMSettingTeamClass *klass)
                            "",
                            G_TYPE_PTR_ARRAY,
                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[NM_TEAM_ATTRIBUTE_LINK_WATCHERS],
-                                 &nm_sett_info_propert_type_team_link_watchers);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[NM_TEAM_ATTRIBUTE_LINK_WATCHERS],
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
+                                       .compare_fcn = compare_fcn_link_watchers,
+                                       .to_dbus_fcn = _nm_team_settings_property_to_dbus,
+                                       .typdata_from_dbus.gprop_fcn =
+                                           _nm_team_settings_property_from_dbus_link_watchers,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                       .from_dbus_is_full = TRUE));
 
     /* ---dbus---
      * property: interface-name
@@ -1827,8 +1833,9 @@ nm_setting_team_class_init(NMSettingTeamClass *klass)
 
     g_object_class_install_properties(object_class, G_N_ELEMENTS(obj_properties), obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_TEAM,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_TEAM,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-tun.c b/src/libnm-core-impl/nm-setting-tun.c
index d75dcf80..c2694847 100644
--- a/src/libnm-core-impl/nm-setting-tun.c
+++ b/src/libnm-core-impl/nm-setting-tun.c
@@ -32,12 +32,12 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_MODE,
                                   PROP_MULTI_QUEUE, );
 
 typedef struct {
-    char *           owner;
-    char *           group;
-    NMSettingTunMode mode;
-    bool             pi : 1;
-    bool             vnet_hdr : 1;
-    bool             multi_queue : 1;
+    char *  owner;
+    char *  group;
+    guint32 mode;
+    bool    pi;
+    bool    vnet_hdr;
+    bool    multi_queue;
 } NMSettingTunPrivate;
 
 /**
@@ -198,77 +198,8 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 static void
-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:
-        g_value_set_uint(value, priv->mode);
-        break;
-    case PROP_OWNER:
-        g_value_set_string(value, priv->owner);
-        break;
-    case PROP_GROUP:
-        g_value_set_string(value, priv->group);
-        break;
-    case PROP_PI:
-        g_value_set_boolean(value, priv->pi);
-        break;
-    case PROP_VNET_HDR:
-        g_value_set_boolean(value, priv->vnet_hdr);
-        break;
-    case PROP_MULTI_QUEUE:
-        g_value_set_boolean(value, priv->multi_queue);
-        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)
-{
-    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);
-        break;
-    case PROP_OWNER:
-        g_free(priv->owner);
-        priv->owner = g_value_dup_string(value);
-        break;
-    case PROP_GROUP:
-        g_free(priv->group);
-        priv->group = g_value_dup_string(value);
-        break;
-    case PROP_PI:
-        priv->pi = g_value_get_boolean(value);
-        break;
-    case PROP_VNET_HDR:
-        priv->vnet_hdr = g_value_get_boolean(value);
-        break;
-    case PROP_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 *self)
-{
-    NMSettingTunPrivate *priv = NM_SETTING_TUN_GET_PRIVATE(self);
-
-    priv->mode = NM_SETTING_TUN_MODE_TUN;
-}
+{}
 
 /**
  * nm_setting_tun_new:
@@ -286,18 +217,6 @@ nm_setting_tun_new(void)
 }
 
 static void
-finalize(GObject *object)
-{
-    NMSettingTun *       setting = NM_SETTING_TUN(object);
-    NMSettingTunPrivate *priv    = NM_SETTING_TUN_GET_PRIVATE(setting);
-
-    g_free(priv->owner);
-    g_free(priv->group);
-
-    G_OBJECT_CLASS(nm_setting_tun_parent_class)->finalize(object);
-}
-
-static void
 nm_setting_tun_class_init(NMSettingTunClass *klass)
 {
     GObjectClass *  object_class        = G_OBJECT_CLASS(klass);
@@ -306,9 +225,8 @@ nm_setting_tun_class_init(NMSettingTunClass *klass)
 
     g_type_class_add_private(klass, sizeof(NMSettingTunPrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-    object_class->finalize     = finalize;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify = verify;
 
@@ -322,14 +240,16 @@ nm_setting_tun_class_init(NMSettingTunClass *klass)
      *
      * Since: 1.2
      */
-    obj_properties[PROP_MODE] =
-        g_param_spec_uint(NM_SETTING_TUN_MODE,
-                          "",
-                          "",
-                          0,
-                          G_MAXUINT,
-                          NM_SETTING_TUN_MODE_TUN,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_TUN_MODE,
+                                              PROP_MODE,
+                                              0,
+                                              G_MAXUINT32,
+                                              NM_SETTING_TUN_MODE_TUN,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingTunPrivate,
+                                              mode);
 
     /**
      * NMSettingTun:owner:
@@ -339,12 +259,13 @@ nm_setting_tun_class_init(NMSettingTunClass *klass)
      *
      * Since: 1.2
      */
-    obj_properties[PROP_OWNER] = g_param_spec_string(NM_SETTING_TUN_OWNER,
-                                                     "",
-                                                     "",
-                                                     NULL,
-                                                     G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE
-                                                         | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_TUN_OWNER,
+                                              PROP_OWNER,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingTunPrivate,
+                                              owner);
 
     /**
      * NMSettingTun:group:
@@ -354,12 +275,13 @@ nm_setting_tun_class_init(NMSettingTunClass *klass)
      *
      * Since: 1.2
      */
-    obj_properties[PROP_GROUP] = g_param_spec_string(NM_SETTING_TUN_GROUP,
-                                                     "",
-                                                     "",
-                                                     NULL,
-                                                     G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE
-                                                         | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_TUN_GROUP,
+                                              PROP_GROUP,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingTunPrivate,
+                                              group);
 
     /**
      * NMSettingTun:pi:
@@ -369,13 +291,14 @@ nm_setting_tun_class_init(NMSettingTunClass *klass)
      *
      * Since: 1.2
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_TUN_PI,
-                                        PROP_PI,
-                                        FALSE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_tun_get_pi);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_TUN_PI,
+                                               PROP_PI,
+                                               FALSE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingTunPrivate,
+                                               pi);
 
     /**
      * NMSettingTun:vnet-hdr:
@@ -385,13 +308,14 @@ nm_setting_tun_class_init(NMSettingTunClass *klass)
      *
      * Since: 1.2
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_TUN_VNET_HDR,
-                                        PROP_VNET_HDR,
-                                        FALSE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_tun_get_vnet_hdr);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_TUN_VNET_HDR,
+                                               PROP_VNET_HDR,
+                                               FALSE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingTunPrivate,
+                                               vnet_hdr);
 
     /**
      * NMSettingTun:multi-queue:
@@ -403,18 +327,20 @@ nm_setting_tun_class_init(NMSettingTunClass *klass)
      *
      * Since: 1.2
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_TUN_MULTI_QUEUE,
-                                        PROP_MULTI_QUEUE,
-                                        FALSE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_tun_get_multi_queue);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_TUN_MULTI_QUEUE,
+                                               PROP_MULTI_QUEUE,
+                                               FALSE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingTunPrivate,
+                                               multi_queue);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_TUN,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_TUN,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-user.c b/src/libnm-core-impl/nm-setting-user.c
index 0f99baa2..4068f337 100644
--- a/src/libnm-core-impl/nm-setting-user.c
+++ b/src/libnm-core-impl/nm-setting-user.c
@@ -232,7 +232,7 @@ nm_setting_user_get_keys(NMSettingUser *setting, guint *out_len)
         return priv->keys;
     }
 
-    priv->keys = nm_utils_strdict_get_keys(priv->data, TRUE, out_len);
+    priv->keys = nm_strdict_get_keys(priv->data, TRUE, out_len);
 
     /* don't return %NULL, but hijack the @keys fields as a pseudo
      * empty strv array. */
@@ -254,18 +254,10 @@ nm_setting_user_get_keys(NMSettingUser *setting, guint *out_len)
 const char *
 nm_setting_user_get_data(NMSettingUser *setting, const char *key)
 {
-    NMSettingUser *       self = setting;
-    NMSettingUserPrivate *priv;
-
-    g_return_val_if_fail(NM_IS_SETTING_USER(self), NULL);
+    g_return_val_if_fail(NM_IS_SETTING_USER(setting), NULL);
     g_return_val_if_fail(key, NULL);
 
-    priv = NM_SETTING_USER_GET_PRIVATE(self);
-
-    if (!priv->data)
-        return NULL;
-
-    return g_hash_table_lookup(priv->data, key);
+    return nm_g_hash_table_lookup(NM_SETTING_USER_GET_PRIVATE(setting)->data, key);
 }
 
 /**
@@ -397,34 +389,21 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_data(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
-    NMSettingUserPrivate *priv, *pri2;
+    NMSettingUserPrivate *priv;
+    NMSettingUserPrivate *pri2;
 
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_USER_DATA)) {
-        if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
-            return NM_TERNARY_DEFAULT;
+    if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+        return NM_TERNARY_DEFAULT;
 
-        if (!set_b)
-            return TRUE;
-
-        priv = NM_SETTING_USER_GET_PRIVATE(NM_SETTING_USER(set_a));
-        pri2 = NM_SETTING_USER_GET_PRIVATE(NM_SETTING_USER(set_b));
-        return nm_utils_hashtable_equal(priv->data, pri2->data, TRUE, g_str_equal)
-               && nm_utils_hashtable_equal(priv->data_invalid,
-                                           pri2->data_invalid,
-                                           TRUE,
-                                           g_str_equal);
-    }
+    if (!set_b)
+        return TRUE;
 
-    return NM_SETTING_CLASS(nm_setting_user_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    priv = NM_SETTING_USER_GET_PRIVATE(NM_SETTING_USER(set_a));
+    pri2 = NM_SETTING_USER_GET_PRIVATE(NM_SETTING_USER(set_b));
+    return nm_utils_hashtable_equal(priv->data, pri2->data, TRUE, g_str_equal)
+           && nm_utils_hashtable_equal(priv->data_invalid, pri2->data_invalid, TRUE, g_str_equal);
 }
 
 /*****************************************************************************/
@@ -552,8 +531,7 @@ nm_setting_user_class_init(NMSettingUserClass *klass)
     object_class->set_property = set_property;
     object_class->finalize     = finalize;
 
-    setting_class->compare_property = compare_property;
-    setting_class->verify           = verify;
+    setting_class->verify = verify;
 
     /**
      * NMSettingUser:data: (type GHashTable(utf8,utf8))
@@ -581,14 +559,22 @@ nm_setting_user_class_init(NMSettingUserClass *klass)
                                                    "",
                                                    G_TYPE_HASH_TABLE,
                                                    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_DATA],
-                                 &nm_sett_info_propert_type_strdict);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_DATA],
+        NM_SETT_INFO_PROPERT_TYPE_GPROP(NM_G_VARIANT_TYPE("a{ss}"),
+                                        .typdata_from_dbus.gprop_fcn = _nm_utils_strdict_from_dbus,
+                                        .typdata_to_dbus.gprop_type =
+                                            NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT,
+                                        .compare_fcn   = compare_fcn_data,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                        .from_dbus_is_full = TRUE));
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_USER,
+                             NULL,
+                             properties_override,
+                             G_STRUCT_OFFSET(NMSettingUser, _priv));
 }
diff --git a/src/libnm-core-impl/nm-setting-veth.c b/src/libnm-core-impl/nm-setting-veth.c
index 70cfe50b..43d8da4f 100644
--- a/src/libnm-core-impl/nm-setting-veth.c
+++ b/src/libnm-core-impl/nm-setting-veth.c
@@ -190,5 +190,5 @@ nm_setting_veth_class_init(NMSettingVethClass *klass)
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_VETH);
+    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_VETH, NULL, NULL, 0);
 }
diff --git a/src/libnm-core-impl/nm-setting-vlan.c b/src/libnm-core-impl/nm-setting-vlan.c
index e4e07e35..1124953e 100644
--- a/src/libnm-core-impl/nm-setting-vlan.c
+++ b/src/libnm-core-impl/nm-setting-vlan.c
@@ -90,6 +90,7 @@ guint32
 nm_setting_vlan_get_id(NMSettingVlan *setting)
 {
     g_return_val_if_fail(NM_IS_SETTING_VLAN(setting), 0);
+
     return NM_SETTING_VLAN_GET_PRIVATE(setting)->id;
 }
 
@@ -676,22 +677,13 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static GVariant *
-_override_flags_get(const NMSettInfoSetting *               sett_info,
-                    guint                                   property_idx,
-                    NMConnection *                          connection,
-                    NMSetting *                             setting,
-                    NMConnectionSerializationFlags          flags,
-                    const NMConnectionSerializationOptions *options)
+_override_flags_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     return g_variant_new_uint32(nm_setting_vlan_get_flags((NMSettingVlan *) setting));
 }
 
 static gboolean
-_override_flags_not_set(NMSetting *         setting,
-                        GVariant *          connection_dict,
-                        const char *        property,
-                        NMSettingParseFlags parse_flags,
-                        GError **           error)
+_override_flags_not_set(_NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     /* we changed the default value for FLAGS. When an older client
      * doesn't serialize the property, we assume it is the old default. */
@@ -747,9 +739,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     case PROP_PARENT:
         g_value_set_string(value, priv->parent);
         break;
-    case PROP_ID:
-        g_value_set_uint(value, priv->id);
-        break;
     case PROP_FLAGS:
         g_value_set_flags(value, priv->flags);
         break;
@@ -760,7 +749,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_take_boxed(value, priority_maplist_to_strv(priv->egress_priority_map));
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_get_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -776,9 +765,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         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;
@@ -793,7 +779,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
             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);
+        _nm_setting_property_set_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -885,14 +871,16 @@ nm_setting_vlan_class_init(NMSettingVlanClass *klass)
      *   prefer the detected ID from the DEVICE over VLAN_ID.
      * ---end---
      */
-    obj_properties[PROP_ID] =
-        g_param_spec_uint(NM_SETTING_VLAN_ID,
-                          "",
-                          "",
-                          0,
-                          4095,
-                          0,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_VLAN_ID,
+                                              PROP_ID,
+                                              0,
+                                              4095,
+                                              0,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingVlanPrivate,
+                                              id);
 
     /**
      * NMSettingVlan:flags:
@@ -927,8 +915,11 @@ nm_setting_vlan_class_init(NMSettingVlanClass *klass)
         properties_override,
         obj_properties[PROP_FLAGS],
         NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_UINT32,
-                                       .to_dbus_fcn           = _override_flags_get,
-                                       .missing_from_dbus_fcn = _override_flags_not_set, ));
+                                       .to_dbus_fcn = _override_flags_get,
+                                       .compare_fcn = _nm_setting_property_compare_fcn_default,
+                                       .missing_from_dbus_fcn = _override_flags_not_set,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                       .from_dbus_is_full = TRUE));
 
     /**
      * NMSettingVlan:ingress-priority-map:
@@ -994,8 +985,9 @@ nm_setting_vlan_class_init(NMSettingVlanClass *klass)
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_VLAN,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-vpn.c b/src/libnm-core-impl/nm-setting-vpn.c
index 351f3fd7..a543051b 100644
--- a/src/libnm-core-impl/nm-setting-vpn.c
+++ b/src/libnm-core-impl/nm-setting-vpn.c
@@ -47,11 +47,6 @@ typedef struct {
      */
     char *user_name;
 
-    /* Whether the VPN stays up across link changes, until the user
-     * explicitly disconnects it.
-     */
-    gboolean persistent;
-
     /* The hash table is created at setting object
      * init time and should not be replaced.  It is
      * a char * -> char * mapping, and both the key
@@ -72,6 +67,12 @@ typedef struct {
 
     /* Timeout for the VPN service to establish the connection */
     guint32 timeout;
+
+    /* Whether the VPN stays up across link changes, until the user
+     * explicitly disconnects it.
+     */
+    bool persistent;
+
 } NMSettingVpnPrivate;
 
 /**
@@ -240,7 +241,7 @@ nm_setting_vpn_get_data_keys(NMSettingVpn *setting, guint *out_length)
 {
     g_return_val_if_fail(NM_IS_SETTING_VPN(setting), NULL);
 
-    return nm_utils_strdict_get_keys(NM_SETTING_VPN_GET_PRIVATE(setting)->data, TRUE, out_length);
+    return nm_strdict_get_keys(NM_SETTING_VPN_GET_PRIVATE(setting)->data, TRUE, out_length);
 }
 
 /**
@@ -277,7 +278,7 @@ foreach_item_helper(NMSettingVpn *self, GHashTable **p_hash, NMVpnIterFunc func,
     nm_assert(NM_IS_SETTING_VPN(self));
     nm_assert(func);
 
-    keys = nm_utils_strv_make_deep_copied(nm_utils_strdict_get_keys(*p_hash, TRUE, &len));
+    keys = nm_strv_make_deep_copied(nm_strdict_get_keys(*p_hash, TRUE, &len));
     if (len == 0u) {
         nm_assert(!keys);
         return;
@@ -407,9 +408,7 @@ nm_setting_vpn_get_secret_keys(NMSettingVpn *setting, guint *out_length)
 {
     g_return_val_if_fail(NM_IS_SETTING_VPN(setting), NULL);
 
-    return nm_utils_strdict_get_keys(NM_SETTING_VPN_GET_PRIVATE(setting)->secrets,
-                                     TRUE,
-                                     out_length);
+    return nm_strdict_get_keys(NM_SETTING_VPN_GET_PRIVATE(setting)->secrets, TRUE, out_length);
 }
 
 /**
@@ -850,41 +849,30 @@ compare_property_secrets(NMSettingVpn *a, NMSettingVpn *b, NMSettingCompareFlags
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_secrets(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
-    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(set_a), NM_SETTING_VPN(set_b), flags);
-    }
-
-    return NM_SETTING_CLASS(nm_setting_vpn_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+        return NM_TERNARY_DEFAULT;
+    return compare_property_secrets(NM_SETTING_VPN(set_a), NM_SETTING_VPN(set_b), flags);
 }
 
 static gboolean
 clear_secrets(const NMSettInfoSetting *        sett_info,
-              guint                            property_idx,
+              const NMSettInfoProperty *       property_info,
               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;
+    NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE(setting);
     GHashTableIter       iter;
     const char *         secret;
     gboolean             changed = TRUE;
 
-    if (!prop_spec || !NM_FLAGS_HAS(prop_spec->flags, NM_SETTING_PARAM_SECRET))
+    if (!property_info->param_spec
+        || !NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET))
         return FALSE;
 
-    nm_assert(nm_streq(prop_spec->name, NM_SETTING_VPN_SECRETS));
+    nm_assert(nm_streq(property_info->param_spec->name, NM_SETTING_VPN_SECRETS));
 
     if (!priv->secrets)
         return FALSE;
@@ -913,12 +901,7 @@ clear_secrets(const NMSettInfoSetting *        sett_info,
 }
 
 static gboolean
-vpn_secrets_from_dbus(NMSetting *         setting,
-                      GVariant *          connection_dict,
-                      const char *        property,
-                      GVariant *          value,
-                      NMSettingParseFlags parse_flags,
-                      GError **           error)
+vpn_secrets_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     NMSettingVpn *       self                = NM_SETTING_VPN(setting);
     NMSettingVpnPrivate *priv                = NM_SETTING_VPN_GET_PRIVATE(self);
@@ -941,12 +924,7 @@ vpn_secrets_from_dbus(NMSetting *         setting,
 }
 
 static GVariant *
-vpn_secrets_to_dbus(const NMSettInfoSetting *               sett_info,
-                    guint                                   property_idx,
-                    NMConnection *                          connection,
-                    NMSetting *                             setting,
-                    NMConnectionSerializationFlags          flags,
-                    const NMConnectionSerializationOptions *options)
+vpn_secrets_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE(setting);
     GVariantBuilder      builder;
@@ -963,7 +941,7 @@ vpn_secrets_to_dbus(const NMSettInfoSetting *               sett_info,
 
     g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}"));
 
-    keys = nm_utils_strdict_get_keys(priv->secrets, TRUE, &len);
+    keys = nm_strdict_get_keys(priv->secrets, TRUE, &len);
     for (i = 0; i < len; i++) {
         const char *         key          = keys[i];
         NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
@@ -1130,7 +1108,6 @@ nm_setting_vpn_class_init(NMSettingVpnClass *klass)
     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;
 
@@ -1172,13 +1149,14 @@ nm_setting_vpn_class_init(NMSettingVpnClass *klass)
      * the VPN will attempt to stay connected across link changes and outages,
      * until explicitly disconnected.
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_VPN_PERSISTENT,
-                                        PROP_PERSISTENT,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_vpn_get_persistent);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_VPN_PERSISTENT,
+                                               PROP_PERSISTENT,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingVpnPrivate,
+                                               persistent);
 
     /**
      * NMSettingVpn:data: (type GHashTable(utf8,utf8)):
@@ -1229,6 +1207,7 @@ nm_setting_vpn_class_init(NMSettingVpnClass *klass)
         obj_properties[PROP_SECRETS],
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("a{ss}"),
                                        .to_dbus_fcn   = vpn_secrets_to_dbus,
+                                       .compare_fcn   = compare_fcn_secrets,
                                        .from_dbus_fcn = vpn_secrets_from_dbus, ));
 
     /**
@@ -1252,8 +1231,9 @@ nm_setting_vpn_class_init(NMSettingVpnClass *klass)
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_VPN,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-vrf.c b/src/libnm-core-impl/nm-setting-vrf.c
index a5ee7395..f998a7dd 100644
--- a/src/libnm-core-impl/nm-setting-vrf.c
+++ b/src/libnm-core-impl/nm-setting-vrf.c
@@ -81,38 +81,6 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    NMSettingVrf *self = NM_SETTING_VRF(object);
-
-    switch (prop_id) {
-    case PROP_TABLE:
-        g_value_set_uint(value, self->table);
-        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)
-{
-    NMSettingVrf *self = NM_SETTING_VRF(object);
-
-    switch (prop_id) {
-    case PROP_TABLE:
-        self->table = g_value_get_uint(value);
-        break;
-    default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
-        break;
-    }
-}
-
-/*****************************************************************************/
-
-static void
 nm_setting_vrf_init(NMSettingVrf *setting)
 {}
 
@@ -134,11 +102,12 @@ nm_setting_vrf_new(void)
 static void
 nm_setting_vrf_class_init(NMSettingVrfClass *klass)
 {
-    GObjectClass *  object_class  = G_OBJECT_CLASS(klass);
-    NMSettingClass *setting_class = NM_SETTING_CLASS(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->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify = verify;
 
@@ -149,16 +118,18 @@ nm_setting_vrf_class_init(NMSettingVrfClass *klass)
      *
      * Since: 1.24
      **/
-    obj_properties[PROP_TABLE] =
-        g_param_spec_uint(NM_SETTING_VRF_TABLE,
-                          "",
-                          "",
-                          0,
-                          G_MAXUINT32,
-                          0,
-                          G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_VRF_TABLE,
+                                              PROP_TABLE,
+                                              0,
+                                              G_MAXUINT32,
+                                              0,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingVrf,
+                                              table);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_VRF);
+    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_VRF, NULL, properties_override, 0);
 }
diff --git a/src/libnm-core-impl/nm-setting-vxlan.c b/src/libnm-core-impl/nm-setting-vxlan.c
index f2aeb66a..c2d23a7f 100644
--- a/src/libnm-core-impl/nm-setting-vxlan.c
+++ b/src/libnm-core-impl/nm-setting-vxlan.c
@@ -53,11 +53,11 @@ typedef struct {
     guint ttl;
     guint ageing;
     guint limit;
-    bool  learning : 1;
-    bool  proxy : 1;
-    bool  rsc : 1;
-    bool  l2_miss : 1;
-    bool  l3_miss : 1;
+    bool  proxy;
+    bool  learning;
+    bool  rsc;
+    bool  l2_miss;
+    bool  l3_miss;
 } NMSettingVxlanPrivate;
 
 /**
@@ -536,7 +536,6 @@ nm_setting_vxlan_init(NMSettingVxlan *self)
 
     priv->destination_port = DST_PORT_DEFAULT;
     priv->ageing           = 300;
-    priv->learning         = TRUE;
 }
 
 /**
@@ -595,6 +594,7 @@ nm_setting_vxlan_class_init(NMSettingVxlanClass *klass)
         "",
         NULL,
         G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
+
     /**
      * NMSettingVxlan:id:
      *
@@ -765,13 +765,14 @@ nm_setting_vxlan_class_init(NMSettingVxlanClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_VXLAN_PROXY,
-                                        PROP_PROXY,
-                                        FALSE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_vxlan_get_proxy);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_VXLAN_PROXY,
+                                               PROP_PROXY,
+                                               FALSE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingVxlanPrivate,
+                                               proxy);
 
     /**
      * NMSettingVxlan:learning:
@@ -781,13 +782,14 @@ nm_setting_vxlan_class_init(NMSettingVxlanClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_VXLAN_LEARNING,
-                                        PROP_LEARNING,
-                                        TRUE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_vxlan_get_learning);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_VXLAN_LEARNING,
+                                               PROP_LEARNING,
+                                               TRUE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingVxlanPrivate,
+                                               learning);
 
     /**
      * NMSettingVxlan:rsc:
@@ -796,13 +798,14 @@ nm_setting_vxlan_class_init(NMSettingVxlanClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_VXLAN_RSC,
-                                        PROP_RSC,
-                                        FALSE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_vxlan_get_rsc);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_VXLAN_RSC,
+                                               PROP_RSC,
+                                               FALSE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingVxlanPrivate,
+                                               rsc);
 
     /**
      * NMSettingVxlan:l2-miss:
@@ -811,13 +814,14 @@ nm_setting_vxlan_class_init(NMSettingVxlanClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_VXLAN_L2_MISS,
-                                        PROP_L2_MISS,
-                                        FALSE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_vxlan_get_l2_miss);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_VXLAN_L2_MISS,
+                                               PROP_L2_MISS,
+                                               FALSE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingVxlanPrivate,
+                                               l2_miss);
 
     /**
      * NMSettingVxlan:l3-miss:
@@ -826,18 +830,20 @@ nm_setting_vxlan_class_init(NMSettingVxlanClass *klass)
      *
      * Since: 1.2
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_VXLAN_L3_MISS,
-                                        PROP_L3_MISS,
-                                        FALSE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_vxlan_get_l3_miss);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_VXLAN_L3_MISS,
+                                               PROP_L3_MISS,
+                                               FALSE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingVxlanPrivate,
+                                               l3_miss);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit_full(setting_class,
-                                  NM_META_SETTING_TYPE_VXLAN,
-                                  NULL,
-                                  properties_override);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_VXLAN,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-wifi-p2p.c b/src/libnm-core-impl/nm-setting-wifi-p2p.c
index 8f677479..06d450fc 100644
--- a/src/libnm-core-impl/nm-setting-wifi-p2p.c
+++ b/src/libnm-core-impl/nm-setting-wifi-p2p.c
@@ -35,10 +35,9 @@
 NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_PEER, PROP_WPS_METHOD, PROP_WFD_IES, );
 
 typedef struct {
-    char *  peer_mac_address;
+    char *  peer;
     GBytes *wfd_ies;
-
-    NMSettingWirelessSecurityWpsMethod wps_method;
+    guint32 wps_method;
 } NMSettingWifiP2PPrivate;
 
 struct _NMSettingWifiP2P {
@@ -70,7 +69,7 @@ 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;
+    return NM_SETTING_WIFI_P2P_GET_PRIVATE(setting)->peer;
 }
 
 /**
@@ -113,7 +112,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 {
     NMSettingWifiP2PPrivate *priv = NM_SETTING_WIFI_P2P_GET_PRIVATE(setting);
 
-    if (!priv->peer_mac_address) {
+    if (!priv->peer) {
         g_set_error_literal(error,
                             NM_CONNECTION_ERROR,
                             NM_CONNECTION_ERROR_MISSING_PROPERTY,
@@ -125,7 +124,7 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
         return FALSE;
     }
 
-    if (!nm_utils_hwaddr_valid(priv->peer_mac_address, ETH_ALEN)) {
+    if (!nm_utils_hwaddr_valid(priv->peer, ETH_ALEN)) {
         g_set_error_literal(error,
                             NM_CONNECTION_ERROR,
                             NM_CONNECTION_ERROR_INVALID_PROPERTY,
@@ -150,53 +149,6 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 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:
-        nm_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)
 {}
 
@@ -216,25 +168,14 @@ nm_setting_wifi_p2p_new(void)
 }
 
 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);
+    GObjectClass *  object_class        = G_OBJECT_CLASS(setting_wifi_p2p_class);
+    NMSettingClass *setting_class       = NM_SETTING_CLASS(setting_wifi_p2p_class);
+    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;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify = verify;
 
@@ -254,11 +195,13 @@ nm_setting_wifi_p2p_class_init(NMSettingWifiP2PClass *setting_wifi_p2p_class)
      *   (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);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_WIFI_P2P_PEER,
+                                              PROP_PEER,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingWifiP2P,
+                                              _priv.peer);
 
     /**
      * NMSettingWifiP2P:wps-method:
@@ -270,14 +213,16 @@ nm_setting_wifi_p2p_class_init(NMSettingWifiP2PClass *setting_wifi_p2p_class)
      *
      * 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);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_WIFI_P2P_WPS_METHOD,
+                                              PROP_WPS_METHOD,
+                                              0,
+                                              G_MAXUINT32,
+                                              NM_SETTING_WIRELESS_SECURITY_WPS_METHOD_DEFAULT,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE,
+                                              NMSettingWifiP2P,
+                                              _priv.wps_method);
 
     /**
      * NMSettingWifiP2P:wfd-ies:
@@ -291,14 +236,19 @@ nm_setting_wifi_p2p_class_init(NMSettingWifiP2PClass *setting_wifi_p2p_class)
      *
      * 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);
+    _nm_setting_property_define_direct_bytes(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_WIFI_P2P_WFD_IES,
+                                             PROP_WFD_IES,
+                                             NM_SETTING_PARAM_FUZZY_IGNORE,
+                                             NMSettingWifiP2P,
+                                             _priv.wfd_ies);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_WIFI_P2P);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_WIFI_P2P,
+                             NULL,
+                             properties_override,
+                             0);
 }
diff --git a/src/libnm-core-impl/nm-setting-wimax.c b/src/libnm-core-impl/nm-setting-wimax.c
index 6d9f3284..f7fed08e 100644
--- a/src/libnm-core-impl/nm-setting-wimax.c
+++ b/src/libnm-core-impl/nm-setting-wimax.c
@@ -208,7 +208,6 @@ 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);
 }
@@ -252,20 +251,20 @@ nm_setting_wimax_class_init(NMSettingWimaxClass *klass)
      *
      * Deprecated: 1.2: WiMAX is no longer supported.
      **/
-    obj_properties[PROP_MAC_ADDRESS] =
-        g_param_spec_string(NM_SETTING_WIMAX_MAC_ADDRESS,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_MAC_ADDRESS],
-                                 &nm_sett_info_propert_type_mac_address);
+    _nm_setting_property_define_direct_mac_address(properties_override,
+                                                   obj_properties,
+                                                   NM_SETTING_WIMAX_MAC_ADDRESS,
+                                                   PROP_MAC_ADDRESS,
+                                                   NM_SETTING_PARAM_NONE,
+                                                   NMSettingWimaxPrivate,
+                                                   mac_address,
+                                                   .direct_set_string_mac_address_len = ETH_ALEN);
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_WIMAX,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-wired.c b/src/libnm-core-impl/nm-setting-wired.c
index 4acc5641..8c9bdcd4 100644
--- a/src/libnm-core-impl/nm-setting-wired.c
+++ b/src/libnm-core-impl/nm-setting-wired.c
@@ -66,7 +66,7 @@ typedef struct {
     NMTernary               accept_all_mac_addresses;
     guint32                 speed;
     guint32                 mtu;
-    bool                    auto_negotiate : 1;
+    bool                    auto_negotiate;
 } NMSettingWiredPrivate;
 
 /**
@@ -147,9 +147,9 @@ _nm_setting_wired_is_valid_s390_option(const char *option)
     }
 
     return option
-           && (nm_utils_strv_find_binary_search(valid_s390_opts,
-                                                G_N_ELEMENTS(valid_s390_opts) - 1,
-                                                option)
+           && (nm_strv_find_binary_search(valid_s390_opts,
+                                          G_N_ELEMENTS(valid_s390_opts) - 1,
+                                          option)
                >= 0);
 }
 
@@ -626,7 +626,7 @@ nm_setting_wired_add_s390_option(NMSettingWired *setting, const char *key, const
         };
         priv->s390_options.len++;
     } else {
-        if (!nm_utils_strdup_reset(&priv->s390_options.arr[idx].value_str_mutable, value))
+        if (!nm_strdup_reset(&priv->s390_options.arr[idx].value_str_mutable, value))
             return TRUE;
     }
 
@@ -976,23 +976,11 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_cloned_mac_address(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
-    if (nm_streq(sett_info->property_infos[property_idx].name,
-                 NM_SETTING_WIRED_CLONED_MAC_ADDRESS)) {
-        return !set_b
-               || nm_streq0(NM_SETTING_WIRED_GET_PRIVATE(set_a)->cloned_mac_address,
-                            NM_SETTING_WIRED_GET_PRIVATE(set_b)->cloned_mac_address);
-    }
-
-    return NM_SETTING_CLASS(nm_setting_wired_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    return !set_b
+           || nm_streq0(NM_SETTING_WIRED_GET_PRIVATE(set_a)->cloned_mac_address,
+                        NM_SETTING_WIRED_GET_PRIVATE(set_b)->cloned_mac_address);
 }
 
 /*****************************************************************************/
@@ -1242,22 +1230,14 @@ finalize(GObject *object)
 {
     NMSettingWiredPrivate *priv = NM_SETTING_WIRED_GET_PRIVATE(object);
 
-    g_free(priv->port);
-    g_free(priv->duplex);
-    g_free(priv->s390_nettype);
-
     _s390_options_clear(priv);
 
-    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);
 }
 
@@ -1274,8 +1254,7 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
     object_class->set_property = set_property;
     object_class->finalize     = finalize;
 
-    setting_class->verify           = verify;
-    setting_class->compare_property = compare_property;
+    setting_class->verify = verify;
 
     /**
      * NMSettingWired:port:
@@ -1291,12 +1270,13 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      * description: The property is not saved by the plugin.
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_WIRED_PORT,
-                                       PROP_PORT,
-                                       NM_SETTING_PARAM_NONE,
-                                       nm_setting_wired_get_port);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_WIRED_PORT,
+                                              PROP_PORT,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingWiredPrivate,
+                                              port);
 
     /**
      * NMSettingWired:speed:
@@ -1353,12 +1333,13 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      *    "duplex" parameter in the ETHOOL_OPTS variable.
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_WIRED_DUPLEX,
-                                       PROP_DUPLEX,
-                                       NM_SETTING_PARAM_NONE,
-                                       nm_setting_wired_get_duplex);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_WIRED_DUPLEX,
+                                              PROP_DUPLEX,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingWiredPrivate,
+                                              duplex);
 
     /**
      * NMSettingWired:auto-negotiate:
@@ -1380,14 +1361,15 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      *    "speed" and "duplex" parameters (skips link configuration).
      * ---end---
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_WIRED_AUTO_NEGOTIATE,
-                                        PROP_AUTO_NEGOTIATE,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_wired_get_auto_negotiate,
-                                        .to_dbus_data.including_default = TRUE);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_WIRED_AUTO_NEGOTIATE,
+                                               PROP_AUTO_NEGOTIATE,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingWiredPrivate,
+                                               auto_negotiate,
+                                               .to_dbus_including_default = TRUE);
 
     /**
      * NMSettingWired:mac-address:
@@ -1413,15 +1395,14 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      *    permanent MAC address exists, the MAC address initially configured on the device.
      * ---end---
      */
-    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);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_MAC_ADDRESS],
-                                 &nm_sett_info_propert_type_mac_address);
+    _nm_setting_property_define_direct_mac_address(properties_override,
+                                                   obj_properties,
+                                                   NM_SETTING_WIRED_MAC_ADDRESS,
+                                                   PROP_MAC_ADDRESS,
+                                                   NM_SETTING_PARAM_INFERRABLE,
+                                                   NMSettingWiredPrivate,
+                                                   device_mac_address,
+                                                   .direct_set_string_mac_address_len = ETH_ALEN);
 
     /**
      * NMSettingWired:cloned-mac-address:
@@ -1472,9 +1453,14 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
         "",
         NULL,
         G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_CLONED_MAC_ADDRESS],
-                                 &nm_sett_info_propert_type_cloned_mac_address);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_CLONED_MAC_ADDRESS],
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_BYTESTRING,
+                                       .compare_fcn           = compare_fcn_cloned_mac_address,
+                                       .to_dbus_fcn           = _nm_utils_hwaddr_cloned_get,
+                                       .from_dbus_fcn         = _nm_utils_hwaddr_cloned_set,
+                                       .missing_from_dbus_fcn = _nm_utils_hwaddr_cloned_not_set, ));
 
     /* ---dbus---
      * property: assigned-mac-address
@@ -1531,12 +1517,13 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      *   cloned-mac-address.
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK,
-                                       PROP_GENERATE_MAC_ADDRESS_MASK,
-                                       NM_SETTING_PARAM_FUZZY_IGNORE,
-                                       nm_setting_wired_get_generate_mac_address_mask);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK,
+                                              PROP_GENERATE_MAC_ADDRESS_MASK,
+                                              NM_SETTING_PARAM_FUZZY_IGNORE,
+                                              NMSettingWiredPrivate,
+                                              generate_mac_address_mask);
 
     /**
      * NMSettingWired:mac-address-blacklist:
@@ -1627,12 +1614,13 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      * example: NETTYPE=qeth
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_WIRED_S390_NETTYPE,
-                                       PROP_S390_NETTYPE,
-                                       NM_SETTING_PARAM_INFERRABLE,
-                                       nm_setting_wired_get_s390_nettype);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_WIRED_S390_NETTYPE,
+                                              PROP_S390_NETTYPE,
+                                              NM_SETTING_PARAM_INFERRABLE,
+                                              NMSettingWiredPrivate,
+                                              s390_nettype);
 
     /**
      * NMSettingWired:s390-options: (type GHashTable(utf8,utf8)):
@@ -1641,6 +1629,10 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      * and values must be strings.  Allowed keys include "portno", "layer2",
      * "portname", "protocol", among others.  Key names must contain only
      * alphanumeric characters (ie, [a-zA-Z0-9]).
+     *
+     * Currently, NetworkManager itself does nothing with this information.
+     * However, s390utils ships a udev rule which parses this information
+     * and applies it to the interface.
      **/
     /* ---ifcfg-rh---
      * property: s390-options
@@ -1708,12 +1700,13 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
      * example: ETHTOOL_OPTS="wol gs sopass 00:11:22:33:44:55"
      * ---end---
      */
-    _nm_setting_property_define_string(properties_override,
-                                       obj_properties,
-                                       NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD,
-                                       PROP_WAKE_ON_LAN_PASSWORD,
-                                       NM_SETTING_PARAM_NONE,
-                                       nm_setting_wired_get_wake_on_lan_password);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_WIRED_WAKE_ON_LAN_PASSWORD,
+                                              PROP_WAKE_ON_LAN_PASSWORD,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingWiredPrivate,
+                                              wol_password);
 
     /**
      * NMSettingWired:accept-all-mac-addresses:
@@ -1741,8 +1734,9 @@ nm_setting_wired_class_init(NMSettingWiredClass *klass)
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_WIRED,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-wireguard.c b/src/libnm-core-impl/nm-setting-wireguard.c
index 3290ff56..27b81488 100644
--- a/src/libnm-core-impl/nm-setting-wireguard.c
+++ b/src/libnm-core-impl/nm-setting-wireguard.c
@@ -902,8 +902,8 @@ typedef struct {
     guint32              fwmark;
     guint32              mtu;
     guint16              listen_port;
+    bool                 peer_routes;
     bool                 private_key_valid : 1;
-    bool                 peer_routes : 1;
 } NMSettingWireGuardPrivate;
 
 /**
@@ -1460,12 +1460,7 @@ nm_setting_wireguard_clear_peers(NMSettingWireGuard *self)
 /*****************************************************************************/
 
 static GVariant *
-_peers_dbus_only_synth(const NMSettInfoSetting *               sett_info,
-                       guint                                   property_idx,
-                       NMConnection *                          connection,
-                       NMSetting *                             setting,
-                       NMConnectionSerializationFlags          flags,
-                       const NMConnectionSerializationOptions *options)
+_peers_dbus_only_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     NMSettingWireGuard *       self = NM_SETTING_WIREGUARD(setting);
     NMSettingWireGuardPrivate *priv;
@@ -1563,12 +1558,7 @@ _peers_dbus_only_synth(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-_peers_dbus_only_set(NMSetting *         setting,
-                     GVariant *          connection_dict,
-                     const char *        property,
-                     GVariant *          value,
-                     NMSettingParseFlags parse_flags,
-                     GError **           error)
+_peers_dbus_only_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     GVariantIter iter_peers;
     GVariant *   peer_var;
@@ -1687,6 +1677,7 @@ _peers_dbus_only_set(NMSetting *         setting,
     success = TRUE;
 
 out:
+    *out_is_modified = peers_changed;
     if (peers_changed)
         _peers_notify(setting);
     return success;
@@ -1842,12 +1833,12 @@ need_secrets(NMSetting *setting)
 
 static gboolean
 clear_secrets(const NMSettInfoSetting *        sett_info,
-              guint                            property_idx,
+              const NMSettInfoProperty *       property_info,
               NMSetting *                      setting,
               NMSettingClearSecretsWithFlagsFn func,
               gpointer                         user_data)
 {
-    if (nm_streq(sett_info->property_infos[property_idx].name, NM_SETTING_WIREGUARD_PEERS)) {
+    if (nm_streq(property_info->name, NM_SETTING_WIREGUARD_PEERS)) {
         NMSettingWireGuardPrivate *priv          = NM_SETTING_WIREGUARD_GET_PRIVATE(setting);
         gboolean                   peers_changed = FALSE;
         guint                      i, j;
@@ -1894,7 +1885,7 @@ clear_secrets(const NMSettInfoSetting *        sett_info,
     }
 
     return NM_SETTING_CLASS(nm_setting_wireguard_parent_class)
-        ->clear_secrets(sett_info, property_idx, setting, func, user_data);
+        ->clear_secrets(sett_info, property_info, setting, func, user_data);
 }
 
 static int
@@ -1997,43 +1988,32 @@ update_one_secret(NMSetting *setting, const char *key, GVariant *value, GError *
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_peers(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
     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 (!set_b)
-            return TRUE;
+    if (NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE))
+        return NM_TERNARY_DEFAULT;
 
-        a_priv = NM_SETTING_WIREGUARD_GET_PRIVATE(set_a);
-        b_priv = NM_SETTING_WIREGUARD_GET_PRIVATE(set_b);
+    if (!set_b)
+        return TRUE;
 
-        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;
+    a_priv = NM_SETTING_WIREGUARD_GET_PRIVATE(set_a);
+    b_priv = NM_SETTING_WIREGUARD_GET_PRIVATE(set_b);
 
-            if (nm_wireguard_peer_cmp(a_peer, b_peer, flags) != 0)
-                return FALSE;
-        }
+    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;
 
-        return TRUE;
+        if (nm_wireguard_peer_cmp(a_peer, b_peer, flags) != 0)
+            return FALSE;
     }
 
-    return NM_SETTING_CLASS(nm_setting_wireguard_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    return TRUE;
 }
 
 static void
@@ -2372,7 +2352,6 @@ nm_setting_wireguard_init(NMSettingWireGuard *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;
     priv->ip4_auto_default_route = NM_TERNARY_DEFAULT;
     priv->ip6_auto_default_route = NM_TERNARY_DEFAULT;
 }
@@ -2422,7 +2401,6 @@ nm_setting_wireguard_class_init(NMSettingWireGuardClass *klass)
     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;
@@ -2514,13 +2492,14 @@ nm_setting_wireguard_class_init(NMSettingWireGuardClass *klass)
      *
      * Since: 1.16
      **/
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_WIREGUARD_PEER_ROUTES,
-                                        PROP_PEER_ROUTES,
-                                        TRUE,
-                                        NM_SETTING_PARAM_INFERRABLE,
-                                        nm_setting_wireguard_get_peer_routes);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_WIREGUARD_PEER_ROUTES,
+                                               PROP_PEER_ROUTES,
+                                               TRUE,
+                                               NM_SETTING_PARAM_INFERRABLE,
+                                               NMSettingWireGuardPrivate,
+                                               peer_routes);
 
     /**
      * NMSettingWireGuard:mtu:
@@ -2597,12 +2576,14 @@ nm_setting_wireguard_class_init(NMSettingWireGuardClass *klass)
         NM_SETTING_WIREGUARD_PEERS,
         NM_SETT_INFO_PROPERT_TYPE_DBUS(NM_G_VARIANT_TYPE("aa{sv}"),
                                        .to_dbus_fcn   = _peers_dbus_only_synth,
+                                       .compare_fcn   = compare_fcn_peers,
                                        .from_dbus_fcn = _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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_WIREGUARD,
+                             NULL,
+                             properties_override,
+                             G_STRUCT_OFFSET(NMSettingWireGuard, _priv));
 }
diff --git a/src/libnm-core-impl/nm-setting-wireless-security.c b/src/libnm-core-impl/nm-setting-wireless-security.c
index 9e3c8d81..0bfc30c8 100644
--- a/src/libnm-core-impl/nm-setting-wireless-security.c
+++ b/src/libnm-core-impl/nm-setting-wireless-security.c
@@ -1290,13 +1290,19 @@ set_secret_flags(NMSetting *          setting,
         ->set_secret_flags(setting, secret_name, flags, error);
 }
 
-/* NMSettingWirelessSecurity:wep-key-type is an enum, but needs to be marshalled
- * as 'u', not 'i', for backward-compatibility.
- */
 static GVariant *
-wep_key_type_to_dbus(const GValue *from)
+wep_key_type_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
-    return g_variant_new_uint32(g_value_get_enum(from));
+    NMWepKeyType t;
+
+    t = nm_setting_wireless_security_get_wep_key_type(NM_SETTING_WIRELESS_SECURITY(setting));
+    if (t == NM_WEP_KEY_TYPE_UNKNOWN)
+        return NULL;
+
+    /* NMSettingWirelessSecurity:wep-key-type is an enum, but needs to be marshalled
+     * as 'u', not 'i', for backward-compatibility.
+     */
+    return g_variant_new_uint32(t);
 }
 
 /*****************************************************************************/
@@ -1397,15 +1403,15 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         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);
+        priv->proto = nm_strv_to_gslist(g_value_get_boxed(value), 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);
+        priv->pairwise = nm_strv_to_gslist(g_value_get_boxed(value), 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);
+        priv->group = nm_strv_to_gslist(g_value_get_boxed(value), TRUE);
         break;
     case PROP_PMF:
         priv->pmf = g_value_get_int(value);
@@ -1924,10 +1930,14 @@ nm_setting_wireless_security_class_init(NMSettingWirelessSecurityClass *klass)
                           NM_TYPE_WEP_KEY_TYPE,
                           NM_WEP_KEY_TYPE_UNKNOWN,
                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_WEP_KEY_TYPE],
-                                 &nm_sett_info_propert_type_plain_u,
-                                 .to_dbus_data.gprop_to_dbus_fcn = wep_key_type_to_dbus, );
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_WEP_KEY_TYPE],
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_UINT32,
+                                       .to_dbus_fcn   = wep_key_type_to_dbus,
+                                       .compare_fcn   = _nm_setting_property_compare_fcn_default,
+                                       .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                       .from_dbus_is_full = TRUE));
 
     /**
      * NMSettingWirelessSecurity:wps-method:
@@ -1993,8 +2003,9 @@ nm_setting_wireless_security_class_init(NMSettingWirelessSecurityClass *klass)
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_WIRELESS_SECURITY,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-wireless.c b/src/libnm-core-impl/nm-setting-wireless.c
index 5bdedf9b..f5a4cb6a 100644
--- a/src/libnm-core-impl/nm-setting-wireless.c
+++ b/src/libnm-core-impl/nm-setting-wireless.c
@@ -63,7 +63,7 @@ typedef struct {
     guint32                   mtu;
     guint32                   powersave;
     guint32                   wowl;
-    bool                      hidden : 1;
+    bool                      hidden;
 } NMSettingWirelessPrivate;
 
 /**
@@ -690,10 +690,7 @@ nm_setting_wireless_add_seen_bssid(NMSettingWireless *setting, const char *bssid
     if (!priv->seen_bssids) {
         priv->seen_bssids = g_ptr_array_new_with_free_func(g_free);
     } else {
-        if (nm_utils_strv_find_first((char **) priv->seen_bssids->pdata,
-                                     priv->seen_bssids->len,
-                                     lower_bssid)
-            >= 0)
+        if (nm_strv_ptrarray_find_first(priv->seen_bssids, lower_bssid) >= 0)
             return FALSE;
     }
 
@@ -743,12 +740,7 @@ nm_setting_wireless_get_seen_bssid(NMSettingWireless *setting, guint32 i)
 }
 
 static GVariant *
-_to_dbus_fcn_seen_bssids(const NMSettInfoSetting *               sett_info,
-                         guint                                   property_idx,
-                         NMConnection *                          connection,
-                         NMSetting *                             setting,
-                         NMConnectionSerializationFlags          flags,
-                         const NMConnectionSerializationOptions *options)
+_to_dbus_fcn_seen_bssids(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     if (options && options->seen_bssids)
         return options->seen_bssids[0] ? g_variant_new_strv(options->seen_bssids, -1) : NULL;
@@ -769,12 +761,7 @@ _to_dbus_fcn_seen_bssids(const NMSettInfoSetting *               sett_info,
 }
 
 static gboolean
-_from_dbus_fcn_seen_bssids(NMSetting *         setting,
-                           GVariant *          connection_dict,
-                           const char *        property,
-                           GVariant *          value,
-                           NMSettingParseFlags parse_flags,
-                           GError **           error)
+_from_dbus_fcn_seen_bssids(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     NMSettingWirelessPrivate *priv;
     gs_free const char **     s = NULL;
@@ -784,6 +771,7 @@ _from_dbus_fcn_seen_bssids(NMSetting *         setting,
     if (_nm_utils_is_manager_process) {
         /* in the manager process, we don't accept seen-bssid from the client.
          * Do nothing.  */
+        *out_is_modified = FALSE;
         return TRUE;
     }
 
@@ -1102,40 +1090,26 @@ mac_addr_rand_ok:
 }
 
 static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
+compare_fcn_cloned_mac_address(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
 {
-    if (sett_info->property_infos[property_idx].param_spec
-        == obj_properties[PROP_CLONED_MAC_ADDRESS]) {
-        return !set_b
-               || nm_streq0(NM_SETTING_WIRELESS_GET_PRIVATE(set_a)->cloned_mac_address,
-                            NM_SETTING_WIRELESS_GET_PRIVATE(set_b)->cloned_mac_address);
-    }
-    if (sett_info->property_infos[property_idx].param_spec == obj_properties[PROP_SEEN_BSSIDS]) {
-        return !set_b
-               || (nm_strv_ptrarray_cmp(NM_SETTING_WIRELESS_GET_PRIVATE(set_a)->seen_bssids,
-                                        NM_SETTING_WIRELESS_GET_PRIVATE(set_b)->seen_bssids)
-                   == 0);
-    }
+    return !set_b
+           || nm_streq0(NM_SETTING_WIRELESS_GET_PRIVATE(set_a)->cloned_mac_address,
+                        NM_SETTING_WIRELESS_GET_PRIVATE(set_b)->cloned_mac_address);
+}
 
-    return NM_SETTING_CLASS(nm_setting_wireless_parent_class)
-        ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+static NMTernary
+compare_fcn_seen_bssids(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    return !set_b
+           || (nm_strv_ptrarray_cmp(NM_SETTING_WIRELESS_GET_PRIVATE(set_a)->seen_bssids,
+                                    NM_SETTING_WIRELESS_GET_PRIVATE(set_b)->seen_bssids)
+               == 0);
 }
 
 /*****************************************************************************/
 
 static GVariant *
-nm_setting_wireless_get_security(const NMSettInfoSetting *               sett_info,
-                                 guint                                   property_idx,
-                                 NMConnection *                          connection,
-                                 NMSetting *                             setting,
-                                 NMConnectionSerializationFlags          flags,
-                                 const NMConnectionSerializationOptions *options)
+nm_setting_wireless_get_security(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     if (!_nm_connection_serialize_non_secret(flags))
         return NULL;
@@ -1182,9 +1156,6 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     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;
@@ -1194,18 +1165,12 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
     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;
@@ -1219,14 +1184,11 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_set_uint(value, nm_setting_wireless_get_mtu(setting));
         break;
     case PROP_SEEN_BSSIDS:
-        g_value_take_boxed(value,
-                           priv->seen_bssids ? nm_utils_strv_dup((char **) priv->seen_bssids->pdata,
-                                                                 priv->seen_bssids->len,
-                                                                 TRUE)
-                                             : NULL);
-        break;
-    case PROP_HIDDEN:
-        g_value_set_boolean(value, nm_setting_wireless_get_hidden(setting));
+        g_value_take_boxed(
+            value,
+            priv->seen_bssids
+                ? nm_strv_dup((char **) priv->seen_bssids->pdata, priv->seen_bssids->len, TRUE)
+                : NULL);
         break;
     case PROP_POWERSAVE:
         g_value_set_uint(value, nm_setting_wireless_get_powersave(setting));
@@ -1241,7 +1203,7 @@ get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
         g_value_set_enum(value, priv->ap_isolation);
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_get_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -1255,11 +1217,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
     gboolean                  bool_val;
 
     switch (prop_id) {
-    case PROP_SSID:
-        if (priv->ssid)
-            g_bytes_unref(priv->ssid);
-        priv->ssid = g_value_dup_boxed(value);
-        break;
     case PROP_MODE:
         g_free(priv->mode);
         priv->mode = g_value_dup_string(value);
@@ -1271,21 +1228,12 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
     case PROP_CHANNEL:
         priv->channel = g_value_get_uint(value);
         break;
-    case PROP_BSSID:
-        g_free(priv->bssid);
-        priv->bssid = g_value_dup_string(value);
-        break;
     case PROP_RATE:
         priv->rate = g_value_get_uint(value);
         break;
     case PROP_TX_POWER:
         priv->tx_power = g_value_get_uint(value);
         break;
-    case PROP_MAC_ADDRESS:
-        g_free(priv->device_mac_address);
-        priv->device_mac_address =
-            _nm_utils_hwaddr_canonical_or_invalid(g_value_get_string(value), ETH_ALEN);
-        break;
     case PROP_CLONED_MAC_ADDRESS:
         bool_val = !!priv->cloned_mac_address;
         g_free(priv->cloned_mac_address);
@@ -1337,9 +1285,6 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         }
         break;
     }
-    case PROP_HIDDEN:
-        priv->hidden = g_value_get_boolean(value);
-        break;
     case PROP_POWERSAVE:
         priv->powersave = g_value_get_uint(value);
         break;
@@ -1353,7 +1298,7 @@ set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *ps
         priv->ap_isolation = g_value_get_enum(value);
         break;
     default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        _nm_setting_property_set_property_direct(object, prop_id, value, pspec);
         break;
     }
 }
@@ -1394,10 +1339,6 @@ finalize(GObject *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);
@@ -1419,8 +1360,7 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
     object_class->get_property = get_property;
     object_class->finalize     = finalize;
 
-    setting_class->verify           = verify;
-    setting_class->compare_property = compare_property;
+    setting_class->verify = verify;
 
     /**
      * NMSettingWireless:ssid:
@@ -1440,11 +1380,13 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
      * example: ESSID="Quick Net"
      * ---end---
      */
-    obj_properties[PROP_SSID] = g_param_spec_boxed(NM_SETTING_WIRELESS_SSID,
-                                                   "",
-                                                   "",
-                                                   G_TYPE_BYTES,
-                                                   G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_bytes(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_WIRELESS_SSID,
+                                             PROP_SSID,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingWirelessPrivate,
+                                             ssid);
 
     /**
      * NMSettingWireless:mode:
@@ -1530,14 +1472,14 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
      * example: BSSID=00:1E:BD:64:83:21
      * ---end---
      */
-    obj_properties[PROP_BSSID] = g_param_spec_string(NM_SETTING_WIRELESS_BSSID,
-                                                     "",
-                                                     "",
-                                                     NULL,
-                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_BSSID],
-                                 &nm_sett_info_propert_type_mac_address);
+    _nm_setting_property_define_direct_mac_address(properties_override,
+                                                   obj_properties,
+                                                   NM_SETTING_WIRELESS_BSSID,
+                                                   PROP_BSSID,
+                                                   NM_SETTING_PARAM_NONE,
+                                                   NMSettingWirelessPrivate,
+                                                   bssid,
+                                                   .direct_set_string_mac_address_len = ETH_ALEN);
 
     /**
      * NMSettingWireless:rate:
@@ -1608,15 +1550,14 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
      *    permanent MAC address exists, the MAC address initially configured on the device.
      * ---end---
      */
-    obj_properties[PROP_MAC_ADDRESS] =
-        g_param_spec_string(NM_SETTING_WIRELESS_MAC_ADDRESS,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_MAC_ADDRESS],
-                                 &nm_sett_info_propert_type_mac_address);
+    _nm_setting_property_define_direct_mac_address(properties_override,
+                                                   obj_properties,
+                                                   NM_SETTING_WIRELESS_MAC_ADDRESS,
+                                                   PROP_MAC_ADDRESS,
+                                                   NM_SETTING_PARAM_NONE,
+                                                   NMSettingWirelessPrivate,
+                                                   device_mac_address,
+                                                   .direct_set_string_mac_address_len = ETH_ALEN);
 
     /**
      * NMSettingWireless:cloned-mac-address:
@@ -1666,9 +1607,14 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
         "",
         NULL,
         G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS);
-    _nm_properties_override_gobj(properties_override,
-                                 obj_properties[PROP_CLONED_MAC_ADDRESS],
-                                 &nm_sett_info_propert_type_cloned_mac_address);
+    _nm_properties_override_gobj(
+        properties_override,
+        obj_properties[PROP_CLONED_MAC_ADDRESS],
+        NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_BYTESTRING,
+                                       .compare_fcn           = compare_fcn_cloned_mac_address,
+                                       .to_dbus_fcn           = _nm_utils_hwaddr_cloned_get,
+                                       .from_dbus_fcn         = _nm_utils_hwaddr_cloned_set,
+                                       .missing_from_dbus_fcn = _nm_utils_hwaddr_cloned_not_set, ));
 
     /* ---dbus---
      * property: assigned-mac-address
@@ -1786,7 +1732,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
         obj_properties[PROP_SEEN_BSSIDS],
         NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING_ARRAY,
                                        .to_dbus_fcn   = _to_dbus_fcn_seen_bssids,
-                                       .from_dbus_fcn = _from_dbus_fcn_seen_bssids, ));
+                                       .from_dbus_fcn = _from_dbus_fcn_seen_bssids,
+                                       .compare_fcn   = compare_fcn_seen_bssids, ));
 
     /**
      * NMSettingWireless:mtu:
@@ -1833,13 +1780,14 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
      * description: Whether the network hides the SSID.
      * ---end---
      */
-    _nm_setting_property_define_boolean(properties_override,
-                                        obj_properties,
-                                        NM_SETTING_WIRELESS_HIDDEN,
-                                        PROP_HIDDEN,
-                                        FALSE,
-                                        NM_SETTING_PARAM_NONE,
-                                        nm_setting_wireless_get_hidden);
+    _nm_setting_property_define_direct_boolean(properties_override,
+                                               obj_properties,
+                                               NM_SETTING_WIRELESS_HIDDEN,
+                                               PROP_HIDDEN,
+                                               FALSE,
+                                               NM_SETTING_PARAM_NONE,
+                                               NMSettingWirelessPrivate,
+                                               hidden);
 
     /**
      * NMSettingWireless:powersave:
@@ -1916,7 +1864,8 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
         properties_override,
         "security",
         NM_SETT_INFO_PROPERT_TYPE_DBUS(G_VARIANT_TYPE_STRING,
-                                       .to_dbus_fcn = nm_setting_wireless_get_security, ));
+                                       .to_dbus_fcn = nm_setting_wireless_get_security,
+                                       .compare_fcn = _nm_setting_property_compare_fcn_ignore, ));
 
     /**
      * NMSettingWireless:wake-on-wlan:
@@ -1985,8 +1934,9 @@ nm_setting_wireless_class_init(NMSettingWirelessClass *klass)
 
     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);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_WIRELESS,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting-wpan.c b/src/libnm-core-impl/nm-setting-wpan.c
index 28a6478f..78cdc036 100644
--- a/src/libnm-core-impl/nm-setting-wpan.c
+++ b/src/libnm-core-impl/nm-setting-wpan.c
@@ -22,15 +22,15 @@
 
 /* Ideally we'll be able to get these from a public header. */
 #ifndef IEEE802154_ADDR_LEN
-    #define IEEE802154_ADDR_LEN 8
+#define IEEE802154_ADDR_LEN 8
 #endif
 
 #ifndef IEEE802154_MAX_PAGE
-    #define IEEE802154_MAX_PAGE 31
+#define IEEE802154_MAX_PAGE 31
 #endif
 
 #ifndef IEEE802154_MAX_CHANNEL
-    #define IEEE802154_MAX_CHANNEL 26
+#define IEEE802154_MAX_CHANNEL 26
 #endif
 
 /*****************************************************************************/
@@ -43,10 +43,10 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE(PROP_MAC_ADDRESS,
 
 typedef struct {
     char *  mac_address;
-    guint16 pan_id;
-    guint16 short_address;
-    gint16  page;
-    gint16  channel;
+    guint32 pan_id;
+    guint32 short_address;
+    gint32  page;
+    gint32  channel;
 } NMSettingWpanPrivate;
 
 /**
@@ -201,73 +201,8 @@ verify(NMSetting *setting, NMConnection *connection, GError **error)
 /*****************************************************************************/
 
 static void
-get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-    NMSettingWpan *setting = NM_SETTING_WPAN(object);
-
-    switch (prop_id) {
-    case PROP_MAC_ADDRESS:
-        g_value_set_string(value, nm_setting_wpan_get_mac_address(setting));
-        break;
-    case PROP_PAN_ID:
-        g_value_set_uint(value, nm_setting_wpan_get_pan_id(setting));
-        break;
-    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;
-    }
-}
-
-static void
-set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
-{
-    NMSettingWpanPrivate *priv = NM_SETTING_WPAN_GET_PRIVATE(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), IEEE802154_ADDR_LEN);
-        break;
-    case PROP_PAN_ID:
-        priv->pan_id = g_value_get_uint(value);
-        break;
-    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->pan_id        = G_MAXUINT16;
-    priv->short_address = G_MAXUINT16;
-    priv->page          = NM_SETTING_WPAN_PAGE_DEFAULT;
-    priv->channel       = NM_SETTING_WPAN_CHANNEL_DEFAULT;
-}
+{}
 
 /**
  * nm_setting_wpan_new:
@@ -285,26 +220,16 @@ nm_setting_wpan_new(void)
 }
 
 static void
-finalize(GObject *object)
-{
-    NMSettingWpanPrivate *priv = NM_SETTING_WPAN_GET_PRIVATE(object);
-
-    g_free(priv->mac_address);
-
-    G_OBJECT_CLASS(nm_setting_wpan_parent_class)->finalize(object);
-}
-
-static void
 nm_setting_wpan_class_init(NMSettingWpanClass *klass)
 {
-    GObjectClass *  object_class  = G_OBJECT_CLASS(klass);
-    NMSettingClass *setting_class = NM_SETTING_CLASS(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();
 
     g_type_class_add_private(setting_class, sizeof(NMSettingWpanPrivate));
 
-    object_class->get_property = get_property;
-    object_class->set_property = set_property;
-    object_class->finalize     = finalize;
+    object_class->get_property = _nm_setting_property_get_property_direct;
+    object_class->set_property = _nm_setting_property_set_property_direct;
 
     setting_class->verify = verify;
 
@@ -321,39 +246,47 @@ nm_setting_wpan_class_init(NMSettingWpanClass *klass)
      *   (e.g. 76:d8:9b:87:66:60:84:ee).
      * ---end---
      */
-    obj_properties[PROP_MAC_ADDRESS] =
-        g_param_spec_string(NM_SETTING_WPAN_MAC_ADDRESS,
-                            "",
-                            "",
-                            NULL,
-                            G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+    _nm_setting_property_define_direct_string(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_WPAN_MAC_ADDRESS,
+                                              PROP_MAC_ADDRESS,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingWpanPrivate,
+                                              mac_address,
+                                              .direct_set_string_mac_address_len =
+                                                  IEEE802154_ADDR_LEN);
 
     /**
      * NMSettingWpan:pan-id:
      *
      * IEEE 802.15.4 Personal Area Network (PAN) identifier.
      **/
-    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);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_WPAN_PAN_ID,
+                                              PROP_PAN_ID,
+                                              0,
+                                              G_MAXUINT16,
+                                              G_MAXUINT16,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingWpanPrivate,
+                                              pan_id);
 
     /**
      * NMSettingWpan:short-address:
      *
      * Short IEEE 802.15.4 address to be used within a restricted environment.
      **/
-    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);
+    _nm_setting_property_define_direct_uint32(properties_override,
+                                              obj_properties,
+                                              NM_SETTING_WPAN_SHORT_ADDRESS,
+                                              PROP_SHORT_ADDRESS,
+                                              0,
+                                              G_MAXUINT16,
+                                              G_MAXUINT16,
+                                              NM_SETTING_PARAM_NONE,
+                                              NMSettingWpanPrivate,
+                                              short_address);
 
     /**
      * NMSettingWpan:page:
@@ -363,13 +296,16 @@ nm_setting_wpan_class_init(NMSettingWpanClass *klass)
      *
      * 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);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_WPAN_PAGE,
+                                             PROP_PAGE,
+                                             G_MININT16,
+                                             G_MAXINT16,
+                                             NM_SETTING_WPAN_PAGE_DEFAULT,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingWpanPrivate,
+                                             page);
 
     /**
      * NMSettingWpan:channel:
@@ -379,15 +315,22 @@ nm_setting_wpan_class_init(NMSettingWpanClass *klass)
      *
      * 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);
+    _nm_setting_property_define_direct_int32(properties_override,
+                                             obj_properties,
+                                             NM_SETTING_WPAN_CHANNEL,
+                                             PROP_CHANNEL,
+                                             G_MININT16,
+                                             G_MAXINT16,
+                                             NM_SETTING_WPAN_CHANNEL_DEFAULT,
+                                             NM_SETTING_PARAM_NONE,
+                                             NMSettingWpanPrivate,
+                                             channel);
 
     g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties);
 
-    _nm_setting_class_commit(setting_class, NM_META_SETTING_TYPE_WPAN);
+    _nm_setting_class_commit(setting_class,
+                             NM_META_SETTING_TYPE_WPAN,
+                             NULL,
+                             properties_override,
+                             NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
 }
diff --git a/src/libnm-core-impl/nm-setting.c b/src/libnm-core-impl/nm-setting.c
index 54e1a176..9ef9e96c 100644
--- a/src/libnm-core-impl/nm-setting.c
+++ b/src/libnm-core-impl/nm-setting.c
@@ -54,6 +54,9 @@ G_DEFINE_ABSTRACT_TYPE(NMSetting, nm_setting, G_TYPE_OBJECT)
 /*****************************************************************************/
 
 static GenData *_gendata_hash(NMSetting *setting, gboolean create_if_necessary);
+static gboolean set_property_from_dbus(const NMSettInfoProperty *property_info,
+                                       GVariant *                src_value,
+                                       GValue *                  dst_value);
 
 /*****************************************************************************/
 
@@ -171,12 +174,15 @@ _nm_properties_override_assert(const NMSettInfoProperty *prop_info)
         /* we always require a dbus_type. */
         nm_assert(property_type->dbus_type);
 
-        /* from_dbus_fcn and gprop_from_dbus_fcn cannot both be set. */
-        nm_assert(!property_type->from_dbus_fcn || !property_type->gprop_from_dbus_fcn);
+        if (property_type->typdata_from_dbus.gprop_fcn)
+            nm_assert(property_type->from_dbus_fcn == _nm_setting_property_from_dbus_fcn_gprop);
+
+        if (property_type->from_dbus_fcn == _nm_setting_property_from_dbus_fcn_gprop)
+            nm_assert(prop_info->param_spec);
 
         if (!prop_info->param_spec) {
-            /* if we don't have a param_spec, we cannot have gprop_from_dbus_fcn. */
-            nm_assert(property_type->from_dbus_fcn || !property_type->gprop_from_dbus_fcn);
+            /* if we don't have a param_spec, we cannot have typdata_from_dbus.gprop_fcn. */
+            nm_assert(property_type->from_dbus_fcn || !property_type->typdata_from_dbus.gprop_fcn);
         }
     }
 #endif
@@ -227,11 +233,11 @@ _property_infos_sort_cmp_setting_connection(gconstpointer p_a,
 
 static const NMSettInfoProperty *const *
 _property_infos_sort(const NMSettInfoProperty *property_infos,
-                     guint                     property_infos_len,
+                     guint16                   property_infos_len,
                      NMSettingClass *          setting_class)
 {
     const NMSettInfoProperty **arr;
-    guint                      i;
+    guint16                    i;
 
 #if NM_MORE_ASSERTS > 5
     /* assert that the property names are all unique and sorted. */
@@ -263,15 +269,30 @@ _property_infos_sort(const NMSettInfoProperty *property_infos,
     return arr;
 }
 
+static int
+_property_lookup_by_param_spec_sort(gconstpointer p_a, gconstpointer p_b, gpointer user_data)
+{
+    const NMSettInfoPropertLookupByParamSpec *a = p_a;
+    const NMSettInfoPropertLookupByParamSpec *b = p_b;
+
+    NM_CMP_DIRECT(a->param_spec_as_uint, b->param_spec_as_uint);
+    return 0;
+}
+
 void
-_nm_setting_class_commit_full(NMSettingClass *            setting_class,
-                              NMMetaSettingType           meta_type,
-                              const NMSettInfoSettDetail *detail,
-                              GArray *                    properties_override)
+_nm_setting_class_commit(NMSettingClass *            setting_class,
+                         NMMetaSettingType           meta_type,
+                         const NMSettInfoSettDetail *detail,
+                         GArray *                    properties_override,
+                         gint16                      private_offset)
 {
     NMSettInfoSetting *sett_info;
-    gs_free GParamSpec **property_specs = NULL;
-    guint                i, n_property_specs, override_len;
+    gs_free GParamSpec **               property_specs = NULL;
+    guint                               n_property_specs;
+    NMSettInfoPropertLookupByParamSpec *lookup_by_iter;
+    guint                               override_len;
+    guint                               i;
+    guint16                             j;
 
     nm_assert(NM_IS_SETTING_CLASS(setting_class));
     nm_assert(!setting_class->setting_info);
@@ -284,25 +305,26 @@ _nm_setting_class_commit_full(NMSettingClass *            setting_class,
     nm_assert(!sett_info->property_infos_len);
     nm_assert(!sett_info->property_infos);
 
+    property_specs =
+        g_object_class_list_properties(G_OBJECT_CLASS(setting_class), &n_property_specs);
+
     if (!properties_override) {
         override_len        = 0;
-        properties_override = _nm_sett_info_property_override_create_array();
-    } else
+        properties_override = _nm_sett_info_property_override_create_array_sized(n_property_specs);
+    } else {
         override_len = properties_override->len;
 
-    property_specs =
-        g_object_class_list_properties(G_OBJECT_CLASS(setting_class), &n_property_specs);
+        for (i = 0; i < override_len; i++) {
+            NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i);
 
-    for (i = 0; i < properties_override->len; i++) {
-        NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i);
+            nm_assert((!!p->name) != (!!p->param_spec));
 
-        nm_assert((!!p->name) != (!!p->param_spec));
-
-        if (!p->name) {
-            nm_assert(p->param_spec);
-            p->name = p->param_spec->name;
-        } else
-            nm_assert(!p->param_spec);
+            if (!p->name) {
+                nm_assert(p->param_spec);
+                p->name = p->param_spec->name;
+            } else
+                nm_assert(!p->param_spec);
+        }
     }
 
 #if NM_MORE_ASSERTS > 10
@@ -310,18 +332,18 @@ _nm_setting_class_commit_full(NMSettingClass *            setting_class,
     for (i = 0; i < override_len; i++) {
         const NMSettInfoProperty *p = &g_array_index(properties_override, NMSettInfoProperty, i);
         gboolean                  found = FALSE;
-        guint                     j;
+        guint                     k;
 
         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))
+        for (k = 0; k < n_property_specs; k++) {
+            if (!nm_streq(property_specs[k]->name, p->name))
                 continue;
             nm_assert(!found);
             found = TRUE;
-            nm_assert(p->param_spec == property_specs[j]);
+            nm_assert(p->param_spec == property_specs[k]);
         }
         nm_assert(found == (p->param_spec != NULL));
     }
@@ -353,36 +375,54 @@ _nm_setting_class_commit_full(NMSettingClass *            setting_class,
         nm_assert(p->param_spec);
 
         vtype = p->param_spec->value_type;
-        if (vtype == G_TYPE_BOOLEAN)
-            p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(G_VARIANT_TYPE_BOOLEAN);
-        else if (vtype == G_TYPE_UCHAR)
-            p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(G_VARIANT_TYPE_BYTE);
-        else if (vtype == G_TYPE_INT)
+        if (vtype == G_TYPE_INT)
             p->property_type = &nm_sett_info_propert_type_plain_i;
         else if (vtype == G_TYPE_UINT)
             p->property_type = &nm_sett_info_propert_type_plain_u;
         else if (vtype == G_TYPE_INT64)
-            p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(G_VARIANT_TYPE_INT64);
-        else if (vtype == G_TYPE_UINT64)
-            p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(G_VARIANT_TYPE_UINT64);
-        else if (vtype == G_TYPE_STRING)
-            p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(G_VARIANT_TYPE_STRING);
-        else if (vtype == G_TYPE_DOUBLE)
-            p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(G_VARIANT_TYPE_DOUBLE);
+            p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(
+                G_VARIANT_TYPE_INT64,
+                .compare_fcn       = _nm_setting_property_compare_fcn_default,
+                .from_dbus_fcn     = _nm_setting_property_from_dbus_fcn_gprop,
+                .from_dbus_is_full = TRUE);
+        else if (vtype == G_TYPE_STRING) {
+            nm_assert(nm_streq(p->name, NM_SETTING_NAME)
+                      == (!NM_FLAGS_HAS(p->param_spec->flags, G_PARAM_WRITABLE)));
+            if (!NM_FLAGS_HAS(p->param_spec->flags, G_PARAM_WRITABLE))
+                p->property_type = &nm_sett_info_propert_type_setting_name;
+            else {
+                p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(
+                    G_VARIANT_TYPE_STRING,
+                    .compare_fcn       = _nm_setting_property_compare_fcn_default,
+                    .from_dbus_fcn     = _nm_setting_property_from_dbus_fcn_gprop,
+                    .from_dbus_is_full = TRUE);
+            }
+        } else if (vtype == G_TYPE_DOUBLE)
+            p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(
+                G_VARIANT_TYPE_DOUBLE,
+                .compare_fcn       = _nm_setting_property_compare_fcn_default,
+                .from_dbus_fcn     = _nm_setting_property_from_dbus_fcn_gprop,
+                .from_dbus_is_full = TRUE);
         else if (vtype == G_TYPE_STRV)
-            p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(G_VARIANT_TYPE_STRING_ARRAY);
-        else if (vtype == G_TYPE_BYTES) {
             p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(
-                G_VARIANT_TYPE_BYTESTRING,
-                .typdata_to_dbus.gprop_type = NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_BYTES);
-        } else if (g_type_is_a(vtype, G_TYPE_ENUM)) {
+                G_VARIANT_TYPE_STRING_ARRAY,
+                .compare_fcn       = _nm_setting_property_compare_fcn_default,
+                .from_dbus_fcn     = _nm_setting_property_from_dbus_fcn_gprop,
+                .from_dbus_is_full = TRUE);
+        else if (g_type_is_a(vtype, G_TYPE_ENUM)) {
             p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(
                 G_VARIANT_TYPE_INT32,
-                .typdata_to_dbus.gprop_type = NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_ENUM);
+                .typdata_to_dbus.gprop_type = NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_ENUM,
+                .compare_fcn                = _nm_setting_property_compare_fcn_default,
+                .from_dbus_fcn              = _nm_setting_property_from_dbus_fcn_gprop,
+                .from_dbus_is_full          = TRUE);
         } else if (g_type_is_a(vtype, G_TYPE_FLAGS)) {
             p->property_type = NM_SETT_INFO_PROPERT_TYPE_GPROP(
                 G_VARIANT_TYPE_UINT32,
-                .typdata_to_dbus.gprop_type = NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_FLAGS);
+                .typdata_to_dbus.gprop_type = NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_FLAGS,
+                .compare_fcn                = _nm_setting_property_compare_fcn_default,
+                .from_dbus_fcn              = _nm_setting_property_from_dbus_fcn_gprop,
+                .from_dbus_is_full          = TRUE);
         } else
             nm_assert_not_reached();
 
@@ -397,9 +437,22 @@ has_property_type:
 
     setting_class->setting_info = &nm_meta_setting_infos[meta_type];
     sett_info->setting_class    = setting_class;
+
+    if (private_offset == NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS) {
+        int o;
+
+        o = g_type_class_get_instance_private_offset(setting_class);
+        nm_assert(o != NM_SETT_INFO_PRIVATE_OFFSET_FROM_CLASS);
+        nm_assert(o > G_MININT16);
+        nm_assert(o < 0);
+        private_offset = o;
+    }
+    sett_info->private_offset = private_offset;
+
     if (detail)
         sett_info->detail = *detail;
     nm_assert(properties_override->len > 0);
+    nm_assert(properties_override->len < G_MAXUINT16);
     sett_info->property_infos_len = properties_override->len;
     sett_info->property_infos =
         nm_memdup(properties_override->data, sizeof(NMSettInfoProperty) * properties_override->len);
@@ -408,6 +461,33 @@ has_property_type:
                                                             sett_info->property_infos_len,
                                                             setting_class);
 
+    nm_assert(sett_info->property_infos_len < G_MAXUINT16);
+    sett_info->property_lookup_by_param_spec_len = 0;
+    for (j = 0; j < sett_info->property_infos_len; j++) {
+        if (sett_info->property_infos[j].param_spec) {
+            sett_info->property_lookup_by_param_spec_len++;
+        }
+    }
+    sett_info->property_lookup_by_param_spec =
+        g_new(NMSettInfoPropertLookupByParamSpec, sett_info->property_lookup_by_param_spec_len);
+    lookup_by_iter =
+        (NMSettInfoPropertLookupByParamSpec *) sett_info->property_lookup_by_param_spec;
+    for (j = 0; j < sett_info->property_infos_len; j++) {
+        const NMSettInfoProperty *property_info = &sett_info->property_infos[j];
+
+        if (property_info->param_spec) {
+            *(lookup_by_iter++) = (NMSettInfoPropertLookupByParamSpec){
+                .param_spec_as_uint = (uintptr_t) ((gpointer) property_info->param_spec),
+                .property_info      = property_info,
+            };
+        }
+    }
+    g_qsort_with_data(sett_info->property_lookup_by_param_spec,
+                      sett_info->property_lookup_by_param_spec_len,
+                      sizeof(NMSettInfoPropertLookupByParamSpec),
+                      _property_lookup_by_param_spec_sort,
+                      NULL);
+
     g_array_free(properties_override, TRUE);
 }
 
@@ -415,7 +495,7 @@ const NMSettInfoProperty *
 _nm_sett_info_setting_get_property_info(const NMSettInfoSetting *sett_info,
                                         const char *             property_name)
 {
-    const NMSettInfoProperty *property;
+    const NMSettInfoProperty *property_info;
     gssize                    idx;
 
     nm_assert(property_name);
@@ -434,13 +514,13 @@ _nm_sett_info_setting_get_property_info(const NMSettInfoSetting *sett_info,
     if (idx < 0)
         return NULL;
 
-    property = &sett_info->property_infos[idx];
+    property_info = &sett_info->property_infos[idx];
 
-    nm_assert(idx == 0 || strcmp(property[-1].name, property[0].name) < 0);
+    nm_assert(idx == 0 || strcmp(property_info[-1].name, property_info[0].name) < 0);
     nm_assert(idx == sett_info->property_infos_len - 1
-              || strcmp(property[0].name, property[1].name) < 0);
+              || strcmp(property_info[0].name, property_info[1].name) < 0);
 
-    return property;
+    return property_info;
 }
 
 const NMSettInfoSetting *
@@ -457,6 +537,47 @@ _nm_setting_class_get_sett_info(NMSettingClass *setting_class)
     return sett_info;
 }
 
+const NMSettInfoProperty *
+_nm_sett_info_property_lookup_by_param_spec(const NMSettInfoSetting *sett_info,
+                                            const GParamSpec *       param_spec)
+{
+    NMSettInfoPropertLookupByParamSpec needle;
+    int                                imin;
+    int                                imax;
+    int                                imid;
+    int                                cmp;
+
+    nm_assert(sett_info);
+    nm_assert(param_spec);
+
+    /* ensure that "int" is large enough to contain the index variables. */
+    G_STATIC_ASSERT_EXPR(sizeof(int) > sizeof(sett_info->property_lookup_by_param_spec_len));
+
+    if (sett_info->property_lookup_by_param_spec_len == 0)
+        return NULL;
+
+    needle.param_spec_as_uint = (uintptr_t) ((gpointer) param_spec);
+
+    imin = 0;
+    imax = sett_info->property_lookup_by_param_spec_len - 1;
+    while (imin <= imax) {
+        imid = imin + (imax - imin) / 2;
+
+        cmp = _property_lookup_by_param_spec_sort(&sett_info->property_lookup_by_param_spec[imid],
+                                                  &needle,
+                                                  NULL);
+        if (cmp == 0)
+            return sett_info->property_lookup_by_param_spec[imid].property_info;
+
+        if (cmp < 0)
+            imin = imid + 1;
+        else
+            imax = imid - 1;
+    }
+
+    return NULL;
+}
+
 /*****************************************************************************/
 
 void
@@ -494,124 +615,602 @@ _nm_setting_use_legacy_property(NMSetting * setting,
                                 const char *legacy_property,
                                 const char *new_property)
 {
-    GVariant *setting_dict, *value;
+    gs_unref_variant GVariant *setting_dict = NULL;
+    gs_unref_variant GVariant *value        = NULL;
+
+    if (!connection_dict) {
+        /* we also allow the caller to provide no connection_dict.
+         *
+         * We hit this code bug when being called by update_one_secret().
+         * In this case, we use the legacy property, because we are not
+         * sophisticated enough to mediate between deprecated and legacy
+         * properties...
+         *
+         * However, in practice this code is unreachable, because update_one_secret()
+         * only ends up calling from_dbus_fcn() for certain properties, and none
+         * of those are actually deprecated (for now). So this cannot really happen. */
+        return nm_assert_unreachable_val(FALSE);
+    }
 
     setting_dict = g_variant_lookup_value(connection_dict,
                                           nm_setting_get_name(NM_SETTING(setting)),
                                           NM_VARIANT_TYPE_SETTING);
+
     g_return_val_if_fail(setting_dict != NULL, FALSE);
 
     /* If the new property isn't set, we have to use the legacy property. */
     value = g_variant_lookup_value(setting_dict, new_property, NULL);
-    if (!value) {
-        g_variant_unref(setting_dict);
+    if (!value)
         return TRUE;
-    }
-    g_variant_unref(value);
+    nm_clear_pointer(&value, g_variant_unref);
 
     /* Otherwise, clients always prefer new properties sent from the daemon. */
-    if (!_nm_utils_is_manager_process) {
-        g_variant_unref(setting_dict);
+    if (!_nm_utils_is_manager_process)
         return FALSE;
-    }
 
     /* The daemon prefers the legacy property if it exists. */
     value = g_variant_lookup_value(setting_dict, legacy_property, NULL);
-    g_variant_unref(setting_dict);
+    return !!value;
+}
 
-    if (value) {
-        g_variant_unref(value);
-        return TRUE;
-    } else
-        return FALSE;
+/*****************************************************************************/
+
+static gboolean
+_property_direct_set_string(const NMSettInfoProperty *property_info, char **dst, const char *src)
+{
+    if (property_info->direct_set_string_ascii_strdown)
+        return nm_strdup_reset_take(dst, src ? g_ascii_strdown(src, -1) : NULL);
+    if (property_info->direct_set_string_mac_address_len > 0) {
+        return nm_strdup_reset_take(dst,
+                                    _nm_utils_hwaddr_canonical_or_invalid(
+                                        src,
+                                        property_info->direct_set_string_mac_address_len));
+    }
+    if (property_info->direct_set_string_ip_address_addr_family != 0) {
+        return nm_strdup_reset_take(dst,
+                                    _nm_utils_ipaddr_canonical_or_invalid(
+                                        property_info->direct_set_string_ip_address_addr_family,
+                                        src));
+    }
+    return nm_strdup_reset(dst, src);
+}
+
+void
+_nm_setting_property_get_property_direct(GObject *   object,
+                                         guint       prop_id,
+                                         GValue *    value,
+                                         GParamSpec *pspec)
+{
+    NMSetting *               setting = NM_SETTING(object);
+    const NMSettInfoSetting * sett_info;
+    const NMSettInfoProperty *property_info;
+
+    sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
+    nm_assert(sett_info);
+
+    property_info = _nm_sett_info_property_lookup_by_param_spec(sett_info, pspec);
+    if (!property_info)
+        goto out_fail;
+
+    nm_assert(property_info->param_spec == pspec);
+
+    switch (property_info->property_type->direct_type) {
+    case NM_VALUE_TYPE_BOOL:
+    {
+        const bool *p_val =
+            _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+
+        g_value_set_boolean(value, *p_val);
+        return;
+    }
+    case NM_VALUE_TYPE_INT32:
+    {
+        const gint32 *p_val =
+            _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+
+        g_value_set_int(value, *p_val);
+        return;
+    }
+    case NM_VALUE_TYPE_UINT32:
+    {
+        const guint32 *p_val =
+            _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+
+        g_value_set_uint(value, *p_val);
+        return;
+    }
+    case NM_VALUE_TYPE_UINT64:
+    {
+        const guint64 *p_val =
+            _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+
+        g_value_set_uint64(value, *p_val);
+        return;
+    }
+    case NM_VALUE_TYPE_ENUM:
+    {
+        const int *p_val =
+            _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+
+        g_value_set_enum(value, *p_val);
+        return;
+    }
+    case NM_VALUE_TYPE_FLAGS:
+    {
+        const guint *p_val =
+            _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+
+        g_value_set_flags(value, *p_val);
+        return;
+    }
+    case NM_VALUE_TYPE_STRING:
+    {
+        const char *const *p_val =
+            _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+
+        g_value_set_string(value, *p_val);
+        return;
+    }
+    case NM_VALUE_TYPE_BYTES:
+    {
+        const GBytes *const *p_val =
+            _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+
+        g_value_set_boxed(value, *p_val);
+        return;
+    }
+    default:
+        goto out_fail;
+    }
+
+    return;
+
+out_fail:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+}
+
+void
+_nm_setting_property_set_property_direct(GObject *     object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec *  pspec)
+{
+    NMSetting *               setting = NM_SETTING(object);
+    const NMSettInfoSetting * sett_info;
+    const NMSettInfoProperty *property_info;
+
+    sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
+    nm_assert(sett_info);
+
+    property_info = _nm_sett_info_property_lookup_by_param_spec(sett_info, pspec);
+    if (!property_info)
+        goto out_fail;
+
+    nm_assert(property_info->param_spec == pspec);
+
+    switch (property_info->property_type->direct_type) {
+    case NM_VALUE_TYPE_BOOL:
+    {
+        bool *   p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        gboolean v;
+
+        v = g_value_get_boolean(value);
+        if (*p_val == v)
+            return;
+        *p_val = v;
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_INT32:
+    {
+        gint32 *p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        int     v;
+
+        v = g_value_get_int(value);
+        if (*p_val == v)
+            return;
+        *p_val = v;
+
+        /* truncation cannot happen, because the param_spec is supposed to have suitable
+         * minimum/maximum values so that we are in range for int32. */
+        nm_assert(*p_val == v);
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_UINT32:
+    {
+        guint32 *p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        guint    v;
+
+        v = g_value_get_uint(value);
+        if (*p_val == v)
+            return;
+        *p_val = v;
+
+        /* truncation cannot happen, because the param_spec is supposed to have suitable
+         * minimum/maximum values so that we are in range for uint32. */
+        nm_assert(*p_val == v);
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_UINT64:
+    {
+        guint64 *p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        guint64  v;
+
+        v = g_value_get_uint64(value);
+        if (*p_val == v)
+            return;
+        *p_val = v;
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_ENUM:
+    {
+        int *p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        int  v;
+
+        v = g_value_get_enum(value);
+        if (*p_val == v)
+            return;
+        *p_val = v;
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_FLAGS:
+    {
+        guint *p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        guint  v;
+
+        v = g_value_get_flags(value);
+        if (*p_val == v)
+            return;
+        *p_val = v;
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_STRING:
+        if (!_property_direct_set_string(
+                property_info,
+                _nm_setting_get_private(setting, sett_info, property_info->direct_offset),
+                g_value_get_string(value)))
+            return;
+        goto out_notify;
+    case NM_VALUE_TYPE_BYTES:
+    {
+        GBytes **p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        GBytes * v;
+        _nm_unused gs_unref_bytes GBytes *old = NULL;
+
+        v = g_value_get_boxed(value);
+        if (nm_g_bytes_equal0(*p_val, v))
+            return;
+        old    = *p_val;
+        *p_val = v ? g_bytes_ref(v) : NULL;
+        goto out_notify;
+    }
+    default:
+        goto out_fail;
+    }
+
+    return;
+
+out_notify:
+    /* If explicit-notify would be set, we would need to emit g_object_notify_by_pspec().
+     *
+     * Currently we never set that, also because we still support glib 2.40. */
+    nm_assert(!NM_FLAGS_HAS(pspec->flags, 1 << 30 /* G_PARAM_EXPLICIT_NOTIFY */));
+    return;
+
+out_fail:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+}
+
+/*****************************************************************************/
+
+static void
+_init_direct(NMSetting *setting)
+{
+    const NMSettInfoSetting *sett_info;
+    guint16                  i;
+
+    sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
+    nm_assert(sett_info);
+
+    for (i = 0; i < sett_info->property_infos_len; i++) {
+        const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
+
+        /* We don't emit any g_object_notify_by_pspec(), because this is
+         * only supposed to be called during initialization of the GObject
+         * instance. */
+
+        switch (property_info->property_type->direct_type) {
+        case NM_VALUE_TYPE_NONE:
+            break;
+        case NM_VALUE_TYPE_BOOL:
+        {
+            bool *p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+            gboolean def_val;
+
+            def_val = NM_G_PARAM_SPEC_GET_DEFAULT_BOOLEAN(property_info->param_spec);
+            nm_assert(*p_val == FALSE);
+            *p_val = def_val;
+            break;
+        }
+        case NM_VALUE_TYPE_INT32:
+        {
+            gint32 *p_val =
+                _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+            int def_val;
+
+            def_val = NM_G_PARAM_SPEC_GET_DEFAULT_INT(property_info->param_spec);
+            nm_assert(*p_val == 0);
+            *p_val = def_val;
+            break;
+        }
+        case NM_VALUE_TYPE_UINT32:
+        {
+            guint32 *p_val =
+                _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+            guint def_val;
+
+            def_val = NM_G_PARAM_SPEC_GET_DEFAULT_UINT(property_info->param_spec);
+            nm_assert(*p_val == 0);
+            *p_val = def_val;
+            break;
+        }
+        case NM_VALUE_TYPE_UINT64:
+        {
+            guint64 *p_val =
+                _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+            guint64 def_val;
+
+            def_val = NM_G_PARAM_SPEC_GET_DEFAULT_UINT64(property_info->param_spec);
+            nm_assert(*p_val == 0);
+            *p_val = def_val;
+            break;
+        }
+        case NM_VALUE_TYPE_ENUM:
+        {
+            int *p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+            int  def_val;
+
+            def_val = NM_G_PARAM_SPEC_GET_DEFAULT_ENUM(property_info->param_spec);
+            nm_assert(*p_val == 0);
+            *p_val = def_val;
+            break;
+        }
+        case NM_VALUE_TYPE_FLAGS:
+        {
+            guint *p_val =
+                _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+            guint def_val;
+
+            def_val = NM_G_PARAM_SPEC_GET_DEFAULT_FLAGS(property_info->param_spec);
+            nm_assert(*p_val == 0);
+            *p_val = def_val;
+            break;
+        }
+        case NM_VALUE_TYPE_STRING:
+            nm_assert(!NM_G_PARAM_SPEC_GET_DEFAULT_STRING(property_info->param_spec));
+            nm_assert(!(
+                *((const char *const *)
+                      _nm_setting_get_private(setting, sett_info, property_info->direct_offset))));
+            break;
+        case NM_VALUE_TYPE_BYTES:
+            nm_assert(!(
+                *((const GBytes *const *)
+                      _nm_setting_get_private(setting, sett_info, property_info->direct_offset))));
+            break;
+        default:
+            nm_assert_not_reached();
+            break;
+        }
+    }
+}
+
+static void
+_finalize_direct(NMSetting *setting)
+{
+    const NMSettInfoSetting *sett_info;
+    guint16                  i;
+
+    sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
+    nm_assert(sett_info);
+
+    for (i = 0; i < sett_info->property_infos_len; i++) {
+        const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
+
+        /* We only:
+         *
+         * - reset fields where there is something to free. E.g. boolean
+         *   properties are not reset to their default.
+         * - clear/free properties, without emitting g_object_notify_by_pspec(),
+         *   because this is called only during finalization. */
+
+        switch (property_info->property_type->direct_type) {
+        case NM_VALUE_TYPE_NONE:
+        case NM_VALUE_TYPE_BOOL:
+        case NM_VALUE_TYPE_INT32:
+        case NM_VALUE_TYPE_UINT32:
+        case NM_VALUE_TYPE_UINT64:
+        case NM_VALUE_TYPE_ENUM:
+        case NM_VALUE_TYPE_FLAGS:
+            break;
+        case NM_VALUE_TYPE_STRING:
+        {
+            char **p_val =
+                _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+
+            nm_clear_g_free(p_val);
+            break;
+        }
+        case NM_VALUE_TYPE_BYTES:
+        {
+            GBytes **p_val =
+                _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+
+            nm_clear_pointer(p_val, g_bytes_unref);
+            break;
+        }
+        default:
+            nm_assert_not_reached();
+            break;
+        }
+    }
 }
 
 /*****************************************************************************/
 
 GVariant *
-_nm_setting_property_to_dbus_fcn_get_boolean(const NMSettInfoSetting *               sett_info,
-                                             guint                                   property_idx,
-                                             NMConnection *                          connection,
-                                             NMSetting *                             setting,
-                                             NMConnectionSerializationFlags          flags,
-                                             const NMConnectionSerializationOptions *options)
-{
-    const NMSettInfoProperty *property_info = &sett_info->property_infos[property_idx];
-    gboolean                  val;
-
-    val = !!property_info->to_dbus_data.get_boolean(setting);
-    if (!property_info->to_dbus_data.including_default
-        && val == NM_G_PARAM_SPEC_GET_DEFAULT_BOOLEAN(property_info->param_spec))
-        return NULL;
-    return g_variant_ref(nm_g_variant_singleton_b(val));
+_nm_setting_property_to_dbus_fcn_direct(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+{
+    switch (property_info->property_type->direct_type) {
+    case NM_VALUE_TYPE_BOOL:
+    {
+        gboolean val;
+
+        val = *((bool *) _nm_setting_get_private(setting, sett_info, property_info->direct_offset));
+        if (!property_info->to_dbus_including_default
+            && val == NM_G_PARAM_SPEC_GET_DEFAULT_BOOLEAN(property_info->param_spec))
+            return NULL;
+        return g_variant_ref(nm_g_variant_singleton_b(val));
+    }
+    case NM_VALUE_TYPE_INT32:
+    {
+        gint32 val;
+
+        val =
+            *((gint32 *) _nm_setting_get_private(setting, sett_info, property_info->direct_offset));
+        if (!property_info->to_dbus_including_default
+            && val == NM_G_PARAM_SPEC_GET_DEFAULT_INT(property_info->param_spec))
+            return NULL;
+        return nm_g_variant_maybe_singleton_i(val);
+    }
+    case NM_VALUE_TYPE_UINT32:
+    {
+        guint32 val;
+
+        val = *(
+            (guint32 *) _nm_setting_get_private(setting, sett_info, property_info->direct_offset));
+        if (!property_info->to_dbus_including_default
+            && val == NM_G_PARAM_SPEC_GET_DEFAULT_UINT(property_info->param_spec))
+            return NULL;
+        return g_variant_new_uint32(val);
+    }
+    case NM_VALUE_TYPE_UINT64:
+    {
+        guint64 val;
+
+        val = *(
+            (guint64 *) _nm_setting_get_private(setting, sett_info, property_info->direct_offset));
+        if (!property_info->to_dbus_including_default
+            && val == NM_G_PARAM_SPEC_GET_DEFAULT_UINT64(property_info->param_spec))
+            return NULL;
+        return g_variant_new_uint64(val);
+    }
+    case NM_VALUE_TYPE_ENUM:
+    {
+        int val;
+
+        val = *((int *) _nm_setting_get_private(setting, sett_info, property_info->direct_offset));
+        if (!property_info->to_dbus_including_default
+            && val == NM_G_PARAM_SPEC_GET_DEFAULT_ENUM(property_info->param_spec))
+            return NULL;
+        return nm_g_variant_maybe_singleton_i(val);
+    }
+    case NM_VALUE_TYPE_FLAGS:
+    {
+        guint val;
+
+        val =
+            *((guint *) _nm_setting_get_private(setting, sett_info, property_info->direct_offset));
+        if (!property_info->to_dbus_including_default
+            && val == NM_G_PARAM_SPEC_GET_DEFAULT_FLAGS(property_info->param_spec))
+            return NULL;
+        return g_variant_new_uint32(val);
+    }
+    case NM_VALUE_TYPE_STRING:
+    {
+        const char *val;
+
+        /* For string properties that are implemented via this function, the default is always NULL.
+         * In general, having strings default to NULL is most advisable.
+         *
+         * Setting "including_default" for a string makes no sense because a
+         * GVariant of type "s" cannot express NULL. */
+        nm_assert(!NM_G_PARAM_SPEC_GET_DEFAULT_STRING(property_info->param_spec));
+        nm_assert(!property_info->to_dbus_including_default);
+
+        val = *((const char *const *) _nm_setting_get_private(setting,
+                                                              sett_info,
+                                                              property_info->direct_offset));
+        if (!val)
+            return NULL;
+        if (!val[0])
+            return g_variant_ref(nm_g_variant_singleton_s_empty());
+        return g_variant_new_string(val);
+    }
+    case NM_VALUE_TYPE_BYTES:
+    {
+        const GBytes *val;
+
+        /* Bytes have always NULL as default. Setting "including_default" has no defined meaning
+         * (but it could have). */
+        nm_assert(!property_info->to_dbus_including_default);
+
+        val = *((const GBytes *const *) _nm_setting_get_private(setting,
+                                                                sett_info,
+                                                                property_info->direct_offset));
+        if (!val)
+            return NULL;
+        return nm_g_bytes_to_variant_ay(val);
+    }
+    default:
+        return nm_assert_unreachable_val(NULL);
+    }
 }
 
 GVariant *
-_nm_setting_property_to_dbus_fcn_get_string(const NMSettInfoSetting *               sett_info,
-                                            guint                                   property_idx,
-                                            NMConnection *                          connection,
-                                            NMSetting *                             setting,
-                                            NMConnectionSerializationFlags          flags,
-                                            const NMConnectionSerializationOptions *options)
-{
-    const NMSettInfoProperty *property_info = &sett_info->property_infos[property_idx];
-    const char *              val;
-
-    /* For string properties that are implemented via this function, the default is always NULL.
-     * In general, having strings default to NULL is most advisable.
-     *
-     * Setting "including_default" for a string makes no sense because a
-     * GVariant of type "s" cannot express NULL. */
+_nm_setting_property_to_dbus_fcn_direct_mac_address(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+{
+    const char *val;
+
+    nm_assert(property_info->property_type == &nm_sett_info_propert_type_direct_mac_address);
+    nm_assert(property_info->property_type->direct_type == NM_VALUE_TYPE_STRING);
     nm_assert(!NM_G_PARAM_SPEC_GET_DEFAULT_STRING(property_info->param_spec));
-    nm_assert(!property_info->to_dbus_data.including_default);
+    nm_assert(!property_info->to_dbus_including_default);
 
-    val = property_info->to_dbus_data.get_string(setting);
-    if (!val)
-        return NULL;
-    if (!val[0])
-        return g_variant_ref(nm_g_variant_singleton_s_empty());
-    return g_variant_new_string(val);
+    val = *((const char *const *) _nm_setting_get_private(setting,
+                                                          sett_info,
+                                                          property_info->direct_offset));
+    return nm_utils_hwaddr_to_dbus(val);
 }
 
 GVariant *
-_nm_setting_property_to_dbus_fcn_gprop(const NMSettInfoSetting *               sett_info,
-                                       guint                                   property_idx,
-                                       NMConnection *                          connection,
-                                       NMSetting *                             setting,
-                                       NMConnectionSerializationFlags          flags,
-                                       const NMConnectionSerializationOptions *options)
-{
-    const NMSettInfoProperty *const property   = &sett_info->property_infos[property_idx];
-    nm_auto_unset_gvalue GValue     prop_value = {
+_nm_setting_property_to_dbus_fcn_ignore(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+{
+    return NULL;
+}
+
+GVariant *
+_nm_setting_property_to_dbus_fcn_gprop(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
+{
+    nm_auto_unset_gvalue GValue prop_value = {
         0,
     };
     GArray *tmp_array;
 
-    nm_assert(property->param_spec);
-    nm_assert(property->property_type->to_dbus_fcn == _nm_setting_property_to_dbus_fcn_gprop);
-    nm_assert(property->property_type->typdata_to_dbus.gprop_type
-                  == NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_DEFAULT
-              || !property->to_dbus_data.gprop_to_dbus_fcn);
+    nm_assert(property_info->param_spec);
+    nm_assert(property_info->property_type->to_dbus_fcn == _nm_setting_property_to_dbus_fcn_gprop);
 
-    g_value_init(&prop_value, property->param_spec->value_type);
+    g_value_init(&prop_value, property_info->param_spec->value_type);
 
-    g_object_get_property(G_OBJECT(setting), property->param_spec->name, &prop_value);
+    g_object_get_property(G_OBJECT(setting), property_info->param_spec->name, &prop_value);
 
-    if (!property->to_dbus_data.including_default
-        && g_param_value_defaults(property->param_spec, &prop_value))
+    if (!property_info->to_dbus_including_default
+        && g_param_value_defaults(property_info->param_spec, &prop_value))
         return NULL;
 
-    switch (property->property_type->typdata_to_dbus.gprop_type) {
+    switch (property_info->property_type->typdata_to_dbus.gprop_type) {
     case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_DEFAULT:
-        if (property->to_dbus_data.gprop_to_dbus_fcn)
-            return property->to_dbus_data.gprop_to_dbus_fcn(&prop_value);
-
-        return g_dbus_gvalue_to_gvariant(&prop_value, property->property_type->dbus_type);
-    case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_BYTES:
-        nm_assert(G_VALUE_HOLDS(&prop_value, G_TYPE_BYTES));
-        return nm_utils_gbytes_to_variant_ay(g_value_get_boxed(&prop_value));
+        return g_dbus_gvalue_to_gvariant(&prop_value, property_info->property_type->dbus_type);
     case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_ENUM:
-        return g_variant_new_int32(g_value_get_enum(&prop_value));
+        return nm_g_variant_maybe_singleton_i(g_value_get_enum(&prop_value));
     case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_FLAGS:
         return g_variant_new_uint32(g_value_get_flags(&prop_value));
     case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_GARRAY_UINT:
@@ -622,52 +1221,409 @@ _nm_setting_property_to_dbus_fcn_gprop(const NMSettInfoSetting *               s
         return nm_g_variant_new_au((const guint32 *) tmp_array->data, tmp_array->len);
     case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT:
         nm_assert(G_VALUE_HOLDS(&prop_value, G_TYPE_HASH_TABLE));
-        return nm_utils_strdict_to_variant_ass(g_value_get_boxed(&prop_value));
-    case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_MAC_ADDRESS:
-        return nm_utils_hwaddr_to_dbus(g_value_get_string(&prop_value));
+        return nm_strdict_to_variant_ass(g_value_get_boxed(&prop_value));
     }
 
     return nm_assert_unreachable_val(NULL);
 }
 
+gboolean
+_nm_setting_property_from_dbus_fcn_ignore(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+{
+    *out_is_modified = FALSE;
+    return TRUE;
+}
+
+gboolean
+_nm_setting_property_from_dbus_fcn_direct_mac_address(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+{
+    gsize         length = 0;
+    const guint8 *array;
+
+    nm_assert(property_info->param_spec);
+    nm_assert(property_info->property_type == &nm_sett_info_propert_type_direct_mac_address);
+    nm_assert(g_variant_type_equal(property_info->property_type->dbus_type, "ay"));
+    nm_assert(
+        g_variant_type_equal(g_variant_get_type(value), property_info->property_type->dbus_type));
+    nm_assert(property_info->direct_set_string_mac_address_len > 0);
+
+    array = g_variant_get_fixed_array(value, &length, 1);
+
+    if (nm_strdup_reset_take(
+            _nm_setting_get_private(setting, sett_info, property_info->direct_offset),
+            length > 0 ? nm_utils_hwaddr_ntoa(array, length) : NULL)) {
+        g_object_notify_by_pspec(G_OBJECT(setting), property_info->param_spec);
+    } else
+        *out_is_modified = FALSE;
+
+    return TRUE;
+}
+
+gboolean
+_nm_setting_property_from_dbus_fcn_direct(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+{
+    nm_assert(property_info->param_spec);
+    nm_assert(NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_WRITABLE));
+    nm_assert(!NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_CONSTRUCT_ONLY));
+    nm_assert(!property_info->property_type->typdata_from_dbus.gprop_fcn);
+
+#define _variant_get_value_transform(property_info, value, gtype, gvalue_get, out_val) \
+    ({                                                                                 \
+        const NMSettInfoProperty const *_property_info = (property_info);              \
+        const GType                     _gtype         = (gtype);                      \
+        GVariant *                      _value         = (value);                      \
+        gboolean                        _success       = FALSE;                        \
+                                                                                       \
+        nm_assert(_property_info->param_spec->value_type == _gtype);                   \
+        if (_property_info->property_type->from_dbus_direct_allow_transform) {         \
+            nm_auto_unset_gvalue GValue _gvalue = G_VALUE_INIT;                        \
+                                                                                       \
+            g_value_init(&_gvalue, _gtype);                                            \
+            if (_nm_property_variant_to_gvalue(_value, &_gvalue)) {                    \
+                *(out_val) = (gvalue_get(&_gvalue));                                   \
+                _success   = TRUE;                                                     \
+            }                                                                          \
+        }                                                                              \
+        _success;                                                                      \
+    })
+
+    *out_is_modified = FALSE;
+
+    switch (property_info->property_type->direct_type) {
+    case NM_VALUE_TYPE_BOOL:
+    {
+        bool *   p_val;
+        gboolean v;
+
+        if (g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN))
+            v = g_variant_get_boolean(value);
+        else {
+            if (!_variant_get_value_transform(property_info,
+                                              value,
+                                              G_TYPE_BOOLEAN,
+                                              g_value_get_boolean,
+                                              &v))
+                goto out_error_wrong_dbus_type;
+            v = !!v;
+        }
+
+        p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        if (*p_val == v)
+            goto out_unchanged;
+        *p_val = v;
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_INT32:
+    {
+        const GParamSpecInt *param_spec;
+        gint32 *             p_val;
+        int                  v;
+
+        if (g_variant_is_of_type(value, G_VARIANT_TYPE_INT32)) {
+            G_STATIC_ASSERT(sizeof(int) >= sizeof(gint32));
+            v = g_variant_get_int32(value);
+        } else {
+            if (!_variant_get_value_transform(property_info,
+                                              value,
+                                              G_TYPE_INT,
+                                              g_value_get_int,
+                                              &v))
+                goto out_error_wrong_dbus_type;
+        }
+
+        p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        if (*p_val == v)
+            goto out_unchanged;
+
+        param_spec = NM_G_PARAM_SPEC_CAST_INT(property_info->param_spec);
+        if (v < param_spec->minimum || v > param_spec->maximum)
+            goto out_error_param_spec_validation;
+        *p_val = v;
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_UINT32:
+    {
+        const GParamSpecUInt *param_spec;
+        guint32 *             p_val;
+        guint                 v;
+
+        if (g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) {
+            G_STATIC_ASSERT(sizeof(guint) >= sizeof(guint32));
+            v = g_variant_get_uint32(value);
+        } else {
+            if (!_variant_get_value_transform(property_info,
+                                              value,
+                                              G_TYPE_UINT,
+                                              g_value_get_uint,
+                                              &v))
+                goto out_error_wrong_dbus_type;
+        }
+
+        p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        if (*p_val == v)
+            goto out_unchanged;
+
+        param_spec = NM_G_PARAM_SPEC_CAST_UINT(property_info->param_spec);
+        if (v < param_spec->minimum || v > param_spec->maximum)
+            goto out_error_param_spec_validation;
+        *p_val = v;
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_UINT64:
+    {
+        const GParamSpecUInt64 *param_spec;
+        guint64 *               p_val;
+        guint64                 v;
+
+        if (g_variant_is_of_type(value, G_VARIANT_TYPE_UINT64))
+            v = g_variant_get_uint64(value);
+        else {
+            if (!_variant_get_value_transform(property_info,
+                                              value,
+                                              G_TYPE_UINT64,
+                                              g_value_get_uint64,
+                                              &v))
+                goto out_error_wrong_dbus_type;
+        }
+
+        p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        if (*p_val == v)
+            goto out_unchanged;
+
+        param_spec = NM_G_PARAM_SPEC_CAST_UINT64(property_info->param_spec);
+        if (v < param_spec->minimum || v > param_spec->maximum)
+            goto out_error_param_spec_validation;
+        *p_val = v;
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_ENUM:
+    {
+        const GParamSpecEnum *param_spec;
+        int *                 p_val;
+        int                   v;
+
+        param_spec = NM_G_PARAM_SPEC_CAST_ENUM(property_info->param_spec);
+
+        if (g_variant_is_of_type(value, G_VARIANT_TYPE_INT32)) {
+            G_STATIC_ASSERT(sizeof(int) >= sizeof(gint32));
+            v = g_variant_get_int32(value);
+        } else {
+            if (!_variant_get_value_transform(property_info,
+                                              value,
+                                              G_TYPE_FROM_CLASS(param_spec->enum_class),
+                                              g_value_get_flags,
+                                              &v))
+                goto out_error_wrong_dbus_type;
+        }
+
+        p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        if (*p_val == v)
+            goto out_unchanged;
+
+        if (!g_enum_get_value(param_spec->enum_class, v))
+            goto out_error_param_spec_validation;
+        *p_val = v;
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_FLAGS:
+    {
+        const GParamSpecFlags *param_spec;
+        guint *                p_val;
+        guint                  v;
+
+        param_spec = NM_G_PARAM_SPEC_CAST_FLAGS(property_info->param_spec);
+
+        if (g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) {
+            G_STATIC_ASSERT(sizeof(guint) >= sizeof(guint32));
+            v = g_variant_get_uint32(value);
+        } else {
+            if (!_variant_get_value_transform(property_info,
+                                              value,
+                                              G_TYPE_FROM_CLASS(param_spec->flags_class),
+                                              g_value_get_flags,
+                                              &v))
+                goto out_error_wrong_dbus_type;
+        }
+
+        p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        if (*p_val == v)
+            goto out_unchanged;
+
+        if ((v & param_spec->flags_class->mask) != v)
+            goto out_error_param_spec_validation;
+        *p_val = v;
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_STRING:
+    {
+        gs_free char *v_free = NULL;
+        char **       p_val;
+        const char *  v;
+
+        if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
+            v = g_variant_get_string(value, NULL);
+        } else {
+            if (!_variant_get_value_transform(property_info,
+                                              value,
+                                              G_TYPE_STRING,
+                                              g_value_dup_string,
+                                              &v_free))
+                goto out_error_wrong_dbus_type;
+            v = v_free;
+        }
+
+        p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        if (!_property_direct_set_string(property_info, p_val, v))
+            goto out_unchanged;
+
+        goto out_notify;
+    }
+    case NM_VALUE_TYPE_BYTES:
+    {
+        gs_unref_bytes GBytes *v = NULL;
+        GBytes **              p_val;
+
+        if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BYTESTRING))
+            goto out_error_wrong_dbus_type;
+
+        v = nm_g_bytes_new_from_variant_ay(value);
+
+        p_val = _nm_setting_get_private(setting, sett_info, property_info->direct_offset);
+        if (nm_g_bytes_equal0(*p_val, v))
+            goto out_unchanged;
+
+        NM_SWAP(p_val, &v);
+        goto out_notify;
+    }
+    default:
+        break;
+    }
+
+    nm_assert_not_reached();
+
+out_unchanged:
+    return TRUE;
+
+out_notify:
+    *out_is_modified = TRUE;
+    g_object_notify_by_pspec(G_OBJECT(setting), property_info->param_spec);
+    return TRUE;
+
+out_error_wrong_dbus_type:
+    if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
+        return TRUE;
+    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_info->property_type->dbus_type
+                    ? g_variant_type_peek_string(property_info->property_type->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_info->name);
+    return FALSE;
+
+out_error_param_spec_validation:
+    if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
+        return TRUE;
+    g_set_error(error,
+                NM_UTILS_ERROR,
+                NM_UTILS_ERROR_UNKNOWN,
+                _("value of type '%s' is invalid or out of range for property '%s'"),
+                g_variant_get_type_string(value),
+                property_info->name);
+    g_prefix_error(error, "%s.%s: ", nm_setting_get_name(setting), property_info->name);
+    return FALSE;
+}
+
+gboolean
+_nm_setting_property_from_dbus_fcn_gprop(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
+{
+    nm_auto_unset_gvalue GValue object_value = G_VALUE_INIT;
+    gs_free_error GError *local              = NULL;
+
+    nm_assert(property_info->param_spec);
+
+    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. */
+        *out_is_modified = FALSE;
+        if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
+            return TRUE;
+        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_info->property_type->dbus_type
+                        ? g_variant_type_peek_string(property_info->property_type->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_info->name);
+        return FALSE;
+    }
+
+    if (!nm_g_object_set_property(G_OBJECT(setting),
+                                  property_info->param_spec->name,
+                                  &object_value,
+                                  &local)) {
+        *out_is_modified = FALSE;
+        if (!NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
+            return TRUE;
+        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_info->name);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
 static GVariant *
 property_to_dbus(const NMSettInfoSetting *               sett_info,
-                 guint                                   property_idx,
+                 const NMSettInfoProperty *              property_info,
                  NMConnection *                          connection,
                  NMSetting *                             setting,
                  NMConnectionSerializationFlags          flags,
                  const NMConnectionSerializationOptions *options,
                  gboolean                                ignore_flags)
 {
-    const NMSettInfoProperty *property = &sett_info->property_infos[property_idx];
-    GVariant *                variant;
+    GVariant *variant;
 
-    nm_assert(property->property_type->dbus_type);
+    nm_assert(property_info->property_type->dbus_type);
 
-    if (!property->property_type->to_dbus_fcn) {
-        nm_assert(!property->param_spec);
-        nm_assert(!property->to_dbus_data.none);
+    if (!property_info->property_type->to_dbus_fcn) {
+        nm_assert(!property_info->param_spec);
         return NULL;
     }
 
-    if (property->param_spec
-        && (!ignore_flags
-            && !NM_FLAGS_HAS(property->param_spec->flags, NM_SETTING_PARAM_TO_DBUS_IGNORE_FLAGS))) {
-        if (!NM_FLAGS_HAS(property->param_spec->flags, G_PARAM_WRITABLE))
-            return NULL;
+    nm_assert(!property_info->param_spec
+              || NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_WRITABLE)
+              || property_info->property_type == &nm_sett_info_propert_type_setting_name);
 
-        if (NM_FLAGS_HAS(property->param_spec->flags, NM_SETTING_PARAM_LEGACY)
+    if (property_info->param_spec && !ignore_flags
+        && !NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_TO_DBUS_IGNORE_FLAGS)) {
+        if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_LEGACY)
             && !_nm_utils_is_manager_process)
             return NULL;
 
-        if (NM_FLAGS_HAS(property->param_spec->flags, NM_SETTING_PARAM_SECRET)) {
+        if (NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET)) {
             NMSettingSecretFlags f = NM_SETTING_SECRET_FLAG_NONE;
 
             if (NM_FLAGS_ANY(flags,
                              NM_CONNECTION_SERIALIZE_WITH_SECRETS_AGENT_OWNED
                                  | NM_CONNECTION_SERIALIZE_WITH_SECRETS_SYSTEM_OWNED
                                  | NM_CONNECTION_SERIALIZE_WITH_SECRETS_NOT_SAVED)) {
-                if (!nm_setting_get_secret_flags(setting, property->param_spec->name, &f, NULL))
+                if (!nm_setting_get_secret_flags(setting,
+                                                 property_info->param_spec->name,
+                                                 &f,
+                                                 NULL))
                     return NULL;
             }
 
@@ -679,48 +1635,40 @@ property_to_dbus(const NMSettInfoSetting *               sett_info,
         }
     }
 
-    variant = property->property_type
-                  ->to_dbus_fcn(sett_info, property_idx, connection, setting, flags, options);
+    variant = property_info->property_type
+                  ->to_dbus_fcn(sett_info, property_info, connection, setting, flags, options);
     nm_g_variant_take_ref(variant);
 
-    nm_assert(!variant || g_variant_is_of_type(variant, property->property_type->dbus_type));
+    nm_assert(!variant || g_variant_is_of_type(variant, property_info->property_type->dbus_type));
 
     return variant;
 }
 
 static gboolean
-set_property_from_dbus(const NMSettInfoProperty *property, GVariant *src_value, GValue *dst_value)
+set_property_from_dbus(const NMSettInfoProperty *property_info,
+                       GVariant *                src_value,
+                       GValue *                  dst_value)
 {
-    nm_assert(property->param_spec);
-    nm_assert(property->property_type->dbus_type);
+    nm_assert(property_info->param_spec);
+    nm_assert(property_info->property_type->dbus_type);
 
-    if (property->property_type->gprop_from_dbus_fcn) {
+    if (property_info->property_type->typdata_from_dbus.gprop_fcn) {
         if (!g_variant_type_equal(g_variant_get_type(src_value),
-                                  property->property_type->dbus_type))
+                                  property_info->property_type->dbus_type))
             return FALSE;
-        property->property_type->gprop_from_dbus_fcn(src_value, dst_value);
-    } else if (dst_value->g_type == G_TYPE_BYTES) {
+        property_info->property_type->typdata_from_dbus.gprop_fcn(src_value, dst_value);
+        return TRUE;
+    }
+
+    if (dst_value->g_type == G_TYPE_BYTES) {
         if (!g_variant_is_of_type(src_value, G_VARIANT_TYPE_BYTESTRING))
             return FALSE;
 
         _nm_utils_bytes_from_dbus(src_value, dst_value);
-    } else {
-        GValue tmp = G_VALUE_INIT;
-
-        g_dbus_gvariant_to_gvalue(src_value, &tmp);
-        if (G_VALUE_TYPE(&tmp) == G_VALUE_TYPE(dst_value))
-            *dst_value = tmp;
-        else {
-            gboolean success;
-
-            success = g_value_transform(&tmp, dst_value);
-            g_value_unset(&tmp);
-            if (!success)
-                return FALSE;
-        }
+        return TRUE;
     }
 
-    return TRUE;
+    return _nm_property_variant_to_gvalue(src_value, dst_value);
 }
 
 /**
@@ -747,7 +1695,9 @@ _nm_setting_to_dbus(NMSetting *                             setting,
     NMSettingPrivate *       priv;
     GVariantBuilder          builder;
     const NMSettInfoSetting *sett_info;
-    guint                    n_properties, i;
+    guint                    n_properties;
+    guint                    i;
+    guint16                  j;
     const char *const *      gendata_keys;
 
     g_return_val_if_fail(NM_IS_SETTING(setting), NULL);
@@ -765,12 +1715,14 @@ _nm_setting_to_dbus(NMSetting *                             setting,
     }
 
     sett_info = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
-    for (i = 0; i < sett_info->property_infos_len; i++) {
-        gs_unref_variant GVariant *dbus_value = NULL;
+    for (j = 0; j < sett_info->property_infos_len; j++) {
+        const NMSettInfoProperty *property_info = &sett_info->property_infos[j];
+        gs_unref_variant GVariant *dbus_value   = NULL;
 
-        dbus_value = property_to_dbus(sett_info, i, connection, setting, flags, options, FALSE);
+        dbus_value =
+            property_to_dbus(sett_info, property_info, connection, setting, flags, options, FALSE);
         if (dbus_value) {
-            g_variant_builder_add(&builder, "{sv}", sett_info->property_infos[i].name, dbus_value);
+            g_variant_builder_add(&builder, "{sv}", property_info->name, dbus_value);
         }
     }
 
@@ -878,6 +1830,80 @@ _nm_setting_new_from_dbus(GType               setting_type,
 }
 
 static gboolean
+_property_set_from_dbus(const NMSettInfoSetting * sett_info,
+                        const NMSettInfoProperty *property_info,
+                        NMSetting *               setting,
+                        GVariant *                connection_dict,
+                        GVariant *                value,
+                        NMSettingParseFlags       parse_flags,
+                        gboolean *                out_is_modified,
+                        GError **                 error)
+{
+    gs_free_error GError *local       = NULL;
+    NMTernary             is_modified = NM_TERNARY_DEFAULT;
+    gboolean              success;
+
+    NM_SET_OUT(out_is_modified, FALSE);
+
+    if (!property_info->property_type->from_dbus_fcn) {
+        nm_assert(!property_info->param_spec);
+        return TRUE;
+    }
+
+    if (property_info->property_type->from_dbus_is_full) {
+        /* These hooks perform their own type checking, and can coerce/ignore
+         * a value regardless of the D-Bus type. */
+    } else if (!g_variant_type_equal(g_variant_get_type(value),
+                                     property_info->property_type->dbus_type)) {
+        /* for backward behavior, fail unless best-effort is chosen. */
+        if (NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
+            return TRUE;
+        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_info->property_type->dbus_type
+                        ? g_variant_type_peek_string(property_info->property_type->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_info->name);
+        return FALSE;
+    }
+
+    success = property_info->property_type->from_dbus_fcn(sett_info,
+                                                          property_info,
+                                                          setting,
+                                                          connection_dict,
+                                                          value,
+                                                          parse_flags,
+                                                          &is_modified,
+                                                          &local);
+
+    /* We allow the from_dbus_fcn() to leave is_modified at NM_TERNARY_DEFAULT,
+     * which we assume to also mean that it was modified. That is, we err on the
+     * side of assuming modification happened. */
+    NM_SET_OUT(out_is_modified, is_modified != FALSE);
+
+    if (!success) {
+        if (property_info->property_type->from_dbus_is_full) {
+            /* the error we received from from_dbus_fcn() should be propagated, even
+             * in non-strict mode. */
+        } else if (!NM_FLAGS_HAS(parse_flags, NM_SETTING_PARSE_FLAGS_STRICT))
+            return TRUE;
+        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_info->name);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean
 init_from_dbus(NMSetting *                     setting,
                GHashTable *                    keys,
                GVariant *                      setting_dict,
@@ -886,8 +1912,7 @@ init_from_dbus(NMSetting *                     setting,
                GError **                       error)
 {
     const NMSettInfoSetting *sett_info;
-
-    guint i;
+    guint16                  i;
 
     nm_assert(NM_IS_SETTING(setting));
     nm_assert(!NM_FLAGS_ANY(parse_flags, ~NM_SETTING_PARSE_FLAGS_ALL));
@@ -931,56 +1956,21 @@ init_from_dbus(NMSetting *                     setting,
         gs_unref_variant GVariant *value        = NULL;
         gs_free_error GError *local             = NULL;
 
-        if (property_info->param_spec && !(property_info->param_spec->flags & G_PARAM_WRITABLE))
+        if (property_info->property_type == &nm_sett_info_propert_type_setting_name)
             continue;
 
-        value = g_variant_lookup_value(setting_dict, property_info->name, NULL);
+        nm_assert(!property_info->param_spec
+                  || NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_WRITABLE));
 
-        if (value && keys)
-            g_hash_table_remove(keys, property_info->name);
-
-        if (value && property_info->property_type->from_dbus_fcn) {
-            if (!g_variant_type_equal(g_variant_get_type(value),
-                                      property_info->property_type->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_info->property_type->dbus_type
-                        ? g_variant_type_peek_string(property_info->property_type->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_info->name);
-                return FALSE;
-            }
+        value = g_variant_lookup_value(setting_dict, property_info->name, NULL);
 
-            if (!property_info->property_type->from_dbus_fcn(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_info->name);
-                return FALSE;
-            }
-        } else if (!value && property_info->property_type->missing_from_dbus_fcn) {
-            if (!property_info->property_type->missing_from_dbus_fcn(setting,
-                                                                     connection_dict,
-                                                                     property_info->name,
-                                                                     parse_flags,
-                                                                     &local)) {
+        if (!value) {
+            if (property_info->property_type->missing_from_dbus_fcn
+                && !property_info->property_type->missing_from_dbus_fcn(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,
@@ -991,44 +1981,21 @@ init_from_dbus(NMSetting *                     setting,
                 g_prefix_error(error, "%s.%s: ", nm_setting_get_name(setting), property_info->name);
                 return FALSE;
             }
-        } else if (value && property_info->param_spec) {
-            nm_auto_unset_gvalue GValue object_value = G_VALUE_INIT;
+            continue;
+        }
 
-            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_info->property_type->dbus_type
-                        ? g_variant_type_peek_string(property_info->property_type->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_info->name);
-                return FALSE;
-            }
+        if (keys)
+            g_hash_table_remove(keys, property_info->name);
 
-            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_info->name);
-                return FALSE;
-            }
-        }
+        if (!_property_set_from_dbus(sett_info,
+                                     property_info,
+                                     setting,
+                                     connection_dict,
+                                     value,
+                                     parse_flags,
+                                     NULL,
+                                     error))
+            return FALSE;
     }
 
     return TRUE;
@@ -1047,19 +2014,21 @@ init_from_dbus(NMSetting *                     setting,
 const GVariantType *
 nm_setting_get_dbus_property_type(NMSetting *setting, const char *property_name)
 {
-    const NMSettInfoProperty *property;
+    const NMSettInfoProperty *property_info;
 
     g_return_val_if_fail(NM_IS_SETTING(setting), NULL);
     g_return_val_if_fail(property_name != NULL, NULL);
 
-    property = _nm_setting_class_get_property_info(NM_SETTING_GET_CLASS(setting), property_name);
+    property_info =
+        _nm_setting_class_get_property_info(NM_SETTING_GET_CLASS(setting), property_name);
 
-    g_return_val_if_fail(property != NULL, NULL);
+    g_return_val_if_fail(property_info != NULL, NULL);
 
-    nm_assert(property->property_type);
-    nm_assert(g_variant_type_string_is_valid((const char *) property->property_type->dbus_type));
+    nm_assert(property_info->property_type);
+    nm_assert(
+        g_variant_type_string_is_valid((const char *) property_info->property_type->dbus_type));
 
-    return property->property_type->dbus_type;
+    return property_info->property_type->dbus_type;
 }
 
 gboolean
@@ -1117,6 +2086,9 @@ _gobject_copy_property(GObject *src, GObject *dst, const char *property_name, GT
 static void
 duplicate_copy_properties(const NMSettInfoSetting *sett_info, NMSetting *src, NMSetting *dst)
 {
+    gboolean frozen = FALSE;
+    guint16  i;
+
     if (sett_info->detail.gendata_info) {
         GenData *gendata = _gendata_hash(src, FALSE);
 
@@ -1135,33 +2107,30 @@ duplicate_copy_properties(const NMSettInfoSetting *sett_info, NMSetting *src, NM
         }
     }
 
-    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];
 
-        for (i = 0; i < sett_info->property_infos_len; i++) {
-            const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
+        if (!property_info->param_spec)
+            continue;
 
-            if (property_info->param_spec) {
-                if ((property_info->param_spec->flags & (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY))
-                    != G_PARAM_WRITABLE)
-                    continue;
+        nm_assert(!NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_CONSTRUCT_ONLY));
+        if (property_info->property_type == &nm_sett_info_propert_type_setting_name)
+            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;
-            }
-        }
+        nm_assert(NM_FLAGS_HAS(property_info->param_spec->flags, G_PARAM_WRITABLE));
 
-        if (frozen)
-            g_object_thaw_notify(G_OBJECT(dst));
+        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));
     }
+
+    if (frozen)
+        g_object_thaw_notify(G_OBJECT(dst));
 }
 
 /**
@@ -1356,110 +2325,157 @@ _nm_setting_should_compare_secret_property(NMSetting *           setting,
     return TRUE;
 }
 
-static NMTernary
-compare_property(const NMSettInfoSetting *sett_info,
-                 guint                    property_idx,
-                 NMConnection *           con_a,
-                 NMSetting *              set_a,
-                 NMConnection *           con_b,
-                 NMSetting *              set_b,
-                 NMSettingCompareFlags    flags)
-{
-    const NMSettInfoProperty *property_info = &sett_info->property_infos[property_idx];
-    const GParamSpec *        param_spec    = property_info->param_spec;
-
-    if (!param_spec)
-        return NM_TERNARY_DEFAULT;
+/*****************************************************************************/
 
+gboolean
+_nm_setting_compare_flags_check(const GParamSpec *    param_spec,
+                                NMSettingCompareFlags flags,
+                                NMSetting *           set_a,
+                                NMSetting *           set_b)
+{
     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;
+        return 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;
+        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;
+        return FALSE;
 
     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 (nm_streq(param_spec->name, NM_SETTING_NAME))
-        return NM_TERNARY_DEFAULT;
+        return FALSE;
 
     if (NM_FLAGS_HAS(param_spec->flags, NM_SETTING_PARAM_SECRET)
         && !_nm_setting_should_compare_secret_property(set_a, set_b, param_spec->name, flags))
+        return FALSE;
+
+    return TRUE;
+}
+
+NMTernary
+_nm_setting_property_compare_fcn_ignore(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    return NM_TERNARY_DEFAULT;
+}
+
+NMTernary
+_nm_setting_property_compare_fcn_direct(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    gconstpointer p_a;
+    gconstpointer p_b;
+
+    nm_assert(NM_IN_SET(property_info->property_type->to_dbus_fcn,
+                        _nm_setting_property_to_dbus_fcn_direct,
+                        _nm_setting_property_to_dbus_fcn_direct_mac_address));
+
+    if (!property_info->param_spec)
+        return nm_assert_unreachable_val(NM_TERNARY_DEFAULT);
+
+    if (!_nm_setting_compare_flags_check(property_info->param_spec, flags, set_a, set_b))
         return NM_TERNARY_DEFAULT;
 
-    if (set_b) {
+    if (!set_b)
+        return TRUE;
+
+    p_a = _nm_setting_get_private(set_a, sett_info, property_info->direct_offset);
+    p_b = _nm_setting_get_private(set_b, sett_info, property_info->direct_offset);
+
+    switch (property_info->property_type->direct_type) {
+    case NM_VALUE_TYPE_BOOL:
+        return *((const bool *) p_a) == *((const bool *) p_b);
+    case NM_VALUE_TYPE_INT32:
+        return *((const gint32 *) p_a) == *((const gint32 *) p_b);
+    case NM_VALUE_TYPE_UINT32:
+        return *((const guint32 *) p_a) == *((const guint32 *) p_b);
+    case NM_VALUE_TYPE_UINT64:
+        return *((const guint64 *) p_a) == *((const guint64 *) p_b);
+    case NM_VALUE_TYPE_ENUM:
+        return *((const int *) p_a) == *((const int *) p_b);
+    case NM_VALUE_TYPE_FLAGS:
+        return *((const guint *) p_a) == *((const guint *) p_b);
+    case NM_VALUE_TYPE_STRING:
+        return nm_streq0(*((const char *const *) p_a), *((const char *const *) p_b));
+    case NM_VALUE_TYPE_BYTES:
+        return nm_g_bytes_equal0(*((const GBytes *const *) p_a), *((const GBytes *const *) p_b));
+    default:
+        return nm_assert_unreachable_val(TRUE);
+    }
+}
+
+NMTernary
+_nm_setting_property_compare_fcn_default(_NM_SETT_INFO_PROP_COMPARE_FCN_ARGS _nm_nil)
+{
+    nm_assert(property_info->property_type->direct_type == NM_VALUE_TYPE_NONE);
+
+    if (!property_info->param_spec)
+        return nm_assert_unreachable_val(NM_TERNARY_DEFAULT);
+
+    if (!_nm_setting_compare_flags_check(property_info->param_spec, flags, set_a, set_b))
+        return NM_TERNARY_DEFAULT;
+
+    if (!set_b)
+        return TRUE;
+
+    {
         gs_unref_variant GVariant *value1 = NULL;
         gs_unref_variant GVariant *value2 = NULL;
 
         value1 = property_to_dbus(sett_info,
-                                  property_idx,
+                                  property_info,
                                   con_a,
                                   set_a,
                                   NM_CONNECTION_SERIALIZE_ALL,
                                   NULL,
                                   TRUE);
         value2 = property_to_dbus(sett_info,
-                                  property_idx,
+                                  property_info,
                                   con_b,
                                   set_b,
                                   NM_CONNECTION_SERIALIZE_ALL,
                                   NULL,
                                   TRUE);
-        if (nm_property_compare(value1, value2) != 0)
-            return NM_TERNARY_FALSE;
+        return nm_property_compare(value1, value2) == 0;
     }
-
-    return NM_TERNARY_TRUE;
 }
 
 static NMTernary
-_compare_property(const NMSettInfoSetting *sett_info,
-                  guint                    property_idx,
-                  NMConnection *           con_a,
-                  NMSetting *              set_a,
-                  NMConnection *           con_b,
-                  NMSetting *              set_b,
-                  NMSettingCompareFlags    flags)
+_compare_property(const NMSettInfoSetting * sett_info,
+                  const NMSettInfoProperty *property_info,
+                  NMConnection *            con_a,
+                  NMSetting *               set_a,
+                  NMConnection *            con_b,
+                  NMSetting *               set_b,
+                  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(property_info);
     nm_assert(NM_SETTING_GET_CLASS(set_a) == sett_info->setting_class);
     nm_assert(!set_b || NM_SETTING_GET_CLASS(set_b) == sett_info->setting_class);
 
-    compare_result =
-        NM_SETTING_GET_CLASS(set_a)
-            ->compare_property(sett_info, property_idx, con_a, set_a, con_b, set_b, flags);
+    compare_result = property_info->property_type
+                         ->compare_fcn(sett_info, property_info, con_a, set_a, con_b, set_b, flags);
 
-    nm_assert(NM_IN_SET(compare_result, NM_TERNARY_DEFAULT, NM_TERNARY_FALSE, NM_TERNARY_TRUE));
+    nm_assert_is_ternary(compare_result);
 
     /* 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)
+    nm_assert(!NM_FLAGS_HAS(flags, NM_SETTING_COMPARE_FLAG_INFERRABLE) || !property_info->param_spec
+              || NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_INFERRABLE)
               || compare_result == NM_TERNARY_DEFAULT);
 
 #if NM_MORE_ASSERTS > 10
-    /* assert that compare_property() is symeric. */
-    nm_assert(!set_b
-              || compare_result
-                     == NM_SETTING_GET_CLASS(set_a)->compare_property(sett_info,
-                                                                      property_idx,
-                                                                      con_b,
-                                                                      set_b,
-                                                                      con_a,
-                                                                      set_a,
-                                                                      flags));
+    /* assert that compare_fcn() is symeric. */
+    nm_assert(
+        !set_b
+        || compare_result
+               == property_info->property_type
+                      ->compare_fcn(sett_info, property_info, con_b, set_b, con_a, set_a, flags));
 #endif
 
     return compare_result;
@@ -1491,7 +2507,7 @@ _nm_setting_compare(NMConnection *        con_a,
                     NMSettingCompareFlags flags)
 {
     const NMSettInfoSetting *sett_info;
-    guint                    i;
+    guint16                  i;
 
     g_return_val_if_fail(NM_IS_SETTING(a), FALSE);
     g_return_val_if_fail(NM_IS_SETTING(b), FALSE);
@@ -1516,7 +2532,8 @@ _nm_setting_compare(NMConnection *        con_a,
     }
 
     for (i = 0; i < sett_info->property_infos_len; i++) {
-        if (_compare_property(sett_info, i, con_a, a, con_b, b, flags) == NM_TERNARY_FALSE)
+        if (_compare_property(sett_info, &sett_info->property_infos[i], con_a, a, con_b, b, flags)
+            == NM_TERNARY_FALSE)
             return FALSE;
     }
 
@@ -1581,7 +2598,6 @@ _nm_setting_diff(NMConnection *        con_a,
                  GHashTable **         results)
 {
     const NMSettInfoSetting *sett_info;
-    guint                    i;
     NMSettingDiffResult      a_result         = NM_SETTING_DIFF_RESULT_IN_A;
     NMSettingDiffResult      b_result         = NM_SETTING_DIFF_RESULT_IN_B;
     NMSettingDiffResult      a_result_default = NM_SETTING_DIFF_RESULT_IN_A_DEFAULT;
@@ -1589,6 +2605,7 @@ _nm_setting_diff(NMConnection *        con_a,
     gboolean                 results_created  = FALSE;
     gboolean                 compared_any     = FALSE;
     gboolean                 diff_found       = FALSE;
+    guint16                  i;
 
     g_return_val_if_fail(results != NULL, FALSE);
     g_return_val_if_fail(NM_IS_SETTING(a), FALSE);
@@ -1672,12 +2689,12 @@ _nm_setting_diff(NMConnection *        con_a,
         }
     } else {
         for (i = 0; i < sett_info->property_infos_len; i++) {
-            NMSettingDiffResult       r = NM_SETTING_DIFF_RESULT_UNKNOWN;
-            const NMSettInfoProperty *property_info;
+            NMSettingDiffResult       r             = NM_SETTING_DIFF_RESULT_UNKNOWN;
+            const NMSettInfoProperty *property_info = &sett_info->property_infos[i];
             NMTernary                 compare_result;
             GParamSpec *              prop_spec;
 
-            compare_result = _compare_property(sett_info, i, con_a, a, con_b, b, flags);
+            compare_result = _compare_property(sett_info, property_info, con_a, a, con_b, b, flags);
             if (compare_result == NM_TERNARY_DEFAULT)
                 continue;
 
@@ -1698,15 +2715,14 @@ _nm_setting_diff(NMConnection *        con_a,
                  *
                  * We need to double-check whether the property should be ignored by
                  * looking at @a alone. */
-                if (_compare_property(sett_info, i, con_a, a, NULL, NULL, flags)
+                if (_compare_property(sett_info, property_info, con_a, a, NULL, NULL, flags)
                     == NM_TERNARY_DEFAULT)
                     continue;
             }
 
             compared_any = TRUE;
 
-            property_info = &sett_info->property_infos[i];
-            prop_spec     = property_info->param_spec;
+            prop_spec = property_info->param_spec;
 
             if (b) {
                 if (compare_result == NM_TERNARY_FALSE) {
@@ -1826,6 +2842,7 @@ nm_setting_enumerate_values(NMSetting *setting, NMSettingValueIterFn func, gpoin
 {
     const NMSettInfoSetting *sett_info;
     guint                    i;
+    guint16                  j;
 
     g_return_if_fail(NM_IS_SETTING(setting));
     g_return_if_fail(func != NULL);
@@ -1864,9 +2881,9 @@ nm_setting_enumerate_values(NMSetting *setting, NMSettingValueIterFn func, gpoin
         return;
     }
 
-    for (i = 0; i < sett_info->property_infos_len; i++) {
+    for (j = 0; j < sett_info->property_infos_len; j++) {
         NM_SETTING_GET_CLASS(setting)->enumerate_values(
-            _nm_sett_info_property_info_get_sorted(sett_info, i),
+            _nm_sett_info_property_info_get_sorted(sett_info, j),
             setting,
             func,
             user_data);
@@ -1878,7 +2895,7 @@ aggregate(NMSetting *setting, int type_i, gpointer arg)
 {
     NMConnectionAggregateType type = type_i;
     const NMSettInfoSetting * sett_info;
-    guint                     i;
+    guint16                   i;
 
     nm_assert(NM_IN_SET(type,
                         NM_CONNECTION_AGGREGATE_ANY_SECRETS,
@@ -1953,13 +2970,13 @@ _nm_setting_aggregate(NMSetting *setting, NMConnectionAggregateType type, gpoint
 
 static gboolean
 clear_secrets(const NMSettInfoSetting *        sett_info,
-              guint                            property_idx,
+              const NMSettInfoProperty *       property_info,
               NMSetting *                      setting,
               NMSettingClearSecretsWithFlagsFn func,
               gpointer                         user_data)
 {
     NMSettingSecretFlags flags      = NM_SETTING_SECRET_FLAG_NONE;
-    GParamSpec *         param_spec = sett_info->property_infos[property_idx].param_spec;
+    GParamSpec *         param_spec = property_info->param_spec;
 
     if (!param_spec)
         return FALSE;
@@ -2008,20 +3025,20 @@ _nm_setting_clear_secrets(NMSetting *                      setting,
 {
     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);
+    NMSettingClass *         klass;
+    guint16                  i;
 
     g_return_val_if_fail(NM_IS_SETTING(setting), FALSE);
 
-    my_clear_secrets = NM_SETTING_GET_CLASS(setting)->clear_secrets;
+    klass = 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++) {
-        changed |= my_clear_secrets(sett_info, i, setting, func, user_data);
+        changed |= klass->clear_secrets(sett_info,
+                                        &sett_info->property_infos[i],
+                                        setting,
+                                        func,
+                                        user_data);
     }
     return changed;
 }
@@ -2056,14 +3073,13 @@ _nm_setting_need_secrets(NMSetting *setting)
 static int
 update_one_secret(NMSetting *setting, const char *key, GVariant *value, GError **error)
 {
-    const NMSettInfoProperty *property;
-    GParamSpec *              prop_spec;
-    GValue                    prop_value = {
-        0,
-    };
+    const NMSettInfoSetting * sett_info;
+    const NMSettInfoProperty *property_info;
+    gboolean                  is_modified;
 
-    property = _nm_setting_class_get_property_info(NM_SETTING_GET_CLASS(setting), key);
-    if (!property) {
+    sett_info     = _nm_setting_class_get_sett_info(NM_SETTING_GET_CLASS(setting));
+    property_info = _nm_sett_info_setting_get_property_info(sett_info, key);
+    if (!property_info) {
         g_set_error_literal(error,
                             NM_CONNECTION_ERROR,
                             NM_CONNECTION_ERROR_PROPERTY_NOT_FOUND,
@@ -2072,32 +3088,25 @@ update_one_secret(NMSetting *setting, const char *key, GVariant *value, GError *
         return NM_SETTING_UPDATE_SECRET_ERROR;
     }
 
-    /* Silently ignore non-secrets */
-    prop_spec = property->param_spec;
-    if (!prop_spec || !(prop_spec->flags & NM_SETTING_PARAM_SECRET))
+    if (!property_info->param_spec
+        || !NM_FLAGS_HAS(property_info->param_spec->flags, NM_SETTING_PARAM_SECRET)) {
+        /* Silently ignore non-secrets */
         return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
-
-    if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) && G_IS_PARAM_SPEC_STRING(prop_spec)) {
-        /* String is expected to be a common case. Handle it specially and check
-         * whether the value is already set. Otherwise, we just reset the
-         * property and assume the value got modified.
-         */
-        char *v;
-
-        g_object_get(G_OBJECT(setting), prop_spec->name, &v, NULL);
-        if (g_strcmp0(v, g_variant_get_string(value, NULL)) == 0) {
-            g_free(v);
-            return NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
-        }
-        g_free(v);
     }
 
-    g_value_init(&prop_value, prop_spec->value_type);
-    set_property_from_dbus(property, value, &prop_value);
-    g_object_set_property(G_OBJECT(setting), prop_spec->name, &prop_value);
-    g_value_unset(&prop_value);
+    if (!_property_set_from_dbus(sett_info,
+                                 property_info,
+                                 setting,
+                                 NULL,
+                                 value,
+                                 NM_SETTING_PARSE_FLAGS_BEST_EFFORT,
+                                 &is_modified,
+                                 NULL)) {
+        /* Silently ignore errors. */
+    }
 
-    return NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED;
+    return is_modified ? NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED
+                       : NM_SETTING_UPDATE_SECRET_SUCCESS_UNCHANGED;
 }
 
 /**
@@ -2354,12 +3363,7 @@ nm_setting_to_string(NMSetting *setting)
 }
 
 static GVariant *
-_nm_setting_get_deprecated_virtual_interface_name(const NMSettInfoSetting *      sett_info,
-                                                  guint                          property_idx,
-                                                  NMConnection *                 connection,
-                                                  NMSetting *                    setting,
-                                                  NMConnectionSerializationFlags flags,
-                                                  const NMConnectionSerializationOptions *options)
+_nm_setting_get_deprecated_virtual_interface_name(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     NMSettingConnection *s_con;
 
@@ -2378,34 +3382,120 @@ _nm_setting_get_deprecated_virtual_interface_name(const NMSettInfoSetting *
 
 const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_interface_name =
     NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING,
+                                        .compare_fcn = _nm_setting_property_compare_fcn_ignore,
                                         .to_dbus_fcn =
                                             _nm_setting_get_deprecated_virtual_interface_name, );
 
+const NMSettInfoPropertType nm_sett_info_propert_type_setting_name =
+    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING,
+                                        .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_ignore,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_ignore,
+                                        .from_dbus_is_full = TRUE,
+                                        .compare_fcn = _nm_setting_property_compare_fcn_ignore);
+
 const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_ignore_i =
     NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(
         G_VARIANT_TYPE_INT32,
         /* No functions set. This property type is to silently ignore the value on D-Bus. */
-    );
+        .compare_fcn = _nm_setting_property_compare_fcn_ignore);
 
 const NMSettInfoPropertType nm_sett_info_propert_type_deprecated_ignore_u =
     NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(
         G_VARIANT_TYPE_UINT32,
         /* No functions set. This property type is to silently ignore the value on D-Bus. */
-    );
+        .compare_fcn = _nm_setting_property_compare_fcn_ignore);
 
 const NMSettInfoPropertType nm_sett_info_propert_type_plain_i =
-    NM_SETT_INFO_PROPERT_TYPE_GPROP_INIT(G_VARIANT_TYPE_INT32);
+    NM_SETT_INFO_PROPERT_TYPE_GPROP_INIT(G_VARIANT_TYPE_INT32,
+                                         .compare_fcn   = _nm_setting_property_compare_fcn_default,
+                                         .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                         .from_dbus_is_full = TRUE);
 
 const NMSettInfoPropertType nm_sett_info_propert_type_plain_u =
-    NM_SETT_INFO_PROPERT_TYPE_GPROP_INIT(G_VARIANT_TYPE_UINT32);
-
-const NMSettInfoPropertType nm_sett_info_propert_type_boolean = NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(
-    G_VARIANT_TYPE_BOOLEAN,
-    .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_get_boolean);
-
-const NMSettInfoPropertType nm_sett_info_propert_type_string =
+    NM_SETT_INFO_PROPERT_TYPE_GPROP_INIT(G_VARIANT_TYPE_UINT32,
+                                         .compare_fcn   = _nm_setting_property_compare_fcn_default,
+                                         .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                         .from_dbus_is_full = TRUE);
+
+const NMSettInfoPropertType nm_sett_info_propert_type_direct_boolean =
+    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_BOOLEAN,
+                                        .direct_type   = NM_VALUE_TYPE_BOOL,
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_direct,
+                                        .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_direct,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
+                                        .from_dbus_is_full                = TRUE,
+                                        .from_dbus_direct_allow_transform = TRUE);
+
+const NMSettInfoPropertType nm_sett_info_propert_type_direct_int32 =
+    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_INT32,
+                                        .direct_type   = NM_VALUE_TYPE_INT32,
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_direct,
+                                        .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_direct,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
+                                        .from_dbus_is_full                = TRUE,
+                                        .from_dbus_direct_allow_transform = TRUE);
+
+const NMSettInfoPropertType nm_sett_info_propert_type_direct_uint32 =
+    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_UINT32,
+                                        .direct_type   = NM_VALUE_TYPE_UINT32,
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_direct,
+                                        .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_direct,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
+                                        .from_dbus_is_full                = TRUE,
+                                        .from_dbus_direct_allow_transform = TRUE);
+
+const NMSettInfoPropertType nm_sett_info_propert_type_direct_uint64 =
+    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_UINT64,
+                                        .direct_type   = NM_VALUE_TYPE_UINT64,
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_direct,
+                                        .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_direct,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
+                                        .from_dbus_is_full                = TRUE,
+                                        .from_dbus_direct_allow_transform = TRUE);
+
+const NMSettInfoPropertType nm_sett_info_propert_type_direct_string =
     NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING,
-                                        .to_dbus_fcn = _nm_setting_property_to_dbus_fcn_get_string);
+                                        .direct_type   = NM_VALUE_TYPE_STRING,
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_direct,
+                                        .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_direct,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
+                                        .from_dbus_is_full                = TRUE,
+                                        .from_dbus_direct_allow_transform = TRUE);
+
+const NMSettInfoPropertType nm_sett_info_propert_type_direct_bytes =
+    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_BYTESTRING,
+                                        .direct_type   = NM_VALUE_TYPE_BYTES,
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_direct,
+                                        .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_direct,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
+                                        .from_dbus_is_full                = TRUE,
+                                        .from_dbus_direct_allow_transform = TRUE);
+
+const NMSettInfoPropertType nm_sett_info_propert_type_direct_enum =
+    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_INT32,
+                                        .direct_type   = NM_VALUE_TYPE_ENUM,
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_direct,
+                                        .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_direct,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
+                                        .from_dbus_is_full                = TRUE,
+                                        .from_dbus_direct_allow_transform = TRUE);
+
+const NMSettInfoPropertType nm_sett_info_propert_type_direct_flags =
+    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_UINT32,
+                                        .direct_type   = NM_VALUE_TYPE_FLAGS,
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_direct,
+                                        .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_direct,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct,
+                                        .from_dbus_is_full                = TRUE,
+                                        .from_dbus_direct_allow_transform = TRUE);
+
+const NMSettInfoPropertType nm_sett_info_propert_type_direct_mac_address =
+    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(
+        G_VARIANT_TYPE_BYTESTRING,
+        .direct_type   = NM_VALUE_TYPE_STRING,
+        .compare_fcn   = _nm_setting_property_compare_fcn_direct,
+        .to_dbus_fcn   = _nm_setting_property_to_dbus_fcn_direct_mac_address,
+        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_direct_mac_address);
 
 /*****************************************************************************/
 
@@ -2501,7 +3591,7 @@ _nm_setting_option_get_all(NMSetting *         setting,
         return len;
 
     if (G_UNLIKELY(!gendata->names)) {
-        gendata->names = nm_utils_strdict_get_keys(hash, TRUE, NULL);
+        gendata->names = nm_strdict_get_keys(hash, TRUE, NULL);
     }
 
     if (out_values) {
@@ -2844,9 +3934,36 @@ nm_setting_init(NMSetting *setting)
 {}
 
 static void
+constructed(GObject *object)
+{
+    NMSetting *     self  = NM_SETTING(object);
+    NMSettingClass *klass = NM_SETTING_GET_CLASS(self);
+
+    /* we don't support that NMSetting subclasses override constructed.
+     * They all must have no G_PARAM_CONSTRUCT/G_PARAM_CONSTRUCT_ONLY
+     * properties, otherwise the automatism of _init_direct() needs
+     * careful adjustment. */
+    nm_assert(G_OBJECT_CLASS(klass)->constructed == constructed);
+
+    /* we always initialize the defaults of the (direct) properties. Note that:
+     *
+     * - we don't use CONSTRUCT properties, because they have an overhead during
+     *   each object creation. Via _init_direct() we can do it more efficiently.
+     *
+     * - we always call this, because we want to get all default values right.
+     *   We even call this for NMSetting subclasses that (historically) are not
+     *   yet aware of this happening.
+     */
+    _init_direct(self);
+
+    G_OBJECT_CLASS(nm_setting_parent_class)->constructed(object);
+}
+
+static void
 finalize(GObject *object)
 {
-    NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE(object);
+    NMSetting *       self = NM_SETTING(object);
+    NMSettingPrivate *priv = NM_SETTING_GET_PRIVATE(self);
 
     if (priv->gendata) {
         g_free(priv->gendata->names);
@@ -2856,6 +3973,8 @@ finalize(GObject *object)
     }
 
     G_OBJECT_CLASS(nm_setting_parent_class)->finalize(object);
+
+    _finalize_direct(self);
 }
 
 static void
@@ -2865,13 +3984,13 @@ nm_setting_class_init(NMSettingClass *setting_class)
 
     g_type_class_add_private(setting_class, sizeof(NMSettingPrivate));
 
+    object_class->constructed  = constructed;
     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             = clear_secrets;
     setting_class->for_each_secret           = for_each_secret;
     setting_class->duplicate_copy_properties = duplicate_copy_properties;
diff --git a/src/libnm-core-impl/nm-simple-connection.c b/src/libnm-core-impl/nm-simple-connection.c
index 6b5b1184..f16d9d59 100644
--- a/src/libnm-core-impl/nm-simple-connection.c
+++ b/src/libnm-core-impl/nm-simple-connection.c
@@ -161,7 +161,7 @@ dispose(GObject *object)
 
 #if NM_MORE_ASSERTS
     g_signal_handlers_disconnect_by_data(object,
-                                         (gpointer) &_nmtst_connection_unchanging_user_data);
+                                         (gpointer) &_nm_assert_connection_unchanging_user_data);
 #endif
 
     nm_connection_clear_secrets(connection);
diff --git a/src/libnm-core-impl/nm-team-utils.c b/src/libnm-core-impl/nm-team-utils.c
index bd9ef510..f67c08be 100644
--- a/src/libnm-core-impl/nm-team-utils.c
+++ b/src/libnm-core-impl/nm-team-utils.c
@@ -523,11 +523,10 @@ _team_attr_data_cmp(const TeamAttrData *attr_data,
     } else if (!is_port && attr_data->team_attr == NM_TEAM_ATTRIBUTE_MASTER_RUNNER_TX_HASH) {
         v_ptrarray_a = *((const GPtrArray *const *) val_a);
         v_ptrarray_b = *((const GPtrArray *const *) val_b);
-        NM_CMP_RETURN(
-            nm_utils_strv_cmp_n(v_ptrarray_a ? (const char *const *) v_ptrarray_a->pdata : NULL,
-                                v_ptrarray_a ? v_ptrarray_a->len : 0u,
-                                v_ptrarray_b ? (const char *const *) v_ptrarray_b->pdata : NULL,
-                                v_ptrarray_b ? v_ptrarray_b->len : 0u));
+        NM_CMP_RETURN(nm_strv_cmp_n(v_ptrarray_a ? (const char *const *) v_ptrarray_a->pdata : NULL,
+                                    v_ptrarray_a ? v_ptrarray_a->len : 0u,
+                                    v_ptrarray_b ? (const char *const *) v_ptrarray_b->pdata : NULL,
+                                    v_ptrarray_b ? v_ptrarray_b->len : 0u));
     } else
         nm_assert_not_reached();
     return 0;
@@ -1136,12 +1135,12 @@ _team_setting_value_master_runner_tx_hash_set_list(NMTeamSetting *    self,
     gboolean                                changed;
     guint                                   i;
 
-    if (nm_utils_strv_cmp_n(self->d.master.runner_tx_hash
-                                ? (const char *const *) self->d.master.runner_tx_hash->pdata
-                                : NULL,
-                            self->d.master.runner_tx_hash ? self->d.master.runner_tx_hash->len : 0u,
-                            arr,
-                            len)
+    if (nm_strv_cmp_n(self->d.master.runner_tx_hash
+                          ? (const char *const *) self->d.master.runner_tx_hash->pdata
+                          : NULL,
+                      self->d.master.runner_tx_hash ? self->d.master.runner_tx_hash->len : 0u,
+                      arr,
+                      len)
         == 0) {
         changed = FALSE;
         goto out;
@@ -2245,8 +2244,7 @@ _team_setting_verify_properties(const NMTeamSetting *self, GError **error)
             } else if (attr_data->value_type == NM_VALUE_TYPE_STRING) {
                 const char *v = *((const char *const *) p_field);
 
-                if (nm_utils_strv_find_first((char **) attr_data->range.r_string.valid_names, -1, v)
-                    < 0) {
+                if (nm_strv_find_first(attr_data->range.r_string.valid_names, -1, v) < 0) {
                     g_set_error(error,
                                 NM_CONNECTION_ERROR,
                                 NM_CONNECTION_ERROR_INVALID_SETTING,
@@ -2265,9 +2263,7 @@ _team_setting_verify_properties(const NMTeamSetting *self, GError **error)
                 for (i = 0; i < self->d.master.runner_tx_hash->len; i++) {
                     const char *val = self->d.master.runner_tx_hash->pdata[i];
 
-                    if (!val
-                        || (nm_utils_strv_find_first((char **) _valid_names_runner_tx_hash, -1, val)
-                            < 0)) {
+                    if (!val || (nm_strv_find_first(_valid_names_runner_tx_hash, -1, val) < 0)) {
                         g_set_error(error,
                                     NM_CONNECTION_ERROR,
                                     NM_CONNECTION_ERROR_INVALID_SETTING,
@@ -2293,8 +2289,7 @@ _team_setting_verify_properties(const NMTeamSetting *self, GError **error)
             if (!_team_setting_has_field(self, attr_data))
                 continue;
             if (self->d.master.runner
-                && (nm_utils_strv_find_first((char **) e->valid_runners, -1, self->d.master.runner)
-                    >= 0))
+                && (nm_strv_find_first(e->valid_runners, -1, self->d.master.runner) >= 0))
                 continue;
             if (e->valid_runners[1] == NULL) {
                 g_set_error(error,
@@ -2729,18 +2724,12 @@ _nm_setting_get_team_setting(struct _NMSetting *setting)
     return _nm_setting_team_port_get_team_setting(NM_SETTING_TEAM_PORT(setting));
 }
 
-static GVariant *
-_nm_team_settings_property_to_dbus(const NMSettInfoSetting *               sett_info,
-                                   guint                                   property_idx,
-                                   NMConnection *                          connection,
-                                   NMSetting *                             setting,
-                                   NMConnectionSerializationFlags          flags,
-                                   const NMConnectionSerializationOptions *options)
+GVariant *
+_nm_team_settings_property_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     NMTeamSetting *     self = _nm_setting_get_team_setting(setting);
     const TeamAttrData *attr_data =
-        _team_attr_data_get(self->d.is_port,
-                            sett_info->property_infos[property_idx].param_spec->param_id);
+        _team_attr_data_get(self->d.is_port, property_info->param_spec->param_id);
 
     if (attr_data->team_attr == NM_TEAM_ATTRIBUTE_CONFIG) {
         const char *config;
@@ -2779,34 +2768,40 @@ _nm_team_settings_property_to_dbus(const NMSettInfoSetting *               sett_
     return NULL;
 }
 
-static void
-_nm_team_settings_property_from_dbus_link_watchers(GVariant *dbus_value, GValue *prop_value)
+void
+_nm_team_settings_property_from_dbus_link_watchers(
+    _NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil)
 {
-    g_value_take_boxed(prop_value,
-                       _nm_utils_team_link_watchers_from_variant(dbus_value, FALSE, NULL));
+    g_value_take_boxed(to, _nm_utils_team_link_watchers_from_variant(from, FALSE, NULL));
 }
 
 const NMSettInfoPropertType nm_sett_info_propert_type_team_b =
     NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_BOOLEAN,
-                                        .to_dbus_fcn = _nm_team_settings_property_to_dbus, );
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_default,
+                                        .to_dbus_fcn   = _nm_team_settings_property_to_dbus,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                        .from_dbus_is_full = TRUE);
 
 const NMSettInfoPropertType nm_sett_info_propert_type_team_i =
     NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_INT32,
-                                        .to_dbus_fcn = _nm_team_settings_property_to_dbus, );
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_default,
+                                        .to_dbus_fcn   = _nm_team_settings_property_to_dbus,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                        .from_dbus_is_full = TRUE);
 
 const NMSettInfoPropertType nm_sett_info_propert_type_team_s =
     NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING,
-                                        .to_dbus_fcn = _nm_team_settings_property_to_dbus, );
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_default,
+                                        .to_dbus_fcn   = _nm_team_settings_property_to_dbus,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                        .from_dbus_is_full = TRUE);
 
 const NMSettInfoPropertType nm_sett_info_propert_type_team_as =
     NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(NM_G_VARIANT_TYPE("as"),
-                                        .to_dbus_fcn = _nm_team_settings_property_to_dbus, );
-
-const NMSettInfoPropertType nm_sett_info_propert_type_team_link_watchers =
-    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(NM_G_VARIANT_TYPE("aa{sv}"),
-                                        .to_dbus_fcn = _nm_team_settings_property_to_dbus,
-                                        .gprop_from_dbus_fcn =
-                                            _nm_team_settings_property_from_dbus_link_watchers, );
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_default,
+                                        .to_dbus_fcn   = _nm_team_settings_property_to_dbus,
+                                        .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                        .from_dbus_is_full = TRUE);
 
 /*****************************************************************************/
 
diff --git a/src/libnm-core-impl/nm-team-utils.h b/src/libnm-core-impl/nm-team-utils.h
index 2039c16e..d888e191 100644
--- a/src/libnm-core-impl/nm-team-utils.h
+++ b/src/libnm-core-impl/nm-team-utils.h
@@ -7,7 +7,7 @@
 #define __NM_TEAM_UITLS_H__
 
 #if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-    #error Cannot use this header.
+#error Cannot use this header.
 #endif
 
 #include "libnm-glib-aux/nm-value-type.h"
@@ -266,6 +266,5 @@ extern const NMSettInfoPropertType nm_sett_info_propert_type_team_b;
 extern const NMSettInfoPropertType nm_sett_info_propert_type_team_i;
 extern const NMSettInfoPropertType nm_sett_info_propert_type_team_s;
 extern const NMSettInfoPropertType nm_sett_info_propert_type_team_as;
-extern const NMSettInfoPropertType nm_sett_info_propert_type_team_link_watchers;
 
 #endif /* __NM_TEAM_UITLS_H__ */
diff --git a/src/libnm-core-impl/nm-utils-private.h b/src/libnm-core-impl/nm-utils-private.h
index eb545d0f..679fd6b4 100644
--- a/src/libnm-core-impl/nm-utils-private.h
+++ b/src/libnm-core-impl/nm-utils-private.h
@@ -7,7 +7,7 @@
 #define __NM_UTILS_PRIVATE_H__
 
 #if !((NETWORKMANAGER_COMPILATION) &NM_NETWORKMANAGER_COMPILATION_WITH_LIBNM_CORE_PRIVATE)
-    #error Cannot use this header.
+#error Cannot use this header.
 #endif
 
 #include "nm-setting-private.h"
@@ -32,22 +32,20 @@ gboolean _nm_utils_wps_method_validate(NMSettingWirelessSecurityWpsMethod wps_me
 
 /* D-Bus transform funcs */
 
-extern const NMSettInfoPropertType nm_sett_info_propert_type_strdict;
-
-extern const NMSettInfoPropertType nm_sett_info_propert_type_mac_address;
+gboolean _nm_property_variant_to_gvalue(GVariant *src_value, GValue *dst_value);
 
-extern const NMSettInfoPropertType nm_sett_info_propert_type_cloned_mac_address;
+extern const NMSettInfoPropertType nm_sett_info_propert_type_strdict;
 
 extern const NMSettInfoPropertType nm_sett_info_propert_type_assigned_mac_address;
 
-extern const NMSettInfoPropertType nm_sett_info_propert_type_bridge_vlans;
-
-void _nm_utils_strdict_from_dbus(GVariant *dbus_value, GValue *prop_value);
+void _nm_utils_strdict_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil);
 
 void _nm_utils_bytes_from_dbus(GVariant *dbus_value, GValue *prop_value);
 
 char *_nm_utils_hwaddr_canonical_or_invalid(const char *mac, gssize length);
 
+char *_nm_utils_ipaddr_canonical_or_invalid(int addr_family, const char *ip);
+
 gboolean _nm_utils_hwaddr_link_local_valid(const char *mac);
 
 gboolean _nm_sriov_vf_parse_vlans(NMSriovVF *vf, const char *str, GError **error);
@@ -58,4 +56,14 @@ gboolean _nm_utils_bridge_vlan_verify_list(GPtrArray * vlans,
                                            const char *setting,
                                            const char *property);
 
+NMTernary _nm_utils_bridge_compare_vlans(GPtrArray *vlans_a, GPtrArray *vlans_b);
+
+GVariant *_nm_team_settings_property_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil);
+
+void _nm_team_settings_property_from_dbus_link_watchers(
+    _NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil);
+
+GVariant *_nm_utils_ip4_dns_to_variant(const char *const *dns, gssize len);
+GVariant *_nm_utils_ip6_dns_to_variant(const char *const *dns, gssize len);
+
 #endif
diff --git a/src/libnm-core-impl/nm-utils.c b/src/libnm-core-impl/nm-utils.c
index 12e3bd40..ccdd4b92 100644
--- a/src/libnm-core-impl/nm-utils.c
+++ b/src/libnm-core-impl/nm-utils.c
@@ -29,6 +29,7 @@
 #include "nm-setting-private.h"
 #include "nm-crypto.h"
 #include "nm-setting-bond.h"
+#include "nm-setting-bond-port.h"
 #include "nm-setting-bridge.h"
 #include "nm-setting-bridge-port.h"
 #include "nm-setting-infiniband.h"
@@ -759,25 +760,28 @@ _nm_utils_hash_values_to_slist(GHashTable *hash)
 }
 
 void
-_nm_utils_strdict_from_dbus(GVariant *dbus_value, GValue *prop_value)
+_nm_utils_strdict_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_GPROP_FCN_ARGS _nm_nil)
 {
     GVariantIter iter;
     const char * key, *value;
     GHashTable * hash;
 
     hash = g_hash_table_new_full(nm_str_hash, g_str_equal, g_free, g_free);
-    g_variant_iter_init(&iter, dbus_value);
+    g_variant_iter_init(&iter, from);
     while (g_variant_iter_next(&iter, "{&s&s}", &key, &value))
         g_hash_table_insert(hash, g_strdup(key), g_strdup(value));
 
-    g_value_take_boxed(prop_value, hash);
+    g_value_take_boxed(to, hash);
 }
 
 const NMSettInfoPropertType nm_sett_info_propert_type_strdict =
     NM_SETT_INFO_PROPERT_TYPE_GPROP_INIT(NM_G_VARIANT_TYPE("a{ss}"),
-                                         .gprop_from_dbus_fcn = _nm_utils_strdict_from_dbus,
+                                         .typdata_from_dbus.gprop_fcn = _nm_utils_strdict_from_dbus,
                                          .typdata_to_dbus.gprop_type =
-                                             NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT);
+                                             NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT,
+                                         .compare_fcn   = _nm_setting_property_compare_fcn_default,
+                                         .from_dbus_fcn = _nm_setting_property_from_dbus_fcn_gprop,
+                                         .from_dbus_is_full = TRUE);
 
 GHashTable *
 _nm_utils_copy_strdict(GHashTable *strdict)
@@ -835,7 +839,7 @@ _nm_utils_bytes_from_dbus(GVariant *dbus_value, GValue *prop_value)
 /*****************************************************************************/
 
 GSList *
-_nm_utils_strv_to_slist(char **strv, gboolean deep_copy)
+nm_strv_to_gslist(char **strv, gboolean deep_copy)
 {
     GSList *list = NULL;
     gsize   i;
@@ -884,7 +888,7 @@ _nm_utils_slist_to_strv(const GSList *slist, gboolean deep_copy)
 }
 
 GPtrArray *
-_nm_utils_strv_to_ptrarray(char **strv)
+nm_strv_to_ptrarray(char **strv)
 {
     GPtrArray *ptrarray;
     gsize      i, l;
@@ -1283,18 +1287,28 @@ nm_utils_wpa_psk_valid(const char *psk)
 GVariant *
 nm_utils_ip4_dns_to_variant(char **dns)
 {
+    return _nm_utils_ip4_dns_to_variant(NM_CAST_STRV_CC(dns), -1);
+}
+
+GVariant *
+_nm_utils_ip4_dns_to_variant(const char *const *dns, gssize len)
+{
     GVariantBuilder builder;
+    gsize           l;
     gsize           i;
 
+    if (len < 0)
+        l = NM_PTRARRAY_LEN(dns);
+    else
+        l = len;
+
     g_variant_builder_init(&builder, G_VARIANT_TYPE("au"));
 
-    if (dns) {
-        for (i = 0; dns[i]; i++) {
-            guint32 ip = 0;
+    for (i = 0; i < l; i++) {
+        in_addr_t ip;
 
-            inet_pton(AF_INET, dns[i], &ip);
+        if (inet_pton(AF_INET, dns[i], &ip) == 1)
             g_variant_builder_add(&builder, "u", ip);
-        }
     }
 
     return g_variant_builder_end(&builder);
@@ -1592,18 +1606,28 @@ nm_utils_ip4_get_default_prefix(guint32 ip)
 GVariant *
 nm_utils_ip6_dns_to_variant(char **dns)
 {
+    return _nm_utils_ip6_dns_to_variant(NM_CAST_STRV_CC(dns), -1);
+}
+
+GVariant *
+_nm_utils_ip6_dns_to_variant(const char *const *dns, gssize len)
+{
     GVariantBuilder builder;
     gsize           i;
+    gsize           l;
+
+    if (len < 0)
+        l = NM_PTRARRAY_LEN(dns);
+    else
+        l = len;
 
     g_variant_builder_init(&builder, G_VARIANT_TYPE("aay"));
-    if (dns) {
-        for (i = 0; dns[i]; i++) {
-            struct in6_addr ip;
+    for (i = 0; i < l; i++) {
+        struct in6_addr ip;
 
-            if (inet_pton(AF_INET6, dns[i], &ip) != 1)
-                continue;
-            g_variant_builder_add(&builder, "@ay", nm_g_variant_new_ay_in6addr(&ip));
-        }
+        if (inet_pton(AF_INET6, dns[i], &ip) != 1)
+            continue;
+        g_variant_builder_add(&builder, "@ay", nm_g_variant_new_ay_in6addr(&ip));
     }
     return g_variant_builder_end(&builder);
 }
@@ -2859,7 +2883,7 @@ _nm_sriov_vf_parse_vlans(NMSriovVF *vf, const char *str, GError **error)
     gs_free const char **vlans = NULL;
     guint                i;
 
-    vlans = nm_utils_strsplit_set(str, ";");
+    vlans = nm_strsplit_set(str, ";");
     if (!vlans) {
         g_set_error_literal(error,
                             NM_CONNECTION_ERROR,
@@ -3625,6 +3649,23 @@ nm_utils_wifi_strength_bars(guint8 strength)
         return "    ";
 }
 
+gboolean
+_nm_property_variant_to_gvalue(GVariant *src_value, GValue *dst_value)
+{
+    GValue   tmp = G_VALUE_INIT;
+    gboolean success;
+
+    g_dbus_gvariant_to_gvalue(src_value, &tmp);
+    if (G_VALUE_TYPE(&tmp) == G_VALUE_TYPE(dst_value)) {
+        *dst_value = tmp;
+        return TRUE;
+    }
+
+    success = g_value_transform(&tmp, dst_value);
+    g_value_unset(&tmp);
+    return success;
+}
+
 /**
  * nm_utils_hwaddr_len:
  * @type: the type of address; either <literal>ARPHRD_ETHER</literal> or
@@ -3857,6 +3898,25 @@ _nm_utils_hwaddr_canonical_or_invalid(const char *mac, gssize length)
         return g_strdup(mac);
 }
 
+char *
+_nm_utils_ipaddr_canonical_or_invalid(int addr_family, const char *ip)
+{
+    NMIPAddr addr_bin;
+
+    nm_assert_addr_family(addr_family);
+
+    if (!ip)
+        return NULL;
+
+    if (!nm_utils_parse_inaddr_bin(addr_family, ip, NULL, &addr_bin))
+        return g_strdup(ip);
+
+    if (nm_ip_addr_is_null(addr_family, &addr_bin))
+        return NULL;
+
+    return nm_utils_inet_ntop_dup(addr_family, &addr_bin);
+}
+
 /*
  * Determine if given Ethernet address is link-local
  *
@@ -4002,47 +4062,41 @@ nm_utils_hwaddr_to_dbus(const char *str)
     return nm_g_variant_new_ay(buf, len);
 }
 
-static GVariant *
-_nm_utils_hwaddr_cloned_get(const NMSettInfoSetting *               sett_info,
-                            guint                                   property_idx,
-                            NMConnection *                          connection,
-                            NMSetting *                             setting,
-                            NMConnectionSerializationFlags          flags,
-                            const NMConnectionSerializationOptions *options)
+GVariant *
+_nm_utils_hwaddr_cloned_get(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_free char *addr = NULL;
 
-    nm_assert(nm_streq(sett_info->property_infos[property_idx].name, "cloned-mac-address"));
+    nm_assert(nm_streq(property_info->name, "cloned-mac-address"));
 
     g_object_get(setting, "cloned-mac-address", &addr, NULL);
     return nm_utils_hwaddr_to_dbus(addr);
 }
 
-static gboolean
-_nm_utils_hwaddr_cloned_set(NMSetting *         setting,
-                            GVariant *          connection_dict,
-                            const char *        property,
-                            GVariant *          value,
-                            NMSettingParseFlags parse_flags,
-                            GError **           error)
+gboolean
+_nm_utils_hwaddr_cloned_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     gsize         length;
     const guint8 *array;
     char *        str;
 
-    nm_assert(nm_streq0(property, "cloned-mac-address"));
+    nm_assert(nm_streq0(property_info->name, "cloned-mac-address"));
 
     if (!_nm_setting_use_legacy_property(setting,
                                          connection_dict,
                                          "cloned-mac-address",
-                                         "assigned-mac-address"))
+                                         "assigned-mac-address")) {
+        *out_is_modified = FALSE;
         return TRUE;
+    }
 
     length = 0;
     array  = g_variant_get_fixed_array(value, &length, 1);
 
-    if (!length)
+    if (!length) {
+        *out_is_modified = FALSE;
         return TRUE;
+    }
 
     str = nm_utils_hwaddr_ntoa(array, length);
     g_object_set(setting, "cloned-mac-address", str, NULL);
@@ -4050,37 +4104,22 @@ _nm_utils_hwaddr_cloned_set(NMSetting *         setting,
     return TRUE;
 }
 
-static gboolean
-_nm_utils_hwaddr_cloned_not_set(NMSetting *         setting,
-                                GVariant *          connection_dict,
-                                const char *        property,
-                                NMSettingParseFlags parse_flags,
-                                GError **           error)
+gboolean
+_nm_utils_hwaddr_cloned_not_set(_NM_SETT_INFO_PROP_MISSING_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     nm_assert(nm_streq0(property, "cloned-mac-address"));
     return TRUE;
 }
 
-const NMSettInfoPropertType nm_sett_info_propert_type_cloned_mac_address =
-    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_BYTESTRING,
-                                        .to_dbus_fcn           = _nm_utils_hwaddr_cloned_get,
-                                        .from_dbus_fcn         = _nm_utils_hwaddr_cloned_set,
-                                        .missing_from_dbus_fcn = _nm_utils_hwaddr_cloned_not_set, );
-
 static GVariant *
-_nm_utils_hwaddr_cloned_data_synth(const NMSettInfoSetting *               sett_info,
-                                   guint                                   property_idx,
-                                   NMConnection *                          connection,
-                                   NMSetting *                             setting,
-                                   NMConnectionSerializationFlags          flags,
-                                   const NMConnectionSerializationOptions *options)
+_nm_utils_hwaddr_cloned_data_synth(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_free char *addr = NULL;
 
     if (!_nm_connection_serialize_non_secret(flags))
         return NULL;
 
-    nm_assert(nm_streq0(sett_info->property_infos[property_idx].name, "assigned-mac-address"));
+    nm_assert(nm_streq0(property_info->name, "assigned-mac-address"));
 
     g_object_get(setting, "cloned-mac-address", &addr, NULL);
 
@@ -4102,20 +4141,17 @@ _nm_utils_hwaddr_cloned_data_synth(const NMSettInfoSetting *               sett_
 }
 
 static gboolean
-_nm_utils_hwaddr_cloned_data_set(NMSetting *         setting,
-                                 GVariant *          connection_dict,
-                                 const char *        property,
-                                 GVariant *          value,
-                                 NMSettingParseFlags parse_flags,
-                                 GError **           error)
+_nm_utils_hwaddr_cloned_data_set(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
-    nm_assert(nm_streq0(property, "assigned-mac-address"));
+    nm_assert(nm_streq0(property_info->name, "assigned-mac-address"));
 
     if (_nm_setting_use_legacy_property(setting,
                                         connection_dict,
                                         "cloned-mac-address",
-                                        "assigned-mac-address"))
+                                        "assigned-mac-address")) {
+        *out_is_modified = FALSE;
         return TRUE;
+    }
 
     g_object_set(setting,
                  "cloned-mac-address",
@@ -4126,26 +4162,10 @@ _nm_utils_hwaddr_cloned_data_set(NMSetting *         setting,
 
 const NMSettInfoPropertType nm_sett_info_propert_type_assigned_mac_address =
     NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(G_VARIANT_TYPE_STRING,
+                                        .compare_fcn   = _nm_setting_property_compare_fcn_ignore,
                                         .to_dbus_fcn   = _nm_utils_hwaddr_cloned_data_synth,
                                         .from_dbus_fcn = _nm_utils_hwaddr_cloned_data_set, );
 
-static void
-_nm_utils_hwaddr_from_dbus(GVariant *dbus_value, GValue *prop_value)
-{
-    gsize         length = 0;
-    const guint8 *array  = g_variant_get_fixed_array(dbus_value, &length, 1);
-    char *        str;
-
-    str = length ? nm_utils_hwaddr_ntoa(array, length) : NULL;
-    g_value_take_string(prop_value, str);
-}
-
-const NMSettInfoPropertType nm_sett_info_propert_type_mac_address =
-    NM_SETT_INFO_PROPERT_TYPE_GPROP_INIT(
-        G_VARIANT_TYPE_BYTESTRING,
-        .gprop_from_dbus_fcn        = _nm_utils_hwaddr_from_dbus,
-        .typdata_to_dbus.gprop_type = NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_MAC_ADDRESS);
-
 /*****************************************************************************/
 
 /* Validate secret-flags. Most settings don't validate them, which is a bug.
@@ -4583,24 +4603,41 @@ nm_utils_check_virtual_device_compatibility(GType virtual_type, GType other_type
                          FALSE);
 
     if (virtual_type == NM_TYPE_SETTING_BOND) {
-        return (other_type == NM_TYPE_SETTING_INFINIBAND || other_type == NM_TYPE_SETTING_WIRED
-                || other_type == NM_TYPE_SETTING_BRIDGE || other_type == NM_TYPE_SETTING_BOND
-                || other_type == NM_TYPE_SETTING_TEAM || other_type == NM_TYPE_SETTING_VLAN);
-    } else if (virtual_type == NM_TYPE_SETTING_BRIDGE) {
-        return (other_type == NM_TYPE_SETTING_WIRED || other_type == NM_TYPE_SETTING_BOND
-                || other_type == NM_TYPE_SETTING_TEAM || other_type == NM_TYPE_SETTING_VLAN);
-    } else if (virtual_type == NM_TYPE_SETTING_TEAM) {
-        return (other_type == NM_TYPE_SETTING_WIRED || other_type == NM_TYPE_SETTING_BRIDGE
-                || other_type == NM_TYPE_SETTING_BOND || other_type == NM_TYPE_SETTING_TEAM
-                || other_type == NM_TYPE_SETTING_VLAN);
-    } else if (virtual_type == NM_TYPE_SETTING_VLAN) {
-        return (other_type == NM_TYPE_SETTING_WIRED || other_type == NM_TYPE_SETTING_WIRELESS
-                || other_type == NM_TYPE_SETTING_BRIDGE || other_type == NM_TYPE_SETTING_BOND
-                || other_type == NM_TYPE_SETTING_TEAM || other_type == NM_TYPE_SETTING_VLAN);
-    } else {
-        g_warn_if_reached();
-        return FALSE;
+        return NM_IN_SET(other_type,
+                         NM_TYPE_SETTING_BOND,
+                         NM_TYPE_SETTING_BRIDGE,
+                         NM_TYPE_SETTING_INFINIBAND,
+                         NM_TYPE_SETTING_TEAM,
+                         NM_TYPE_SETTING_VLAN,
+                         NM_TYPE_SETTING_WIRED,
+                         NM_TYPE_SETTING_WIRELESS);
+    }
+    if (virtual_type == NM_TYPE_SETTING_BRIDGE) {
+        return NM_IN_SET(other_type,
+                         NM_TYPE_SETTING_BOND,
+                         NM_TYPE_SETTING_TEAM,
+                         NM_TYPE_SETTING_VLAN,
+                         NM_TYPE_SETTING_WIRED);
+    }
+    if (virtual_type == NM_TYPE_SETTING_TEAM) {
+        return NM_IN_SET(other_type,
+                         NM_TYPE_SETTING_BOND,
+                         NM_TYPE_SETTING_BRIDGE,
+                         NM_TYPE_SETTING_TEAM,
+                         NM_TYPE_SETTING_VLAN,
+                         NM_TYPE_SETTING_WIRED);
+    }
+    if (virtual_type == NM_TYPE_SETTING_VLAN) {
+        return NM_IN_SET(other_type,
+                         NM_TYPE_SETTING_BOND,
+                         NM_TYPE_SETTING_BRIDGE,
+                         NM_TYPE_SETTING_TEAM,
+                         NM_TYPE_SETTING_VLAN,
+                         NM_TYPE_SETTING_WIRED,
+                         NM_TYPE_SETTING_WIRELESS);
     }
+
+    return FALSE;
 }
 
 /*****************************************************************************/
@@ -5445,18 +5482,13 @@ nm_utils_base64secret_normalize(const char *base64_key,
     return TRUE;
 }
 
-static GVariant *
-_nm_utils_bridge_vlans_to_dbus(const NMSettInfoSetting *               sett_info,
-                               guint                                   property_idx,
-                               NMConnection *                          connection,
-                               NMSetting *                             setting,
-                               NMConnectionSerializationFlags          flags,
-                               const NMConnectionSerializationOptions *options)
+GVariant *
+_nm_utils_bridge_vlans_to_dbus(_NM_SETT_INFO_PROP_TO_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *vlans = NULL;
     GVariantBuilder              builder;
     guint                        i;
-    const char *                 property_name = sett_info->property_infos[property_idx].name;
+    const char *                 property_name = property_info->name;
 
     nm_assert(property_name);
 
@@ -5492,13 +5524,8 @@ _nm_utils_bridge_vlans_to_dbus(const NMSettInfoSetting *               sett_info
     return g_variant_builder_end(&builder);
 }
 
-static gboolean
-_nm_utils_bridge_vlans_from_dbus(NMSetting *         setting,
-                                 GVariant *          connection_dict,
-                                 const char *        property,
-                                 GVariant *          value,
-                                 NMSettingParseFlags parse_flags,
-                                 GError **           error)
+gboolean
+_nm_utils_bridge_vlans_from_dbus(_NM_SETT_INFO_PROP_FROM_DBUS_FCN_ARGS _nm_nil)
 {
     gs_unref_ptrarray GPtrArray *vlans = NULL;
     GVariantIter                 vlan_iter;
@@ -5539,15 +5566,25 @@ _nm_utils_bridge_vlans_from_dbus(NMSetting *         setting,
         g_ptr_array_add(vlans, vlan);
     }
 
-    g_object_set(setting, property, vlans, NULL);
+    g_object_set(setting, property_info->name, vlans, NULL);
 
     return TRUE;
 }
 
-const NMSettInfoPropertType nm_sett_info_propert_type_bridge_vlans =
-    NM_SETT_INFO_PROPERT_TYPE_DBUS_INIT(NM_G_VARIANT_TYPE("aa{sv}"),
-                                        .to_dbus_fcn   = _nm_utils_bridge_vlans_to_dbus,
-                                        .from_dbus_fcn = _nm_utils_bridge_vlans_from_dbus, );
+NMTernary
+_nm_utils_bridge_compare_vlans(GPtrArray *vlans_a, GPtrArray *vlans_b)
+{
+    guint l = nm_g_ptr_array_len(vlans_a);
+    guint i;
+
+    if (l != nm_g_ptr_array_len(vlans_b))
+        return FALSE;
+    for (i = 0; i < l; i++) {
+        if (nm_bridge_vlan_cmp(vlans_a->pdata[i], vlans_b->pdata[i]))
+            return FALSE;
+    }
+    return TRUE;
+}
 
 gboolean
 _nm_utils_bridge_vlan_verify_list(GPtrArray * vlans,
diff --git a/src/libnm-core-impl/nm-vpn-plugin-info.c b/src/libnm-core-impl/nm-vpn-plugin-info.c
index dc77dfd0..2ffb7774 100644
--- a/src/libnm-core-impl/nm-vpn-plugin-info.c
+++ b/src/libnm-core-impl/nm-vpn-plugin-info.c
@@ -337,7 +337,7 @@ nm_vpn_plugin_info_list_load()
     uid = getuid();
 
     for (i = 0; i < G_N_ELEMENTS(dir); i++) {
-        if (!dir[i] || nm_utils_strv_find_first((char **) dir, i, dir[i]) >= 0)
+        if (!dir[i] || nm_strv_find_first(dir, i, dir[i]) >= 0)
             continue;
 
         infos = _nm_vpn_plugin_info_list_load_dir(dir[i], TRUE, uid, NULL, NULL);
@@ -552,7 +552,7 @@ _list_find_by_service(GSList *list, const char *name, const char *service)
         if (name && !nm_streq(name, priv->name))
             continue;
         if (service && !nm_streq(priv->service, service)
-            && (nm_utils_strv_find_first(priv->aliases, -1, service) < 0))
+            && (nm_strv_find_first(priv->aliases, -1, service) < 0))
             continue;
 
         return list->data;
@@ -639,7 +639,7 @@ nm_vpn_plugin_info_list_find_service_type(GSList *list, const char *name)
 
     /* check the hard-coded list of short-names. They all have the same
      * well-known prefix org.freedesktop.NetworkManager and the name. */
-    if (nm_utils_strv_find_first((char **) known_names, G_N_ELEMENTS(known_names), name) >= 0)
+    if (nm_strv_find_first(known_names, G_N_ELEMENTS(known_names), name) >= 0)
         return g_strdup_printf("%s.%s", NM_DBUS_INTERFACE, name);
 
     /* try, if there exists a plugin with @name under org.freedesktop.NetworkManager.
@@ -1277,7 +1277,7 @@ finalize(GObject *object)
     g_free(priv->auth_dialog);
     g_strfreev(priv->aliases);
     g_free(priv->filename);
-    g_hash_table_unref(priv->keys);
+    nm_g_hash_table_unref(priv->keys);
 
     nm_clear_pointer(&priv->keyfile, g_key_file_unref);
 
diff --git a/src/libnm-core-impl/tests/test-crypto.c b/src/libnm-core-impl/tests/test-crypto.c
index bdbcd0f3..1c072f71 100644
--- a/src/libnm-core-impl/tests/test-crypto.c
+++ b/src/libnm-core-impl/tests/test-crypto.c
@@ -135,7 +135,7 @@ test_load_private_key(const char *path,
         /* Compare the crypto decrypted key against a known-good decryption */
         if (!g_file_get_contents(decrypted_path, &contents, &length, NULL))
             g_assert_not_reached();
-        g_assert(nm_utils_gbytes_equal_mem(array, contents, length));
+        g_assert(nm_g_bytes_equal_mem(array, contents, length));
     }
 }
 
diff --git a/src/libnm-core-impl/tests/test-general.c b/src/libnm-core-impl/tests/test-general.c
index 57864948..5c831cb7 100644
--- a/src/libnm-core-impl/tests/test-general.c
+++ b/src/libnm-core-impl/tests/test-general.c
@@ -478,6 +478,38 @@ test_nm_hash(void)
     g_assert_cmpint(NM_HASH_COMBINE_BOOLS(guint16, 0, 0, 1, 1, 0, 0, 0, 1), ==, 0x031);
     g_assert_cmpint(NM_HASH_COMBINE_BOOLS(guint16, 0, 0, 0, 1, 1, 0, 0, 0, 1), ==, 0x031);
     g_assert_cmpint(NM_HASH_COMBINE_BOOLS(guint16, 1, 0, 0, 1, 1, 0, 0, 0, 1), ==, 0x131);
+    g_assert_cmpint(NM_HASH_COMBINE_BOOLS(guint16, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1),
+                    ==,
+                    0x131);
+    g_assert_cmpint(NM_HASH_COMBINE_BOOLS(guint16, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1),
+                    ==,
+                    0x8131);
+    g_assert_cmpint(
+        NM_HASH_COMBINE_BOOLS(guint32, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1),
+        ==,
+        0x8131);
+    g_assert_cmpint(
+        NM_HASH_COMBINE_BOOLS(guint32, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1),
+        ==,
+        0x28131);
+
+#if _NM_CC_SUPPORT_AUTO_TYPE
+    {
+        _nm_auto_type x = NM_HASH_COMBINE_BOOLS(guint8, 0, 0, 1, 1, 0, 0, 0, 1);
+
+        G_STATIC_ASSERT(sizeof(x) == 1);
+        g_assert(((typeof(x)) -1) > 0);
+    }
+
+    {
+        _nm_auto_type x = NM_HASH_COMBINE_BOOLS(guint16, 0, 0, 1, 1, 0, 0, 0, 1);
+
+        G_STATIC_ASSERT(sizeof(x) == 2);
+        g_assert(((typeof(x)) -1) > 0);
+    }
+#endif
+
+    NM_STATIC_ASSERT_EXPR_VOID(NM_HASH_COMBINE_BOOLS(int, 1, 0, 1) == 5);
 }
 
 /*****************************************************************************/
@@ -510,27 +542,25 @@ test_nm_g_slice_free_fcn(void)
 /*****************************************************************************/
 
 static void
-_do_test_nm_utils_strsplit_set_f_one(NMUtilsStrsplitSetFlags flags,
-                                     const char *            str,
-                                     gsize                   words_len,
-                                     const char *const *     exp_words)
+_do_test_nm_strsplit_set_f_one(NMUtilsStrsplitSetFlags flags,
+                               const char *            str,
+                               gsize                   words_len,
+                               const char *const *     exp_words)
 {
 #define DELIMITERS   " \n"
 #define DELIMITERS_C ' ', '\n'
 
     gs_free const char **words = NULL;
     gsize                i, j, k;
-    const gboolean       f_allow_escaping =
-        NM_FLAGS_HAS(flags, NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING);
-    const gboolean f_preserve_empty =
-        NM_FLAGS_HAS(flags, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY);
+    const gboolean     f_allow_escaping = NM_FLAGS_HAS(flags, NM_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING);
+    const gboolean     f_preserve_empty = NM_FLAGS_HAS(flags, NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY);
     const char *       s1;
     gsize              initial_offset;
     gs_strfreev char **words_g = NULL;
 
-    g_assert(!NM_FLAGS_ANY(flags,
-                           ~(NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING
-                             | NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY)));
+    g_assert(!NM_FLAGS_ANY(
+        flags,
+        ~(NM_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING | NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY)));
 
     /* assert that the expected words are valid (and don't contain unescaped delimiters). */
     for (i = 0; i < words_len; i++) {
@@ -587,16 +617,16 @@ _do_test_nm_utils_strsplit_set_f_one(NMUtilsStrsplitSetFlags flags,
             }
             g_assert(words_g[words_len] == NULL);
             g_assert_cmpint(NM_PTRARRAY_LEN(words_g), ==, words_len);
-            g_assert(nm_utils_strv_cmp_n(exp_words, words_len, words_g, -1) == 0);
+            g_assert(nm_strv_cmp_n(exp_words, words_len, words_g, -1) == 0);
         }
     }
 
-    if (flags == NM_UTILS_STRSPLIT_SET_FLAGS_NONE && nmtst_get_rand_bool())
-        words = nm_utils_strsplit_set(str, DELIMITERS);
-    else if (flags == NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY && nmtst_get_rand_bool())
-        words = nm_utils_strsplit_set_with_empty(str, DELIMITERS);
+    if (flags == NM_STRSPLIT_SET_FLAGS_NONE && nmtst_get_rand_bool())
+        words = nm_strsplit_set(str, DELIMITERS);
+    else if (flags == NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY && nmtst_get_rand_bool())
+        words = nm_strsplit_set_with_empty(str, DELIMITERS);
     else
-        words = nm_utils_strsplit_set_full(str, DELIMITERS, flags);
+        words = nm_strsplit_set_full(str, DELIMITERS, flags);
 
     g_assert_cmpint(NM_PTRARRAY_LEN(words), ==, words_len);
 
@@ -611,7 +641,7 @@ _do_test_nm_utils_strsplit_set_f_one(NMUtilsStrsplitSetFlags flags,
         g_assert_cmpstr(exp_words[i], ==, words[i]);
     g_assert(words[words_len] == NULL);
 
-    g_assert(nm_utils_strv_cmp_n(exp_words, words_len, words, -1) == 0);
+    g_assert(nm_strv_cmp_n(exp_words, words_len, words, -1) == 0);
 
     s1 = words[0];
     g_assert(s1 >= (char *) &words[words_len + 1]);
@@ -719,14 +749,14 @@ _do_test_nm_utils_strsplit_set_f_one(NMUtilsStrsplitSetFlags flags,
 }
 
 static void
-_do_test_nm_utils_strsplit_set_f(NMUtilsStrsplitSetFlags flags,
-                                 const char *            str,
-                                 gsize                   words_len,
-                                 const char *const *     exp_words)
+_do_test_nm_strsplit_set_f(NMUtilsStrsplitSetFlags flags,
+                           const char *            str,
+                           gsize                   words_len,
+                           const char *const *     exp_words)
 {
-    _do_test_nm_utils_strsplit_set_f_one(flags, str, words_len, exp_words);
+    _do_test_nm_strsplit_set_f_one(flags, str, words_len, exp_words);
 
-    if (NM_FLAGS_HAS(flags, NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY)) {
+    if (NM_FLAGS_HAS(flags, NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY)) {
         gs_unref_ptrarray GPtrArray *exp_words2 = NULL;
         gsize                        k;
 
@@ -736,32 +766,32 @@ _do_test_nm_utils_strsplit_set_f(NMUtilsStrsplitSetFlags flags,
                 g_ptr_array_add(exp_words2, (gpointer) exp_words[k]);
         }
 
-        _do_test_nm_utils_strsplit_set_f_one(flags & (~NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY),
-                                             str,
-                                             exp_words2->len,
-                                             (const char *const *) exp_words2->pdata);
+        _do_test_nm_strsplit_set_f_one(flags & (~NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY),
+                                       str,
+                                       exp_words2->len,
+                                       (const char *const *) exp_words2->pdata);
     }
 }
 
-#define do_test_nm_utils_strsplit_set_f(flags, str, ...) \
-    _do_test_nm_utils_strsplit_set_f(flags, str, NM_NARG(__VA_ARGS__), NM_MAKE_STRV(__VA_ARGS__))
+#define do_test_nm_strsplit_set_f(flags, str, ...) \
+    _do_test_nm_strsplit_set_f(flags, str, NM_NARG(__VA_ARGS__), NM_MAKE_STRV(__VA_ARGS__))
 
-#define do_test_nm_utils_strsplit_set(allow_escaping, str, ...)                                   \
-    do_test_nm_utils_strsplit_set_f((allow_escaping) ? NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING \
-                                                     : NM_UTILS_STRSPLIT_SET_FLAGS_NONE,          \
-                                    str,                                                          \
-                                    ##__VA_ARGS__)
+#define do_test_nm_strsplit_set(allow_escaping, str, ...)                             \
+    do_test_nm_strsplit_set_f((allow_escaping) ? NM_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING \
+                                               : NM_STRSPLIT_SET_FLAGS_NONE,          \
+                              str,                                                    \
+                              ##__VA_ARGS__)
 
 static void
-_do_test_nm_utils_strsplit_set_simple(NMUtilsStrsplitSetFlags flags,
-                                      const char *            str,
-                                      gsize                   words_len,
-                                      const char *const *     exp_words)
+_do_test_nm_strsplit_set_simple(NMUtilsStrsplitSetFlags flags,
+                                const char *            str,
+                                gsize                   words_len,
+                                const char *const *     exp_words)
 {
     gs_free const char **tokens = NULL;
     gsize                n_tokens;
 
-    tokens = nm_utils_strsplit_set_full(str, DELIMITERS, flags);
+    tokens = nm_strsplit_set_full(str, DELIMITERS, flags);
 
     if (!tokens) {
         g_assert_cmpint(words_len, ==, 0);
@@ -772,7 +802,7 @@ _do_test_nm_utils_strsplit_set_simple(NMUtilsStrsplitSetFlags flags,
     g_assert_cmpint(words_len, >, 0);
     n_tokens = NM_PTRARRAY_LEN(tokens);
 
-    if (nm_utils_strv_cmp_n(exp_words, words_len, tokens, -1) != 0) {
+    if (nm_strv_cmp_n(exp_words, words_len, tokens, -1) != 0) {
         gsize i;
 
         g_print(">>> split \"%s\" (flags %x) got %zu tokens (%zu expected)\n",
@@ -794,107 +824,87 @@ _do_test_nm_utils_strsplit_set_simple(NMUtilsStrsplitSetFlags flags,
     }
     g_assert_cmpint(words_len, ==, NM_PTRARRAY_LEN(tokens));
 }
-#define do_test_nm_utils_strsplit_set_simple(flags, str, ...)   \
-    _do_test_nm_utils_strsplit_set_simple((flags),              \
-                                          (str),                \
-                                          NM_NARG(__VA_ARGS__), \
-                                          NM_MAKE_STRV(__VA_ARGS__))
+#define do_test_nm_strsplit_set_simple(flags, str, ...) \
+    _do_test_nm_strsplit_set_simple((flags), (str), NM_NARG(__VA_ARGS__), NM_MAKE_STRV(__VA_ARGS__))
 
 static void
-test_nm_utils_strsplit_set(void)
+test_nm_strsplit_set(void)
 {
     gs_unref_ptrarray GPtrArray *words_exp = NULL;
     guint                        test_run;
 
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_NONE, NULL);
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_NONE, "");
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_NONE, " ");
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_NONE, "a  b", "a", "b");
-
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, NULL);
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, "");
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, " ", "", "");
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, "  ", "", "", "");
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, "a  ", "a", "", "");
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
-                                    "a  b",
-                                    "a",
-                                    "",
-                                    "b");
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
-                                    " ab  b",
-                                    "",
-                                    "ab",
-                                    "",
-                                    "b");
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
-                                    "ab  b",
-                                    "ab",
-                                    "",
-                                    "b");
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, "abb", "abb");
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
-                                    "abb  bb ",
-                                    "abb",
-                                    "",
-                                    "bb",
-                                    "");
-    do_test_nm_utils_strsplit_set_f(NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
-                                    "abb bcb ",
-                                    "abb",
-                                    "bcb",
-                                    "");
-
-    do_test_nm_utils_strsplit_set(FALSE, NULL);
-    do_test_nm_utils_strsplit_set(FALSE, "");
-    do_test_nm_utils_strsplit_set(FALSE, "\n");
-    do_test_nm_utils_strsplit_set(TRUE, " \t\n", "\t");
-    do_test_nm_utils_strsplit_set(FALSE, "a", "a");
-    do_test_nm_utils_strsplit_set(FALSE, "a b", "a", "b");
-    do_test_nm_utils_strsplit_set(FALSE, "a\rb", "a\rb");
-    do_test_nm_utils_strsplit_set(FALSE, "  a\rb  ", "a\rb");
-    do_test_nm_utils_strsplit_set(FALSE, "  a bbbd afds ere", "a", "bbbd", "afds", "ere");
-    do_test_nm_utils_strsplit_set(FALSE,
-                                  "1 2 3 4 5 6 7 8 9 0 "
-                                  "1 2 3 4 5 6 7 8 9 0 "
-                                  "1 2 3 4 5 6 7 8 9 0",
-                                  "1",
-                                  "2",
-                                  "3",
-                                  "4",
-                                  "5",
-                                  "6",
-                                  "7",
-                                  "8",
-                                  "9",
-                                  "0",
-                                  "1",
-                                  "2",
-                                  "3",
-                                  "4",
-                                  "5",
-                                  "6",
-                                  "7",
-                                  "8",
-                                  "9",
-                                  "0",
-                                  "1",
-                                  "2",
-                                  "3",
-                                  "4",
-                                  "5",
-                                  "6",
-                                  "7",
-                                  "8",
-                                  "9",
-                                  "0");
-    do_test_nm_utils_strsplit_set(TRUE, "\\", "\\");
-    do_test_nm_utils_strsplit_set(TRUE, "\\ ", "\\ ");
-    do_test_nm_utils_strsplit_set(TRUE, "\\\\", "\\\\");
-    do_test_nm_utils_strsplit_set(TRUE, "\\\t", "\\\t");
-    do_test_nm_utils_strsplit_set(TRUE, "foo\\", "foo\\");
-    do_test_nm_utils_strsplit_set(TRUE, "bar foo\\", "bar", "foo\\");
-    do_test_nm_utils_strsplit_set(TRUE, "\\ a b\\ \\  c", "\\ a", "b\\ \\ ", "c");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_NONE, NULL);
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_NONE, "");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_NONE, " ");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_NONE, "a  b", "a", "b");
+
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, NULL);
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, "");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, " ", "", "");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, "  ", "", "", "");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, "a  ", "a", "", "");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, "a  b", "a", "", "b");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, " ab  b", "", "ab", "", "b");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, "ab  b", "ab", "", "b");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, "abb", "abb");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
+                              "abb  bb ",
+                              "abb",
+                              "",
+                              "bb",
+                              "");
+    do_test_nm_strsplit_set_f(NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY, "abb bcb ", "abb", "bcb", "");
+
+    do_test_nm_strsplit_set(FALSE, NULL);
+    do_test_nm_strsplit_set(FALSE, "");
+    do_test_nm_strsplit_set(FALSE, "\n");
+    do_test_nm_strsplit_set(TRUE, " \t\n", "\t");
+    do_test_nm_strsplit_set(FALSE, "a", "a");
+    do_test_nm_strsplit_set(FALSE, "a b", "a", "b");
+    do_test_nm_strsplit_set(FALSE, "a\rb", "a\rb");
+    do_test_nm_strsplit_set(FALSE, "  a\rb  ", "a\rb");
+    do_test_nm_strsplit_set(FALSE, "  a bbbd afds ere", "a", "bbbd", "afds", "ere");
+    do_test_nm_strsplit_set(FALSE,
+                            "1 2 3 4 5 6 7 8 9 0 "
+                            "1 2 3 4 5 6 7 8 9 0 "
+                            "1 2 3 4 5 6 7 8 9 0",
+                            "1",
+                            "2",
+                            "3",
+                            "4",
+                            "5",
+                            "6",
+                            "7",
+                            "8",
+                            "9",
+                            "0",
+                            "1",
+                            "2",
+                            "3",
+                            "4",
+                            "5",
+                            "6",
+                            "7",
+                            "8",
+                            "9",
+                            "0",
+                            "1",
+                            "2",
+                            "3",
+                            "4",
+                            "5",
+                            "6",
+                            "7",
+                            "8",
+                            "9",
+                            "0");
+    do_test_nm_strsplit_set(TRUE, "\\", "\\");
+    do_test_nm_strsplit_set(TRUE, "\\ ", "\\ ");
+    do_test_nm_strsplit_set(TRUE, "\\\\", "\\\\");
+    do_test_nm_strsplit_set(TRUE, "\\\t", "\\\t");
+    do_test_nm_strsplit_set(TRUE, "foo\\", "foo\\");
+    do_test_nm_strsplit_set(TRUE, "bar foo\\", "bar", "foo\\");
+    do_test_nm_strsplit_set(TRUE, "\\ a b\\ \\  c", "\\ a", "b\\ \\ ", "c");
 
     words_exp = g_ptr_array_new_with_free_func(g_free);
     for (test_run = 0; test_run < 100; test_run++) {
@@ -942,52 +952,40 @@ test_nm_utils_strsplit_set(void)
             words_len = 0;
         }
 
-        _do_test_nm_utils_strsplit_set_f((f_allow_escaping
-                                              ? NM_UTILS_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING
-                                              : NM_UTILS_STRSPLIT_SET_FLAGS_NONE)
-                                             | NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
-                                         str,
-                                         words_len,
-                                         (const char *const *) words_exp->pdata);
+        _do_test_nm_strsplit_set_f(
+            (f_allow_escaping ? NM_STRSPLIT_SET_FLAGS_ALLOW_ESCAPING : NM_STRSPLIT_SET_FLAGS_NONE)
+                | NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
+            str,
+            words_len,
+            (const char *const *) words_exp->pdata);
     }
 
-    do_test_nm_utils_strsplit_set_simple(NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED, "\t", "\t");
-    do_test_nm_utils_strsplit_set_simple(NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED
-                                             | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP,
-                                         "\t");
-    do_test_nm_utils_strsplit_set_simple(NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED
-                                             | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP
-                                             | NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
-                                         "\t",
-                                         "");
-    do_test_nm_utils_strsplit_set_simple(NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED
-                                             | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP
-                                             | NM_UTILS_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
-                                         "\t\\\t\t\t\\\t",
-                                         "\t\t\t\t");
-
-    do_test_nm_utils_strsplit_set_simple(NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED, "\ta", "\ta");
-    do_test_nm_utils_strsplit_set_simple(NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED
-                                             | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP,
-                                         "\ta",
-                                         "a");
-    do_test_nm_utils_strsplit_set_simple(NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED,
-                                         "\ta\\ b\t\\ ",
-                                         "\ta b\t ");
-    do_test_nm_utils_strsplit_set_simple(NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED
-                                             | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP,
-                                         "\ta\\ b\t\\ \t",
-                                         "a b\t ");
-    do_test_nm_utils_strsplit_set_simple(NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED, "a\\  b", "a ", "b");
-    do_test_nm_utils_strsplit_set_simple(NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED,
-                                         "\ta\\  b",
-                                         "\ta ",
-                                         "b");
-    do_test_nm_utils_strsplit_set_simple(NM_UTILS_STRSPLIT_SET_FLAGS_ESCAPED
-                                             | NM_UTILS_STRSPLIT_SET_FLAGS_STRSTRIP,
-                                         "\ta\\  b",
-                                         "a ",
-                                         "b");
+    do_test_nm_strsplit_set_simple(NM_STRSPLIT_SET_FLAGS_ESCAPED, "\t", "\t");
+    do_test_nm_strsplit_set_simple(NM_STRSPLIT_SET_FLAGS_ESCAPED | NM_STRSPLIT_SET_FLAGS_STRSTRIP,
+                                   "\t");
+    do_test_nm_strsplit_set_simple(NM_STRSPLIT_SET_FLAGS_ESCAPED | NM_STRSPLIT_SET_FLAGS_STRSTRIP
+                                       | NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
+                                   "\t",
+                                   "");
+    do_test_nm_strsplit_set_simple(NM_STRSPLIT_SET_FLAGS_ESCAPED | NM_STRSPLIT_SET_FLAGS_STRSTRIP
+                                       | NM_STRSPLIT_SET_FLAGS_PRESERVE_EMPTY,
+                                   "\t\\\t\t\t\\\t",
+                                   "\t\t\t\t");
+
+    do_test_nm_strsplit_set_simple(NM_STRSPLIT_SET_FLAGS_ESCAPED, "\ta", "\ta");
+    do_test_nm_strsplit_set_simple(NM_STRSPLIT_SET_FLAGS_ESCAPED | NM_STRSPLIT_SET_FLAGS_STRSTRIP,
+                                   "\ta",
+                                   "a");
+    do_test_nm_strsplit_set_simple(NM_STRSPLIT_SET_FLAGS_ESCAPED, "\ta\\ b\t\\ ", "\ta b\t ");
+    do_test_nm_strsplit_set_simple(NM_STRSPLIT_SET_FLAGS_ESCAPED | NM_STRSPLIT_SET_FLAGS_STRSTRIP,
+                                   "\ta\\ b\t\\ \t",
+                                   "a b\t ");
+    do_test_nm_strsplit_set_simple(NM_STRSPLIT_SET_FLAGS_ESCAPED, "a\\  b", "a ", "b");
+    do_test_nm_strsplit_set_simple(NM_STRSPLIT_SET_FLAGS_ESCAPED, "\ta\\  b", "\ta ", "b");
+    do_test_nm_strsplit_set_simple(NM_STRSPLIT_SET_FLAGS_ESCAPED | NM_STRSPLIT_SET_FLAGS_STRSTRIP,
+                                   "\ta\\  b",
+                                   "a ",
+                                   "b");
 }
 
 /*****************************************************************************/
@@ -3062,8 +3060,7 @@ test_setting_new_from_dbus_bad(void)
                                                        "i",
                                                        10););
     conn = _connection_new_from_dbus(dict, &error);
-    g_assert(conn);
-    g_assert_no_error(error);
+    nmtst_assert_success(conn, error);
     setting = nm_connection_get_setting(conn, NM_TYPE_SETTING_WIRELESS);
     g_assert(setting);
     g_assert_cmpint(nm_setting_wireless_get_rate(NM_SETTING_WIRELESS(setting)), ==, 10);
@@ -3823,6 +3820,7 @@ test_connection_diff_a_only(void)
           {NM_SETTING_CONNECTION_AUTH_RETRIES, NM_SETTING_DIFF_RESULT_IN_A},
           {NM_SETTING_CONNECTION_MDNS, NM_SETTING_DIFF_RESULT_IN_A},
           {NM_SETTING_CONNECTION_LLMNR, NM_SETTING_DIFF_RESULT_IN_A},
+          {NM_SETTING_CONNECTION_DNS_OVER_TLS, NM_SETTING_DIFF_RESULT_IN_A},
           {NM_SETTING_CONNECTION_MUD_URL, NM_SETTING_DIFF_RESULT_IN_A},
           {NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A},
           {NULL, NM_SETTING_DIFF_RESULT_UNKNOWN}}},
@@ -5016,7 +5014,7 @@ test_connection_changed_signal(void)
     connection = new_test_connection();
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     (GCallback) test_connection_changed_cb,
+                     G_CALLBACK(test_connection_changed_cb),
                      &changed);
 
     /* Add new setting */
@@ -5042,7 +5040,7 @@ test_setting_connection_changed_signal(void)
     connection = nm_simple_connection_new();
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     (GCallback) test_connection_changed_cb,
+                     G_CALLBACK(test_connection_changed_cb),
                      &changed);
 
     s_con = (NMSettingConnection *) nm_setting_connection_new();
@@ -5078,7 +5076,7 @@ test_setting_bond_changed_signal(void)
     connection = nm_simple_connection_new();
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     (GCallback) test_connection_changed_cb,
+                     G_CALLBACK(test_connection_changed_cb),
                      &changed);
 
     s_bond = (NMSettingBond *) nm_setting_bond_new();
@@ -5104,7 +5102,7 @@ test_setting_ip4_changed_signal(void)
     connection = nm_simple_connection_new();
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     (GCallback) test_connection_changed_cb,
+                     G_CALLBACK(test_connection_changed_cb),
                      &changed);
 
     s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
@@ -5180,7 +5178,7 @@ test_setting_ip6_changed_signal(void)
     connection = nm_simple_connection_new();
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     (GCallback) test_connection_changed_cb,
+                     G_CALLBACK(test_connection_changed_cb),
                      &changed);
 
     s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new();
@@ -5247,7 +5245,7 @@ test_setting_vlan_changed_signal(void)
     connection = nm_simple_connection_new();
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     (GCallback) test_connection_changed_cb,
+                     G_CALLBACK(test_connection_changed_cb),
                      &changed);
 
     s_vlan = (NMSettingVlan *) nm_setting_vlan_new();
@@ -5282,7 +5280,7 @@ test_setting_vpn_changed_signal(void)
     connection = nm_simple_connection_new();
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     (GCallback) test_connection_changed_cb,
+                     G_CALLBACK(test_connection_changed_cb),
                      &changed);
 
     s_vpn = (NMSettingVpn *) nm_setting_vpn_new();
@@ -5309,7 +5307,7 @@ test_setting_wired_changed_signal(void)
     connection = nm_simple_connection_new();
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     (GCallback) test_connection_changed_cb,
+                     G_CALLBACK(test_connection_changed_cb),
                      &changed);
 
     s_wired = (NMSettingWired *) nm_setting_wired_new();
@@ -5332,7 +5330,7 @@ test_setting_wireless_changed_signal(void)
     connection = nm_simple_connection_new();
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     (GCallback) test_connection_changed_cb,
+                     G_CALLBACK(test_connection_changed_cb),
                      &changed);
 
     s_wifi = (NMSettingWireless *) nm_setting_wireless_new();
@@ -5353,7 +5351,7 @@ test_setting_wireless_security_changed_signal(void)
     connection = nm_simple_connection_new();
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     (GCallback) test_connection_changed_cb,
+                     G_CALLBACK(test_connection_changed_cb),
                      &changed);
 
     s_wsec = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new();
@@ -5420,7 +5418,7 @@ test_setting_802_1x_changed_signal(void)
     connection = nm_simple_connection_new();
     g_signal_connect(connection,
                      NM_CONNECTION_CHANGED,
-                     (GCallback) test_connection_changed_cb,
+                     G_CALLBACK(test_connection_changed_cb),
                      &changed);
 
     s_8021x = (NMSetting8021x *) nm_setting_802_1x_new();
@@ -7121,7 +7119,7 @@ test_hexstr2bin(void)
             g_assert(b);
         else
             g_assert(!b);
-        g_assert(nm_utils_gbytes_equal_mem(b, items[i].expected, items[i].expected_len));
+        g_assert(nm_g_bytes_equal_mem(b, items[i].expected, items[i].expected_len));
     }
 }
 
@@ -7209,11 +7207,10 @@ test_nm_strquote(void)
 
 /*****************************************************************************/
 
-#define UUID_NS_ZERO "00000000-0000-0000-0000-000000000000"
-#define UUID_NS_DNS  "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
-#define UUID_NS_URL  "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
-#define UUID_NS_OID  "6ba7b812-9dad-11d1-80b4-00c04fd430c8"
-#define UUID_NS_X500 "6ba7b814-9dad-11d1-80b4-00c04fd430c8"
+#define NM_UUID_NS_DNS  "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
+#define NM_UUID_NS_URL  "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
+#define NM_UUID_NS_OID  "6ba7b812-9dad-11d1-80b4-00c04fd430c8"
+#define NM_UUID_NS_X500 "6ba7b814-9dad-11d1-80b4-00c04fd430c8"
 
 static const NMUuid *
 _uuid(const char *str)
@@ -7230,11 +7227,18 @@ _test_uuid(int         uuid_type,
            const char *expected_uuid,
            const char *str,
            gssize      slen,
-           gpointer    type_args)
+           const char *type_args)
 {
-    gs_free char *uuid_test = NULL;
+    gs_free char *uuid_test   = NULL;
+    NMUuid        type_args_u = NM_UUID_INIT_ZERO();
 
-    uuid_test = nm_uuid_generate_from_string_str(str, slen, uuid_type, type_args);
+    if (type_args) {
+        if (!nm_uuid_parse(type_args, &type_args_u))
+            g_assert_not_reached();
+    }
+
+    uuid_test =
+        nm_uuid_generate_from_string_str(str, slen, uuid_type, type_args ? &type_args_u : NULL);
 
     g_assert(uuid_test);
     g_assert(nm_utils_is_uuid(uuid_test));
@@ -7245,7 +7249,7 @@ _test_uuid(int         uuid_type,
                 str,
                 (long long) slen,
                 NM_IN_SET(uuid_type, NM_UUID_TYPE_VERSION3, NM_UUID_TYPE_VERSION5)
-                    ? (((const char *) type_args) ?: "(all-zero)")
+                    ? (type_args ?: "(all-zero)")
                     : (type_args ? "(unknown)" : "(null)"),
                 uuid_test,
                 expected_uuid);
@@ -7260,8 +7264,8 @@ _test_uuid(int         uuid_type,
     }
 
     if (NM_IN_SET(uuid_type, NM_UUID_TYPE_VERSION3, NM_UUID_TYPE_VERSION5) && !type_args) {
-        /* For version3 and version5, a missing @type_args is equal to UUID_NS_ZERO */
-        _test_uuid(uuid_type, expected_uuid, str, slen, UUID_NS_ZERO);
+        /* For version3 and version5, a missing @type_args is equal to NM_UUID_NS_ZERO */
+        _test_uuid(uuid_type, expected_uuid, str, slen, NM_UUID_NS_ZERO);
     }
 }
 
@@ -7695,33 +7699,53 @@ test_nm_utils_uuid_generate_from_string(void)
     _test_uuid(NM_UUID_TYPE_VERSION3, "96e17d7a-ac89-38cf-95e1-bf5098da34e1", "test", -1, NULL);
     _test_uuid(NM_UUID_TYPE_VERSION3, "8156568e-4ae6-3f34-a93e-18e2c6cbbf78", "a\0b", 3, NULL);
 
-    _test_uuid(NM_UUID_TYPE_VERSION3, "c87ee674-4ddc-3efe-a74e-dfe25da5d7b3", "", -1, UUID_NS_DNS);
-    _test_uuid(NM_UUID_TYPE_VERSION3, "4c104dd0-4821-30d5-9ce3-0e7a1f8b7c0d", "a", -1, UUID_NS_DNS);
+    _test_uuid(NM_UUID_TYPE_VERSION3,
+               "c87ee674-4ddc-3efe-a74e-dfe25da5d7b3",
+               "",
+               -1,
+               NM_UUID_NS_DNS);
+    _test_uuid(NM_UUID_TYPE_VERSION3,
+               "4c104dd0-4821-30d5-9ce3-0e7a1f8b7c0d",
+               "a",
+               -1,
+               NM_UUID_NS_DNS);
     _test_uuid(NM_UUID_TYPE_VERSION3,
                "45a113ac-c7f2-30b0-90a5-a399ab912716",
                "test",
                -1,
-               UUID_NS_DNS);
+               NM_UUID_NS_DNS);
     _test_uuid(NM_UUID_TYPE_VERSION3,
                "002a0ada-f547-375a-bab5-896a11d1927e",
                "a\0b",
                3,
-               UUID_NS_DNS);
+               NM_UUID_NS_DNS);
     _test_uuid(NM_UUID_TYPE_VERSION3,
                "9a75f5f2-195e-31a9-9d07-8c18b5d3b285",
                "test123",
                -1,
-               UUID_NS_DNS);
-    _test_uuid(NM_UUID_TYPE_VERSION3, "ec794efe-a384-3b11-a0b6-ec8995bc6acc", "x", -1, UUID_NS_DNS);
+               NM_UUID_NS_DNS);
+    _test_uuid(NM_UUID_TYPE_VERSION3,
+               "ec794efe-a384-3b11-a0b6-ec8995bc6acc",
+               "x",
+               -1,
+               NM_UUID_NS_DNS);
 
     _test_uuid(NM_UUID_TYPE_VERSION5, "a7650b9f-f19f-5300-8a13-91160ea8de2c", "a\0b", 3, NULL);
-    _test_uuid(NM_UUID_TYPE_VERSION5, "4f3f2898-69e3-5a0d-820a-c4e87987dbce", "a", -1, UUID_NS_DNS);
-    _test_uuid(NM_UUID_TYPE_VERSION5, "05b16a01-46c6-56dd-bd6e-c6dfb4a1427a", "x", -1, UUID_NS_DNS);
+    _test_uuid(NM_UUID_TYPE_VERSION5,
+               "4f3f2898-69e3-5a0d-820a-c4e87987dbce",
+               "a",
+               -1,
+               NM_UUID_NS_DNS);
+    _test_uuid(NM_UUID_TYPE_VERSION5,
+               "05b16a01-46c6-56dd-bd6e-c6dfb4a1427a",
+               "x",
+               -1,
+               NM_UUID_NS_DNS);
     _test_uuid(NM_UUID_TYPE_VERSION5,
                "c9ed566a-6b79-5d3a-b2b7-96a936b48cf3",
                "test123",
                -1,
-               UUID_NS_DNS);
+               NM_UUID_NS_DNS);
 
     for (i = 0; i < G_N_ELEMENTS(zero_uuids); i++) {
         nm_sprintf_buf(i_str, "%u", i),
@@ -7730,8 +7754,8 @@ test_nm_utils_uuid_generate_from_string(void)
     }
     for (i = 0; i < G_N_ELEMENTS(dns_uuids); i++) {
         nm_sprintf_buf(i_str, "%u", i),
-            _test_uuid(NM_UUID_TYPE_VERSION3, dns_uuids[i].uuid3, i_str, -1, UUID_NS_DNS);
-        _test_uuid(NM_UUID_TYPE_VERSION5, dns_uuids[i].uuid5, i_str, -1, UUID_NS_DNS);
+            _test_uuid(NM_UUID_TYPE_VERSION3, dns_uuids[i].uuid3, i_str, -1, NM_UUID_NS_DNS);
+        _test_uuid(NM_UUID_TYPE_VERSION5, dns_uuids[i].uuid5, i_str, -1, NM_UUID_NS_DNS);
     }
 
     /* examples from cpython unit tests: */
@@ -7739,48 +7763,48 @@ test_nm_utils_uuid_generate_from_string(void)
                "6fa459ea-ee8a-3ca4-894e-db77e160355e",
                "python.org",
                -1,
-               UUID_NS_DNS);
+               NM_UUID_NS_DNS);
     _test_uuid(NM_UUID_TYPE_VERSION5,
                "886313e1-3b8a-5372-9b90-0c9aee199e5d",
                "python.org",
                -1,
-               UUID_NS_DNS);
+               NM_UUID_NS_DNS);
     _test_uuid(NM_UUID_TYPE_VERSION3,
                "9fe8e8c4-aaa8-32a9-a55c-4535a88b748d",
                "http://python.org/",
                -1,
-               UUID_NS_URL);
+               NM_UUID_NS_URL);
     _test_uuid(NM_UUID_TYPE_VERSION5,
                "4c565f0d-3f5a-5890-b41b-20cf47701c5e",
                "http://python.org/",
                -1,
-               UUID_NS_URL);
+               NM_UUID_NS_URL);
     _test_uuid(NM_UUID_TYPE_VERSION3,
                "dd1a1cef-13d5-368a-ad82-eca71acd4cd1",
                "1.3.6.1",
                -1,
-               UUID_NS_OID);
+               NM_UUID_NS_OID);
     _test_uuid(NM_UUID_TYPE_VERSION5,
                "1447fa61-5277-5fef-a9b3-fbc6e44f4af3",
                "1.3.6.1",
                -1,
-               UUID_NS_OID);
+               NM_UUID_NS_OID);
     _test_uuid(NM_UUID_TYPE_VERSION3,
                "658d3002-db6b-3040-a1d1-8ddd7d189a4d",
                "c=ca",
                -1,
-               UUID_NS_X500);
+               NM_UUID_NS_X500);
     _test_uuid(NM_UUID_TYPE_VERSION5,
                "cc957dd1-a972-5349-98cd-874190002798",
                "c=ca",
                -1,
-               UUID_NS_X500);
+               NM_UUID_NS_X500);
 
     _test_uuid(NM_UUID_TYPE_VERSION5,
                "74738ff5-5367-5958-9aee-98fffdcd1876",
                "www.example.org",
                -1,
-               UUID_NS_DNS);
+               NM_UUID_NS_DNS);
 }
 
 /*****************************************************************************/
@@ -7800,7 +7824,7 @@ __test_uuid(const char *expected_uuid, const char *str, gssize slen, char *uuid_
     }
     g_free(uuid_test);
 
-    uuid_test = nm_uuid_generate_from_string_str(str, slen, NM_UUID_TYPE_VERSION3, NM_UUID_NS1);
+    uuid_test = nm_uuid_generate_from_string_str(str, slen, NM_UUID_TYPE_VERSION3, &nm_uuid_ns_1);
 
     g_assert(uuid_test);
     g_assert(nm_utils_is_uuid(uuid_test));
@@ -7821,15 +7845,23 @@ __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 = {};
+    const NMUuid uuid0 = NM_UUID_INIT_ZERO();
+    const NMUuid uuid1 = {};
+    char         buf[37];
 
     g_assert_cmpmem(&uuid0, sizeof(uuid0), _uuid("00000000-0000-0000-0000-000000000000"), 16);
 
+    g_assert_cmpmem(&uuid0, sizeof(NMUuid), &uuid1, sizeof(NMUuid));
+
     g_assert(nm_uuid_is_null(NULL));
     g_assert(nm_uuid_is_null(&uuid0));
+    g_assert(nm_uuid_is_null(&nm_uuid_ns_zero));
     g_assert(nm_uuid_is_null(_uuid("00000000-0000-0000-0000-000000000000")));
     g_assert(!nm_uuid_is_null(_uuid("10000000-0000-0000-0000-000000000000")));
 
+    g_assert_cmpstr(NM_UUID_NS_1, ==, nm_uuid_unparse(&nm_uuid_ns_1, buf));
+    g_assert_cmpstr(NM_UUID_NS_ZERO, ==, nm_uuid_unparse(&nm_uuid_ns_zero, buf));
+
     _test_uuid("b07c334a-399b-32de-8d50-58e4e08f98e3", "", 0, NULL);
     _test_uuid("b8a426cb-bcb5-30a3-bd8f-6786fea72df9", "\0", 1, "");
     _test_uuid("12a4a982-7aae-39e1-951e-41aeb1250959", "a\0", 2, "a");
@@ -7846,6 +7878,32 @@ test_nm_utils_uuid_generate_from_strings(void)
     _test_uuid("dd265bf7-c05a-3037-9939-b9629858a477", "a\0b\0", 4, "a", "b");
 }
 
+static void
+test_nm_uuid_init(void)
+{
+    char buf[37];
+
+    {
+        NMUuid u;
+
+        u = NM_UUID_INIT(47, c4, d7, f9, 2c, 81, 4f, 7b, be, ed, 63, 0a, 7f, 65, cc, 02);
+        g_assert_cmpstr("47c4d7f9-2c81-4f7b-beed-630a7f65cc02", ==, nm_uuid_unparse(&u, buf));
+    }
+    {
+        const NMUuid u =
+            NM_UUID_INIT(47, c4, d7, f9, 2c, 81, 4f, 7b, be, ed, 63, 0a, 7f, 65, cc, 02);
+
+        g_assert_cmpstr("47c4d7f9-2c81-4f7b-beed-630a7f65cc02", ==, nm_uuid_unparse(&u, buf));
+    }
+    {
+        const struct {
+            NMUuid u;
+        } u = {NM_UUID_INIT(47, c4, d7, f9, 2c, 81, 4f, 7b, be, ed, 63, 0a, 7f, 65, cc, 02)};
+
+        g_assert_cmpstr("47c4d7f9-2c81-4f7b-beed-630a7f65cc02", ==, nm_uuid_unparse(&u.u, buf));
+    }
+}
+
 /*****************************************************************************/
 
 static void
@@ -9787,11 +9845,53 @@ test_nm_va_args_macros(void)
     g_assert_cmpint(7, ==, GET_NARG_1(x, x, x, x, x, x, x));
     g_assert_cmpint(8, ==, GET_NARG_1(x, x, x, x, x, x, x, x));
     g_assert_cmpint(9, ==, GET_NARG_1(x, x, x, x, x, x, x, x, x));
-    g_assert_cmpint(10, ==, GET_NARG_1(x, x, x, x, x, x, x, x, x, x));
+    g_assert_cmpint(10, ==, NM_NARG(x, x, x, x, x, x, x, x, x, x));
 
     G_STATIC_ASSERT_EXPR(0 == GET_NARG_1());
     G_STATIC_ASSERT_EXPR(1 == GET_NARG_1(x));
     G_STATIC_ASSERT_EXPR(2 == GET_NARG_1(x, x));
+
+    /* clang-format off */
+    G_STATIC_ASSERT_EXPR(NM_NARG(
+                                 1,2,3,4,5,6,7,8,9,10,
+                                 1,2,3,4,5,6,7,8,9,20,
+                                 1,2,3,4,5,6,7,8,9,30
+                                 ) == 30);
+    G_STATIC_ASSERT_EXPR(NM_NARG(
+                                 1,2,3,4,5,6,7,8,9,10,
+                                 1,2,3,4,5,6,7,8,9,20,
+                                 1,2,3,4,5,6,7,8,9,30,
+                                 1,2,3,4,5,6,7,8,9,40,
+                                 1,2,3,4,5,6,7,8,9,50,
+                                 1,2,3,4,5,6,7,8,9,60,
+                                 1,2,3,4,5,6,7,8,9,70,
+                                 1,2,3,4,5,6,7,8,9,80
+                                 ) == 80);
+    G_STATIC_ASSERT_EXPR(NM_NARG(
+                                 1,2,3,4,5,6,7,8,9,10,
+                                 1,2,3,4,5,6,7,8,9,20,
+                                 1,2,3,4,5,6,7,8,9,30,
+                                 1,2,3,4,5,6,7,8,9,40,
+                                 1,2,3,4,5,6,7,8,9,50,
+                                 1,2,3,4,5,6,7,8,9,60,
+                                 1,2,3,4,5,6,7,8,9,70,
+                                 1,2,3,4,5,6,7,8,9,80,
+                                 1,2,3,4,5,6,7,8,9,90,
+                                 1,2,3,4,5,6,7,8,9,100,
+                                 1,2,3,4,5,6,7,8,9,110,
+                                 1,2,3,4,5,6,7,8,9,120
+                                 ) == 120);
+    /* clang-format on */
+
+    G_STATIC_ASSERT_EXPR(NM_NARG_MAX1() == 0);
+    G_STATIC_ASSERT_EXPR(NM_NARG_MAX1(1) == 1);
+    G_STATIC_ASSERT_EXPR(NM_NARG_MAX1(1, 2) == 1);
+    G_STATIC_ASSERT_EXPR(NM_NARG_MAX1(1, 2, 3) == 1);
+
+    G_STATIC_ASSERT_EXPR(NM_NARG_MAX2() == 0);
+    G_STATIC_ASSERT_EXPR(NM_NARG_MAX2(1) == 1);
+    G_STATIC_ASSERT_EXPR(NM_NARG_MAX2(1, 2) == 2);
+    G_STATIC_ASSERT_EXPR(NM_NARG_MAX2(1, 2, 3) == 2);
 }
 
 /*****************************************************************************/
@@ -10333,7 +10433,7 @@ _strsplit_quoted_assert_strv(const char *       topic,
     g_assert(strv1);
     g_assert(strv2);
 
-    if (nm_utils_strv_equal(strv1, strv2))
+    if (nm_strv_equal(strv1, strv2))
         return;
 
     for (i = 0; strv1[i]; i++) {
@@ -10425,6 +10525,60 @@ test_strsplit_quoted(void)
 /*****************************************************************************/
 
 static void
+test_nm_property_variant_to_gvalue(void)
+{
+#define _test_variant_to_gvalue_bad(variant, gtype)                                     \
+    G_STMT_START                                                                        \
+    {                                                                                   \
+        gs_unref_variant GVariant * _variant = (variant);                               \
+        GType                       _gtype   = (gtype);                                 \
+        nm_auto_unset_gvalue GValue _gvalue  = G_VALUE_INIT;                            \
+                                                                                        \
+        g_value_init(&_gvalue, _gtype);                                                 \
+        g_assert_cmpint(_nm_property_variant_to_gvalue(_variant, &_gvalue), ==, FALSE); \
+    }                                                                                   \
+    G_STMT_END
+
+#define _test_variant_to_gvalue(variant, gtype, check)                                 \
+    G_STMT_START                                                                       \
+    {                                                                                  \
+        gs_unref_variant GVariant * _variant = (variant);                              \
+        GType                       _gtype   = (gtype);                                \
+        nm_auto_unset_gvalue GValue _gvalue  = G_VALUE_INIT;                           \
+        _nm_unused GValue *const gg          = &_gvalue;                               \
+                                                                                       \
+        g_value_init(&_gvalue, _gtype);                                                \
+        g_assert_cmpint(_nm_property_variant_to_gvalue(_variant, &_gvalue), ==, TRUE); \
+        check;                                                                         \
+    }                                                                                  \
+    G_STMT_END
+
+#define _test_variant_to_gvalue_int(variant, gtype, gvalue_get, expected) \
+    _test_variant_to_gvalue((variant), (gtype), g_assert_cmpint(gvalue_get(gg), ==, (expected)))
+
+    _test_variant_to_gvalue_bad(g_variant_new_string(""), G_TYPE_BOOLEAN);
+    _test_variant_to_gvalue(g_variant_new_string(""),
+                            G_TYPE_STRING,
+                            g_assert_cmpstr(g_value_get_string(gg), ==, ""));
+    _test_variant_to_gvalue_int(g_variant_new_boolean(FALSE),
+                                G_TYPE_BOOLEAN,
+                                g_value_get_boolean,
+                                FALSE);
+    _test_variant_to_gvalue_int(g_variant_new_boolean(TRUE),
+                                G_TYPE_BOOLEAN,
+                                g_value_get_boolean,
+                                TRUE);
+    _test_variant_to_gvalue_int(g_variant_new_int32(0), G_TYPE_BOOLEAN, g_value_get_boolean, FALSE);
+    _test_variant_to_gvalue_int(g_variant_new_int32(1), G_TYPE_BOOLEAN, g_value_get_boolean, 1);
+    _test_variant_to_gvalue_int(g_variant_new_int32(2), G_TYPE_BOOLEAN, g_value_get_boolean, 1);
+    _test_variant_to_gvalue_int(g_variant_new_byte(0), G_TYPE_BOOLEAN, g_value_get_boolean, 0);
+    _test_variant_to_gvalue_int(g_variant_new_byte(1), G_TYPE_BOOLEAN, g_value_get_boolean, 1);
+    _test_variant_to_gvalue_int(g_variant_new_byte(2), G_TYPE_BOOLEAN, g_value_get_boolean, 1);
+}
+
+/*****************************************************************************/
+
+static void
 _do_wifi_ghz_freqs(const guint *freqs, const char *band)
 {
     int len;
@@ -10531,7 +10685,7 @@ main(int argc, char **argv)
     g_test_add_func("/core/general/test_c_list_sort", test_c_list_sort);
     g_test_add_func("/core/general/test_dedup_multi", test_dedup_multi);
     g_test_add_func("/core/general/test_utils_str_utf8safe", test_utils_str_utf8safe);
-    g_test_add_func("/core/general/test_nm_utils_strsplit_set", test_nm_utils_strsplit_set);
+    g_test_add_func("/core/general/test_nm_strsplit_set", test_nm_strsplit_set);
     g_test_add_func("/core/general/test_nm_utils_escaped_tokens", test_nm_utils_escaped_tokens);
     g_test_add_func("/core/general/test_nm_in_set", test_nm_in_set);
     g_test_add_func("/core/general/test_nm_in_strset", test_nm_in_strset);
@@ -10743,6 +10897,9 @@ main(int argc, char **argv)
     g_test_add_func("/core/general/test_setting_connection_permissions_property",
                     test_setting_connection_permissions_property);
 
+    g_test_add_func("/core/general/test_nm_property_variant_to_gvalue",
+                    test_nm_property_variant_to_gvalue);
+
     g_test_add_func("/core/general/test_connection_compare_same", test_connection_compare_same);
     g_test_add_func("/core/general/test_connection_compare_key_only_in_a",
                     test_connection_compare_key_only_in_a);
@@ -10810,6 +10967,7 @@ main(int argc, char **argv)
                     test_nm_utils_uuid_generate_from_string);
     g_test_add_func("/core/general/nm_uuid_generate_from_strings",
                     test_nm_utils_uuid_generate_from_strings);
+    g_test_add_func("/core/general/test_nm_uuid_init", test_nm_uuid_init);
 
     g_test_add_func("/core/general/_nm_utils_ascii_str_to_int64", test_nm_utils_ascii_str_to_int64);
     g_test_add_func("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two);
diff --git a/src/libnm-core-impl/tests/test-keyfile.c b/src/libnm-core-impl/tests/test-keyfile.c
index ba7d783b..db193379 100644
--- a/src/libnm-core-impl/tests/test-keyfile.c
+++ b/src/libnm-core-impl/tests/test-keyfile.c
@@ -123,7 +123,7 @@ _assert_gbytes(GBytes *bytes, gconstpointer data, gssize len)
     if (!len)
         g_assert(!bytes);
 
-    g_assert(nm_utils_gbytes_equal_mem(bytes, data, len));
+    g_assert(nm_g_bytes_equal_mem(bytes, data, len));
 }
 
 static GKeyFile *
@@ -350,7 +350,7 @@ _test_8021x_cert_check(NMConnection *         con,
         }
 
         g_assert(blob);
-        g_assert(nm_utils_gbytes_equal_mem(blob, value, val_len));
+        g_assert(nm_g_bytes_equal_mem(blob, value, val_len));
 
         kval = g_key_file_get_string(keyfile, "802-1x", "ca-cert", NULL);
         g_assert(kval);
diff --git a/src/libnm-core-impl/tests/test-setting.c b/src/libnm-core-impl/tests/test-setting.c
index 6b65e98d..7444f2c1 100644
--- a/src/libnm-core-impl/tests/test-setting.c
+++ b/src/libnm-core-impl/tests/test-setting.c
@@ -7,6 +7,8 @@
 
 #include <linux/pkt_sched.h>
 #include <net/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_infiniband.h>
 
 #include "libnm-glib-aux/nm-uuid.h"
 #include "libnm-glib-aux/nm-json-aux.h"
@@ -119,7 +121,7 @@ test_nm_meta_setting_types_by_priority(void)
     G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM
                          == G_N_ELEMENTS(nm_meta_setting_types_by_priority));
 
-    G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM == 51);
+    G_STATIC_ASSERT_EXPR(_NM_META_SETTING_TYPE_NUM == 52);
 
     arr = g_ptr_array_new_with_free_func(g_object_unref);
 
@@ -150,7 +152,7 @@ test_nm_meta_setting_types_by_priority(void)
         for (j = 0; j < i; j++) {
             NMSetting *other = arr->pdata[j];
 
-            if (_nmtst_nm_setting_sort(other, setting) >= 0) {
+            if (_nm_setting_sort_for_nm_assert(other, setting) >= 0) {
                 g_error("sort order for nm_meta_setting_types_by_priority[%d vs %d] is wrong: %s "
                         "should be before %s",
                         j,
@@ -874,6 +876,34 @@ test_bond_normalize(void)
 
 /*****************************************************************************/
 
+static void
+test_dummy_normalize(void)
+{
+    gs_unref_object NMConnection *connection = NULL;
+    NMSettingConnection *         s_con;
+
+    connection = nm_simple_connection_new();
+    s_con      = NM_SETTING_CONNECTION(nm_setting_connection_new());
+    nm_connection_add_setting(connection, NM_SETTING(s_con));
+
+    g_object_set(s_con,
+                 NM_SETTING_CONNECTION_ID,
+                 "dummy-test",
+                 NM_SETTING_CONNECTION_UUID,
+                 nm_uuid_generate_random_str_a(),
+                 NM_SETTING_CONNECTION_TYPE,
+                 NM_SETTING_DUMMY_SETTING_NAME,
+                 NULL);
+
+    nmtst_assert_connection_unnormalizable(connection, 0, 0);
+
+    g_object_set(s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, "dummy1", NULL);
+
+    nmtst_connection_normalize(connection);
+}
+
+/*****************************************************************************/
+
 #define DCB_FLAGS_ALL \
     (NM_SETTING_DCB_FLAG_ENABLE | NM_SETTING_DCB_FLAG_ADVERTISE | NM_SETTING_DCB_FLAG_WILLING)
 
@@ -1183,7 +1213,7 @@ test_nm_json(void)
 #endif
 
 #if WITH_JANSSON != defined(JANSSON_SONAME)
-    #error "WITH_JANSON and JANSSON_SONAME are defined inconsistently."
+#error "WITH_JANSON and JANSSON_SONAME are defined inconsistently."
 #endif
 }
 
@@ -3219,7 +3249,7 @@ _rndt_wired_add_s390_options(NMSettingWired *s_wired, char **out_keyfile_entries
         g_assert(k);
         g_assert(v);
 
-        idx = nm_utils_strv_find_first((char **) opt_keys, n_opts, k);
+        idx = nm_strv_find_first(opt_keys, n_opts, k);
         g_assert(idx >= 0);
         g_assert(!opt_found[idx]);
         opt_found[idx] = TRUE;
@@ -3619,6 +3649,12 @@ test_roundtrip_conversion(gconstpointer test_data)
 
         s_wg = NM_SETTING_WIREGUARD(nm_connection_get_setting(con, NM_TYPE_SETTING_WIREGUARD));
 
+        s_ip.s_4 = NM_SETTING_IP_CONFIG(nm_connection_get_setting(con, NM_TYPE_SETTING_IP4_CONFIG));
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip.s_4), ==, "disabled");
+
+        s_ip.s_6 = NM_SETTING_IP_CONFIG(nm_connection_get_setting(con, NM_TYPE_SETTING_IP6_CONFIG));
+        g_assert_cmpstr(nm_setting_ip_config_get_method(s_ip.s_6), ==, "disabled");
+
         g_ptr_array_add(kf_data_arr,
                         g_strdup_printf("[connection]\n"
                                         "id=%s\n"
@@ -3636,7 +3672,7 @@ test_roundtrip_conversion(gconstpointer test_data)
                                         "[ipv6]\n"
                                         "addr-gen-mode=stable-privacy\n"
                                         "dns-search=\n"
-                                        "method=ignore\n"
+                                        "method=disabled\n"
                                         "\n"
                                         "[proxy]\n"
                                         "",
@@ -3693,7 +3729,7 @@ test_roundtrip_conversion(gconstpointer test_data)
                 "[ipv6]\n"
                 "addr-gen-mode=stable-privacy\n"
                 "dns-search=\n"
-                "method=ignore\n"
+                "method=disabled\n"
                 "\n"
                 "[proxy]\n"
                 "",
@@ -4354,6 +4390,9 @@ test_setting_metadata(void)
         guint                      prop_idx;
         gs_free GParamSpec **property_specs = NULL;
         guint                n_property_specs;
+        guint                n_param_spec;
+        guint                i;
+        guint                j;
 
         g_assert(sis);
 
@@ -4370,8 +4409,22 @@ test_setting_metadata(void)
         g_assert_cmpint(sis->property_infos_len, >, 0);
         g_assert(sis->property_infos);
 
+        {
+            int offset;
+
+            if (sis->private_offset < 0) {
+                offset = g_type_class_get_instance_private_offset(sis->setting_class);
+                g_assert_cmpint(sis->private_offset, ==, offset);
+            } else {
+                /* it would be nice to assert that this class has no private data.
+                 * But we cannot. */
+            }
+        }
+
         h_properties = g_hash_table_new(nm_str_hash, g_str_equal);
 
+        n_param_spec = 0;
+
         for (prop_idx = 0; prop_idx < sis->property_infos_len; prop_idx++) {
             const NMSettInfoProperty *sip = &sis->property_infos[prop_idx];
             GArray *                  property_types_data;
@@ -4380,6 +4433,9 @@ test_setting_metadata(void)
 
             g_assert(sip->name);
 
+            if (sip->param_spec)
+                n_param_spec++;
+
             if (prop_idx > 0)
                 g_assert_cmpint(strcmp(sis->property_infos[prop_idx - 1].name, sip->name), <, 0);
 
@@ -4387,17 +4443,152 @@ test_setting_metadata(void)
             g_assert(sip->property_type->dbus_type);
             g_assert(g_variant_type_string_is_valid((const char *) sip->property_type->dbus_type));
 
+            if (sip->property_type->direct_type == NM_VALUE_TYPE_NONE) {
+                g_assert_cmpint(sip->direct_offset, ==, 0);
+            } else if (sip->property_type->direct_type == NM_VALUE_TYPE_BOOL) {
+                g_assert(sip->property_type == &nm_sett_info_propert_type_direct_boolean);
+                g_assert(g_variant_type_equal(sip->property_type->dbus_type, "b"));
+                g_assert(sip->property_type->to_dbus_fcn
+                         == _nm_setting_property_to_dbus_fcn_direct);
+                g_assert(sip->param_spec);
+                g_assert(sip->param_spec->value_type == G_TYPE_BOOLEAN);
+                can_set_including_default = TRUE;
+            } else if (sip->property_type->direct_type == NM_VALUE_TYPE_INT32) {
+                const GParamSpecInt *pspec;
+
+                g_assert(sip->property_type == &nm_sett_info_propert_type_direct_int32);
+                g_assert(g_variant_type_equal(sip->property_type->dbus_type, "i"));
+                g_assert(sip->property_type->to_dbus_fcn
+                         == _nm_setting_property_to_dbus_fcn_direct);
+                g_assert(sip->param_spec);
+                g_assert(sip->param_spec->value_type == G_TYPE_INT);
+
+                pspec = NM_G_PARAM_SPEC_CAST_INT(sip->param_spec);
+                g_assert_cmpint(pspec->minimum, <=, pspec->maximum);
+                g_assert_cmpint(pspec->default_value, >=, pspec->minimum);
+                g_assert_cmpint(pspec->default_value, <=, pspec->maximum);
+
+                g_assert_cmpint(pspec->minimum, >=, (gint64) G_MININT32);
+                g_assert_cmpint(pspec->maximum, <=, (gint64) G_MAXINT32);
+
+                can_set_including_default = TRUE;
+            } else if (sip->property_type->direct_type == NM_VALUE_TYPE_UINT32) {
+                const GParamSpecUInt *pspec;
+
+                g_assert(sip->property_type == &nm_sett_info_propert_type_direct_uint32);
+                g_assert(g_variant_type_equal(sip->property_type->dbus_type, "u"));
+                g_assert(sip->property_type->to_dbus_fcn
+                         == _nm_setting_property_to_dbus_fcn_direct);
+                g_assert(sip->param_spec);
+                g_assert(sip->param_spec->value_type == G_TYPE_UINT);
+
+                pspec = NM_G_PARAM_SPEC_CAST_UINT(sip->param_spec);
+                g_assert_cmpint(pspec->minimum, <=, pspec->maximum);
+                g_assert_cmpint(pspec->default_value, >=, pspec->minimum);
+                g_assert_cmpint(pspec->default_value, <=, pspec->maximum);
+
+                g_assert_cmpint(pspec->maximum, <=, (guint64) G_MAXUINT32);
+
+                can_set_including_default = TRUE;
+            } else if (sip->property_type->direct_type == NM_VALUE_TYPE_UINT64) {
+                const GParamSpecUInt64 *pspec;
+
+                g_assert(sip->property_type == &nm_sett_info_propert_type_direct_uint64);
+                g_assert(g_variant_type_equal(sip->property_type->dbus_type, "t"));
+                g_assert(sip->property_type->to_dbus_fcn
+                         == _nm_setting_property_to_dbus_fcn_direct);
+                g_assert(sip->param_spec);
+                g_assert(sip->param_spec->value_type == G_TYPE_UINT64);
+
+                pspec = NM_G_PARAM_SPEC_CAST_UINT64(sip->param_spec);
+                g_assert_cmpuint(pspec->minimum, <=, pspec->maximum);
+                g_assert_cmpuint(pspec->default_value, >=, pspec->minimum);
+                g_assert_cmpuint(pspec->default_value, <=, pspec->maximum);
+
+                g_assert_cmpuint(pspec->maximum, <=, G_MAXUINT64);
+
+                can_set_including_default = TRUE;
+            } else if (sip->property_type->direct_type == NM_VALUE_TYPE_ENUM) {
+                const GParamSpecEnum *pspec;
+
+                g_assert(sip->property_type == &nm_sett_info_propert_type_direct_enum);
+                g_assert(g_variant_type_equal(sip->property_type->dbus_type, "i"));
+                g_assert(sip->property_type->to_dbus_fcn
+                         == _nm_setting_property_to_dbus_fcn_direct);
+                g_assert(sip->param_spec);
+                g_assert(g_type_is_a(sip->param_spec->value_type, G_TYPE_ENUM));
+                g_assert(sip->param_spec->value_type != G_TYPE_ENUM);
+
+                pspec = NM_G_PARAM_SPEC_CAST_ENUM(sip->param_spec);
+                g_assert(G_TYPE_FROM_CLASS(pspec->enum_class) == sip->param_spec->value_type);
+                g_assert(g_enum_get_value(pspec->enum_class, pspec->default_value));
+
+                can_set_including_default = TRUE;
+            } else if (sip->property_type->direct_type == NM_VALUE_TYPE_FLAGS) {
+                const GParamSpecFlags *pspec;
+
+                g_assert(sip->property_type == &nm_sett_info_propert_type_direct_flags);
+                g_assert(g_variant_type_equal(sip->property_type->dbus_type, "u"));
+                g_assert(sip->property_type->to_dbus_fcn
+                         == _nm_setting_property_to_dbus_fcn_direct);
+                g_assert(sip->param_spec);
+                g_assert(g_type_is_a(sip->param_spec->value_type, G_TYPE_FLAGS));
+                g_assert(sip->param_spec->value_type != G_TYPE_FLAGS);
+
+                pspec = NM_G_PARAM_SPEC_CAST_FLAGS(sip->param_spec);
+                g_assert_cmpint(pspec->flags_class->mask, !=, 0);
+                g_assert_cmpint(pspec->default_value,
+                                ==,
+                                pspec->flags_class->mask & pspec->default_value);
+
+                can_set_including_default = TRUE;
+            } else if (sip->property_type->direct_type == NM_VALUE_TYPE_STRING) {
+                if (sip->property_type == &nm_sett_info_propert_type_direct_mac_address) {
+                    g_assert(g_variant_type_equal(sip->property_type->dbus_type, "ay"));
+                    g_assert(sip->property_type->to_dbus_fcn
+                             == _nm_setting_property_to_dbus_fcn_direct_mac_address);
+                    g_assert(NM_IN_SET((guint) sip->direct_set_string_mac_address_len,
+                                       ETH_ALEN,
+                                       8,
+                                       INFINIBAND_ALEN));
+                } else {
+                    g_assert(g_variant_type_equal(sip->property_type->dbus_type, "s"));
+                    g_assert(sip->property_type->to_dbus_fcn
+                             == _nm_setting_property_to_dbus_fcn_direct);
+                }
+                g_assert(sip->param_spec);
+                g_assert(sip->param_spec->value_type == G_TYPE_STRING);
+            } else if (sip->property_type->direct_type == NM_VALUE_TYPE_BYTES) {
+                g_assert(g_variant_type_equal(sip->property_type->dbus_type, "ay"));
+                g_assert(sip->property_type->to_dbus_fcn
+                         == _nm_setting_property_to_dbus_fcn_direct);
+                g_assert(sip->param_spec);
+                g_assert(sip->param_spec->value_type == G_TYPE_BYTES);
+            } else
+                g_assert_not_reached();
+
+            if (sip->direct_set_string_ascii_strdown)
+                g_assert(sip->property_type->direct_type == NM_VALUE_TYPE_STRING);
+
+            if (sip->direct_set_string_mac_address_len != 0) {
+                g_assert(NM_IN_SET(sip->property_type,
+                                   &nm_sett_info_propert_type_direct_string,
+                                   &nm_sett_info_propert_type_direct_mac_address));
+                g_assert(sip->property_type->direct_type == NM_VALUE_TYPE_STRING);
+            }
+
+            g_assert(((sip->direct_set_string_mac_address_len != 0)
+                      + (!!sip->direct_set_string_ascii_strdown)
+                      + (sip->direct_set_string_ip_address_addr_family != 0))
+                     <= 1);
+
             if (!sip->property_type->to_dbus_fcn) {
                 /* it's allowed to have no to_dbus_fcn(), to ignore a property. But such
-                 * properties must not have a param_spec and no gprop_to_dbus_fcn. */
+                 * properties must not have a param_spec. */
                 g_assert(!sip->param_spec);
-                g_assert(!sip->to_dbus_data.none);
             } else if (sip->property_type->to_dbus_fcn == _nm_setting_property_to_dbus_fcn_gprop) {
                 g_assert(sip->param_spec);
                 switch (sip->property_type->typdata_to_dbus.gprop_type) {
-                case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_BYTES:
-                    g_assert(sip->param_spec->value_type == G_TYPE_BYTES);
-                    goto check_done;
                 case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_ENUM:
                     g_assert(g_type_is_a(sip->param_spec->value_type, G_TYPE_ENUM));
                     goto check_done;
@@ -4410,40 +4601,82 @@ test_setting_metadata(void)
                 case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_STRDICT:
                     g_assert(sip->param_spec->value_type == G_TYPE_HASH_TABLE);
                     goto check_done;
-                case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_MAC_ADDRESS:
-                    g_assert(sip->param_spec->value_type == G_TYPE_STRING);
-                    goto check_done;
                 case NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_DEFAULT:
                     goto check_done;
                 }
                 g_assert_not_reached();
 check_done:;
-                if (sip->property_type->typdata_to_dbus.gprop_type
-                    != NM_SETTING_PROPERTY_TO_DBUS_FCN_GPROP_TYPE_DEFAULT)
-                    g_assert(!sip->to_dbus_data.gprop_to_dbus_fcn);
                 can_set_including_default = TRUE;
-            } else if (sip->property_type->to_dbus_fcn
-                       == _nm_setting_property_to_dbus_fcn_get_boolean) {
-                g_assert(sip->param_spec);
-                g_assert(sip->param_spec->value_type == G_TYPE_BOOLEAN);
-                g_assert(sip->to_dbus_data.get_boolean);
-                can_set_including_default = TRUE;
-            } else if (sip->property_type->to_dbus_fcn
-                       == _nm_setting_property_to_dbus_fcn_get_string) {
-                g_assert(sip->param_spec);
-                g_assert(sip->param_spec->value_type == G_TYPE_STRING);
-                g_assert(sip->to_dbus_data.get_string);
             }
 
             if (!can_set_including_default)
-                g_assert(!sip->to_dbus_data.including_default);
+                g_assert(!sip->to_dbus_including_default);
 
-            g_assert(!sip->property_type->from_dbus_fcn
-                     || !sip->property_type->gprop_from_dbus_fcn);
+            g_assert(sip->property_type->from_dbus_fcn || !sip->param_spec);
+            if (sip->property_type->typdata_from_dbus.gprop_fcn) {
+                g_assert(sip->property_type->from_dbus_fcn
+                         == _nm_setting_property_from_dbus_fcn_gprop);
+            }
+            if (sip->property_type->from_dbus_direct_allow_transform) {
+                g_assert(sip->property_type->from_dbus_fcn
+                         == _nm_setting_property_from_dbus_fcn_direct);
+            }
+            if (sip->property_type->from_dbus_fcn == _nm_setting_property_from_dbus_fcn_direct) {
+                /* for the moment, all direct properties allow transformation. */
+                g_assert(sip->property_type->from_dbus_direct_allow_transform);
+            }
+
+            if (sip->property_type->from_dbus_fcn == _nm_setting_property_from_dbus_fcn_gprop)
+                g_assert(sip->param_spec);
+            if (sip->property_type->from_dbus_fcn) {
+                if (sip->property_type->direct_type != NM_VALUE_TYPE_NONE) {
+                    g_assert(NM_IN_SET(sip->property_type->from_dbus_fcn,
+                                       _nm_setting_property_from_dbus_fcn_direct_ip_config_gateway,
+                                       _nm_setting_property_from_dbus_fcn_direct_mac_address,
+                                       _nm_setting_property_from_dbus_fcn_direct));
+                }
+            }
+
+            g_assert(sip->property_type->from_dbus_is_full
+                     == NM_IN_SET(sip->property_type->from_dbus_fcn,
+                                  _nm_setting_property_from_dbus_fcn_direct,
+                                  _nm_setting_property_from_dbus_fcn_gprop,
+                                  _nm_setting_property_from_dbus_fcn_ignore));
 
             if (!g_hash_table_insert(h_properties, (char *) sip->name, sip->param_spec))
                 g_assert_not_reached();
 
+            if (sip->property_type->compare_fcn == _nm_setting_property_compare_fcn_default) {
+                g_assert(sip->param_spec);
+                g_assert_cmpstr(sip->name, !=, NM_SETTING_NAME);
+            } else if (sip->property_type->compare_fcn == _nm_setting_property_compare_fcn_direct) {
+                g_assert(sip->param_spec);
+                g_assert(sip->property_type->direct_type != NM_VALUE_TYPE_NONE);
+                g_assert(NM_IN_SET(sip->property_type->to_dbus_fcn,
+                                   _nm_setting_property_to_dbus_fcn_direct,
+                                   _nm_setting_property_to_dbus_fcn_direct_mac_address));
+            } else if (sip->property_type->compare_fcn == _nm_setting_property_compare_fcn_ignore) {
+                if (NM_IN_SET(sip->property_type,
+                              &nm_sett_info_propert_type_deprecated_ignore_i,
+                              &nm_sett_info_propert_type_deprecated_ignore_u,
+                              &nm_sett_info_propert_type_assigned_mac_address)) {
+                    /* pass */
+                } else if (!sip->param_spec) {
+                    /* pass */
+                } else if (nm_streq(sip->name, NM_SETTING_NAME)) {
+                    /* pass */
+                } else {
+                    /* ignoring a property for comparison make only sense in very specific cases. */
+                    g_assert_not_reached();
+                }
+            } else if (sip->property_type->compare_fcn) {
+                /* pass */
+            } else {
+                g_assert_not_reached();
+            }
+            g_assert((sip->property_type->compare_fcn != _nm_setting_property_compare_fcn_direct)
+                     || (sip->property_type->direct_type != NM_VALUE_TYPE_NONE));
+
             property_types_data = g_hash_table_lookup(h_property_types, sip->property_type);
             if (!property_types_data) {
                 property_types_data = g_array_new(FALSE, FALSE, sizeof(guint));
@@ -4460,32 +4693,51 @@ check_done:;
 
                 g_assert_cmpstr(sip->name, ==, sip->param_spec->name);
 
+                g_assert(NM_FLAGS_HAS(sip->param_spec->flags, G_PARAM_WRITABLE)
+                         != nm_streq(sip->name, NM_SETTING_NAME));
+                g_assert((sip->property_type == &nm_sett_info_propert_type_setting_name)
+                         == nm_streq(sip->name, NM_SETTING_NAME));
+
                 g_value_init(&val, sip->param_spec->value_type);
                 g_object_get_property(G_OBJECT(setting), sip->name, &val);
 
                 if (sip->param_spec->value_type == G_TYPE_STRING) {
-                    const char *default_value;
-
-                    default_value = ((const GParamSpecString *) sip->param_spec)->default_value;
-                    if (default_value) {
-                        /* having a string property with a default != NULL is really ugly. They
-                         * should be best avoided... */
-                        if (meta_type == NM_META_SETTING_TYPE_DCB
-                            && nm_streq(sip->name, NM_SETTING_DCB_APP_FCOE_MODE)) {
-                            /* Whitelist the properties that have a non-NULL default value. */
-                            g_assert_cmpstr(default_value, ==, NM_SETTING_DCB_FCOE_MODE_FABRIC);
-                        } else
-                            g_assert_not_reached();
-                    }
+                    /* String properties should all have a default value of NULL. Otherwise,
+                     * it's ugly. */
+                    g_assert_cmpstr(((const GParamSpecString *) sip->param_spec)->default_value,
+                                    ==,
+                                    NULL);
+                    g_assert(!NM_G_PARAM_SPEC_GET_DEFAULT_STRING(sip->param_spec));
 
                     if (nm_streq(sip->name, NM_SETTING_NAME))
                         g_assert_cmpstr(g_value_get_string(&val), ==, msi->setting_name);
                     else
-                        g_assert_cmpstr(g_value_get_string(&val), ==, default_value);
+                        g_assert_cmpstr(g_value_get_string(&val), ==, NULL);
                 }
 
                 if (NM_FLAGS_HAS(sip->param_spec->flags, NM_SETTING_PARAM_TO_DBUS_IGNORE_FLAGS))
                     g_assert(sip->property_type->to_dbus_fcn);
+
+                g_assert(!NM_FLAGS_HAS(sip->param_spec->flags, G_PARAM_CONSTRUCT));
+                g_assert(!NM_FLAGS_HAS(sip->param_spec->flags, G_PARAM_CONSTRUCT_ONLY));
+
+                if (NM_FLAGS_HAS(sip->param_spec->flags, NM_SETTING_PARAM_SECRET)) {
+                    if (sip->param_spec->value_type == G_TYPE_STRING) {
+                        g_assert_cmpstr(NM_G_PARAM_SPEC_GET_DEFAULT_STRING(sip->param_spec),
+                                        ==,
+                                        NULL);
+                    } else if (sip->param_spec->value_type == G_TYPE_BYTES) {
+                        /* pass */
+                    } else if (sip->param_spec->value_type == G_TYPE_HASH_TABLE) {
+                        g_assert(NM_IS_SETTING_VPN(setting));
+                        g_assert_cmpstr(sip->name, ==, NM_SETTING_VPN_SECRETS);
+                    } else {
+                        g_error("secret %s.%s is of unexpected property type %s",
+                                nm_setting_get_name(setting),
+                                sip->name,
+                                g_type_name(sip->param_spec->value_type));
+                    }
+                }
             }
         }
 
@@ -4533,6 +4785,44 @@ check_done:;
             g_assert_cmpstr(sis->property_infos[0].name, ==, NM_SETTING_NAME);
         } else
             g_assert_cmpint(meta_type, !=, NM_META_SETTING_TYPE_ETHTOOL);
+
+        g_assert_cmpint(n_param_spec, >, 0);
+        g_assert_cmpint(n_param_spec, ==, sis->property_lookup_by_param_spec_len);
+        g_assert(sis->property_lookup_by_param_spec);
+        for (i = 0; i < sis->property_lookup_by_param_spec_len; i++) {
+            const NMSettInfoPropertLookupByParamSpec *p = &sis->property_lookup_by_param_spec[i];
+            guint                                     n_found;
+
+            if (i > 0) {
+                g_assert_cmpint(sis->property_lookup_by_param_spec[i - 1].param_spec_as_uint,
+                                <,
+                                p->param_spec_as_uint);
+            }
+            g_assert(p->property_info);
+            g_assert(p->property_info >= sis->property_infos);
+            g_assert(p->property_info < &sis->property_infos[sis->property_infos_len]);
+            g_assert(p->property_info
+                     == &sis->property_infos[p->property_info - sis->property_infos]);
+
+            g_assert(p->property_info->param_spec);
+            g_assert(p->param_spec_as_uint
+                     == ((uintptr_t) ((gpointer) p->property_info->param_spec)));
+
+            g_assert(_nm_sett_info_property_lookup_by_param_spec(sis, p->property_info->param_spec)
+                     == p->property_info);
+
+            n_found = 0;
+            for (j = 0; j < sis->property_infos_len; j++) {
+                const NMSettInfoProperty *pip2 = &sis->property_infos[j];
+
+                if (pip2->param_spec
+                    && p->param_spec_as_uint == ((uintptr_t) ((gpointer) pip2->param_spec))) {
+                    g_assert(pip2 == p->property_info);
+                    n_found++;
+                }
+            }
+            g_assert(n_found == 1);
+        }
     }
 
     {
@@ -4552,10 +4842,14 @@ check_done:;
                 const NMSettInfoPropertType *pt_2 = a_property_types[prop_idx_2];
 
                 if (!g_variant_type_equal(pt->dbus_type, pt_2->dbus_type)
-                    || pt->to_dbus_fcn != pt_2->to_dbus_fcn
+                    || pt->direct_type != pt_2->direct_type || pt->to_dbus_fcn != pt_2->to_dbus_fcn
                     || pt->from_dbus_fcn != pt_2->from_dbus_fcn
+                    || pt->compare_fcn != pt_2->compare_fcn
                     || pt->missing_from_dbus_fcn != pt_2->missing_from_dbus_fcn
-                    || pt->gprop_from_dbus_fcn != pt_2->gprop_from_dbus_fcn
+                    || memcmp(&pt->typdata_from_dbus,
+                              &pt_2->typdata_from_dbus,
+                              sizeof(pt->typdata_from_dbus))
+                           != 0
                     || memcmp(&pt->typdata_to_dbus,
                               &pt_2->typdata_to_dbus,
                               sizeof(pt->typdata_to_dbus))
@@ -4632,44 +4926,44 @@ test_setting_connection_secondaries_verify(void)
 
         g_object_set(s_con, NM_SETTING_CONNECTION_SECONDARIES, arr->pdata, NULL);
 
-#define _assert_secondaries(s_con, expected)                                                       \
-    G_STMT_START                                                                                   \
-    {                                                                                              \
-        NMSettingConnection *const _s_con    = (s_con);                                            \
-        const char *const *        _expected = (expected);                                         \
-        GArray *                   _secondaries;                                                   \
-        const guint                _expected_len = NM_PTRARRAY_LEN(_expected);                     \
-        gs_strfreev char **        _sec_strv     = NULL;                                           \
-        guint                      _i;                                                             \
-                                                                                                   \
-        g_assert(_expected);                                                                       \
-                                                                                                   \
-        if (nmtst_get_rand_bool()) {                                                               \
-            _secondaries = _nm_setting_connection_get_secondaries(_s_con);                         \
-            g_assert_cmpint(_expected_len, ==, nm_g_array_len(_secondaries));                      \
-            g_assert((_expected_len == 0) == (!_secondaries));                                     \
-            g_assert(nm_utils_strv_equal(_expected,                                                \
-                                         _secondaries ? nm_strvarray_get_strv(&_secondaries, NULL) \
-                                                      : NM_PTRARRAY_EMPTY(const char *)));         \
-        }                                                                                          \
-                                                                                                   \
-        if (nmtst_get_rand_bool()) {                                                               \
-            g_object_get(_s_con, NM_SETTING_CONNECTION_SECONDARIES, &_sec_strv, NULL);             \
-            g_assert_cmpint(_expected_len, ==, NM_PTRARRAY_LEN(_sec_strv));                        \
-            g_assert((_expected_len == 0) == (!_sec_strv));                                        \
-            g_assert(nm_utils_strv_equal(_expected, _sec_strv ?: NM_STRV_EMPTY()));                \
-        }                                                                                          \
-                                                                                                   \
-        g_assert_cmpint(nm_setting_connection_get_num_secondaries(_s_con), ==, _expected_len);     \
-        if (nmtst_get_rand_bool()) {                                                               \
-            for (_i = 0; _i < _expected_len; _i++) {                                               \
-                g_assert_cmpstr(nm_setting_connection_get_secondary(_s_con, _i),                   \
-                                ==,                                                                \
-                                _expected[_i]);                                                    \
-            }                                                                                      \
-            g_assert_null(nm_setting_connection_get_secondary(_s_con, _expected_len));             \
-        }                                                                                          \
-    }                                                                                              \
+#define _assert_secondaries(s_con, expected)                                                   \
+    G_STMT_START                                                                               \
+    {                                                                                          \
+        NMSettingConnection *const _s_con    = (s_con);                                        \
+        const char *const *        _expected = (expected);                                     \
+        GArray *                   _secondaries;                                               \
+        const guint                _expected_len = NM_PTRARRAY_LEN(_expected);                 \
+        gs_strfreev char **        _sec_strv     = NULL;                                       \
+        guint                      _i;                                                         \
+                                                                                               \
+        g_assert(_expected);                                                                   \
+                                                                                               \
+        if (nmtst_get_rand_bool()) {                                                           \
+            _secondaries = _nm_setting_connection_get_secondaries(_s_con);                     \
+            g_assert_cmpint(_expected_len, ==, nm_g_array_len(_secondaries));                  \
+            g_assert((_expected_len == 0) == (!_secondaries));                                 \
+            g_assert(nm_strv_equal(_expected,                                                  \
+                                   _secondaries ? nm_strvarray_get_strv(&_secondaries, NULL)   \
+                                                : NM_PTRARRAY_EMPTY(const char *)));           \
+        }                                                                                      \
+                                                                                               \
+        if (nmtst_get_rand_bool()) {                                                           \
+            g_object_get(_s_con, NM_SETTING_CONNECTION_SECONDARIES, &_sec_strv, NULL);         \
+            g_assert_cmpint(_expected_len, ==, NM_PTRARRAY_LEN(_sec_strv));                    \
+            g_assert((_expected_len == 0) == (!_sec_strv));                                    \
+            g_assert(nm_strv_equal(_expected, _sec_strv ?: NM_STRV_EMPTY()));                  \
+        }                                                                                      \
+                                                                                               \
+        g_assert_cmpint(nm_setting_connection_get_num_secondaries(_s_con), ==, _expected_len); \
+        if (nmtst_get_rand_bool()) {                                                           \
+            for (_i = 0; _i < _expected_len; _i++) {                                           \
+                g_assert_cmpstr(nm_setting_connection_get_secondary(_s_con, _i),               \
+                                ==,                                                            \
+                                _expected[_i]);                                                \
+            }                                                                                  \
+            g_assert_null(nm_setting_connection_get_secondary(_s_con, _expected_len));         \
+        }                                                                                      \
+    }                                                                                          \
     G_STMT_END
 
         _assert_secondaries(s_con, (const char *const *) arr->pdata);
@@ -4688,14 +4982,14 @@ test_setting_connection_secondaries_verify(void)
             if (is_normalized)
                 s = uuid_normalized;
 
-            if (nm_utils_strv_find_first((char **) arr_norm->pdata, arr_norm->len, s) >= 0)
+            if (nm_strv_ptrarray_find_first(arr_norm, s) >= 0)
                 continue;
 
             g_ptr_array_add(arr_norm, g_strdup(s));
         }
         g_ptr_array_add(arr_norm, NULL);
 
-        was_normalized = !nm_utils_strv_equal((char **) arr->pdata, (char **) arr_norm->pdata);
+        was_normalized = !nm_strv_equal((char **) arr->pdata, (char **) arr_norm->pdata);
 
         if (was_normalized)
             nmtst_assert_connection_verifies_and_normalizable(con);
@@ -4713,6 +5007,38 @@ test_setting_connection_secondaries_verify(void)
 
 /*****************************************************************************/
 
+static void
+test_6lowpan_1(void)
+{
+    gs_unref_object NMConnection *con = NULL;
+    NMSetting6Lowpan *            s_6low;
+    gs_free char *                value = NULL;
+
+    con = nmtst_create_minimal_connection("test-sec", NULL, NM_SETTING_6LOWPAN_SETTING_NAME, NULL);
+
+    s_6low = NM_SETTING_6LOWPAN(nm_connection_get_setting(con, NM_TYPE_SETTING_6LOWPAN));
+    g_assert(s_6low);
+
+    g_assert_cmpstr(nm_setting_6lowpan_get_parent(s_6low), ==, NULL);
+    g_object_get(s_6low, NM_SETTING_6LOWPAN_PARENT, &value, NULL);
+    g_assert_cmpstr(value, ==, NULL);
+    nm_clear_g_free(&value);
+
+    g_object_set(s_6low, NM_SETTING_6LOWPAN_PARENT, "hello", NULL);
+    g_assert_cmpstr(nm_setting_6lowpan_get_parent(s_6low), ==, "hello");
+    g_object_get(s_6low, NM_SETTING_6LOWPAN_PARENT, &value, NULL);
+    g_assert_cmpstr(value, ==, "hello");
+    nm_clear_g_free(&value);
+
+    g_object_set(s_6low, NM_SETTING_6LOWPAN_PARENT, "world", NULL);
+    g_assert_cmpstr(nm_setting_6lowpan_get_parent(s_6low), ==, "world");
+    g_object_get(s_6low, NM_SETTING_6LOWPAN_PARENT, &value, NULL);
+    g_assert_cmpstr(value, ==, "world");
+    nm_clear_g_free(&value);
+}
+
+/*****************************************************************************/
+
 NMTST_DEFINE();
 
 int
@@ -4741,6 +5067,8 @@ main(int argc, char **argv)
     g_test_add_func("/libnm/settings/bond/compare", test_bond_compare);
     g_test_add_func("/libnm/settings/bond/normalize", test_bond_normalize);
 
+    g_test_add_func("/libnm/settings/dummy/normalize", test_dummy_normalize);
+
     g_test_add_func("/libnm/settings/dcb/flags-valid", test_dcb_flags_valid);
     g_test_add_func("/libnm/settings/dcb/flags-invalid", test_dcb_flags_invalid);
     g_test_add_func("/libnm/settings/dcb/app-priorities", test_dcb_app_priorities);
@@ -4752,6 +5080,8 @@ main(int argc, char **argv)
     g_test_add_func("/libnm/settings/ethtool/ring", test_ethtool_ring);
     g_test_add_func("/libnm/settings/ethtool/pause", test_ethtool_pause);
 
+    g_test_add_func("/libnm/settings/6lowpan/1", test_6lowpan_1);
+
     g_test_add_func("/libnm/settings/sriov/vf", test_sriov_vf);
     g_test_add_func("/libnm/settings/sriov/vf-dup", test_sriov_vf_dup);
     g_test_add_func("/libnm/settings/sriov/vf-vlan", test_sriov_vf_vlan);